
- •Теория вычислительных процессов и структур
- •1. Предварительные математические сведения
- •1.2. Операции над множествами Объединение множеств
- •Пересечение множеств
- •Разность множеств
- •1.3. Множества цепочек
- •1.4. Языки
- •1.5. Алгоритмы
- •1.6. Некоторые понятия теории графов
- •2. Введение в компиляцию
- •2.1. Задание языков программирования
- •2.2. Синтаксис и семантика
- •2.3. Процесс компиляции
- •2.4. Лексический анализ
- •2.5. Работа с таблицами
- •2.6. Синтаксический анализ
- •2.7. Генератор кода
- •Алгоритм.
- •2.8. Оптимизация кода
- •2.9. Исправление ошибок
- •2.10. Резюме
- •3. Теория языков
- •3.1. Способы определения языков
- •3.2. Грамматики
- •Пример.
- •3.3. Грамматики с ограничениями на правила
- •3.4. Распознаватели
- •3.5. Регулярные множества, их распознавание
- •3.6. Регулярные множества и конечные автоматы
- •3.7. Графическое представление конечных автоматов
- •3.8. Конечные автоматы и регулярные множества
- •3.9. Минимизация конечных автоматов
- •3.10. Контекстно-свободные языки
- •3.10.1. Деревья выводов
- •3.10.2. Преобразование кс–грамматик
- •3.10.3. Грамматика без циклов
- •3.10.4. Нормальная форма Хомского
- •3.10.5. Нормальная формула Грейбах
- •3.11. Автоматы с магазинной памятью
- •3.11.1. Основные определения
- •3.11.2. Эквивалентность мп-автоматов и кс-грамматик
- •4.1. Эквивалентность мп-автоматов и кс-грамматик
- •4.2. Ll(1)-грамматики
- •4.3. Ll(1)-таблица разбора
- •5. Синтаксический анализ снизу вверх
- •5.1. Разбор снизу вверх
- •5.2. Lr(1) - таблица разбора
- •5.3. Построение lr – таблицы разбора
- •5.4. Сравнение ll – и lr – методов разбора
- •6. Включение действий в синтаксис
- •6.1. Получение четверок
- •6.2. Работа с таблицей символов
- •7. Проектирование компиляторов
- •7.1. Число проходов
- •7.2. Таблицы символов
- •Identifier, type.
- •Int procedure rehash(int n)
- •Int procedure rehash(int n)
- •7.3. Таблица видов
- •8. Распределение памяти
- •8.1. Стек времени прогона
- •Integer a, b, X, y
- •Int table[1:10, -5:5].
- •8.2. Методы вызова параметров
- •8.3. Обстановка выполнения процедур
- •8.4. «Куча»
- •8.5. Счетчик ссылок
- •8.6. Сборка мусора
- •9. Генерация кода
- •(Тип – адреса, номер - блока, смещение).
- •9.2. Структура данных для генерации кода
- •9.3. Генерация кода для типичных конструкций
- •9.3.1. Присвоение
- •9.3.2. Условные зависимости
- •If b then c else d
- •9.3.3. Описание идентификаторов
- •9.3.4. Циклы
- •9.3.5. Вход и выход из блока
- •9.3.6. Прикладные реализации
- •9.4. Проблемы, связанные с типами
- •9.5. Время компиляции и время прогона
- •10. Исправление и диагностика ошибок
- •10.1. Типы ошибок
- •10.2. Лексические ошибки
- •10.3. Ошибки в употреблении скобок
- •Begin end
- •Case esac
- •10.4. Синтаксические ошибки
- •10.5. Методы исправления синтаксических ошибок
- •End begin
- •10.6. Предупреждения
- •10.7. Сообщения о синтаксических ошибках
- •10.8. Контекстно-зависимые ошибки
- •Identifier xyz not declared
- •Identifier blank alredy declared in block
- •10.9. Ошибки, связанные с употреблением типов
- •Int I; char c;
- •10.10. Ошибки, допускаемые во время прогона
- •10.11. Ошибки, связанные с нарушением ограничений
9. Генерация кода
9.1. Генерация промежуточного кода.
Как отмечалось ранее, код генерируется при обходе дерева, построенного анализатором. Обычно в современных трансляторах генерация кода осуществляется параллельно с построением дерева, но может осуществляться и как отдельный проход. Генерация кода осуществляется в два этапа:
генерация не зависящего от машины промежуточного кода;
генерация машинного кода для конкретной ЭВМ.
Во многих компиляторах оба эти процесса осуществляются за один проход.
Обычно промежуточный код получается разбиением сложной структуры исходного языка на более удобные для обращения элементы.
Одним из распространенных видов промежуточного кода является четверки. Например, выражение
можно представить как четверки следующим образом:
Здесь целые числа соответствуют идентификаторам, присвоенным компилятором. Четверки можно считать промежуточным кодом высокого уровня. Такой код называют трехадресным кодом (два адреса для операндов и один для результата).
Другой вариант кода – тройки (двухадресный код). Каждая тройка состоит из двух адресов операндов и знака операции.
Выражение
можно представить в виде четверок:
и виде троек:
Если сам операнд является тройкой, то используется ее позиция (регистр) для хранения результата.
Как тройки,так и четверки можно распространить не только на выражения, но и на другие конструкции языка. Например, присвоение
в виде четверки представляется как
,
а в виде тройки – как
.
Не менее популярны
в качестве промежуточного кода префиксные
и постфиксные нотации.
В префиксной
нотации каждый знак операции появляется
перед своими операндами,
а в
постфиксной
- после.
Например, инфиксное выражение
в префиксной нотации имеет вид
,
а в постфиксной -
.
Префиксная нотация известна также как польская запись, а постфиксная - как обратная польская запись (запись Лукашевича). Например, выражение
в префиксной форме записывается следующим образом:
,
а в постфиксной так:
.
В префиксной и постфиксной нотации скобки уже не употребляются, т.к. здесь никогда не возникает сомнения относительно того, какие операнды принадлежат тем или иным знакам операций. В этих нотациях не существует приоритета знака операции.
Перегруппировку
в результате преобразования
в
можно осуществить с помощью стека.
При этом алгоритм сведется к трем
действиям:
напечатать идентификатор, когда он встретится при чтении инфиксного выражения слева направо;
поместить в стек знак операции, когда он встретится;
когда встретится конец выражения (или подвыражения), выдать на печать этот знак операции, который находится в стеке.
Префиксные и постфиксные выражения можно также получать из представления выражения в виде бинарного дерева (рис. 9.1).
Пример:
Рис. 9.1. Бинарное дерево
Чтобы получить представление префиксного выражения дерево обходят сверху в порядке:
посещение корня;
обход левого поддерева сверху;
обход правого поддерева сверху,
что дает
.
Для получения постфиксного представления дерево обходят снизу:
обход левого поддерева снизу;
обход правого поддерева снизу;
посещение корня.
В результате имеем:
.
Далее все исследования будем проводить в терминах промежуточного языка
тип - команды параметры
Тип команды может быть,например, вызовом стандартного обозначения операции:
.
Здесь II+ - сложение двух целых чиселA, B и C служат во время прогона адресами двух операндов и результата. Для того чтобы в промежуточном коде можно было воспользоваться адресами во время прогона, распределение памяти к этому моменту должно быть уже закончено.
Промежуточный код напоминает префиксную нотацию в том смысле, что знак операции всегда предшествует своим операндам. Но он имеет менее общий характер, т.к. сами операнды не могут быть префиксными выражениями. При получении промежуточного кода для хранения адресов операндов до тех пор,пока не будет напечатан знак операции, используется стек. Поскольку знак операции можно установить лишь после того, как будут известны операнды, стек служит также для хранения каждого знака операции на то время, пока не определены оба операнда.
Адрес на время прогона соотносится со стеком, и каждый адрес можно представить тройкой вида