Antlr4——处理递归和优先级
in 默认分类 with 0 comment

好的,我们来详细展开讲解 第五步:处理递归和优先级。这是语法设计中最关键也最容易出错的部分,直接决定了语法能否正确解析复杂的表达式结构。

1. 优先级问题的本质

为什么需要优先级?

考虑表达式:1 + 2 * 3

优先级问题就是确保运算符按照正确的顺序进行结合。


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 分层设计的优势

  1. 清晰性:每个规则只负责一个优先级级别
  2. 可维护性:容易添加新的优先级级别
  3. 可读性:层次结构明确
  4. 性能:解析效率更高

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. 最佳实践总结

优先级设计清单

  1. 低优先级在前,高优先级在后
  2. 使用分层设计,每个规则一个优先级级别
  3. 直接左递归用于左结合运算符
  4. 右递归用于右结合运算符
  5. 原子表达式放在最底层
  6. 括号表达式具有最高优先级(在原子层处理)

记忆口诀

"先低后高,左递常用,右递赋值,括号最牛"

掌握了优先级处理,你就解决了语法设计中最关键的问题。下一步我们将学习更高级的语法特性。