Spe3dForth ist eine Programmiersprache, angelehnt an das konzept der von Charles H. Moore im Jahre 1969 entwickelt Programmiersprache Forth.
Table of Contents
Features
prefix-notiertes Programmier- und Script-system
Spe3dForth Die Sprache zeichnet sich syntaktisch dadurch aus, dass damit in prefix- oder auch umgekehrt polnischer Notation (UPN) programmiert wird.
Beispiel: infix-notierter mathematischer Ausdruck: 3 * 4 + 1 Die äquivalente Präfix-notation: 3 4 * 1 + in SpeedForth: 3 4 mul 1 add mit klammerung: ((3 4 mul) 1 add) Die Klammern werden von innen her aufgelöst.
Der Spe3dFoth Interpreter arbeitet zum Zweck der Programmabarbeitung mit einem Stack auf dem solange Parameter abgelegt werden bis ein funktions- oder Thread-Token im Programm erscheint. Grundlegende Funktionen werden sofort abgearbeitet, selbst definierte als inline-funktion rekursiv abgearbeitet.
Paralleles Programmieren
Thread-handling erfolgt in Spe3dForth im Gegensatz zu den meisten Programmiersprachen auf eine sehr einfache Weise. Die parallel auszuführenden Threads werden definiert und mittels eines Befehls gemeinsam gestartet.
Jeder Thread bekommt seinen eigenen virtuellen Speicher, was Deadlocks unmöglich macht, da keine gleichzeitigen Schreibzugriffe auf Ressourcen erfolgen können. Auch Probleme wie "write after read" o.ä. sind hiermit ausgeschlossen.
Auch hält die Verarbeitung des Hauptprogramms an der Stelle des Thread-Starts an und wird erst fortgesetzt nachdem alle Threads terminiert sind.
Dies bringt viele Vorteile im Handling aber auch einige Einschränkungen im Anwendungsbereich mit sich.
Das Pro und Kontro sowie ein geplanter Ausweg werden unter Spe3dForth#Ausweg diskutiert
Datenbank-Funktionalität
Spe3dForth speichert 3D-Objekte in einer Hash-basierten Datenbank ab. Objekte können erstellt, manipuliert und gelöscht werden.
Man kann mehrere Objekte mittels Mengenoperationen zu Gruppen zusammenfassen, auf welche dann Transformationen gemeinsam angewandt werden.
Programmaufbau
SpeedForth verwendet präfix-notation(umgekehrte polnische Notation) geschrieben, was folgende Vorteile mit sich bringt:
- Schnelle Programm-Abarbeitung
- Funktionen können mehrere Rückgabewerte haben
- sehr einfache syntax
- sehr kleiner Interpreter(<100Kb)
Syntax
[parameterliste] [befehl]
[parameterliste]: Mit Leerzeichen getrennte liste von Variablentypen
- z.b. 1.2 1.0 myBox1...
[befehl]: eine Forth-grundfunktion
- mul, print, div, sub, ...
Variablen
Variablen in Forth werden ohne angabe des gewünschten Typs erstellt. Die Befehlszeile "10 a l_var" erstellt eine lokale Variable mit dem Namen "a", dem Inhalt "10" und dem Typ "long".
Variablentypen
- Basisdatentypen
- long – eine ganze Zahl
- float – ein fließkomma-Wert (z.b. 3.14, -1.0, ...)
- bool – ein boolscher Wert (1, 0, true, false)
- string – eine Zeichenkette, eingeschlossen in doppelte Hochkommas( z.b. "Hello World")
- erweiterte Datentypen
- newIdentifier – ein Bezeichner der mit einem Buchstaben beginnt und danach aus zahlen und Buchstaben bestehen kann
- objectType – Ein 3D-Objekt typ. Wird von der Visualisierung bereitgestellt(z.b. Sphere, Polygon,...)
- object3D – Ein 3D-Objekt
- object3DSet – Eine Gruppe von 3D-Objekten
- object3DMember – Ein Wert eines 3D-Objektes(z.b. ID, Color_R, Color_G, Color_B, ...)
- Matrix3f – ist Platzhalter für eine liste von 9 float-Werten
- definition per "code"-Befehl: "9.0 8.0 7.0 6.0 5.0 4.0 3.0 2.0 1.0" matrix1 code
- ergebnismatrix:
| 1.0 | 2.0 | 3.0 | | 4.0 | 5.0 | 6.0 | | 7.0 | 8.0 | 9.0 |
Notiz: im weiteren werden die Basisdatentypen long, bool, String und float unter der Bezeichnung basictypes zusammengefasst.
Unterprogramme
Unterprogramme in Forth können sehr einfach erstellt werden. Sie können bei Ausführung auf dem lokalen und globalen Stack arbeiten.
Unterprogramme können globale und lokale Variablen erstellen.
Funktionsdeklaration
Unterprogramme werden mit dem Befehl "code" erstellt.
- "1 add" inc code
- erstellt ein Unterprogramme mit dem Namen "inc" und dem Inhalt "1 add". Der auszuführende code muss in Anführungszeichen stehen
Unterprogrammaufruf
Unterprogramme werden genau wie Forth-Methoden aufgerufen, indem ihr name auf den Stack gelegt wird:
- "1 add" inc code
- 1 inc print
- Ausgabe: 2
Ablaufsteuerung
Der Programmablauf kann mit den beiden befehlen "do" und "if" beeinflusst werden.
Schleife
Eine schleife braucht zwei Argumente: Die Anzahl der Schleifendurchläufe und den auszuführenden code.
Der code wird dann so oft ausgeführt wie es im ersten Parameter angegeben ist Dieser code wird wie ein Unterprogramm behandelt.
Beispiel: #do nimmt die letzten beiden parameter vom stack und führt dann viermal "print" aus. #print nimmt sich mit jedem Aufruf einen paramter vom Stack. 1 "b=" 2 "a=" 4 "print" do #Ausgabe: a=2 b=1
Entscheidung
Eine Entscheidung kann getroffen werden, indem man den If-Befehl aufruft. Dieser erwartet dri paramter auf dem Stack:
ein Boolean, was als Bedingung benutzt wird und zwei Strings, die jeweils auszuführenden code enthalten. Falls der Bedingungsparamter 1 ist, wird das erste programm ausgeführt Ansonsten das zweite Programm.
Beispiel: #definiert eine variable "y" mit wert=0 0 y g_var #führt den If-befehl aus. dieser wiederum entscheidet anhand des #ergebnisses von "true x and", welcher code nun ausgeführt wird true x and "y inc" "y dec" if &#Ausgabe: -1 (wenn x=true) 1 (wenn x=false)
Threads
In Spe3dForth ist es möglich, parallel zu programmieren. Um dies dem Programmierer so einfach wie möglich zu machen, wurde ein spezielles MultiThreading System entwickelt.
- Threads werden von einem Merger verwaltet.
- bei der Deklaration eines Threads wird dieser in den Merger eingefügt. Ein ThreadPack wird erstellt und kann durch weitere Threads vergrößert werden
- Der Merger muss explizit gestartet werden, was durch den Befehl "startThreads" realisiert wird.
- Threads können lokale Variablen, wie es Unterprogramme können, erstellen
- Threads können auch globale Variablen erstellen
- nach Beendigung aller Threads werden deren neue globale Variablen in SpeedForth eingemischt(merge)
- Sollte ein thread eine Globale Variable erstellen wollen, die schon existiert bzw. ein anderer Thread eine gleichnamige globale Variable erstellt hat, wird die neue Variable verworfen, sodass die schon vorhandene Variable unverändert bleibt
- das Hauptprogramm wird für die Zeit der Threadausführung angehalten und erst fortgesetzt, wenn alle vorher gestarteten Threads terminiert haben
- bildlich gesprochen trennt sich der Hauptprogrammstrang an der Stelle auf, an der die Threads gestartet werden. Mit Beendigung aller Threads wird das Hauptprogramm weiter ausgeführt.
Threads deklarieren
Threads werden deklariert indem der innerhalb auszuführende code in geschweifte klammern gesetzt wird.
#035;Beispiel:
#definition
{2 1 mul a g_var}
{2 2 mul b g_var}
{2 3 mul c g_var}
#merger starten
startThreads
#globale von den threads erstellte variablen auf den Stack legen
"\n" c "c=" "\n" b "b=" "\n" a "a="
#stack in einer schleife ausgeben
get_stack_size "print" do
#Ausgabe:
a=2
b=4
c=6
Pro und Kontra Spe3dForthThreads
Pro
- paralleles Programmieren ist extrem einfach
- Deadlocks, Konkurrierende Zugriffe auf gemeinsame Ressourcen sowie Synchronisationsprobleme existieren nicht
Kontra
- Keine "echte" Nebenläufigkeit der Threads neben dem Hauptprogramm
- keine kommunikation zwischen den Threads eines ThreadPacks
- um optimale geschwindigkeitsvorteile zu erhalten, sollten die Threads eine ähnliche Ausführungsdauer haben. Ansonsten schwinden die Threading-bedingten Geschwindigkeitsvorteile enorm.
Ausweg
- für eine spätere Version von Spe3dForth sind Prozesse geplant, die miteinander über eine art Socket kommunizieren können(s. Roadmap )
3D-Funktionalität
Spe3d bietet die Verwaltung und bearbeitung von 3D-Objekten an.
objekte erstellen
- Spe3D ist immer mit einer visualisierung verbunden wenn man es startet.
- Eine visualisierung erstellt neue 3D-Objekte bestimmter Typen, macht sie der Datenbank bekannt und stellt diese dar.
- Die verfügbaren Objekttypen werden automatisch beim Laden der Visualisierung von dieser abgefragt.
Objekte werden mit den Befehlen "l_obj" und "g_obj" (lokal/global) erstellt.
#Syntax: [objectType] [neuer Variablenname:newIdentifier] g_obj # [objectType] [neuer Variablenname:newIdentifier] l_obj #Beispiel: #erstellt ein neues globales 3D-Objekt Sphere3D mySphere1 g_obj
Objekteigenschaften verändern
Jedes Objekt besitzt Eigenschaften, welche von ihm beutzt werden. Alle Objekte haben zum Erstellungszeitpunkt die gleichen Eigenschaften(Color_r, dimx, radius,...). Da jedoch nicht jedes Objekt alle eigenschaften haben kann (Quader haben z.b. keinen radius etc.), wird vor dem Setzen oder Lesen einer Eigenschaft abgefragt ob dieses spezielle Objekt auch diese Eigenschaft besitzt. Der Versuch auf eine nicht vorhandene Eigenschaft zu schreiben bzw. von ihr zu lesen, endet in einer Fehlermeldung. Die Programmabarbeitung wird danach fortgesetzt.
Objekteigenschaften lesen
#Syntax: [Eigenschaft:object3DMember] [Objekt:object3D] get_obj_member #Beispiel: #erstellt ein neues globales 3D-Objekt mySphere1 Sphere3D g_obj #legt den rot-anteil der Objektfarbe von mySphere1 auf den Stack corlor_r mySphere1 get_obj_member
Objekteigenschaften schreiben
#Syntax: [Wert:BasicType] [Eigenschaft:object3DMember] [Objekt:object3D] set_obj_member #Beispiel: #erstellt ein neues globales 3D-Objekt mySphere1 Sphere3D g_obj #legt den rot-anteil der Objektfarbe von mySphere1 fest 1.0 corlor_r mySphere1 set_obj_member
Objekt-transformationen hinzufügen
Translation
Eine Translation bedeutet eine Verschiebung des Objekts um den gegebenen Vektor. Mit dem Befehl "translate" wird einem Objekt eine Translationsanimation hinzugefügt.
#Syntax: [z-richtung:float] [y-richtung:float] [x-richtung:float] [animationsstart:long] [animationsdauer:long] [Objekt:object3D] translate # #Beispiel: # #erstellt ein neues globales 3D-Objekt mySphere1 Sphere3D g_obj # #fügt mySphere1 eine Translationzum punkt 1.0, 1.0, 1.0 hinzu. #Animationsdauer: 10000 ms, Startzeitpunkt: 5000 ms 1.0 1.0 1.0 10000 5000 mySphere1 translate
Rotation
Eine Rotation bedeutet eine Drehung des Objekts um eine gegebene rotationsachse. Diese achse wird durch zwei vektoren festgelegt. der erste Vektor gibt einen punkt auf der Rotationsachse an, der Zweite eine Richtung. Damit ist die Achse vektoriell hinreichend bestimmt. Die Rotation erfolgt dann um den gegebenen Winkel.
Mit dem Befehl "rotate" wird einem Objekt eine Rotationsanimation hinzugefügt.
#Syntax:
[Vx:float] [Vy:float] [Vz:float] _
[x-richtung:float] [y-richtung:float] _
[z-richtung:float] [Winkel:float] [animationsdauer:long] _
[animationsstart:long] [Objekt:object3D]
rotate
#Beispiel:
#erstellt ein neues globales 3D-Objekt
mySphere1 Sphere3D g_obj
#fügt mySphere1 eine Rotation um die Achse {(1.0, 1.0, 1.0),(1.5, 0.0, 0.0)}
um 180 Grad(3.14 im Bogenmaß) hinzu.
#Animationsdauer: 10000 ms, Startzeitpunkt: 5000 ms
1.0 1.0 1.0 1.5 0.0 0.0 3.14 10000 5000 mySphere1 rotate
Skalierung
Eine Skalierung bedeutet eine Verzerrung des Objekts um den gegebenen Vektor. Mit dem Befehl "scale" wird einem Objekt eine Skalierungsanimation hinzugefügt.
#Syntax: [x-skalierung:float] [y-skalierung:float] [z-skalierung:float] [animationsdauer:long] [animationsstart:long] [Objekt:object3D] scale #Beispiel: #erstellt ein neues globales 3D-Objekt mySphere1 Sphere3D g_obj #fügt mySphere1 eine Skalierung um den Vektor 1.0, 1.0, 1.0 hinzu. #Animationsdauer: 10000 ms, Startzeitpunkt: 5000 ms 1.0 1.0 1.0 10000 5000 mySphere1 rotate
Allgemeine Transformation
Eine allgemeine Transformation wird mittels einer 3x3-Matrix angegeben. Diese Transformation ist sehr allgemein und ermöglicht komplexe Transformationen. Mit dem Befehl "scale" wird einem Objekt eine Transformationsanimation hinzugefügt.
#Syntax: [Matrix3f] [animationsdauer:long] [animationsstart:long] [Objekt:object3D] scale #Beispiel: #erstellt ein neues globales 3D-Objekt mySphere1 Sphere3D g_obj #fügt mySphere1 eine Transformationsmatrix hinzu Animationsdauer: 10000 ms, Startzeitpunkt: 5000 ms "9.0 8.0 7.0 6.0 5.0 4.0 3.0 2.0 1.0" matrix1 code matrix1 10000 5000 mySphere1 rotate
Kamera/Viewport
Die Kamera kann in SPE3D wie ein allgemeines 3D-Objekt verwendet werden. Hinsichtlich der Transformationen ist zu beachten dass die Kamera nicht wirklich eine solche ist, sondern dass alle Veränderungen der Ansicht daher kommen, dass der gesamte Raum manipuliert wird. Eine Translation der Kamera in positiver X-Richtung wirkt für den Betrachter daher wie eine Verschiebung der kamera in negativer X-Richtung.