MAL-Homepage Inhaltsverzeichnis



MAL Dokumentation: Internes




Inhalt:

1. ALLGEMEINES
2. FEHLERMELDUNGEN
3. BEDARF VON STACKEINTRÄGEN
4. DER STACK
5. STACKEINTRÄGE
6. VERBUNDE
6.1. Indizieren
6.2. Komponentenanzahl
6.3. Realverbunde
7. EINRICHTEN NEUER STACKEINTRÄGE
8. DAS HEADERFILE MAL.H
9. BEHANDLUNG VON NAMEN
10. CODESTRINGS
11. STANDARDAUSGABE
12. DIE KLASSE SOURCETYP
13. INTERPRETATION
14. DER INTERPRETER
15. ZAHLENUMWANDLUNG
16. SYSTEMVARIABLE
17. FUNKTIONEN UND OPERATOREN
18. VEKTOREN UND MATRIZEN


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:
Der Speicher wird niemals dichtgeschoben. Es wird mit entsprechendem Speicherausbau und einer endlichen Laufzeit der Programme gerechnet.

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;
Jeder Typ von Stackeintrag hat einen eigenen Kopftyp, der den Datensatz stackkopftyp beinhaltet.

Die Namen für die Kopftypen enden stets mit der Zeichenfolge kopf, die Namen des dazugehörigen Zeigertyps haben die Zeichenfolge stack vorangestellt.

Beispiel String:
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:

Beispiele:
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:

Die Prozeduren
  • void swap()
  • void drop()
  • void dup()
  • void cut()
sind funktionsgleich mit den gleichnamigen MAL-Worten. holen Strings vom Stack, wobei zwischen langen (l=long) und kurzen (s=small) Strings unterschieden wird. Die Längen der Strings sind in der Datei mal.cpu definiert.

Die Funktionen void verbundanfang() und void verbundende() wirken wie die MAL-Worte
[ bzw. ].

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.

Analog existieren zum Lesen von Namen zwei Funktionen, wobei die erstere das Ergebnis am Stack ablegt:
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.

Alle Standardausgaben (wie z.B. beim Wort .) erfolgen mit
fprintf(sout, ...)

MAL-Homepage Inhaltsverzeichnis


12. DIE KLASSE SOURCETYP

MAL-Sourcecode kann von verschiedensten Quellen stammen:

Als gemeinsame Nahtstelle dient eine Variable *source vom Typ source_class oder von dieser Klasse abgeleitete, die u.a. folgende Funktionen zur Verfügung stellt:
  • char byte();
  • void nextbyte();
  • int ende();
byte() enthält den zuletzt gelesenen Buchstaben, nextbyte() schaltet um einen Buchstaben weiter und ende() übergibt 1, wenn das Ende des Source erreicht ist.

MAL-Homepage Inhaltsverzeichnis


13. INTERPRETATION

Die Prozedur wortholen() holt Zeichen aus der Variablen *source bis zum nächsten Trennzeichen. Das isolierte Wort (oder Zahl) wird in der globalen Variablen wort abgespeichert.

Ist in wort keine Zahl, so wird mit wort_suchen() das Vokabular durchsucht. wort_suchen() übergibt als Returnwert die Nummer des gefundenen Wortes im Vokabular. Übergibt es einen Returnwert >=0, so wurde das Wort gefunden und die Datenadresse in der globalen Variablen datenadresse hinterlegt. Das Ergebnis von wort_suchen() wird normalerweise in der globalen Variable wortnr zwischengespeichert.

Mit vokabular[wortnr].procedure() wird der Code ausgeführt. Dieser kann mit *datenadresse seine Daten adressieren (soferne er welche hat).

Beispiel das Wort
if:
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:

Die davon abgeleitete Klasse doppelp_interpreter_class wird beim Erzeugen einer neuen Doppelpunktdefinition benötigt, um die Länge des Codes zu bestimmen. Sie führt also nur einen Syntaxcheck des Codes durch und sucht das Ende der Doppelpunktdefinition.

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);


void funktionen(funktionentyp funktion) { if (need(1)) return; . . }
Beispiel: Sinusfunktion
void sinus()
{ funktionen(sin); 
}
Damit kann sin auf verschiedenste Datentypen angewandt werden.

Analog für Operatoren:
typedef real (*operatortyp)(real,real);


void operatoren(operatortyp op) { if (need(2)) return; stackinfotyp t1=info(stackptr-1); stackinfotyp t2=info(stackptr); . . }


Beispiel: Multiplikation
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).

Die Funktion size() berechnet den Betrag des Vektors, print() und print(..) geben den Vektor am Bildschirm aus und push() dient zum Abspeichern des Vektors (der Matrix) am MAL-Stack.

MAL-Homepage Inhaltsverzeichnis