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

Материал из Викиконспекты
Перейти к: навигация, поиск
Строка 7: Строка 7:
  
 
==Алгоритм устранения левой рекурсии==
 
==Алгоритм устранения левой рекурсии==
Приведем алгоритм, позволяющий для к.с. грамматики '''без ε-правил''' построить эквивалентную ей к.с. грамматику (без ε-правил), не содержащую левой рекурсии.
+
Приведем алгоритм, позволяющий для к.с. грамматики ''без <tex> \varepsilon </tex>-правил'' построить эквивалентную ей к.с. грамматику (без <tex> \varepsilon </tex>-правил), не содержащую левой рекурсии.
  
 
Для произвольной грамматики <tex>\Gamma</tex> левую рекурсию можно устранить следующим образом:
 
Для произвольной грамматики <tex>\Gamma</tex> левую рекурсию можно устранить следующим образом:
#Воспользоваться [[Удаление_eps-правил_из_грамматики | алгоритмом удаления &epsilon;-правил]]. Получим грамматику без &epsilon;-правил для языка <tex>L(\Gamma) \setminus \lbrace \epsilon \rbrace</tex>
+
#Воспользоваться [[Удаление_eps-правил_из_грамматики | алгоритмом удаления <tex> \varepsilon </tex>-правил]]. Получим грамматику без <tex> \varepsilon </tex>-правил для языка <tex>L(\Gamma) \setminus \lbrace \epsilon \rbrace</tex>
 
#Воспользоваться алгоритмом для новой грамматики
 
#Воспользоваться алгоритмом для новой грамматики
 
#Если <tex>\epsilon</tex> присутствовал в языке исходной грамматики, добавить новый начальный символ <tex>S'</tex> и правила <tex>S' \rightarrow S \, | \, \epsilon </tex>
 
#Если <tex>\epsilon</tex> присутствовал в языке исходной грамматики, добавить новый начальный символ <tex>S'</tex> и правила <tex>S' \rightarrow S \, | \, \epsilon </tex>
Строка 17: Строка 17:
 
Опишем процедуру, устраняющую все правила вида <tex>A \rightarrow A\alpha</tex> для фиксированного нетерминала <tex>A</tex>.
 
Опишем процедуру, устраняющую все правила вида <tex>A \rightarrow A\alpha</tex> для фиксированного нетерминала <tex>A</tex>.
  
Запишем все правила вывода из <tex>A</tex> в виде
+
<ol>
 +
<li>Запишем все правила вывода из <tex>A</tex> в виде
 +
<tex>A \rightarrow A\alpha_1\,|\,\ldots\,|\,A\alpha_n\,|\,\beta_1\,|\,\ldots\,|\,\beta_m </tex>, где
 +
<ul>
 +
<li> <tex>\alpha</tex> - непустая последовательность терминалов и нетерминалов (<tex>\alpha \ne \epsilon </tex>)</li>
 +
<li> <tex>\beta</tex> - непустая последовательность терминалов и нетерминалов, не начинающаяся с <tex>A</tex>.</li>
 +
</ul>
 +
<li>Заменим правила вывода из <tex>A</tex> на: <tex>A \rightarrow \beta_1A^\prime\, |\, \ldots\,  |\,  \beta_mA^\prime \,|\, \beta_1 \,|\, \ldots \,|\, \beta_m</tex> </li>
  
<tex>A \rightarrow A\alpha_1\,|\,\ldots\,|\,A\alpha_n\,|\,\beta_1\,|\,\ldots\,|\,\beta_m </tex>
+
<li>И создадим новый нетерминал <tex>A^\prime \rightarrow \alpha_1A^\prime\,  |\,  \ldots\, |\, \alpha_nA^\prime | \alpha_1\,  |\,  \ldots\, |\, \alpha_n</tex> </li>
 
+
</li>
где
+
</ol>
* <tex>\alpha</tex> - непустая последовательность терминалов и нетерминалов (<tex>\alpha \ne \epsilon </tex>)
 
* <tex>\beta</tex> - непустая последовательность терминалов и нетерминалов, не начинающаяся с <tex>A</tex>.
 
 
 
Заменим правила вывода из <tex>A</tex> на:
 
 
 
<tex>A \rightarrow \beta_1A^\prime\, |\, \ldots\,  |\,  \beta_mA^\prime \,|\, \beta_1 \,|\, \ldots \,|\, \beta_m</tex>
 
 
 
И создадим новый нетерминал
 
 
 
<tex>A^\prime \rightarrow \alpha_1A^\prime\,  |\,  \ldots\, |\, \alpha_nA^\prime | \alpha_1\,  |\,  \ldots\, |\, \alpha_n</tex>
 
  
 
===Устранение произвольной левой рекурсии===
 
===Устранение произвольной левой рекурсии===

Версия 03:21, 27 ноября 2011

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


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


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

Приведем алгоритм, позволяющий для к.с. грамматики без [math] \varepsilon [/math]-правил построить эквивалентную ей к.с. грамматику (без [math] \varepsilon [/math]-правил), не содержащую левой рекурсии.

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

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

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

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

  1. Запишем все правила вывода из [math]A[/math] в виде [math]A \rightarrow A\alpha_1\,|\,\ldots\,|\,A\alpha_n\,|\,\beta_1\,|\,\ldots\,|\,\beta_m [/math], где
    • [math]\alpha[/math] - непустая последовательность терминалов и нетерминалов ([math]\alpha \ne \epsilon [/math])
    • [math]\beta[/math] - непустая последовательность терминалов и нетерминалов, не начинающаяся с [math]A[/math].
  2. Заменим правила вывода из [math]A[/math] на: [math]A \rightarrow \beta_1A^\prime\, |\, \ldots\, |\, \beta_mA^\prime \,|\, \beta_1 \,|\, \ldots \,|\, \beta_m[/math]
  3. И создадим новый нетерминал [math]A^\prime \rightarrow \alpha_1A^\prime\, |\, \ldots\, |\, \alpha_nA^\prime | \alpha_1\, |\, \ldots\, |\, \alpha_n[/math]

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

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

for i = 1 to n {
  for j = 1 to i – 1 {
    рассмотреть все правила вывода из [math]A_j[/math]
    [math]A_j \rightarrow \delta_1 | \ldots | \delta_k[/math]
    заменить каждое правило [math]A_i \rightarrow A_j \gamma[/math] на
    [math]A_i \rightarrow \delta_1\gamma | \ldots | \delta_k\gamma[/math]
  }
  устранить непосредственную левую рекурсию для [math]A_i[/math]
}

Инвариант: после [math]j[/math] итераций внутреннего цикла для [math]i[/math]

  • для [math]k \lt i[/math] правые части правил вывода из [math]A_k[/math] не начинаются с [math]A_1, A_2, \ldots , A_k[/math]
  • правые части правил вывода из [math]A_i[/math] не начинаются с [math]A_1, A_2, \ldots , A_j[/math]
  • правые части правил вывода не начинаются с добавленных алгоритмом нетерминалов [math]A_k ^{\prime}[/math]
  • грамматика не содержит ε-правил

(проверяется индукцией по парам [math](i,j)[/math])

Таким образом, после применения алгоритма все правила вывода имеют вид

  • [math]A \rightarrow c \alpha [/math], где [math]c[/math] - терминал, [math]A[/math] - произвольный нетерминал
  • [math]A_i \rightarrow A_j \alpha [/math], где [math]i \lt j[/math], [math]A_i , A_j[/math] - нетерминалы из исходной грамматики
  • [math]A_i^{\prime} \rightarrow A_j \alpha [/math], где [math]A_i^{\prime}[/math] - новый нетерминал, [math]A_j[/math] - нетерминал из исходной грамматики

Если теперь перенумеровать нетерминалы, сохранив порядок для старых и присвоив всем новым меньшие номера, то все правила будут иметь вид

  • [math]B_i \rightarrow c \alpha [/math], где [math]c[/math] - терминал
  • [math]B_i \rightarrow B_j \alpha [/math], где [math]i \lt j[/math]

Литература

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