基本的に私がやろうとしているのは、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 文字をサロゲート ペアとして定義する必要があると確信していますが、後で処理します。今のところ、閉鎖の問題を取り除く方法を知る必要があります。