#java #antlr4
#java #antlr4
Вопрос:
Вот начало моих правил лексера:
F_TEXT_START
: {! matchingFText}? 'f"' {matchingFText = true;}
;
F_TEXT_PH_ESCAPE
: {matchingFText amp;amp; ! matchingFTextPh}? '{=/'
;
F_TEXT_PH_START
: {matchingFText amp;amp; ! matchingFTextPh}? '{=' {matchingFTextPh = true;}
;
F_TEXT_PH_END
: {matchingFText amp;amp; matchingFTextPh}? '}' {matchingFTextPh = false;}
;
F_TEXT_CHAR
: {matchingFText amp;amp; ! matchingFTextPh}? (~('"' | '{') | '""' | '{' ~'=')
;
F_TEXT_END
: {matchingFText amp;amp; ! matchingFTextPh}? '"' {matchingFText = false;}
;
IF
: {! matchingFText || matchingFTextPh}? 'if'
;
ELIF
: {! matchingFText || matchingFTextPh}? 'elif'
;
// Lots of other keywords
fragment LETTER
: ('A' .. 'Z' | 'a' .. 'z' | '_')
;
VARIABLE
: {! matchingFText || matchingFTextPh}? LETTER (LETTER | DIGIT)*
;
Что я делаю, так это помещаю свой форматированный текст не просто как обычный текстовый токен, а с буквой f перед, но я добавляю его в свое дерево синтаксического анализа, чтобы иметь возможность определить, есть ли ошибки при синтаксическом анализе (с помощью всего parser.start()
). Итак, форматированный текст начинается с f"
, заканчивается на "
, любой "
должен быть заменен на ""
и может содержать заполнители, начинающиеся с {=
и заканчивающиеся на }
, но если вы хотите на самом деле написать {=
, вам придется заменить его на {=/
.
Проблема в том, что в обычном форматированном текстовом содержимом (не заполнителе) лексер начал обрабатывать не только F_TEXT_CHAR
, но и другие правила лексера, такие как переменные. То, что я сделал, кажется довольно глупым, я просто поместил семантические предикаты для каждого другого правила, чтобы избежать их сопоставления в содержимом форматированного текста (но все еще в заполнителе).
Разве нет лучшего способа?
Ответ №1:
Я бы использовал для этого лексический режим. Чтобы использовать лексические режимы, вам придется определить отдельные грамматики лексера и синтаксического анализатора. Вот краткая демонстрация:
lexer grammar TestLexer;
F_TEXT_START
: 'f"' -> pushMode(F_TEXT)
;
VARIABLE
: LETTER (LETTER | DIGIT)*
;
F_TEXT_PH_ESCAPE
: '{=/'
;
F_TEXT_PH_END
: '}' -> popMode
;
SPACES
: [ trn] -> skip
;
fragment LETTER
: [a-zA-Z_]
;
fragment DIGIT
: [0-9]
;
mode F_TEXT;
F_TEXT_CHAR
: ~["{] | '""' | '{' ~'='
;
F_TEXT_PH_START
: '{=' -> pushMode(DEFAULT_MODE)
;
F_TEXT_END
: '"' -> popMode
;
Используйте лексер в своем анализаторе следующим образом:
parser grammar TestParser;
options {
tokenVocab=TestLexer;
}
// ...
Если вы теперь обозначите входные данные f"mu {=mu}" mu
, вы получите следующие токены:
F_TEXT_START `f"`
F_TEXT_CHAR `mu `
F_TEXT_PH_START `{=`
VARIABLE `mu`
F_TEXT_PH_END `}`
F_TEXT_END `"`
VARIABLE `mu`
Комментарии:
1. Спасибо вам за это! Но я не понимаю: почему вы не поставили
F_TEXT_PH_START
вF_TEXT
режим? Кстати, спасибо за ваше репозиторий GitHub «mu», это очень помогло!2. Я думал,
F_TEXT_PH_START
что это может происходить только внутри форматированной строки. Если это не так, соответствующим образом скорректируйте грамматику моего примера.3. Да, это так! Кроме того, он может объединяться в
F_TEXT_CHAR
вот так:F_TEXT_CHAR: ~["{] | '""' | '{' ~'=' | '{=/';
4. Кроме того, я уже использую
{
и}
в своей грамматике по умолчанию (но это не может быть вexpression
, что я прошу в синтаксическом анализаторе для заполнителя. Каким будет семантический предикат для этого?5. Я обнаружил семантическую префиксацию при чтении кода antlr:
_modeStack.contains(1)