Java中用解释器模式构建DSL的AST,需将文法规则映射为类,划分终结符(如NumberExpression)与非终结符(如AddExpression),手动组合对象形成树,Context传递只读共享数据,各interpret()方法职责单一、可独立测试。

在 Java 中用解释器模式为 DSL 构建抽象语法树(AST),核心是把 DSL 的每条文法规则映射为一个 Java 类,再通过组合这些类的实例,形成一棵可递归遍历的树。关键不在于写多少代码,而在于清晰划分终结符与非终结符、统一解释入口、让树结构自然反映语义逻辑。
明确 DSL 文法并划分表达式角色
先用 BNF 或类似方式定义 DSL 的最小语法单元。例如,一个简单算术 DSL 可定义为:
- expression ::= term | expression "+" term | expression "-" term
- term ::= number | "(" expression ")"
- number ::= 正整数(如 123)
据此划分角色:
-
AbstractExpression:声明
interpret(Context ctx)方法,所有节点都实现它 -
TerminalExpression:如
NumberExpression,直接返回字面值,不递归 -
NonterminalExpression:如
AddExpression、SubtractExpression、GroupExpression,内部持有一个或多个子表达式引用,解释时委托子节点并组合结果
用组合方式构建 AST 对象树
AST 不是解析器自动生成的,而是由客户端(比如词法+语法分析器)手动 new 出来的对象树。例如对字符串 "3 + (4 - 1)":
- 先识别出数字 3 → 创建
NumberExpression(3) - 识别 '+' → 创建
AddExpression(left, right),其中 left 是上面的 3 节点 - 识别 '(' → 触发进入子表达式,解析出
4 - 1得到SubtractExpression(NumberExpression(4), NumberExpression(1)) - 将该减法节点作为 right 填入 AddExpression
最终得到的对象结构就是一棵真正的 AST:根是 AddExpression,左子是 NumberExpression(3),右子是 SubtractExpression,其下再挂两个 NumberExpression。这棵树完全由 Java 对象构成,无字符串拼接、无反射、不依赖外部库。
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
Context 传递共享上下文信息
Context 不是必须的全局变量,而是解释过程中可能需要的只读数据容器。例如 DSL 支持变量,就可在 Context 中用 Map 存变量值;若需记录解析位置或错误信息,也可放进去。每个 interpret() 方法都接收它,但只读不修改——保证解释过程无副作用,便于测试和复用。
保持解释逻辑单一且可测
每个表达式类的 interpret() 方法只做一件事:对自身语义求值。例如:
-
NumberExpression.interpret()直接返回封装的 int 值 -
AddExpression.interpret()调用 left.interpret() + right.interpret() -
GroupExpression.interpret()单纯返回 inner.interpret()
这样每个类职责清晰,单元测试只需构造单个对象加 Context,断言返回值即可,无需启动整个 DSL 引擎。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










