Как мне удалить конфликт выбора в JavaCC? - PullRequest
0 голосов
/ 02 ноября 2018

Мои правила производства следующие:

OtherNonTerminal := NonTerminal | {}
NonTerminal := <TOKEN>:A() | <TOKEN>:A(), Nonterminal()

В JavaCC, NonTerminal имеет конфликт выбора:

void OtherNonTerminal() : {}
{
    Nonterminal() | {}
}

void Nonterminal() : {}
{
    <TOKEN> <COLON> A()
|
    <TOKEN> <COLON> A() <COMMA> Nonterminal()
}

Будет ли это одним из способов избавиться от конфликта выбора, и будет ли программа работать так, как указано, как мое правило производства без терминала?

void Nonterminal() : {}
{
    <TOKEN> <COLON> A() (<COMMA> NonTerminal())? 
}

1 Ответ

0 голосов
/ 02 ноября 2018

Почему возникает проблема

В JavaCC, метод по умолчанию для определения того, какую ветвь выбрать, заключается в просмотре следующего токена. Если этот токен совместим с первым вариантом, первый вариант выбран, и это решение необратимо. Возврата нет.

Так посмотрите на выбор

void Nonterminal() : {}
{
    <TOKEN> <COLON> A()
|
    <TOKEN> <COLON> A() <COMMA> Nonterminal()
}

и предположим, что следующий токен на входе - <TOKEN>. Первый выбор будет сделан независимо от того, будет ли <COMMA> позже. Другими словами

  <TOKEN> <COLON> A()
| <TOKEN> <COLON> A() <COMMA> Nonterminal()

эквивалентно

  <TOKEN> <COLON> A()

За исключением того, что первый выдаст предупреждающее сообщение, поскольку JavaCC видит, что написанное вами не имеет смысла.


Хорошее решение

Ответ на ваш вопрос "да". Одно из решений - сделать то, что вы сделали, и исключить общий префикс

void Nonterminal() : {}
{
    <TOKEN> <COLON> A() (<COMMA> NonTerminal())? 
}

Решение, которое может быть лучше в зависимости от

Если по какой-то причине вы не знаете, на что рассчитывать, вы также можете сделать следующее

void Nonterminal() : {}
{
    LOOKAHEAD( <TOKEN> <COLON> A() <COMMA>)
    <TOKEN> <COLON> A() <COMMA> Nonterminal()
|
    <TOKEN> <COLON> A()
}

Здесь парсер будет смотреть вперед во входном потоке. Если он видит запятую, первый выбор сделан. В противном случае второй.

Это может быть полезно, если семантические действия различны в обоих случаях. Э.Г.

  LOOKAHEAD( <TOKEN> <COLON> A() <COMMA>)
  <TOKEN> {doSomething();} <COLON> A() <COMMA> Nonterminal()
|
  <TOKEN> {doSomethingDifferent();} <COLON> A()

Нерекурсивное решение.

Третий вариант -

void Nonterminal() : {}
{
    Foo() (<COMMA>  Foo() )*
}

void Foo() : {}
{
    <TOKEN> <COLON> A()
}
...