MAL-Homepage
Inhaltsverzeichnis
MAL Dokumentation: Internes
1. ALLGEMEINES
Hier werden die Prozeduren und Variablen aus dem C++ Sourcecode
des MAL-Interpreters beschrieben. Das Einbinden neuer Worte
in den MAL-Kernel und das Compilieren des Interpreters ist
im Kapitel Erweiterung beschrieben.
Den Sourcecode kann man im
Folder source_folder (im Public-Pool) finden. Er kann am einfachsten
durch einen Aufruf des Wortes source
geöffnet werden. Alle hier beschriebenen Funktionen und
und Variablen sind in der Datei mal.h deklariert.
Der Interpreter verwendet als Datentyp für Gleitkommazahlen einen
Datentyp real, der in der Datei mal.cpu normalerweise
als ´float´ definiert ist. Will man alle Berechnungen im ganzen
MAL-System auf doppelte Genauigkeit umstellen, so genügt es
daher den Datentyp ´real´ als ´double´ zu definieren. Selbstverständlich
geht das auf Kosten des Speicherplatzes.
Der Datentyp keytyp ist ein 32-Bit vorzeichenloser
Integer-Typ, also entweder als ´unsigned int´ oder ´unsigned long´
definiert.
Bei der Umstellung der in ´mal.cpu´ definierten Datentypen muß man
bedenken, daß die in den Pools gespeicherten Daten in der Folge nicht
mehr lesbar sind. Daher müssen vor der Typumstellung alle Pools auf
Metaformat und nach der Umstellung wieder zurück konvertiert werden.
MAL-Homepage Inhaltsverzeichnis
2. FEHLERMELDUNGEN
Für die Ausgabe von Fehlermeldungen exisiert die Prozedur
fmeld(). Sie setzt die globale
Variable fehler auf 1 und gibt
die Meldung aus.
Beispiel:
void lesen_von_datei() { datei=fopen("xyz","r"); if (!datei) fmeld("Fehler beim Dateizugriff"); if (fehler) return; . . . }Solange die Variable fehler auf 1 gesetzt ist (erst wenn der Interpretier wieder in den Bedienmodus geht, wird sie auf Null gesetzt), wird die Ausgabe weiterer Fehlermeldungen unterdrückt.
MAL-Homepage Inhaltsverzeichnis
3. BEDARF VON STACKEINTRÄGEN
Zur Überprüfung, ob für ein Wort die nötige Anzahl von
Stackeinträgen vorhanden ist, existiert die Funktion
need(). Sie ruft im
Fehlerfall gleich intern fmeld() auf.
Beispiel:
void multiplizieren() { if (need(2)) return; real x=pull(); real y=pull(); push(x*y); }Jede Routine, die Stackeinträge benötigt, sollte need() am Anfang aufrufen!
MAL-Homepage Inhaltsverzeichnis
4. DER STACK
Der Stack ist ein Array vom Typ void*
(Variablenname ustack), deren
Komponenten auf die eigentlichen Stackdaten verweisen. Die Maximalanzahl
von Stackeinträgen ist mit der Konstante maxstack
definiert. Die zentrale Funktion zum Einrichten neuer Stackeinträge
(create()) überprüft diese.
In der Variablen stackptr
befindet sich der Stackpointer. Er zeigt stets auf den Top of Stack.
Die Adresse des Top of Stack Datensatzes erhält man demnach
mit ustack[stackptr].
Beispiel: Wort drop
void drop() { delete ustack[stackptr--]; }Hinweis:
MAL-Homepage Inhaltsverzeichnis
5. STACKEINTRÄGE
Die Byteanzahlen für die diversen Datentypen sind maschinen- bzw.
compilerabhängig, daher wird hier nicht näher auf die tatsächlichen
Aufteilungen im Speicher, sondern auf den prinzipiellen Aufbau eingegangen.
Alle Stackeinträge haben einen einheitlichen Kopf:
typedef struct { keytyp laenge; keytyp namptr; stackinfotyp info; } stackkopftyp;
typedef struct { stackkopftyp header; char wert[1];} stringkopf; typedef stringkopf *stackstring;Der Datentyp stackstring ist also ein Pointer auf einen MAL-String.
MAL-Homepage Inhaltsverzeichnis
6. VERBUNDE
Es gibt zwei Arten von Verbunden: homogene und inhomogene.
Homogene Verbunde sind Verbunde, in denen ausschließlich eine
Art von Stackeinträgen ist (alle Einträge ohne Name). Inhomogene Vergunde sind alle sonstigen.
Homogene Verbunde haben eine andere Adressierungsstruktur als inhomogene.
Die für
alle Komponenten gemeinsame Information (der Komponentenkopf) wird
nur einmal (im Verbundkopf) gespeichert. Damit benötigen homogene
Verbunde weniger Speicherplatz.
Außerdem kann direkt adressiert werden, da alle Komponenten gleich lang sind.
Für Realverbunde besteht daher die Möglichkeit, eine Klasse
vectortyp zuzuordnen, mit der die
Zugriffe zu Komponenten vereinfacht wird.
Beispiel:
vectortyp feld=makerealvector(stackptr); printf("dritter Eintrag von feld=%g\n",feld[3]);Die Indizes bei vectortyp laufen wie in MAL beginnend mit 1.
MAL-Homepage Inhaltsverzeichnis
6.1. Indizieren
Indiziert wird mit der Prozedur indizieren().
int indizieren(int sp,keytyp index);Der Verbund aus dem indiziert wird bleibt unverändert am Stack (nicht wie beim Wort ,). Es wird auch nicht automatisch dereferenziert (muß im Bedarfsfall mit codeausfuehren() explizit durchgeführt werden).
MAL-Homepage Inhaltsverzeichnis
6.2. Komponentenanzahl
Für die Bestimmung der Komponentenanzahl eines Verbunds
oder der Zeichenanzahl eines Strings verwendet man
maxindex().
keytyp maxindex(int sp=stackptr);
MAL-Homepage Inhaltsverzeichnis
6.3. Realverbunde
Für Realverbunde existieren einige komfortable Hilfsroutinen:
void ramp() { keytyp anz=(keytyp)pull(); vectortyp rampe=create_realvector(anz); for (keytyp i=0;i<anz;i++) rampe[i+1]=i; }
void sinus() { vectortyp feld=makerealvector(); for (keytyp i=1;i<=maxindex();i++) feld[i]=sin(feld[i]); }
MAL-Homepage Inhaltsverzeichnis
7. EINRICHTEN NEUER STACKEINTRÄGE
Alle Stackeinträge müssen mit der Prozedur create()
eingerichtet werden. Sie inkrementiert den Stackpointer, reserviert den
Speicherbereich für den Datensatz (mit new)
und überprüft die notwendigen Voraussetzungen.
void* create(keytyp laenge,stackinfotyp info);Als Ergebnis wird ein Pointer auf den eingerichteten Datensatz übergeben (dieser ist mit ustack[stackptr] ident).
MAL-Homepage Inhaltsverzeichnis
8. DAS HEADERFILE MAL.H
Die Include-Datei mal.h wird in allen Moduldateien verwendet.
Sie enthält die Typdefinitionen für die Stackeinträge, die Klasse
vectortyp und Deklarationen für
globale Varable (z.B fehler) und Prozeduren.
Viele der Prozeduren und Variablen befinden sich in der Datei mal.C
(bzw mal.cpp unter Windows).
Prozeduren, die Informationen über Stackeinträge holen, sind meist so
definiert, daß sie auf beliebige Stackeinträge (nicht nur den Top of Stack)
angewandt werden können. Gibt man jedoch keinen Stackpointer an, so
gilt als Vorbesetzung stackptr, also der Top of Stack.
Informationen über Stackeinträge:
MAL-Homepage Inhaltsverzeichnis
9. BEHANDLUNG VON NAMEN
Es existieren zwei Prozeduren zum Setzen von Namen:
void setname()und
void setname(char *name)Erstere verlang den Namen als String am Stack.
int getname()und
int getname(char *name)Als Returnwert wird 0 übergeben, wenn kein Name existiert.
MAL-Homepage Inhaltsverzeichnis
10. CODESTRINGS
Die Umwandlung eines Strings am Top of Stack in einen
Codestring erfolgt mit der Prozedur
setcode(),
sie wirkt wie das Wort >code.
Die Prozedur codeausfuehren()
übernimmt die komplette Dereferenzierung (inklusive Poolverweise und
Doppelpunktdefinitionen) des Top of Stack.
MAL-Homepage Inhaltsverzeichnis
11. STANDARDAUSGABE
Hinweise und Fehlermeldungen, Bedienaufforderungen und dergleichen,
also Ausgaben, die nicht mit dem Wort output
umgeleitet werden sollen, können einfach mit
printf( ... )ausgegeben werden.
fprintf(sout, ...)
MAL-Homepage Inhaltsverzeichnis
12. DIE KLASSE SOURCETYP
MAL-Sourcecode kann von verschiedensten Quellen stammen:
MAL-Homepage Inhaltsverzeichnis
13. INTERPRETATION
void if_anweisung() { real x=pull(); if (fehler) return; wortholen(wort); wortnr=wort_suchen(wort); if (wortnr<0) {fmeld("?");return;}; if (x>0) { datenadresse=vokabular[wortnr].daten; vokabular[wortnr].procedure(); } }
MAL-Homepage Inhaltsverzeichnis
14. DER INTERPRETER
Der Interpreter wird für verschiedenste Zwecke benötigt, nicht nur
für die normale Interpretation während der Laufzeit. Zum Beispiel
muß vor dem Abspeichern einer Doppelpunktdefinition deren letzter
Strichpunkt bestimmt werden. Zu diesem Zweck muß ihr Code ohne
Ausführung interpretiert werden. Auch bei der Komprimierfunktion
im MAL-Editor ist ein Interpreter nötig.
Damit der Code für alle jene Interpreter ident und leicht zu warten ist,
wird für alle diese Zwecke die Klasse
interpreter_class (in der Datei interp.cpp)
verwendet. Sie hat zwei Public-Members:
MAL-Homepage Inhaltsverzeichnis
15. ZAHLENUMWANDLUNG
Für die Zahlenkonvertierung bei Eingabe existiert die Prozedur
realzahl().
int realzahl(char *wort,real &zahl);Der Returnwert ist 0, wenn wort keine als Zahl interpretierbare Zeichenfolge enthält. Zur Unterscheidung von Zahlen und Worten dient die Funktion isword(). Sie übergibt 1 falls der übergebende String ein Wort ist, andernfalls 0.
int isword(char *wort)Die Zahlenkonvertierung für die Ausgabe von Realzahlen kann man mit dem Standard C-Befehl sprintf(..) vornehmen. Der Formatstring kann mit get_format() ermittelt werden. Da innerhalb eines Wortes keine Formatumschaltungen möglich sind (außer, wenn Codes ausgeführt werden), genügt es get_format() einmal aufzurufen und das Ergebnis zu merken.
void get_format(char *format);
MAL-Homepage Inhaltsverzeichnis
16. SYSTEMVARIABLE
Für Systemvariable mit Real-Werten existieren die Funktionen
get_sysvar() und set_sysvar().
set_sysvar() richtet die Variable ein,
wenn sie noch nicht lokal definiert ist.
Die Vorbesetzung preset wird verwendet,
wenn die Variable nicht existiert oder keine Realzahl enthält.
MAL-Homepage Inhaltsverzeichnis
17. FUNKTIONEN UND OPERATOREN
Weil mathematische Funktionen und Operatoren für verschiedenste
Datentypen verwendet werden können, sind die Adressierungsstrukturen
vorprogrammiert. Für Funktionen existiert eine Prozedur
funktionen(), die mit der
entsprechenden mathematischen Funktion versorgt werden muß.
typedef double (*funktionentyp)(double);Beispiel: Sinusfunktion
void funktionen(funktionentyp funktion) { if (need(1)) return; . . }
void sinus() { funktionen(sin); }Damit kann sin auf verschiedenste Datentypen angewandt werden.
typedef real (*operatortyp)(real,real);
void operatoren(operatortyp op) { if (need(2)) return; stackinfotyp t1=info(stackptr-1); stackinfotyp t2=info(stackptr); . . }
real multiplizieren(real op1,real op2) { return op1*op2; }
void multiplikation() { operatoren(multiplizieren); }
MAL-Homepage Inhaltsverzeichnis
18. VEKTOREN UND MATRIZEN
In der Datei vector.C sind die Klassen
definiert. Die entsprechenden Deklarationen befinden sich in der Datei vector.h.
Die Klassen double_vector und
double_matrix sind für Vektoren
bzw. Matrizen mit frei wählbarer Komponentenanzahl, wobei die
Komponenten vom Typ double sind.
Es folgt ein Ausschnitt aus den Deklarationen für die Klasse
double_vector. Die Funktionen
können weitgehend auch bei allen anderen Klassen (oder sinngemäß)
gefunden werden. Die Matrixklassen bieten darüber hinaus noch komplexere
Berechungen, wie zum Beispiel Eigenwerte, Eigenvektoren oder Lineare
Gleichungssysteme (letzteres kann auch überbestimmt sein).
double_vector(keytyp dimension); double_vector(double_vector &a); ~double_vector() { delete data; } void zero(); double& operator[](int i); void operator=(double_vector right); void operator=(vector3d right); void operator=(vector2d right); friend double_vector operator+(double_vector a,double_vector b); friend double_vector operator-(double_vector b,double_vector b); void operator+=(double_vector b); void operator-=(double_vector b); friend double_vector operator*(double_vector a,double skalar); void operator*=(double skalar); double size(); void normalize(); void print(); void print(char *title); void push();Die Funktion s(...,...) berechnet das Skalarprodukt von zwei Vektoren und der Operator | das Spatprodukt (existiert nur für vector3d).
MAL-Homepage Inhaltsverzeichnis