Устранение левой рекурсии — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Устранение непосредственной левой рекурсии)
(Алгоритм устранения произвольной левой рекурсии)
Строка 32: Строка 32:
 
  for все нетерминалы <tex>A_i</tex>  
 
  for все нетерминалы <tex>A_i</tex>  
 
   for все нетерминалы <tex>A_j</tex>, такие, что <tex> 1 \leq j < i </tex> и  
 
   for все нетерминалы <tex>A_j</tex>, такие, что <tex> 1 \leq j < i </tex> и  
     рассмотреть все правила вывода из <tex>A_j</tex>: <tex>A_j \Rightarrow \delta_1 | \ldots | \delta_k</tex>.
+
     рассмотреть все правила вывода из <tex>A_j</tex>: <tex>A_j \to \delta_1 | \ldots | \delta_k</tex>.
     заменить каждое правило <tex>A_i \Rightarrow A_j \gamma</tex> на <tex>A_i \Rightarrow \delta_1\gamma | \ldots | \delta_k\gamma</tex>.
+
     заменить каждое правило <tex>A_i \to A_j \gamma</tex> на <tex>A_i \to \delta_1\gamma | \ldots | \delta_k\gamma</tex>.
 
   устранить непосредственную левую рекурсию для <tex>A_i</tex>.
 
   устранить непосредственную левую рекурсию для <tex>A_i</tex>.
 
</div>
 
</div>
  
После <tex>i</tex> итерации внешнего цикла в любой продукции вида <tex> A_k \Rightarrow A_l\alpha </tex> где <tex> i > k </tex> будет <tex> l > k</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>.
В результате после каждой итерации растет нижний предел m всех продукций вида <tex>A_i \Rightarrow A_m\alpha</tex> до тех пор, пока не будет достигнуто <tex>m \geq i</tex>. Затем из <tex>A_i</tex> устраняется непосредственная левая рекурсия и достигается m > i.
+
Очевидно, что одна итерация алгоритма не меняет язык, а значит язык получившейся в итоге грамматики совпадает с исходным.
 +
 
 +
 
 +
Алгоритм не работает для грамматик с <tex>\epsilon</tex> переходами и с грамматиками имеющими <tex>A \Rightarrow^+ A</tex>.
  
 
==Пример==
 
==Пример==

Версия 20:20, 7 января 2013

Определение:
Говорят, что контекстно-свободная (КС) грамматика [math]\Gamma[/math] содержит непосредственную левую рекурсию, если она содержит правило вида [math]A \Rightarrow A\alpha[/math].


Определение:
Говорят, что КС-грамматика [math]\Gamma[/math] содержит левую рекурсию (left recursion), если в ней существует вывод вида [math]A \Rightarrow^* A\alpha[/math].


Методы нисходящего разбора (top-down parsers) не в состоянии работать с леворекурсивными грамматиками. Проблема в том, что продукция вида [math]A \Rightarrow^* A\alpha[/math] может применяться бесконечно долго, так и не выработав некий терминальный символ, который можно было бы сравнить со строкой. Поэтому требуется преобразование грамматики, которое бы устранило левую рекурсию.


Устранение непосредственной левой рекурсии

Опишем процедуру, устраняющую все правила вида [math]A \to A\alpha[/math], для фиксированного нетерминала [math]A[/math].

  1. Запишем все правила вывода из [math]A[/math] в виде: [math]A \to A\alpha_1\,|\,\ldots\,|\,A\alpha_n\,|\,\beta_1\,|\,\ldots\,|\,\beta_m [/math], где
    • [math]\alpha[/math] — непустая последовательность терминалов и нетерминалов ([math]\alpha \nrightarrow \varepsilon [/math]);
    • [math]\beta[/math] — непустая последовательность терминалов и нетерминалов, не начинающаяся с [math]A[/math].
  2. Заменим правила вывода из [math]A[/math] на [math]A \to\beta_1A^\prime\, |\, \ldots\, |\, \beta_mA^\prime \,|\, \beta_1 \,|\, \ldots \,|\, \beta_m[/math].
  3. Создадим новый нетерминал [math]{A^\prime} \to \alpha_1{A^\prime}, |\, \ldots\, |\, \alpha_n{A^\prime} | \alpha_1\, |\, \ldots\, |\, \alpha_n[/math].

Изначально нетерминал [math]A[/math] порождает сроки вида [math]\beta\alpha_{i0}\alpha_{i1} \ldots \alpha_{ik}[/math]. В новой грамматике нетерминал [math]A[/math] порождает [math]\beta{A^\prime}[/math], а [math]A^\prime[/math] порождает строки вида [math]\alpha_{i0}\alpha_{i1} \ldots \alpha_{ik}[/math]. Из этого очевидно, что изначальная грамматика эквивалентна новой.

Алгоритм устранения произвольной левой рекурсии

Пусть [math]N = \lbrace A_1, A_2, \ldots , A_n \rbrace[/math] — множество всех нетерминалов.

for все нетерминалы [math]A_i[/math] 
  for все нетерминалы [math]A_j[/math], такие, что [math] 1 \leq j \lt  i [/math] и 
    рассмотреть все правила вывода из [math]A_j[/math]: [math]A_j \to \delta_1 | \ldots | \delta_k[/math].
    заменить каждое правило [math]A_i \to A_j \gamma[/math] на [math]A_i \to \delta_1\gamma | \ldots | \delta_k\gamma[/math].
  устранить непосредственную левую рекурсию для [math]A_i[/math].

На [math]i[/math] итерации внешнего цикла все правила вида [math]A_i \to A_j \gamma[/math] где [math] j \lt i [/math] заменяются на [math]A_i \to \delta_1\gamma | \ldots | \delta_k\gamma[/math] где [math]A_j \to \delta_1 | \ldots | \delta_k[/math]. Таким образом остается только избавиться от непосредственной рекурсии для [math]A_i[/math]. Очевидно, что одна итерация алгоритма не меняет язык, а значит язык получившейся в итоге грамматики совпадает с исходным.


Алгоритм не работает для грамматик с [math]\epsilon[/math] переходами и с грамматиками имеющими [math]A \Rightarrow^+ A[/math].

Пример

Дана грамматика

[math]A \Rightarrow S\alpha [/math]

[math]S \Rightarrow S\beta | A\gamma | b[/math]

Среди продукций [math]A[/math] непосредственной рекурсии нет, поэтому во время первой итерации внешнего цикла ничего не происходит. Во время второй итерации внешнего цикла продукция [math] S \Rightarrow A\gamma [/math] переходит в [math] S \Rightarrow S\alpha\gamma [/math].

Грамматика имеет вид

[math]A \Rightarrow S\alpha [/math]

[math]S \Rightarrow {S}{\beta} | {S}{\alpha}{\gamma} | \beta[/math]

Устраняем левую рекурсию для [math]S[/math]

[math] S \Rightarrow \beta{S_1}[/math]

[math] {S_1} \Rightarrow \beta{S_1} | \alpha\gamma{S_1}[/math]



Для произвольной грамматики [math]\Gamma[/math] левую рекурсию можно устранить следующим образом:

  1. Воспользуемся алгоритмом удаления [math] \varepsilon [/math]-правил. Получим грамматику без [math] \varepsilon [/math]-правил для языка [math]L(\Gamma) \setminus \lbrace \varepsilon \rbrace[/math].
  2. Воспользуемся алгоритмом устранения произвольной левой рекурсии.
  3. Если [math]\varepsilon[/math] присутствовал в языке исходной грамматики, добавим новый начальный символ [math]S'[/math] и правила [math]S' \rightarrow S \, | \, \varepsilon [/math].

Литература

  • Хопкрофт Д., Мотвани Р., Ульман Д.Введение в теорию автоматов, языков и вычислений, 2-е изд. : Пер. с англ. — Москва, Издательский дом «Вильямс», 2002. — 528 с. : ISBN 5-8459-0261-4 (рус.)
  • Robert C. MooreRemoving Left Recursion from Context-Free Grammars