问题描述
我想创建一个模板插件,作为第一步将任意字符串转换为它的编译AST表示(像scala解释器,我猜)。因此,编译器插件可以例如将someString分配给HELLO WORLD:
I would like to create a templating plugin and as the first step convert an arbitrary string to it's "compiled" AST representation (as the scala interpreter does, I guess). So a compiler plugin could e.g assign someString to "HELLO WORLD":
@StringAnnotation("""("hello world").toString.toUpperCase""")
var someString = ""
插件简而言之:
- runafter解析器
- 创建一个新的仅表示编译器和VirtualFile注释内容
- 编译并打印unit.body
请参阅:
a)
现在,object o {val x = 0}
返回一个AST, var x = 1+ 2
不是因为它不是有效的.scala文件。我如何解决这个问题?
a)Right now, "object o{val x = 0}"
returns an AST, but e.g. "var x = 1+ 2"
doesn't because it wouldn't be a valid .scala file. How can I fix this?
b)
只有Presentation是一个不错的选择吗?我应该替代重写computeInternalPhases与适当的阶段或使用-Ystop:阶段
b)Is onlyPresentation a good choice? Should I instead overriding computeInternalPhases with the appropriate phases or use -Ystop:phase?
c)
是可能的外部编译器的环境绑定到内部一个,所以例如
c)Is it possible to bind the environment of the outer compiler to the inner one, so that e.g.
var x = _
(...)
@StringAnnotation("x += 3")
会工作吗?
我发现下面的代码[1]使用解释器和一个变量做类似的事情:
I found following code[1] using an interpreter and one variable which does something similar:
Interpreter interpreter = new Interpreter(settings);
String[] context = { "FOO" };
interpreter.bind("context", "Array[String]", context);
interpreter
.interpret("de.tutorials.scala2.Test.main(context)");
context[0] = "BAR";
interpreter
.interpret("de.tutorials.scala2.Test.main(context)");
[1]
感谢
完成代码:
class AnnotationsPI(val global: Global) extends Plugin {
import global._
val name = "a_plugins::AnnotationsPI" //a_ to run before namer
val description = "AST Trans PI"
val components = List[PluginComponent](Component)
private object Component extends PluginComponent with Transform with TypingTransformers with TreeDSL {
val global: AnnotationsPI.this.global.type = AnnotationsPI.this.global
val runsAfter = List[String]("parser");
val phaseName = AnnotationsPI.this.name
def newTransformer(unit: CompilationUnit) = {
new AnnotationsTransformer(unit)
}
val SaTpe = "StringAnnotation".toTypeName
class AnnotationsTransformer(unit: CompilationUnit) extends TypingTransformer(unit) {
/** When using <code>preTransform</code>, each node is
* visited before its children.
*/
def preTransform(tree: Tree): Tree = tree match {
case anno@ValDef(Modifiers(_, _, List(Apply(Select(New(Ident(SaTpe)), _), List(Literal(Constant(a))))), _), b, c, d) => //Apply(Select(New(Ident(SaTpe)), /*nme.CONSTRUCTOR*/_), /*List(x)*/x)
val str = a.toString
val strArr = str.getBytes("UTF-8")
import scala.tools.nsc.{ Global, Settings, SubComponent }
import scala.tools.nsc.reporters.{ ConsoleReporter, Reporter }
val settings = new Settings()
val compiler = new Global(settings, new ConsoleReporter(settings)) {
override def onlyPresentation = true
}
val run = new compiler.Run
val vfName = "Script.scala"
var vfile = new scala.tools.nsc.io.VirtualFile(vfName)
val os = vfile.output
os.write(strArr, 0, str.size) // void write(byte[] b, int off, int len)
os.close
new scala.tools.nsc.util.BatchSourceFile(vfName, str)
run.compileFiles(vfile :: Nil)
for (unit <- run.units) {
println("Unit: " + unit)
println("Body:\n" + unit.body)
}
tree
case _ =>
tree
}
override def transform(tree: Tree): Tree = {
super.transform(preTransform(tree))
}
}
}
推荐答案
你可以使用treeFrom(aString),它是scala重构项目的一部分()。没有回答关于交叉绑定的问题,虽然...
I don't know if this helps you much, but instead of fiddling with the Interpreter, you can use treeFrom( aString ) which is part of the scala-refactoring project ( http://scala-refactoring.org/ ). doesn't answer your question about cross-bindings, though...
这篇关于编译字符串到AST内CompilerPlugin?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!