Дан контекстно‑свободный грамматический фрагмент, который порождает амбигуитет при разборе арифметических выражений с операциями + и * без приоритета: S -> S + S | S * S | (S) | id — преобразуйте грамматику в эквивалентную без неоднозначности, приведите алгоритм парсинга и обсудите, в каких случаях необходима более мощная модель (например, LR vs LL)

6 Ноя в 08:38
4 +4
0
Ответы
1
Коротко и по делу.
1) Преобразование грамматики в однозначную (задать приоритет и ассоциативность: ∗* выше, обе — левосторонние):
E→E+T∣TE \to E + T \mid TEE+TT T→T∗F∣FT \to T * F \mid FTTFF F→(E)∣idF \to (E) \mid idF(E)id
Эта грамматика однозначна: она задаёт приоритет ∗* над +++ и лево‑ассоциативность обоих операторов.
Для удобства парсинга верхнего уровня (убрать левую рекурсию) эквивалентная LL‑форма:
E→T E′E \to T\;E'ETE E′→+ T E′∣εE' \to +\;T\;E' \mid \varepsilonE+TEε T→F T′T \to F\;T'TFT T′→∗ F T′∣εT' \to *\;F\;T' \mid \varepsilonTFTε F→(E)∣idF \to (E) \mid idF(E)id
2) Алгоритм парсинга
- Для LL(1) (рекурсивный спуск, predictive parsing) можно реализовать функции:
parseE():
parseT()
parseEprime()
parseEprime():
if (current == '+') { consume('+'); parseT(); parseEprime(); }
else return
parseT():
parseF()
parseTprime()
parseTprime():
if (current == '*') { consume('*'); parseF(); parseTprime(); }
else return
parseF():
if (current == '(') { consume('('); parseE(); consume(')'); }
else if (current == id) consume(id);
else error
(Лексемы и ошибки — стандартно.)
- Для LR‑парсеров (LR(0)/SLR(1)/LALR(1)/LR(1)) можно взять исходную (лево-рекурсивную) форму
E→E+E∣E∗E∣(E)∣idE \to E + E \mid E * E \mid (E) \mid idEE+EEE(E)id но лучше использовать однозначную с разделением уровней (первые правила выше). LR‑парсер автоматически управляет левой рекурсией и ассоциативностью; при конфликтах применяют декларации приоритета/ассоциативности или переписывают грамматику.
3) Когда нужна более мощная модель (LR vs LL)
- LL(1) (рекурсивный спуск) прост и прозрачен, но требует:
- отсутствие левой рекурсии (надо её устранять),
- отсутствие неоднозначных выборов без достаточного lookahead,
- грамматика должна быть LL(1) (или преобразуема в неё).
- LR (особенно LALR(1) / LR(1)) мощнее:
- поддерживает левую рекурсию напрямую,
- допускает больше конструкций (непрямое/жёсткое факторирование, более сложные разветвления),
- лучше справляется с грамматиками реальных языков (C, Java) и с конфликтами, которые трудно устранить для LL(1).
Примеры, когда нужен LR:
- грамматика не может быть преобразована в LL(1) без существенной потери структуры,
- конструкции с «развешенным» контекстом (dangling else — хотя его часто решают приоритетом),
- необходимость точного анализа без ручного переписывания множества правил.
Вывод: для арифметики достаточно LL(1)‑грамы (представленной выше) и рекурсивного спуска; для сложных языков и грамматик с леворекурсией/конфликтами предпочтителен LR‑парсер.
6 Ноя в 09:04
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир