Контекстовільні мови. Дерева розбору. Неоднозначні граматики, страница 3

            Ліворекурсивна пара продукцій A→Aα | β може бути замінена неліворекурсивними продукціями:

            A → βA' ,

            A'→ αA'| ε

без зміни кількості рядків, що породжуються з A. Цього правила достатньо для багатьох граматик.

            Розглянемо таку граматику для арифметичних виразів:

            EE+T | T ,

            FT*F | F ,

            F(E) | id .

            Після видалення рекурсії з продукцій для Eі T одержимо:

            ETE' ,        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ліворекурсивний, оскільки SAaSda, але ця рекурсія не є безпосередньою.

            Наведений  нижче алгоритм дозволяє  видалити   всі  ліві   рекурсії  з граматики.

            Алгоритм 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>і.

              Розглянутий алгоритм гарантовано працює з граматиками, які не мають циклів (породжень типу AA) і ε-продукцій (продукцій типу 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. Таким чином, лівофакторизовані продукції наберуть вигляду: