Когда я запускаю этот код:
public static void main(String[] args) {
String source = "received:last week";
testLexer lexer = new testLexer(CharStreams.fromString(source));
testParser parser = new testParser(new CommonTokenStream(lexer));
System.out.println(parser.parse().toStringTree(parser));
}
ошибка line 1:0 no viable alternative at input 'received'
выводится в STDERR. Когда я меняю {false}?
на {true}?
, ввод обрабатывается правильно (как и ожидалось).
Если вы ожидали, что ввод будет проанализирован как received ':' text
из-за предиката {false}?
, вы не понимаете, как работает лексер ANTLR. Лексер производит токены независимо от анализатора. Не имеет значения, что синтаксический анализатор пытается сопоставить токен TEXT
, ваш ввод всегда токенизируется одинаково.
Лексер работает так:
- постарайтесь поглотить как можно больше персонажей
- если есть два или более лексических правила, которые соответствуют одним и тем же символам, пусть первое, определенное, выиграет
Учитывая эти правила, ясно, что "received:last week"
токенизируется как RECEIVED
, ':'
и LASTWEEK
токен.
РЕДАКТИРОВАТЬ
Возможно ли иметь грамматику, которая может анализировать это полученное: на прошлой неделе как полученное на прошлой неделе, только если последней неделе предшествует полученное, но если, например, у меня есть тема: последняя неделя для анализа как тема на прошлой неделе
Вы можете сделать лексер несколько зависимым от контекста, используя лексические режимы. Затем вы должны создать отдельные грамматики лексера и синтаксического анализатора, которые могут выглядеть следующим образом:
TestLexer.g4
lexer grammar TestLexer;
RECEIVED : 'received' -> pushMode(RECEIVED_MODE);
SUBJECT : 'subject';
TEXT : ~[ :]+;
COLON : ':';
SPACES : SPACE+ -> skip;
fragment SPACE : [ \t\r\n];
mode RECEIVED_MODE;
LASTWEEK : 'last' SPACE+ 'week' -> popMode;
RECEIVED_MODE_COLON : ':' -> type(COLON);
RECEIVED_MODE_TEXT : ~[ :]+ -> type(TEXT), popMode;
вы можете использовать приведенный выше лексер в грамматике парсера следующим образом:
TestParser.g4
parser grammar TestParser;
options {
tokenVocab=TestLexer;
}
...
Теперь "received:last week"
будет токенизироваться как:
'received' `received`
COLON `:`
LASTWEEK `last week`
EOF `<EOF>`
и "subject:last week"
будут токенизироваться как:
'subject' `subject`
COLON `:`
TEXT `last`
TEXT `week`
EOF `<EOF>`
РЕДАКТИРОВАТЬ II
Вы также можете переместить создание last week
в парсер следующим образом:
received
: RECEIVED ':' last_week
;
subject
: SUBJECT ':' text
;
last_week
: LAST WEEK
;
text
: TEXT
| LAST
| WEEK
;
RECEIVED : 'received';
SUBJECT : 'subject';
LAST : 'last';
WEEK : 'week';
TEXT : ~[ :]+;
person
Bart Kiers
schedule
28.07.2020