ANTLR4语法:得到不匹配的输入错误



我定义了以下语法:

grammar Test;
parse: expr EOF;
expr :  IF comparator FROM field THEN                                                                   #comparatorExpr
;
dateTime        :   DATE_TIME;
number          :   (INT|DECIMAL);
field           :   FIELD_IDENTIFIER;
op              :   (GT | GE | LT | LE | EQ);
comparator      :   op (number|dateTime);
fragment LETTER : [a-zA-Z];
fragment DIGIT  : [0-9];
IF                   : '$IF';
FROM                 : '$FROM';
THEN                 : '$THEN';
OR                   : '$OR';
GT                   : '>' ;
GE                   : '>=' ;
LT                   : '<' ;
LE                   : '<=' ;
EQ                   : '=' ;
INT                  : DIGIT+;
DECIMAL              : INT'.'INT;
DATE_TIME            : (INT|DECIMAL)('M'|'y'|'d');
FIELD_IDENTIFIER     : (LETTER|DIGIT)(LETTER|DIGIT|' ')*;
WS                   : [ rtu000Cn]+ -> skip;

我尝试解析以下输入:

$IF >=15 $FROM AgeInYears $THEN

它给了我以下错误:

line 1:6 mismatched input '15 ' expecting {INT, DECIMAL, DATE_TIME}

我发现的所有SO帖子都指出了这个错误的相同原因-相同的LEXER规则。但是我不明白为什么15可以匹配DECIMAL-它需要.在2 int之间,或者DATE_TIME-它也有m|d|y后缀。

如有任何提示,将不胜感激。

运行查看Lexer生成的令牌流总是一个好主意:

grun Test parse -tokens -tree Test.txt
[@0,0:2='$IF',<'$IF'>,1:0]
[@1,4:5='>=',<'>='>,1:4]
[@2,6:8='15 ',<FIELD_IDENTIFIER>,1:6]
[@3,9:13='$FROM',<'$FROM'>,1:9]
[@4,15:25='AgeInYears ',<FIELD_IDENTIFIER>,1:15]
[@5,26:30='$THEN',<'$THEN'>,1:26]
[@6,31:30='<EOF>',<EOF>,1:31]
line 1:6 mismatched input '15 ' expecting {INT, DECIMAL, DATE_TIME}
(parse (expr $IF (comparator (op >=) 15 ) $FROM (field AgeInYears ) $THEN) <EOF>)

这里我们看到"15 "(15space)已经被FIELD_IDENTIFIER规则匹配。因为它有三个输入字符长,所以ANTLR更喜欢Lexer规则,而不是只匹配2个字符的INT规则。

对于这个特定的输入,您可以通过将FIELD_IDENTIFIER规则修改为

来解决这个问题:
FIELD_IDENTIFIER: (LETTER | DIGIT)+ (' '+ (LETTER | DIGIT))*;
grun Test parse -tokens -tree Test.txt
[@0,0:2='$IF',<'$IF'>,1:0]
[@1,4:5='>=',<'>='>,1:4]
[@2,6:7='15',<INT>,1:6]
[@3,9:13='$FROM',<'$FROM'>,1:9]
[@4,15:24='AgeInYears',<FIELD_IDENTIFIER>,1:15]
[@5,26:30='$THEN',<'$THEN'>,1:26]
[@6,31:30='<EOF>',<EOF>,1:31]
(parse (expr $IF (comparator (op >=) (number 15)) $FROM (field AgeInYears) $THEN) <EOF>)

也就是说,我怀疑尝试在FIELD_IDENTIFIER中允许空格(没有某种开始/停止标记)可能会成为您在此工作时的持续痛苦来源。(在大多数语言中没有看到这一点是有原因的,并不是没有人认为允许多词标识符会很方便。它需要一个贪婪的词法分析器规则,该规则可能优先于其他规则(就像这里所做的那样)。

最新更新