antlr грамматика: разрешить совпадение пробелов только в строке шаблона - PullRequest
1 голос
/ 17 апреля 2019

Я хочу проанализировать строки шаблона:

`Some text ${variable.name} and so on ... ${otherVariable.function(parameter)} ...`

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

varname: VAR ;
variable: varname funParameter? ('.' variable)* ;
templateString: '`' (TemplateStringLiteral* '${' variable '}' TemplateStringLiteral*)+ '`' ;
funParameter: '(' variable? (',' variable)*  ')' ;

WS      : [ \t\r\n\u000C]+ -> skip ;
TemplateStringLiteral: ('\\`' | ~'`') ;
VAR : [$]?[a-zA-Z0-9_]+|[$] ;

Когда анализируется вход для грамматики, строка шаблона больше не имеет пробелов, потому чтоWS -> пропустить.Когда я помещаю TemplateStringLiteral перед WS, я получаю сообщение об ошибке:

посторонний ввод '' ожидающий {'`'}

Как я могу разрешить разбирать пробелы ине пропускается только внутри строки шаблона?

1 Ответ

2 голосов
/ 18 апреля 2019

То, что в данный момент происходит

При тестировании вашего примера с текущей грамматикой, отображающей сгенерированные токены, лексер дает следующее:

[@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: ('\\`' | ~'`')+ ;

, чтобы оно захватывало более одного символа и, следовательно, было предпочтительным.У этого есть две причины, почему он не работает:

  1. Как лексер будет соответствовать чему-либо правилу VAR, когда-либо?

  2. Правило 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.

...