问题描述
我正在第一步使用antlr4,并尝试解析欧洲格式DD.MM.YYYY
的部分日期。
我想识别正常日期,例如15.05.2020
或7.5.20
,但也要识别仅包含月份和年份的日期,例如05.2020
或5.20
,此外还需要识别仅包含2020
或20
之类的年份。在我的应用程序中,我想访问日期的所有部分(日,月和年),其中某些部分可能为空/空。
到目前为止,这是我的语法。
grammar LogicalDateExpressions;
stmt : date EOF
;
date : (YEAR)
| (MONTH DOT YEAR)
| (DAY DOT MONTH DOT YEAR)
;
YEAR : ([12] [0-9] [0-9] [0-9])
| ([0-9] [0-9])
;
MONTH : ('0'? [1-9])
| ('1' [012])
;
DAY : ('0'? [1-9])
| ([12][0-9])
| ('3'[01])
;
DOT : '.';
WS : [ \t\r\n\u000C]+ -> skip;
此语法适用于单个年份(2020
),但无法识别月份-年份组合(05.2020
)。 grun -tokens
告诉了我以下内容。
[@0,0:1='05',<YEAR>,1:0]
[@1,2:2='.',<'.'>,1:2]
[@2,3:6='2020',1:3]
[@3,9:8='<EOF>',<EOF>,2:0]
line 1:2 mismatched input '.' expecting <EOF>
所以我不知所措,发现解析器规则date
是问题所在,我将其重写为
date : (
(DAY DOT)?
MONTH DOT
)?
YEAR
;
但是我仍然遇到同样的错误。然后我想也许我需要重新排列词法分析器规则。因此,我将它们写为DAY-> MONTH-> YEAR,而不是YEAR-> MONTH-> DAY。但是grun
告诉了我。
[@0,<DAY>,2:0]
line 1:3 mismatched input '2020' expecting MONTH
我还试图更改解析器规则date
中or'ed替代项的顺序,但这也没有解决。然后,我尝试更改词法分析器规则DAY,MONTH,YEAR,使它们成为解析器规则(日,月,年)。由于看到了解析器规则中显然不允许使用[0-9]表示法而遇到一些错误后,我将语法更改为此。
date : (year)
| (month DOT year)
| (day DOT month DOT year)
;
[...]
year : (('1'|'2') DIGIT DIGIT DIGIT)
| (DIGIT DIGIT)
;
month : ('0'? DIGIT_NO_ZERO)
| ('1' ('0'|'1'|'2'))
;
day : ('0'? DIGIT_NO_ZERO)
| (('1'|'2') DIGIT)
| ('3' ('0'|'1'))
;
[...]
DIGIT : [0-9];
DIGIT_NO_ZERO : [1-9];
那真是太可惜了。 grun
告诉我。
[@0,0:0='0',<'0'>,1:1='5',<DIGIT>,1:1]
[@2,1:2]
[@3,3:3='2',<'2'>,1:3]
[@4,4:4='0',1:4]
[@5,5:5='2',1:5]
[@6,6:6='0',1:6]
[@7,2:0]
line 1:1 no viable alternative at input '05'
据我了解,我所寻找的语言是普通语言。每个输入都是明确的。因此,我尝试将整个“逻辑”纳入词法分析器,并成功完成了以下语法。
grammar LogicalDateExpressions;
stmt : date EOF
;
date : DT
;
DT : (
((('0'? [1-9])|([12][0-9])|('3'[01])) DOT)? // Day
(('0'? [1-9])|('1' [012])) DOT // Month
)?
((DIGIT DIGIT DIGIT DIGIT)|(DIGIT DIGIT)) // Year
;
DIGIT : [0-9];
DOT : '.';
WS : [ \t\r\n\u000C]+ -> skip;
它解析我给它的每个输入。但是问题在于,每个输入都只是一个DT
。
[@0,0:6='05.2020',<DT>,2:0]
我无法区分访问者/收听者中的日期,月份和年份,因为词法分析器规则中不允许使用标签。
所以我的问题是,第一个给定的语法在哪里出现问题,我需要对其进行更改以使其起作用?
通过查看grun的令牌输出,我想我可能会理解一天,一个月和/或一年中每个输入的问题可能是模棱两可的,但从整体上来说,与点结合起来应该不是问题。我该如何告诉antlr?
解决方法
所以我的问题是,第一个给定的语法在哪里出现问题,我需要对其进行更改以使其起作用?
问题在于语法分析器没有驱动词法分析器。这意味着当解析器尝试匹配令牌DAY DOT MONTH
且输入为01.01
时,词法分析器将不会为这两个{创建一个DAY
和一个MONTH
{1}},但有两个01
令牌。 ANTLR的词法分析器是这样工作的:尝试为令牌捕获尽可能多的字符,并且当有两个或多个匹配相同字符的令牌时(例如MONTH
和{{ 1}}),让令牌定义第一个“获胜”(即01
令牌)。无法解决这个问题。
您可以做的事情是这样的(未经测试):
DAY