- •Университет наяновой
- •М. А. Шамашов основные структуры данных и алгоритмы компиляции
- •Предисловие
- •Введение
- •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.1.2. Рекурсивный спуск
Суть метода рекурсивного спуска состоит в том, что для каждого нетерминала грамматики X разрабатывается рекурсивная процедура, осуществляющая разбор цепочек, выводимых из X. Процедуре передается позиция входной цепочки, начиная с которой предполагается наличие фрагмента, выводимого из X. Процедура сопоставляет цепочку в указанном месте с правыми частями правил для X и вызывает по мере необходимости другие процедуры для распознавания промежуточных целей.
Чтобы прокомментировать этот метод, запишем процедуры для нетерминальных символов следующей грамматики:
инстр перем вырif выр then инстр
if выр then инстр else инстр
перем ii (выр)
выр терм выр терм
терм множ терм множ
множ перем (выр)
Перепишем эту грамматику, используя расширенную Бэкусову нормальную форму (РБНФ) с включением факультативных (необязательных) фрагментов – и итерации– {} (фрагмент повторяется ноль или более раз).
инстр перем выр if выр then инстр [ else инстр ]
перем i [ (выр) ]
выр терм { терм }
терм множ { множ }
множ перем (выр)
Запишем процедуры на языке, подобном Паскалю с соблюдением следующих условий:
(1) Глобальная переменная NxtSymb всегда содержит следующий символ, а точнее очередную лексему исходной программы, подлежащую обработке. При вызове процедуры для поиска новой цели первый символ, который процедура должна исследовать уже находится в NxtSymb. Для того чтобы обеспечить такую работу, перед тем как выйти из процедуры с сообщением об успехе, символ, следующий за уже рассмотренной подцепочкой, помещается в NxtSymb.
(2) Процедура Scan готовит очередной символ (лексему) входной цепочки и помещает его в NxtSymb.
(3) Программа Error вызывается при обнаружении ошибки. Она выводит сообщение об ошибке и для идентификации ошибки ей можно передать код ошибки в качестве параметра. Эта процедура может завершать разбор или пытаться нейтрализовать ошибку.
(4) Для того чтобы начать синтаксический анализ исходной строки, в головной программе сначала вызывается программа Scan, которая поместит первый символ в NxtSymb, а затем вызывается процедура State, анализирующая инструкцию.
Процедуры для всех нетерминалов грамматики и комментарии к ним представлены на рис. 5.4. Преимущества рассмотренного метода очевидны. Программируя компилятор, можно реорганизовать правила так, чтобы они согласовывались с процедурами. Предполагается, что автор компилятора настолько хорошо знаком с исходным языком, что может провести модификацию грамматики, которая избавляет от возвратов. Метод сохраняет свою гибкость и по отношению к семантической обработке. С этой целью в любое место каждой процедуры можно включить группу команд, выполняющих семантические действия по переводу исходного текста.
Основной же недостаток метода состоит в том, что на программирование и отладку синтаксического анализатора затрачивается больше усилий, чем в чисто автоматизированных системах. Тем не менее, он нашел применение при построении целого ряда компиляторов. Кроме того, совсем не трудно написать такую программу, которая воспринимала бы правила подходящей грамматики и порождала бы рекурсивные процедуры на языке программирования высокого уровня для нетерминалов предложенной грамматики.