В 2019 году с февраля по март у меня был проект — **Агидель**. Обратил внимание на это, когда изучал свои старые репозитории на гитхабе, пока передвигал их на сурсхат в июле 2023. В старом блоге нашлась интересная статья про это. Переопубликовываю! С комментариями и дополнениями! => https://t.me/bpblog/1301 | Обсудить в телеграме Я также переопубликовал исходный код под лицензией CC0. Ссылки в конце статьи. = Про Агидель Языков программирования/разметки очень много, даже больше, чем сто. Разных синтаксисов чуть меньше. Так какой же синтаксис лучший? Я знаю какой. Этот синтаксис называется //лиспоподобным синтаксисом//. Его отличительная черта — много скобок. Вот примеры вычисления чисел фибоначчи на разных лиспах: ```clojure ;; Clojure — клоюра (defn fib [n] (case n 0 0 1 1 (+ (fib (- n 1)) (fib (- n 2))))) ;; Common Lisp — общелисп (defun fibonacci-iterative (n &aux (f0 0) (f1 1)) (case n (0 f0) (1 f1) (t (loop for n from 2 to n for a = f0 then b and b = f1 then result for result = (+ a b) finally (return result))))) ;; Scheme — схема (define (fib-rec n) (if (< n 2) n (+ (fib-rec (- n 1)) (fib-rec (- n 2))))) ``` Ну, в общем, понятно. Скобки. Я люблю скобки. Я хотел иметь возможность писать на си, используя такие скобки. Что же делать? Сделать такую программу, которая на вход получает программу в лисповом синтаксисе: ```clojure (import stdio.h) (defun (main int) () [printf "hello world!\n"] ; квадратные скобки для вызова сишных функций (return 0)) ``` И выводит ту же программу, но уже в сишном синтаксисе: ```c #include int main () { printf("hello world!\n"); return 0; } ``` Название такой программы пришло в голову быстро — [[https://ru.wikipedia.org/wiki/Белая_(приток_Камы) | Агидель]], в честь реки. Со временем пришло осознание, что можно предоставить возможность пользователю добавлять поддержку других языков. То есть, я планировал сделать [[https://en.wikipedia.org/wiki/General-purpose_macro_processor | макро-процессор]] с синтаксисом как в лиспе. = Первая итерация Сразу начал делать. Придумал архитектуру, которая оставалась в остальных итерациях: синтрансы и плагины. **Синтрансы** (syntrans = syntax transformer) — штуки, которые получают на вход исходный код в одной форме и выводят его же в другой форме, что-нибудь изменив. Например, один синтранс вырезает комментарии из кода, другой преобразует мои расширения лиспового синтаксиса в обычные s-выражения, другой всё в итоге превращает во что-то исполняемое. **Плагины** — просто наборы макросов. Пользователь сам подключает те, которые ему нужны. В первой итерации я имплементировал синтрансы как отдельные программы. Вот как-то так выполнялась транспиляция программ: ```bash $ cat hello-world.lisp | agidel-discomment | agidel-disbracket\ | agidel-quotify | agidel-prepare | bash - > hello-world.c ``` То есть, в итоге текст на Агидели превращался в скрипт на баше. Этот скрипт вызывал макросы, которые тоже были реализованы как отдельные мини-программы. Довольно элегантно. Такая архитектура позволяла писать отдельные макросы на любом языке. Вот один из них: ```c #include #include #include "libagidel.h" int main(int argc, char** argv) { for (int i = 1; i < argc; i++) { if (is_string(argv[i]) || is_angled(argv[i])) printf("#include %s\n", argv[i]); else printf("#include <%s>\n", argv[i]); } return 0; } ``` Я сидел-реализовывал поддержку си, но потом я решил, что как-то мерзенько вышло. Поменял архитектуру и переделал с нуля. = Вторая итерация Решил и синтрансы, и плагины реализовать как модули схемы. Схема — диалект лиспа, самый классный из них. У схемы очень много реализаций, я выбрал ту, которая называется [[http://call-cc.org | Chicken Scheme]]. С этого момента стал коммитить всё на гитхаб. Я старался делать хотя бы один коммит в день (и у меня получалось). Теперь у меня в профиле красивая зелёная полоска. //Забавно это читать в 2023, когда большая часть моих публичных коммитов на гитхаб — зеркала с сурсхата, репозитории Агидели с гитхаба удаляются, а эта статья перевыпускается с гитхаб-пейчжс на микоризу.// Вот тот же макрос, который я привёл в качестве примера прошлой итерации, только новой версии: ```scheme (-define-syntax import (syntax-rules () ((_ o ...) (-string-append (-map (-lambda (f) (-if (-string? f) (format "#include ~A\n" f) (format "#include <~A>\n" f)))))))) ``` Как видно, почти все схемовские функции начинаются с дефиса. Я сделал это, чтобы избежать коллизии имён с агидельными макросами. Как и в прошлый раз, ядро сделал, начал реализовывать поддержку си, но потом решил, что опять вышло как-то по-дурацки. Поменял архитектуру. = Третья итерация Это был микс первых двух итераций. Синтрансы реализованы как отдельные мини-программы, а плагины как модули схемы, которые подрубались когда надо. Главной программой являлся простой скрипт на баше, который генерировал код на схеме, который скармливал интерпретатору прямо там. Потом я переписал эту часть на Агидель/sh. Вот так выглядит исходный код главной программы: ```clojure (shebang!) (set import_statement "(import (prefix (only scheme define string-append display) AGIDEL/) (only scheme quote)") (for-each-cli-arg plugin (set import_statement + "(agidel-plugin $plugin)")) (set import_statement + ")") [csi -batch -quiet -eval "(begin (module agidel_temp (main) $import_statement (AGIDEL/define (main) (AGIDEL/display (AGIDEL/string-append $(cat /dev/stdin))))) (import agidel_temp) (main))"] [echo] ``` Верно, я так далеко дошёл в этой итерации, что написал Агидель на Агидели. Тот же макрос, что и в прошлых двух итерациях, но ещё раз: ```scheme (define (import . fs) (-apply -string-append (-map (lambda (f) (-if (-string? f) (format "#include \"~A\"\n" f) (format "#include <~A>\n" f))) fs))) ``` В реализации поддержки си в этой итерации я преуспел больше всего, но потом я разочаровался в Агидели вообще. = Почему я разочаровался? Я уже реализовал почти весь си. Я даже написал пару программ на Агидель/си, которые прекрасно транспилировались в валидный си. Так что не так? Я сравнил кусок кода на си, который я перевёл, и кусок кода на Агидель/си. Внимательно посмотрел. Смотрел. Смотрел. Понял следующее: *. Код на голом си читабельнее и понятнее. *. Когда я пишу на Агидель/си, я на самом деле пишу на голом си в голове и потом выражаю это на Агидель/си. То есть, выполняю двойную работу. К тому же, *. Мелкие косяки мешали брать и использовать Агидель/си везде и всюду. Что-то не реализовано, тут всё вылетает, потому что макросы в схеме дурацкие, а тут вообще семиколоны лишние получаются. Взвесил всё на весах. Решил, что можно разочароваться. = Потери Начались моральные страдания от того, что я несколько месяцев делал проект, в котором в итоге разочаровался. Ребята из чата «Клавиатуры и микроконтроллеры» не бросили меня, поддержали. Рассказали про важность полученного опыта. Оказалось, что даже кто-то ждал релиза первой версии, чтобы пользоваться Агиделью. Получил немного вдохновения для четвёртой итерации, но быстро его потерял. //Абзац выше был написан до того, как я сделал Вакидзаси, начав мою прекрасную славу клавиатурщика. А из чата я уже года два как вышел, а ещё года четыре как не называется так, как его назвал я в тексте.// Но пока я делал Агидель, я безнадёжно влюбился в макро-процессоры. В мои планы входит изучить что там на рынке есть. Я неплохо знаком с //C Preprocessor//, с ним, наверное, неплохо знакомы все. В следующей статье я расскажу, как использовать его не по назначению. Про это уже написали другие люди, кстати: => https://stackoverflow.com/questions/652788/what-is-the-worst-real-world-macros-pre-processor-abuse-youve-ever-come-across/652802 | Тред на стак-оверфло => http://jkorpela.fi/html/cpre.html | Как использовать си-препроцессор для HTML (2000) = Заключение В общем-то, Агидель можно использовать. Вот два репозитория: => https://git.sr.ht/~bouncepaw/agidel | Главный репозиторий с синтрансами и всем таким => https://git.sr.ht/~bouncepaw/agidel-stdlib | Стандартные плагины для шелла, си и ардуино //Ещё нашёлся какой-то [[https://git.sr.ht/~bouncepaw/agidel-syntrans | отдельный репозиторий с синтрансами]]. Это либо огрызок второй итерации, либо пригрызок недоделанной четвёртой итерации. Не стал разбираться.// Документации нет. Может быть, я её даже напишу. Может быть, я найду вдохновение и буду использовать Агидель не для генерации си, а для чего-нибудь ещё, и продолжку разработку. Но может и нет. //Документацию я не написал, вдохновение я так и не нашёл.//