- •Университет наяновой
- •М. А. Шамашов основные структуры данных и алгоритмы компиляции
- •Предисловие
- •Введение
- •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
6.3. Семантические подпрограммы для перевода в тетрады
Попытаемся провести разбор выражения a(bc), используя грамматику простого предшествования для арифметических выражений (см. пример 5.15 и рис. 5.18), и сгенерируем тетрады
, b, c, M1
, a, M1, M2
по ходу дела составляя семантические подпрограммы. Логичнее всего генерировать первую тетраду в семантической подпрограмме для правила E ET1. Итак, будем выполнять восходящий разбор по алгоритму Вирта–Вебера до того момента, пока не возникнет необходимость в применении этого правила. На рис. 6.9 а приводятся сентенциальные формы, порождаемые на каждом шаге (подчеркнута основа), а на рис. 6.9 б показано частичное семантическое дерево, построенное к этому моменту.
На следующем шаге ET1 приводится к E; одновременно с этим семантическая подпрограмма должна выдать тетраду. К сожалению, мы не можем ее построить, так как текущая сентенциальная форма не содержит информации об именах операндов E и T1. Эта информация была потеряна, когда выполнялись редукции M b и M c.
При получении ПОЛИЗа у нас не возникало подобных затруднений, так как при выполнении редукций M b и M c имена b и c заносились в выходную цепочку. Очевидно, при генерировании тетрад необходимо где–то хранить имена идентификаторов (значения констант) до момента их использования и это сохранение должна выполнять подпрограмма, связанная с правилом M i , где i – произвольный идентификатор или константа. И лучшим местом такого хранения, конечно же, является стек. Назовем его вспомогательным стеком и обозначим через V, а индекс m (вначале он равен нулю) будет указывать на вершину этого стека.
Таким образом, семантическая подпрограмма, связанная с правилом M i будет иметь вид:
INC(m); V[m]C[i]
Напомним, что C[i] – вершина синтаксического стека из алгоритма восходящего разбора, рассмотренного в разделе 5.2.1.
Точно также как и при переводе в ПОЛИЗ здесь будут отсутствовать семантические действия, связанные с правилами TM, T1T, ET1 и M(E). С остальными же правилами будет связана процедура формирования тетрады с четырьмя параметрами – PutTetrad(O, X, Y, Z), где O – операция, X и Y – операнды, а Z – результат. В качестве результата Z будут использоваться генерируемые имена M1, M2, , обозначающие подвыражения. Для этих целей будет использоваться счетчик n, который вначале имеет нулевое значение. Таким образом, семантическая подпрограмма связанная с правилом E ET1 будет иметь вид:
INC(n); PutTetrad(‘+’, V[m–1], V[m], Mn); DEC(m); V[m]:= Mn;
Суть семантических подпрограмм такого сорта состоит в том, что генерируется тетрада, где в качестве операндов используется два верхних значения из временного стека V, изначально хранящие имена идентификаторов и значения констант, помещенных туда при редукции по правилу M i . После формирования тетрады два верхних значения из стека V (для бинарной операции) заменяются на имя результата тетрады. Полный перечень семантических действий по переводу в тетрады, связанный с правилами грамматики арифметических выражений, представлен на рис. 6.10.