使用 Antlr4 解析日期时出错

Error while parsing a date with 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' | '12';

fragment DIGIT : ('0' .. '9');

但它无法解析我期望工作的值。例如,输入 11/04/2017 会产生错误:

   line 1:0 mismatched input '11' expecting DATE

我的第一个猜测是词法分析器无法确定某些值 (1-12) 是 DATE 还是 MONTH,这就是导致问题的原因。但是当我试图通过用解析器规则替换它们来修复它时,我遇到了同样的问题:

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 之类的东西,在歧义范围之外。

我做错了什么?

正如你所说,"the tokens overlap"(注意31模棱两可的,可能是短年份)。在这种情况下,将选择最长可能匹配的词法分析器规则。如果有两个或多个长度相同的匹配项,它将选择第一个(按照它们出现的顺序)。 (我想我之前在 www.antlr.org 上读过这篇文章)

所以只是改变规则的顺序 "solves" 问题 – 或者将其向前推(注意 DATESHORT_YEARMONTH 之前):

grammar Dates;

formattedDate : (DATE '/' MONTH '/' year);
year : SHORT_YEAR | FULL_YEAR;

DATE : (('0'..'2')? DIGIT) | '30' | '31';
SHORT_YEAR : DIGIT DIGIT;
FULL_YEAR : ('19' | '20' | '21') DIGIT DIGIT;
MONTH : ('0'? DIGIT) | '11' | '12';

fragment DIGIT : ('0' .. '9');

产量 line 1:3 mismatched input '04' expecting MONTH.


一个可能的解决方案是使用词法分析器语法模式:

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]