- •4.Восходящие методы обработки языков
- •41. Неформальное описание восходящего анализа
- •4.2.1.Определение lr(к)-грамматики
- •4.2.2. Алгоритм разбора для lr(k)-грамматики
- •4.2.3.Алгоритм построения управляющей таблицы для lr(0)-грамматики без -правил
- •4.2.4.Алгоритм построения управляющей таблицы для slr(1)-грамматики без -правил
- •4.2.5.Включение -правилв lr(0)-иSlr (1)-грамматики
4.2.2. Алгоритм разбора для lr(k)-грамматики
Для любой LR(k)-грамматики G = < T, N, S, R > можно построить детерминированный анализатор, который выдает правый разбор входной цепочки.
Анализатор состоит из магазина, входной ленты, выходной ленты и управляющего устройства (см. рис. 4.5).
М
Входная
лента
Рис. 4.5
Магазин
Выходная лента
Например, для грамматики G2 (рис. 4.3) грамматическое вхождение A5,2 представляет собой нетерминал А из второй позиции правой части пятого правила вывода. Если символ входит в правую часть i-го правила только один раз, то второй индекс будем опускать. Например. А1 – это грамматическое вхождение нетерминала А в первое правило, а А5 – грамматическое вхождение нетерминала А в пятое правило. Перед началом работы алгоритма магазин пуст (содержит маркер дна).
Управляющее устройство анализатора представляет собой таблицу Ʈ, строки которой отмечены символами Т Vp {}. Одна строка такой таблицы называется LR(k)-таблицей. Каждая LR(k)-таблица задает две функции: функцию действия f и функцию переходов g.
Функция действия f, аргументом которой служит цепочка u T*, принимает значения из множества { ДОПУСК, ОШИБКА, ПЕРЕНОС, (СВЕРТКА, i) }.
Аргументом функции переходов g является символ Х V, а ее значениями – элементы множества { ОШИБКА } Vp.
Опишем LК(k)-алгоритм разбора.
Вход
Анализируемая цепочка z = t1 t2 … tj … tn T*, где j – номер текущего символа входной цепочки, находящегося под читающей головкой.
Управляющая таблица Ʈ (множество LR(k)-таблиц) для LR(k)-грамматики
G = < T, N, S, R >.
Выход
Если z L(G), то правый разбор цепочки z, в противном случае – сигнал об ошибке
Описание алгоритма
j :=0.
j := j + 1. Если j > n, то выдать сообщение об ошибке и перейти к п. 5.
Определить цепочку u следукщим образом:
а) если k = 0, то u = tj ;
б) если k 1 и j+k-1 n, то u = tj tj+1 … tj+k-1 – первые k символов цепочки
tj, tj+1, …, tn ;
в) если k 1 и j+k-1 > n, то u = tj tj+1 … tn– остаток входной цепочки.
Применить функцию действия f из строки таблицы Ʈ, отмеченной верхним cимволом магазина T, к цепочке u.
а) f(u) =ПЕРЕНОС. Определить функцию переходов g(tj) из строки таблицы Ʈ, отмеченной символом Т из верхушки магазина. Если g(tj) = T’ и T’ Vp {}, то записать Т’ в магазин и перейти к п.2. Если g(tj) = ОШИБКА, то выдать сигнал об ошибке и перейти к п. 5.
б) f(u) = (СВЕРТКА, i) и A – правило вывода с номером i грамматики G. Удалить из верхней части магазина символов, в результате чего в верхушке магазина окажется символ T’ Vp {}, и выдать номер правила i на выходную ленту. Определить символ T = g(A) из cтроки таблицы Ʈ, отмеченной символом T’, записать его в магазин и перейти к п. 3.
в) f(u) = ОШИБКА.Выдать сообщение об ошибке и перейти к п. 5.
г) f(u) = ДОПУСК. Объявить цепочку, записанную на выходной ленте, правым разбором входной цепочки z.
5) Останов.
Рассмотрим работу LR(k) -анализатора на примере.
Пример 4.1.
На рис. 4.6 изображена управляющая таблица для LR(0)-грамматики G2, правила вывода которой приведены на рис. 4.3.
Буквы С, П и Д в этой таблице (и во всех следующих таблицах) служат условными обозначениями значений функции f(u): ПЕРЕНОС, СВЕРТКА и ДОПУСК соответственно, а пустые элементы таблицы имеют значение ОШИБКА.
Заметим, что в данном примере магазинный алфавит Vp представляет собой множество грамматических вхождений символов грамматики в правила вывода. Первая строка управляющей таблицы отмечена грамматическим вхождением So начального символа грамматики S в правую часть нулевого правила вывода S’ S пополненной грамматики G2’, полученной из исходной грамматики G2.
T |
f(u) |
g(X) | ||||||||
a |
b |
c |
|
a |
b |
c |
S |
A |
B | |
S0 |
|
|
|
Д |
|
|
|
|
|
|
a1 |
П |
П |
П |
|
a5 |
b3 |
c6 |
|
A1 |
B4 |
A1 |
П |
П |
П |
|
|
b1 |
|
|
|
|
b1 |
С,1 |
С,1 |
С,1 |
С,1 |
|
|
|
|
|
|
c2 |
С,2 |
С,2 |
С,2 |
С,2 |
|
|
|
|
|
|
b3 |
П |
П |
П |
|
a1 |
|
c2 |
S3 |
|
|
S3 |
С,3 |
С,3 |
С,3 |
С,3 |
|
|
|
|
|
|
B4 |
П |
П |
П |
|
|
b4 |
|
|
|
|
b4 |
С,4 |
С,4 |
С,4 |
С,4 |
|
|
|
|
|
|
a5 |
П |
П |
П |
|
a5 |
b3 |
c6 |
|
A5 |
B4 |
A5 |
С,5 |
С,5 |
С,5 |
С,5 |
|
|
|
|
|
|
c6 |
С,6 |
С,6 |
С,6 |
С,6 |
|
|
|
|
|
|
|
П |
П |
П |
|
a1 |
|
c2 |
S0 |
|
|
Рис. 4.6
Работу алгоритма опишем в терминах конфигураций, представляющих собой тройки вида (T, ax, ), где T – цепочка магазинных символов (Т - верхний символ магазина), ax – необработанная часть входной цепочки, начинающаяся символом a (для k = 0 длина цепочки u равна 1), – выход, построенный к настоящему моменту времени.
Рассмотрим последовательность тактов, которую выполнит LR(k)-алгоритм при
анализе входной цепочки abcb.
Начальная конфигурация алгоритма – (, abcb, ) (в вершине магазина находится маркер дна магазина, а текущим входным символом является символ а). Для строки управляющей таблицы, отмеченной символом , f(A) = ПЕРЕНОС, а g(A)=a1, поэтому в магазин записывается символ a1 (грамматическое вхождение символа a в правую часть первого правила), входная головка сдвигается на один символ вправо, а алгоритм переходит в конфигурацию (a1, bcb, ). Для строки таблицы, отмеченной символом a1, f(b) = ПЕРЕНОС, а g(b) = b3, следовательно, алгоритм перейдет в конфигурацию (a1b3, cb, ). Аналогично для магазинного символа b3 и текущего символа входной цепочки с магазин перейдет в конфигурацию (a1b3c2, b, ).
Рассмотрим теперь строку управляющей таблицы Ʈ, помеченную грамматическим вхождением с2. В этом случае f(b) = (С. 2), значит, необходимо выполнить свертку с использованием правила (2) S c. Правая часть этого правила содержит только один символ, поэтому удаляем из магазина символ с2 и определяем значение функции переходов для символа S из левой части правила (2) в отроке управляющей таблицы Ʈ, отмеченной символом b3, который стал верхним символом магазина. Теперь g(S)=S3 и, следовательно, алгоритм перейдет в конфигурацию
(a1b3 S3, b, 2), и в выходную цепочку запишется 2 (номер использованного правила).
Поступая дальше подобным образом, получим следующую последовательность тактов работы анализатора (символ ÷ используется для перехода из одной конфигурации в другую):
(a1b3 S3, b, 2) ÷ (a1A1, b, 23) ÷ (a1A1b1, , 23) ÷ (S0, , 231),
где конфигурация (S0, , 231) является заключительной, а цепочка 231 – правым разбором цепочки abcb.
Приведем последовательность тактов, которую выполнит алгоритм при анализе входной цепочки aabc, содержащей синтаксическую ошибку.
(, aabc, ) ÷ (, aabc, ) ÷ (a1, abc, ) ÷ (a1a5, bc, ) ÷ (a1a5b3, c, ) ÷
(a1a5b3c2, , ) ÷ (a1a5b3S3, , 2) ÷ (a1a5A5, , 23) ÷ (a1B4, , 235)
В последней конфигурации f() = ОШИБКА. Алгоритм выдает сообщение об ошибке и заканчивает работу.
Продемонстрировав работу LR(k)-анализатора для конкретных входных цепочек, рассмотрим построение управляющей таблицы Ʈ LR(k)-анализатора для k=0 и некоторого подмножества LR(1)-грамматик (алгоритм проверки принадлежности грамматики классу LR(k)-грамматик для произвольного k предполагает построение большого числа вспомогательных множеств, поэтому его использование для k>1 для решения практических задач не оправдано).