Нерелевантное правило нарушает грамматику ANTLR4
Я строю грамматику ANTLR4 для синтаксического анализа строк из источника данных - похоже, если не во многом так же, как StringTemplate, за исключением того, что мне не нравится этот синтаксис, поэтому я пишу свой собственный (также просто для удовольствия и обучения, так как это мой первый опыт с ANTLR). Моя грамматика в настоящее время выглядит следующим образом (это упрощено от того, что у меня есть на самом деле, но я убедился, что это "хороший пример" и демонстрирует ту же проблему, о которой я спрашиваю):
grammar Combined1;
file:
.*? (repToken .*?)+
| .*?
;
foreach: '@foreach' WS* '(' WS* repvar WS* ')' WS* '{' content=file '}' ;
with: '@with' WS* '(' WS* repvar WS* ')' WS* '{' content=file '}' ;
// withx: '@withx' WS* '(' WS* repvar WS* ')' WS* '{' content=file '}' ;
repvar: '@' (
'$'
| '(' nestedIdentifier ')'
| nestedIdentifier
) ;
repToken:
foreach
| with
// | withx
| repvar
;
nestedIdentifier: Identifier ('.' Identifier)* ;
Identifier: [A-Za-z_] [A-Za-z0-9_]* ;
WS: [ \t\r\n] ;
Other: ( . ) ;
Эта грамматика прекрасно работает, что позволяет мне выполнять замены, такие как:
string template = "Test: @foreach(@list){@$}";
Process(template, new { list = new [] { "A", "B", "C" } });
и результат будет:
Test: ABC
(Механика обработки дерева для получения этого результата относительно проста, но не имеет отношения к вопросу, поэтому я не предоставляю этот код.)
У меня такой вопрос... если я добавлю (раскомментирую) правило "withx" прямо под with:
правило, и я забыл включить (раскомментировать) withx
к альтернативам в repToken
тогда мой пример выше ломается, хотя это не имеет ничего общего с withx
, Как только я добавлю withx
в качестве альтернативы repToken
тогда мой пример снова работает. Зачем??
Вот что я знаю:
- Несмотря на погоду
withx
включен или нет, мой лексер правильно возвращает 12 токенов:Test
,:
,' '
,@foreach
,(
,@
,list
,)
,{
,@
,item
, Это неудивительно, поскольку я только добавил правило синтаксического анализатора и не коснулся токенов лексера (кроме добавления неявного токена '@withx'). - Прежде чем я добавлю
withx
Как правило, мой синтаксический анализатор правильно группирует все токены после @foreach как дочерние элементы ForeachContext, в результате чего получается FileContext с 4 дочерними элементами (3 TerminalNodeImpl и RepTokenContext). - После того как я добавлю
withx
Как правило, мой синтаксический анализатор по какой-то причине не распознает остальные токены как принадлежащие ForeachContext, что приводит к FileContext с 10 дочерними элементами, ни один из которых не является ForeachContext, но который имеет все TerminalNodeImpl с 2 RepTokenContext, соответствующими @list и @$.
Я совершенно сбит с толку, почему добавление правила парсера, которое не имеет ничего общего с моим вводом, может привести к сбою парсера. Помогите!?
РЕДАКТИРОВАНИЕ 17.03.2014 : JavaMan запросил дерево разбора в каждом сценарии, чтобы пояснить описание выше. Я не знаю, как сгенерировать графическое представление дерева разбора, которое он сделал, но вот два скриншота из отладчика Visual Studio, иллюстрирующие разницу... Обратите внимание, что в этих изображениях я использую более длинные имена - в частности, ReplacementTokenContext для repToken.
Первый, когда я включаю withx
в альтернативном списке (обратите внимание, что дерево по сути FileContext -> ReplacementTokenContext (индекс узла 3) -> ForeachContext):
И второе, когда я не включаю withx
в альтернативном списке (обратите внимание, что дерево по сути FileContext -> TerminalNodeImpl "@foreach" (индекс узла 3):
1 ответ
Со всей вашей грамматикой плюс withx
Правило и 2 строки ввода, я могу получить этот узел дерева разбора repToken
группировка входного текста @foreach под foreach
узел:
Это выглядит как правильный разбор для меня. Это то, что вы хотите? Может ли быть проблема с вашим кодом посетителя? Вы получили то же самое дерево разбора? Было бы лучше, если бы вы могли опубликовать свое дерево разбора здесь.
Кстати, как насчет отправки всех пробелов в скрытый канал и удаления всех токенов WS из правил парсера?
РЕДАКТИРОВАТЬ:
Я использую ANTLR4 V4.1 только с целью Java, поэтому я не могу быть уверен, что это ошибка с целью C# или v4.2. Но обе грамматики дают мне одно и то же дерево разбора в Java. Существует инструмент под названием TestRig (по крайней мере, в Java target), который может генерировать дерево разбора в форме GUI или ASCII:
java org.antlr.v4.runtime.misc.TestRig Combine1 file -tree in.cpp > treeres.txt
Запустив указанную выше команду с использованием двух упомянутых вами версий грамматики и одного и того же входного файла, я получил такое же ASCII-представление дерева разбора:
(file string template = " Test : (repToken (foreach @foreach ( (repvar @ (nestedIdentifier list)) ) { (file (repToken (repvar @ $))) })) " ; \r \n Process ( template , new { list = new [ ] { " A " , " B " , " C " } } ) ;)
Графический вывод слишком большой, поэтому я не включаю их здесь. Так что, по крайней мере, в Java одно и то же дерево разбора генерируется с или без withx
править.
Я предлагаю вам дважды проверить с помощью инструмента TestRig или попробовать проверить с помощью цели Java.