С июня 2009 года я занимаюсь разработкой интерпретатора Си. (я уже упоминал об этом в статье о вызовах функций).
Сейчас уже реализовано достаточно много конструкций: циклы, выбор, вычисление выражений, вызовы функций (как объявленных пользователем, так и стандартных), инклуды и другое.
Название CPrompt программа получила по аналогии с «prompt» — приглашением командной строки (D:>, root@comp#, ...).
Запуск на интерпретацию файла с исходным кодом делается просто:
$cprompt /path/to/file.c
Интерпретация разделена на 3 этапа:
1) Препроцессинг
2) Построение дерева выполнения
3) Вызов main()
Первый этап, препроцессинг, важный и скучный: обрабатывает инклуды, дефайны, комментарии и другое.
На выходе — чистый код, без директив и лишнего текста.
Второй этап — построение дерева выполнения. Тут берется код и строится граф (дерево), в корне которого лежит некий объект APPLICATION, который содержит информацию о запускаемом файле. В поддеревьях — функции, каждая лексема переходит в элемент дерева. Каждый элемент графа имеет свой «тип» — число, которое показывает его предназначение.
Функции делятся на 2 типа — объявленные пользователем и «внешние». Последние — например, стандартные из libc, из других разделяемых библиотек.
На выходе получается дерево, подобное этому:
…
Tree was built:
t0;APPLICATION;;;
| t0;FUNCTIONS;;;
| | t14;double;cos;;
| | t14;double;floor;;
| | t14;int;isdigit;;
…
| | t2;double;round;;
| | | t5;floor(value+0.5);;;
| | t14;int;printf;;
| | t2;int;main;;
| | | t1;int;l;round(7.2);
…
(это часть лога, который выводит интерпретатор).
t0, t2, t14 — тип элементов.
0 — без типа
1 — выражение (присвоение считается одной из операций, наравне с +,-,*… но в другом приоритете).
2 — функция
5 — «return»
14 — «внешняя функция»,
И другие, для разных действий.
Аргументы к функциям хранятся в структурах, поэтому их нет в видимой части дерева. На самом деле указатели на них прописаны в в каждой ветке функции.
Все выполнение программы логируется очень подробно — при указании параметра --dbg выводит в стандартный вывод много информации.
Если посмотреть лог, то вы заметите что выполнение разделено на 5 пунктов, а не на 3 как я говорил в начале. Два пункта — перед препроцессингом и после — это парсинг исходного текста. Парсинг перед обработкой — и разбор обработанного текста.
Единственный выход за рамки стандарта, который я себе позволил — добавил конструкцию «outside», которая ввиду невозможности статической линковки позволяет объявлять функции из внешних библиотек. Сейчас возможно объявить только те функции, с библиотеками которых был собран интерпретатор, но в будущем будет возможность импорта из разделяемых библиотек.
Например,
outside cdecl: double cos(double x);
outside cdecl: double floor(double x);
Где cdecl — соглашение вызова функции. Подробнее о таком вызове функций.
Пишется продукт на С++.
Посмотреть или скачать исходный код (Google Code).
Надеюсь, скоро появится рабочий релиз продукта, который можно будет использовать, так как с каждым днем я всё ближе приближаюсь к стандарту. Конечная цель — полная поддержка C99.
На такой код лог будет таким.
Много лишнего, но при желании разобрать можно.
А какие ваши варианты использования интерпретатора си? Где бы можно было применить этот продукт?
UPD перенес в Языки программирования
UPD2 Добавил в репозиторий make-файл.
Для компиляции:
и запускаем ./cprompt /path/to/file
Сейчас уже реализовано достаточно много конструкций: циклы, выбор, вычисление выражений, вызовы функций (как объявленных пользователем, так и стандартных), инклуды и другое.
Название CPrompt программа получила по аналогии с «prompt» — приглашением командной строки (D:>, root@comp#, ...).
Немного о работе самого интерпретатора
Запуск на интерпретацию файла с исходным кодом делается просто:
$cprompt /path/to/file.c
Интерпретация разделена на 3 этапа:
1) Препроцессинг
2) Построение дерева выполнения
3) Вызов main()
Первый этап, препроцессинг, важный и скучный: обрабатывает инклуды, дефайны, комментарии и другое.
На выходе — чистый код, без директив и лишнего текста.
Второй этап — построение дерева выполнения. Тут берется код и строится граф (дерево), в корне которого лежит некий объект APPLICATION, который содержит информацию о запускаемом файле. В поддеревьях — функции, каждая лексема переходит в элемент дерева. Каждый элемент графа имеет свой «тип» — число, которое показывает его предназначение.
Функции делятся на 2 типа — объявленные пользователем и «внешние». Последние — например, стандартные из libc, из других разделяемых библиотек.
На выходе получается дерево, подобное этому:
…
Tree was built:
t0;APPLICATION;;;
| t0;FUNCTIONS;;;
| | t14;double;cos;;
| | t14;double;floor;;
| | t14;int;isdigit;;
…
| | t2;double;round;;
| | | t5;floor(value+0.5);;;
| | t14;int;printf;;
| | t2;int;main;;
| | | t1;int;l;round(7.2);
…
(это часть лога, который выводит интерпретатор).
t0, t2, t14 — тип элементов.
0 — без типа
1 — выражение (присвоение считается одной из операций, наравне с +,-,*… но в другом приоритете).
2 — функция
5 — «return»
14 — «внешняя функция»,
И другие, для разных действий.
Аргументы к функциям хранятся в структурах, поэтому их нет в видимой части дерева. На самом деле указатели на них прописаны в в каждой ветке функции.
Все выполнение программы логируется очень подробно — при указании параметра --dbg выводит в стандартный вывод много информации.
Если посмотреть лог, то вы заметите что выполнение разделено на 5 пунктов, а не на 3 как я говорил в начале. Два пункта — перед препроцессингом и после — это парсинг исходного текста. Парсинг перед обработкой — и разбор обработанного текста.
Единственный выход за рамки стандарта, который я себе позволил — добавил конструкцию «outside», которая ввиду невозможности статической линковки позволяет объявлять функции из внешних библиотек. Сейчас возможно объявить только те функции, с библиотеками которых был собран интерпретатор, но в будущем будет возможность импорта из разделяемых библиотек.
Например,
outside cdecl: double cos(double x);
outside cdecl: double floor(double x);
Где cdecl — соглашение вызова функции. Подробнее о таком вызове функций.
Пишется продукт на С++.
Посмотреть или скачать исходный код (Google Code).
Надеюсь, скоро появится рабочий релиз продукта, который можно будет использовать, так как с каждым днем я всё ближе приближаюсь к стандарту. Конечная цель — полная поддержка C99.
Пример
#include <cmath>
int main(int argv,char* argc[])
{
int l=round(7.2);
}
На такой код лог будет таким.
Много лишнего, но при желании разобрать можно.
А какие ваши варианты использования интерпретатора си? Где бы можно было применить этот продукт?
UPD перенес в Языки программирования
UPD2 Добавил в репозиторий make-файл.
Для компиляции:
svn checkout cprompt.googlecode.com/svn/trunk/ cprompt
cd cprompt
make
и запускаем ./cprompt /path/to/file