Написание простого DSL компилятора на Delphi (6. Дамп AST)

Перевод поста Writing a Simple DSL Compiler with Delphi (6. AST Dumper).

Эта статья представляет описание инструмента для тестирования моего игрушечного языка программирования. Если вы только начинаете читать эту серию, то я бы рекомендовал вам начать с этого поста.

Пожалуйста, имейте в виду, что эта статья описывают начальную реализацию парсера. Если вы хотите просматривать код во время чтения статьи, убедитесь, что вы переключились на ветку dsl_v1.

Теперь, когда мы имеем работающий токинезатор и парсер генерирующие на выходе AST, мы можем начать работать над компилятором. Тем не менее, было бы отлично проверить корректность выходных данных парсера. Или по другому — нам нужны модульные тесты.

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

Чтобы сделать это, мы напишем генератор кода который вместо машинного кода генерирует исходную программу. Мы можем сделать просто поместив новую фабрику codegen в гибкий каркас компилятора.

codegen будет сохранять результирующую программу в список строк sl, который мы можем передать в анонимную процедуру (CodegenFactory) из-за полезного захвата переменной.

Наш генератор кода — TSimpleDSLCodegenDump должен реализовывать интерфейс ISimpleDSLCodegen содержащий одну функцию — Generate. Эта функция берет AST и возвращает что-то запускаемое — ISimpleDSLProgram. Поскольку наш код не знает как создавать запускаемую программу, он будет возвращать nil в этом параметре.

Полезные данные будут сохранены в список строк (TStringList) который передан в конструктор.

Основная функция генерации кода сохраняет ссылку на AST в поле так что мы имеем доступ к нему из всех методов и затем восстаёт исходный код функция за функцией вызовами DumpFunction.

Код программы собирается в поле FText: string. Два вспомогательных метода WritelnText используются для добавления данных в это поле.

Я покажу только две части этого "генератора кода" так как он довольно скучен. Как первый пример, эта функция записывает исходный код одной функции.

Ссылка на текущую функцию сохраняется снаружи так как она нужна нам в методе который записывает название переменной (не показан здесь). Затем мы записываем название функции, с последующим списком параметров. Хелпер Join для строк соединяет параметры в одну строку с разделителем в виде запятой. В конце мы вызываем DumpBlock для записи тела функции.

Во втором примере, метод DumpExpression, записывает выражение. Он вызывает DumpTerm для записи первого слагаемого, записывает оператор (если он есть) и записывает второе слагаемое.

Если мы запустим этот "генератор кода" на простой программе вычисляющей i-тое число Фибоначчи, то...

...мы увидим что результат довольно близок к оригиналу

Разделители немного отличаются, но это не имеет значения так как мой игрушечный язык игнорирует пробельные символы. Это даёт нам уверенность что AST действительно правильно сгенерированно (как минимум в некоторых случаях). Чтобы быть более уверенными в правильной работе парсера мы могли бы написать больше тестов... или мы можем просто поиграть с компилятором, написать больше программ и проверить что они работают корректно. Однако, чтобы это сделать нам нужен сам компилятор.

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

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

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