之前在 scala的诊断方法(2) 在repl下用reify查看表达式的翻译结果 我们介绍过使用reify来查看表达式被翻译后的语法树。
但 reify 不支持类,方法,变量的定义和赋值语句,比如def foo(i:Int) = i+1
, val a=2
等;
不能用 reify(x@y=2)
来看 val x@y=2
这样的赋值操作到底被翻译成了什么。
不过我们可以利用ToolBox来对一段字符串做解析,并eval,看一下例子:
scala> import scala.tools.reflect.ToolBox
scala> val tb = scala.reflect.runtime.currentMirror.mkToolBox()
scala> val tree = tb.parse("1 to 3 map (_+1)")
scala> val result = tb.eval(tree)
result: Any = Vector(2, 3, 4)
上面例子中,对字符串 “1 to 3 map (_+1)” 解析成语法树,并执行这个语法树,得到该表达式的结果。
就像是执行脚本一样。
之前我们在旺旺群里有人问过如何让scala当脚本来执行,我们认为scala是静态类型系统,jsr223 scripting-engine只支持动态语言;可能第三方有通过即时编译为字节码再加载来实现,现在scala2.10中已经提供了eval的方式
有着这个方法,我们在repl下可以完全像命令行使用 scala -Xprint:typer -e 'xxx'
一样来看翻译后的结果了:
scala> val tree = tb.parse("val x@y=2")
tree: tb.u.Tree =
{
<synthetic> private[this] val x$1 = 2: @scala.unchecked match {
case (x @ (y @ _)) => scala.Tuple2(x, y)
};
val x = x$1._1;
val y = x$1._2
}
可以看到 val x@y = 2
的过程导致模式匹配。
注意一点是在脚本中所定义方法,类,等是无法在当前的repl上下文获取到的,eval只返回脚本最终的结果。
而声明变量、方法、类 所得到是unit,所以在脚本中定义的方法只能在脚本中执行,无法在当前上下文执行。
相当于只能在”沙箱环境”中运行。
scala> tb.eval(tb.parse("def foo()={println(\"hi\")}"))
res1: Any = ()
scala> foo()
<console>:9: error: not found: value foo
foo()
^
scala> tb.eval(tb.parse("def foo()={println(\"hi\")}; foo()"))
hi
res3: Any = ()
不过可能有种变通的方式,是把eval里定义的方法封装在对象里,返回这个对象。然后通过这个对象来执行方法。
scala> tb.eval(tb.parse("class A{def foo()={println(\"hi\")}}; new A"))
res4: Any = __wrapper$5$2048074efd664b699b731dc2ff1ecf66.__wrapper$5$2048074efd664b699b731dc2ff1ecf66$A$1@74f8ac62
对象的类型是个嵌套多层的类型,不知怎么造型为可执行foo方法的类型。
最后一个例子,可以把类型转成CompoundType来调用。底层调用方式是反射。
应该可以用 Structural Types 吧
val a = tb.eval(tb.parse("class A{def foo()={println(\"hi\")}}; new A")).asInstanceOf[{ def foo() }]
a.foo()
虽然我自己测试找不到方法… 包括 a.getClass.getMethods 里面也找不到foo方法..
如何传入对象引用