Примерно год назад я писал язык программирования под названием ALLang. Расшифровка его звучит так: Another LISP Language, что незамысловато даёт понимание его второсортности. Тем не менее, таковой язык всё же предлагает интересные особенности в своей реализации со стороны эзотерических языков. Писал я ALLang исключительно по фану. Но начнём мы пожалуй по порядку с его высокоуровневых особенностей и будем постепенно углубляться вниз, в самую его крамешную бездну эзотерического внутреннего выполнения. Язык программирования ALLang выглядит как обычный язык LISP диалекта Scheme. При этом язык ALLang является чисто функциональным языком программирования. У него не существует операций ввода/вывода, переменных, циклов и т.п. вещей связанных как-либо с императивными языками. В языке ALLang существует три основных ключевых слова, описывающих инструкции: include, define, if. Инструкция include содержит ещё два ключевых слова assembly и source. В сумме ALLang содержит пять ключевых слов. Результат исполнения при x = 5: В сравнении с языками LISP диалекта Scheme здесь есть некоторые "тонкости". Во-первых, существует два типа библиотек - assembly и source. Первый тип библиотеки подгружает низкоуровневые детали исполнения языка, которые могут модифицировать язык как угодно, добавляя всеразличные операции. Второй тип подгружает код написанный уже на языке ALLang, собственно является типичным способом импортирования кода.  Если мы посмотрим в код lib/vms/init.vms, то увидим просто точку начала исполнения в виде функции main, хоть и в виде ассемблерного кода (о котором мы поговорим позднее). Библиотеки типа source мы оставим напоследок. Сейчас же давайте посмотрим "узкие" места языка программирования. Во-первых, язык действительно не обладает никакими операциями ввода и вывода, сам язык - это чистый алгоритм и не более. Алгоритму на вход поступают аргументы (в функцию main), а на выходе код возвращает результат выполнения. Во-вторых, язык работает исключительно с числами типа int32, он не знает что такое int8, string, struct, list и т.п. В общем он крайне примитивен. В-третьих, у него нет ни переменных, ни циклов. Всю цикличность он производит через рекурсию, а таковая рекурсия неоптимизирована! Это было сделано исключительно для того, чтобы сохранить уже приобретённую примитивность языка, но о таковой ещё будет разговор на низком уровне разбора языка. Таким образом, язык крайне плох в практическом своём применении, но с другой стороны его особенность подгрузки ассемблерного кода может кардинально его изменить, в том числе добавив некоторые типы, циклы и т.п. Иными словами, сам язык можно рассматривать как конструктор для развлечения программиста. Язык программирования ALLang базируется на самописной стековой виртуальной машине CVM. Таковая виртуальная машина достаточно гибка и не менее примитивна, в сравнении с языком ALLang. Она принимает на вход свой ассемблерный код и преобразует его в машинный код (байткод) для своего исполнения.  Основные инструкции CVM изображены в следующей таблице. Bytecode Stack Args Instruction 0x0A 0 1 push 0x0B 1 0 pop 0x0C 1 0 inc 0x0D 1 0 dec 0x0E 3 0 jg 0x0F 3 0 je 0x1A 1 0 jmp 0x2A 2 0 stor 0x3A 1 0 load 0x4A 1 0 call 0x5A 0 0 hlt Компиляция языка ALLang выполняется в несколько этапов: Компиляция. Происходит преобразование высокоуровневого кода в код языка ассемблера для виртуальной машины CVM. Трансляция. Происходит преобразование ассемблерного кода в машинный код (байткод) виртуальной машиной CVM. Исполнение. Виртуальная машина CVM начинает исполнять байткод. Такое же можно описать просто в виде интерфейсных функций: Следовательно, виртуальная машина CVM никак не привязана к языку ALLang и может самостоятельно исполнять другие коды, которые просто были написаны на её ассемблерном диалекте. Как пример: Результат исполнения выглядит так: Кто более внимательный, тот обнаружит, что CVM выполнила инструкцию mul, хотя таковой не было представлено в списке инструкций. Суть заключается в том, что помимо основных инструкций, благодаря которым в теории (в теории) можно выполнить всё, таковая виртуальная машина также предоставляет более практические инструкции, которые изображены в следующей таблице.  Bytecode Stack Args Instruction 0xA0 2 0 add 0xB0 2 0 sub 0xC0 2 0 mul 0xD0 2 0 div 0xE0 2 0 mod 0xF0 2 0 shr 0xA1 2 0 shl 0xB1 2 0 xor 0xC1 2 0 and 0xD1 2 0 or 0xE1 1 0 not 0xF1 3 0 jl 0xA2 3 0 jne 0xB2 3 0 jle 0xC2 3 0 jge 0xD2 1 0 allc И вот здесь начинается самая интересная часть связанная с языком ALLang. Таковой язык программирования базируется исключительно на основных операциях виртуальной машины CVM. Это значит, что любое сложение он выполняет через множество рекурсивных операций инкрементирования. Любое умножение выполняет через множество рекурсивных операций сложения и т.д. В этом истинная ужасность данного языка и одновременно его красота. Можно сказать, что таковой язык придерживается "втупую" аксиомы Пеано каждый раз, когда ему необходимо что-либо сделать.  Именно по этой причине, если мы скажем ALLang вычислить факториал с x = 6, он просто рухнет из-за того, что стек виртуальной машины будет переполнен. Зная это, мы теперь можем посмотреть как его внутреннию ужасную компиляцию в язык ассемблера, так и его библиотечные функции.   *Код факториала мог выглядить куда более миниатюрным, если бы использовались добавочные (а не основные) функции виртуальной машины, и если бы не использовалась везде и постоянно рекурсия. Теперь посмотрим некоторые реализации библиотечных функций, разберём assembly и source типы. Начнём пожалуй с assembly, директория lib/vms. Библиотека типа assembly добавляет обёрточные функции над инструкциями виртуальной машины CVM с целью, чтобы язык ALLang мог ими пользоваться. Как пример, операция inc. В данном случае, labl _inc - это определение имени функции и таковую мы действительно можем вызвать из ALLang, если импортируем таковую библиотеку прямо или косвенно (как пример через высокоуровневые функции inc, add и т.д.).  Операции push -2 и load подгружают переменную из аргумента функции, далее при помощи операции inc создаётся новая переменная, к которой прибавляется единица. При помощи операций push -1, push -3, stor результат инкрементирования перемещается обратно в принимаемый аргумент. Операция pop удаляет созданную переменную. Операция jmp перепрыгивает обратно на точку вызова функции labl _inc. В общем по такому шаблону создаются и другие низкоуровневые функции для языка ALLang. Функция декремента: Функция сравнения двух чисел (==): Функция сравнения двух чисел (>):  В нашем контексте я привёл лишь четыре низкоуровневых функций и один код связанный с инициализацией функции main. На самом деле этого вполне достаточно, но могут существовать также функции, которые будут нечистыми со стороны функционального программирования. Например, виртуальная машина позволяет вводить несколько аргументов и получать несколько результатов. В нашем же случае мы можем принимать несколько аргументов, но выводим всегда одно число. Мы можем легко исправить данный случай просто написав функции _get (для игнорирования аргументов и взятия их по адресу) и _set (для сохранения результатов): Результат исполнения: Функция _set: Функция _get:  Вышеописанный код показывает, что мы можем в буквальном смысле модифицировать язык ALLang налету, изменяя его же правила. Тем не менее, показанный код наверное не очень хорош в том плане, что он всё же "грязнит" чистую функциональность языка. Теперь давайте приступим к рассмотрению высокоуровневых функций языка ALLang, которые находятся в директории lib/all. Таковых функций я написал достаточно большое количество, но давайте разберём лишь несколько. Наверное самыми основными функциями являются функции инкрементирования и декрементирования, но уже в виде высокоуровневых примитивов. Как пример, функция inc: Здесь происходит обычное импортирование низкоуровневой функции и её последующая обёртка. Ровно такая же ситуация с функциями dec, eq, gr. Иногда есть необходимость использовать не функцию eq, а обратную ей функцию. В таком случае нужно лишь выполнить операцию eq и применить к ней операцию not. Также оно и работает в ALLang. Функция not: Функция ret:  Далее, операция сложения может быть представлена как её рекурсивное применение операции с операцией инкрементирования. Функция умножения: Функция вычитания: Функция деления: Функция abs: Функция neg:  Далее логические функции представляются следующим образом: Функция not: Функция or: Функция xor:  В результате, мы буквально восстанавливаем все базовые функции с нуля, которые в других языках программирования представляются как по умолчанию (логично, ведь кому в здравом уме захочется писать собственные функции сложения, умножения и т.д.). Данные "конструкторы" становятся возможными лишь из природы языка ALLang, когда таковой сильно привязан к языку ассемблера виртуальной машины CVM (аналогии на самом деле существуют и в нормальных языках, подобия языка Си). Сам ALLang и виртуальная машина CVM написаны на языке Си. Кому интересно, весь исходный код ALLang'a вы можете посмотреть тут, а код CVM тут. Что там, что там находится малое количество кода. Чтобы проверить выполнение ALLang, можно воспользоваться следующей инструкцией: Как вы увидели, сам язык программирования является более эзотерическим языком, нежели реально применимым. Тем не менее, его интересной особенностью является лёгкая "кастомизация", при помощи которой можно буквально модифицировать язык посредством ассемблерного кода, добавляя всё больше новых операций. Другой, не менее интересной особенностью, является ужасное его выполнение при котором большинство операций вычисляются через множество вызовов inc и dec в рекурсивном исполнении (из-за чего стек виртуальной машины от такой наглости перестаёт наполняться). Ну и последним интересным пунктом является его чистая функциональность и примитивность исполнения, что в настоящее время уже редко где можно встретить. Поэтому язык является по своей сути бесполезным и красиво ужасным (а может и ужасно красивым).  Криптограф, Программист