Лекции по построению компилятора на Pascal
завершающая глава по генерации кода, обычно для какого-нибудь воображаемого
процессора с простым набором команд.
финальная глава или две, посвященные оптимизации. Эта глава часто остается
непрочитанной, очень часто.
В этой серии я буду использовать совсем другой подход. Для начала, я не остановлюсь долго
на выборе. Я покажу вам путь, который работает. Если же вы хотите изучить возможности,
хорошо… я поддержу вас... но я буду держаться того, что я знаю. Я также пропущу
большинство тех теорий, которые заставляют людей засыпать. Не поймите меня
неправильно: я не преуменьшаю важность теоретических знаний, они жизненно
необходимы, когда приходится иметь дело с более сложными элементами какого либо языка.
Но необходимо более важные вещи ставить на первое место. Мы же будем иметь дело с
методами, 95% которых не требуют много теории для работы.
Я также буду рассматривать только один метод синтаксического анализа: рекурсивный
спуск, который является единственным полностью пригодным методом при ручном
написании компилятора. Другие методы полезны только в том случае, если у вас есть
инструменты типа Yacc, и вам совсем неважно, сколько памяти будет использовать готовый
продукт.
Я также возьму страницу из работы Рона Кейна, автора Small C. Поскольку почти все другие
авторы компиляторов исторически использовали промежуточный язык подобно P-коду и
разделяли компилятор на две части («front end», который производит P-код, и «back end»,
который обрабатывает P-код, для получения выполняемого объектного кода), Рон показал
нам, что очень просто заставить компилятор непосредственно производить выполняемый
объектный код в форме языковых утверждений ассемблера. Такой код не самый компактный
в мире код... генерация оптимизированного кода - гораздо более трудная работа. Но этот
метод работает и работает достаточно хорошо. И чтобы не оставить вас с мыслью, что наш
конечный продукт не будет представлять никакой ценности, я собираюсь показать вам как
создать компилятор с небольшой оптимизацией.
Наконец, я собираюсь использовать некоторые приемы, которые мне показались наиболее
полезными для того, чтобы понимать, что происходит, не продираясь сквозь дремучий лес.
Основным из них является использование одно-символьных токенов, не содержащих
пробелов, на ранней стадии разработки. Я считаю, что если я могу создать синтаксический
анализатор для распознавания и обработки I-T-L, то я смогу сделать тоже и с IF-THEN-ELSE.
На втором уроке я покажу вам, как легко расширить простой синтаксический анализатор для
поддержки токенов произвольной длины. Следующий прием состоит в том, что я полностью
игнорирую файловый ввод/вывод, показывая этим, что если я могу считывать данные с
клавиатуры и выводить результат на экран я могу также делать это и с файлами на диске.
Опыт показывает, что как только транслятор заработает правильно очень просто
перенаправить ввод/вывод на файлы. Последний прием заключается в том, что я не пытаюсь
выполнять коррекцию/восстановление после ошибок. Программа, которую мы будем
создавать, будет распознавать ошибки и просто остановится на первой из них, точно также
как это происходит в Turbo Pascal. Будут и некоторые другие приемы, которые вы увидите
по ходу дела. Большинство из них вы не найдете в каком либо учебнике по компиляторам, но
они работают.
Несколько слов о стиле программирования и эффективности. Как вы увидите, я стараюсь
писать программы в виде маленьких, легко понятных фрагментов. Ни одна из процедур, с
которыми мы будем работать, не будет состоять более чем из 15-20 строк. Я горячий
приверженец принципа KISS (Keep It Simple, Sidney – Делай это проще, Сидней) в
программировании. Я никогда не пытаюсь сделать что-либо сложное, когда можно сделать
просто. Неэффективно? Возможно, но вам понравится результат. Как сказал Брайан
Керниган, сначала заставьте программу работать, затем заставьте программу работать
быстро. Если позднее вы захотите вернуться и подправить что-либо в вашем продукте, вы
сможете сделать это т.к. код будет совершенно понятным. Если вы поступаете так, я, тем не