
- •Содержание
- •Введение
- •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. Ошибки, допускаемые во время прогона
- •Контрольные вопросы
- •Список литературы
232
Не исключена и другая ошибка: в одном и том же блоке идентификатор описан дважды. Если возникает такая ситуация, то анализатор обычно генерирует сообщение
IDENTIFIER blank ALREDY DECLARED IN BLOCK
Чтобы избежать неоднозначности, в таблице символов на каждом уровне блоков для каждого идентификатора должен появиться один элемент. Что предпримет компилятор, когда он встречает повторное описание идентификатора в блоке? Оптимальным вариантом было бы проведение во время компиляции подробного анализа части программы, что позволило бы просмотреть, как этот идентификатор использу-
ется в блоке, и решить, какое из описаний ему более всего соответствует.
11.9. Ошибки, связанные с употреблением типов
Правила, определяющие, где в программе возможно появление различных значений, не являются контекстно-независимыми. Если идентификатор описан таким образом, что ему могут присваиваться значения в виде целых чисел, то во многих языках попытка присвоить ему литерное значение будет считаться недопустимой.
int i; char c; i=c;
Такая ошибка обнаружится во время компиляции с помощью таблицы символов и выдается сообщение вида
MODE char CANNOT BE COERCED TO int.
Это способствует идентификации ошибки, но вряд ли поможет начинающему программисту.
Особый случай составляют типы, создаваемые программистом, особенно с использованием указателей, поскольку в этом случае в описание одного типа может быть включен другой тип, определяемый пользователем (взаимно рекурсивный тип). Такие ошибки вообще нельзя обнаружить, пока все виды не будут полностью описаны. В этом случае сообщения об ошибках будут выдаваться между прохода-
ми компилятора. |
|
|
|
|
|
Существуют |
и |
другие |
ошибки, связанные |
с |
контекстно- |
зависимыми аспектами типичных языков: |
|
|
1)неправильное число индексов массива;
2)неправильное число параметров для вызова процедуры или функции;
3)несовместимость типа (или вида) фактического параметра в вызове с типом формального параметра;
233
4) невозможность определения знака операции по его операндам. Обычно на такие ошибки компилятор может выдавать четкие - со
общения.
11.10.Ошибки, допускаемые во время прогона
Во время прогона в программах могут возникать ошибки, которые нельзя предусмотреть в процессе компиляции. При прогоне могут возникать следующие типы ошибок:
1)нахождение индекса массива вне области действия;
2)целочисленное переполнение (вызванное, например, попыткой сложить два наибольших целых числа, допускаемых реализацией);
3)попытка чтения за пределами файла.
В языках с динамическими типами до времени прогона нельзя обнаружить более широкий класс ошибок(ошибки употребления типов, ошибки, связанные с присвоением и др.)
Обычно компиляторы стараются предотвратить возможность возникновения таких ошибок до прогона. Одно из решений – дать исчерпывающую формулировку задачи, например результат деления на ноль определить как ноль, выходящий за пределы области действия, индекс считать эквивалентным какому-нибудь значению в пределах области действия, при попытке чтения за пределами файла выполнять некоторое стандартное действие и т.д.
Однако такая исчерпывающая формулировка задачи имеет свои «подводные камни»: могут остаться незамеченными ошибки программирования или ошибки данных. Программисты обычно не ожидают, что во время прогона их программ произойдет деление на ноль, - им об этом нужно сообщить. Тем не менее, нежелательно, чтобы из-за этого прерывалось выполнение программы. Компромиссное решение – напечатать сообщение об ошибке времени прогона, когда она возникает, но позволить программе выполнить какие-либо стандартные действия, чтобы она могла продолжать работу и находить дальнейшие ошибки.
В случае ошибки, возникающей в процессе прогона, не всегда можно четко объяснить программисту, что именно неправильно. К этому моменту программа уже транслирована в машинный код, а программисту понятны только ссылки на исходный текст. Поэтому система, работающая при прогоне, должна иметь доступ к таблице идентификаторов и другим таблицам и следить за номерами строк в исходной программе. Таблицы, требуемые для диагностики, к началу прогона могут уже не находиться в основной памяти, но в случае