好的,我们来详细展开讲解 第五步:处理递归和优先级。这是语法设计中最关键也最容易出错的部分,直接决定了语法能否正确解析复杂的表达式结构。
1. 优先级问题的本质
为什么需要优先级?
考虑表达式:1 + 2 * 3
- 数学正确:
1 + (2 * 3) = 7(乘法优先) - 错误解析:
(1 + 2) * 3 = 9(如果优先级处理不当)
优先级问题就是确保运算符按照正确的顺序进行结合。
2. ANTLR4 的解决方案:直接左递归
ANTLR4 最大的改进就是支持直接左递归,这让优先级定义变得非常自然。
2.1 什么是直接左递归?
// 直接左递归:规则直接在自己的第一个备选分支中引用自己
expression
: expression '+' term // 左递归:expression 在左边
| term
;2.2 左递归 vs 右递归
| 类型 | 示例 | 结合性 | ANTLR4支持 |
|---|---|---|---|
| 直接左递归 | expr : expr '+' term | 左结合 | ✅ 支持 |
| 间接左递归 | expr : term; term : expr '*' factor | 左结合 | ❌ 不支持 |
| 右递归 | expr : term '+' expr | 右结合 | ✅ 支持 |
3. 优先级设计模式
3.1 基本模式:从低优先级到高优先级
核心原则:在语法规则中,低优先级运算符在前,高优先级运算符在后。
expression
: expression '||' expression # LogicalOr // 最低优先级
| expression '&&' expression # LogicalAnd // 稍高
| expression '==' expression # Equal // 更高
| expression '>' expression # Greater // 再高
| expression '+' expression # Add // 更高
| expression '*' expression # Multiply // 更高
| INT # Int // 最高优先级(原子项)
;❌ 错误示例(优先级反了):
expression
: expression '*' expression # Multiply // 错误:高优先级在前
| expression '+' expression # Add // 低优先级在后
| INT # Int
;这样会错误地解析 1 + 2 * 3 为 (1 + 2) * 3
3.2 标准算术优先级层次
expression
: expression ('||') expression # LogicalOr // 级别1:逻辑或
| expression ('&&') expression # LogicalAnd // 级别2:逻辑与
| expression ('==' | '!=') expression # Equality // 级别3:相等比较
| expression ('<' | '>' | '<=' | '>=') expression # Relational // 级别4:关系比较
| expression ('+' | '-') expression # Additive // 级别5:加减
| expression ('*' | '/' | '%') expression # Multiplicative // 级别6:乘除模
| ('!' | '-') expression # Unary // 级别7:一元运算符
| primary # Primary // 级别8:基本表达式
;4. 分层设计模式(推荐)
为了避免单一规则过于复杂,推荐使用分层设计:
4.1 清晰的层次结构
expression
: logicalOrExpression
;
logicalOrExpression
: logicalAndExpression ('||' logicalAndExpression)*
;
logicalAndExpression
: equalityExpression ('&&' equalityExpression)*
;
equalityExpression
: relationalExpression (('==' | '!=') relationalExpression)*
;
relationalExpression
: additiveExpression (('<' | '>' | '<=' | '>=') additiveExpression)*
;
additiveExpression
: multiplicativeExpression (('+' | '-') multiplicativeExpression)*
;
multiplicativeExpression
: unaryExpression (('*' | '/' | '%') unaryExpression)*
;
unaryExpression
: ('!' | '-') unaryExpression
| primaryExpression
;
primaryExpression
: literal
| IDENTIFIER
| '(' expression ')'
;4.2 分层设计的优势
- 清晰性:每个规则只负责一个优先级级别
- 可维护性:容易添加新的优先级级别
- 可读性:层次结构明确
- 性能:解析效率更高
5. 结合性处理
5.1 左结合(Left-Associative)
大多数运算符是左结合的:a + b + c = (a + b) + c
ANTLR4 自动处理:直接左递归天然就是左结合的
additiveExpression
: additiveExpression '+' multiplicativeExpression // 左结合
| multiplicativeExpression
;5.2 右结合(Right-Associative)
少数运算符是右结合的:a = b = c = a = (b = c)
使用右递归:
assignmentExpression
: IDENTIFIER '=' assignmentExpression // 右结合
| logicalOrExpression
;5.3 结合性示例对比
// 左结合运算符(加减乘除)
additiveExpression
: additiveExpression '+' multiplicativeExpression
| multiplicativeExpression
;
// 解析 a + b + c 为 ((a + b) + c)
// 右结合运算符(赋值、幂运算)
assignmentExpression
: IDENTIFIER '=' assignmentExpression
| conditionalExpression
;
// 解析 a = b = c 为 (a = (b = c))6. 处理括号和原子表达式
6.1 括号表达式
括号用于显式指定优先级,应该在最底层处理:
primaryExpression
: literal # Literal
| IDENTIFIER # Variable
| '(' expression ')' # Parenthesized
| functionCall # FunctionCall
;6.2 原子表达式(Atomic Expressions)
原子表达式是不可再分的最小单元:
primaryExpression
: INT_LITERAL # IntLiteral
| FLOAT_LITERAL # FloatLiteral
| STRING_LITERAL # StringLiteral
| BOOLEAN_LITERAL # BooleanLiteral
| NULL_LITERAL # NullLiteral
| IDENTIFIER # Identifier
| '(' expression ')' # ParenthesizedExpr
| arrayLiteral # ArrayLiteral
| objectLiteral # ObjectLiteral
;7. 一元运算符的特殊处理
7.1 一元运算符的优先级
一元运算符有最高优先级(除了原子表达式):
unaryExpression
: ('!' | '-' | '+') unaryExpression # UnaryOp
| postfixExpression # PostfixExpr
;
postfixExpression
: primaryExpression
| postfixExpression '[' expression ']' # ArrayAccess
| postfixExpression '.' IDENTIFIER # MemberAccess
| postfixExpression '(' argumentList? ')' # FunctionCall
;7.2 前缀 vs 后缀运算符
// 前缀运算符:!x, -5, ++i
prefixExpression
: ('!' | '-' | '++' | '--') prefixExpression
| primaryExpression
;
// 后缀运算符:i++, arr[0], obj.property
postfixExpression
: primaryExpression
| postfixExpression '[' expression ']'
| postfixExpression '.' IDENTIFIER
| postfixExpression '(' argumentList? ')'
| postfixExpression '++'
| postfixExpression '--'
;8. 完整示例:C 语言风格的表达式优先级
expression
: assignmentExpression
;
assignmentExpression
: IDENTIFIER '=' assignmentExpression # Assignment
| conditionalExpression # CondExpr
;
conditionalExpression
: logicalOrExpression ('?' expression ':' conditionalExpression)?
;
logicalOrExpression
: logicalAndExpression ('||' logicalAndExpression)*
;
logicalAndExpression
: bitwiseOrExpression ('&&' bitwiseOrExpression)*
;
bitwiseOrExpression
: bitwiseXorExpression ('|' bitwiseXorExpression)*
;
bitwiseXorExpression
: bitwiseAndExpression ('^' bitwiseAndExpression)*
;
bitwiseAndExpression
: equalityExpression ('&' equalityExpression)*
;
equalityExpression
: relationalExpression (('==' | '!=') relationalExpression)*
;
relationalExpression
: shiftExpression (('<' | '>' | '<=' | '>=') shiftExpression)*
;
shiftExpression
: additiveExpression (('<<' | '>>') additiveExpression)*
;
additiveExpression
: multiplicativeExpression (('+' | '-') multiplicativeExpression)*
;
multiplicativeExpression
: castExpression (('*' | '/' | '%') castExpression)*
;
castExpression
: '(' typeName ')' castExpression # Cast
| unaryExpression # UnaryExpr
;
unaryExpression
: ('++' | '--' | '&' | '*' | '+' | '-' | '!' | '~') unaryExpression
| postfixExpression
;
postfixExpression
: primaryExpression
| postfixExpression '[' expression ']'
| postfixExpression '.' IDENTIFIER
| postfixExpression '->' IDENTIFIER
| postfixExpression '(' argumentList? ')'
| postfixExpression '++'
| postfixExpression '--'
;
primaryExpression
: IDENTIFIER
| constant
| '(' expression ')'
;
constant
: INT_LITERAL
| FLOAT_LITERAL
| CHAR_LITERAL
| STRING_LITERAL
;9. 常见错误和调试技巧
9.1 优先级错误示例
// ❌ 错误:优先级顺序反了
expression
: expression '*' expression // 乘法(高优先级)
| expression '+' expression // 加法(低优先级)应该在前面!
| INT
;
// ✅ 正确:低优先级在前
expression
: expression '+' expression // 加法(低优先级)
| expression '*' expression // 乘法(高优先级)
| INT
;9.2 左递归错误
// ❌ 错误:间接左递归(ANTLR4不支持)
expression : term ;
term : factor | term '*' factor ; // 正确:直接左递归
factor : expression | INT ; // 错误:间接左递归到expression
// ✅ 正确:全部使用直接左递归
expression
: expression '+' term
| term
;
term
: term '*' factor
| factor
;
factor
: INT
| '(' expression ')'
;9.3 调试技巧
expression
: expression '+' term {System.out.println("Add: " + $expression.text + " + " + $term.text);}
| term {System.out.println("Term: " + $term.text);}
;
term
: term '*' factor {System.out.println("Multiply: " + $term.text + " * " + $factor.text);}
| factor {System.out.println("Factor: " + $factor.text);}
;10. 测试优先级是否正确
10.1 测试用例设计
// 测试表达式和期望的解析结构
1 + 2 * 3 // 应该解析为 1 + (2 * 3)
a = b = c + d // 应该解析为 a = (b = (c + d))
!x && y || z // 应该解析为 ((!x && y) || z)
a + b > c * d // 应该解析为 (a + b) > (c * d)10.2 使用 ANTLR 工具测试
# 生成解析树查看优先级是否正确
antlr4 YourGrammar.g4
javac YourGrammar*.java
grun YourGrammar expression -tree <<< "1 + 2 * 3"
grun YourGrammar expression -gui <<< "1 + 2 * 3" # 图形化查看11. 最佳实践总结
优先级设计清单
- ✅ 低优先级在前,高优先级在后
- ✅ 使用分层设计,每个规则一个优先级级别
- ✅ 直接左递归用于左结合运算符
- ✅ 右递归用于右结合运算符
- ✅ 原子表达式放在最底层
- ✅ 括号表达式具有最高优先级(在原子层处理)
记忆口诀
"先低后高,左递常用,右递赋值,括号最牛"
掌握了优先级处理,你就解决了语法设计中最关键的问题。下一步我们将学习更高级的语法特性。
本文由 jxxxy 创作,采用 知识共享署名4.0 国际许可协议进行许可。
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。