Parsing 如何在ANTLR中解析部分日期?

Parsing 如何在ANTLR中解析部分日期?,parsing,antlr,antlr4,Parsing,Antlr,Antlr4,我正在采取使用antlr4的第一步,并尝试以欧洲格式解析部分日期DD.MM.YYYY 我想识别一个正常的日期,比如15.05.2020或7.5.20,但也要识别只包含月份和年份的日期,比如05.2020或5.20,以及只包含年份以外的日期,比如2020或20。在我的应用程序中,我希望能够访问日期(日、月和年)的所有部分,其中一些部分可能为空/空 这是我到目前为止的语法 grammar LogicalDateExpressions; stmt : date EOF ;

我正在采取使用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',<YEAR>,1:3]
[@3,9:8='<EOF>',<EOF>,2:0]
line 1:2 mismatched input '.' expecting <EOF>
但我还是犯了同样的错误。然后我想也许我需要重新排列lexer规则。所以,我写的不是年->月->日,而是日->月->年。但是格伦告诉我

[@0,0:1='05',<DAY>,1:0]
[@1,2:2='.',<'.'>,1:2]
[@2,3:6='2020',<YEAR>,1:3]
[@3,9:8='<EOF>',<EOF>,2:0]
line 1:3 mismatched input '2020' expecting MONTH
[@0,0:0='0',<'0'>,1:0]
[@1,1:1='5',<DIGIT>,1:1]
[@2,2:2='.',<'.'>,1:2]
[@3,3:3='2',<'2'>,1:3]
[@4,4:4='0',<'0'>,1:4]
[@5,5:5='2',<'2'>,1:5]
[@6,6:6='0',<'0'>,1:6]
[@7,9:8='<EOF>',<EOF>,2:0]
line 1:1 no viable alternative at input '05'
那也很糟糕<代码>格伦告诉我的

[@0,0:1='05',<DAY>,1:0]
[@1,2:2='.',<'.'>,1:2]
[@2,3:6='2020',<YEAR>,1:3]
[@3,9:8='<EOF>',<EOF>,2:0]
line 1:3 mismatched input '2020' expecting MONTH
[@0,0:0='0',<'0'>,1:0]
[@1,1:1='5',<DIGIT>,1:1]
[@2,2:2='.',<'.'>,1:2]
[@3,3:3='2',<'2'>,1:3]
[@4,4:4='0',<'0'>,1:4]
[@5,5:5='2',<'2'>,1:5]
[@6,6:6='0',<'0'>,1:6]
[@7,9:8='<EOF>',<EOF>,2:0]
line 1:1 no viable alternative at input '05'
它解析我给它的每一个输入。但问题是,每个输入都只是一个
DT

[@0,0:6='05.2020',<DT>,1:0]
[@1,9:8='<EOF>',<EOF>,2:0]
[@0,0:6='05.2020',1:0]
[@1,9:8='',,2:0]
我无法区分访客/听众中的日期、月份和年份,因为lexer规则中不允许使用标签


所以我的问题是,第一个给定的语法的问题在哪里,我需要改变什么才能使它起作用

从grun的令牌输出来看,我想我可能理解了这个问题,一天、一个月和/或一年的每个输入都可能是不明确的,但作为一个整体,结合点输入,它不应该是模糊的。我怎么能告诉antlr呢

所以我的问题是,第一个给定的语法的问题在哪里,我需要改变什么才能使它起作用

问题是lexer不是由解析器驱动的。这意味着,当解析器尝试匹配标记
DAY-DOT-MONTH
且输入为
01.01
时,lexer不会为这两个
01
标记创建
DAY
MONTH
,而是创建两个
MONTH
标记。这就是ANTLR的lexer的工作原理:尝试为一个令牌获取尽可能多的字符,当有2个或更多的令牌匹配相同的字符时(比如
01
可以由
DAY
MONTH
匹配),让令牌首先定义为“赢”(即
MONTH
令牌)。这是没有办法的

您可以这样做(未经测试):


谢谢你的解释和代码,我想我明白了。顺便说一句,代码运行得非常好。不管怎样,如果我说我的例子中的lexer是模糊的,从技术上讲是正确的吗?我不会这么说,尽管我理解人们是这样的。由于ANTLR的lexer的工作方式(匹配贪婪,在平局的情况下,第一条规则获胜),模糊性并不存在。当然,不客气!
[@0,0:6='05.2020',<DT>,1:0]
[@1,9:8='<EOF>',<EOF>,2:0]
date
 : year
 | month DOT year
 | day DOT month DOT year
 ;

day
 : N_01_12
 | N_13_31
 ;

month
 : N_01_12
 ;

year
 : N_01_12
 | N_13_31
 | N_32_99
 | N_1000_2999
 ;

N_01_12
 : '0'? D    // 01-09
 | '1' [0-2] // 10-12
 ;

N_13_31
 : '1' [3-9] // 13-19
 | '2' D     // 20-29
 | '3' [01]  // 30-31
 ;

N_32_99
 : '3' [2-9] // 32-39
 | [4-9] D   // 40-99
 ;

N_1000_2999
 : [12] D D D // 1000-2999
 ;

fragment D : [0-9];