Грамматика antlr: разрешить сопоставление пробелов только в строке шаблона

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

`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 -> skip. Когда я помещаю TemplateStringLiteral перед WS, я получаю сообщение об ошибке:

посторонний ввод "ожидая {'' '}

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


person Martin Cup    schedule 17.04.2019    source источник
comment
Связанный: stackoverflow.com/questions/53504903/parse-string-antlr   -  person sepp2k    schedule 17.04.2019
comment
Мартин, я думаю, что ваш вопрос на самом деле не о пробелах, поскольку ваши проблемы с синтаксическим анализом не имеют к нему никакого отношения (см. Мой ответ). Я думаю, вы могли бы улучшить этот вопрос, сосредоточив его больше на симптомах (строка не распознается, хотя на первый взгляд правила кажутся удовлетворительными). Если сформулировать таким образом, это может помочь будущим посетителям с аналогичными проблемами. Если хотите, я могу попробовать отредактировать ваш вопрос. Дай мне знать :)   -  person AplusKminus    schedule 18.04.2019
comment
Не могли бы вы принять ответ, если бы он решил вашу проблему? Или предоставьте более подробную информацию, если нет?   -  person AplusKminus    schedule 28.04.2019


Ответы (1)


Что сейчас происходит

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

[@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;

В этой грамматике используются режимы

Причина этого та же, что и выше, Some переведено в VAR.

person AplusKminus    schedule 18.04.2019
comment
Примечание. Вы по-прежнему можете использовать строковые литералы в грамматиках анализатора для правил лексера, которые определены с помощью одного строкового литерала в грамматике лексера. То есть вы все равно можете писать ',' вместо ЗАПЯТЫЙ в грамматике парсера, даже если у вас есть отдельная грамматика лексера. - person sepp2k; 18.04.2019