Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
УМК по СПО.doc
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
1.79 Mб
Скачать

6.5.Удаление левой рекурсии

Основная трудность при использовании предсказывающего анализа - это нахождение такой грамматики для входного языка, по которой можно построить таблицу анализа с однозначно определенными входами. Иногда с помощью некоторых простых преобразований грамматику, не являющуюся LL(1), можно привести к эквивалентной LL(1)-грамматике. Среди этих преобразований наиболее эффективными являются левая факторизация и удаление левой рекурсии. Здесь необходимо сделать два замечания. Во-первых, не всякая грамматика после этих преобразований становится LL(1), и, во-вторых, после таких преобразований получающаяся грамматика может стать менее понимаемой.

Непосредственную левую рекурсию, т.е. рекурсию вида AA, можно удалить следующим способом. Сначала группируем A-правила:

AA1 | A2 | … | Am | 1 | 2 | … | n |

где никакая из строк i не начинается с A. Затем заменяем этот набор правил на

A1A’ | 2A’ | … | nA’

A’1A’ | 2 A’ | … | m A’ | 

где A' - новый нетерминал. Из нетерминала A можно вывести те же цепочки, что и раньше, но теперь нет левой рекурсии. С помощью этой процедуры удаляются все непосредственные левые рекурсии, но не удаляется левая рекурсия, включающая два или более шага. Приведенный ниже алгоритм позволяет удалить все левые рекурсии из грамматики:

Вход. КС-грамматика G без -правил (правил вида A ).

Выход. КС-грамматика G' без левой рекурсии, эквивалентная G.

Метод. Выполнить шаги 1 и 2.

Упорядочить нетерминалы грамматики G в произвольном порядке.

Выполнить следующую процедуру:

for (i=1;i<=n;i++){

for (j=1;j<=i-1;j++){

пусть Aj 1 | 2 | ... | k - все текущие правила для Aj;

заменить все правила вида Ai Aj

на правила Ai 1 | 2 | ... | k;

}

удалить правила вида Ai Ai;

удалить непосредственную левую рекурсию в правилах для Ai;

}

После (i - 1)-й итерации внешнего цикла на шаге 2 для любого правила вида AkAs, где k < i, выполняется s > k. В результате на следующей итерации (по i) внутренний цикл (по j) последовательно увеличивает нижнюю границу по m в любом правиле AiAm, пока не будет m≥i. Затем, после удаления непосредственной левой рекурсии для Ai-правил, m становится больше i.

Получающаяся грамматика без левой рекурсии может иметь -правила.

6.6.Алгоритм разбора для ll(1)-грамматик

Для LL(l)- грамматик алгоритм работы распознавателя предельно прост. Он за­ключается всего в двух условиях, проверяемых на шаге выбора альтернати­вы. Исходными данными для этих условий являются символ aVT, обозреваемый считывающей головкой МП-автомата (текущий символ входной цепочки), и символ AVN, находящийся на верхушке стека автомата2.

Эти условия можно сформулировать так:

  • необходимо выбрать в качестве альтернативы правило Ах, если aFIRST(l,x);

  • необходимо выбрать в качестве альтернативы правило А, если aFOLLOW(l,A).

Если ни одно из этих условий не выполняется (нет соответствующих правил), то цепочка не принадлежит заданному языку и МП-автомат не принимает ее (алго­ритм должен сигнализировать об ошибке).

Работа автомата на шаге «выброса» остается без изменений.

Кроме того, чтобы убедиться, является ли заданная грамматика G(VT,VN,P,S) LL(1)-грамматикой, необходимо и достаточно проверить следующее условие: для каждого символа AVN, для которого в грамматике существует более одного правила вида А1||2|...|n, должно выполняться требование

FIRST(l, i,FOLLOW(l,A))  FIRST(l, jFOLLOW(l,A)) = 

 ij, n  i  0 n  j  0.

Очевидно, что если для символа AVN отсутствует правило вида А, то соглас­но этому требованию все множества FIRST(1,1), FIRST(l, 2), ..., FIRST(l, n) должны попарно не пересекаться, если же присутствует правило А, то они не должны также пересекаться со множеством FOLLOW(l,A). Отсюда видно, что LL(1)-грамматика не может содержать для одного и того же нетерминального символа AVN двух правил, начинающихся с одного и того же терминального символа.

Условие, накладываемое на правила LL(1)-грамматики, является довольно жест­ким. Очень немногие реальные грамматики могут быть отнесены к классу LI.(l)-грамматик. Например, даже довольно простая грамматика G({a},{S}, {Sa|aS}, S) не удовлетворяет этому условию (хотя она является LL(2)-грамматикой и даже регулярной праволинейной грамматикой).

Иногда удастся преобразовать правила грамматики так, чтобы они удовлетворяли требованию LL(1)-грамматик. Например, приведенная выше грамматика может быть преобразована к виду G'({a},{S,A}, (SaA, A|S}, S)3. В такой форме она уже является LL(1)-грамматикой (это можно проверить). Но формального метода преобразовать произвольную КС-грамматику к виду LL(1)-грамматики или убедиться в том, что такое преобразование невозможно, не существует. Пер­вое преобразование правил грамматики, которое можно рекомендовать, — устра­нение левой рекурсии4. Второе преобразование носит название «левая фактори­зация», оно уже было упомянуто выше при знакомстве с методом рекурсивного спуска. Это преобразование заключается в следующем: если для символа А VN существует ряд правил

Аа1|а2|...|аn|1|2|…|m , i: i(VTVN)', j: j (VTVN)', aVT

и ни одна цепочка символов j не начинается с символа а, тогда во множество не­терминальных символов грамматики VN добавляется новый символ А', а прави­ла для А и А' записываются следующим образом: АаА'|1,|2|...|m и A'1|2|...|n. Левую факторизацию можно применять к правилам грамматики несколько раз с целью исключить для каждого нетерминального символа правила, начинающие­ся с одних и тех же терминальных символов. Однако применение этих двух пре­образований отнюдь не гарантирует, что произвольную КС-грамматику удастся привести к виду LL(1)-грамматики.

Для того чтобы запрограммировать работу МП-автомата, выполняющего разбор входных цепочек символов языка, заданного LL(1)-грамматикой, надо научить­ся строить множества символов FIRST(l,x) и FOLLOW(1,A). Для множества FIRST(l,x) все очевидно, если цепочка х начинается с терминального симво­ла, если же она начинается с нетерминального символа В (х = By, x (VTVN)*, y (VTVN)'), то FIRST(l.x) = FIRST(1,B). Следовательно, для LL(1)-грамматик остается только найти алгоритм построения множеств FIRST(1,B) и FOLLOW(l,A) для всех нетерминальных символов A,BVN.

Исходными данными для этих алгоритмов служат правила грамматики.