ANTLR 4.5 - Несоответствующий ввод 'x', ожидающий 'x'

Я начал использовать ANTLR и заметил, что он довольно изменчив с его правилами lexer. Очень неприятным примером является следующее:

grammar output;

test: FILEPATH NEWLINE TITLE ;

FILEPATH: ('A'..'Z'|'a'..'z'|'0'..'9'|':'|'\\'|'/'|' '|'-'|'_'|'.')+ ;
NEWLINE: '\r'? '\n' ;
TITLE: ('A'..'Z'|'a'..'z'|' ')+ ;

Эта грамматика не будет соответствовать чему-то вроде:

C:\test.txt
х

Как ни странно, если я изменяю TITLE на TITLE: 'x' ;, он все равно терпит неудачу на этот раз, вызывая сообщение об ошибке "mismatched input" x "Ожидание" x ", что сильно запутывает. Еще более странно, если я заменю использование TITLE в test на FILEPATH, все работает (хотя FILEPATH будет соответствовать больше, чем я считаю совпадением, поэтому в целом это недействительное решение для меня).

Я очень смущен, почему ANTLR дает такие чрезвычайно странные ошибки, а затем внезапно работает без видимых причин, когда тасует вещи.

Ответ 1

Похоже, это распространенное недоразумение ANTLR:

Языковая обработка в ANTLR:

Обработка языка осуществляется в два строго разделенных этапа:

  • Лексирование, то есть разбиение текста на токены
  • Разбор, то есть построение дерева разбора из токенов

Поскольку лексизация должна предшествовать синтаксическому анализу, есть следствие: лексер не зависит от синтаксического анализатора, синтаксический анализатор не может влиять на синтаксический анализ.

Lexing

Лексирование в ANTLR работает следующим образом:

  • все правила с первым символом в верхнем регистре являются правилами лексера
  • лексер начинается с начала и пытается найти правило, которое лучше всего соответствует текущему вводу
  • наилучшее совпадение - это совпадение с максимальной длиной, то есть токен, полученный в результате добавления следующего входного символа к совпадению с максимальной длиной, не соответствует ни одному правилу лексера.
  • токены генерируются из совпадений:
    • если одно правило соответствует максимальной длине, соответствующий токен помещается в поток токенов.
    • если несколько правил соответствуют максимальной длине, то первый определенный токен в грамматике передается в поток токенов

Пример: что не так с вашей грамматикой

Ваша грамматика имеет два важных правила:

FILEPATH: ('A'..'Z'|'a'..'z'|'0'..'9'|':'|'\\'|'/'|' '|'-'|'_'|'.')+ ;
TITLE: ('A'..'Z'|'a'..'z'|' ')+ ;

Каждое совпадение, которое соответствует TITLE, также будет соответствовать FILEPATH. И FILEPATH определяется перед TITLE: поэтому каждый токен, который вы ожидаете стать заголовком, будет FILEPATH.

Для этого есть два совета:

  • не нарушайте правила лексера (ни один токен не должен совпадать с супернабором другого).
  • если ваши токены намеренно совпадают с одинаковыми строками, то расположите их в правильном порядке (в вашем случае этого будет достаточно).
  • если вам нужен лексер, управляемый парсером, вы должны перейти на другой генератор парсера: PEG-Parsers или GLR-Parsers будут делать это (но, конечно, это может вызвать другие проблемы).

Ответ 2

У меня та же ошибка, но я не могу представить, какое правило лексера может быть задействовано.
Я пытаюсь проанализировать некоторые CDE файлы Cobol - я думаю, что это довольно специфично для HP Nonstop.

Во всяком случае, то, что я хочу разобрать это что-то вроде

* SCHEMA PRODUCED DATE - TIME : 1/29/2019 - 15:17:01
?SECTION MYREQUEST,NONSTOP
* Definition MYREQUEST created on 05/11/2016 at 11:05
  01 MYREQUEST. 
  ...

и парсер не работает с

mismatched input '?SECTION foo' expecting '?'

Моя грамматика такая:

grammar CdeFile;

cdeFile : line+ ;
line : sectionLine ;
sectionLine : QUESTIONMARK SECTION sectionName '\r'? '\n' ;
sectionName : TEXTLIST ;

QUESTIONMARK : '?' ;
SECTION: S E C T I O N ;

TEXTLIST : TEXT (',' TEXT)* ;
TEXT : ~[,\n\r"]+ ;

WS : ( ' ' | '\t' | '\f' )+ -> skip;
LINE_COMMENT 
     : '*' {Column == 1}? '*'* ~('\n'|'\r')* '\r'? '\n' ->skip 
     ; 


// case insensitive chars
fragment A:('a'|'A');
fragment B:('b'|'B');
fragment C:('c'|'C');
fragment D:('d'|'D');
fragment E:('e'|'E');
fragment F:('f'|'F');
fragment G:('g'|'G');
fragment H:('h'|'H');
fragment I:('i'|'I');
fragment J:('j'|'J');
fragment K:('k'|'K');
fragment L:('l'|'L');
fragment M:('m'|'M');
fragment N:('n'|'N');
fragment O:('o'|'O');
fragment P:('p'|'P');
fragment Q:('q'|'Q');
fragment R:('r'|'R');
fragment S:('s'|'S');
fragment T:('t'|'T');
fragment U:('u'|'U');
fragment V:('v'|'V');
fragment W:('w'|'W');
fragment X:('x'|'X');
fragment Y:('y'|'Y');
fragment Z:('z'|'Z');

Значения QUESTIONMARK синхронизированы, все восстановлено - все еще это странное сообщение.

Ответ 3

Это не было напрямую проблемой OP, но для тех, у кого такое же сообщение об ошибке, вот что вы можете проверить.


Когда я ввел новое ключевое слово, у меня было такое же Mismatched Input 'x' expecting 'x' смутное сообщение об ошибке. Причиной для меня было то, что я поместил новое ключевое слово после моего правила VARNAME, которое назначило его как имя переменной, а не как новое ключевое слово. Я исправил это, поместив ключевые слова перед правилом VARNAME.