Antlr4 ANTLR 4.1变量ANTLR 4令牌多重性产生错误:“1”;至少具有一个可匹配空字符串的可选项的闭包;
基本上,我要做的是在ANTLR 4.1中为国际化的资源标识符创建语法。到目前为止,我所经历的最困难的一段时间是试图让ipv6address的生产规则正常工作。ipv6address在中的定义方式是,对于该生产规则,ABNF格式基本上有9种不同的替代方案:Antlr4 ANTLR 4.1变量ANTLR 4令牌多重性产生错误:“1”;至少具有一个可匹配空字符串的可选项的闭包;,antlr4,abnf,Antlr4,Abnf,基本上,我要做的是在ANTLR 4.1中为国际化的资源标识符创建语法。到目前为止,我所经历的最困难的一段时间是试图让ipv6address的生产规则正常工作。ipv6address在中的定义方式是,对于该生产规则,ABNF格式基本上有9种不同的替代方案: IPv6address = 6( h16 ":" ) ls32 / "::" 5( h16 ":" ) ls32
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是有效十六进制数字的lexer规则。我尝试用ANTLR语法编写ABNF语法,如下所示:
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 ;
显然,我也想消除警告,但我需要消除这样的错误,即“ipv6address”包含一个闭包,其中至少有一个选项可以匹配空字符串。我在StackOverflow上看到过关于多个备选方案错误的类似帖子。但是,它们都没有处理可以匹配空字符串的闭包。我还非常确定我将不得不将UCSCHAR past\uFFFF中的Unicode字符定义为代理项对,但这将在以后处理。现在只需要知道如何摆脱闭包问题。您的
h16
规则使用(…)*
而不是(…)+
,这允许它匹配0位数字。当您在语法中放置h16*
时,这意味着您允许在解析树中执行任意数量的Nothing操作,这将始终导致系统内存不足的无限循环(创建没有标记的解析树节点)。出现了很多问题:
0 280Z28所说的
1.
'250'..'255'
与字符串'250'
不匹配<代码>“255”:您需要匹配原始ABNF规范中描述的数字范围:
荷兰银行
ANTLR
2. 你有很多相互冲突的lexer规则。以这些为例:
HEXDIG : [0-9A-F] ;
ALPHA : [a-zA-Z] ;
因为HEXDIG
是在ALPHA
之前定义的,所以lexer在看到'a'
时总是创建一个HEXDIG
。您必须认识到,lexer不会根据解析器希望接收的内容生成令牌。lexer将按自己的方式运行,并且不会为大写字母A-F
生成ALPHA
3.
fragment
规则只能在其他lexer规则(或其他fragment
规则)中使用。不能在解析器规则中使用它们
4. 这不是一个真正的问题,但是谓词让你的语法很难阅读:如果可能的话,尽量减少谓词是我的经验法则 你的规则:
h16
locals [int i = 1;]
: ( {$i>=1 && $i<=4}? HEXDIG {$i++;} )* ;
甚至:
h16
: HEXDIG (HEXDIG (HEXDIG HEXDIG?)?)?
;
这些问题大多很容易解决,但#2是一个更棘手的问题。您可以(应该?)做的是让lexer创建单字符标记,并让解析器将这些单字符标记匹配成一个整体。您如何让解析器匹配官方ABNF的
dec八位字节
产品的示例:
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的某个地方。这确实解决了我的问题。对于问题0,我这样写是因为我遵循Data.g4的最终ANTLR 4参考中的示例,其中序列规则尝试匹配n个整数。我咖啡因摄入不足,忘记了允许零次重复。我继续使用你对h16的第二个建议。我可能会继续使用语义谓词,尽管对于ipv6address来说,另一种方法只是冗长的。至于将v3语法放在Github上,我非常感谢您的提议,但为了学习,我正在尽我所能。再次感谢。当然,没问题@Zak_Hoskins。对于那些在问答中遇到困难的人,我在这里上传了一个ANTLRv4版本
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?)?)?
;
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';