如何解析 ANTLR 中的部分日期?
How to parse a partial date in ANTLR?
我正在迈出使用 antlr4 的第一步,并尝试解析欧洲格式的部分日期 DD.MM.YYYY
。
我想识别像 15.05.2020
或 7.5.20
这样的正常日期,但也想识别仅包含月份和年份的日期,例如 05.2020
或 5.20
,除此之外只包含一年以外的日期,例如 2020
或 20
。在我的应用程序中,我想访问某个日期(日、月和年)的所有部分,其中某些部分可能是 empty/null.
这是我到目前为止的语法。
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>
因此,根据我的一知半解,我认为解析器规则 date
是问题所在,我将其重写为
date : (
(DAY DOT)?
MONTH DOT
)?
YEAR
;
但我仍然遇到同样的错误。然后我想也许我需要重新排序词法分析器规则。所以我写的不是 YEAR -> MONTH -> DAY,而是 DAY -> MONTH -> YEAR。但是 grun
告诉我。
[@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
我还尝试更改解析器规则 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: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'
据我所知,我正在寻找的语言是一种普通语言。每个输入都是明确的。所以我试图将整个“逻辑”放入词法分析器中,我成功地使用了以下语法。
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>,1:0]
[@1,9:8='<EOF>',<EOF>,2:0]
我无法区分 visitor/listener 中的日、月和年,因为词法分析器规则中不允许使用标签。
所以我的问题是第一个给定语法的问题在哪里,我需要更改什么才能使其正常工作?
从 grun 的令牌输出来看,我想我可能会抓住一天、一个月的每个输入的问题 and/or 年可能是模棱两可的,但作为一个整体输入连同点它不应该是。我怎么能告诉 antlr?
So my question is where is the problem with the first given grammar and what do I need to change to make it work?
问题是词法分析器不是由解析器驱动的。这意味着当解析器尝试匹配标记 DAY DOT MONTH
并且输入是 01.01
时,词法分析器不会为这两个 DAY
和 MONTH
创建 01
's,而是两个 MONTH
标记。这就是 ANTLR 的词法分析器的工作方式:尝试为一个标记抓取尽可能多的字符,并且当有 2 个或更多标记匹配相同字符时(比如 01
可以被 DAY
和 MONTH
),让令牌首先定义为“win”(即 MONTH
令牌)。没有办法解决这个问题。
你可以做的是这样的(未经测试):
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];
我正在迈出使用 antlr4 的第一步,并尝试解析欧洲格式的部分日期 DD.MM.YYYY
。
我想识别像 15.05.2020
或 7.5.20
这样的正常日期,但也想识别仅包含月份和年份的日期,例如 05.2020
或 5.20
,除此之外只包含一年以外的日期,例如 2020
或 20
。在我的应用程序中,我想访问某个日期(日、月和年)的所有部分,其中某些部分可能是 empty/null.
这是我到目前为止的语法。
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>
因此,根据我的一知半解,我认为解析器规则 date
是问题所在,我将其重写为
date : (
(DAY DOT)?
MONTH DOT
)?
YEAR
;
但我仍然遇到同样的错误。然后我想也许我需要重新排序词法分析器规则。所以我写的不是 YEAR -> MONTH -> DAY,而是 DAY -> MONTH -> YEAR。但是 grun
告诉我。
[@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
我还尝试更改解析器规则 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: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'
据我所知,我正在寻找的语言是一种普通语言。每个输入都是明确的。所以我试图将整个“逻辑”放入词法分析器中,我成功地使用了以下语法。
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>,1:0]
[@1,9:8='<EOF>',<EOF>,2:0]
我无法区分 visitor/listener 中的日、月和年,因为词法分析器规则中不允许使用标签。
所以我的问题是第一个给定语法的问题在哪里,我需要更改什么才能使其正常工作?
从 grun 的令牌输出来看,我想我可能会抓住一天、一个月的每个输入的问题 and/or 年可能是模棱两可的,但作为一个整体输入连同点它不应该是。我怎么能告诉 antlr?
So my question is where is the problem with the first given grammar and what do I need to change to make it work?
问题是词法分析器不是由解析器驱动的。这意味着当解析器尝试匹配标记 DAY DOT MONTH
并且输入是 01.01
时,词法分析器不会为这两个 DAY
和 MONTH
创建 01
's,而是两个 MONTH
标记。这就是 ANTLR 的词法分析器的工作方式:尝试为一个标记抓取尽可能多的字符,并且当有 2 个或更多标记匹配相同字符时(比如 01
可以被 DAY
和 MONTH
),让令牌首先定义为“win”(即 MONTH
令牌)。没有办法解决这个问题。
你可以做的是这样的(未经测试):
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];