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
.