scala2.10中eval一段script

之前在 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方法的类型。

scala2.10中eval一段script》上有2条评论

  1. Scan

    应该可以用 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方法..

    回复

发表评论

电子邮件地址不会被公开。 必填项已用*标注