Parsing 在antlr4.7中,如何解析ISO 8601 interval之类的规则;P3M2D“;在“之前”;ID";规则

Parsing 在antlr4.7中,如何解析ISO 8601 interval之类的规则;P3M2D“;在“之前”;ID";规则,parsing,antlr4,Parsing,Antlr4,我尝试使用antlr4像“P3M2D”一样解析。但是我遇到了一些障碍,我会感谢你的帮助。我对antlr和编译器都比较陌生 我的语法如下。我将lexer和parser规则结合在一起: grammar test_iso ; // import testLexerRules ; iso : ( date_expr NEWLINE)* EOF; date_expr : date_expr op=( '+' | '-' ) iso8601_interval #dateexpr_Interv

我尝试使用
antlr4
像“P3M2D”一样解析。但是我遇到了一些障碍,我会感谢你的帮助。我对antlr和编译器都比较陌生

我的语法如下。我将lexer和parser规则结合在一起:

grammar test_iso ;
// import testLexerRules ;

iso : ( date_expr NEWLINE)* EOF;

date_expr
    :   date_expr op=( '+' | '-' ) iso8601_interval #dateexpr_Interval
    |   date_expr op='-' date_expr                  #dateexpr_Diff
    |   DATETIME_NAME                               #dateexpr_Named
    |   '(' inner=date_expr ')'                     #dateexpr_Paren
    ;

///////////////////////////////////////////

iso8601_interval
    :   iso8601_interval_d
        { System.out.println("ISO8601_INTERVAL DATE seen " + $text);}
    ;

iso8601_interval_d
    :   'P' ( y=NUMBER_INT 'Y' )? ( m=NUMBER_INT 'M' )? ( w=NUMBER_INT 'W' )? ( d=NUMBER_INT 'D' )?
    ;

///////////////////////////////////////////
// in separate file : test_lexer.g4
// lexer grammar testLexerRules ;
///////////////////////////////////////////

fragment
TODAY 
    :   'today' | 'TODAY' 
    ;
fragment
NOW 
    :   'now' | 'NOW' 
    ;

DATETIME_NAME
    :   TODAY
    |   NOW
    ;

///////////////////////////////////////////

NUMBER_INT
    :   '-'? INT                    // -3, 45
    ;

fragment
DIGIT :     [0-9] ;

fragment
INT :       '0' | [1-9] DIGIT* ;

//////////////////////////////////////////////

//
// identifiers
//

ID 
    :   ALPHA ALPH_NUM* 
    { System.out.println("ID seen " + getText()); }
    ;

ID_SQLFUNC
    :   'h$' ALPHA_UPPER ALPHA_UPPER_NUM*
    { System.out.println("SQL FUNC seen " + getText()); }
    ;

fragment
ALPHA :    [a-zA-Z] ;

fragment
ALPH_NUM : [a-zA-Z_0-9] ;

fragment
ALPHA_UPPER :    [A-Z] ;

fragment
ALPHA_UPPER_NUM : [A-Z_0-9] ;

//////////////////////////////////////////////

NEWLINE : '\r\n' ;
WS  :  [ \t]+ -> skip  ;
在测试运行中,它从不点击
iso8601\u interval\u d
规则,它总是转到
ID
规则

C:\lab>java org.antlr.v4.gui.TestRig test_iso iso -tokens -tree
now + P3M2D
^Z
ID seen P3M2D
[@0,0:2='now',<DATETIME_NAME>,1:0]
[@1,4:4='+',<'+'>,1:4]
[@2,6:10='P3M2D',<ID>,1:6]
[@3,11:12='\r\n',<'
'>,1:11]
[@4,13:12='<EOF>',<EOF>,2:0]
line 1:6 mismatched input 'P3M2D' expecting 'P'
ISO8601_INTERVAL DATE seen P3M2D
(iso (date_expr (date_expr now) + (iso8601_interval (iso8601_interval_d P3M2D))) \r\n <EOF>)
但现在是另一种失败

now + @P3M2D
^Z
ID seen M2D
[@0,0:2='now',<DATETIME_NAME>,1:0]
[@1,4:4='+',<'+'>,1:4]
[@2,6:7='@P',<'@P'>,1:6]
[@3,8:8='3',<NUMBER_INT>,1:8]
[@4,9:11='M2D',<ID>,1:9]
[@5,12:13='\r\n',<'
'>,1:12]
[@6,14:13='<EOF>',<EOF>,2:0]
line 1:9 no viable alternative at input '3M2D'
ISO8601_INTERVAL DATE seen @P3M2D
(iso (date_expr (date_expr now) + (iso8601_interval (iso8601_interval_d @P 3 M2D))) \r\n <EOF>)
now+@P3M2D
^Z
我看过M2D
[@0,0:2='now',1:0]
[@1,4:4='+',,1:4]
[@2,6:7='@P',1:6]
[@3,8:8='3',,1:8]
[@4,9:11='M2D',1:9]
[@5,12:13='\r\n',1:12]
[@6,14:13='',,2:0]
第1行:9在输入“3M2D”处没有可行的替代方案
ISO8601_间隔日期见P3M2D
(iso(日期扩展(现在的日期扩展)+(iso8601时间间隔(iso8601时间间隔d@P3M2D)))\r\n
我相信我不是第一个想到这种事情的人。这里的antlr成语是什么


编辑——我需要语法其他部分中的
ID
标记,我在这里省略了它,以突出我面临的问题。

你不可能想做什么<代码>ID与
iso8601\u间隔
匹配相同的输入。在这种情况下,ANTLR4选择最长的匹配,即
ID
,因为它可以匹配无限数量的字符

在语法中,唯一可行的方法是排除
p
作为可能的
ID
介绍人,这样就可以在整个过程中独家使用


另一个选项是后处理步骤。像任何其他标识符一样解析持续时间,并在语义阶段检查所有看起来像持续时间的ID。这可能是最好的解决方案。

就像通过其他人发现一样,问题在于ID令牌。事实上,iso-8601的持续时间语法是一个有效的ID。除了@Mike提出的解决方案之外。如果调用的内容适合您的需要,您可以在解析iso日期时使用ANTLR排除ID lexer规则。 下面是一个关于它如何工作的例子

parser grammar iso;
options { tokenVocab=iso_lexer; }

iso : ISO_BEGIN ( date_expr NEWLINE)* ISO_END;

date_expr
    :   date_expr op=( '+' | '-' ) iso8601_interval #dateexpr_Interval
    |   date_expr op='-' date_expr                  #dateexpr_Diff
    |   DATETIME_NAME                               #dateexpr_Named
    |   '(' inner=date_expr ')'                     #dateexpr_Paren
    ;

///////////////////////////////////////////

iso8601_interval
    :   iso8601_interval_d
        { System.out.println("ISO8601_INTERVAL DATE seen " + $text);}
    ;

iso8601_interval_d
    :   'P' ( y=NUMBER_INT 'Y' )? ( m=NUMBER_INT 'M' )? ( w=NUMBER_INT 'W' )? ( d=NUMBER_INT 'D' )?
    ;
然后在lexer中

lexer grammar iso_lexer;

//
// identifiers (in DEFAULT_MODE)
//

ISO_BEGIN
    : '<@' -> mode(ISO)
    ;

ID
    :   ALPHA ALPH_NUM*
    { System.out.println("ID seen " + getText()); }
    ;

ID_SQLFUNC
    :   'h$' ALPHA_UPPER ALPHA_UPPER_NUM*
    { System.out.println("SQL FUNC seen " + getText()); }
    ;

WS0  :  [ \t]+ -> skip  ;

// all the following token are scanned only when iso mode is active
mode ISO;
ISO_END
    : '@>' -> mode(DEFAULT_MODE)
    ;

WS0  :  [ \t]+ -> skip  ;
NEWLINE : '\r'? '\n' ;


ADD : '+' ;
SUB : '-' ;
LPAREN : '(' ;
RPAREN : ')' ;
P : 'P' ;
Y : 'Y' ;
M : 'M' ;
W : 'W' ;
D : 'D' ;

DATETIME_NAME
    :   TODAY
    |   NOW
    ;

fragment TODAY:   'today' | 'TODAY'    ;
fragment NOW :   'now' | 'NOW' ;


///////////////////////////////////////////

NUMBER_INT
    :   '-'? INT                    // -3, 45
    ;

fragment DIGIT :     [0-9] ;

fragment INT :       '0' | [1-9] DIGIT* ;

//////////////////////////////////////////////

fragment ALPHA :    [a-zA-Z] ;

fragment ALPH_NUM : [a-zA-Z_0-9] ;

fragment ALPHA_UPPER :    [A-Z] ;

fragment ALPHA_UPPER_NUM : [A-Z_0-9] ;
lexer语法iso_lexer;
//
//标识符(默认_模式下)
//
ISO_开始
:“”->模式(默认模式)
;
WS0:[\t]+->跳过;
换行符:'\r'?'\n′;
加:“+”;
SUB:“-”;
LPAREN:'(';
RPAREN:')';
P:‘P’;
Y:‘Y’;
M:‘M’;
W:‘W’;
D:‘D’;
日期时间名称
:今天
|现在
;
今天片段:“今天”|“今天”;
现在片段:“现在”|“现在”;
///////////////////////////////////////////
编号
:   '-'? INT/-3,45
;
碎片数字:[0-9];
片段INT:'0'|[1-9]位*;
//////////////////////////////////////////////
片段α:[a-zA-Z];
碎片ALPH_NUM:[a-zA-Z_0-9];
碎片αu上部:[A-Z];
碎片ALPHA_UPPER_NUM:[A-Z_0-9];
这样的语法可以解析如下表达式

Pluton Planet <% now + P10Y
%>
深成岩行星 我稍微修改了解析器规则
iso
,以演示ID和周期混合。
希望这有帮助

为什么需要ID标记?我提取了一段语法来突出我的问题。我需要我省略的其他规则中的ID标记。无论谁投了反对票,请您留下评论为什么投反对票?
lexer grammar iso_lexer;

//
// identifiers (in DEFAULT_MODE)
//

ISO_BEGIN
    : '<@' -> mode(ISO)
    ;

ID
    :   ALPHA ALPH_NUM*
    { System.out.println("ID seen " + getText()); }
    ;

ID_SQLFUNC
    :   'h$' ALPHA_UPPER ALPHA_UPPER_NUM*
    { System.out.println("SQL FUNC seen " + getText()); }
    ;

WS0  :  [ \t]+ -> skip  ;

// all the following token are scanned only when iso mode is active
mode ISO;
ISO_END
    : '@>' -> mode(DEFAULT_MODE)
    ;

WS0  :  [ \t]+ -> skip  ;
NEWLINE : '\r'? '\n' ;


ADD : '+' ;
SUB : '-' ;
LPAREN : '(' ;
RPAREN : ')' ;
P : 'P' ;
Y : 'Y' ;
M : 'M' ;
W : 'W' ;
D : 'D' ;

DATETIME_NAME
    :   TODAY
    |   NOW
    ;

fragment TODAY:   'today' | 'TODAY'    ;
fragment NOW :   'now' | 'NOW' ;


///////////////////////////////////////////

NUMBER_INT
    :   '-'? INT                    // -3, 45
    ;

fragment DIGIT :     [0-9] ;

fragment INT :       '0' | [1-9] DIGIT* ;

//////////////////////////////////////////////

fragment ALPHA :    [a-zA-Z] ;

fragment ALPH_NUM : [a-zA-Z_0-9] ;

fragment ALPHA_UPPER :    [A-Z] ;

fragment ALPHA_UPPER_NUM : [A-Z_0-9] ;
Pluton Planet <% now + P10Y
%>