(Dieser Dokument ist eine laufende Arbeit…)
Hinweis: Die "CPP" -Ast enthält einen "dummen" Port dieses Projekts von C bis C ++, das ich aus mehreren Gründen getan habe:
J2 ist ein System, das eine minimalistische Programmiersprache („Edikt“ für „ausführbares Wörterbuch“) mit einem C -Reflexions-/FFI -System kombiniert. Sie können freigegebene Bibliotheken problemlos importieren und die darin enthaltenen Datentypen, Variablen und Funktionen (zumindest in globalem Umfang) direkt verwenden, ohne dass ein „Kleber“ -Code schreiben muss.
Zum Beispiel, wenn ich eine Datei „Inc.c“ habe, die::
typedef struct mystruct { int i; float f; } mystruct;
extern mystruct increment(mystruct x) { x.i+=1; x.f+=1; return x; }
... und ich mache eine gemeinsame Bibliothek daraus:
gcc --shared -g inc.c -o inc.so
... Dann kann ich diese gemeinsame Bibliothek in den Edikt -Interpreter importieren und es richtig machen, sie zu verwenden:
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)
Es sieht einfach aus, aber hier unter den Covers ist hier viel los ...
„Edikt“ ist eine minimalistische Programmiersprache, die ihre Einfachheit ausgibt, indem sie die integrierte Fähigkeit zum Verständnis und dynamisch mit C-Bibliotheken bindet und "native" Zugriff auf C-Typen, Variablen und Methoden von willkürlicher Komplexität bietet, ohne zu schreiben, ohne zu schreiben, ohne zu schreiben, ohne zu schreiben, ohne zu schreiben Wrapper oder Klebercode. Alternativ können Sie es als Reflexionsbibliothek für C -Programme betrachten, mit denen sie zur Laufzeit dynamischen Zugriff auf ihre eigenen Interna aufdecken können.
Die Sprache basiert auf einem Fundament von drei Elementen:
Diese drei Elemente versammeln sich zur Laufzeit in einer mehrzweckigen programmierbaren Umgebung. Das System startet sich selbst durch:
Die Entwicklung des Edikts wurde von Forth, Lisp, Freude, Faktor, TCL, Mathematica und anderen beeinflusst.
Schlüsselpunkte:
while (call=vm_dispatch(call)); // VM's inner loop
(Hinweis: Ich fand diese "Zickzack" -Struktur, die sehr ähnlich ist (und vorherrscht) mein Listree: https://www.nongnu.org/gzz/gi/gi.html)
Der „Listree“ ist die Kerndatenstruktur des Edikts und die VM, die es implementiert. Eine Instanz eines Listree besteht einfach aus einem Listree -Wert, der enthält:
Die Möglichkeit eines Listree -Wertes, der (bezeichnete) Verweise auf andere Listree -Werte enthalten, macht den Listree zu einer „hierarchischen“ oder „rekursiven“ Datenstruktur. Der Listree ist sowohl im Kern des Edikts als auch im System, das es implementiert.
*Es ist in dieser einfachen Erklärung nicht explizit (und das ist absichtlich), aber jedes Etikett bezieht sich tatsächlich auf eine Liste von Verweise auf andere Listree -Werte.
Wie bereits erwähnt, unterhält die VM den gesamten Staat innerhalb eines Listree-Wertes, einschließlich der Unterleusel „Datenstapel“ und „Wörterbuch“, unter anderem.
Im EDICT gibt es zwei Arten von Datenwerten, aber die Werte beider Typen werden unter Verwendung von LISTREE -Werten gespeichert, die entweder im Datenstapel des VM oder in seinem Wörterbuch vorhanden sind.
Die einfachste Art von Wert ist eine "wörtliche". Literale sind nur Text, beschrieben von quadratischen Klammern:
[This is a literal]
Wenn Sie aus einem Lisp-Hintergrund kommen, denken Sie vielleicht, dass der Dolmetscher Literale in S-Expressionen unterteilt, aber dies ist nicht der Fall. Alles zwischen den Klammern ist "buchstäblich" in einem „Datenpuffer“ eines Listree -Wertes dargestellt.
Der Edikt -Dolmetscher verfolgt verschachtelte Quadratklammern, also:
[This is a [nested] literal]
wird als einzelnes wörtliches mit dem Wert "Dies ist ein [verschachtelter] buchstäblicher" interpretiert.
Ein in der Definition eines buchstäblichen "" -Scharakters entgeht dem nächsten Charakter, sodass der Dolmetscher Literale erstellen kann, die (zum Beispiel) unausgeglichene Quadratklammern enthalten:
[This is a literal containing an unbalanced [ bracket]
Wenn der Dolmetscher auf einen stößt, werden literale Werte (oder besser gesagt , auf den Wert - eine wichtige Unterscheidung) auf den Datenstapel platziert.
Otoh, der Dolmetscher macht etwas mehr Verarbeitung bei allem, was kein wörtliches ist. (Es gibt andere Arten von Werten, die keine Literale sind, die ich in einem späteren Abschnitt diskutieren werde…)
Der Ediktprogrammierer kann Werten im Stapel Namen zuweisen und anschließend auf diese Werte mit diesen Namen verweisen. Die Aufgabe sieht einfach so aus:
@mylabel
Die Zuordnung eines Namens zu einem Wert macht tatsächlich mehrere Dinge:
Wenn der Dolmetscher einen Verweis auf eine Etikett sieht, wird diese Referenz durch eine Referenz auf den Wert ersetzt, der dieser Beschriftung zugeordnet ist… mit anderen Worten:
Das Edikt unterscheidet sich auf eine wichtige Weise von anderen Sprachen. Viele Sprachen sind „homoikonisch“, dh Code und Daten werden mit denselben zugrunde liegenden Strukturen dargestellt. (Lisp ist ein traditionelles Beispiel für eine homoikonische Sprache.) Edikt ist weder homoikonisch noch nicht -homoikonisch: Es hat überhaupt keine „Funktionen“. Es hat einfach einen Bewertungsoperator, der auf Werte angewendet werden kann.
Eine grundlegende „funktionsähnliche“ Sache im Edikt könnte aussehen:
[1@x]
(Weisen Sie dem Wert "1" das Etikett "x" zu.)
Beachten Sie, dass es nur ein wörtliches ist .
Um es aufzurufen , wird der Bewertungsoperator verwendet:
[1@x]!
Das Ergebnis davon ist genau das gleiche, als hätte der Dolmetscher gerade Folgendes direkt gelesen:
1@x
All der Bewertungsoperator füttert den Inhalt der Oberseite der Oberseite des Stapels auf den Dolmetscher.
Jetzt: Erinnern Sie sich daran, dass Etiketten Werten zugeordnet werden können und dass Bezeichnungen aufgerufen werden können, um ihre Werte abzurufen, und diese Werte werden in den Stapel gedrückt, und dass der Bewertungsoperator den Inhalt des Stapels zurück in den Interpreter füttert:
[1@x]@f
f!
Diese kleine Sequenz macht Folgendes:
Das Edikt kann C -Bibliothekstypen und globale Variable/Funktionsinformationen über den Abschnitt Debugging (Zwerg) einer Bibliothek importieren, falls dies verfügbar ist. Die Zwerginformationen werden im Wörterbuch verarbeitet und gespeichert, und die VM kann diese Informationen „verstehen“ und im Edikt -Interpreter unter Verwendung derselben einfachen „nativen“ Syntax, die auf wörtlichen Werten betrieben wird, präsentieren.
int! @x
MyCGlobalInt @y
xy Multiply!
(Wischen Sie unten…)
Ref | Referenz |
-REF | Referenz (Schwanz) |
@ | Zuordnung zu TOS |
@Ref | Zuordnung zur Referenz |
/ | TOS veröffentlichen |
/Ref | Release Ref |
^Ref | Metareferenz |
Ref^ | Führen Sie die obere Stapelschicht zusammen, um refitiert zu werden |
^Ref^(^ref +^) | Metareferenz- und Verschmelzung der oberen Stapelschicht zu Ref |
! | ToS bewerten |
Tos <...> | "Bewerten im Diktat-Kontext": Drücken Sie TOS in die DICT, bewerten Sie die Inhalte von <...>, Pop-Top of Dict Stack an TOS. |
Tos (...) | „Bewerten im Code-Kontext“: Null-Stack/Diktierschichten drücken, TOS in den Code-Stack drücken, die Inhalte von Parens bewerten, die Spitze des Codestacks bewerten, die Spitze des Diktierens verwerfen, die Oberseite des Stacks in die vorherige Ebene verkettet. |
Ein einfaches reflektierendes Programm:
int! [3]@ square! stack!
Abbauen:
int | Such- und Push -Wert von „int“ (einem nativen C -Typ) auf den Stapel |
! | Bewerten Sie Top-of-Stack, in diesem Fall eine Instanz eines „int“ zugewiesen |
[3] | Schieben Sie das wörtliche „3“ auf den Stapel |
@ | Abtretung; In diesem Fall wird die Zeichenfolge „3“ automatisch in ein C „int“ gezwungen. |
square | Suchen Sie und schieben Sie den Wert von „Quadrat“ (einer nativen C -Methode) auf den Stapel |
! | Bewerten Sie Top-of-Stack, in diesem Fall ein FFI-Aufruf zur nativen C-Methode „Quadrat“ |
stack | Suchen Sie und schieben Sie den Wert von „Stack“ (einer nativen C -Methode) auf den Stapel |
! | Bewerten Sie die Spitze des Stacks ... |
Sie werden int 0x9 sehen, dh 3 quadratisch.
Die explizite Schöpfung und Zuordnung einer C -Ganzzahl wird nur für das Beispiel angezeigt. Eine einfachere Version wäre:
[3] square! stack!
Der gleiche Zwang wäre beim Marshalle von FFI -Argumenten automatisch durchgeführt worden.
Beachten Sie, dass "Code" nicht automatisch ausgewertet wird. Die Bewertung wird explizit über "!" Code ist nur Daten, bis Sie sich entscheiden, ihn auszuführen:
[3] square stack! | Daten und "Code" auf Stack, unegelisiert |
! stack! | Bewerten Sie TOS und beobachten Sie die Ergebnisse |
Fakultät:
[@n int_iszero(n) 1 | int_mul(fact(int_dec(n)) n)]@fact
In einem Listree enthält ein Wert ein Wörterbuch von Schlüssel-/CLL-Paaren, wobei jede CLL ("zirkulär verknüpfte List", die eine doppelte Warteschlange implementiert, eine oder mehrere Verweise auf Werte enthält, von denen jedes ein Wörterbuch enthält. .. Und so weiter. Die Schlüssel-/DEQ -Komponente eines Listree ist eine BST -Implementierung, die auf der "einfachen" RBTree -Variation von Arne Andersson basiert. (http://user.it.uu.se/~arnea/ps/simp.pdf)
Die VM ist sehr einfach; Es bewertet Bytecodes, von denen jeder ein Index in ein Array von C -Methoden ist, die die Bytecode implementiert.
ZURÜCKSETZEN | Klar beleuchtet |
Ext | Decodieren Sie die Zeichensequenz und setzen Sie lit ("[One Two 3]", "ABC") |
Ext_push | Leuchten Sie auf den Datenstapel |
Ref | Erstellen Sie Dictionary Reference Ref von LIT |
Deref | Entschlossenheit Ref im Wörterbuch |
ZUORDNEN | Pop -Top of Data Stack und in das Wörterbuch am Standort Ref ("@" einfügen ("@") |
ENTFERNEN | Freigabewert bei Ref. |
Bewerten | Pop -Top of Data Stack und a) Rufen Sie FFI an oder b) auf den Codestapel und ergibt VM |
Ctx_push | Pop Top of Data Stack und schieben Sie ihn zum Kopf des Wörterbuchstapels, drücken Sie eine neue Ebene in den Datenstapel |
CTX_POP | Sicherung der Top zwei Schichten von Datenstapel, Pop -Leiter des Wörterbuchstapels und drücken Sie ihn auf den Datenstapel |
Fun_push | Pop -Top of Data Stack und drücken Sie den Func -Stack, fügen Sie den Datenstapel Ebene hinzu, fügen Sie eine Nullschicht zum Wörterbuch hinzu |
Fun_eval | Pushen Sie {Fun_pop} in den Code -Stack, das Pop -Top of Func Stack und machen Sie "Eval". |
Fun_pop | Sicherungsschicht zwei Stapelschichten, Null -Wörterbuchschicht wegwerfen |
WERFEN | Eine Ausnahme* werfen* |
FANGEN | Eine Ausnahme aufnehmen* |
*Ausnahmen sind, wie sowohl VM-
Diese einfache Reihe von Operationen reicht aus, um mit dem Wörterbuch zu interagieren, Sprachkonstrukte rekursiv zu bewerten (die VM ist eine stacklose Implementierung) und vor allem C -Typen, Funktionen und Daten ausnutzen. Es ist ein Bare-Bones-Framework, das sich auf seine enge Kopplung an C beruht, um seine Fähigkeiten mühelos zu erweitern.
(Jede GNU/Linux -Distribution, die ihr Salz wert ist, hat diese:
Um zu rennen: Angenommen, GCC, CMake und Bibliotheken sind nicht zu alt, rennen Sie einfach "make", um auf die Reply zu bauen und in die Replung einzusteigen.