|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
2004 г
4. Синтаксический анализ4.1 КС-грамматики и МП-автоматыПусть G = (N, T, P, S) - контекстно-свободная грамматика. Введем несколько важных понятий и определений. Вывод, в
котором
в любой
сентенциальной
форме на
каждом шаге
делается
подстановка
самого
левого
нетерминала,
называется
левосторонним.
Если S Упорядоченным графом называется пара (V, E), где V есть множество вершин, а E - множество линейно упорядоченных списков дуг, каждый элемент которого имеет вид ((v, v1), (v, v2), ..., (v, vn)). Этот элемент указывает, что из вершины v выходят n дуг, причем первой из них считается дуга, входящая в вершину v1, второй - дуга, входящая в вершину v2, и т.д. Упорядоченным
помеченным
деревом
называется
упорядоченный
граф (V, E),
основой
которого
является
дерево и для
которого
определена
функция f : V Упорядоченное помеченное дерево D называется деревом вывода (или деревом разбора) цепочки w в КС-грамматике G = (N, T, P, S), если выполнены следующие условия:
Грамматика G называется неоднозначной, если существует цепочка w, для которой имеется два или более различных деревьев вывода в G. Грамматика
G называется
леворекурсивной,
если в ней
имеется
нетерминал
A такой, что
существует
вывод A Автомат с
магазинной
памятью
(МП-автомат) -
это семерка
M = (Q, T,
Конфигурацией МП-автомата называется тройка (q, w, u), где
Такт работы
МП-автомата
M будем
представлять
в виде
бинарного
отношения ![]() Q, a T {e}, w T*, Z и
u, v *.
Начальной
конфигурацией
МП-автомата
M называется
конфигурация
вида (q0, w, Z0),
где w Заключительная
конфигурация
- это
конфигурация
вида (q, e, u),
где q Введем
транзитивное
и рефлексивно-транзитивное
замыкание
отношения Говорят,
что цепочка w
допускается
МП-автоматом
M, если
(q0, w, Z0) Язык, допускаемый (распознаваемый, определяемый) автоматом M (обозначается L(M)) - это множество всех цепочек, допускаемых автоматом M. Пример 4.1. Рассмотрим МП-автомат ![]()
D(q0, a, Z) = {(q0, aZ)}, D(q0, b, Z) = {(q0, bZ)}, D(q0, a, a) = {(q0, aa), {q1, e)}, D(q0, a, b) = {(q0, ab)}, D(q0, b, a) = {(q0, ba)}, D(q0, b, b) = {(q0, bb), (q1, e)}, D(q1, a, a) = {(q1, e)}, D(q1, b, b) = {(q1, e)}, D(q1, e, Z) = {(q2, e)}.
Нетрудно
показать,
что L(M) = {wwR|w Иногда
допустимость
определяют
несколько
иначе:
цепочка w
допускается
МП-автоматом
M, если (q0, w, Z0) Теорема 4.1. Язык допускается магазинным автоматом тогда и только тогда, когда он допускается (некоторым другим автоматом) опустошением магазина. Доказательство. Пусть L = L(M) для
некоторого
МП-автомата
M = (Q, T, Пусть M' = (Q
Автомат
сначала
переходит в
конфигурацию
(q0, w, Z0Z0') в
соответствии
с
определением
D' в п.2, затем
в (q, e, Y 1...Y kZ0'), q
Обратно,
пусть M = (Q, T, Пусть
M' = (Q
Нетрудно показать по индукции, что L = L(M'). __ Одним из важнейших результатов теории контекстно-свободных языков является доказательство эквивалентности МП-автоматов и КС-грамматик. Теорема 4.2. Язык является контекстно-свободным тогда и только тогда, когда он допускается магазинным автоматом. Доказательство. Пусть G = (N, T, P, S) - КС-грамматика. Построим МП-автомат M, допускающий язык L(G) опустошением магазина. Пусть
M = ({q}, T, N
Фактически,
этот
МП-автомат
в точности
моделирует
все возможные
выводы в
грамматике
G. Нетрудно
показать по
индукции,
что для любой
цепочки w
Обратно,
пусть M = (Q, T, Пусть G = ({ [qZr] | q, r
Нетерминалы и правила вывода грамматики определены так, что работе автомата M при обработке цепочки w соответствует левосторонний вывод w в грамматике G. Индукцией
по числу
шагов вывода
в G или числу
тактов M
нетрудно
показать,
что (q, w, A) Тогда, если
w МП-автомат M =
(Q, T,
Язык, допускаемый ДМП-автоматом, называется детерминированным КС-языком. Так как функция переходов ДМП-автомата содержит не более одного элемента для любой тройки аргументов, мы будем пользоваться записью D(q, a, Z) = (p, u) для обозначения D(q, a, Z) = {(p, u)}. Пример 4.2. Рассмотрим ДМП-автомат ![]()
D(q0, X, Y ) = (q0, XY ), X D(q0, c, Y ) = (q1, Y ), Y D(q1, X, X) = (q1, e), X D(q1, e, Z) = (q2, e).
Нетрудно
показать,
что этот
детерминированный
МП-автомат
допускает
язык L = {wcwR|w К сожалению, ДМП-автоматы имеют меньшую распознавательную способность, чем МП-автоматы. Доказано, в частности, что существуют КС-языки, не являющиеся детерминированными КС-языками (таковым, например, является язык из примера 4.1). Рассмотрим еще одну важную разновидность МП-автомата. Расширенным
автоматом с
магазинной
памятью
назовем
семерку
M = (Q, T, Теорема 4.3. Пусть M = (Q, T, Расширенный
МП-автомат M = (Q, T,
Теорема 4.4. Пусть M = (Q, T, ДМП-автомат и расширенный ДМП-автомат лежат в основе рассматриваемых далее в этой главе, соответственно, LL и LR-анализаторов.
4.2 Преобразования КС-грамматикРассмотрим ряд преобразований, позволяющих «улучшить» вид контекстно-свободной грамматики, без изменения порождаемого ею языка. Назовем
символ X
Алгоритм 4.1. Устранение недостижимых символов. Вход. КС-грамматика G = (N, T, P, S). Выход. КС-грамматика G' = (N', T', P', S) без недостижимых символов, такая, что L(G') = L(G). Метод. Выполнить шаги 1-4:
Назовем
символ X
Алгоритм 4.2. Устранение бесполезных символов. Вход. КС-грамматика G = (N, T, P, S). Выход. КС-грамматика G' = (N', T', P', S) без бесполезных символов, такая, что L(G') = L(G). Метод. Выполнить шаги 1-5:
КС-грамматика без бесполезных символов называется приведенной. Легко видеть, что для любой КС-грамматики существует эквивалентная приведенная. В дальнейшем будем предполагать, что все рассматривамые грамматики - приведенные.
4.3 Предсказывающий разбор сверху-вниз
4.3.1 Алгоритм разбора сверху-внизПусть дана КС-грамматика G = (N, T, P, S). Рассмотрим предсказывающий разбор (или разбор сверху-вниз) для грамматики G. Основная задача предсказывающего разбора - определение правила вывода, которое нужно применить к нетерминалу. Процесс предсказывающего разбора с точки зрения построения дерева разбора проиллюстрирован на рис. 4.1.
Фрагменты
недостроенного
дерева
соответствуют
сентенциальным
формам.
Вначале
дерево
состоит
только
из одной
вершины,
соответствующей
аксиоме S. В
этот момент
по первому
символу
входной
цепочки
предсказывающий
анализатор
должен
определить
правило S На рис. 4.2 приведена структура предсказывающего анализатора, который определяет очередное правило с помощью таблицы. Такую таблицу можно построить непосредственно по грамматике.
Таблично-управляемый предсказывающий анализатор имеет входную ленту, управляющее устройство (программу), таблицу анализа, магазин (стек) и выходную ленту. Входная лента содержит анализируемую строку, заканчивающуюся символом $ - маркером конца строки. Выходная лента содержит последовательность примененных правил вывода. Таблица анализа - это двумерный массив M[A, a], где A - нетерминал, и a - терминал или символ $. Значением M[A, a] может быть некоторое правило грамматики или элемент «ошибка». Магазин может содержать последовательность символов грамматики с $ на дне. В начальный момент магазин содержит только начальный символ грамматики на верхушке и $ на дне. Анализатор работает следующим образом. Вначале анализатор находится в конфигурации, в которой магазин содержит S$, на входной ленте w$ (w - анализируемая цепочка), выходная лента пуста. На каждом такте анализатор рассматривает X - символ на верхушке магазина и a - текущий входной символ. Эти два символа определяют действия анализатора. Имеются следующие возможности. 1. Если X = a = $, анализатор останавливается, сообщает об успешном окончании разбора и выдает содержимое выходной ленты. 2. Если X = a 3. Если X -
терминал, и X 4. Если X - нетерминал, анализатор заглядывает в таблицу M[X, a]. Возможны два случая:
Работа анализатора может быть задана следующей программой:
Пример 4.3. Рассмотрим грамматику арифметических выражений G = ({E, E', T, T', F}, {id, +, *, (, )}, P, E) с правилами:
Таблица предсказывающего анализатора для этой грамматики приведена на рис. 4.3. Пустые клетки таблицы соответствуют элементу «ошибка».
При разборе входной цепочки id + id * id$ анализатор совершает последовательность шагов, изображенную на рис. 4.4. Заметим, что анализатор осуществляет в точности левый вывод. Если за уже просмотренными входными символами поместить символы грамматики в магазине, то можно получить в точности левые сентенциальные формы вывода. Дерево разбора для этой цепочки приведено на рис. 4.5.
4.3.2 Функции FIRST и FOLLOWПри построении таблицы предсказывающего анализатора нам потребуются две функции - FIRST и FOLLOW. Пусть G = (N, T, P, S) -
КС-грамматика.
Для Определим
FOLLOW(A) для
нетерминала A
как множество
терминалов
a, которые
могут
появиться
непосредственно
справа от A в
некоторой
сентенциальной
форме
грамматики,
т.е. множество
терминалов
a таких, что
существует
вывод
вида S Рассмотрим алгоритмы вычисления функции FIRST.
Алгоритм 4.3. Вычисление FIRST для символов грамматики. Вход. КС-грамматика G = (N, T, P, S). Выход.
Множество FIRST(X)
для каждого
символа X Метод. Выполнить шаги 1-3:
Алгоритм 4.4. Вычисление FIRST для цепочки. Вход. КС-грамматика G = (N, T, P, S). Выход.
Множество
FIRST(X1X2...Xn), Xi Метод. Выполнить шаги 1-3:
Рассмотрим алгоритм вычисления функции FOLLOW.
Алгоритм 4.5. Вычисление FOLLOW для нетерминалов грамматики. Вход. КС-грамматика G = (N, T, P, S). Выход.
Множество FOLLOW(X)
для каждого
символа X Метод. Выполнить шаги 1-4:
Пример 4.4. Рассмотрим грамматику из примера 4.3. Для нее
Например, id и
левая скобка
добавляются
к FIRST(F) на шаге 3 при
i = 1, поскольку
FIRST(id) = {id} и FIRST(”(”) = {”(”} в
соответствии
с шагом 1. На
шаге 3 при i = 1, в
соответствии
с правилом
вывода T При
вычислении
множеств FOLLOW
на шаге 2 в FOLLOW(E)
включается
$. На шаге 3, на
основании
правила F
4.3.3 Конструирование таблицы предсказывающего анализатораДля
конструирования
таблицы
предсказывающего
анализатора
по грамматике
G может быть
использован
алгоритм,
основанный
на следующей
идее.
Предположим,
что A
Алгоритм 4.6. Построение таблицы предсказывающего анализатора. Вход. КС-грамматика G = (N, T, P, S). Выход.
Таблица M[A, a]
предсказывающего
анализатора,
A Метод. Для
каждого
правила
вывода A
Пример 4.5. Применим
алгоритм 4.6 к
грамматике
из примера
4.3. Поскольку
FIRST(TE') = FIRST(T) = {(, id }, в
соответствии
с правилом
вывода E В
соответствии
с правилом
вывода E' Таблица анализа, построенная алгоритмом 4.6 для этой грамматики, приведена на рис. 4.3.
4.3.4 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
Язык, для которого существует порождающая его LL(1)-грамматика, называют LL(1)-языком. Доказано, что проблема определения того, порождает ли грамматика LL-язык, является алгоритмически неразрешимой. Пример 4.6. Неоднозначная грамматика не является LL(1). Примером может служить следующая грамматика G = ({S, E}, {if, then, else, a, b}, P, S) с правилами:
Эта грамматика является неоднозначной, что иллюстрируется на рис. 4.6.
4.3.5 Удаление левой рекурсииОсновная трудность при использовании предсказывающего анализа - это нахождение такой грамматики для входного языка, по которой можно построить таблицу анализа с однозначно определенными входами. Иногда с помощью некоторых простых преобразований грамматику, не являющуюся LL(1), можно привести к эквивалентной LL(1)-грамматике. Среди этих преобразований наиболее эффективными являются левая факторизация и удаление левой рекурсии. Здесь необходимо сделать два замечания. Во-первых, не всякая грамматика после этих преобразований становится LL(1), и, во-вторых, после таких преобразований получающаяся грамматика может стать менее понимаемой. Непосредственную
левую
рекурсию,
т.е. рекурсию
вида A ![]() i не
начинается
с A. Затем
заменяем
этот набор
правил на
![]() ![]()
Алгоритм 4.7. Удаление левой рекурсии. Вход.
КС-грамматика
G без e-правил
(правил
вида A Выход. КС-грамматика G' без левой рекурсии, эквивалентная G. Метод. Выполнить шаги 1 и 2.
После (i - 1)-й
итерации
внешнего
цикла на
шаге 2 для
любого
правила
вида Ak Алгоритм 4.7
применим,
если
грамматика
не имеет
e-правил
(правил вида
A
4.3.6 Левая факторизацияOсновная идея левой факторизации в том, что в том случае, когда неясно, какую из двух альтернатив надо использовать для развертки нетерминала A, нужно изменить A-правила так, чтобы отложить решение до тех пор, пока не будет достаточно информации для принятия правильного решения. Если A ![]() ![]() Алгоритм 4.8. Левая факторизация грамматики. Вход. КС-грамматика G. Выход. Левофакторизованная КС-грамматика G', эквивалентная G. Метод. Для
каждого
нетерминала
A ищем самый
длинный
префикс ![]() , на
![]() ![]() Пример 4.7. Рассмотрим вновь грамматику условных операторов из примера 4.6:
После левой факторизации грамматика принимает вид
К сожалению, грамматика остается неоднозначной, а значит, и не LL(1).
4.3.7 Рекурсивный спускВыше был рассмотрен таблично-управляемый вариант предсказывающего анализа, когда магазин явно использовался в процессе работы анализатора. Можно предложить другой вариант предсказывающего анализа, в котором каждому нетерминалу сопоставляется процедура (вообще говоря, рекурсивная), и магазин образуется неявно при вызовах таких процедур. Процедуры рекурсивного спуска могут быть записаны, как показано ниже. В процедуре
A для случая,
когда
имеется
правило A
void A(){ // A if (InSym if (parse(ui)) result("A else error(); else //Вариант 1: if (имеется
правило A //Вариант 1.1 if (InSym result("A else error(); //Конец варианта 1.1 //Вариант 1.2: result("A //Конец варианта 1.2 //Конец варианта 1 //Вариант 2: if (нет
правила
A error(); //Конец варианта 2 }
boolean parse(u){ // из u не выводится e! v = u; while (v if (X - терминал a) if (InSym return(false); else InSym = getInsym(); else // X - нетерминал B B(); v = z; } return(true); }
4.3.8 Восстановление после синтаксических ошибокВ приведенных программах рекурсивного спуска использовалась процедура реакции на синтаксические ошибки error(). В простейшем случае эта процедура выдает диагностику и завершает работу анализатора. Но можно попытаться некоторым разумным образом продолжить работу. Для разбора сверху вниз можно предложить следующий простой алгоритм. Если в момент обнаружения ошибки на верхушке магазина оказался нетерминальный символ A и для него нет правила, соответствующего входному символу, то сканируем вход до тех пор, пока не встретим символ либо из FIRST(A), либо из FOLLOW(A). В первом случае разворачиваем A по соответствующему правилу, во втором - удаляем A из магазина. Если на верхушке магазина терминальный символ, то можно удалить все терминальные символы с верхушки магазина вплоть до первого (сверху) нетерминального символа и продолжать так, как это было описано выше. 4.4 Разбор снизу-вверх типа сдвиг-свертка
4.4.1 ОсноваВ процессе разбора снизу-вверх типа сдвиг-свертка строится дерево разбора входной цепочки, начиная с листьев (снизу) к корню (вверх). Этот процесс можно рассматривать как «свертку» цепочки w к начальному символу грамматики. На каждом шаге свертки подцепочка, которую можно сопоставить правой части некоторого правила вывода, заменяется символом левой части этого правила вывода, и если на каждом шаге выбирается правильная подцепочка, то в обратном порядке прослеживается правосторонний вывод (рис. 4.7). Здесь ко входной цепочке, так же как и при анализе LL(1)-грамматик, приписан концевой маркер $.
Подцепочка
сентенциальной
формы,
которая
может быть
сопоставлена
правой части
некоторого
правила
вывода,
свертка по
которому к
левой части
правила
соответствует
одному шагу
в обращении
правостороннего
вывода,
называется
основой
цепочки.
Самая левая
подцепочка,
которая
сопоставляется
правой части
некоторого
правила
вывода A Формально,
основа
правой
сентенциальной
формы z - это
правило
вывода A Вообще
говоря,
грамматика
может быть
неоднозначной,
поэтому не
единственным
может быть
правосторонний
вывод Чтобы
восстановить
этот вывод
в обратном
порядке,
выделяем
основу Таким образом, главная задача анализатора типа сдвиг-свертка - это выделение и отсечение основы. 4.4.2 LR(1)-анализаторыВ названии LR(1) символ L указывает на то, что входная цепочка читается слева-направо, R - на то, что строится правый вывод, наконец, 1 указывает на то, что анализатор видит один символ непрочитанной части входной цепочки. LR(1)-анализ привлекателен по нескольким причинам:
Схематически структура LR(1)-анализатора изображена на рис. 4.8. Анализатор состоит из входной ленты, выходной ленты, магазина, управляющей программы и таблицы анализа (LR(1)-таблицы), которая имеет две части - функцию действий (Action) и функцию переходов (Goto). Управляющая программа одна и та же для всех LR(1)-анализаторов, разные анализаторы отличаются только таблицами анализа. Программа анализатора читает символы на входной ленте по одному за шаг. В процессе анализа используется магазин, в котором хранятся строки вида S0X1S1X2S2...XmSm (Sm - верхушка магазина). Каждый Xi - символ грамматики (терминальный или нетерминальный), а Si - символ состояния. Заметим, что символы грамматики (либо символы состояний) не обязательно должны размещаться в магазине. Однако, их использование облегчает понимание поведения LR-анализатора.
Элемент
функции
действий Action[Sm, ai]
для символа
состояния
Sm и входа
ai
Элемент
функции
переходов Goto[Sm, A]
для символа
состояния
Sm и входа A
Конфигурацией LR(1)-анализатора называется пара, первая компонента которой - содержимое магазина, а вторая - непросмотренный вход: ![]() Эта конфигурация соответствует правой сентенциальной форме ![]() Префиксы правых сентенциальных форм, которые могут появиться в магазине анализатора, называются активными префиксами. Основа сентенциальной формы всегда располагается на верхушке магазина. Таким образом, активный префикс - это такой префикс правой сентенциальной формы, который не переходит правую границу основы этой формы. В начале работы анализатора в магазине находится только символ начального состояния S0, на входной ленте - анализируемая цепочка с маркером конца. Очередной шаг анализатора определяется текущим входным символом ai и символом состояния на верхушке магазина Sm следующим образом. Пусть LR(1)-анализатор находится в конфигурации
![]() Анализатор может проделать один из следующих шагов:
Пример 4.8. Рассмотрим грамматику арифметических выражений G = ({E, T, F}, {id, +, *}, P, E) с правилами:
На рис. 4.9 изображены функции Action и Goto, образующие LR(1)-таблицу для этой грамматики. Для Элемент Si функции Action означает сдвиг и помещение в магазин состояния с номером i, Rj - свертку по правилу номер j, acc - допуск, пустая клетка - ошибку. Для функции Goto символ i означает помещение в магазин состояния с номером i, пустая клетка - ошибку. На входе id + id * id последовательность состояний магазина и входной ленты показаны на рис. 4.10. Например, в первой строке LR-анализатор находится в нулевом состоянии и «видит» первый входной символ id. Действие S6 в нулевой строке и столбце id в поле Action (рис. 4.9) означает сдвиг и помещение символа состояния 6 на верхушку магазина. Это и изображено во второй строке: первый символ id и символ состояния 6 помещаются в магазин, а id удаляется со входной ленты.
Текущим
входным
символом
становится +,
и действием
в состоянии
6 на вход +
является
свертка по F 4.4.3 Конструирование LR(1)-таблицыРассмотрим теперь алгоритм конструирования таблицы, управляющей LR(1)-анализатором. Пусть G = (N, T, P, S) - КС-грамматика. Пополненной грамматикой для данной грамматики G называется КС-грамматика ![]() S.
Это
дополнительное
правило
вводится для
того, чтобы
определить,
когда
анализатор
должен
остановить
разбор и
зафиксировать
допуск
входа. Таким
образом,
допуск
имеет место
тогда и
только
тогда, когда
анализатор
готов
осуществить
свертку по
правилу S' LR(1)-ситуацией
называется
пара [A Будем
говорить, что
LR(1)-ситуация [A Будем говорить, что ситуация допустима, если она допустима для какого-либо активного префикса. Пример 4.9. Рассмотрим грамматику G = ({S, B}, {a, b}, P, S) с правилами
Существует
правосторонний
вывод S Центральная идея метода заключается в том, что по грамматике строится детерминированный конечный автомат, распознающий активные префиксы. Для этого ситуации группируются во множества, которые и образуют состояния автомата. Ситуации можно рассматривать как состояния недетерминированного конечного автомата, распознающего активные префиксы, а их группировка на самом деле есть процесс построения детерминированного конечного автомата из недетерминированного. Анализатор, работающий слева-направо по типу сдвиг-свертка, должен уметь распознавать основы на верхушке магазина. Состояние автомата после прочтения содержимого магазина и текущий входной символ определяют очередное действие автомата. Функцией переходов этого конечного автомата является функция переходов LR-анализатора. Чтобы не просматривать магазин на каждом шаге анализа, на верхушке магазина всегда хранится то состояние, в котором должен оказаться этот конечный автомат после того, как он прочитал символы грамматики в магазине от дна к верхушке. Рассмотрим
ситуацию
вида [A Система множеств допустимых LR(1)-ситуаций для всевозможных активных префиксов пополненной грамматики называется канонической системой множеств допустимых LR(1)-ситуаций. Алгоритм построения канонической системы множеств приведен ниже.
Алгоритм 4.9. Конструирование канонической системы множеств допустимых LR(1)-ситуаций. Вход. КС-грамматика G = (N, T, P, S). Выход. Каноническая система C множеств допустимых LR(1)-ситуаций для грамматики G. Метод. Заключается в выполнении для пополненной грамматики G' процедуры items, которая использует функции closure и goto.
function closure(I){ /* I - множество ситуаций */ do{ for (каждой
ситуации
[A каждого
правила
вывода B каждого
терминала
b из FIRST( такого,
что [B добавить
[B } while (к I можно добавить новую ситуацию); return I; }
function goto(I,X){ /* I - множество ситуаций; X - символ грамматики */ Пусть
J = {[A return closure(J); }
procedure items(G'){ /* G' - пополненная грамматика */ I0 = closure({[S' C = {I0}; do{ for (каждого множества ситуаций I из системы C, каждого символа грамматики X такого, что goto(I, X) не пусто и не принадлежит C) добавить goto(I, X) к системе C; } while (к C можно добавить новое множество ситуаций);
Если I -
множество
ситуаций,
допустимых
для
некоторого
активного
префикса
Работа
алгоритма
построения
системы C
множеств
допустимых
LR(1)-ситуаций
начинается
с того, что в C
помещается
начальное
множество
ситуаций
I0 = closure({[S' Рассмотрим теперь, как по системе множеств LR(1)-ситуаций строится LR(1)-таблица, т.е. функции действий и переходов LR(1)-анализатора.
Алгоритм 4.10. Построение LR(1)-таблицы. Вход. Каноническая система C = {I0, I1, ..., In} множеств допустимых LR(1)-ситуаций для грамматики G. Выход. Функции Action и Goto, составляющие LR(1)-таблицу для грамматики G. Метод. Для каждого состояния i функции Action[i, a] и Goto[i, X] строятся по множеству ситуаций Ii:
Таблица на основе функций Action и Goto, полученных в результате работы алгоритма 4.10, называется канонической LR(1)-таблицей. LR(1)-анализатор, работающий с этой таблицей, называется каноническим LR(1)-анализатором. Пример 4.10. Рассмотрим следующую грамматику, являющуюся пополненной для грамматики из примера 4.8:
Множества ситуаций и переходы по goto для этой грамматики приведены на рис. 4.11. LR(1)-таблица для этой грамматики приведена на рис. 4.9.
4.4.4 LR(1)-грамматикиЕсли для КС-грамматики G функция Action, полученная в результате работы алгоритма 4.10, не содержит неоднозначно определенных входов, то грамматика называется LR(1)-грамматикой. Язык L называется LR(1)-языком, если он может быть порожден некоторой LR(1)-грамматикой. Иногда используется другое определение LR(1)-грамматики. Грамматика называется LR(1), если из условий 1. S' 2. S' 3. FIRST(w) = FIRST(y) следует, что uAy = zBx (т.е. u = z, A = B и x = y). Согласно
этому
определению,
если uvw и uvy -
правовыводимые
цепочки
пополненной
грамматики,
у которых
FIRST(w) = FIRST(y) и A Можно доказать, что эти два определения эквивалентны. Если грамматика не является LR(1), то анализатор типа сдвиг-свертка при анализе некоторой цепочки может достигнуть конфигурации, в которой он, зная содержимое магазина и следующий входной символ, не может решить, делать ли сдвиг или свертку (конфликт сдвиг/свертка), или не может решить, какую из нескольких сверток применить (конфликт свертка/свертка). В частности, неоднозначная грамматика не может быть LR(1). Для доказательства рассмотрим два различных правых вывода (1) S (2) S Нетрудно
заметить,
что LR(1)-условие
(согласно
второму
определению
LR(1)-грамматики)
нарушается
для
наименьшего
из чисел i,
для которых
un-i Пример 4.11. Рассмотрим вновь грамматику условных операторов:
Если
анализатор
типа
сдвиг-свертка
находится в
конфигурации,
такой что
необработанная
часть
входной
цепочки
имеет вид else...$, а
в магазине
находится
...if E then S, то нельзя
определить,
является ли if E then S
основой, вне
зависимости
от того,
что лежит
в магазине
ниже. Это
конфликт
сдвиг/свертка.
В зависимости
от того, что
следует на
входе за else,
правильной
может быть
свертка по
S Эта грамматика может быть преобразована к LR(1)-виду следующим образом:
Справедливы также следующие утверждения [2]. Теорема 4.5. Каждый LR(1)-язык является детерминированным КС-языком. Теорема 4.6. Если L - детерминированный КС-язык, то существует LR(1)-грамматика, порождающая L.
4.4.5 Восстановление после синтаксических ошибокОдним из простейших методов восстановления после ошибки при LR(1)-анализе является следующий. При синтаксической ошибке просматриваем магазин от верхушки, пока не найдем состояние s с переходом на выделенный нетерминал A. Затем сканируются входные символы, пока не будет найден такой, который допустим после A. В этом случае на верхушку магазина помещается состояние Goto[s, A] и разбор продолжается. Для нетерминала A может иметься несколько таких вариантов. Обычно A - это нетерминал, представляющий одну из основных конструкций языка, например оператор. При более детальной проработке реакции на ошибки можно в каждой пустой клетке анализатора поставить обращение к своей подпрограмме. Такая подпрограмма может вставлять или удалять входные символы или символы магазина, менять порядок входных символов.
4.4.6 Варианты LR-анализаторовЧасто построенные таблицы для LR(1)-анализатора оказываются довольно большими. Поэтому при практической реализации используются различные методы их сжатия. С другой стороны, часто оказывается, что при построении для языка синтаксического анализатора типа «сдвиг-свертка» достаточно более простых методов. Некоторые из этих методов базируются на основе LR(1)-анализаторов. Одним из способов такого упрощения является LR(0)-анализ - частный случая LR-анализа, когда ни при построении таблиц, ни при анализе не учитывается аванцепочка. Еще одним вариантом LR-анализа являются так называемые SLR(1)-анализаторы (Simple LR(1)). Они строятся следующим образом. Пусть C = {I0, I1, ..., In} - набор множеств допустимых LR(0)-ситуаций. Состояния анализатора соответствуют Ii. Функции действий и переходов анализатора определяются следующим образом.
Распространенным
вариантом
LR(1)-анализа
является
также
LALR(1)-анализ. Он
основан на
объединении
(слиянии)
некоторых
таблиц.
Назовем
ядром
множества
LR(1)-ситуаций
множество
их первых
компонент
(т.е. во
множестве
ситуаций не
учитываются
аванцепочки).
Объединим
все
множества
ситуаций с
одинаковыми
ядрами, а в
качестве
аванцепочек
возьмем
объединение
аванцепочек.
Функции Action и
Goto строятся
очевидным
образом.
Если
функция
Action таким
образом
построенного
анализатора
не имеет
конфликтов,
то он
называется
LALR(1)-анализатором
(LookAhead LR(1)).
|
|
CITForum © 1997–2025