- •Университет наяновой
- •М. А. Шамашов основные структуры данных и алгоритмы компиляции
- •Предисловие
- •Введение
- •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
3.3. Неупорядоченная таблица или метод линейного списка
Наиболее простой метод поиска состоит в том, что входное слово сопоставляется с каждым элементом хранимым в памяти массива или списка слов, до тех пор, пока не происходит совпадение (если оно возможно). Добавляются же элементы к текущему концу заполненной части массива или к последнему элементу в списке. У этого метода есть лишь один недостаток: поиск по длинной таблице займет много времени! Если в таблице N элементов, то ожидаемое число сопоставлений, необходимых для обнаружения случайно выбранного элемента, равно (N+1)/2.
3.4. Упорядоченная таблица. Бинарный, двоичный или логарифмический поиск
Время поиска по таблице большого объема можно значительно сократить, если элементы таблицы упорядочены по аргументу, например, лексикографически, т.е. по алфавиту. Эффективным методом поиска в таком списке является бинарный (двоичный, логарифмический) поиск. Имя S, которое следует найти, сравнивается с аргументом элемента с номером (N+1)/2 в середине таблицы. Если этот элемент не является требуемым, то мы должны просматривать только блок элементов пронумерованных от 1 до (N+1)/2–1 или блок от (N+1)/2+1 до N в зависимости от того, меньше или больше искомый идентификатор S того элемента, с которым его сравнивали. Затем мы повторяем процесс для блока меньшего размера. Так как на каждом шаге число элементов, которые могут содержать S, сокращается наполовину, то максимальное число сравнений –1+log2N. Если N=2, то необходимо, самое большое, 2 сравнения (N=4, – то 3, N=8, – то 4). Для N=128 потребуется не более 8 сравнений, в то время как среднее время поиска в неупорядоченной таблице того же объема составит 64 сравнения.
Основной недостаток бинарного поиска – необходимость преобразования таблицы при добавлении элементов. Надо не просто добавлять элементы к концу таблицы, а вставлять их туда, где они обязаны находится в соответствии с выбранным порядком, т.е. использовать метод упорядоченных вставок. Он применяется, когда таблица часто просматривается до того, как она полностью заполнена, и, следовательно, должна быть упорядочена на всех этапах. Элемент S вставляется в таблицу следующим образом:
1. Находится k для которого Sk < S < Sk+1.
-
Элемент N сдвигается на позицию N+1, N–1 – на позицию N и т.д. Элемент с номером k+1 сдвигается на позицию k+2.
-
S заносится на место k+1.
Если заполнение таблицы предшествует поискам, то упорядочение проще сделать после заполнения.
Процедуру двоичного поиска можно легко применить к бинарным деревьям, где каждый элемент таблицы имеет два указателя. Один из них указывает на середину списка слов, которые идут после данного слова, а другой на середину списка слов, которые предшествую ему. Такое дерево зачастую называют деревом поиска.
На рис. 3.3 показано представление таблицы в виде дерева поиска для некоторого подмножества ключевых слов и стандартных типов языка С.
При поиске слова for в этом дереве оно последовательно сравнивается со словами if, do, enum и for.
Разместив дерево в памяти, можно добавлять к нему слова не нарушая этого размещения. Слово bitset, например, присоединится к слову break, а слово delete к слову default. Но, к сожалению, после таких добавлений результирующее время поиска может превышать логарифмическую границу. Для слова delete, например, потребуется 6 сравнений. Если желательно расширить множество слов, сохранив логарифмическое время поиска, необходимо проводить реорганизацию структуры дерева.