Устранение левой рекурсии — различия между версиями
Shagal (обсуждение | вклад) (→Устранение непосредственной левой рекурсии) |
Shagal (обсуждение | вклад) |
||
Строка 45: | Строка 45: | ||
==Алгоритм устранения произвольной левой рекурсии== | ==Алгоритм устранения произвольной левой рекурсии== | ||
+ | Воспользуемся [[Удаление_eps-правил_из_грамматики | алгоритмом удаления <tex> \varepsilon </tex>-правил]]. Получим грамматику без <tex> \varepsilon </tex>-правил для языка <tex>L(\Gamma) \setminus \lbrace \epsilon \rbrace</tex>. | ||
Упорядочим нетерминалы и будим добиваться того, чтобы не было правил вида <tex>A_i \to A_j\alpha</tex>, где <tex>j < i</tex>. | Упорядочим нетерминалы и будим добиваться того, чтобы не было правил вида <tex>A_i \to A_j\alpha</tex>, где <tex>j < i</tex>. | ||
Строка 59: | Строка 60: | ||
</div> | </div> | ||
+ | Если <tex>\varepsilon</tex> присутствовал в языке исходной грамматики, добавим новый начальный символ <tex>S'</tex> и правила <tex>S' \to S \, | \, \varepsilon </tex>. | ||
На <tex>i</tex> итерации внешнего цикла все правила вида <tex>A_i \to A_j \gamma</tex> где <tex> j < i </tex> заменяются на <tex>A_i \to \delta_1\gamma | \ldots | \delta_k\gamma</tex> где <tex>A_j \to \delta_1 | \ldots | \delta_k</tex>. Таким образом остается только избавиться от непосредственной рекурсии для <tex>A_i</tex>. | На <tex>i</tex> итерации внешнего цикла все правила вида <tex>A_i \to A_j \gamma</tex> где <tex> j < i </tex> заменяются на <tex>A_i \to \delta_1\gamma | \ldots | \delta_k\gamma</tex> где <tex>A_j \to \delta_1 | \ldots | \delta_k</tex>. Таким образом остается только избавиться от непосредственной рекурсии для <tex>A_i</tex>. |
Версия 12:46, 8 января 2013
Определение: |
Говорят, что контекстно-свободная (КС) грамматика содержит непосредственную левую рекурсию (direct left recursion), если она содержит правило вида . |
Определение: |
Говорят, что КС-грамматика | содержит левую рекурсию (left recursion), если в ней существует вывод вида .
Методы нисходящего разбора (top-down parsers) не в состоянии работать с леворекурсивными грамматиками. Проблема в том, что продукция вида может применяться бесконечно долго, так и не выработав некий терминальный символ, который можно было бы сравнить со строкой. Поэтому требуется преобразование грамматики, которое бы устранило левую рекурсию.
Содержание
Устранение непосредственной левой рекурсии
Опишем процедуру, устраняющую все правила вида
, для фиксированного нетерминала .- Запишем все правила вывода из
- — непустая последовательность терминалов и нетерминалов ( );
- — непустая последовательность терминалов и нетерминалов, не начинающаяся с .
в виде:
, где
- Заменим правила вывода из на .
- Создадим новый нетерминал .
Изначально нетерминал
порождает сроки вида . В новой грамматике нетерминал порождает , а порождает строки вида . Из этого очевидно, что изначальная грамматика эквивалентна новой.Пример
Есть непосредственная левая рекурсия
. Добавим нетерминал и добавим правила , .Новая грамматика:
В новой грамматике нет непосредственной левой рекурсии, но нетерминал
леворекурсивен, так как естьАлгоритм устранения произвольной левой рекурсии
Воспользуемся алгоритмом удаления . Получим грамматику без -правил -правил для языка .
Упорядочим нетерминалы и будим добиваться того, чтобы не было правил вида
, где . Можно заметить, что если данное условие выполняется для , то в грамматике не будет , а значит надо будет только устранить непосредственную левую рекурсию для .
Пусть — упорядоченное множество всех нетерминалов.
for все нетерминалыfor все нетерминалы , такие, что и рассмотреть все правила вывода из : . заменить каждое правило на . устранить непосредственную левую рекурсию для .
Если
присутствовал в языке исходной грамматики, добавим новый начальный символ и правила .На
итерации внешнего цикла все правила вида где заменяются на где . Таким образом остается только избавиться от непосредственной рекурсии для . Очевидно, что одна итерация алгоритма не меняет язык, а значит язык получившийся в итоге грамматики совпадает с исходным.Алгоритм не работает для грамматик с алгоритмом удаления . -правил
переходами и с грамматиками имеющими . Поэтому для произвольной грамматики необходимо сначала воспользоваться алгоритмомАсимптотика
Пусть
количество правил для нетерминала . Тогда итерация внешнего цикла будет выполняться за , что меньше чем , значит асимптотика алгоритма .Проблема этого алгоритма в том, что в зависимости от порядка нетерминалов в множестве размер грамматки может получиться экспоненциальным.
Пример грамматики для которой имеет значение порядок нетерминалов
для
Упорядочим множество нетерминалов по возрастанию индексов. Легко заметить, что правила для
будут представлять из себя все двоичные вектора длины , а значит размер грамматики будет экспоненциальным. Если упорядочить нетерминалы по убыванию в грамматике изменений не будет.Пример
Дана грамматика
Среди правил
непосредственной рекурсии нет, поэтому во время первой итерации внешнего цикла ничего не происходит. Во время второй итерации внешнего цикла правило переходит в .Грамматика имеет вид
Устраняем левую рекурсию для
Литература
- Хопкрофт Д., Мотвани Р., Ульман Д. — Введение в теорию автоматов, языков и вычислений, 2-е изд. : Пер. с англ. — Москва, Издательский дом «Вильямс», 2002. — 528 с. : ISBN 5-8459-0261-4 (рус.)
- Robert C. Moore — Removing Left Recursion from Context-Free Grammars