- •Университет наяновой
- •М. А. Шамашов основные структуры данных и алгоритмы компиляции
- •Предисловие
- •Введение
- •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.1. Общий вид таблиц
Таблицы всех типов имеют общий вид, представленный на рис. 3.1, где слева перечисляются аргументы, а справа соответствующие значения.
Каждый элемент таблицы обычно занимает более одного машинного слова. Если элемент занимает K слов памяти и нужно хранить N элементов, то для организации таблицы необходимо иметь KN слов памяти. Расположить информацию можно двумя способами :
1. Каждый элемент поместить в K последовательных слов и иметь таблицу из KN слов.
2. Иметь K таблиц, с именами T1, T2, ...,Tk из N слов в каждой таблице. Весь i–ый элемент будет при этом находиться в словах T1 i, T2 i, ...,Tk i.
Вопрос выбора между этими двумя методами – это только вопрос удобства программирования.
В нашем случае аргументами таблицы являются символы или идентификаторы, а значениями их характеристики (атрибуты). Так как число символов в идентификаторе может быть самым разным, то как уже отмечалось, в аргументе, обычно, вместо самого идентификатора помещают указатель на идентификатор. Это сохраняет фиксированный размер аргумента. Сами же идентификаторы хранятся в специальном списке строк. Число литер в каждом идентификаторе может храниться как часть аргумента или в списке идентификаторов прямо перед ним. На рисунке 3.2 показаны оба эти способа на примере таблицы, содержащей элементы для идентификаторов I, max и J.
Если число элементов в поле значения переменно, то в этом поле таблицы также следует помещать указатель на эту информацию. Формат таблицы лучше оставлять фиксированным.
Рассмотрим существующие способы организации таблиц и оценим время требуемое на поиск элементов и их добавление.
3.2. Прямой доступ к таблице или метод индексов
Суть этого метода состоит в том, что по символам цепочки (идентификатора) вычисляется индекс, который обеспечивает прямой доступ к таблице (индекс массива). В качестве примера рассмотрим проблему идентификации переменных языка MINI–BASIC. Переменная в нем либо английская буква, либо буква, за которой следует цифра. Всего имеется 286 допустимых переменных MINI–BASIC. Так как это число невелико, то можно позволить себе завести по одному элементу таблицы для каждой допустимой переменной. Тогда проблема поиска переменной сводиться к простому преобразованию имени в индекс. Один из способов состоит в присваивание индексу значения от 1 до 26 для каждой входной буквы английского алфавита. Далее, если следующий символ – произвольная цифра d, то к индексу буквы прибавляется число 26(d+1). Это значит, что переменная Z получит номер 26, A0 – 27, а Z9–286.
Индексный метод можно эффективно применять при выполнении 3–х условий. Во–первых, число слов не должно быть слишком большим. Поэтому, такой метод нельзя применять даже для имен переменных ФОРТРАНа (не более шести символов), т.к. их более миллиарда. Во–вторых, индекс должен легко вычисляться. Это условие может исключить даже небольшое множество ключевых слов, так как хороший способ построения индекса по ним вряд ли удастся предложить. В–третьих, объем множества слов фиксируется при построении, так как изменение метода вычисления индекса во время компиляции требует реорганизации всей таблицы. Если учесть эти три условия, то становится очевидным, что рассматриваемым методом можно воспользоваться нечасто. Тем не менее, когда он применим, поиск и включение элемента (отметка что он включен) осуществляется с минимальными затратами времени.