Эзотерических языков программирования существует весьма много. Некоторые из них известны, а некоторые - совсем нет. Один из достаточно полных списков эзотерических языков программирования расположен здесь:
http://esolangs.org/wiki/Language_list, там же есть краткие описания.
Просмотр описаний показал, что большая часть языков не представляет особого интереса: либо они вторичны, либо идея в них скучная и банальная. Но есть и достаточно интересные языки, конечно, с учетом, что основная цель их создания - это вовсе не программирование на них.
Среди всех языков мне показались интересными языки, построенные на концепции очереди. Основных их нашлось два: Q-BAL и Oroogu. Q-BAL более сложный, а вот Oroogu имеет весьма простой синтаксис. Но Oroogu настолько малоизвестный, что описание пришлось добывать через
http://web.archive.org. Там же были добыты примеры и даже исходные тексты (на Си) компилятора. Идея, заложенная в Oroogu мне понравилась, поэтому я решил вкратце рассказать об основных особенностях этого языка.
Итак, язык программирования Oroogu. Автором языка является Georg Kraml, цель создания языка неизвестна. Основными типом данных в Oroogu является очередь. Работа с очередью происходит в обычной манере: можно добавлять элементы в конец очереди, можно извлекать из начала. При этом механизма доступа по индексу к произвольному элементу очереди нет.
Очереди могут включать в себя только целые числа. Точнее говоря, специально для реализации Ninety-Nine Bottles of Beer была добавлена поддержка строк, но в ограниченном виде (нельзя манипулировать содержимым строк).
Конструктор очереди выглядит просто: (1, 3, 5) создает очередь с элементами 1, 3 и 5. Возможно использование оператора .. для заполнения промежуточных значений. Так, например, (1, 3, 5 .. 7) будет содержать целые числа: 1, 3, 5, 6, 7. Этот оператор работает и в сторону уменьшения: (10 .. 1) будет содержать числа в убывающем порядке от 10 до 1 включительно.
В Oroogu есть переменные, но они могут содержать только очереди. Имена переменных не могут начинаться с цифры, длина их не может быть больше 8 символов. Переменные, начинающиеся с букв d и e, имеют особенность: после окончания работы программы их значения будут выданы в stdout и в stderr, соответственно. Переменные не нуждаются в определении.
Выражений в Oroogu не очень много.
Выражение присваивания d = c полностью копирует очередь c и присваивает d. Справа могут быть как переменные, так и конструкторы очереди. Слева допускается указание нескольких переменных через запятую:
var = (1, 2, 3)
i = var
k, m = i
После выполнения все упомянутые переменные (а именно var, i, k, m) будут содержать очереди (1, 2, 3).
Выражение объединения d / c объединяет очереди d и c и результат присваивается d.
var1 = (1, 2, 3)
var2 = (4 .. 6)
var1 / var2
В результате var1 будет содержать (1, 2, 3, 4, 5, 6)
Аналогично оператору присваиванию слева допускается указание нескольких переменных.
Выражение пересечения d \ c удаляет из d элементы c, если они есть.
v \ (2) удаляет 2 из очереди v, если 2 там есть
v \ v полностью очищает очередь v
Доступ к данным очереди и арифметика могут осуществляться только в конструкторе очереди. Доступ к значению из начала очереди осуществляется по имени переменной, в которой эта очередь находится. Запись var извлекает значение из начала очереди var и подставляет его в конструкторе. Запись <var просто подставляет значение из var, не извлекая его из очереди. Из арифметических операций поддерживаются традиционные +, -, /, * и оператор возведения в степень ** и оператор остатка от деления (либо %, либо mod - в разных документацих Oroogu указаны разные варианты).
(1, 2, 1 + 2) определяет очередь со значениям 1, 2, 3.
var = (1, 2, 3)
i = (var - 1, <var, var)
В результате var будет (3), а i - (0, 2, 2).
Стоит отметить, что выражения var = f и var = (f) имеют разный смысл. Первое выражение создает полную копию f, а второе выражение - извлекает из f первый элемент, который будет единственным значением очереди i.
В Oroogu нет оператора if в явном виде, но есть условный цикл. Он состоит из двух выражений, записанных подряд. Первое выражение - это заголовок цикла, а второе выражение - это тело цикло, причем тело цикла пишется обязательно в скобках, но может включать в себя несколько выражений.
Первым шагом выполняется заголовок цикла. Получаемая в результате выполнения заголовка очередь просматривается на предмет наличия элементов. Если есть хотя бы один элемент, то выполняется тело цикла. Затем опять идет просмотр очереди заголовка, и цикл повторяется до тех пор, пока в этой очереди есть хотя бы один элемент.
Стоит отметить, что при пустом теле цикла будет происходить зацикливание. Так же понятно, что тело цикла должно тем или иным способом уменьшать очередь заголовка, иначе тоже будет зацикливание.
Пример: i = (1..100) (dump / (<i * i)) собирает в переменной dump квадраты чисел от 1 до 100 включительно, после завершения программы dump (поскольку начинается с буквы d) будет выведен в stdout.
Заголовок цикла i = (1..100) присваивает переменной i очередь, содержащую значения от 1 до 100 включительно. Эта же очередь и будет очередью заголовка цикла. Поскольку первоначально она содержит элементы, то выполняется тело цикла: (dump / (<i * i)). В теле цикла (<i * i) берется значение из начала i, но без извлечения его из очереди, затем берется опять это же значение, но с извлечением. Эти значения (а они равны) перемножаются. Затем dump объединяется с получившейся очередью из одного элемента. Поскольку в теле цикла есть извлечение из очереди i, то цикл конечный. Он пройдет по всем значениям i от 1 до 100, после чего завершится.
Таковы вкратце основные конструкции. Еще стоит отметить, что в Oroogu нет функций, но есть возможность включать куски кода на стадии компиляции из файла.
Важным является то, что язык Oroogu является полным по Тьюрингу. Автор языка доказывает это неформально через сравнение с Т-полным языкомFloop.
Еще примеры.
Вывод Hello, world!:
d / ("Hello, world!")
К очереди d добавляется строка "Hello, world!", после завершения работы значение очереди d выводится в stdout, поскольку имя начинается с буквы d.
Вывод первых 10 чисел Фибоначчи:
buf = (0, 1)
i = (1 .. 10) (d, buf / (buf + <buf) null = (i))
Первоначально buf содержит очередь (0, 1). Затем организуется цикл. В заголовке цикла определяется очередь i, которая первоначально будет содержать значения от 1 до 10 включительно.
В теле цикла конструкция (buf + <buf) извлекает из buf значение и прибавляет к нему следующее значение из buf (но уже не извлекая его). Получившаяся очередь из одного элемента добавляется к очередям d и buf. Выражение null = (i) служит для простого извлечения значения из очереди i (тем самым исключая зацикливание и организуя 10-кратное выполнение тела цикла).
Таким образом, в d накапливается результат (все подсчитанные числа Фибоначчи), а в buf всегда держатся два значения, второе из которых - это последнее подсчитанное на данной итерации число Фибоначчи, а первое - предыдущее значение, точнее значение, которое будет использовано для вычисления следующего числа Фибоначчи.
Не совсем корректный вариант "Ninety-nine Bottles of Beer":
b = (99 .. 1)
(
d / (<b, "bottles of beer on the wall, ")
d / (<b, "bottles of beer.\n")
d / ("Take one down and pass it around, ")
d / (b - 1, "bottles of beer.\n\n")
)
Простой пример, в котором организуется цикл по перебору значений от 99 до 1 включительно. В теле цикла несколько раз используется первое значение из b без извлечения (конструкция <b), а затем под завершение тела цикла первое значение извлекается для организации последовательного прохождения по всем значениям b. Особенностью этого примера является разве что использование строк.
Таков, в общем, язык программирования Oroogu. Не стоит относится к нему как к языку, который может быть большим, чем простая игрушка или демонстрация идеи. Но мне он показался забавным.