То, что в данный момент происходит
При тестировании вашего примера с текущей грамматикой, отображающей сгенерированные токены, лексер дает следующее:
[@0,0:0='`',<'`'>,1:0]
[@1,1:4='Some',<VAR>,1:1]
[@2,6:9='text',<VAR>,1:6]
[@3,11:12='${',<'${'>,1:11]
[@4,13:20='variable',<VAR>,1:13]
[@5,21:21='.',<'.'>,1:21]
[@6,22:25='name',<VAR>,1:22]
[@7,26:26='}',<'}'>,1:26]
... shortened ...
[@26,85:84='<EOF>',<EOF>,2:0]
Это говорит вам, что Some
, которыйпредполагалось, что TemplateStringLiteral*
на самом деле был ограничен до VAR
.Почему это происходит?
Как упоминалось в этом ответе, antlr использует максимально длинное совпадение для создания токена.Поскольку ваше правило TemplateStringLiteral
соответствует только одиночным символам, а правило VAR
соответствует бесконечному числу, лексер, очевидно, использует последнее для сопоставления Some
.
Что вы можете попробовать (Спойлер: не сработает)
Вы можете попытаться изменить правило следующим образом:
TemplateStringLiteral: ('\\`' | ~'`')+ ;
, чтобы оно захватывало более одного символа и, следовательно, было предпочтительным.У этого есть две причины, почему он не работает:
Как лексер будет соответствовать чему-либо правилу VAR
, когда-либо?
Правило TemplateStringLiteral
теперь также соответствует ${
, поэтому запрещает правильное распознавание начала блока шаблона.
Как добиться того, чего вы действительно хотите
Там может бытьдругое решение, но оно работает:
Файл MartinCup.g4:
parser grammar MartinCup;
options { tokenVocab=MartinCupLexer; }
templateString
: BackTick TemplateStringLiteral* (template TemplateStringLiteral*)+ BackTick
;
template
: TemplateStart variable TemplateEnd
;
variable
: varname funParameter? (Dot variable)*
;
varname
: VAR
;
funParameter
: OpenPar variable? (Comma variable)* ClosedPar
;
Файл MartinCupLexer.g4:
lexer grammar MartinCupLexer;
BackTick : '`' ;
TemplateStart
: '${' -> pushMode(templateMode)
;
TemplateStringLiteral
: '\\`'
| ~'`'
;
mode templateMode;
VAR
: [$]?[a-zA-Z0-9_]+
| [$]
;
OpenPar : '(' ;
ClosedPar : ')' ;
Comma : ',' ;
Dot : '.' ;
TemplateEnd
: '}' -> popMode;
Эта грамматика использует режимы лексера , чтобы различать внутреннюю и внешнюю части фигурных скобок.Правило VAR
теперь активно только после обнаружения ${
и остается активным только до чтения }
.Таким образом, он не перехватывает не шаблонный текст, такой как Some
.
Обратите внимание, что использование режимов лексера требует разделенной грамматики (отдельные файлы для грамматик синтаксического анализатора и лексера).Поскольку в грамматике синтаксического анализатора не разрешены никакие правила лексера, мне пришлось ввести токены для скобок, запятой, точки и обратных кавычек.
О пробелах
Я предполагаю, что вы хотите оставить пробелы внутри "обычного текста", но не допускать пробелов внутри шаблонов.Поэтому я просто удалил правило WS
.Вы всегда можете добавить его заново, если хотите.
Я проверил вашу альтернативную грамматику, где вы поставили TemplateStringLiteral
выше WS
, но, вопреки вашим наблюдениям, это дает мне:
строка 1: 1 посторонний ввод 'Некоторые' ожидают {'$ {', TemplateStringLiteral}
Причина этого та же, что и выше, Some
привязана к VAR
.