MAL-Homepage
Inhaltsverzeichnis
MAL Dokumentation: Erweiterung
1. EINLEITUNG
Eine Besonderheit der Sprache MAL ist ihre Erweiterbarkeit.
Neben der Möglichkeit, den Befehlssatz durch Doppelpunktdefinitionen
zu erweitern, ist auch die direkte Einbindung neuer Worte in den
MAL-Interpreter auf C++-Ebene unterstützt. In gleicher Weise ist
es dem Anwender ermöglicht, Verbesserungen an bestehenden
MAL-Worten vorzunehmen.
Der Code zu neuen MAL-Worten kann entweder in bestehenden
oder neu angelegten Source-Dateien untergebracht werden. Auf
C++-Ebene exitstiert zu jedem MAL-Wort eine Prozedur, sowie
ein speziell aufgebauter Kommentarblock, der nebst einer
Kurzbeschreibung des Wortes auch die Zuordnung des Namens auf
MAL-Ebene zum Prozedurnamen auf C++-Ebene enthält. Ein
Generatorprogramm (malgen) durchsucht alle
Source-Dateien und bringt die Befehlslisten (Dateien ´liste.voc´, ´proc.voc´)
des MAL-Hauptprogramms (Datei ´mal.C´) auf den neuesten Stand.
Um ein neu implementiertes Wort auf MAL-Ebene aufrufbar zu
machen, sind also folgende Schritte erforderlich:
MAL-Homepage Inhaltsverzeichnis
2. COMPILIEREN UND LINKEN 2.1. Windows Betriebssysteme
MAL-Homepage
Inhaltsverzeichnis
Unter WIN98, WIN2000 oder WIN-NT wurde MAL mit dem Borland
C++-Builder (Version 5.0, deutsch) compiliert. Wenn Sie den Compiler
installiert habe, genügt es das Projekt 'mal' zu öffnen und mit der
Taste F9 die Compilierung zu starten.
2.2. LINUX und DOS
Die LINUX-Version von MAL wurde unter Redhat 5.0 und Redhat 6.0
compiliert und getestet. Für DOS wurde der GNU-C++-Compiler
verwendet. Der Download für den passenden DOS-Compiler
wurde aus Platzgründen von der MAL-Homepage entfernt.
Die entsprechende Datei (gpp.zip) kann aber auf
Anfrage beim
Autor
bezogen werden.
Expandieren Sie die Datei 'gpp.zip' zum Beispiel in eine
Directory 'c:\gpp'. Sie finden dann dort eine Datei 'autoexec.bat'
mit den Einstellungen, die der Compiler benötigt. Fügen Sie diese
in Ihre 'autoexec.bat'-Datei ein, dann ist der Compiler nach
Neustart des System installiert.
Das Compilieren des kompletten MAL-Interpreters geschieht
durch Aufruf des Batch-Jobs cmal. Nach fehlerfreier Übersetzung
kann mit dem Batch-Job lmal der Interpreter gelinkt werden.
Die beiden Batch-Dateien sind je nach Betriebssystem unterschiedlich
aufgebaut. Bei UNIX-Betriebssystemen ist die Liste der zu
kompilierenden und einzubindenden Dateien jeweils direkt in
cmal bzw. lmal angeführt. Unter MS-DOS
existieren die Hilfsdateien mal.src mit der Liste der
Source-Dateien und mal.lnk mit der Liste der einzubindenen Object-Files.
Hat man aber nur an einer oder an einigen wenigen Dateien
Änderungen vorgenommen, so genügt es, diese mit der Option
´-c´ zu Compilieren und anschließend lmal aufzurufen:
Beispiel (für DOS oder LINUX):
gcc -c mathe.C lmal
MAL-Homepage Inhaltsverzeichnis
3. DER GENERATOR MALGEN
Damit der Interpreter eine in C++ implementierte Prozedur als MAL-Wort
erkennt, muß ein entsprechender Verweis in das Vokabular eingetragen
werden. Dies geschieht in der Initialisierungsphase des Interpreters.
Der Code für die Initialisierung des Vokabulars befindet sich in der
Datei liste.voc, die vom Generator ´malgen´ erzeugt wird
und mit #include in der Sourcedatei
mal.C eingebunden ist. Außerdem werden die Deklarationszeilen
der Prozeduren für die Compilation benötigt. Diese befinden sich in der
Datei proc.voc, die ebenfalls von malgen erzeugt wird.
Für die Aktualisierung der Help-Datenbank wird von malgen
darüber hinaus noch eine Datei help.mal erzeugt. Sie
wird bei der Ausführung des Wortes
update_help
(Folder: admin) interpretiert.
Das Programm malgen bearbeitet alle Dateien in der
Arbeitsdirectory, deren Name die Zeichenfolge ´.c´, ´.C´, ´.h´
oder ´.cpu´ enthält. In diesen Dateien wird nach Zeilen mit der
Zeichenfolge /*... am Zeilenanfang
gesucht. Diese gelten als Einleitung für den Kommentarblock eines
MAL-Wortes auf C++ Ebene.
Beschreibung (Kommentarblock) eines MAL-Wortes:
/*.................................................. MAL-Name C++-Name Topic Autor Eine Zeile Kurzbeschreibung. Langbeschreibung (Syntax, Legende, Beispiele...) ..................................................*/Die Zeichenfolge /*..... am Zeilenanfang kennzeichnet den Anfang und .... das Ende des Kommentarblocks, der im Prinzip an beliebiger Stelle, vorzugsweise aber vor der Definition der entsprechenden C-Routine im Sourcecode steht.
vokabular[wortanz++]=new word_class("breakpoint",breakpoint); vokabular[wortanz++]=new word_class("make",make); vokabular[wortanz++]=new word_class("(",kommentar); vokabular[wortanz++]=new word_class("test_key_codes",test_key_codes); vokabular[wortanz++]=new word_class("amenue",amenue); vokabular[wortanz++]=new word_class("leftbound_menu",leftbound_menu); vokabular[wortanz++]=new word_class("writemask",writemask);Auszug aus ´proc.voc´:
extern void breakpoint(); extern void make(); extern void kommentar(); extern void test_key_codes(); extern void amenue(); extern void leftbound_menu(); extern void writemask();Beispiel (Code eines einfachen Wortes auf C++-Ebene):
/*................................................. testword testprocedure test Miller A. Ausgabe eines Textes am Bildschirm. -> Es wird der Text ´Das ist ein Testwort´ ausgegeben. ..................................................*/ void testprocedure() { printf("Das ist ein Testwort\n"); }Wird der obige Code zum Beispiel in die Datei ´worte.C´ editiert, so kann das Wort testwort folgendermaßen in das Vokabular aufgenommen werden:
malgen gcc -c mal.C worte.C lmal
MAL-Homepage Inhaltsverzeichnis
4. STACKDATEN LESEN
Detailbeschreibungen für die hier angeführten Prozeduren befinden sich im
Kapitel Internes.
Jedes Wort, das Eingangsdaten vom Stack erwarted sollte zu Beginn die Funktion
need(int) aufrufen. Sie prüft, ob
überhaupt genug Einträge am Stack vorhanden sind und übergibt im Fehlerfall
den Funktionswert 1 (sonst 0).
Skalare können mit der Funktion pull()
vom Stack gelesen werden. Der Stackpointer wird dabei dekrementiert. Analog
existieren für das Holen von Strings die Prozeduren
spullstring(sstring*) (Für kurze Strings,
Typ ´sstring´) oder lpullstring(lstring*) (für lange Strings, Typ ´lstring´).
Mit der Funktion indizieren(int stackptr,int index)
kann eine Komponente aus einem Verbund gelesen werden. Der Verbund
wird dabei aber nicht vom Stack gelöscht. Der Parameter
stackptr ermöglicht es auch,
von Verbunden, die nicht am Top of Stack sind, zu indizieren.
In der Regel sollte man nach jedem Aufruf von indizieren()
die Prozedur codeausfuehren() aufrufen.
Sie bewerkstelligt die automatische Ausführung von Codevariablen und das
automatische Dereferenzieren von Poolverweisen. Selbstverständlich gibt es
auch Fälle, in denen der Aufruf von codeausfuehren()
nicht angebracht ist (z. B. im Wort
>voc).
maxindex(int sp) oder
maxindex() kann verwendet werden,
um die Anzahl der Komponenten in einem Verbund zu bestimmen.
Für die Bearbeitung von Realverbunden ist die Verwendung der
Klasse vectortyp hilfreich. Ein Realverbund kann mit der
Funktion makerealvector() auf ein
Objekt vom Typ vectortyp umgewandelt werden.
vectortyp stellt den
Operator [ ] zum Schreiben und Lesen
der Komponenten zur Verfügung. Der Index läuft dabei, wie in MAL üblich, ab 1.
Beispiel:
void liste_realverbund() { if (need(1)) return; keytyp anzahl=maxindex(); vectortyp liste=makerealvector(); for (keytyp i=1;i<=anzahl;i++) printf("%g\n",liste[i]); }Um dieses Beispiel als reguläres MAL-Wort zu verwenden, müßten noch entsprechende Fehlerabfragen eingebaut werden. Sowohl in maxindex() als auch in makerealvector() können Fehler auftreten, wenn kein Realverbund am Stack übergeben wird.
MAL-Homepage Inhaltsverzeichnis
5. STACKDATEN SCHREIBEN
Mit den Prozeduren push(real)
oder pushstring(char *) können Zahlen
oder Strings am Stack gelegt werden. Verbunde können mit den Prozeduren
verbundanfang() und
verbundende() erzeugt werden.
Sie sind funktionsgleich mit den Worten
[ und ].
Die Prozeduren drop(),
swap() oder dup()
können genauso wie die gleichnamigen MAL-Worte angewandt werden.
Mit der Prozedur create_realvector(keytyp anzahl)
kann ein Objekt der Klasse vectortyp erzeugt
werden. Die Komponenten dieses Objekts können dann unter
Zuhilfename des Operators [ ] geladen werden.
Beispiel:
void ramp() { if (need(1)) return; keytyp anzahl=(keytyp)pull(); vectortyp rampe=create_realvector(anzahl); for(kreytyp i=0;i<anz;i++) rampe[i+1]=i; }
MAL-Homepage Inhaltsverzeichnis
6. FEHLERMELDUNGEN
Bei Laufzeitfehlern ist die Prozedur fmeld(char *)
für die Ausgabe des Fehlermeldungstextes aufzurufen. Sie setzt die globale Variable
fehler auf 1, was die Ausgabe weiterer Fehlermeldungen von
Folgefehlern unterdrückt. Außerdem wird nach dem Aufruf von
fmeld() die weiter Interpretation
unterbrochen und in den Debugging-Modus gewechselt.
Der Aufruf von fmeld() verhindert aber nicht,
daß das gerade bearbeitete Wort zu Ende bearbeitet wird. Man muß daher
gegebenanfalls die Variable fehler abfragen und, falls sie gesetzt ist,
die Prozedur mit return verlassen. In
diesem Zusammenhang ist zu beachten, daß manche Prozeduren intern
fmeld() aufrufen. So sollte zum Beispiel
nach dem Aufruf folgender Prozeduren
if (fehler) return;zu finden sein um gröbere Programmfehler abzufangen.
MAL-Homepage Inhaltsverzeichnis