Устранение левой рекурсии — различия между версиями
Shagal (обсуждение | вклад) (Отмена правки 28697 участника Shagal (обсуждение)) |
Shagal (обсуждение | вклад) |
||
Строка 1: | Строка 1: | ||
{{Определение | {{Определение | ||
− | |definition=Говорят, что [[Контекстно-свободные грамматики, вывод, лево- и правосторонний вывод, дерево разбора|контекстно-свободная(КС) грамматика]] <tex>\Gamma</tex> содержит '''непосредственную левую рекурсию''', если она содержит правило вида <tex>A \ | + | |definition=Говорят, что [[Контекстно-свободные грамматики, вывод, лево- и правосторонний вывод, дерево разбора|контекстно-свободная(КС) грамматика]] <tex>\Gamma</tex> содержит '''непосредственную левую рекурсию''', если она содержит правило вида <tex>A \Rightarrow A\alpha</tex>. |
}} | }} | ||
{{Определение | {{Определение | ||
Строка 6: | Строка 6: | ||
}} | }} | ||
− | Методы нисходящего разбора(top | + | Методы нисходящего разбора(top-down parsers) не в состоянии работать с леворекурсивными грамматиками. Проблема в том, что продукция вида <tex>A \Rightarrow^* A\alpha</tex> может применяться бесконечно долго, так и не выработав некий терминальный символ, который можно было бы сравнить со строкой. Поэтому требуется преобразование грамматики, которое бы устранило левую рекурсию. |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
==Устранение непосредственной левой рекурсии== | ==Устранение непосредственной левой рекурсии== | ||
Строка 32: | Строка 25: | ||
</ol> | </ol> | ||
− | + | Нетерминал A порождает те же строки, что и ранее, но без левой рекурсии. Эта процедура устраняет все непосредственные рекурсии из продукций для A, при условии, что ни одна строка <tex>\alpha_i</tex> не является <tex>\epsilon</tex>. | |
− | |||
− | |||
− | + | ==Алгоритм устранения произвольной левой рекурсии== | |
− | + | Пусть <tex>N = \lbrace A_1, A_2, \ldots , A_n \rbrace</tex> {{---}} множество всех нетерминалов. | |
− | |||
− | |||
− | == | ||
− | Пусть <tex>N = \lbrace A_1, A_2, \ldots , A_n \rbrace</tex> {{---}} | ||
<div> | <div> | ||
for все нетерминалы <tex>A_i</tex> | for все нетерминалы <tex>A_i</tex> | ||
Строка 60: | Строка 47: | ||
*<tex>B_i \rightarrow B_j \alpha </tex>, где <tex>i < j</tex>. | *<tex>B_i \rightarrow B_j \alpha </tex>, где <tex>i < j</tex>. | ||
− | + | После <tex>i^ой</tex> итерации внешнего цикла в любой продукции вида <tex>A_k \rightarrow A_l\alpha<tex> где </tex> i > k будет l > k</tex>. | |
+ | В результате после каждой итерации растет нижний предел m, таких, что существует продукция вида <tex>A_i \rightarrow A_m\alpha</tex> до тех пор, пока не будет достигнуто <tex>m \geq i</tex>. Затем из <tex>A_i</tex> устраняется непосредственная левая рекурсия и достигается m > i. | ||
+ | |||
+ | |||
Для произвольной грамматики <tex>\Gamma</tex> левую рекурсию можно устранить следующим образом: | Для произвольной грамматики <tex>\Gamma</tex> левую рекурсию можно устранить следующим образом: |
Версия 11:50, 7 января 2013
Определение: |
Говорят, что контекстно-свободная(КС) грамматика содержит непосредственную левую рекурсию, если она содержит правило вида . |
Определение: |
Говорят, что КС-грамматика | содержит левую рекурсию(left recursion), если в ней существует вывод вида .
Методы нисходящего разбора(top-down parsers) не в состоянии работать с леворекурсивными грамматиками. Проблема в том, что продукция вида может применяться бесконечно долго, так и не выработав некий терминальный символ, который можно было бы сравнить со строкой. Поэтому требуется преобразование грамматики, которое бы устранило левую рекурсию.
Устранение непосредственной левой рекурсии
Опишем процедуру, устраняющую все правила вида
, для фиксированного нетерминала .- Запишем все правила вывода из
- — непустая последовательность терминалов и нетерминалов ( );
- — непустая последовательность терминалов и нетерминалов, не начинающаяся с .
в виде:
, где
- Заменим правила вывода из на .
- Создадим новый нетерминал .
Нетерминал A порождает те же строки, что и ранее, но без левой рекурсии. Эта процедура устраняет все непосредственные рекурсии из продукций для A, при условии, что ни одна строка
не является .
Алгоритм устранения произвольной левой рекурсии
Пусть
— множество всех нетерминалов.for все нетерминалыfor все нетерминалы , такие, что и рассмотреть все правила вывода из : . заменить каждое правило на . устранить непосредственную левую рекурсию для .
Таким образом, после применения алгоритма все правила вывода имеют вид:
- , где — терминал, — произвольный нетерминал;
- , где , — нетерминалы из исходной грамматики;
- , где — новый нетерминал, — нетерминал из исходной грамматики.
Если теперь перенумеровать нетерминалы, сохранив порядок для старых и присвоив всем новым меньшие номера, то все правила будут иметь вид:
- , где — терминал;
- , где .
После
итерации внешнего цикла в любой продукции вида i > k будет l > k</tex>. В результате после каждой итерации растет нижний предел m, таких, что существует продукция вида до тех пор, пока не будет достигнуто . Затем из устраняется непосредственная левая рекурсия и достигается m > i.
Для произвольной грамматики
левую рекурсию можно устранить следующим образом:- Воспользуемся алгоритмом удаления . Получим грамматику без -правил -правил для языка .
- Воспользуемся алгоритмом устранения произвольной левой рекурсии.
- Если присутствовал в языке исходной грамматики, добавим новый начальный символ и правила .
Литература
- Хопкрофт Д., Мотвани Р., Ульман Д. — Введение в теорию автоматов, языков и вычислений, 2-е изд. : Пер. с англ. — Москва, Издательский дом «Вильямс», 2002. — 528 с. : ISBN 5-8459-0261-4 (рус.)
- Robert C. Moore — Removing Left Recursion from Context-Free Grammars