Пишу, по большей части, про историю, свою жизнь и немного про программирование.

99 бутылок: CFAQ

Отдаю очередную дань своему давнему хобби — написанию американской традиционной песни про пиво на разнообразных языках программирования.

72. CFAQ (читается «СИФАК»). Очень люблю самодельные языки программирования, есть в них какое-то милое очарование, крафтовость с нотками олдскула. Услада сердцу после вездесущего Кровавого Энтерпрайза.

Когда узнал, что у известного писателя-фантаста Леонида Каганова есть собственный фреймворк для чипа ESP8266 со встроенным самописным скриптовым языком программирования «СИФАК», не мог пройти мимо. Леонид хоть и зарабатывает на жизнь литературными трудом, но когда-то работал программистом, разрабатывая на ассемблере модули устройств для геофизики и дозиметрии.

Язык, конечно, довольно простой, без поддержки подпрограмм или функций (их можно эмулировать отдельными файлами, передавая параметры через глобальные переменные), но с циклами, ветвлениями и переменными.

Из-за этой особенности пришлось идти на ухищрения — например код, который определяет грамматическое число, во избежание дублирования, я обернул циклом и вызвал два раза.

# 99.TXT. Написано Степанищевым Евгением, апрель 2020
B = 99
repeat -1 {
    Bt = {B}

    repeat 2 {
        if {Bt} = 0 {
            set S = no bottles
        } else {
            if {Bt} = 1 {
                set S = 1 bottle
            } else {
                set S = {Bt} bottles
            }
        }

        if {Bt} = {B} {
            echo {S} of beer on the wall, {S} of beer.<br>
        } else {
            echo Take one down and pass it around, {S} of beer on the wall.<br>
        }

        Bt -= 1
    }

    B -= 1

    if {B} = 0 {
        echo No more bottles of beer on the wall, no more bottles of beer.<br>
        echo Go to the store and buy some more, 99 bottles of beer on the wall.

        exit
    }
}

Первую версию программы писал вайтбордингом — мне её просто негде было запустить. К счастью Леонид предоставил тестовый стенд на побаловаться, я исправил все ошибки и теперь программа гарантированно работает, проверено. Правда все 99 «бутылок» она вывести не может — не хватает памяти в буфере вывода, так что испытывал на десяти.

Выполнение программы на стенде, предоставленном автором языка

Скудность конструкций языка компенсируется относительным богатством встроенных функций для работы с различным оборудованием и взаимодействия с внешним миром (например, он умеет выводить информацию через бота в мессенджер «Телеграм»), но в моей программе это, разумеется, не пригодилось.

7 комментариев
PastorGL 2020

Хех. В понедельник как раз буду читать по скайпу молодым коллегам лекцию «Как написать интерпретатор в сотню с небольшим строк (на основе стековой VM с пятком опкодов) для скриптинга объектной модели приложения».

Циклы, ветвления, переменные, + возможность дёрнуть аннотированную функцию из хоста.

Частенько балуюсь таким на своих проектах, чтобы писать развесистые конфиги, но не заморачиваться встройкой полноценных скриптовых движков. В принципе, для примера можно будет заюзать и 99 бутылок :)

Евгений Степанищев 2020

Ну да, для такой цели это идеальный алгоритм :)

Fyodor Ustinov 2020

«Как написать интерпретатор в сотню с небольшим строк (на основе стековой VM с пятком опкодов) для скриптинга объектной модели приложения»
Люди вынуждены придумывать FORTH снова и снова. Что-ж за беда такая...

PastorGL 2020

На игрушечном языке, где есть только опкоды IF, ELSE, END, FOREACH, LET, и CALL, а все переменные глобальные и могут иметь типы «список» и «одиночное значение», «99 бутылок» будут выглядеть примерно так:

```
LET TOTAL 99
CALL .. TOTAL,0
LET NUM _
FOREACH NUM
CALL cat NUM,» bottles»
LET BOTTLES _
CALL -eq 0,NUM
LET ZERO _
IF ZERO
LET BOTTLES «No more bottles»
END
CALL -eq 1,NUM
LET LAST _
IF LAST
LET BOTTLES «Last bottle»
END
CALL -eq TOTAL,NUM
LET FIRST _
IF FIRST
ELSE
CALL print «Take one down and pass it around, «,BOTTLES,» of beer on the wall.»
END
CALL print BOTTLES,» of beer on the wall, «,BOTTLES,» of beer.»
END
CALL print «Go to the store and buy some more, «,TOTAL,» bottles of beer on the wall.»
```

Функции .. (сгенерировать список от и до), print (вывод), -eq (равенство) и cat (собрать из «списка» «одиночное значение») все вызываются из хоста. Подчёркивание — возврат из последней функции.

Интерпретатор этого дела укладывается в 72 строки на жабе, плюс ещё небольшая кучка интерфейсов и пара функций на разбор синтаксиса (нормальный лексер писать для учебного проекта смысла нет). На гитхаб выкладывать не стану, потому что если кому-то придёт в голову его использовать, нехорошо будет.

PastorGL 2020

Люди вынуждены придумывать FORTH снова и снова. Что-ж за беда такая...

Сами конфиги свои пишите на форте, магистра Йоды последователи.

Уж лучше лисп, чем этот ваш гадкий форт.

PastorGL 2020

Блин, эгея испохабила все кавычки и сожрала отступы. Что за блоговый движок такой, который не поддерживает вывод исходника между ``` и ``` ?

Евгений Степанищев 2020

«Что за блоговый движок такой, который не поддерживает вывод исходника между ``` и ```, четыре буквы по вертикали» :))

Собственно, мне и в голову не пришло бы их использовать в любом другом месте, почему именно маркдаун? В комментариях в «Эгее», кстати, очень мало что доступно из разметки.

PastorGL 2020

почему именно маркдаун?

^ вот почему. Цитаты же поддерживаются. Жирный тоже есть. Как-то даже и не ожидаешь, что из всего маркдауна сделано только фиг да маленько.

Евгений Степанищев 2020

Это может быть вики-разметка, например :) Собственно, думаю, это она и есть, вроде в Е2, из которого вышла Эгея, она и была.

Simm Mstr 2020

Я один заметил, что Каганов — ЛеониГ? :)

Евгений Степанищев 2020

Вот блин, спасибо, поправил! :)