Я хотел бы выполнить обход AST с помощью макросов для типов AST, таких как:
trait Node
case class Root(children: Seq[Node]) extends Node {
override def toString = s"Root(${children.size})"
}
case class Bi(left: Node, var right: Node) extends Node
case class Leaf(id: String) extends Node
В следующем коде я могу обнаружить элементы T
, которые имеют тип B
, и создать функцию называя их. Я также хотел бы обнаружить члены с типом Seq[B]
(и, возможно, другие контейнеры, содержащие B
). Я попытался создать тип TypeApply(Ident(TermName("Seq")), List(tq"$B"))
, который, кажется, работает, однако вызов .tpe
для этого возвращает null
, поэтому я не могу вызвать <:<
для результата.
Как я могу сделать f.asMethod.returnType <:< Seq[B]
?
def walker[B, T <: B]: (T, B => Unit) => Unit = macro walker_impl[B, T]
def walker_impl[B: c.WeakTypeTag, T: c.WeakTypeTag](c: blackbox.Context): c.Expr[(T, B => Unit) => Unit] = {
import c.universe._
val T = weakTypeOf[T]
val B = weakTypeOf[B]
val seqType = TypeApply(Ident(TermName("Seq")), List(tq"$B"))
val dive = T.decls.collect {
case f if f.isMethod && f.asMethod.paramLists.isEmpty && f.asMethod.isGetter && f.asMethod.returnType <:< B =>
q"t.$f"
case f if f.isMethod && f.asMethod.paramLists.isEmpty &&
f.asMethod.isGetter && f.asMethod.returnType <:< seqType.tpe
=>
q"t.$f" // TODO: decompose the seq
}
c.Expr[(T, B => Unit) => Unit](
q"(t: $T, f: $B => Unit) => Seq(..$dive).map(f)"
)
}
Полный проект можно посмотреть на https://github.com/OndrejSpanel/ScalaMacroMembers