使用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]