2015-09-17 34 views
7

Mam więc adnotację o funkcji (DefDef). Ta adnotacja ma parametry. Jednak nie mam pojęcia, jak uzyskać parametry od konstruktora.Uzyskiwanie parametrów z notatki Scala Macro

Przykład użycia:

class TestMacro { 
    @Foo(true) 
    def foo(): String = "" 
    foo 
} 

Oto kod adnotacji:

class Foo(b: Boolean) extends StaticAnnotation { 
    def macroTransform(annottees: Any*) = macro Foo.impl 
} 

object Foo { 
    def impl(c: whitebox.Context)(annottees: c.Tree*): c.Expr[Any] = { 
    import c.universe._ 
    //how do I get value of `b` here??? 
    c.abort(c.enclosingPosition, "message") 
    } 
} 

Odpowiedz

7

Co o tym:

val b: Boolean = c.prefix.tree match { 
    case q"new Foo($b)" => c.eval[Boolean](c.Expr(b)) 
} 

Dla Uzupełniająco jest to pełny źródło:

import scala.reflect.macros.Context 
import scala.language.experimental.macros 
import scala.annotation.StaticAnnotation 
import scala.annotation.compileTimeOnly 
import scala.reflect.api.Trees 
import scala.reflect.runtime.universe._ 

class Foo(b: Boolean) extends StaticAnnotation { 
    def macroTransform(annottees: Any*) :Any = macro FooMacro.impl 
} 

object FooMacro { 
    def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = { 
    import c.universe._ 
    val b: Boolean = c.prefix.tree match { 
     case q"new Foo($b)" => c.eval[Boolean](c.Expr(b)) 
    } 
    c.abort(c.enclosingPosition, "message") 
    } 
} 
+0

To rozwiązanie jest interesujące, ale z jakiegoś powodu wydaje mi się "hacky". Również chcę, aby '' b'' był typu Boolean, a nie typu Any. Tak jak ktoś może powiedzieć '' @Foo ("AAA") '' i myśleć, że ich kod skompiluje się, patrząc na sygnaturę konstruktora. Poza tym stanie się bardziej zagmatwany, jeśli zdecyduję się dodać więcej argumentów. Ale to rozwiązanie działa, więc dziękuję! –

+0

Odpowiedź edytowano, aby odzwierciedlić Twoje obawy. –

+0

Ten kod nie jest teraz kompilowany. Po zmianie typu parametru z Boolean na Any wszystko jest w porządku. Komunikat o błędzie: '' wyrażenie typu Null nie kwalifikuje się do niejawnej konwersji '' –

1

To jest odpowiedź, która pokazuje odmianę techniki Federico, jeśli chcesz użyć adnotacji statycznej, która ma opcjonalne nazwane argumenty. W takim przypadku należy wziąć pod uwagę możliwe wyrazy wywołania w instrukcji dopasowania sprawy. Opcjonalny argument może być jawnie nazwany, może być podany bez nazwy lub może nie być obecny. Każdy z nich pojawia się w czasie kompilacji jako osobny wzorzec w c.prefix.tree, jak pokazano poniżej.

@compileTimeOnly("Must enable the Scala macro paradise compiler plugin to expand static annotations") 
class noop(arg1: Int, arg2: Int = 0) extends StaticAnnotation { 
    def macroTransform(annottees: Any*): Any = macro AnnotationMacros.noop 
} 

class AnnotationMacros(val c: whitebox.Context) { 
    import c.universe._ 

    // an annotation that doesn't do anything: 
    def noop(annottees: c.Expr[Any]*): c.Expr[Any] = { 
    // cases for handling optional arguments 
    val (arg1q, arg2q) = c.prefix.tree match { 
     case q"new noop($arg1, arg2 = $arg2)" => (arg1, arg2) // user gave named arg2 
     case q"new noop($arg1, $arg2)" => (arg1, arg2)   // arg2 without name 
     case q"new noop($arg1)" => (arg1, q"0")    // arg2 defaulted 
     case _ => c.abort(c.enclosingPosition, "unexpected annotation pattern!") 
    } 

    // print out the values 
    println(s"arg1= ${evalTree[Int](arg1q)} arg2= ${evalTree[Int](arg2q)}") 

    // just return the original annotee: 
    annottees.length match { 
     case 1 => c.Expr(q"{ ${annottees(0)} }") 
     case _ => c.abort(c.enclosingPosition, "Only one annottee!") 
    } 
    } 

    def evalTree[T](tree: Tree) = c.eval(c.Expr[T(c.untypecheck(tree.duplicate))) 
} 

Oto przykład inwokacja, że ​​nazwy arg2, a więc będzie pasować do pierwszego wzoru - case q"new noop($arg1, arg2 = $arg2)" - powyżej:

object demo { 
    // I will match this pattern: case q"new noop($arg1, arg2 = $arg2)" 
    @noop(1, arg2 = 2) 
    trait someDeclarationToAnnotate 
} 

Należy również zauważyć, że ze względu na sposób działają te wzory, trzeba jawnie podaj domyślną wartość argumentu wewnątrz kodu makr, która jest niestety nieco zhakowana, ale ostateczna oceniana klasa nie jest dla ciebie dostępna.

W ramach eksperymentu próbowałem faktycznie utworzyć klasę, dzwoniąc pod numer evalTree[scope.of.class.noop](c.prefix.tree), ale kompilator Scali zgłosił błąd, ponieważ uznał, że odwołanie do adnotacji wewnątrz kodu makr adnotacji jest nielegalne.