
- •Введение
- •1 Предварительные математические сведения
- •1.1 Множества
- •1.2 Операции и отношения
- •1.2.1 Операции над множествами
- •1.2.2 Отношения на множествах
- •1.3.1 Цепочки
- •1.3.2 Операции над цепочками
- •1.4.2 Операции над языком
- •1.5 Алгоритмы
- •1.5.1 Частичные алгоритмы
- •1.5.2 Всюду определенные алгоритмы
- •1.5.3 Рекурсивные алгоритмы
- •1.5.4 Задание алгоритмов
- •1.5.5 Проблемы
- •1.6 Некоторые понятия теории графов
- •1.6.1 Ориентированные графы
- •1.6.2 Ориентированные ациклические графы
- •1.6.3 Деревья
- •1.6.4 Упорядоченные графы
- •2 Введение в компиляцию
- •2.1 Задание языков программирования
- •2.2 Синтаксис и семантика
- •2.3 Процесс компиляции
- •2.4 Лексический анализ
- •2.5 Работа с таблицами
- •2.6 Синтаксический анализ
- •2.7 Генератор кода
- •2.8 Оптимизация кода
- •2.9 Исправление ошибок
- •2.10 Резюме
- •3 Теория языков
- •3.1 Способы определения языков
- •3.2 Грамматики
- •3.3 Грамматики с ограничениями на правила
- •3.4 Распознаватели
- •3.5.1 Определения
- •3.8 Конечные автоматы и регулярные множества
- •3.9.1 Постановка задачи
- •3.10 Контекстно-свободные языки
- •3.10.2 Преобразование КС-грамматик
- •3.10.2.1. Алгоритм проверки пустоты языка
- •3.10.2.2. Алгоритм устранения недостижимых символов
- •3.10.2.3. Алгоритм устранения бесполезных символов
- •3.10.2.5. Алгоритм устранения цепных правил
- •3.10.3 Грамматика без циклов
- •3.10.4 Нормальная форма Хомского
- •3.10.5 Нормальная форма Грейбах
- •3.11 Автоматы с магазинной памятью
- •3.11.1 Основные определения
- •4.1 LL(k)-грамматики
- •4.2.2 Алгоритм поиска направляющих символов
- •4.2.2.1 Множество предшествующих символов
- •4.2.2.2 Множество последующих символов
- •4.2.2.3 Множество направляющих символов
- •4.3 LL(1)-таблица разбора
- •4.3.1 Построение таблицы
- •5 Синтаксический анализ снизу вверх
- •5.1 LR(k)-грамматики
- •5.2 LR(1)-грамматики
- •5.3 LR(1)-таблица разбора
- •5.3.1 Состояния анализатора
- •5.3.2 Построение таблицы
- •5.3.3 LR-конфликты
- •5.3.4 Разбор цепочки по таблице
- •5.4 Сравнение LL- и LR-методов разбора
- •6 Включение действий в синтаксис
- •6.2 Работа с таблицей символов
- •7 Проектирование компиляторов
- •7.1 Число проходов
- •7.2 Таблицы символов
- •7.2.2 Бинарное дерево
- •7.4.1 Стек времени прогона
- •7.4.2 Методы вызова параметров
- •7.4.3 Обстановка выполнения процедур
- •8 Генерация кода
- •8.1 Генерация промежуточного кода
- •8.2 Структура данных для генерации кода
- •8.3.1 Присвоение
- •8.3.2 Условные зависимости
- •8.3.3 Описание идентификаторов
- •8.3.4 Циклы
- •8.3.5 Вход и выход из блока
- •8.3.6 Прикладные реализации
- •8.4 Проблемы, связанные с типами
- •8.5 Время компиляции и время прогона
- •9 Исправление и диагностика ошибок
- •9.1 Типы ошибок
- •9.2 Лексические ошибки
- •9.3 Ошибки в употреблении скобок
- •9.4 Синтаксические ошибки
- •9.4.1 Методы исправления синтаксических ошибок
- •9.4.2 Предупреждения
- •9.4.3 Сообщения о синтаксических ошибках
- •9.5 Контекстно-зависимые ошибки
- •9.6 Ошибки, связанные с употреблением типов
- •9.7 Ошибки, допускаемые во время прогона
- •9.8 Ошибки, связанные с нарушением ограничений
- •Заключение
- •Список литературы
- •Глоссарий

148
5 СИНТАКСИЧЕСКИЙ АНАЛИЗ СНИЗУ ВВЕРХ
5.1 LR(K)-ГРАММАТИКИ
Мы рассмотрели проблему левостороннего разбора как частный случай синтаксического анализа. Обобщая выводы, можно констатировать, что ме-
тоды разбора могут быть нисходящими, т.е. идущими от стартового символа к предложению, и восходящими – от предложения к стартовому символу.
Предложения, читаемые слева направо, норма для большинства естествен-
ных языков, хотя проблема разбора в них не всегда тривиальна. Однако ряд естественных языков и языков программирования имеют другую структуру.
В этом случае удобнее использовать правосторонний вывод и соответствую-
щие грамматики. Эти грамматики называются LR-грамматиками и в син-
таксическом разборе используют технологию снизу вверх [3, 4, 5].
Обозначения в написании LR(k)-грамматики означают:
–L – строки разбираются слева направо;
–R – используются свертки правых частей правил к левым;
–k – выбор между сдвигом и сверткой производится с помощью
предварительного просмотра k символов.
Дадим определение понятиям сдвига и свертки. Синтаксический анали-
затор, работающий по принципу «снизу вверх», выполняет действия двух ти-
пов [2]:
1)сдвиг – считывание и помещение символа в стек, что соответствует движению на один пункт вдоль какого-нибудь правила грамматики;
2)свертка (приведение) – замещение множества элементов в верхней части стека каким-либо нетерминалом грамматики с помощью одно-
го из порождающих правил этой грамматики.
· · · · · · · · · · · · · · · · · · · · · · · · |
|
Пример · · · · · · · · · · · · · · · · · · · · · · · |
|
|
|

149
Рассмотрим, например, язык, генерируемый правилами:
(1)VARS real ID IDLIST
(2)IDLIST , ID IDLIST
(3)IDLIST ;
(4)ID a-z
Здесь a-z – это диапазон символов от a до z. Правила специально подобраны так, чтобы удовлетворять как LL-, так и LR-грамматике. В чем будут разли-
чия при разборе предложения «real a, b, c;» методом сверху вниз и методом снизу вверх?
Разбор сверху вниз (LL) начинается со стартового символа S = VARS, а
его цель – спускаясь вниз, найти в дереве вывода лист «real a, b, c;». Если та-
кой лист будет найден, то предложение принадлежит языку, иначе нет:
VARS 1 real ID IDLIST
2 real a IDLIST
3 real a, ID IDLIST
4 real a, b IDLIST
5 real a, b, ID IDLIST
6 real a, b, c IDLIST
7 real a, b, c;
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
Сентенциальная форма – это последовательность симво-
лов (терминалов и нетерминалов), выводимых из начального сим-
вола грамматики.
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
Соответственно, если
S l* υ

150
в процессе левостороннего вывода, то υ – левая сентенциальная форма.
По аналогии с левой сентенциальной формой, последовательность сим-
волов ω, выводимых из начального символа грамматики в процессе право-
стороннего вывода, называется правой сентенциальной формой:
S r* ω
· · · · · · · · · · · · · · · · · · · · · · · · |
|
Пример · · · · · · · · · · · · · · · · · · · · · · · |
|
|
|
Разбор снизу вверх (LR) идет в обратном порядке – предполагая, что предложение «real a, b, c;» – лист дерева вывода, делается попытка поднять-
ся вверх к корню дерева (стартовому символу S = VARS). Если это удается, то данная цепочка принадлежит языку. Таким образом, синтаксические анали-
заторы, работающие по принципу LR, сводят предложения языка к началь-
ному символу путем последовательного применения правил грамматики.
Для LR-разбора используется стек. Каждый символ, считанный анали-
затором, немедленно помещается в стек анализатора:
real a, b, c; |
|
real |
|
|
|
|
|
real a , b, c; |
|
a |
|
|
|
||
|
|
real |
|
|
|
|
|
Это действие называется «сдвиг» (англ. shift). Стрелка ставится непо-
средственно после последнего считанного символа. Затем анализатор заме-
няет a с помощью правила (4) на нетерминал в его левой части. Это действие называется «приведение» или «свертка» (англ. reduce). Правые части правил
заменяются их соответствующими левыми частями:
real a , b, c; |
ID |
|
real |
|
|
Далее анализатор считывает следующие символы:

|
151 |
|
|
real a, b, c; |
|
|
|
|
, |
|
|
|
|
ID |
|
|
|
real |
|
real a, b , c; |
|
b |
|
|
|
||
|
|
||
|
|
, |
|
|
|
ID |
|
|
|
real |
|
|
|
|
|
Снова применяется свертка по правилу (4): real a, b , c; ID
,
ID
|
real |
|
|
Считываем два следующих символа: |
|
real a, b, c; |
|
, |
|
|
ID |
|
, |
|
ID |
|
real |
|
|
|
|
real a, b, c ; |
c |
|
, |
|
ID |
|
, |
|
ID |
|
real |
|
|
Очередное действия – приведение с использованием правила (4):
real a, b, c ; |
ID |
|
|

|
152 |
|
|
|
|
|
|
, |
|
|
ID |
|
|
, |
|
|
ID |
|
|
real |
Считываем последний символ: |
|
|
|
|
|
real a, b, c; |
|
|
|
; |
|
|
|
ID |
|
|
, |
|
|
ID |
|
|
, |
|
|
ID |
|
|
real |
|
|
|
Выполняем приведение по правилу (3): real a, b, c IDLIST
ID
,
ID
,
ID
real
Теперь три верхних символа на стеке можно свернуть по правилу (2):
real a, b, c |
IDLIST |
|
ID |
|
, |
|
ID |
|
real |
Затем еще раз: |
|
|

|
153 |
|
|
real a, b, c |
|
IDLIST |
|
|
|
||
|
|
ID |
|
|
|
real |
|
|
|
|
|
И последнее действие – свертка по правилу (1):
real a, b, c |
VARS |
|
|
Разбор считается завершенным, когда в стеке останется только началь-
ный символ и предложение считано целиком. Стек разбора соответствует ча-
сти автомата с магазинной памятью.
В итоге получили следующий процесс разбора: real a, b, c; 1 real ID, b, c;
2 real ID, ID, c;3 real ID, ID, ID;
4 real ID, ID, ID IDLIST
5 real ID, ID IDLIST
6 real ID IDLIST
7 VARS
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
Однако данная грамматика весьма проста, к тому же праволинейна. В
ней всегда легко выбрать альтернативу порождающего правила при LL-
разборе либо сделать выбор между сверткой и сдвигом при LR-разборе. Но так бывает далеко не всегда.
· · · · · · · · · · · · · · · · · · · · · · · · |
|
Пример · · · · · · · · · · · · · · · · · · · · · · · |
|
|
|
Например, рассмотрим следующую LL-грамматику:
E F E′
E′ ADD
E′ MUL
154
E′ e
ADD + E
MUL * E
F ( E )
F a
Попробуем вывести из нее цепочку «(a + a)*a»:
E1 F E′
2 ( E ) E′
3 ( F E′ ) E′
4 ( a E′ ) E′
Адалее возникают затруднения. У порождающего правила E′ есть три альтернативы, но узнать, какую из них выбрать, не заглядывая вперед, не-
возможно. Если же выбрать неверную альтернативу, то придется возвра-
щаться назад, что противоречит принципам LL-разбора. Для этого и строится
LL-таблица – по ней выбор альтернативы осуществляется однозначно.
Подобные проблемы возникают и при LR-разборе. Для примера рас-
смотрим грамматику:
(1)VARS real IDLIST ;
(2)IDLIST ID , IDLIST
(3)IDLIST ID
(4)ID a-z
Возьмем предложение «real a, b, c;» и начнем его приведение к старто-
вому символу:
real a, b, c; 1 real ID, b, c;
2 real IDLIST, b, c;3 real IDLIST, ID, c;
4 real IDLIST, IDLIST, c;

155
5 real IDLIST, IDLIST, ID;
6 real IDLIST, IDLIST, IDLIST;
Несмотря на то, что предложение является верным, разбор зашел в ту-
пик. Причина в том, что на втором шаге была выполнена лишняя процедура свертки. Необходимо было сделать сдвиг:
real a, b, c; 1 real ID , b, c;2 real ID , ID , c;3 real ID , ID , ID ;
4 real ID , ID , IDLIST ;5 real ID , IDLIST ;
6 real IDLIST ;7 VARS
Как и в предыдущем примере, заранее выбрать, что предпочтительнее – сдвиг или свертка – в общем случае невозможно, а возвраты противоречат принципам LR-разбора. Следовательно, для успешного разбора требуется по-
строение LR-таблицы.
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
Учитывая, что свертку правила вида A e (e-правила) мож-
но проводить сколь угодно раз в любой ситуации, очевидно, что грамматика типа LR не может иметь e-правил. С другой стороны,
при использовании правостороннего вывода отсутствуют ограни-
чения LL-грамматик – множества направляющих символов в LR-
грамматиках могут пересекаться, и, в частности, разрешена левая рекурсия.
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·