
- •Содержание
- •Введение
- •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. Ошибки, допускаемые во время прогона
- •Контрольные вопросы
- •Список литературы
220
10.3.6.Прикладные реализации
Во время компиляции в соответствии с прикладной реализацией идентификатора, например х в х + 4, необходимо:
1)найти в таблице символов запись, соответствующую определяющей реализации (int x) идентификатора;
2)поместить в нижний стек статические характеристики, соответст-
вующие идентификатору.
Подразумевается, что в нижний стек также помещаются статические характеристики констант и т.д.
10.4. Проблемы, связанные с типами
Основной проблемой для трансляторов с языков высокого уровня является приведение (автоматическое изменение) типов. Здесь можно выделить, как минимум, шесть задач.
1)Распроцедуриваниепереход от procedure real к real.
2)Разыменование, например переход от pointer real к real.
3)Объединение, например переход от real к strutct(real,char).
4)Векторизация, например переход от real к real [ ].
5)Обобщение, например переход от int к real.
6)Чистка, например переход от real к void.
Возможность осуществления приведения зависит от синтаксической позиции. Например, в левой части присвоения может иметь место только распроцедуривание (вызов процедур без параметров), а в правой части - любое из шести приведений. Иногда возникает необходимость нескольких приведений. Например, если х имеет вид pointer real и a –pointer int, то прежде чем производить присвоениех=а, необходимо сначала разыменовать, а затем обобщить.
В зависимости от того, какие приведения могут выполняться в синтаксических позициях, последние называются мягкими, слабыми, раскрытыми, крепкими и сильными. Например, левая часть присвоения называется мягкой (допускает только распроцедуривание), а правая часть - сильной (допускает любое приведение). Кроме ограничений типов приведений, разрешаемых в заданной синтаксической позиции, существуют правила, определяющие порядок осуществления различных приведений. Например, объединение может произойти только один раз и не должно следовать за векторизацией. Можно определить грамматику, которая генерирует все допустимые последовательности приведений в заданной синтаксической позиции, например:
SOFT => deprocedure |
221
deprocedure SOFT
Любое предложение, генерированное посредством SOFT, представляет собой допустимую последовательность приведений в мягкой синтаксической позиции (т.е. в левой части присвоения).
Для раскрытия позиции(например, индекса в a[i]) справедливы следующие правила:
MEEK => deprocedure | deprocedure MEEK | dereference | dereference MEEK.
Другими словами, в раскрытой позиции можно выполнять распроцедуривание и разыменование любое число раз и в любом порядке, например:
pointer procedure poiter int в вид int
Для сильной позиции (например, правая часть присвоения или параметр в вызове процедуры) правила таковы:
STRONG => dereference STRONG | deprocedure STRONG | unit |
unit ROW | widen |
widen widen | widen ROW | widen widen ROW | ROW |
ROW => row | row ROW
Вид данных до выполнения приведений называется априорным, а после выполнения - апостериорным. В случае сильных и раскрытых синтаксических позиций известны и априорный и апостериорный виды. Для других позиций известен лишь априорный вид и некоторая информация об апостериорном виде, например о том, что он должен начинаться со struct или pointer struct, или о том, что он не должен начинаться с proc, как в левой части присвоения.
Компилятор, применяя соответствующую грамматику, генерирует последовательность приведений из априорного вида к известному либо к подходящему апостериорному виду. Если нельзя найти никакой последовательности приведений, программа синтаксически неправильная.
222
С другой стороны, если подходящая последовательность существует, компилятор, применяя приведения по порядку, генерирует код времени прогона.
Еще один вид приведения - чистка. Чистка представляет собой особую форму приведения и происходит в тех местах, где стоит точка с запятой
x=y;
Еще одна сложность связана с выбирающим предложением. В предложении
x + if b then 1 else 2.3
во время компиляции необходимо знать тип(вид) правого операнда знака «+». Все варианты выбирающего предложения должны приводить к общему виду, называемому объектным. Этот процесс называется уравнением, и его правила подразумевают, что последовательность сильных приведений можно применять во всех вариантах, кроме одного, в котором используется лишь последовательность приведений, уместных лишь для синтаксической позиции выбирающего предложения. В вышеприведенном примере выбирающее предложение находиться в крепкой синтаксической позиции, которая не допускает расширения. Однако внутри выбирающего предложения один вариант допускает сильное приведение, что может повлечь за собой расширение. В этом случае объектным видом окажетсяreal, и первый вариант следует расширить, а второй нежелательно подвергать приведению.
Действия компилятора при обращении с выбирающими предложениями заключаются в том, что статические характеристики всех вариантов выбирающего предложения помещаются в нижний стек, а затем выводятся объектный вид и различные последовательности приведения для каждого варианта. Если какая-либо последовательность вызывает необходимость генерации кода во время прогона, ее можно выделить в отдельный поток, и между двумя этими потоками ввести указатели, чтобы во время следующего прохода код можно было -со единить в нужном порядке.
10.5. Время компиляции и время прогона
Как отмечалось ранее, генератор кода во время компиляции обращается к нижнему стеку и генерирует код операций, которые будут выполняться во время прогона. Что именно должно быть сделано во
223
время компиляции, а что во время прогона существенно зависит от языка программирования. Тем не менее, разработчик компилятора имеет возможность разделять функции компиляции и прогона. Например, не вполне ясно, повлечет ли разыменование за собой какие-либо действия при прогоне или нет. На первый взгляд может показаться, что нет, так как это просто замена в памяти одного значения другим и изменение адреса времени прогона. Новым адресом будет тот, на который указывает первоначальный адрес. Однако, значение указателя может быть неизвестным при компиляции, новый адрес можно обозначить, изменив первоначальный адрес так, чтобы в нем указывался дополнительный косвенный уровень. Т.е. в некоторых случаях требуется выполнить действие (переслать значение по новому адресу).
Для повышения эффективности выдаваемого кода при компиляции можно проделать дополнительную работу, которую показывают компиляцией. В оптимизацию входит удаление кодов из циклов там, где это не влияет на значение программы, исключение возможности вычисления идентичных выражений более одного раза и. Особоет.д
внимание уделяется возможности избежать перезаписи сложных структур данных во время прогона.
Контрольные вопросы
1.Технология создания промежуточного кода. Виды промежуточного кода.
2.Алгоритм преобразования арифметического выражения в префиксную и постфиксную форму.
3.Формализация записи промежуточного кода.
4.Структуры данных для генерации промежуточного кода.
5.Алгоритм генерации промежуточного кода для арифметических выражений.
6.Таблица блоков, ее назначение.
7.Генерация кода для присвоения.
8.Генерация кода для условных зависимостей.
9.Генерация кода для описания идентификаторов.
10.Генерация кода для циклов.
11.Генерация кода для входа и выхода из блока.
12.Генерация кода для прикладной реализации.
13.Проблемы генерации кода, связанные с типами.
14.Работа генератора кода во время компиляции и во время прогона.