2014-12-22 30 views
5

Mam następujące makra:Jaki jest najlepszy sposób, aby ponownie ustanowić rodzaj spójności po transformacji drzewo z makrami

def testMacro[T](x: T): Option[T] = macro testMacroImpl[T] 

def testMacroImpl[T: c.WeakTypeTag](c: Context)(x: c.Expr[T]): c.Expr[Option[T]] = { 
    import c.universe._ 
    val newTree = x.tree match { 
     case Block(List(first), second) => { 
     val newFirst = first match { 
      case ValDef(mods, name, _, rhs) => ValDef(mods, name, EmptyTree, q"Some($rhs)") 
     } 
     Block(List(newFirst), second) 
     } 
    } 
    c.Expr[Option[T]](newTree) 
    } 

Zasadniczo powinno to tylko przemienia to:

testMacro { 
    val aa = "hello" 
    aa 
} 

do

{ 
    val aa = Some("hello") 
    aa 
} 

Nie powiedzie się jednak z następującym błędem:

[error] found : String 
[error] required: Option[String] 
[error]  val f = IdiomBracket.testMacro{ 
[error] 
             ^

Moje dochodzenie pokazuje, że jest to spowodowane tym, że otrzymuje wpisane drzewo, dla którego identyfikator aa ma typ String. Ze względu na przekształcenie kodu jest to teraz typ Option[String], ale typ identyfikatora nie został zaktualizowany. Próbowałem tworzenia nowego (bez typu) identyfikator, a to tylko sprawia, że ​​błąd ten:

[error] found : <notype> 
[error] required: Option[String] 
[error]  val f = IdiomBracket.testMacro{ 
[error]         ^

Próbowałem typ sprawdzania drzewa przed wysłaniem go w nadziei, że będzie wypełnienie odpowiedniego typu, ale że wydaje się nie mieć żadnego efektu.

Dla odniesienia, tutaj jest to samo makro, które tworzy nowy Ident i wykonuje typologię, która niestety nadal nie działa.

def testMacroImpl[T: c.WeakTypeTag](c: Context)(x: c.Expr[T]): c.Expr[Option[T]] = { 
    import c.universe._ 
    val newTree = x.tree match { 
     case Block(List(first), second) => { 
     val newFirst = first match { 
      case ValDef(mods, name, _, rhs) => ValDef(mods, name, EmptyTree, q"Some($rhs)") 
     } 
     val newSecond = second match { 
      case ident: Ident => Ident(ident.name) 
     } 
     Block(List(newFirst), newSecond) 
     } 
    } 
    c.Expr[Option[T]](c.typecheck(newTree)) 
    } 

Odpowiedz

4

wiersz:

case ValDef(mods, name, _, rhs) => ValDef(mods, name, EmptyTree, q"Some($rhs)") 

potrzeby bycia zmieniono na tę linię:

case ValDef(mods, name, _, rhs) => ValDef(mods, name, TypeTree(), q"Some($rhs)") 

Prawdopodobnie nigdy nie ma sensu umieszczanie EmptyTree jako typu w ValDef. Szkoda, że ​​kompilator nie mógł mi tego powiedzieć, zaoszczędziłoby mi 48 godzin pracy w niepełnym wymiarze godzin.

1

Co powiesz na dopasowanie quasiquote? Dla mnie to działało:

case q"val $a = $b" => q"val $a = Some($b)" 

Poniżej kompletny przykład z dziennika konsoli (w Scala 2.11.4 oraz wiązek makro)

val universe: reflect.runtime.universe.type = reflect.runtime.universe 
import universe._ 
import reflect.runtime.currentMirror 
import tools.reflect.ToolBox 
val toolbox = currentMirror.mkToolBox() 
import scala.reflect.macros.blackbox.Context 
import scala.language.experimental.macros 

class Impl(val c:Context) { 
    def optIt[T: c.WeakTypeTag](x:c.Expr[T]) = { 
    import c.universe._ 
    val newTree = x.tree match { 
     case Block(List(first), second) => { 
     val newFirst = first match { 
      case q"val $a = $b" => q"val $a = Some($b)" 
     } 
     val newSecond = second match { 
      case ident: Ident => Ident(ident.name) 
      case x => println(s"x=$x"); x 
     } 
     Block(List(newFirst), newSecond) 
     } 
    } 
    c.Expr[Option[T]](c.typecheck(newTree)) 
    } 
} 

scala> def testMacro[T](x:T) = macro Impl.optIt[T] 
warning: there was one deprecation warning; re-run with -deprecation for details 
defined term macro testMacro: [T](x: T)Option[T] 

scala> testMacro { 
    | val aa = "hello" 
    | aa 
    | } 
res4: Option[String] = Some(hello) 

scala> testMacro { 
    | val ii = 5 
    | ii 
    | } 
res5: Option[Int] = Some(5) 
+0

Rzeczywiście działa, ale nie rozwiązuje podstawowego problemu, jeśli nie mogę z jakiegoś powodu użyć quasiquotes. – jedesah