
- •Содержание
- •Введение
- •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.8. Оптимизация кода
- •2.9. Исправление ошибок
- •2.10. Резюме
- •Контрольные вопросы
- •3. Теория языков
- •3.1. Способы определения языков
- •3.2. Грамматики
- •3.4. Распознаватели
- •3.5. Регулярные множества, их распознавание и порождение
- •5.2. LR(1) - таблица разбора
- •5.3. Построение LR – таблицы разбора
- •5.4. Сравнение LL – и LR – методов разбора
- •Контрольные вопросы
- •6. Оптимизация кода
- •6.1. Оптимизация линейного участка
- •6.1.1. Модель линейного участка
- •6.1.2. Преобразование блока
- •6.1.3. Графическое представление блоков
- •6.1.4. Критерий эквивалентности блоков
- •6.1.5. Оптимизация блоков
- •6.1.6. Алгебраические преобразования
- •6.2. Арифметические выражения
- •6.2.1. Модель машины
- •6.2.2. Разметка дерева
- •6.2.3. Программы с командами STORE
- •6.2.4. Влияние некоторых алгебраических законов
- •6.3. Программы с циклами
- •6.3.1. Модель программы
- •6.3.2. Анализ потока управления
- •Алгоритм вычисления прямого доминирования
- •6.3.3. Примеры преобразования программ
- •Удаления бесполезных операторов
- •Замена сложных операций
- •6.3.4. Оптимизация циклов
- •Перемещение кода
- •Индуктивное перемещение
- •Замена сложных операций
- •6.4. Анализ потоков данных
- •6.4.1. Интервалы
- •6.4.2. Анализ потоков данных с помощью интервалов
- •6.4.3. Несводимые графы управления
- •7. Включение действий в синтаксис
- •7.1. Получение четверок
- •7.2. Работа с таблицей символов
- •Контрольные вопросы
- •8. Проектирование компиляторов
- •8.1. Число проходов
- •8.2. Таблицы символов
- •8.3. Таблица видов
- •Контрольные вопросы
- •9. Распределение памяти
- •9.1. Стек времени прогона
- •9.2. Методы вызова параметров
- •9.3. Обстановка выполнения процедур
- •9.4. «Куча»
- •9.5. Счетчик ссылок
- •9.6. Сборка мусора
- •Контрольные вопросы
- •10. Генерация кода
- •10.1. Генерация промежуточного кода.
- •10.2. Структура данных для генерации кода
- •10.3. Генерация кода для типичных конструкций
- •10.3.1. Присвоение
- •10.3.2. Условные зависимости
- •10.3.3. Описание идентификаторов
- •10.3.4. Циклы
- •10.3.5. Вход и выход из блока
- •10.3.6. Прикладные реализации
- •10.4. Проблемы, связанные с типами
- •10.5. Время компиляции и время прогона
- •Контрольные вопросы
- •11. Исправление и диагностика ошибок
- •11.1. Типы ошибок
- •11.2. Лексические ошибки
- •11.3. Ошибки в употреблении скобок
- •11.4. Синтаксические ошибки
- •11.5. Методы исправления синтаксических ошибок
- •11.6. Предупреждения
- •11.7. Сообщения о синтаксических ошибках
- •11.8. Контекстно-зависимые ошибки
- •11.9. Ошибки, связанные с употреблением типов
- •11.10. Ошибки, допускаемые во время прогона
- •Контрольные вопросы
- •Список литературы

215
В этой таблице есть запись для каждого блока программы, и эту запись можно рассматривать как структуру, имеющую поля, которые соответствуют номеру уровня блока, размеру статического стека идентификаторов, размеру статического рабочего стека и т.д. Такую таблицу можно заполнять во время прохода, генерирующего код, и с ее помощью во время следующего прохода вычислять смещения адресов рабочего стека по отношению к текущей рамке стека.
Таким образом, во время генерации кода используются следующие структуры данных:
-нижний стек;
-верхний стек;
-стек значений операций;
-таблица блоков;
-таблица видов и таблица символов из предыдущего прохода.
10.3. Генерация кода для типичных конструкций
10.3.1.Присвоение
destination := source
Значение, соответствующее источнику, присваивается значению, которое является адресом:
р := x + y (значение x + y присвоить p)
Допустим, что статические характеристики источника и получателя находятся в вершине нижнего стека. Последовательность действий: из нижнего стека удаляются два верхних элемента, затем выполняются следующие операции.
Проверяется непротиворечивость типов получателя и источника. Так как получатель представляет собой адрес, источник должен давать что-нибудь приемлемое для присвоения этому адресу. В зависимости от реализуемого языка типыполучателя и источника можно определенным образом менять до выполнения операции присвоения. (Если источник — целое число, то его можно сначала преобразовать в вещественное, а затем присвоить адресу, имеющему тип вещественного числа.)
216
Там, где необходимо, проверяются правила области действия(для некоторых языков источник не может иметь меньшую область дейст-
вия, чем получатель).
begin pointer real xx begin real x
xx := x end
присвоение недопустимо, и это может быть обнаружено во время компиляции, если в таблице символов или в нижнем стеке имеетсяин формация об области действия.
Генерируется код присвоения, имеющий форму
ASSIGN type, S, D,
где S — адрес источника, D — адрес получателя.
Если язык ориентирован на выражения(то есть само присвоение имеет значение), статические характеристики этого значения помещаются в нижний стек.
10.3.2.Условные зависимости
if B then C else D
При генерации кода для такой условной зависимости во время компиляции выполняются три действия. Грамматика с включенными действиями имеет вид:
CONDITIONAL ® if B<A1> then C<A2> else D<A3>
Действия А1, А2, А3 (next — значение номера следующей метки, присваиваемое компилятором) означают:
А1. Проверить тип В, применяя любые необходимые преобразования типа для получения логического значения. Выдать код для перехода к L<next>, если В есть «ложь»:
JUMPF L<next>, <address of B>
Поместить в стек значение next (обычно для этого используется стек знаков операций). Увеличить next на 1. (Угловые скобки используются для обозначения значений величин, заключенных в них).
А2. Генерировать код для перехода через ветвь else (то есть переход к концу условной зависимости):
GO TO L<next>.
217
Удалить из стека номер метки (помещенный в стек действием А1), назвать ее i, генерировать код для размещения метки:
SET LABEL L<i>.
Поместить в стек значение next. Увеличить next на 1.
А3. Удалить из стека номер метки (j). Генерировать код для размещения метки:
SET LABEL L<j>.
Если условная зависимость сама является выражением, компилятор должен знать, где хранить его значение, независимо от того, какая часть является then или else.
Аналогично можно обращаться с вложенными условными выражениями.
10.3.3.Описание идентификаторов
Допустим, что типы всех идентификаторов выяснены в предыдущем проходе и помещены в таблицу символов. Адреса распределяются во время прохода, генерирующего код. Перечислим действия, выполняемые во время компиляции.
В таблице символов производится поиск записи, соответствующей идентификатору х.
Текущее значение указателя стека идентификаторов дает адрес, который нужно выделить под х. Этот адрес
(idstack, current block number, instack pointer)
включается в таблицу символов, а указатель стека идентификаторов увеличивается на статический размер значения, соответствующего х.
Если х имеет динамическую часть (например, массив), то генерируется код для размещения динамической памяти во время прогона.
10.3.4.Циклы
Рассмотрим простейший пример цикла:
for i to 10 do something.
Для генерации кода требуется четыре действия, которые размещаются следующим образом:
for i <A1> to 10 <A2> do <A3> something <A4>.
Эти действия таковы.
А1. Выделить память для управляющей переменнойi. Поместить сначала в эту память 1:
MOVE «1», address (управляющая переменная).
218
А2. Генерировать код для записи в память значения верхнего предела рабочего стека:
MOVE address (ulimit) (wostack, current block number, wostack pointer)
(wostack pointer — указатель рабочего стека).Увеличить указатель рабочего стека и уменьшить указатель нижнего стека, где хранились статические характеристики верхнего предела.
А3. Поместить метку
SET LABEL L<next>.
Увеличить next на 1.
Выдать код для сравнения управляющей переменной с верхним пределом и перейти кL<next>, если управляющая переменная больше верхнего предела:
JUMPG L<next>, address (controlled variable), address (ulimit).
Поместить в стек значение next. Поместить в стек значение (next
– 1). Увеличить next на 1.
А4. Генерировать код для увеличения управляющей переменной
PLUS address (controlled variable), 1.
Удалить из стека номер (i). Генерировать код для перехода к
L(i):
GO TO L<i>.
Удалить из стека номер метки (j). Поместить метку в конец цикла:
SET LABEL L<j>.
Таким образом, цикл
for i to 10 do something
генерирует код следующего вида:
MOVE «1», address (controlled variable) MOVE address (ulimit), wostack pointer SET LABEL L1
JUMPG L2 address (controlled variable), address (ulimit) (something)
GO TO L1
SET LABEL L2.
Действия А4 можно видоизменять, если приращение управляющей переменной будет не стандартным (1), а иным.
for i by 5 to 10 do.
Для этого придется вычислять приращение и хранить его значение в рабочем стеке, чтобы использовать как приращение.
Если цикл содержит часть while, то
for i to 10 while a<b do.