2

基本的に私がやろうとしているのは、ANTLR 4.1 で国際化リソース識別子の文法を作成することです。これまでで最も苦労したのは、ipv6address のプロダクション ルールを正しく機能させることです。RFC 3987で ipv6address が定義されている方法は、基本的に、そのプロダクション ルールだけで ABNF 形式に 9 つの異なる選択肢があることです。

IPv6address    =                            6( h16 ":" ) ls32
              /                       "::" 5( h16 ":" ) ls32
              / [               h16 ] "::" 4( h16 ":" ) ls32
              / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
              / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
              / [ *3( h16 ":" ) h16 ] "::"    h16 ":"   ls32
              / [ *4( h16 ":" ) h16 ] "::"              ls32
              / [ *5( h16 ":" ) h16 ] "::"              h16
              / [ *6( h16 ":" ) h16 ] "::" 

ここで、ls32 と h16 は両方とも次のように定義されたサブルールです。

ls32           = ( h16 ":" h16 ) / IPv4address

そしてh16の場合:

h16            = 1*4HEXDIG

ここで、HEXDIG は有効な 16 進数のレクサー規則です。このABNF文法を次のようなANTLR構文で記述しようとしました:

grammar IRI;                                    


iri     : scheme ':' ihier_part ('?' iquery)? ('#' ifragment)? ;

ihier_part  : ('//' iauthority ipath_abempty
    | ipath_absolute
    | ipath_rootless)?
    ;

iri_reference   : iri                               
    | irelative_ref                         
    ;

absolute_IRI    : scheme ':' ihier_part ('?' iquery)? ;

irelative_ref   : irelative_part ('?' iquery)? ('#' ifragment)? ;

irelative_part  : ('//' iauthority ipath_abempty
    | ipath_absolute
    | ipath_noscheme)?
    ;

iauthority      : (iuserinfo '@')? ihost (':' port)? ;

iuserinfo       : (iunreserved | pct_encoded | sub_delims | ':')* ;

ihost           : ip_literal
    | ipv4address
    | ireg_name
    ;

ireg_name       : (iunreserved | pct_encoded | sub_delims)* ;

ipath   : (ipath_abempty                        
    | ipath_absolute                        
    | ipath_noscheme                        
    | ipath_rootless)?                      
    ;

ipath_abempty   : ('/' isegment)* ;

ipath_absolute  : '/' (isegment_nz ('/' isegment)*)? ;

ipath_noscheme  : isegment_nz_nc ('/' isegment)* ;

ipath_rootless  : isegment_nz ('/' isegment)* ;


isegment    : (ipchar)* ;

isegment_nz : (ipchar)+ ;

isegment_nz_nc  : (iunreserved | pct_encoded | sub_delims | '@')+ ;     

ipchar      : iunreserved
    | pct_encoded
    | sub_delims
    | ':'
    | '@'
    ;

iquery      : (ipchar | IPRIVATE | '/' | '?')* ;

ifragment   : (ipchar | '/' | '?')* ;

iunreserved : ALPHA
    | DIGIT
    | '-'
    | '.'
    | '_'
    | '~'
    | UCSCHAR
    ;

fragment
UCSCHAR     : '\u00A0'..'\uD7FF'   | '\uF900'..'\uFDCF'   | '\uFDF0'..'\uFFEF'  
    | '\u40000'..'\u4FFFD' | '\u50000'..'\u5FFFD' | '\u60000'..'\u6FFFD'
    | '\u70000'..'\u7FFFD' | '\u80000'..'\u8FFFD' | '\u90000'..'\u9FFFD'    
    | '\uA0000'..'\uAFFFD' | '\uB0000'..'\uBFFFD' | '\uC0000'..'\uCFFFD'
    | '\uD0000'..'\uDFFFD' | '\uE1000'..'\uEFFFD'
    ;

fragment
IPRIVATE    : '\uE000'..'\uF8FF' | '\uF0000'..'\uFFFFD' | '\u100000'..'\u10FFFD' ;

scheme      : ALPHA (ALPHA | DIGIT | '+' | '-' | '.')* ;

port        : (DIGIT)* ;

ip_literal  : '[' (ipv6address | ipvFuture) ']' ;

ipvFuture   : 'v' (HEXDIG)+ '.' (unreserved | sub_delims | ':')+ ;

ipv6address
locals [int i1, i2, i3, i4, i5, i6, i7, i8, i9, i10 = 0;]               
    : ( {$i1<=6}? h16 ':' {$i1++;} )* ls32                  
    | '::' ( {$i2<=5}? h16 ':' {$i2++;} )* ls32
    | (h16)? '::' ( {$i3<=4}? h16 ':' {$i3++;} )* ls32
    | ((h16 ':')? h16)? '::' ( {$i4<=3}? h16 ':'{$i4++;} )* ls32
    | (( {$i5>=0 && $i5<=2}? h16 ':' {$i5++;} )* h16)? '::' ( {$i6<=2}? h16 ':' {$i6++;} )* ls32
    | (( {$i7>=0 && $i7<=3}? h16 ':' {$i7++;} )* h16)? '::' h16 ':' ls32
    | (( {$i8>=0 && $i8<=4}? h16 ':' {$i8++;} )* h16)? '::' ls32
    | (( {$i9>=0 && $i9<=5}? h16 ':' {$i9++;} )* h16)? '::' h16
    | (( {$i10>=0 && $i10<=6}? h16 ':' {$i10++;} )* h16)* '::'
    ;

h16
locals [int i = 1;]
    : ( {$i>=1 && $i<=4}? HEXDIG {$i++;} )* ;       

ls32        : h16 ':' h16 ;

ipv4address : DEC_OCTET '.' DEC_OCTET '.' DEC_OCTET '.' DEC_OCTET ;

DEC_OCTET   : '0'..'9'                      
    | '10'..'99'
    | '100'..'199'
    | '200'..'249'
    | '250'..'255'
    ;

pct_encoded : '%' HEXDIG HEXDIG ;

unreserved  : ALPHA | DIGIT | '-' | '.' | '_' | '~' ;

reserved    : gen_delims
    | sub_delims
    ;

gen_delims  : ':' | '/' | '?' | '#' | '[' | ']' | '@' ;         

sub_delims  : '!' | '$' | '&' | '\'' | '(' | ')' ;              



DIGIT  : [0-9] ;                                
HEXDIG : [0-9A-F] ;
ALPHA  : [a-zA-Z] ;
WS     : [' ' | '\t' | '\r' | '\n']+ -> skip ;

私の ANTLR 文法では、ipv6address と h16 の両方について、ABNF 文法で定義された多重度規則を指定するためにセマンティック述語を使用しようとしています。org.antlr.v4.Tool クラスを実行すると、次の出力が得られます。

warning(125): IRI.g4:68:20: implicit definition of token 'IPRIVATE' in parser
warning(125): IRI.g4:78:4: implicit definition of token 'UCSCHAR' in parser
error(153): IRI.g4:100:0: rule 'ipv6address' contains a closure with at least one alternative that can match an empty string
warning(154): IRI.g4:40:0: rule 'ipath' contains an optional block with at least one alternative that can match an empty string
warning(154): IRI.g4:100:0: rule 'ipv6address' contains an optional block with at least one alternative that can match an empty string
warning(154): IRI.g4:100:0: rule 'ipv6address' contains an optional block with at least one alternative that can match an empty string
warning(154): IRI.g4:100:0: rule 'ipv6address' contains an optional block with at least one alternative that can match an empty string
warning(154): IRI.g4:100:0: rule 'ipv6address' contains an optional block with at least one alternative that can match an empty string
warning(154): IRI.g4:100:0: rule 'ipv6address' contains an optional block with at least one alternative that can match an empty string
warning(154): IRI.g4:100:0: rule 'ipv6address' contains an optional block with at least one alternative that can match an empty string

もちろん、警告も取り除きたいのですが、「ipv6address」には、空の文字列に一致する可能性のある少なくとも 1 つの代替手段を含むクロージャーが含まれているというエラーを取り除く必要があります。複数の代替エラーについて、StackOverflow で同様の投稿を見たことがあります。ただし、空の文字列に一致する可能性のあるクロージャを扱ったものはありませんでした。また、UCSCHAR の \uFFFF より後の Unicode 文字をサロゲート ペアとして定義する必要があると確信していますが、後で処理します。今のところ、閉鎖の問題を取り除く方法を知る必要があります。

4

2 に答える 2

1

うまくいかないことがかなりあります:


0

280Z28が言ったこと。


1

'250'..'255'文字列と一致しません"250"... "255": 元の ABNF 仕様で説明されているように、数値範囲を一致させる必要があります。

ABNF

dec-octet      = DIGIT                 ; 0-9
               / %x31-39 DIGIT         ; 10-99
               / "1" 2DIGIT            ; 100-199
               / "2" %x30-34 DIGIT     ; 200-249
               / "25" %x30-35          ; 250-255

ANTLR

dec_octet
 : digit
 | non_zero_digit digit
 | D1 digit digit
 | ...
 ;

2

競合するレクサー ルールが多数あります。たとえば、次のとおりです。

HEXDIG : [0-9A-F] ;
ALPHA  : [a-zA-Z] ;

HEXDIGは の前に定義されているため、たとえばALPHA、レクサーは を検出すると常に を作成します。レクサーは、パーサーが受け取りたいものに基づいてトークンを生成しないことに注意してください。lexer は独自の方法で動作し、大文字の を生成することはありません。HEXDIG'A'ALPHAA-F


3

fragmentrules は、他のレクサー ルール (または他のfragmentルール) 内でのみ使用できます。パーサー ルール内では使用できません。


4

実際には問題ではありませんが、述語があると文法が読みにくくなります。可能であれば、述語を最小限に抑えることが私の経験則です。

あなたのルール:

h16
locals [int i = 1;]
    : ( {$i>=1 && $i<=4}? HEXDIG {$i++;} )* ;

次のように記述できます。

h16
 : HEXDIG HEXDIG HEXDIG HEXDIG
 | HEXDIG HEXDIG HEXDIG
 | HEXDIG HEXDIG
 | HEXDIG
 ;

あるいは:

h16
 : HEXDIG (HEXDIG (HEXDIG HEXDIG?)?)?
 ;


これらの問題のほとんどは簡単に修正できますが、#2 はより難しい問題です。できること (すべきこと) は、レクサーに単一文字のトークンを作成させ、パーサーにこれらの単一文字のトークンを全体に一致させることです。dec-octetパーサーを公式の ABNFのプロダクションと一致させる方法の例:

dec_octet
 : digit                               // 0-9
 | non_zero_digit digit                // 10-99
 | D1 digit digit                      // 100-199
 | D2 (D0 | D1 | D2 | D3 | D4) digit   // 200-249
 | D2 D5 (D0 | D1 | D2 | D3 | D4 | D5) // 250-255
 ;

digit
 : D0
 | non_zero_digit
 ;

non_zero_digit
 : D1 | D2 | D3 | D4 | D5 | D6 | D7 | D8 | D9
 ;

// lexer rules
D0 : '0';
D1 : '1';
D2 : '2';
D3 : '3';
D4 : '4';
D5 : '5';
D6 : '6';
D7 : '7';
D8 : '8';
D9 : '9';

私はかつて ANTLR 3 用の IRI 文法を書いたことがあります。必要に応じて、Github のどこかに置くことができます。

于 2014-01-29T22:04:28.423 に答える