当前位置: 首页 > 知识库问答 >
问题:

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

琴英华
2023-03-14

我正在进行使用antlr4的第一步,并尝试以欧洲格式dd.mm.yyyy解析部分日期。

我想要识别像15.05.20207.5.20这样的正常日期,但也要识别像05.20205.20这样的只包含月份和年份的日期,以及像202020这样的只包含年份的日期。在我的应用程序中,我希望能够访问日期(日、月和年)的所有部分,其中一些部分可能是空的/空的。

这是我到目前为止的语法。

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               
     ;

但我还是犯了同样的错误。然后我想也许我需要重新排序lexer规则。所以我写的不是年->月->日,而是日->月->年。但是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选项的顺序,但也没有成功。然后,我尝试更改lexer规则DAY、MONTH和yeary,使其成为解析器规则(DAY,MONTH,yeary)。在得到一些错误之后,因为解析器规则中显然不允许使用[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'

据我所知,我正在寻找的语言是一种正规的语言。并且每一个输入都是明确无误的。因此,我试图将整个“逻辑”融入到lexer中,并成功地使用了下面的语法。

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]

从grun的令牌输出来看,我想我可能会理解这个问题,一天、一个月和/或一年的每一个输入都可能是模棱两可的,但作为一个整体的输入,连同点,它不应该是模棱两可的。我怎么告诉antlr?

共有1个答案

越雨泽
2023-03-14

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

问题是lexer不是由解析器驱动的。这意味着当解析器试图匹配标记day DOT month,并且输入为01.01时,lexer不会为这两个01创建daymonth,而是创建两个month标记。这就是ANTLR的lexer的工作原理:尝试为一个令牌获取尽可能多的字符,当有2个或更多的令牌匹配相同的字符时(比如01可以通过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];
 类似资料:
  • 我正在尝试从Cisco IOS配置解析以下命令:

  • 在我的Android项目中,我使用了reverfit2作为API调用和GSON作为转换器。将json转换为POJO并遵循在同一项目的50+API中也使用的常用方法。 但是,在这种特定的情况下,一些项被解析并分配给变量,而其他项则不是。 如何解决这种部分解析?

  • 我使用ANTLR Version4创建编译器。第一阶段是Lexer部分。我创建了“compilerlexer.g4”文件,并在其中输入了lexer规则。 compilerlexer.g4: null 有几十个这样的警告和错误。病因是什么? 一般问题:使用组合语法和单独使用lexer和parser有什么不同?如何连接单独的语法和lexer文件?

  • 问题内容: 我用antlr 4.4编写了这样的语法: 然后我使用antlr 4.4生成解析器和词法分析器,此过程成功 生成类后,我编写了一些使用语法的Java代码 以上所有代码都是CSV字符串的解析器,例如:““ a”,“ b”,c“ 窗口输出: 我想知道如何从代码背后的方法(getErrors()或…)中获取此错误,而不是由于输出窗口的结果 谁能帮我 ? 问题答案: 使用ANTLR进行CSV解析

  • 我想在Java中解析自定义的多种日期格式。这是我的代码 场景1:模式的顺序: yyMMdd'h'HH yyMMdd DateTimeFormatter-formatter=new DateTimeFormatterBuilder().appendOptional(模式的日期时间格式设置工具(“yyMMdd'h'HH”)).appddOptional(格式的日期时间格式化工具(“yyMMdd”).t

  • 问题内容: 我有以下json: 基于的更改。 知道这一点,有没有办法使该字段保持字符串?这个想法是使用调用传递的正确处理程序,然后在其中使用正确的struct 解析字符串。 例: 先感谢您。 问题答案: 使用json.RawMessage获取字段的原始JSON文本: 像这样使用它: 在操场上跑。