
3.3 Видалення лівої рекурсії
Граматика
ліворекурсивна, якщо в ній є нетермінал
A
такий, що існує породження A
Aα
для деякого рядка α. Ліворекурсивні
граматики не можуть аналізуватися
методами “зверху-вниз”,
тому необхідне видалення лівої рекурсії.
Ліворекурсивна пара продукцій A→Aα | β може бути замінена неліворекурсивними продукціями:
A → βA' ,
A' → αA' | ε
без зміни кількості рядків, що породжуються з A. Цього правила достатньо для багатьох граматик.
Розглянемо таку граматику для арифметичних виразів:
E → E+T | T ,
F → T*F | F ,
F → (E) | id .
Після видалення рекурсії з продукцій для E і T одержимо:
E → TE' , E’ → +TE' | ε ,
T → FT ' , T’ → *FT ' | ε , (3.1)
F → (E) | id .
Неважливо, яка загальна кількість A-продукцій. Ми можемо видалити безпосередню ліву рекурсію з них за допомогою такої технології. Спочатку групуємо A-продукції:
A → Aα1 | Aα2 | ... | Aαm | β1 | β 2 | .... | β n ,
де βi не починається з A. Потім заміняємо ці A-продукції на
A → β1A' | β 2A' | .... | β n A' ,
A'→ α1A' | α2A' | .... | αmA' | ε .
Нетермінал A породжує ті самі рядки, що і раніше, але тепер немає лівої рекурсії. За допомогою цієї процедури видаляються всі безпосередні ліві рекурсії, але не видаляється ліва рекурсія, що включає два або більше кроків породження. Розглянемо, наприклад, граматику:
S → Aa | b ;
A→ Ac | Sd | ε .
Нетермінал
S
ліворекурсивний,
оскільки
S
Aa
Sda,
але
ця рекурсія не є безпосередньою.
Наведений нижче алгоритм дозволяє видалити всі ліві рекурсії з граматики.
Алгоритм 3.1 Видалення лівої рекурсії
Крок 1 Упорядковуємо нетермінали в довільному порядку A1, A2, …, An.
Крок 2
for і:=1 to n do begin
for j:=1 to і -1 do begin
Замінити всі продукції вигляду Ai → Ajγ
продукціями Ai → δ1γ | δ2γ | ... | δkγ,
де Aj → δ1 | δ2 | ... | δk – всі поточні Aj-продукції
end
Видалити безпосередню ліву рекурсію серед
Ai- продукцій
end
Обґрунтування працездатності алгоритму базується на тому, що після і-1 ітерації зовнішнього циклу for кроку 2 для будь-якої продукції вигляду Ak→Al α, k < i, повинно бути l >k. У результаті на наступній ітерації внутрішнього циклу (за j) послідовно збільшується нижня границя m всіх продукцій A i→ Amα, поки не буде досягнуто m≥і. Потім, видаляючи безпосередню ліву рекурсію для Ai-продукцій, одержуємо m>і.
Розглянутий алгоритм гарантовано працює з граматиками, які не мають циклів (породжень типу A A) і ε-продукцій (продукцій типу A → ε). Як цикли, так і ε-продукції можуть бути вилучені попередньо.
3.4 Ліва факторизація
Ліва факторизація являє собою перетворення граматики у форму, придатну для предиктивного аналізу.
Основна ідея лівої факторизації полягає у тому, що, коли незрозуміло, яку з двох альтернативних продукцій потрібно використовувати для розгортання нетермінала A, потрібно переробити A-продукції так, щоб відкласти рішення доти, поки із вхідного потоку не буде прочитано достатньо символів для правильного вибору.
Наприклад, якщо є дві продукції
stmt → if expr then stmt else stmt
| if expr then stmt ,
то, після знаходження у вхідному потоці if, ми не можемо відразу вибрати ні одну з них. У загальному випадку, якщо A→αβ1 | αβ2 – дві A-продукції і вхідний потік починається з непорожнього рядка, виведеного з α, ми не можемо сказати, буде використовуватися перша чи друга продукція. Однак можна відкласти рішення, розширивши A до αA'. У цьому випадку, після розгляду вхідного потоку, що виводиться з α, ми працюємо з A', розширюючи його до β1 або до β2. Таким чином, лівофакторизовані продукції наберуть вигляду:
A → αА' ,
А' → β1 | β2 .
Алгоритм 3.2 Ліва факторизація граматики
Для кожного нетермінала A шукаємо найдовший префікс α, спільний для двох або більше його альтернатив. Якщо α ≠ ε, тобто існує нетривіальний спільний префікс, заміняємо всі A-продукції A → αβ1 | αβ 2 | ... | αβ n | γ, де γ представляє всі альтернативи, які не починаються з α, продукціями:
A → αА' | γ ,
А' → β1 | β2 | ... | βn .
Тут А' – новий нетермінал. Повторно застосовуємо це перетворення, поки ніякі дві альтернативи не будуть мати спільний префікс.
Розглянемо знову граматику умовних операторів:
S → iEtS | iEtSeS | a ,
E →b .
Тут i, t, e означають if, then, else, E і S відповідають expr і stmt.
Після лівої факторизації граматика набере такого вигляду:
S → iEtSS' | a ,
S' → eS | ε ,
E → b .