如何解决Grammar Kit中自定义语言BNF的嵌套表达式解析问题?
解决IntelliJ自定义语言BNF的嵌套表达式优先级冲突问题
我在为IntelliJ插件编写自定义语言的BNF规则时,遇到了嵌套表达式的优先级冲突问题。自定义语言包含二元运算表达式和数组引用表达式,最初写的BNF规则如下:
{ extends(".*_expr")=expr tokens=[ id="regexp:[a-zA-Z_][a-zA-Z0-9_]*" number="regexp:[0-9]+" ] } expr ::= binary_expr| array_ref_expr | const_expr const_expr ::= number binary_expr ::= expr '+' expr array_ref_expr ::= id '[' expr ']'
解析a[1+1]时会报错:
']' expected, got '+'
调试后发现,数组引用表达式的优先级设置导致括号内无法识别二元运算;但如果交换优先级,又无法正确解析a[1]+1这类需要先算数组引用再算加法的表达式。调整优先级或结合性都无法同时适配两种场景。
解决方案:拆分表达式层级明确优先级
核心思路是通过分层定义表达式,让数组引用的优先级高于二元运算,同时允许括号内的表达式包含所有运算类型。修改后的BNF规则如下:
{ extends(".*_expr")=expr tokens=[ id="regexp:[a-zA-Z_][a-zA-Z0-9_]*" number="regexp:[0-9]+" ] } # 最顶层表达式,包含所有运算类型 expr ::= binary_expr | array_ref_expr | primary_expr # 二元运算:优先级低于数组引用,右操作数限定为基础表达式 binary_expr ::= expr '+' primary_expr # 数组引用:基于基础表达式定义,括号内支持任意表达式 array_ref_expr ::= primary_expr '[' expr ']' # 基础表达式:最高优先级单元,包含常量、标识符、括号包裹的完整表达式 primary_expr ::= const_expr | id | '(' expr ')' const_expr ::= number
规则说明:
- primary_expr:作为最高优先级的基础单元,包含常量、标识符,以及用括号包裹的任意表达式——这保证了
a[1+1]中括号内的1+1能被完整解析为合法表达式。 - array_ref_expr:基于primary_expr定义,意味着数组引用只能作用于基础表达式(比如id或括号表达式),同时括号内可以是任意expr(包括二元运算),解决了嵌套运算的解析问题。
- binary_expr:定义为
expr '+' primary_expr,确保二元运算的优先级低于数组引用——解析a[1]+1时,会先处理a[1](属于primary_expr层级),再处理加法,符合运算顺序预期。
修改后两种场景的表达式都能被正确解析:
a[1+1]:括号内的1+1被完整识别为expr,外层数组引用正常解析;a[1]+1:先解析a[1]作为加法左操作数,再处理右侧的1,运算顺序正确。
内容的提问来源于stack exchange,提问作者G. Seinfeld
相关产品推荐
相关产品推荐

