(Этот документ находится в стадии разработки ...)
Примечание: филиал «CPP» содержит «тупой» порт этого проекта от C до C ++, который я сделал по нескольким причинам:
J2 - это система, которая объединяет минималистский язык программирования («Эдикт» для «исполняемого словаря») с системой C Отражным/FFI. Он позволяет легко импортировать общие библиотеки и использовать дата, переменные и функции внутри него (по крайней мере, те, кто в глобальном объеме), без необходимости писать какой -либо код «клея».
Например, если у меня есть файл «Inc.c», содержащий:
typedef struct mystruct { int i; float f; } mystruct;
extern mystruct increment(mystruct x) { x.i+=1; x.f+=1; return x; }
... и я делаю из этого общую библиотеку:
gcc --shared -g inc.c -o inc.so
... тогда я могу импортировать эту общую библиотеку в интерпретатор Эдикта и получить право на использование:
e> loadlib([inc.so]) @mylib
Finished curating module
e> mylib<mystruct! <2@i 4@f> @x increment(x)>/ stack!
VMRES_STACK
<null>
structure_type "struct mystruct"
member "i"
base_type "int" 0x3 (0x7f361c0121a0)
member "f"
base_type "float" 5 (0x7f361c0121a4)
Это выглядит просто, но здесь много всего происходит, под обложками ...
«Эдикт»-это минималистский язык программирования, который компенсирует его простоту, обладая встроенной способностью понимать и динамически связываться с библиотеками C, предоставляя «нативные» доступ к типам C, переменным и методам произвольной сложности, без письма обертки или код клея. В качестве альтернативы, вы можете рассматривать это как библиотеку отражения для программ C, которая позволяет им раскрывать динамический доступ к своим внутренним внутренним органам во время выполнения.
Язык построен на основе из трех элементов:
Эти три элемента собираются во время выполнения в многоцелевую программируемую среду. Система начинает самостоятельно:
На развитие эдикта повлияла Forth, Lisp, Joy, Factor, TCL, Mathematica и другие.
Ключевые моменты:
while (call=vm_dispatch(call)); // VM's inner loop
(Примечание: я нашел эту «зигзаговую» структуру, которая очень похожа (и предшествовала) My Listree: https://www.nongnu.org/gzz/gi/gi.html)
«Listree» является основной структурой данных эдикта и виртуальной машины, которая его реализует. Экземпляр Listree состоит просто из значения Listree, которое содержит:
Возможность значения Listree содержать (помеченные) ссылки на другие значения Listree - это то, что делает Listree «иерархическим» или «рекурсивной» структурой данных. Listree находится как в ядре Эдикта, так и в системе, которая его реализует.
*Это не ясно в этом простом объяснении (и это намеренно), но каждая этикетка фактически относится к списку ссылок на другие значения Listree.
Как уже упоминалось, ВМ поддерживает все свое состояние в пределах значения Listree, в том числе «стек данных» и «Словарь», среди других.
В указании существует два типа значений данных, но значения обоих типов хранятся с использованием значений Listree, которые существуют либо в стеке данных виртуальной машины, либо в его словаре.
Самый простой тип значения - это «буквальный». Литералы - это просто текст, очерченные квадратными скобками:
[This is a literal]
Если вы пришли из LISP, вы можете подумать, что интерпретатор разбивает литералы на S-EXPRESS, но это не так. Все между кронштейнами представлено «буквально» в «буфере данных» Listree.
Интерпретатор указывает на вложенные квадратные кронштейны, так что:
[This is a [nested] literal]
интерпретируется как единственный буквальный со значением «Это [вложенное] буквальное».
«» Символ, появляющийся в определении буквального «ухода» следующего персонажа, позволяя интерпретатору создавать литералы, содержащие (например,) несбалансированные квадратные скобки:
[This is a literal containing an unbalanced [ bracket]
Когда интерпретатор сталкивается с одним, буквальные значения (или, скорее, ссылки на значение - важное различие) просто помещаются в стек данных.
OTOH, переводчик делает немного больше обработки на все, что не является буквальным. (Есть и другие виды ценностей, которые не являются литералами, которые я буду обсуждать в более позднем разделе…)
Программист Edict может назначать имена значениям в стеке, а затем ссылаться на эти значения этими именами. Задание выглядит просто так:
@mylabel
Назначение имени на значение фактически делает несколько вещей:
Когда интерпретатор видит ссылку на этикетку, эта ссылка заменяется ссылкой на значение, связанное с этой меткой ... Другими словами:
ЭКИКТ отличается от других языков одним важным способом. Многие языки являются «гомоконией», т.е. код и данные представлены с использованием тех же основных структур. (LISP является традиционным примером гомоиконического языка.) Эдикт не является ни гомоиконическим, ни не -гомоконическим: у него вообще нет «функций». У него просто есть оператор оценки, который может быть применен к значениям.
Базовая «функциональная» вещь в эдикте может выглядеть как:
[1@x]
(Назначьте метку «x» значению «1».)
Обратите внимание, что это просто буквальный .
Чтобы вызвать его, используется оператор оценки :
[1@x]!
Результат этого точно такой же, как если бы интерпретатор только что прочитал следующее:
1@x
Все, что делает оператор оценки, это подает содержимое верхней части вершины стека интерпретатору.
Теперь: напомним, что этикетки могут быть назначены значениям, и что этикетки могут быть вызваны для вспоминания их значений, и эти значения направляются в стек, и что оператор оценки подает содержимое стека обратно в интерпретатор:
[1@x]@f
f!
Эта маленькая последовательность делает следующее:
Эдикт может импортировать типы библиотеки C и информацию о глобальной переменной/функции через раздел отладки (карликовой) библиотеки, если она доступна. Информация о карлике обрабатывается и хранится в словаре, и виртуальная машина может «понимать» эту информацию и представлять ее в интерпретаторе Edict, используя тот же простой «нативный» синтаксис, который работает на буквальных значениях.
int! @x
MyCGlobalInt @y
xy Multiply!
(WIP ниже…)
Рефери | Ссылка |
-Реф | Ссылка (хвост) |
@ | Назначение TOS |
@Ref | Назначение на ссылку |
/ | Выпустить Tos |
/Ref | Выпуск Ref |
^Ref | Метареференция |
Ref^ | Слияние верхнего слоя стека до рефлекса |
^Ref^(^ref +^) | Metareference и Merge Top Stack Layer на Ref |
! | Оценить TOS |
Tos <...> | «Оценить в дикте-контексте»: подтолкнуть TOS, чтобы DICT, оценить содержимое <...>, поп-вершина DICT Stack to TOS. |
Tos (...) | «Оценить в контексте кода»: Нажмите уровни стека/дикта NULL, нажмите на стек кода, оцените содержимое парен, оцените верхнюю часть стека кода, отбросьте верхнюю часть DICT, конкатенате верх стека на предыдущий уровень. |
Простая рефлексивная программа:
int! [3]@ square! stack!
Авария:
int | Поиск и толкание значения «int» (нативный тип C) на стек |
! | Оценить верхнюю часть, в данном случае распределите экземпляр «int» |
[3] | Нажмите буквальный «3» на стек |
@ | Назначение; В этом случае строка «3» автоматически принуждается в C «int» |
square | Поиск и нажмите значение «квадратного» (нативного метода C) на стек |
! | Оценить верхнюю часть, в данном случае вызов FFI на Native C Метод «Square» |
stack | Поиск и нажмите значение «стека» (нативный метод C) на стек |
! | Оценить верхнюю часть стека ... |
Вы увидите int 0x9, то есть 3 квадрат.
Явное создание и назначение целого числа C показаны только для примера; Более простая версия будет:
[3] square! stack!
Такое же принуждение было бы выполнено автоматически во время маршалирования аргументов FFI.
Обратите внимание, что «код» не оценивается автоматически; Оценка вызывает явно через "!". Код - это просто данные, пока вы не решите «запустить» его:
[3] square stack! | Данные и «код» в стеке, не оценили |
! stack! | Оценить TOS и наблюдать за результатами |
Факториал:
[@n int_iszero(n) 1 | int_mul(fact(int_dec(n)) n)]@fact
В Listree значение содержит словарь пар клавиш/CLL, где каждый CLL («круговой лист», внедряющий двойную очередь), содержит одну или несколько ссылок на значения, каждый из которых содержит словарь. .. И так далее. Ключ/DEQ компонент Listree - это реализация BST, основанная на «простой» вариации RBTree Арне Андерссона. (http://user.it.uu.se/~ Arnea/ps/simp.pdf)
Виртуальная машина очень проста; Он оценивает байткоды, каждый из которых является индексом в массив методов C, который реализует байт -код.
ПЕРЕЗАГРУЗИТЬ | Чистого зажженного |
Допредный | Декодировать последовательность символов и установить Lit ("[One Two 3]", "ABC") |
Ext_push | Нажимайте зажженные на стек данных |
Рефери | Создать справочную ссылку на словарь из Lit |
Дереф | Разрешить ссылку в словаре |
НАЗНАЧАТЬ | POP TOP стека данных и вставьте в словарь на местоположении REF ("@") |
УДАЛЯТЬ | Значение выпуска на ссылке |
Оценка | POP TOP стека данных и A) Вызовите FFI, или B) нажмите на стек кода и выпустите виртуальную машину |
Ctx_push | POP TOP стека данных и подтолкнуть его к Head of Dictionary Stack, нажмите новый уровень в стек данных |
Ctx_pop | Обеспечить два верхних уровня стека данных, поп -голова словарного стека и подтолкнуть его в стек данных |
Fun_push | Поп -вершина стека данных и нажмите на стек Func, добавьте слой в стек данных, добавьте нулевый слой в словарь |
Fun_eval | Push {fun_pop} к стеку кода, поп -вершина стека Func и сделайте "eval" |
Fun_pop | Верхние верхние слои с предохранителем, выбросьте нулевой слой слоя |
БРОСАТЬ | Бросить исключение* |
ЛОВИТЬ | Поймать исключение* |
*За исключением того, как реализуются как ошибки, так и условные виртуальные машины, и они возится с состоянием виртуальной машины чуть больше, чем средняя операция, заслуживая свой собственный абзац.
Этот простой набор операций достаточен для взаимодействия с словарем, рекурсивно оценить языковые конструкции (виртуальная машина - это реализация без стека) и, что наиболее важно, эксплойтские типы C, функции и данные. Это платформа с обнаженными костями, которая опирается на ее жесткую связь с C, чтобы легко расширить свои возможности.
(Любое дистрибуция GNU/Linux, стоимостью их соли:
Для запуска: при условии, что GCC, CMAKE и библиотеки не слишком древние, просто запустите «сделать», чтобы построить и попасть в реплику.