我定义了以下语法:
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 "(1
5
space
)已经被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
中允许空格(没有某种开始/停止标记)可能会成为您在此工作时的持续痛苦来源。(在大多数语言中没有看到这一点是有原因的,并不是没有人认为允许多词标识符会很方便。它需要一个贪婪的词法分析器规则,该规则可能优先于其他规则(就像这里所做的那样)。