# 第4章 语法分析:把 Token 组装成结构
# 一、前言
语法分析把 Token 规约为结构化的 Parse Tree,并通过优先级与结合性确保表达式按预期解析。
# 二、目标
- 理解优先级与左结合的设计方式
- 能用 dump-parse-tree 观察解析结果
- 识别常见错误(运算符歧义、优先级错位)
# 三、设计
术语说明:
- 左结合:
a-b-c
解析为(a-b)-c
- 层级法:从低到高分层实现优先级
核心流程图:
架构交互图:
# 四、实现
目录树(关注项):
src
└── main
├── antlr4/com/lxg/antlr/Lxg.g4
└── java/com/lxg/frontend/AstBuilder.java
命令:
java -jar target/my-language-0.1.0-SNAPSHOT.jar examples/arithmetic.lxg --dump-parse-tree | cat
优先级层次(自低到高):
expr → equality → comparison → addition → multiplication → unary → primary
代码对照:表达式层次与一元规则(节选)
expr: equality;
equality: comparison (( '==' | '!=' ) comparison)*;
comparison: addition (( '<' | '>' | '<=' | '>=' ) addition)*;
addition: multiplication (( '+' | '-' ) multiplication)*;
multiplication: unary (( '*' | '/' ) unary)*;
unary: ( '+' | '-' | '!' ) unary | primary;
代码对照:AstBuilder 中 addition 的左结合折叠
@Override
public Object visitAddition(LxgParser.AdditionContext ctx) {
Object left = visit(ctx.multiplication(0));
for (int i = 1; i < ctx.multiplication().size(); i++) {
String opText = ctx.getChild(2 * i - 1).getText();
Object right = visit(ctx.multiplication(i));
BinaryOp op = "+".equals(opText) ? BinaryOp.ADD : BinaryOp.SUB;
left = new BinaryExpr(pos(ctx.getStart()), (Expression) left, op, (Expression) right);
}
return left;
}
代码对照:AstBuilder 中 multiplication 的左结合折叠
@Override
public Object visitMultiplication(LxgParser.MultiplicationContext ctx) {
Object left = visit(ctx.unary(0));
for (int i = 1; i < ctx.unary().size(); i++) {
String opText = ctx.getChild(2 * i - 1).getText();
Object right = visit(ctx.unary(i));
BinaryOp op = "*".equals(opText) ? BinaryOp.MUL : BinaryOp.DIV;
left = new BinaryExpr(pos(ctx.getStart()), (Expression) left, op, (Expression) right);
}
return left;
}
# 五、测试
- 运行:
--dump-parse-tree
验证树形结构与结合性
# 六、总结
- 通过层级与左结合可稳定解析表达式;调试优先级问题时,自底向上核对每一层