ANTLR: слишком жадное правило

#antlr #antlr4

#antlr #antlr4

Вопрос:

Похоже, у меня проблема с пониманием слишком жадного соответствия правил. Я пытаюсь .g4 использовать лексический файл для раскраски синтаксиса. Вот минимальная (упрощенная) выдержка для того, чтобы сделать эту проблему воспроизводимой:

 lexer grammar ANTLRv4Lexer;

Range
    : '['  RangeChar   ']'
    ;

fragment EscapedChar
    : '\' ~[u]
    | '\u' EscapedCharHex EscapedCharHex EscapedCharHex EscapedCharHex
    ;

fragment EscapedCharHex
    : [0-9A-Fa-f]
    ;

fragment RangeChar
    : ~']'
    | EscapedChar
    ;

Punctuation
    : [:;() ->*[]~|]
    ;

Identifier
    : [a-zA-Z0-9] 
    ;

Whitespace
    : [ t] 
      -> skip
    ;

Newline
    : ( 'r' 'n'?
      | 'n'
      )
      -> skip
    ;

LineComment
    : '//' ~[rn]*
    ;
 

(Неполный) тестовый файл выглядит следующим образом:

    : (~ []\] | EscAny)  -> more
   ;

   // ------

fragment Id
   : NameStartChar NameChar*
   ;


String2Part
    : ( ~['\]
      | EscapeSequence
      ) 
    ;
 

Я не понимаю, почему оно соответствует Range такому жадному:

 [@0,3:3=':',<Punctuation>,1:3]
[@1,5:5='(',<Punctuation>,1:5]
[@2,6:6='~',<Punctuation>,1:6]
[@3,8:135='[]\] | EscAny)  -> morern   ;rnrn   // ------rnrnfragment Idrn   : NameStartChar NameChar*rn   ;rnrnrnString2Partrnt: ( ~['\]',<Range>,1:8]
[@4,141:141='|',<Punctuation>,13:3]
[@5,143:156='EscapeSequence',<Identifier>,13:5]
[@6,162:162=')',<Punctuation>,14:3]
[@7,163:163=' ',<Punctuation>,14:4]
[@8,167:167=';',<Punctuation>,15:1]
[@9,170:169='<EOF>',<EOF>,16:0]
 

Я понимаю, почему в первой строке оно совпадает [ , ] и \ , но почему оно, очевидно, трактуется ] как RangeChar ?

Ответ №1:

Ваш лексер соответствует первому при \] использовании ~']' альтернативы, а затем соответствует оставшемуся ] как EscapedChar . Причина, по которой это происходит, заключается в том, что эта интерпретация приводит к более длинному совпадению, чем то, где \ EscapedChar и ] является концом диапазона, и когда существует несколько допустимых способов сопоставления правила лексера, ANTLR всегда выбирает самый длинный (за исключением случаев, когда *? это задействовано).

Чтобы исправить это, вы должны изменить RangeChar , чтобы обратная косая черта разрешалась только как часть escape-последовательностей, т. Е. Заменить ~']' на ~[]\] .

Комментарии:

1. Большое вам спасибо. Я предположил, что он пропустит ~']' в случае обратной косой черты, потому EscapedChar что совпадение будет длиннее.

2. @ThomasS. Да, но принцип максимального мунка применим к правилу лексера, а не к фрагментам. То есть он идет по пути, который дает максимально возможное совпадение для Range , не RangeChar .