在 CompilerPlugin 中将字符串编译为 AST?

发布于 2024-10-14 14:50:55 字数 3728 浏览 1 评论 0原文

我想创建一个模板插件,并作为第一步将任意字符串转换为其“编译”的 AST 表示形式(我猜,就像 scala 解释器所做的那样)。因此,编译器插件可以将 someString 分配给“HELLO WORLD”:

  @StringAnnotation("""("hello world").toString.toUpperCase""")
  var someString = ""

我当前的第一个镜头插件简而言之:

  • runafter 解析器
  • 创建一个新的仅表示编译器和一个带有注释内容的 VirtualFile
  • 编译和打印 unit.body

请参阅: http://paste.pocoo.org/show/326025/

a) 现在,"object o{val x = 0}" 返回 AST,但例如 "var x = 1+ 2" 不会,因为它不会有效的 .scala 文件。我该如何解决这个问题?

b) onlyPresentation 是一个好的选择吗?我应该用适当的阶段覆盖computeInternalPhases还是使用-Ystop:phase?

c) 是否可以将外部编译器的环境绑定到内部编译器,以便eg

  var x = _
  (...)
  @StringAnnotation("x += 3")

可以工作?

我发现以下代码[1]使用解释器和一个执行类似操作的变量:

  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] http://www.tutorials.de/java/320639-beispiel-zur-einbindung-des-scala-interpreters-kompilierte-scala-anwendungen。 html#post1653884

感谢

完整代码:

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))
      }
    }
  }

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 = ""

My current first shot plugin does in short:

  • runafter parser
  • create a new representation only compiler and a VirtualFile with the annotation content
  • compile and print unit.body

see: http://paste.pocoo.org/show/326025/

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)
Is onlyPresentation a good choice? Should I instead overriding computeInternalPhases with the appropriate phases or use -Ystop:phase?

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")

would work?

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] http://www.tutorials.de/java/320639-beispiel-zur-einbindung-des-scala-interpreters-kompilierte-scala-anwendungen.html#post1653884

thanks

Complete Code:

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))
      }
    }
  }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

温馨耳语 2024-10-21 14:50:55

我不知道这是否对您有很大帮助,但是您可以使用 treeFrom( aString ),它是 scala 重构项目的一部分 ( http://scala-refactoring.org/ )。不过,并没有回答你关于交叉绑定的问题......

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...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文