显然,谓词嵌套太深,导致解析器无法回溯并尝试第二种选择:
date_as_numbers returns [val]
: ymd {$val = $ymd.val} // alternaitve 1
| mdy {$val = $mdy.val} // alternaitve 2
;
当我交换备选方案时:
date_as_numbers returns [val]
: mdy {$val = $mdy.val}
| ymd {$val = $ymd.val}
;
输入
"2/12/2017"
正确分析,但
"2017/12/2"
失败。
我不知道这是预期的行为,还是一个bug(我还没有对新的v4谓词做过太多)。你可以
raise an issue
关于这个。
在玩了一会儿之后,我把规则合并成了一个大的
any_date
规则,并让这些规则从谓词开始,而不是让谓词在中间的某个位置(正如您自己已经暗示的):
grammar sample;
@parser::members {
boolean lte(Token token, int value) {
return Integer.parseInt(token.getText()) <= value;
}
boolean gte(Token token, int value) {
return Integer.parseInt(token.getText()) >= value;
}
}
date_as_numbers returns [String val]
: any_date EOF {$val = $any_date.val;}
;
any_date returns [String val]
: {gte(_input.LT(1), 1) && lte(_input.LT(1), 12)}?
INTEGER '-' day_number '-' year_digits {$val = "y=" + $year_digits.val + ", m=" + $INTEGER.text + ", d=" + $day_number.val;}
| {gte(_input.LT(1), 1) && lte(_input.LT(1), 12)}?
INTEGER '/' day_number '/' year_digits {$val = "y=" + $year_digits.val + ", m=" + $INTEGER.text + ", d=" + $day_number.val;}
| {gte(_input.LT(1), 1900) && lte(_input.LT(1), 2100)}?
INTEGER '-' month_number '-' day_number {$val = "y=" + $INTEGER.text + ", m=" + $month_number.val + ", d=" + $day_number.val;}
| {gte(_input.LT(1), 1900) && lte(_input.LT(1), 2100)}?
INTEGER '/' month_number '/' day_number {$val = "y=" + $INTEGER.text + ", m=" + $month_number.val + ", d=" + $day_number.val;}
;
month_number returns [int val]
: INTEGER {gte($INTEGER, 1) && lte($INTEGER, 12)}?
{$val = Integer.parseInt($INTEGER.text);}
;
day_number returns [int val]
: INTEGER {gte($INTEGER, 1) && lte($INTEGER, 31)}?
{$val = Integer.parseInt($INTEGER.text);}
;
year_4digit returns [int val]
: INTEGER {gte($INTEGER, 1900) && lte($INTEGER, 2100)}?
{$val = Integer.parseInt($INTEGER.text);}
;
year_2digit returns [int val]
: '\''? INTEGER {gte($INTEGER, 65) || lte($INTEGER, 39)}?
{$val = Integer.parseInt($INTEGER.text) >= 65 ? 1900 + Integer.parseInt($INTEGER.text) : 2000 + Integer.parseInt($INTEGER.text);}
;
year_digits returns [int val]
: year_4digit {$val = $year_4digit.val;}
| year_2digit {$val = $year_2digit.val;}
;
INTEGER: '0'..'9'+ ;
(抱歉,没有python)
运行此类时:
import org.antlr.v4.runtime.*;
public class Main {
public static void main(String[] args) {
String[] tests = { "2/12/2017", "2017/12/31", "1-2-'03" };
for (String test : tests) {
sampleLexer lexer = new sampleLexer(CharStreams.fromString(test));
sampleParser parser = new sampleParser(new CommonTokenStream(lexer));
System.out.println(test + " -> " + parser.date_as_numbers().val);
}
}
}
打印以下内容:
2/12/2017 -> y=2017, m=2, d=12
2017/12/31 -> y=2017, m=12, d=31
1-2-'03 -> y=2003, m=1, d=2
我知道,这并不完美,但也许你可以调整一下你目前的语法,让它发挥作用。
编辑
当然,您也可以放弃谓词,改为执行以下操作:
grammar sample;
date_as_numbers
: ymd
| mdy
| failure
;
ymd
: year '/' month '/' day
| year '-' month '-' day
;
mdy
: month '/' day '/' year
| month '-' day '-' year
;
year
: '\''? year_2digits
| NUM_4DIGITS
;
year_2digits
: NUM_1_12
| NUM_13_31
| NUM_2DIGITS
;
month
: NUM_1_12
;
day
: NUM_1_12
| NUM_13_31
;
failure
: NUM_OTHER
;
NUM_1_12
: [1-9] // 1..9
| '1' [0-2] // 10..12
;
NUM_13_31
: '1' [3-9] // 13..19
| '2' D // 20..29
| '3' [01] // 30..31
;
NUM_2DIGITS
: D D
;
NUM_4DIGITS
: '19' D D // 1900..1999
| '20' D D // 2000..2099
| '2100' // 2100
;
NUM_OTHER
: D+
;
fragment D : [0-9];