Создание дерева определения метода из символа метода и тела

Есть ли удобный способ превратить _ 1_ в левую часть дерева определения метода (т. Е. _ 2_) в Scala 2.10?

Например, предположим, что я хочу создать макрос, который будет принимать экземпляр признака и обертывать все методы этого признака некоторыми функциями отладки. Я могу написать следующее:

import scala.language.experimental.macros
import scala.reflect.macros.Context

object WrapperExample {
  def wrap[A](a: A): A = macro wrap_impl[A]

  def wrap_impl[A: c.WeakTypeTag](c: Context)(a: c.Expr[A]) = {
    import c.universe._

    val wrapped = weakTypeOf[A]
    val f = Select(reify(Predef).tree, "println")

    val methods = wrapped.declarations.collect {
      case m: MethodSymbol if !m.isConstructor => DefDef(
        Modifiers(Flag.OVERRIDE),
        m.name,
        Nil, Nil,
        TypeTree(),
        Block(
          Apply(f, c.literal("Calling: " + m.name.decoded).tree :: Nil),
          Select(a.tree, m.name)
        )
      )
    }.toList

  //...
}

Я избавился от утомительной работы по встраиванию этих методов в новый анонимный класс, который реализует эту черту, а затем создает экземпляр этого класса - вы можете найти полный рабочий пример здесь, если вам интересно.

Теперь я могу написать это, например:

scala> trait X { def foo = 1; def bar = 'a }
defined trait X

scala> val x = new X {}
x: X = $anon$1@15dd533

scala> val w: X = WrapperExample.wrap[X](x)
w: X = $1$$1@27c3a4a3

scala> w.foo
Calling: foo
res0: Int = 1

scala> w.bar
Calling: bar
res1: Symbol = 'a

Так что это работает, но только в очень простых случаях - не будет, если у трейта есть методы со списками параметров, с модификаторами доступа, аннотациями и т. Д.

Что мне действительно нужно, так это функция, которая примет символ метода и дерево для нового тела и вернет DefDef. Я начал писать один от руки, но он включает в себя множество неудобных вещей вроде этого:

List(if (method.isImplicit) Some(Flag.IMPLICIT) else None, ...)

Это раздражает, многословно и подвержено ошибкам. Не хватает ли мне более удобного способа сделать это в новом API отражения?


person Travis Brown    schedule 07.12.2012    source источник


Ответы (2)


Насколько мне известно, стандартного способа перейти от символа к определяющему дереву не существует.

Лучше всего, вероятно, будет проходить через c.enclosingRun.units, рекурсивно переходя в каждое из unit.body деревьев по мере продвижения. Если вы видите DefDef, в котором symbol соответствует вашему символу, значит, вы достигли пункта назначения. UPD. Не забудьте duplicate определить дерево перед его повторным использованием!

Этот прием далеко не самый удобный в мире, но он должен работать.

person Eugene Burmako    schedule 07.12.2012
comment
Спасибо (и +1), но что, если черта, которую я хочу обернуть, взята из библиотеки? В этом случае я придерживаюсь описанного выше подхода, верно? - person Travis Brown; 07.12.2012
comment
А, я понимаю, что ты имеешь в виду. Вы можете использовать этот API: rel="noreferred"norefer"> github.com/scalamacros/kepler/blob/. Мы устарели в версии 2.10.1, но вы можете посмотреть, как это реализовано: github.com/scalamacros/kepler/blob/. - person Eugene Burmako; 07.12.2012

Вы можете попробовать следующее. Это работает с несколькими параметрами, каррированными функциями и параметрами типа;)

val methods = wrapped.declarations.collect {
  case m: MethodSymbol if !m.isConstructor => DefDef(
    Modifiers(Flag.OVERRIDE),
    m.name,
    m.typeParams.map(TypeDef(_)),
    m.paramss.map(_.map(ValDef(_))),
    TypeTree(m.returnType),
    Block(
      Apply(f, c.literal("Calling: " + m.name.decoded).tree :: Nil),
      m.paramss.foldLeft(Select(a.tree.duplicate, m.name): Tree)((prev, params) =>
        Apply(prev, params.map(p => Ident(p.name)))
      )
    )
  )
}.toList
person Leo    schedule 08.12.2012
comment
Также есть DefDef(sym, tree), который делает то же самое, но, как упоминалось ниже, этот метод только что устарел вместе с TypeDef(sym) и ValDef(sym). UPD. Это не то же самое, потому что ваш код, кажется, опускает существующие модификаторы. - person Eugene Burmako; 08.12.2012
comment
Также см. Мои комментарии к сути OP: gist.github.com/4234441. У вашего кода та же тонкая проблема, что и у этого кода. - person Eugene Burmako; 08.12.2012
comment
Спасибо, Евгений. Я соответствующим образом обновил свой ответ. Это может объяснить некоторые проблемы с некоторыми из моих макросов, связанные с неприятными сбоями компилятора. - person Leo; 08.12.2012