单条expression规则为何触发互左递归?Antlr语法报错排查
问题描述
我编写了一段未完成的Antlr解析器语法:
parser grammar DemoParser; options { tokenVocab = DemoLexer; } compilation_unit : import_directive* export_directive? file_body EOF ; file_body : top_level_statements | full_program ; top_level_statements : expression+ ; full_program : type_definition+ ; import_directive : Exclamation_Mark? Import full_identifier (Comma full_identifier)* #basic_import | Exclamation_Mark? Import Type full_identifier (Comma full_identifier)* #type_import | Exclamation_Mark? Import Identifier Equals full_identifier (Comma Identifier Equals full_identifier)* #alias ; export_directive : Export full_identifier ; full_identifier : Identifier (Dot Identifier)* ; code_block : Open_Brace expression* Close_Brace ; expression : expression Double_Asterisk expression #power_expression | Minus expression #unary_negation_expression | Plus expression #unary_plus_expression | Exclamation_Mark expression #logical_negation_expression | expression Asterisk expression #multiply_expression | expression Slash expression #divide_expression | expression Percent expression #remainder_expression | expression Plus expression #addition_expression | expression Minus expression #subtraction_expression | expression Double_Less_Than expression #left_shift_expression | expression Double_Greater_Than expression #right_shift_expression | Tilde expression #bitwise_complement_expression | expression op=(Less_Than | Less_Equals | Greater_Than | Greater_Equals) expression #comparison_expression | expression op=(Double_Equals | Exclamation_Equals) expression #equality_expression | expression Ampersand expression #addition_expression | expression Double_Ampersand expression #logical_and_expression | expression Bar expression #or_expression | expression Double_Bar expression #logical_or_expression | expression Caret expression #xor_expression | Caret #typeof_expression | Percent_Caret expression #nameof_expression | expression Double_Dot_Question_Mark expression #implementation_query_exception | expression assignment_operator expression #assignment | expression (Dot Identifier)+ #dotted_expression | range #range_expression | attribute expression #attributed_expression | expression (Dot Identifier)+ arglist #member_access_expression | if_branch elif_branch* else_branch? #prefix_if_expression | (code_block | expression) postfix_if_branch #postfix_if_expression | unless_branch else_unless_branch* else_branch? #prefix_unless_expression | (code_block | expression) postfix_unless_branch #postfix_unless_expression | atom #atom_expression ; atom : expression_atom | integer_atom | real_atom | boolean_atom | string_atom | character_atom | empty_atom | wildcard_atom | identifier_atom ; expression_atom : Open_Paren expression Close_Paren ; integer_atom : Integer_Literal ; real_atom : Real_Literal ; boolean_atom : True | False ; string_atom : String_Literal | Verbatim_String_Literal ; character_atom : Character_Literal ; empty_atom : Open_Paren Close_Paren; wildcard_atom : Underscore ; identifier_atom : Identifier | full_identifier ; assignment_operator : Plus_Equals | Minus_Equals | Asterisk_Equals | Slash_Equals | Percent_Equals | Dot_Equals | Bar_Equals | Double_Bar_Equals | Ampersand_Equals | Double_Ampersand_Equals | Double_Less_Than_Equals | Double_Greater_Than_Equals | Tilde_Equals ; if_branch : Question_Mark expression Equals (code_block | expression) ; postfix_if_branch : Question_Mark expression ; elif_branch : Colon expression Equals (code_block | expression) ; else_branch : Colon Equals (code_block | expression) ; unless_branch : Exclamation_Mark Question_Mark expression Equals (code_block | expression) ; else_unless_branch : Exclamation_Mark Colon expression Equals (code_block | expression) ; postfix_unless_branch : Exclamation_Mark Question_Mark expression ; range : Integer_Literal Double_Dot Caret? Integer_Literal ; arglist : (((Identifier Colon)? expression) (Comma ((Identifier Colon)? expression))*)? Double_Comma? ; attribute : Less_Than full_identifier arglist Greater_Than ; type_definition : Type ;
编译时收到错误:
以下规则集存在相互左递归:[expression]
我此前仅见过两条规则触发该错误,请问此问题的原因是什么?
原因解析
这个错误提示里的“相互左递归”并不局限于两条或多条规则之间的交叉递归调用,单条规则自身的直接左递归也会被Antlr归为“相互左递归集合”(集合里只有这条规则自己)。
你的expression规则存在多处直接左递归:
- 第一个备选分支
expression Double_Asterisk expression就是典型的直接左递归——规则开头直接调用了自身; - 后续的
expression Asterisk expression、expression Slash expression等运算符优先级分支,都是以expression开头的直接左递归; - 还有
(code_block | expression) postfix_if_branch这类分支,其中的expression也会触发递归调用自身的情况。
Antlr 4本身支持直接左递归(用于处理运算符优先级等场景),但如果语法中存在递归路径无法终止的情况,或者Antlr无法正确解析递归结构时,就会抛出这个错误。你可以检查expression规则的分支,确保所有递归路径最终都能走到atom这类终止节点,同时调整分支顺序(比如把非递归的分支放在后面,递归分支按优先级排序)来解决问题。
内容的提问来源于stack exchange,提问作者Jonas _
相关产品推荐
相关产品推荐

