2013-05-07 2 views
0

(для TL; DR, перейти к смелой части лиц)Использования DEF-макросов, чтобы захватить исходный код

Я имею чистую замкнутую систему класса типа с сериализацией (оторванную от POJO сериализации бед). Например:

trait Expr 
case class Const(i: Int) extends Expr 
case class BinOp(a: Expr, b: Expr, op: Int) extends Expr 

Но в ситуациях мне нужно зафиксировать замыкание. Например:

case class Map(a: Expr, fun: Expr => Expr) extends Expr 

Теперь я решил в этот раз с POJO сериализации (ObjectOutputStream и т.д.) для fun. Меня сильно укусили в ногах, потому что я не мог прочитать в Scala 2.10 то, что я сделал сериализацией в 2.9. И в этом случае мне действительно нужно убедиться, что я смогу вернуть свои данные независимо от версии Scala.

Итак ... Я думал, что могу использовать макрос для создания «резервной копии» исходного кода, так что, если POJO-десериализация не удалась, я могу восстановить функцию из источника (используя компилятор на месте/переводчик).

Моя идея была бы

object Map { 
    def apply(a: Expr, fun: Expr => Expr): Map = macro applyImpl 
    private def applyImpl = ??? 

    def unapply(m: Map): Option[(Expr, Expr => Expr)] = Some(m.a -> m.fun) 
} 
trait Map extends Expr { 
    def a: Expr 
    def fun: Expr => Expr 
} 

implicit class ExprOps(val ex: Expr) extends AnyVal { 
    def map(fun: Expr => Expr) = Map(ex, fun) 
} 

ли это, возможно, легко захватить источник вызова как

//   |------------- source of this -------------| 
someExpr.map { case Const(i) => Const(i*i); case x => x } 

(Мое предположение является DEF-макро должен быть уже в map функция ExprOps).

+2

Я не думаю, что вы можете получить источник, но вы можете получить 'Tree':' def applyImpl (c: Context) (a: c.Expr [Expr], fun: c.Expr [Expr => Expr]): c.Expr [Map] = {val source = c.universe.show (fun.tree); ...} ' – senia

+0

@senia спасибо за ссылку и комментарий. Мне не нужен исходный код, потому что он не будет отображаться (возможно) пользователю, я просто хочу, чтобы источник, который я мог перекомпилировать в одно и то же дерево, даже если сериализованная версия говорит 'Tree' (поэтому я не хочу сериализовать дерево напрямую). Итак, я посмотрю на это 'universe.show', которого действительно может быть достаточно. –

ответ

0

Макросы замены текста являются удивительными в таких вещах. Scala не приходит с ними, но подумайте о том, чтобы написать свой собственный! Трансформирование, например.

{# case Const(i) => Const(i*i); case x => x #} 

в

({ case Const(i) => Const(i*i); case x => x }, """case Const(i) => Const(i*i); case x => x""") 

должно быть довольно легко; то вам просто нужно предварительно обработать перед компиляцией. Если вы хотите, чтобы ваша среда IDE не запуталась в разных длинах строк, вы можете сохранить строки в отдельном объекте, например.

{# _+7 #}/*STORE*/ 

идет в

({_+7}, Store.aW4) 

... 

object Store { 
    val aW4 = """_+7""" 
} 
//(EOF) 

(Для достижения наилучших результатов, база-64 закодировать захваченный текст. Вложение будет работать нормально до тех пор, как вы работаете рекурсивно и знают, что вложенности может случиться.)

+0

Мне нужно иметь возможность делать это во время выполнения со встроенным интерпретатором. В моем компилируемом проекте/IDE у меня уже есть доступ к исходному коду :) Я мог бы, конечно, захватить текст из интерпретатора (и я сделал это раньше), но тогда мне нужно возиться с поиском местоположений источника (кроме конечно, я добавляю escape-символы, как вы делаете с '#'). –