2017-12-11 11 views
0

antlr4を使用して「P3M2D」のようにISO 8601 period expressionsを解析しようとしています。しかし、私はいくつかの障害物に当たっており、助けに感謝します。私は、antlrとコンパイラのどちらにも新しくなっています。antlr4.7では、「ID」ルールの前にISO 8601間隔「P3M2D」のようなルールを解析する方法

私の文法は以下の通りです。テストランで

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_interval_dルールを打つことはありません、それは常にIDルールに行く:私はここで一度にレクサーとパーサのルールを組み合わせています。私は「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 
[@0,0:2='now',<DATETIME_NAME>,1:0] 
[@1,4:4='+',<'+'>,1:4] 
[@2,6:6='P',<'P'>,1:6] 
[@3,7:7='3',<NUMBER_INT>,1:7] 
[@4,8:8='M',<'M'>,1:8] 
[@5,9:9='2',<NUMBER_INT>,1:9] 
[@6,10:10='D',<'D'>,1:10] 
[@7,11:12='\r\n',<' 
'>,1:11] 
[@8,13:12='<EOF>',<EOF>,2:0] 
ISO8601_INTERVAL DATE seen P3M2D 
(iso (date_expr (date_expr now) + (iso8601_interval (iso8601_interval_d P 3 M 2 D))) \r\n <EOF>) 

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

が、今、障害の異なる種類

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>) 

私はこのようなことに当たった最初の人ではないと確信しています。ここには何の慣用句がありますか?

EDIT - 私が直面している問題を強調するために、ここで省略した文法の他の部分のIDトークンが必要です。

+0

なぜIDトークンが必要ですか? – gtosto

+0

私の問題を浮き彫りにする文法の断片を抽象化しました。私は省略した他のルールのどこかでIDトークンが必要です。 – Dinesh

+1

ダウンヴォートした人は誰でもコメントを残してください。 – Dinesh

答えて

1

他人にもわかるように、この問題はIDトークンにあります。事実、iso-8601の持続時間構文は有効なIDです。 @Mikeによって解決された解決策に加えて。 island grammarという名前のものがあなたのニーズに合っている場合は、ANTLRのlexical modesを使用して、iso日付の解析中に IDレクサールールを除外できます。 Beloveはそれが私はビットにパーサー規則を変更し

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 
%> 

のような式を解析することができ、レクサーで、その後

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')? 
    ; 

の仕事ができる方法の例がありiso IDと期間の混合を示す。 希望すると、

0

あなたがしたいことはできません。 IDは、同じ入力とiso8601_intervalと一致します。このような場合、ANTLR4は最大一致文字を選択します。これは、無制限の文字数に一致するため、IDです。

イントロデューサとしてPを除外することが可能な唯一の方法は、期間限定で使用できます。

別のオプションは、後処理ステップです。他の識別子と同様に期間を解析し、意味フェーズでは期間のように見えるすべてのIDをチェックします。これはおそらく最良の解決策です。

関連する問題