Конструирование таблицы предсказывающего анализатора

Для конструирования таблицы предсказывающего анализатора по грамматике G может быть использован алгоритм, основанный на следующей идее. Предположим, что A Конструирование таблицы предсказывающего анализатора - student2.ru Конструирование таблицы предсказывающего анализатора - student2.ru - правило вывода грамматики и a Конструирование таблицы предсказывающего анализатора - student2.ru FIRST( Конструирование таблицы предсказывающего анализатора - student2.ru ). Тогда анализатор делает развертку A по Конструирование таблицы предсказывающего анализатора - student2.ru , если входным символом является a. Трудность возникает, когда Конструирование таблицы предсказывающего анализатора - student2.ru = e или Конструирование таблицы предсказывающего анализатора - student2.ru Конструирование таблицы предсказывающего анализатора - student2.ru *e. В этом случае нужно развернуть A в Конструирование таблицы предсказывающего анализатора - student2.ru , если текущий входной символ принадлежит FOLLOW(A) или если достигнут $ и $ Конструирование таблицы предсказывающего анализатора - student2.ru FOLLOW(A).

Алгоритм 4.6. Построение таблицы предсказывающего анализатора.

Вход. КС-грамматика G = (N, T, P, S).

Выход. Таблица M[A, a] предсказывающего анализатора, A Конструирование таблицы предсказывающего анализатора - student2.ru N, a Конструирование таблицы предсказывающего анализатора - student2.ru T Конструирование таблицы предсказывающего анализатора - student2.ru {$}.

Метод. Для каждого правила вывода A Конструирование таблицы предсказывающего анализатора - student2.ru Конструирование таблицы предсказывающего анализатора - student2.ru грамматики выполнить шаги 1 и 2. После этого выполнить шаг 3.

  • Для каждого терминала a из FIRST( Конструирование таблицы предсказывающего анализатора - student2.ru ) добавить A Конструирование таблицы предсказывающего анализатора - student2.ru Конструирование таблицы предсказывающего анализатора - student2.ru к M[A, a].
  • Если e Конструирование таблицы предсказывающего анализатора - student2.ru FIRST( Конструирование таблицы предсказывающего анализатора - student2.ru ), добавить A Конструирование таблицы предсказывающего анализатора - student2.ru Конструирование таблицы предсказывающего анализатора - student2.ru к M[A, b] для каждого терминала b из FOLLOW(A). Кроме того, если e Конструирование таблицы предсказывающего анализатора - student2.ru FIRST( Конструирование таблицы предсказывающего анализатора - student2.ru ) и $ Конструирование таблицы предсказывающего анализатора - student2.ru FOLLOW(A), добавить A Конструирование таблицы предсказывающего анализатора - student2.ru Конструирование таблицы предсказывающего анализатора - student2.ru к M[A, $].
  • Положить все неопределенные входы равными «ошибка».

Пример 4.5. Применим алгоритм 4.6 к грамматике из примера 4.3. Поскольку FIRST(TE') = FIRST(T) = {(, id }, в соответствии с правилом вывода E Конструирование таблицы предсказывающего анализатора - student2.ru TE' входы M[E, ( ] и M[E, id ] становятся равными E Конструирование таблицы предсказывающего анализатора - student2.ru TE'.

В соответствии с правилом вывода E' Конструирование таблицы предсказывающего анализатора - student2.ru +TE' значение M[E', +] равно E' Конструирование таблицы предсказывающего анализатора - student2.ru +TE'. В соответствии с правилом вывода E' Конструирование таблицы предсказывающего анализатора - student2.ru e значения M[E', )] и M[E', $] равны E' Конструирование таблицы предсказывающего анализатора - student2.ru e, поскольку FOLLOW(E') = { ), $}.

Таблица анализа, построенная алгоритмом 4.6 для этой грамматики, приведена на рис. 4.3.

LL(1)-грамматики

Алгоритм 4.6 для построения таблицы предсказывающего анализатора может быть применен к любой КС-грамматике. Однако для некоторых грамматик построенная таблица может иметь неоднозначно определенные входы. Нетрудно доказать, например, что если грамматика леворекурсивна или неоднозначна, таблица будет иметь по крайней мере один неоднозначно определенный вход.

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

Доказано, что алгоритм 4.6 для каждой LL(1)-грамматики G строит таблицу предсказывающего анализатора, распознающего все цепочки из L(G) и только эти цепочки. Нетрудно доказать также, что если G - LL(1)-грамматика, то L(G) - детерминированный КС-язык.

Справедлив также следующий критерий LL(1)-грамматики. Грамматика G = (N, T, P, S) является LL(1)-грамматикой тогда и только тогда, когда для каждой пары правил A Конструирование таблицы предсказывающего анализатора - student2.ru Конструирование таблицы предсказывающего анализатора - student2.ru , A Конструирование таблицы предсказывающего анализатора - student2.ru Конструирование таблицы предсказывающего анализатора - student2.ru из P (т.е. правил с одинаковой левой частью) выполняются следующие 2 условия:

  • FIRST( Конструирование таблицы предсказывающего анализатора - student2.ru ) Конструирование таблицы предсказывающего анализатора - student2.ru FIRST( Конструирование таблицы предсказывающего анализатора - student2.ru ) = Конструирование таблицы предсказывающего анализатора - student2.ru ;
  • Если e Конструирование таблицы предсказывающего анализатора - student2.ru FIRST( Конструирование таблицы предсказывающего анализатора - student2.ru ), то FIRST( Конструирование таблицы предсказывающего анализатора - student2.ru ) Конструирование таблицы предсказывающего анализатора - student2.ru FOLLOW(A) = Конструирование таблицы предсказывающего анализатора - student2.ru .

Язык, для которого существует порождающая его LL(1)-грамматика, называют LL(1)-языком. Доказано, что проблема определения того, порождает ли грамматика LL-язык, является алгоритмически неразрешимой.

Пример 4.6. Неоднозначная грамматика не является LL(1). Примером может служить следующая грамматика G = ({S, E}, {if, then, else, a, b}, P, S) с правилами:

  S Конструирование таблицы предсказывающего анализатора - student2.ru if E then S | if E then S else S | a
  E Конструирование таблицы предсказывающего анализатора - student2.ru b
   

Эта грамматика является неоднозначной, что иллюстрируется на рис. 4.6.

Конструирование таблицы предсказывающего анализатора - student2.ru
Рис. 4.6:  

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

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

Непосредственную левую рекурсию, т.е. рекурсию вида A Конструирование таблицы предсказывающего анализатора - student2.ru A Конструирование таблицы предсказывающего анализатора - student2.ru , можно удалить следующим способом. Сначала группируем A-правила:

Конструирование таблицы предсказывающего анализатора - student2.ru

где никакая из строк Конструирование таблицы предсказывающего анализатора - student2.ru i не начинается с A. Затем заменяем этот набор правил на

Конструирование таблицы предсказывающего анализатора - student2.ru

Конструирование таблицы предсказывающего анализатора - student2.ru

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

Алгоритм 4.7. Удаление левой рекурсии.

Вход. КС-грамматика G без e-правил (правил вида A Конструирование таблицы предсказывающего анализатора - student2.ru e).

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

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

  • Упорядочить нетерминалы грамматики G в произвольном порядке.
  • Выполнить следующую процедуру:

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

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

пусть Aj Конструирование таблицы предсказывающего анализатора - student2.ru Конструирование таблицы предсказывающего анализатора - student2.ru 1 | Конструирование таблицы предсказывающего анализатора - student2.ru 2 | ... | Конструирование таблицы предсказывающего анализатора - student2.ru k - все текущие правила для Aj;

заменить все правила вида Ai Конструирование таблицы предсказывающего анализатора - student2.ru Aj Конструирование таблицы предсказывающего анализатора - student2.ru

на правила Ai Конструирование таблицы предсказывающего анализатора - student2.ru Конструирование таблицы предсказывающего анализатора - student2.ru 1 Конструирование таблицы предсказывающего анализатора - student2.ru | Конструирование таблицы предсказывающего анализатора - student2.ru 2 Конструирование таблицы предсказывающего анализатора - student2.ru | ... | Конструирование таблицы предсказывающего анализатора - student2.ru k Конструирование таблицы предсказывающего анализатора - student2.ru ;

}

удалить правила вида Ai Конструирование таблицы предсказывающего анализатора - student2.ru Ai;

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

}

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

Алгоритм 4.7 применим, если грамматика не имеет e-правил (правил вида A Конструирование таблицы предсказывающего анализатора - student2.ru e). Имеющиеся в грамматике e-правила могут быть удалены предварительно. Получающаяся грамматика без левой рекурсии может иметь e-правила.

Левая факторизация

Oсновная идея левой факторизации в том, что в том случае, когда неясно, какую из двух альтернатив надо использовать для развертки нетерминала A, нужно изменить A-правила так, чтобы отложить решение до тех пор, пока не будет достаточно информации для принятия правильного решения.

Если A Конструирование таблицы предсказывающего анализатора - student2.ru Конструирование таблицы предсказывающего анализатора - student2.ru Конструирование таблицы предсказывающего анализатора - student2.ru 1 | Конструирование таблицы предсказывающего анализатора - student2.ru Конструирование таблицы предсказывающего анализатора - student2.ru 2 - два A-правила и входная цепочка начинается с непустой строки, выводимой из Конструирование таблицы предсказывающего анализатора - student2.ru , мы не знаем, разворачивать ли по первому правилу или по второму. Можно отложить решение, развернув A Конструирование таблицы предсказывающего анализатора - student2.ru Конструирование таблицы предсказывающего анализатора - student2.ru A'. Тогда после анализа того, что выводимо из Конструирование таблицы предсказывающего анализатора - student2.ru , можно развернуть по A' Конструирование таблицы предсказывающего анализатора - student2.ru Конструирование таблицы предсказывающего анализатора - student2.ru 1 или по A' Конструирование таблицы предсказывающего анализатора - student2.ru Конструирование таблицы предсказывающего анализатора - student2.ru 2. Левофакторизованные правила принимают вид

Конструирование таблицы предсказывающего анализатора - student2.ru

Конструирование таблицы предсказывающего анализатора - student2.ru

Алгоритм 4.8. Левая факторизация грамматики.

Вход. КС-грамматика G.

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

Метод. Для каждого нетерминала A ищем самый длинный префикс Конструирование таблицы предсказывающего анализатора - student2.ru , общий для двух или более его альтернатив. Если Конструирование таблицы предсказывающего анализатора - student2.ru Конструирование таблицы предсказывающего анализатора - student2.ru e, т.е. существует нетривиальный общий префикс, заменяем все A-правила

Конструирование таблицы предсказывающего анализатора - student2.ru

где z обозначает все альтернативы, не начинающиеся с Конструирование таблицы предсказывающего анализатора - student2.ru , на

Конструирование таблицы предсказывающего анализатора - student2.ru

Конструирование таблицы предсказывающего анализатора - student2.ru

где A' - новый нетерминал. Повторно применяем это преобразование, пока никакие две альтернативы не будут иметь общего префикса.

Пример 4.7. Рассмотрим вновь грамматику условных операторов из примера 4.6:

  S Конструирование таблицы предсказывающего анализатора - student2.ru if E then S | if E then S else S | a
  E Конструирование таблицы предсказывающего анализатора - student2.ru b
   

После левой факторизации грамматика принимает вид

  S Конструирование таблицы предсказывающего анализатора - student2.ru if E then SS'| a
  S' Конструирование таблицы предсказывающего анализатора - student2.ru else S | e
  E Конструирование таблицы предсказывающего анализатора - student2.ru b
   

К сожалению, грамматика остается неоднозначной, а значит, и не LL(1).

Рекурсивный спуск

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

В процедуре A для случая, когда имеется правило A Конструирование таблицы предсказывающего анализатора - student2.ru ui, такое, что ui Конструирование таблицы предсказывающего анализатора - student2.ru *e (напомним, что не может быть больше одного правила, из которого выводится e), приведены два варианта 1.1 и 1.2. В варианте 1.1 делается проверка, принадлежит ли следующий входной символ FOLLOW(A). Если нет - выдается ошибка. Во втором варианте этого не делается, так что анализ ошибки откладывается на процедуру, вызвавшую A.

void A(){ // A Конструирование таблицы предсказывающего анализатора - student2.ru u1 | u2 | ...| uk

if (InSym Конструирование таблицы предсказывающего анализатора - student2.ru FIRST(ui)) // только одному!

if (parse(ui))

result("A Конструирование таблицы предсказывающего анализатора - student2.ru ui");

else error();

else

//Вариант 1:

if (имеется правило A Конструирование таблицы предсказывающего анализатора - student2.ru ui такое, что ui Конструирование таблицы предсказывающего анализатора - student2.ru *e)

//Вариант 1.1

if (InSym Конструирование таблицы предсказывающего анализатора - student2.ru FOLLOW(A))

result("A Конструирование таблицы предсказывающего анализатора - student2.ru ui");

else error();

//Конец варианта 1.1

//Вариант 1.2:

result("A Конструирование таблицы предсказывающего анализатора - student2.ru ui");

//Конец варианта 1.2

//Конец варианта 1

//Вариант 2:

if (нет правила A Конструирование таблицы предсказывающего анализатора - student2.ru ui такого, что ui Конструирование таблицы предсказывающего анализатора - student2.ru *e)

error();

//Конец варианта 2

}

boolean parse(u){ // из u не выводится e!

v = u;

while (v Конструирование таблицы предсказывающего анализатора - student2.ru e){ // v = Xz

if (X - терминал a)

if (InSym Конструирование таблицы предсказывающего анализатора - student2.ru a)

return(false);

else InSym = getInsym();

else // X - нетерминал B

B();

v = z;

}

return(true);

}

Наши рекомендации