= GEP-4: AstBuilder AST 模板 :icons: font .Metadata **** [horizontal,options="compact"] *编号*:: 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") Listgreeting = 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:方法记忆化 假设您有一个名为 "methodNode" 的变量中包含现有方法 AST: ``` 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 的开发者数量很可能非常少。== 参考资料和有用链接 邮件列表讨论 * groovy-dev: https://marc.info/?l=groovy-dev&m=126632536126812&w=2[Groovy AstBuilder AST 模板] == 更新历史 1 (2010-02-16):: 从 Codehaus wiki 提取的版本 2 (2018-10-14):: 进行了大量微调