You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

单条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 _

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 04:37:16