Там есть реализация регистровой машины и есть внизу маленький тест/пример, что она вообще хоть насколько-то работает и как.
Регистровая машина имеет какой-то набор состояний
State, для простоты в коде это синоним
int, в каждом из которых будет выполнена одна из допустимых команд
Op над содержимым регистров, содержащих каждый по одному множеству. Команда также при выполнении выдаст, в какое следующее состояние надо перейти машине, и
None означает, что пора остановиться. По тому, что позволяет исходная версия языка, я подобрал такие команды:
• Выполнить над значениями двух регистров операцию
+,
*,
- или
~. Ради экономии и здравого смысла эти четыре команды представляются одной, к которой добавлен параметр—тип операции (его задаёт
BinOpType).
• Скопировать значение из одного регистра в другой. Можно выразить через
+ или
*, но пускай.
• Установить значение регистра в
[]. Опять же можно выразить через
~ или
-, но опять же пускай, микрооптимизация не отпускает.
• Добавить к значению в регистре как элемент значение из другого регистра. Эта команда вместе с предыдущей позволят получать значения, представленные в коде литералами
[x1, x2, ...]: взять пустое и надобавлять по очереди
x1,
x2 и т. д..
• Проверить, пустое ли множество в регистре, и перейти в одно или другое состояние в зависимости от этого.
• Попробовать разобрать множество в регистре на
, и если оно пустое, перейти в одно состояние, а если разбирается, присвоить
одному регистру,
другому и перейти в другое состояние. Опять же предыдущую команду можно было бы не вводить, ограничившись этой, но не хочется заставлять при каждом
ife или
whilene сначала вычислять
, а потом ещё вводить мусорный регистр, куда эти значения выкинуть.
• Сделать assert, и если он не выполняется, выдать ошибку; машина умеет показывать, что работа была прервана из-за ошибки и выдавать её, и эту команду я добавил, чтобы был пример команды, имеющей возможность ошибку вызвать (остальные в этом наборе всегда куда-нибудь дальше переходят).
Машину можно пускать (изначально с нулевых значений регистров) пошагово, сразу до конца (если она завершится), и наконец запустить её, придав переданные значения некоторым регистрам, которые будут считаться входными, и после окончания работы выдав значения из каких-то ещё регистров, которые будут таким образом выходными (один и тот же может быть и входным, и выходным, хотя для компиляции это будет нежелательно). Какие регистры при таком запуске считать входными и выходными и в каком порядке соотносить с передаваемыми значениями, задаёт
IOSpec. И кстати я забыл добавить способ изменять значения регистров просто так, не как часть последнего метода, ну да ладно, он вряд ли будет нужен.
Машина при инициализации проверяет, корректный ли ей передали код, так что код каждой команды может не проверять, существует ли регистр, который ей нужен, а методы запуска — существует ли состояние, в которое предполагается переходить. Для этого класс каждой команды реализует методы, выдающие все состояния и регистры, которые могут пригодиться, а не только один метод запуска команды.
Машина-пример в самом низу реализует сложение натуральных чисел, закодированных моим способом, и выводит исходные числа и результат и в виде множеств, и в их численной интерпретации.
Комментарий в
HFSet.deconstruct, если интересно, но не совсем ясно, о том, что если реализовать множества в явную кортежами, в которых значения отсортированы, то можно будет легко отсоединять наименьший или наибольший элемент и выдавать остаток, в то время как интерфейс стандартных множеств из библиотеки такой возможности не содержит (деревьями ли они реализованы или хеш-таблицами, это действительно не будет эффективной операцией, хотя может быть некоторые виды деревьев наверно что-то такое позволяют) (ну можно взять мутабельное множество, взять его копию, сделать с ней
pop(), но это не то).
Наконец там нет никакого парсера, который бы переводил что-то похожее на ассемблерный код в описание машины, потому что при предполагаемом использовании он не пригодится. Хотя мне стоило бы сделать превращение кода машины в строку, чтобы можно было дизассемблировать нагенерированную чем-то (например неправильно) машину. Кажется, все высокоуровневые детали описал.