Старт с
A -> B (sB)* | dAd | z
B -> <id> | sB | A
Заменитель
A -> (<id> | sB | A) (sB)* | dAd | z
Определение
C -> (sB)*
Заменитель
A -> (<id> | sB | A) C | dAd | z
фактор
A -> <id> C | sBC | AC | dAd | z
Определение
D -> <id> C | sBC | dAd | z
Так
A -> D | AC
Снять левый рекурсии
A -> D (C)*
Заменитель C и D
A -> (<id> (sB)* | sB(sB)* | dAd | z) (sB)**
С x** = x*
A -> (<id> (sB)* | sB(sB)* | dAd | z) (sB)*
С x*x* = x*
A -> (<id> | sB | dAd | z) (sB)*
B -> <id> | sB | A
Тот же результат, как Шринивас лет.
Редактировать добавлен после просмотра @ ответ Rymoid в.
В этот момент левая рекурсия была удалена, поэтому мы закончили. Но, как указывает @Rymoid, грамматика все еще неоднозначна и поэтому не LL (1). Ниже я попытаюсь справиться с двусмысленностью, но не найти грамматику LL (1).
Одна проблема заключается в том, что с A =>* sB
выбор sB | A
неоднозначен и не нужен. Начнем с удаления этого выбора. У нас есть
A -> (<id> | sB | dAd | z) (sB)*
B -> <id> | A
Аналогично A =>* <id>
, поэтому выбор <id> | A
неоднозначна и не требуется. У нас есть
A -> (<id> | sB | dAd | z) (sB)*
B -> A
И тогда нам не нужно B
больше.
A -> (<id> | sA | dAd | z) (sA)*
Оставшаяся проблема в том, что, поскольку s
в последующем наборе A
, нет никакого способа узнать, с одной знаком опережающего просмотра, независимо от того, чтобы остаться в петле (sA)*
или выйти из нее.
Исходный вопрос не запрашивал грамматику LL (1), но поскольку сообщение помечено [JavaCC], мы можем предположить, что желаемое - это тот, который работает с JavaCC. Это не совсем то же самое, что LL (1), хотя LL (1) подразумевает, что грамматика будет хорошо работать с JavaCC.
Я предполагаю, что все виды использования A
вне определения A
определенно не следует s
. Чтобы быть конкретным в этом, я буду считать, что есть (только) еще одна продукция, которая составляет S -> A <EOF>
, и что S
является стартовым нетерминалом. Но на самом деле важно то, что у вас никогда не было A
, за которым следует s
, за исключением того, что в текущем определении A
.
Мы
S -> A <EOF>
A -> (<id> | sA | dAd | z) (sA)*
Если у вас есть неоднозначная грамматика, но хотят, чтобы устранить неоднозначность, вопрос, чтобы спросить себя: Какой разобрать я хочу в неоднозначных случаях? Два ответа: «Оставайтесь в цикле как можно дольше». и «Выскочите из петли как можно скорее». (Другие ответы возможны, но вряд ли.)
«Пребывание в петлю как можно дольше»
Это JavaCC по умолчанию, поэтому нет необходимости менять грамматику. Это может вызвать предупреждение. Возможно, это можно будет подавить с LOOKAHEAD(<s>)
в начале цикла.
«Выход из цикла как можно скорее»
сделать две версии A
. A0
никогда не следует s
. A1
всегда следует за s
. (На самом деле следует возможным первый s
, поэтому (sA)*
часть не хотела. Этот выбор соответствует выручая из цикла как можно скорее.)
S -> A0 <EOF>
A0 -> (<id> | sA0 | dA0d | z) [ s (A1s)* A0 ]
A1 -> <id> | sA1 | dA0d | z
Я совершенно уверен, что это однозначное и что A0
определяет тот же язык, что и A
.Это не LL (1), и JavaCC выдаст предупреждение, которое должно быть учтено.
Чтобы сделать его пригодным для JavaCC, мы можем добавить синтаксическое представление LOOKAHEAD(A1 <s>)
к началу цикла.
Удалите обозначение звезды Kleene ('(sB) *'), введя новый нетерминал. В остальном попробуйте включить косвенную левую рекурсию в прямую левую рекурсию путем замены. –
Что может вас смутить, так это то, что эта грамматика неоднозначна (действительно, из-за 'A -> B (sB) *,' B -> sB' и 'B -> A'), что делает невозможным построение LL.Возможно, можно построить грамматику LL (1) для * языка *, которую описывает эта грамматика, но это другой вопрос. –