Хабрахабр

История с продолжением: собственный компилятор Паскаля для Windows с чистого листа

Неожиданно тёплый приём, оказанный публикой Хабра моему посту о самодельном компиляторе XD Pascal для MS-DOS, заставил меня задуматься. Не досадно ли, что любительский проект, которому я отдал немало сил, лежит у меня мёртвым грузом с тех самых пор, как из Windows полностью исчезла виртуальная машина DOS? Итогом размышлений стал компилятор XD Pascal для Windows. Возможно, он лишился некоторой доли ностальгического шарма и утратил возможность наивной работы с графикой через прерывания BIOS. Однако переход на Windows вдохнул новую жизнь в проект и открыл дорогу к давней мечте — самокомпиляции.

Такое упрямство может выглядеть странным, однако проект имел единственную цель — моё собственное удовольствие, и дополнительные инструменты послужили бы здесь лишь помехой. Как и прежде, никакими вспомогательными инструментами для автоматической генерации компиляторов я не пользовался. В этом смысле компилятор разрабатывался с чистого листа.

Пять шагов к самокомпиляции в Windows

Стоит сказать несколько слов об основных задачах, которые пришлось решить на пути от DOS к Windows:

Поскольку в заголовках и секциях требуются точные адреса процедур и переменных, а их можно узнать лишь после вычисления размера кода и глобальных данных, то компиляцию пришлось сделать трёхпроходной. Формирование заголовков и секций исполняемого файла. Помимо официального описания формата Portable Executable, отличным подспорьем на этом этапе стала статья Creating the smallest possible PE executable. Такой кунштюк весьма неизящен, особенно с учётом того, что при каждом проходе заново повторяются все этапы компиляции, начиная с лексического разбора. При первом проходе строится граф вызовов процедур и отмечаются «мёртвые» процедуры; при втором — вычисляются адреса, размер кода и данных, заполняются заголовки; при третьем — генерируется код. Однако он приводит к очень лаконичному исходному коду компилятора и не требует никакого промежуточного представления программы.

Новый генератор кода. Компиляция для Windows потребовала заменить пары регистров «сегмент — смещение» на 32-битные регистры смещений, а также удалить (а местами добавить) префиксы изменения длины операнда (66h) и длины адреса (67h).

Поскольку эти функции требуют передачи аргументов справа налево, пришлось вручную инвертировать порядок аргументов в объявлении и вызовах всех таких функций. Директива для объявления внешних функций Windows API. Все имена функций, объявленных с директивой external, заносятся в таблицы секции импорта исполняемого файла. Ради простоты все аргументы процедур и функций в XD Pascal передаются в виде 32-битных величин; к счастью, это правило справедливо и для функций Windows API, так что взаимодействие с системными библиотеками не привело к усложнению механизма передачи аргументов. Тем самым отпала необходимость в инверсии средствами компилятора.

Вычисление любых выражений в XD Pascal строится так, что все промежуточные результаты имеют длину 32 бита и сохраняются в стеке. Удаление множеств и инфиксных строковых операций из исходного кода. Это требование связано с задачей самокомпиляции. Точнее, он позволил бы иметь множества размером до 32 элементов, но такие множества оказались бы практически бесполезны. Для строк и множеств Паскаля этот подход неприемлем.

Сигнатура обёртки едина для случаев компиляции внешним компилятором (Delphi/Free Pascal) и самокомпиляции; обёртываемые процедуры при этом различаются. Обёртки для некоторых процедур. Идея самокомпиляции привела к обёртыванию вызовов некоторых процедур стандартной библиотеки. Паскаль пестрит процедурами, которые при ближайшем рассмотрении оказывается невозможно реализовать по правилам самого Паскаля: Read, Write, Move и т.д. Таким образом, вся специфика способа компиляции локализуется внутри нескольких обёрток. Для большинства остальных нетипичных процедур понадобились обёртки. Для самых употребительных процедур, в том числе Read и Write, я сделал исключение и реализовал их нетипичными для грамматики языка, но привычными любому знатоку Паскаля. Таким образом, XD Pascal не во всём совместим с Delphi или Free Pascal, однако большой беды в этом нет, поскольку даже сам Free Pascal в режиме совместимости с Delphi фактически остаётся несовместимым.

Компиляция программ с GUI

Задача самокомпиляции, несмотря на своё символическое значение, остаётся ограниченной: компилятор является консольной программой и поэтому выглядит не совсем полноправным обитателем мира Windows. Понадобилось ещё несколько нововведений на пути к компиляции программ с оконным интерфейсом:

Как известно, в Delphi и Free Pascal для этого существует директива $APPTYPE. Директива компилятору для установки типа интерфейса. Тип интерфейса (консольный или графический) должен быть указан в отдельном поле заголовка исполняемого файла. Аналогичная директива $A появилась и в XD Pascal.

Этот тип в XD Pascal не реализован. Операция взятия адреса процедур и функций. В классическом Паскале нет полноценных указателей на процедуры и функции — их в некоторой мере заменяет процедурный тип. Однако обработка событий Windows API построена на обратных вызовах (callbacks), и здесь передача адреса вызываемой процедуры-обработчика вдруг стала насущной необходимостью. Как бы то ни было, до сих пор применение операции @ к процедурам в моём скромном проекте казалось мне бесполезным.

DLL. Явное указание имён подключаемых библиотек. Для консольных программ было достаточно импорта функций Windows API из библиотеки KERNEL32. DLL, GDI32. Программы с GUI потянули за собой USER32. Понадобилось расширить синтаксис директивы external, добавив туда имя библиотеки. DLL и т.д.


Демонстрационная программа с GUI

Что в итоге

В результате получился очень простой самокомпилируемый компилятор для Windows. Вряд ли корректно сравнивать его с могучими коллективными проектами типа Free Pascal. Скорее он попадает в весовую категорию известного любительского BeRo Tiny Pascal. По сравнению с ним XD Pascal имеет заметные преимущества: более строго соблюдается грамматика Паскаля и контролируются ошибки, есть полноценный файловый ввод/вывод, поддерживается арифметика чисел с плавающей точкой, нет зависимости от внешнего ассемблера, допускается компиляция программ с оконным интерфейсом.

Если повезёт, XD Pascal будет внедрён, наряду с BeRo Tiny Pascal, в лабораторный практикум по курсу конструирования компиляторов в МГТУ им. Далее мне предстоит разобраться с ложноположительным срабатыванием некоторых антивирусов — новой проблемой, о которой я и не задумывался в маленьком уютном мирке MS-DOS. Баумана. Н.Э.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть