使用Antlr4分析日期时出错
我尝试使用以下语法分析日期:使用Antlr4分析日期时出错,antlr4,Antlr4,我尝试使用以下语法分析日期: grammar Dates; formattedDate : (DATE '/' MONTH '/' year); year : SHORT_YEAR | FULL_YEAR; SHORT_YEAR : DIGIT DIGIT; FULL_YEAR : ('19' | '20' | '21') DIGIT DIGIT; DATE : (('0'..'2')? DIGIT) | '30' | '31'; MONTH : ('0'? DIGIT) | '11' |
grammar Dates;
formattedDate : (DATE '/' MONTH '/' year);
year : SHORT_YEAR | FULL_YEAR;
SHORT_YEAR : DIGIT DIGIT;
FULL_YEAR : ('19' | '20' | '21') DIGIT DIGIT;
DATE : (('0'..'2')? DIGIT) | '30' | '31';
MONTH : ('0'? DIGIT) | '11' | '12';
fragment DIGIT : ('0' .. '9');
但它无法解析我希望能够工作的值。例如,2017年4月11日输入的11
产生错误:
line 1:0 mismatched input '11' expecting DATE
我的第一个猜测是,有些值(1-12)是lexer无法确定是日期
还是月份
,这是导致问题的原因。但当我试图用解析器规则代替它们来修复它时,我遇到了同样的问题:
formattedDate : (dateNum '/' monthNum '/' year);
year : shortYear | fullYear;
shortYear : DIGIT DIGIT;
fullYear : ('19' | '20' | '21') DIGIT DIGIT;
dateNum : (('0'..'2')? DIGIT) | '30' | '31';
monthNum : ('0'? DIGIT) | '11' | '12';
fragment DIGIT : ('0' .. '9');
而且它似乎仍然在第一个值上挣扎,即使它有点像31
,在模糊的范围之外
我做错了什么?正如你所说,“代币重叠”(注31
不明确,可能是短的一年)。在这种情况下,将选择最长的匹配lexer规则。如果有两个或多个长度相同的匹配项,它将选择第一个(按照它们出现的顺序)。(我想我不久前在www.antlr.org上读过这篇文章)
因此,只要改变规则的顺序就可以“解决”问题,或者将其向前推进(注意日期在短年和月之前):
产生行1:3不匹配的输入'04'预期月份
一种可能的解决方案是使用lexer语法模式:
DatesLexer.g4:
lexer grammar DatesLexer;
// Mode expecting DATE (default mode)
DATE : (('0'..'2')? DIGIT) | '30' | '31';
DATE_BAR : '/'
-> pushMode(readingMonth);
// Mode expecting MONTH
mode readingMonth;
MONTH : ('0'? DIGIT) | '11' | '12';
MONTH_BAR : '/'
-> popMode, pushMode(readingYear);
// Mode expecting *_YEAR
mode readingYear;
SHORT_YEAR : DIGIT DIGIT
-> popMode;
FULL_YEAR : ('19' | '20' | '21') DIGIT DIGIT
-> popMode;
fragment DIGIT : ('0' .. '9');
DatesParser.g4:
parser grammar DatesParser;
options { tokenVocab=DatesLexer; }
formattedDate : (DATE DATE_BAR MONTH MONTH_BAR year);
year : SHORT_YEAR | FULL_YEAR;
结果:
仅供参考:
> antlr4 DatesLexer.g4 [-o outDir]
> antlr4 DatesParser.g4 [-o outDir]
> [cd outDir]
> javac *.java
> grun Dates formattedDate -tokens <file> [-gui]
[@0,0:1='11',<1>,1:0]
[@1,2:2='/',<2>,1:2]
[@2,3:4='04',<3>,1:3]
[@3,5:5='/',<4>,1:5]
[@4,6:9='2017',<6>,1:6]
[@5,10:9='<EOF>',<-1>,1:10]
>antlr4 DatesLexer.g4[-o outDir]
>antlr4 DatesParser.g4[-o outDir]
>[光盘输出目录]
>javac*.java
>grun日期格式化日期-标记[-gui]
[@0,0:1='11',,1:0]
[@1,2:2='/',,1:2]
[@2,3:4='04',,1:3]
[@3,5:5='/',,1:5]
[@4,6:9='2017',,1:6]
[@5,10:9='',,1:10]
我第一次尝试失败的原因很明显。标记重叠,因此lexer不可能是明确的。我在这里也看到了很多关于其他问题的建议,建议不要试图在语法中设置太多限制,而是在解析后将这些限制留给验证错误。但是,我仍然想理解为什么这个特殊的案例不起作用。
> antlr4 DatesLexer.g4 [-o outDir]
> antlr4 DatesParser.g4 [-o outDir]
> [cd outDir]
> javac *.java
> grun Dates formattedDate -tokens <file> [-gui]
[@0,0:1='11',<1>,1:0]
[@1,2:2='/',<2>,1:2]
[@2,3:4='04',<3>,1:3]
[@3,5:5='/',<4>,1:5]
[@4,6:9='2017',<6>,1:6]
[@5,10:9='<EOF>',<-1>,1:10]