使用 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" 问题 – 或者将其向前推(注意 DATE
在 SHORT_YEAR
和 MONTH
之前):
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]
我正在尝试使用以下语法解析日期:
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" 问题 – 或者将其向前推(注意 DATE
在 SHORT_YEAR
和 MONTH
之前):
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]