- •Университет наяновой
- •М. А. Шамашов основные структуры данных и алгоритмы компиляции
- •Предисловие
- •Введение
- •1. Краткий обзор процесса компиляции
- •2. Лексический анализ
- •0123...9 Пробел
- •3. Организация таблиц компилятора
- •3.1. Общий вид таблиц
- •3.2. Прямой доступ к таблице или метод индексов
- •3.3. Неупорядоченная таблица или метод линейного списка
- •3.4. Упорядоченная таблица. Бинарный, двоичный или логарифмический поиск
- •3.5. Сбалансированные деревья
- •3.6. Деревья оптимального поиска
- •3.7.1. Рехеширование
- •3.7.3. Метод цепочек или гроздей
- •4. Общие методы синтаксического анализа
- •4.1. Нисходящий разбор с возвратами
- •4.2. Восходящий разбор с возвратами
- •4.3. Символьный препроцессор на основе бэктрекинга
- •4.3.1. Фаза анализа и перевода грамматики во внутреннее представление
- •4.3.2. Лексичекий анализ в сп
- •4.3.3. Синтаксический анализ в сп
- •4.3.4. Выполнение семантических действий
- •5. Однопроходный синтаксический анализ без возвратов
- •5.1. Ll(k) языки и грамматики
- •5.1.1. Предсказывающие алгоритмы разбора и разбор для ll(1)-грамматик
- •5.1.2. Рекурсивный спуск
- •5.2. Языки и грамматики простого предшествования
- •Xy, если u xy
- •X y, если u xU1) (y l(u1))
- •X y, если (u u1y) (X r(u1)) or
- •5.2.1. Алгоритм Вирта–Вебера для анализа языков простого предшествования
- •5.2.2. Функции предшествования.
- •5.2.3. Проблемы построения грамматик предшествования
- •5.3. Операторная грамматика предшествования
- •6. Введение в семантику
- •6.1. Внутренние формы исходной программы
- •6.1.1. Польская инверсная запись
- •If выр then инстр 1 else инстр 2
- •6.1.2. Интерпретация полиЗа
- •6.1.3. Генерирование команд по полиЗу
- •6.1.4. Тетрады и триады
- •6.2. Семантические подпрограммы перевода инфиксной записи в полиз и аспекты их реализации
- •6.3. Семантические подпрограммы для перевода в тетрады
- •6.4. Метод замельсона–бауэра для перевода в полиз и тетрады
- •6.5. Нейтрализация ошибок
- •6.5.1. Исправления орфографических ошибок
- •6.5.2. Нейтрализация семантических ошибок
- •6.5.3. Нейтрализация синтаксических ошибок
- •7. Машинно-независимая оптимизация программ
- •7.1. Исключение общих подвыражений
- •7.2. Вычисления во время компиляции
- •7.3. Оптимизация булевых выражений
- •7.4. Вынесение инвариантных вычислений за цикл
- •8. Машинно-зависимые фазы компиляции
- •8.1. Распределение памяти
- •8.2. Генерация кода и сборка
- •8.3. Трансляция с языка ассемблера
- •Заключение
- •Список литературы
- •Содержание
- •1. Краткий обзор процесса компиляции 5
- •2. Лексический анализ 10
- •3. Организация таблиц компилятора 16
- •4. Общие методы синтаксического анализа 28
- •5. Однопроходный синтаксический анализ без возвратов 52
- •6. Введение в семантику 78
- •7. Машинно-независимая оптимизация программ 102
- •8. Машинно-зависимые фазы компиляции 109
5.2. Языки и грамматики простого предшествования
Одна из центральных проблем детерминированного восходящего синтаксического анализа состоит в однозначном определении основы – самой левой подцепочки сентенциальной формы, которую требуется редуцировать на данном этапе разбора. Хотелось бы, “перемещаясь” по цепочке слева направо и рассматривая одновременно только два соседних символа, суметь определить, нашли ли мы хвост основы (конец редуцируемой части). А затем, продвигаясь назад к левому концу найти голову основы, опять таки анализируя лишь два соседних символа. То есть мы сталкиваемся с такой задачей: если задана сентенциальная форма . . . x y . . . (где x, y ), то всегда ли x – хвост основы, или x и y принадлежат основе, или возможны другие варианты? Для решения этой задачи требуется еще до начала разбора исследовать грамматику языка и принять решения относительно каждой пары символов x и y.
Пусть x, y грамматики G. Предположим, что в языке, определяемом G, при выводе цепочки языка существует сентенциальная форма . . . x y . . . . На некотором этапе разбора либо x, либо y, либо оба символа одновременно должны войти в основу. При этом возникают три различных варианта:
U
. .
.
. . . x
y
. .
Рис. 5.5
U
. .
.
. x
y
. . .
.
Рис. 5.6
U
.
. x
y
. . . .
. .
Рис. 5.7
3). y – часть основы, а точнее ее голова, а x в основу не входит. Эта ситуация обозначается x y и можно говорить, что x меньше y или x следует за y. При этом символ y должен быть первым, самым левым в правой части некоторого правила U y . . . (рис. 5.7).
Если сентенциальной формы . . . x y . . . при выводе цепочек в грамматике G не существует, то мы будем считать что между упорядоченной парой символов (x, y) не определено отношение предшествования (обозначим этот факт как xy).
Заметим, что ни одно из определенных выше отношений не является симметричным: из xy вовсе не следует, что y x , и уж тем более x y не свидетельствует о том, что y x .
Пример 5.8.
Рассмотрим в качестве примера грамматику G5 со следующим множеством продукций:
S bMb M (L a L Ma)
Языку L, порождаемому грамматикой G5, принадлежат цепочки: bab , b(aa)b , b((aa)a)b , b(((aa)a)a)b и т.д.
Попробуем определить отношения предшествования между символами данной грамматики. На рис. 5.8 представлены различные сентенциальные формы, их синтаксические деревья, основы и те отношения, которые можно получить с их помощью.
На первый взгляд может показаться, что трудно найти все отношения предшествования между символами грамматики. Создается впечатление, что для этого необходимо построить массу синтаксических деревьев. Да и где гарантия, что все отношения уже найдены?
Дадим более строгие определения отношениям предшествования с тем, чтобы формализовать алгоритм их выявления. Прежде всего определим множество самых левых L(U) и самых правых R(U) символов грамматики для нетерминала U:
L(U) = {x | x ( (U x) or (U U1) (x L(U1))},
где здесь и далее U, U1, U2 (нетерминалы), а , ( ) (любые цепочки из терминалов и нетерминалов, возможно пустые).
R(U) = {x | x ( (U x) or (U U1) (x R(U1))},
Иными словами, символы, которые записаны слева во всех правых частях правил (альтернативах) нетерминала U и составляют множество самых левых символов U – L(U). И далее, если левым символом U является нетерминал U1, то левыми для U будут и все самые левые символы U1 (L(U1)L(U)). Множество R(U) определяется аналогично.
Пример 5.9. На рис. 5.9 а представлена таблица самых левых и самых правых символов для нетерминалов грамматики, рассматриваемой в качестве примера 5.8 в данном разделе. На рис. 5.9 б приведена матрица предшествования – наиболее удобная форма представления отношений предшествования между символами грамматики.
Матрица предшествования с рис. 5.9 б формировалась на основании следующих определений отношений предшествования: