Практика мультипарадигмального программирования, 05 лекция (от 26 марта)
Материал из eSyr's wiki.
Предыдущая лекция | Следующая лекция
Содержание |
Компиляция из одного языка в другой
Обычно в качестве другого языка выступает язык Си.
Некоторое время назад до людей дошло, что компилировать в машинный код или ассемблер совершенно необязательно, ибо отслеживать машинозависимые вещи при написании каждого компилятора накладно. Если человек разрабатывает какой-нибудь ЛИСП или какую-нибудь Схему, то ему совершенно не нужно разрабатывать код под интел и под сан.
Почему бы не научиться интегрировать модули на Лиспе и на Си? Это было бы интересно и это было сделано.
Существует несколько трансляторов из Схемы в Си, например stalin, но вот этот stalin-scheme преследует несколько иную цель — он оптимизирует. Но получается файл на Си, но там такая каша, что с чем-то там интегрировать можно забыть просто. Это значит, что не все трансляторы одинаково полезны.
Далее, есть транслятор Scheme→C. Транслятор известный, stalin bootstrapился с него. Но есть один недостаток — последний релиз датируется 1994 годом.
Далее, Chicken Scheme, там жирный бройлер с лямбдой на крылышке. Это уже близко к тому, что надо, по крайней мере достаточно быстро удаётся произвести интеграцию во все стороны, но генерирует код на Си такой, что без поллитры не разберёшь, но он не такой запутанный, как у stalin. Код почти идеальный, но генерируются функции вида f_217. Но если создатели не потрудились добавить структурные отступы, то предполагали, что исходник на C будет human-maintainable, ибо изменять генерируемые файлы — последнее дело.
Вместе с Chicken Scheme поставляется csz. Он, как и многие другие интерпретаторы Схемы, не использует библиотеку readline. Кроме того, есть компилятор chicken. пример использования:
chicken prog.scm gcc -Wall -g prog.c `chicken-conf -libs`
chicken-conf в зависимости от флажков генерирует флаги компиляторов, но можно и просто
gcc -Wall -g prog.c -lchicken
если планируется использовать данный файл в составе проекта, то надо ещё добавить
`chicken-conf -embedded`
Что нового по сравнению с обычной Схемой:
#> //кусок кода на Си <#
Chicken тупо, механически, не анализируя, вставляет этот кусок кода в начало файла. Потом нам позволяют сделать где угодно
(foreign-code "do_something();")
Причём это должен быть один оператор. Но где бы угодно мы её не вставили, мы можем вставить её сколь угодно глубоко. И тогда Chicken добавит ещё и её. Форма эта, если попытаться использовать её значение, ничего не возвращает, точнее #<unspecified>. Для вычисления выражения надо воспользоваться другой вещью:
(foreign-value "fgets(...)" c-string)
И в кавычках должно быть выражение. Но этого мало, так как Схема ещё должна знать, какой тип значения этого выражения (c-string в данном случае). Ещё из этой серии:
(define-foreign-variable myvar <type> ["C expression"])
Если выражение не задать, то будет использовано называние переменной. Если C-expression — lvalue, то можно будет делать
(set! myvar ...)
Есть также ещё пачка выражений для встраивания кусочков сишного кода
Типы и как с ними бороться
- bool — преобразуется в #F и #T
- char
- unsigned-char
- short
- int
- integer
- float
- double
- pointer — предполагается указатель на схемовский объект, если 0, то #F
- nonnull-pointer
- c-pointer — указатель на сишный объект
- nonnull-c-pointer
- u8vector — unsigned char *
- u16vector
- u32vector
- s8vector
- s16vector
- s32vector
- f32vector
- f64vector
- nonnull-u8vector
- nonnull-u16vector
- nonnull-u32vector
- nonnull-s8vector
- nonnull-s16vector
- nonnull-s32vector
- nonnull-f32vector
- nonnull-f64vector
В Лиспе и Схеме есть список — #( ... ), в нём можно нумеровать элементы. Что характерно, размер не передаётся.
Ссылка — (ref ....)
(template T1 T2 ...) — T1<T2 ...>
- scheme-object — со стороны Си это C-word
Разное
- как обозначать пустой список — C_SCHEME_END_OF_LIST, имеет тип C_word
C_word c = C_SCHEME_END_OF_LIST
- car и cdr
- В одной версии C_u_i_car(C_word, C_word) и C_u_i_cdr(C_word, C_word)
- В другой — C_i_car(C_word, C_word) и C_i_cdr(C_word, C_word)
- int C_num_to_int(C_word)
Пример: решето Эратосфена
Весь пример лектор писать не будет, ибо он большой
Была такая функция:
(define (eratosfen max) ...)
Хочется, чтобы main был в Си, а не в Схеме. Надо написать такую вещь:
- В одной версии
(include "chicken-entry-points")
- В другой версии
(include "default-entry-points")
Это файл, со Схемовским синтаксисом, который определяет множество ниточек, за которые можно дёргать. Далее:
- В одной версии
(define-embedded (eratosfen_call (int x)) scheme-object (eratosfen x))
- В другой версии
(define-external (eratosfen_call (int x)) scheme-object (eratosfen x))
когда эта штука отрабатывается, то в сишный код вставляется функция
eratosfen_call(int x);
которую можно вызывать.
Далее основная программа:
#include <stdio.h> #include <chicken.h> /* В исходниках примеров этот инклюд в кавычках. * Вообще, не понятно, почему так. Возможно, один * из разработчиков не преодолел барьер замены * кавычек на угловые скобки */
extern C_word eratosfen_call(int x);
int main() { /* SCHEME_init(0, 0, 0, 0) */ C_word rec = eratosfen_call(2000); C_word t = res; while (t != C_SCHEME_END_OF_LIST) { C_word cur = C_u_i_car(t); int x = C_num_to_int(cur); printf("%d ", x); t = C_u_i_cdr(t); } return 0; }
Как собирать
chicken eratosfen.scm [-include-dir /usr/share/chicken] gcc main.c erstosfen.c `chicken-conf -libs -embedded`
Мораль: следует хватать этот chicken и что-то делать? Можно. Но не обязательно. Факт в том, что если транслируется во что-то другое, то в исходном языке появляются возможности по использованию кусков целевого языка. И тут это не скрывается. В stalin'е же это скрыватеся.
Тема следующей лекции: моделирование отдельных особенностей, через две недели будет изучение InteLib.
Отличные оценки тем, что будет продемонстрировано что-то своё мультипарадигмальное.
Практика мультипарадигмального программирования
Календарь
пн | пн | пн | пн | пн | |
Февраль
| 12 | 19 | |||
Март
| 12 | 19 | 26 | ||
Апрель
| 02 | 09 | 16 |