Antlr4解析不一致

Antlr4 parsing inconsistency

在我刚刚写的一个小测试解析器中,我遇到了一个奇怪的问题,我不太明白。

将其分解为显示问题的最小示例,让我们从以下语法开始:

测试.g4:

grammar Testing;

cscript                           // This is the construct I shortened
    : (statement_list)* ;

statement_list
    : statement ';' statement_list?
    | block
    ;

statement
    : assignment_statement
    ;

block : '{' statement_list? '}' ;

expression
    : left=expression op=('*'|'/') right=expression              # arithmeticExpression
    | left=expression op=('+'|'-') right=expression              # arithmeticExpression
    | left=expression op=Comparison_operator right=expression    # comparisonExpression
    | ID                                                         # variableValueExpression
    | constant                                                   # ignore  // will be executed with the rule name
    ;

assignment_statement
    : ID op=Assignment_operator expression
    ;

constant
    : INT
    | REAL;

Assignment_operator : ('=' | '+=' | '-=') ;

Comparison_operator : ('<' | '>' | '==' | '!=') ;

Comment : '//' .*? '\n' -> skip;

fragment NUM : [0-9];

INT : NUM+;
REAL
    : NUM* '.' NUM+
    | '.' NUM+
    | INT
    ; 

ID : [a-zA-Z_] [a-zA-Z_0-9]*;

WS : [ \t\r\n]+ -> skip;

使用输入

z = x + y;

一切都很好,我们得到了一个从 cscript 到 statement_list、语句、assignment_statement、id 和表达式的解析树。太棒了!

现在,如果我添加声明变量的可能性,一切都会付诸东流:

这是对语法的更改:

cscript
    : (statement_list | variable_declaration ';')* ;

variable_declaration
    : type ID ('=' expression)?
    ;

type
    : 'int'
    | 'real'
    ;

statement_list
    : statement ';' statement_list?
    | block
    ;

statement
    : assignment_statement
    ;

// (continue as before)

突然间,相同的测试输入被错误地分解为两个 statement_lists,每个继续到带有 "missing ';'" 警告的语句,第一个进入不完整的 assignment_statement 的 "z =" 和第二个不完整的 assignment_statement "x +".

我尝试以文本形式显示解析树:

cscript
    statement_list
        statement
            assignment_statement
                'z'
                '=' [marked as error]
        [warning: missing ';']
    statement_list
        statement
            assignment_statement
                'x'
                '+' [marked as error]
        'y' [marked as error]
        ';'

谁能告诉我问题出在哪里? (以及如何修复它?;-))


编辑于 2016-12-26,在 Mike 的评论后:

用显式声明替换所有隐式词法分析器规则后,突然之间,输入 "z = x + y" 起作用了。 (点赞)

我接下来要做的是恢复更多我想到的原始示例,并添加一个新的输入行

int x = 22;

到输入(以前有效,但没有进入最小示例)。现在,that 行失败了。这是测试装置的 -token 输出:

[@0,0:2='int',<4>,1:0]
[@1,4:4='x',<22>,1:4]
[@2,6:6='=',<1>,1:6]
[@3,8:9='22',<20>,1:8]
[@4,10:10=';',<12>,1:10]
[@5,13:13='z',<22>,2:0]
[@6,15:15='=',<1>,2:2]
[@7,17:17='x',<22>,2:4]
[@8,19:19='+',<18>,2:6]
[@9,21:21='y',<22>,2:8]
[@10,22:22=';',<12>,2:9]
[@11,25:24='<EOF>',<-1>,3:0]
line 1:6 mismatched input '=' expecting '='

由于问题似乎出在 variable_declaration 部分,我什至尝试将其分成两个解析规则,如下所示:

cscript
    : (statement_list | variable_declaration_and_assignment SEMICOLON | variable_declaration SEMICOLON)* ;

variable_declaration_and_assignment
    : type ID EQUAL expression
    ;

variable_declaration
    : type ID
    ;

结果:

line 1:6 no viable alternative at input 'intx='

仍然卡住了:-( 顺便说一句:将 "int x = 22;" 拆分为 "int x;" 和 "x = 22;" 是可行的。 感叹


编辑于 2016-12-26,在 Mike 的下一条评论之后:

仔细检查,一切都是词法分析器规则。尽管如此,'=' 和 '=' 之间的不匹配(遗憾的是我无法再重建)让我产生了检查令牌类型的想法。当前状态是:

(简化语法)

cscript
    : (statement_list | variable_declaration)* ;

...

variable_declaration
    : type ID (EQUAL expression)? SEMICOLON
    ;

...

Assignment_operator : (EQUAL | PLUS_EQ | MINUS_EQ) ;

// among others
PLUS_EQ : '+=';
MINUS_EQ : '-=';
EQUAL: '=';

...

缩短输出:

[@0,0:2='int',<4>,1:0]
[@1,4:4='x',<22>,1:4]
[@2,6:6='=',<1>,1:6]
...
line 1:6 mismatched input '=' expecting ';'

这里,如果我理解正确,“=”被解析为标记类型 1,根据 lexer.tokens 输出,它是 Assignment_Operator,而预期的 EQUAL 将是 13 .

这可能是问题所在吗?

好的,看来这里的主要收获是:考虑您的定义以及定义它们的方式。为文字创建明确的词法分析器规则,而不是在解析器规则中隐式定义它们。如果解析器给你奇怪的错误,请检查你从词法分析器获得的标记值,因为它们首先必须是正确的,否则你的解析器就没有机会完成它的工作。