GEP-4


元数据
编号

GEP-4

标题

AstBuilder AST 模板

版本

2

类型

功能

状态

拒绝

评论

改用 Groovy 宏中使用的方法

负责人

Hamlet D’Arcy

创建日期

2010-02-16

最后修改 

2018-10-12

摘要

AstBuilder 在 Groovy 1.7 中引入,用于简化编写 AST 变换的任务。 “fromCode” 方法允许您从代码片段生成 AST。目前,AstBuilder fromCode API 接受一个闭包作为参数,闭包中的代码将转换为 AST。AstBuilder 的一个限制是闭包参数不允许引用周围环境中的变量:无法将参数传递到代码块中。最初讨论过这个问题,但该想法被认为过于复杂,无法融入 1.7 版本。

此 AST 模板功能允许您将参数传递到 AstBuilder 中。与 GString 插值中 $ 的作用类似,需要一种方法将封闭作用域中的变量绑定到 AstBuilder 闭包中。提议的语法是使用牛津括号(见下文)。包含 GEP 4 - AstBuilder AST 模板的 AstBuilder 闭包将绑定括号内的变量。

方法

牛津括号用于将封闭作用域中的变量绑定或准引用到 AstBuilder 参数中。

示例 1:字符串连接

def constant = new ConstantExpression("World")
List<ASTNode> greeting = AstBuilder.buildFromCode { "Hello" + [ | constant | ]  }

生成 AST

BlockStatement
-> ExpressionStatement
   -> BinaryExpression +
      -> ConstantExpresssion - Hello
      -> ConstantExpression - World

示例 2:生成 println

new AstBuilder().buildFromCode { println([ | message | ]) }[0]

等效于调用此方法

private Statement createPrintlnAst(String message) {
   return new ExpressionStatement(
       new MethodCallExpression(
           new VariableExpression("this"),
           new ConstantExpression("println"),
           new ArgumentListExpression(
               new ConstantExpression(message)
           )
       )
   )
}

示例 3:方法的记忆化

假设您已将现有的方法 AST 存储在一个名为“methodNode”的变量中

def methodNode = ...
def parameters = methodNode.parameters

def newMethod = new AstBuilder().buildFromCode {
 if (cache.contains([ | parameters | ])) {
   return cache.get([ | parameters | ])
 }
 def result = [ | methodNode.code | ]
 cache.put([ | parameters | ])
 return result
}

methodNode.code = newMethod[0]

备选方案

有很多语法备选方案

  • Lisp 使用 ´ 和 ,

  • Template Haskell 使用类似于牛津括号但语义略有不同的语法

  • Boo 使用 GEP 4 - AstBuilder AST 模板和 $,但语义略有不同

考虑过使用 $,但由于它已经是具有意义的标识符,因此无法使用。Boo 元方法比较 Boo 元方法功能与 Groovy AST 变换功能重叠。Boo 没有提供带有单独定义的 AstTransformation 子类的 @AstTransformation 接口,而是提供元方法,在编译时调用并生成 AST 变换。

Groovy 提供 AstBuilder 将代码转换为 AST,而 Boo 使用牛津括号。GEP-4 提议使用牛津括号来表示 AST 变量模板,而 Boo 使用 $。考虑 Groovy 中简单的 println AST

new AstBuilder().buildFromCode {
    println([ | message | ])
}[0]

与 Boo 中类似的结构相比

[Meta]
static def methodname(expr as Expression):
    [| println($expr) |]

GEP-4 提议的语法与 Boo 相反:使用牛津括号表示 AST 模板,而不是 $。$ 已经是 Java 中的字符,可以作为变量名称的一部分。

使用相反的语法没有真正的问题。从 Boo AST 变换编写者迁移到 Groovy 的人数很可能非常少。

邮件列表讨论

更新历史

1 (2010-02-16)

从 Codehaus wiki 中提取的版本

2 (2018-10-14)

大量细微调整