- •Лекции по построению компилятора на Pascal Автор неизвестен Оглавление
- •1. Введение введение
- •2. Синтаксический анализ выражений начало
- •Одиночные цифры
- •Выражения с двумя цифрами
- •Общая форма выражения
- •Использование стека
- •Умножение и деление
- •Круглые скобки
- •Унарный минус
- •Слово об оптимизации
- •3. Снова выражения введение
- •Переменные
- •Функции
- •Подробнее об обработке ошибок
- •Присваивание
- •Многосимвольные токены.
- •Пробелы
- •4. Интерпретаторы введение
- •Интерпретатор
- •Немного философии
- •5. Управляющие конструкции введение
- •Немного основ
- •Оператор if
- •Оператор while
- •Оператор loop
- •Цикл for
- •Оператор do
- •Оператор break
- •Заключение
- •6. Булевы выражения введение
- •Грамматика
- •Операторы отношений
- •Исправление грамматики
- •Синтаксический анализатор
- •Объединение с управляющими конструкциями
- •Добавление присваиваний
- •7. Лексический анализ введение
- •Лексический анализ
- •Конечные автоматы и альтернативы
- •Эксперименты по сканированию
- •Конечные автоматы
- •Новые строки
- •Операторы
- •Списки, запятые и командные строки.
- •Становится интересней
- •Возвращение символа
- •Распределенные сканеры против централизованных
- •Объединение сканера и парсера
- •Пара комментариев:
- •Заключение
- •8. Немного философии введение
- •Дорога домой
- •Почему это так просто?
- •Здесь нет ничего сложного!
- •Заключение
- •9. Вид сверху введение
- •Верхний уровень
- •Структура паскаля
- •Расширение
- •Объявления
- •Структура си
- •10. Представление "tiny" введение
- •Подготовка
- •Объявления
- •Объявления и идентификаторы
- •Инициализаторы
- •Выполнимые утверждения
- •Булева логика
- •Управляющие структуры
- •Лексический анализ
- •Многосимвольные имена переменных
- •Снова операторы отношений
- •Ввод/вывод
- •Заключение
- •11. Пересмотр лексического анализа введение
- •Предпосылка
- •Проблема
- •Решение
- •Исправление компилятора
- •Заключение
- •12. Разное введение
- •Точки с запятой
- •Синтаксический сахар
- •Работа с точками с запятой
- •Компромисс
- •Комментарии
- •Односимвольные разделители
- •Многосимвольные разделители
- •Односторонние комментарии
- •Заключение
- •13. Процедуры введение
- •Последнее отклонение
- •Основа для экспериментов
- •Объявление процедуры
- •Вызов процедуры
- •Передача параметров
- •Семантика параметров
- •Передача по значению
- •Что неправильно?
- •Передача по ссылке
- •Локальные переменные
- •Заключение
- •14. Типы введение
- •Что будет дальше?
- •Добавление записей
- •Распределение памяти
- •Объявление типов
- •Присваивания
- •Трусливый выход
- •Более приемлемое решение
- •Литеральные аргументы
- •Аддитивные выражения
- •Почему так много процедур?
- •Мультипликативные выражения
- •Умножение
- •Деление
- •Завершение
- •Приводить или не приводить
- •Заключение
- •15. Назад в будущее введение
- •Новое начало, старое направление
- •Начинаем заново?
- •Модуль input
- •Модуль output
- •Модуль error
- •Лексический и синтаксический анализ
- •Модуль scanner
- •Решения, решения
- •Синтаксический анализ
- •16. Конструирование модулей введение
- •Совсем как классический?
- •Расширение синтаксического анализатора
- •Термы и выражения
- •Присваивания
- •Булева алгебра
16. Конструирование модулей введение
Эта обучающая серия обещает стать возможно одной из самых долгоиграющих мини-серий в истории, конкурирующей только с задержкой на Томе IV Кнута. Начатая в 1988, эта серия вошла в четырехлетнюю паузу в 1990, когда "заботы мира сего", изменения в приоритетах и интересах и необходимость зарабатывать на жизнь казалось забросили ее после Главы 14. Долго терпевшие из вас были наконец вознаграждены весной прошлого года долгожданной Главой 15. В ней я начал попытку поставить серию обратно на рельсы и по ходу дела сделать ее проще для достижения цели, которая состоит в том, чтобы обеспечить вас не только достаточным пониманием трудных тем теории компиляции, но также достаточными инструментами в виде фиксированных подпрограмм и концепций, так чтобы вы были способны продолжать самостоятельно и стали достаточно опытными для того, чтобы создавать свои собственные синтаксические анализаторы и трансляторы. Из-за этой длинной паузы я подумал что следует вернуться назад и повторно рассмотреть концепции, которые мы до этого охватили а также заново сделать некоторые части программы. В прошлом мы никогда сильно не касались разработки программных инструментов промышленного качества... в конце концов я пытался обучать (и обучаться) концепциям, а не промышленной практике. Чтобы сделать это я старался давать вам не законченные компиляторы и анализаторы, а только те отрывки кода, которые иллюстрировали частные случаи, которые мы рассматривали в текущий момент.
Я все еще верю, что это хороший способ изучения любого вопроса; никто не захочет вносить в изменения в программу в 100,000 строк только для того чтобы попробовать новую идею. Но идея работы с обрывками кода а не полными программами также имеет свои недостатки из-за которых мы писали те же самые фрагменты кода много раз. Хотя было полностью доказано, что повторение является хорошим способом обучения новым идеям, также правда и то, что оно может быть не слишком хорошей вещью. Ко времени, когда я завершил Главу 14, я казалось достиг пределов своих способностей манипулировать множеством файлов и множественными версиями тех же самых программ. Кто знает, может быть это одна из причин, по которым я кажется выдохся в то время.
К счастью, более поздние версии Borland Turbo Pascal позволяют нам получить и съесть свой кусок пирога. Используя их концепцию раздельно компилируемых модулей мы все еще можем писать маленькие подпрограммы и функции и сохранять наши основные и тестовые программы маленькими и простыми. Но, однажды написанный, код в модулях Паскаля будет всегда там для нашего использования и его связывание абсолютно безболезненно и прозрачно.
Так как к настоящему времени большинство из вас программируют на C или C++, я знаю, что вы подумаете: Borland с их Turbo Pascal конечно не изобретали понятие раздельно компилируемых модулей. И, конечно, вы правы. Но если вы не использовали TP в последнее время или когда либо, вы можете не понять насколько безболезненный весь этот процесс. Даже в C или C++ вы все еще должны формировать make файл, или вручную, или сообщая компилятору как это сделать. Вы должны также перечислить, используя утверждение "extern" или заголовочные файлы, функции, которые вы хотите импортировать. В TP вы не должны даже делать этого. Вам необходимы только имена модулей, которые вы желаете использовать, и все их процедуры автоматически становятся доступны.
У меня нет намерения заниматься здесь дебатами на тему войн языков, так что я не буду затрагивать эту тему в дальнейшем. Даже я больше не использую Pascal в своей работе... я использую C на работе и С++ для своих статей в Embedded Systems Programming и других журналах. Поверьте мне, когда я намеревался возродить эту серию, я думал долго и интенсивно о переключении и языка и целевой системы на те, которые мы все используем в эти дни, C/C++ и архитектуру PC и возможно также и объектно-ориентированные методы. В конце концов я понял, что это вызовет больше беспорядка, чем сам перерыв. И в конце концов, Pascal все еще остается одним из лучших возможных языков для обучения, не говоря о промышленном программировании. Наконец, TP все еще компилирует на скорости света, гораздо быстрее чем конкурирующие C/C++ компиляторы. А интеллектуальный компоновщик Borland, использованный в TP но не в их продуктах C++ не имеет аналогов. Кроме того, что он намного быстрее, чем Microsoft-совместимые компоновщики, Borland-овский интеллектуальный компоновщик отберет неиспользуемые процедуры и элементы данных даже вплоть до вырезания их из определенных объектов если они не нужны. Один из редких моментов нашей жизни, когда мы не должны идти на компромисс между полнотой и эффективностью. Когда мы пишем модуль TP мы можем сделать его настолько полным как нам нравится, включая любые функции и элементы данных которые, как мы думаем, могут нам когда-либо понадобиться, уверенные, что это не будет создавать ненужного раздутия кода в откомпилированной выполнимой программе.
Главное в действительности в следующем: используя механизм модулей TP мы можем иметь все преимущества и удобства написания маленьких, на вид автономных тестовых программ, без необходимости постоянно переписывать необходимые функции поддержки. Однажды написанные, модули TP сидят там, тихонько ожидая возможности выполнить свой долг и дать нам необходимую поддержку, когда будет необходимо.
Используя этот принцип, в Главе 15 я намеревался минимизировать нашу тенденцию заново изобретать колесо, организуя наш код в отдельные модули Turbo Pascal, каждый из которых содержит различные части компилятора. Мы завершили со следующими модулями:
-
Input
-
Output
-
Errors
-
Scanner
-
Parser
-
CodeGen
Каждый из этих модулей обслуживает разные функции и изолирует специфические области функциональных возможностей. Модули Input и Output, как подразумевают их имена, обеспечивают ввод/вывод символьного потока и важнейший предсказывающий символ, на котором основан наш предсказывающий синтаксический анализатор. Модуль Errors конечно обеспечивает стандартную обработку ошибок. Модуль Scanner содержит все наши булевы функции типа IsAlpha и подпрограммы GetName и GetNumber, которые обрабатывают много символьные токены.
Два модуля, с которыми мы будем в основном работать и те, которые больше всего представляют индивидуальность нашего компилятора - это Parser и CodeGen. Теоретически модуль Parser должен изолировать все аспекты компилятора, которые зависят от синтаксиса компилируемого языка (хотя, как мы видели последний раз, небольшое количество этого синтаксиса перетекает в Scanner). Аналогично, модуль генератора кода, CodeGen, содержит весь код, зависящий от целевой машины. В этой главе мы продолжим разработку функций в этих двух важнейших модулях.