2013-08-27 206 views
2

这是一个简化的示例,但问题依然存在。斯卡拉宏:将树转换/解析为名称

我想要实现这个使用宏(斯卡拉基于伪代码):

(a: Int) => { 
    val z = "toShort" 
    a.z 
} 

如果我具体化,我会获得类似这样:

Function(
    List(
    ValDef(
     Modifiers(Flag.PARAM), 
     newTermName("a"), 
     Ident(scala.Int), 
     EmptyTree 
    ) 
), 
    Block(
    List(
     ValDef(
     Modifiers(), 
     newTermName("z"), 
     TypeTree(), 
     Literal(Constant("toShort")) 
    ) 
    ), 
    Apply(
     Select(
     Ident(newTermName("a")), 
     newTermName("toShort") 
    ), 
     List() 
    ) 
) 
) 

我不知道如何访问为一个值,然后将其用作TermName。

我试图取代newTermName("toShort")newTermName(c.Expr[String](Select(Ident(newTermName("z")))).splice)但是编译器似乎并不喜欢:

exception during macro expansion: java.lang.UnsupportedOperationException: the function you're calling has not been spliced by > the compiler. this means there is a cross-stage evaluation involved, and it needs to be invoked explicitly. if you're sure this is not an oversight, add scala-compiler.jar to the classpath, import scala.tools.reflect.Eval and call <your expr>.eval instead.

我也试过“EVAL”的建议由编译器:newTermName(c.eval(c.Expr[String](...))但既不工作。

我怎么能转换像Select(Ident(newTermName("z")))树(这是一个本地的val值的访问) 一个名称 一个字符串,它可以被用来作为newTermName参数?可能吗?

UPDATE:

带到这里你作为一个gist真正的问题!

由于提前,

+0

'newTermName(“z”)''已经返回一个'Name'。我不明白那个有什么问题。 – sschaef

+0

你的意思是你想从现有的树中提取它吗?像'val Select(Ident(name))= tree'? – sschaef

+0

我想使用'val z'的值作为'newTermName'方法的参数。 'Select(Ident(newTermName(“z”)))'是对该值的访问,然后是:'newTermName(“”)''。所以可以这样做:'a.toShort'。 –

回答

3

我很难理解你想要什么来实现的,以及为什么你正在使用的树木随处可见。树木真的很低级,很难使用,很棘手,而且很难理解代码的作用。 Quasiquotes(http://docs.scala-lang.org/overviews/macros/quasiquotes.html)是确实可行的方法,感谢macro paradise插件(http://docs.scala-lang.org/overviews/macros/paradise.html),您可以在scala 2.10.x版本上使用它们。你可以简单地写q"(a: Int) => {val z = "toShort"; a.z}",你直接得到你刚输入的树形表达式。

要回答你的问题,第一点是要记住宏在编译时进行评估。因此它们不能生成依赖于运行时值的代码。这就是为什么编译器在抱怨你的splice。但是如果你传递一个可以在编译时计算的值,通常是一个文字,那么你可以使用eval在你的宏代码中得到它的值。正如scaladoc所指出的,Eval确实遭受了一个错误。它只能在未定义的树上调用。因此,在s: c.Expr[String]表达式上调用eval的方式将为val s2 = c.eval(c.Expr[String](c.resetAllAttrs(c.tree.duplicate))),这会给您一个String,然后您可以在代码中正常使用它,例如q"(a: Int) => a.${newTermName(s2)}"

把它放在一起,让我们想象你创建一个宏,它将从一个对象和其中的一个String字段中输出一个字符串值。它会给类似

def attr[A](a: A, field: String): String = macro attrImpl[A] 

def attrImpl[A: c.WeakTypeTag](c: Context)(a: c.Expr[A], field: c.Expr[String]) = { 
    import c.universe._ 

    val s = c.eval(c.Expr[String](c.resetAllAttrs(field.tree.duplicate))) 
    c.Expr[String](q"a.${newTermName(s)}") 

} 

REPL会话测试:

scala> object a { val field1 = "field1"; val field2 = "field2" } 
defined module a 

scala> attr(a, "field1") 
res0: String = field1 

scala> attr(a, "field2") 
res1: String = field2 

要了解编译时和运行时的区别,你可以沉思有关下列结果REPL ;-)

scala> val s = "field1"; attr(a, s) 
error: exception during macro expansion: 
scala.tools.reflect.ToolBoxError: reflective compilation has failed: 

$iw is not an enclosing class 
    at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.throwIfErrors(ToolBoxFactory.scala:311) 
    at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.compile(ToolBoxFactory.scala:244) 
    at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.compile(ToolBoxFactory.scala:408) 
    at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.eval(ToolBoxFactory.scala:411) 
    at scala.reflect.macros.runtime.Evals$class.eval(Evals.scala:16) 
    at scala.reflect.macros.runtime.Context.eval(Context.scala:6) 
    at .attrImpl(<console>:14) 


scala> val s = "field1" 
s: String = field1 

scala> attr(a, s) 
res3: String = field1 

希望它有帮助;))

+1

感谢您的回答!可能quasiquotes是要走的路,但它可用于尚未正式发布的scala 2.11。我已经把z作为一个在函数内部定义的值,以便在编译时避免运行时值。你能用这个例子的可能解决方案来扩展你的答案吗? 'c.eval(c.Expr [String](Select(Ident(newTermName(“z”)))))'不起作用:\。看看第197行的要点。 –

+1

您可以在2.10.x产品发布中使用包含quasiquotes的macro paradise插件,如http://docs.scala-lang.org/overviews/macros/paradise.html中所述 – Leo

+0

再次感谢,我认为他们只是在2.11 ... ...你知道为什么'c.Expr [Unit](q“”“println(”hello world!“)”“”)'只是工作和'c .Expr [Int => Short](q“”“(a:Int)=> a.toShort”“”)'不?抛出此错误:\('scala:无法读取事件:rO0ABXNyADlvcmcuamV0YnJhaW5zLmpwcy5pbmNyZW1lbnRhbC ...错误:未知的源文件:[email protected]159c0 java.lang.RuntimeException:未知的源文件:embeddedFile - QuasiquoteCompat .scala @ 36a12aaa83f74d5b8ce323d542d159c0' –