ANTLR 4.1 Переменная ANTLR 4 множественности токенов приводит к ошибке: "замыкание хотя бы с одной альтернативой, которая может соответствовать пустой строке"

По сути, я пытаюсь создать грамматику для интернационализированных идентификаторов ресурсов в ANTLR 4.1. Самое трудное время, которое у меня было до сих пор, - попытаться заставить правильно работать производственное правило для ipv6address. Способ определения ipv6-адреса в RFC 3987 заключается в том, что в основном в формате 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 - это правило лексера для действительных шестнадцатеричных цифр. Я пытался написать эту грамматику 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 я пытаюсь использовать семантические предикаты, чтобы указать правила множественности, определенные в грамматике ABNF, как для ipv6address, так и для h16. Когда я выполняю класс 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" содержит замыкание, по крайней мере, с одной альтернативой, которая может соответствовать пустой строке. Я видел похожие посты в Stackru о множественных ошибках ошибок. Однако ни один из них не имел дело с замыканиями, которые могли бы соответствовать пустой строке. Я также почти уверен, что мне придется определить символы Юникода в UCSCHAR прошлом \uFFFF как суррогатные пары, но об этом я позабочусь позже. Просто нужно знать, как избавиться от проблемы закрытия на данный момент.

2 ответа

Решение

Есть некоторые вещи, которые идут не так, как надо:


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Лексер всегда будет создавать HEXDIG когда он видит 'A', например. Вы должны понимать, что лексер не производит токены на основе того, что парсер хотел бы получить. Лексер пойдет своим путем и никогда не произведет ALPHA для заглавных букв A-F,


3

fragment правила могут использоваться только внутри других правил лексера (или других 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';

Однажды я написал грамматику IRI для ANTLR 3. Если хотите, я мог бы поместить ее где-нибудь в Github.

Ваш h16 Правило использует (...)* вместо (...)+, что позволяет ему соответствовать 0 цифр. Когда вы размещаете h16* в вашей грамматике это означает, что вы разрешаете любое количество пустяков в вашем дереве разбора, что всегда приводит к бесконечному циклу, запускающему вашу систему из памяти (создавая узлы дерева разбора без токенов).

Другие вопросы по тегам