
- •Содержание
- •Введение
- •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. Ошибки, допускаемые во время прогона
- •Контрольные вопросы
- •Список литературы

24
4 |
3 |
|
2 |
||
|
а |
b |
|
1 |
|
c |
Рис. 1.11. Упорядоченный граф
Контрольные вопросы
1.Операции над множествами.
2.Отношения.
3.Замыкание отношений.
4.Отношения порядка.
5.Отображения.
6.Множества цепочек.
7.Операции над цепочками.
8.Языки.
9.Операции над языками.
10.Итерация языка.
11.Гомоморфизм.
12.Алгоритмы.
13.Частичные алгоритмы.
14.Полные алгоритмы.
15.Рекурсивные алгоритмы.
16.Задание алгоритмов.
17.Ориентированные графы.
18.Ориентированные ациклические графы.
19.Деревья. Упорядоченные графы.
2.Введение в компиляцию
2.1. Задание языков программирования
Операции машинного языка вычислительной машины значительно более примитивные, по сравнению со сложными функциями, встречающимися в математике, технике и других областях. Хотя любую функцию, которую можно задать алгоритмом, можно реализовать в виде последовательности чрезвычайно простых команд машинного языка, в большинстве приложений предпочтительнее использовать
25
язык высокого уровня, элементарные команды которого приближаются к типу операций, встречающихся в приложениях. Например, если выполняются матричные операции, то для выражения того обстоятельства, матрица А получается перемножением матриц В и С, удобнее написать команду вида
А=В*С,
чем длинную последовательность операций машинного языка.
Языки программирования могут существенно облегчить, упростить алгоритмическую запись, однако они порождают ряд новых существенных проблем, одна из них - необходимость трансляции языка программирования на машинный язык.
Другая проблема - проблема задания самого языка. Задавая язык программирования, как минимум необходимо определить:
1)множество символов, которые можно использовать для написания правильных программ;
2)множество правильных программ;
3)«смысл» правильной программы.
Первая проблема решается довольно легко. Определить множество правильных программ – это искусство.
Пример. Для многих языков программирования конструкция
L: GOTO L
правильная с точки зрения языка.
Самая сложная – третья проблема. Для решения третьей проблемы было предпринято несколько подходов. Один из методов заключается в определении отображения, связывающего с каждой правильной программой предложение в языке, смысл которого мы понимаем. Тогда можно определить смысл программы, записанной на любом языке программирования, в терминах эквивалентной «программы» в функциональном исчислении. (Под эквивалентной программой понимается программа, выполняющая те же самые функции).
Другой способ придать смысл программам заключается в определении идеализированной машины. Тогда смысл программы выражается в тех действиях, к которым она побуждает эту машину после того, как та начинает работу в некоторой предопределенной начальной конфигурации. В этой схеме интерпретатором данного языка становится абстрактная машина.
Третий подход – вообще игнорировать вопросы о«смысле», оставив его на совести разработчика программы. Этот подход и применяется при построении компиляторов.
Т.е. для нас «смысл» исходной программы состоит просто в выходе компилятора, когда он применяется к этой программе.

26
Мы будем исходить из предположения, что компилятор задан как множество пар (x, y),
где x – программа на походном языке,
y – программа в том языке, на который нужно перевести x. Предполагается, что мы заранее знаем это множество, и наша глав-
ная забота – построить эффективное устройство, которое по данному входу x выдает выход y. Мы будем называть это множество пар (x, y) переводом. Если x – цепочка в алфавите S, а y – цепочка в алфавите D, то перевод - это просто отображение множества S*®D*.
2.2. Синтаксис и семантика
Перевод обычно рассматривают как композицию двух более простых отображений. Первое из них, называемое синтаксическим отображением, связывает с каждым выходом(программа на исходном языке) некоторую структуру, которая служит аргументом второго отображения, называемого семантическим.
Почти всегда структурой любой программы является помеченное дерево. Поэтому сущность алгоритмов перевода обычно сводится к построению подходящих деревьев для входных программ
|
|
|
|
<предложение> |
|
|
|
|
|
|||
<именная группа> |
|
|
<группа сказуемых> |
|
|
|||||||
<определение> <определяемое> |
<глагол> |
<группа> |
|
|
||||||||
|
|
|
|
|
|
<предлог> |
<именная группа> |
|||||
the |
pig |
|
is |
|||||||||
|
|
|
|
|
|
|
|
<определение> |
<определяемое> |
|||
|
|
|
|
|
|
in |
||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
the |
pen |
Рис. 2.1. Древовидная структура английского предложения
В качестве примера, как для цепочек строятся эти деревья, рассмотрим разбиение английского предложения на синтаксические категории (рис. 2.1).
The pig is in the pen.

27
Неконцевые вершины этого дерева помечены синтаксическими категориями, а концевые (листья), помечены концевыми, или терминальными, символами, в данном случае – английскими словами.
Аналогично можно программу, написанную на языке программирования, расчленить на синтаксические компоненты в соответствии с синтаксическими правилами, управляющими этим языком (рис. 2.2).
Пример.
Цепочка a+b*c.
|
|
<выражение> |
|
|
|
|
|
|
||
<выражение> |
|
|
|
<терм> |
|
|
|
|||
+ |
|
|
|
|
||||||
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||
<терм> |
|
<терм> |
|
* |
<множитель> |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<множитель> |
|
<множитель> |
< |
идентификатор> |
||||||
|
|
|
|
|
|
|
|
|
|
|
<идентификатор> |
|
<идентификатор> |
|
с |
||||||
|
а |
|
|
b |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Рис. 2.2. Дерево арифметического выражения
Процесс нахождения синтаксической структуры данного предложения называется синтаксическим анализом, или синтаксическим разбором.
Синтаксический разбор позволяет понять взаимоотношения между различными частями предложения. Термином «синтаксис» языка будем называть отношения, связывающие с каждым предложением языка некоторую синтаксическую структуру, тогда правильное предложение языка можно определить как цепочку символов, синтаксическая структура которой соответствует категории «предложение».
Естественно, нам нужно более строгое определение синтаксиса. Что и будет сделано позднее.
Вторая часть перевода – семантическое отображение, оно отображает структурированный вход в выход, который обычно является программой на машинном языке.
Термином «семантика языка» будем называть отображение, связывающее с синтаксической структурой каждой входной цепочки цепоч-