如何提升Antlr4解析器Call分支优先级?解决函数调用解析异常
问题与解答
问题描述
自定义ANTLR4语法规则
grammar Test; COMMA: ','; OPEN: '('; CLOSE: ')'; ATOM: [0-9a-zA-Z_]+; SPACE: [ \r\n\t]+ -> skip; start: expr EOF; expr : ATOM # Identifier | expr OPEN (expr (COMMA expr)*)? CLOSE # Call | OPEN expr CLOSE # Wrap | expr COMMA expr # Then ;
测试表达式
FUNC(1, (2, 3), 4)
实际解析结果
start: expr: expr "FUNC" "(" expr: expr: expr "1" "," expr: "(" expr: expr "2" "," expr "3" ")" "," expr "4" ")" <EOF>
预期解析结果
start: expr: expr "FUNC" "(" expr "1" "," expr: "(" expr: expr "2" "," expr "3" ")" "," expr "4" ")" <EOF>
疑问
- 如何让解析器优先选择
Call分支而非Then分支? - Antlr4中的
*是否类似正则表达式中的*??
解答
1. 优先匹配Call分支的解决方案
当前语法存在歧义:Then分支(expr COMMA expr)会先把1, (2, 3)合并成一个表达式,再和4通过Then组合,导致Call的参数被错误嵌套。
解决办法是拆分语法规则,明确区分“可作为调用主体/参数的表达式”和“逗号序列”:
grammar Test; COMMA: ','; OPEN: '('; CLOSE: ')'; ATOM: [0-9a-zA-Z_]+; SPACE: [ \r\n\t]+ -> skip; start: expr EOF; // 专门处理逗号分隔的序列(原Then分支) expr : callable (COMMA callable)* # Then ; // 处理原子、函数调用、括号包裹的表达式(原Identifier、Call、Wrap分支) callable : ATOM # Identifier | callable OPEN (expr (COMMA expr)*)? CLOSE # Call | OPEN expr CLOSE # Wrap ;
调整后,callable负责处理能独立作为调用目标或参数的表达式,expr仅处理逗号连接的序列。解析FUNC(...)时,括号内的1, (2, 3), 4会被expr解析为三个独立的callable,正好符合Call的参数列表预期,得到你想要的结构。
2. Antlr4中*的行为说明
Antlr4里的*是贪婪匹配,和正则表达式中的普通*行为一致,而非非贪婪的*?。它会尽可能多地匹配符合规则的元素,直到后续规则无法匹配时才会回溯。
比如规则(expr (COMMA expr)*)会尽可能匹配所有COMMA expr的组合,不会提前终止匹配,只有当后续需要匹配)等符号时才会停止。
内容的提问来源于stack exchange,提问作者AFatNiBBa
相关产品推荐
相关产品推荐

