>binaervarianz >projects >Modula-3
1.
Einleitung warum, weshalb und wieso =?

2.
Variablen Standardtypen

3.
Konstanten genau das

4.
Typen eigene Variablen - Typen

5.
Records zusammengesetzte Variablen

6.
Zeiger dynamisches Arbeiten mit Variablen

7.
Schleifen REPEAT, FOR, WHILE ect.
8.
Bedingungen IF-THEN, CASE ect.

9.
Prozeduren mit, ohne Paramter und oder Rückgabewert

10.
Module eigene Bibliotheken, Auslagerung von Funktionen

11.
Objekte ein Versuch =/

12.
Threads
sowas geht immerhin auch =)

13.
Ausnahmen Exceptions - fangen und werfen =)

14.
Befehls-Referenz Übersicht über Funktionen der einzelnen Bibliotheken (Module)

15.
Download
alle Beispiele gepackt zum download

16.
Links
Ein paar nützliche Adressen =)

kurze Einleitung


Im folgenden werde ich häufig die allgemeine Syntax von Befehlen angeben. Dabei ist zu beachten, das in [ ] geschriebenes nicht zwingend notwendig, sondern möglich ist (achten sie bitte auf die Semikolons). Programmcode ist in dem Format geschrieben :
IO.Put("Hello World\n");
Ein Kommentar steht in Modula in Klammern mit Sternchen (* Anfang und Ende *) und kann auch über mehrere Zeilen gehen. Ansonsten gilt wie in Pascal (und C++) auch : Anweisungen schließt man mit einem Semikolon ; ab (zumindest meistens).

Prinzip von Modula 3


Um ein Programm in Modula 3 zu schreiben benötigt man, abgesehen von einem Compiler, zusätzlich zum Source-Code auch ein Makefile.
Der Source-Code steht in einer Datei mit der Endung .m3, das Makefile heißt m3makefile (Gross- und Kleinschreibung beachten!) und alles zusammen steht in einem Verzeichnis namens src !

 Das Hauptprogramm 

Das ist der Teil, in den ihr euren Code schreibt (zumindest vorerst - das Auslagern in Module erkläre ich etwas später im Text ). Der Code steht zwischen dem BEGIN und END, Typdefinitionen (TYPE) sowie Variablendeklarationen (VAR, CONST) grundsätzlich vor dem BEGIN. Wichtig ist in diesem Beispiel, das die Datei auch Main.m3 heißt. Achtung : Groß- und Kleinschreibung spielt eine Rolle! Das Hauptprogramm sieht strukturmäßig so aus :


"Main.m3"

MODULE Main; 
IMPORT IO;
BEGIN
END Main.

 Das Makefile 

In diesem Teil werden für den Compiler die Module, Interfaces und Dateien mit den Implementationen festgelegt. Wichtig ist auch hier neben dem Namen der Datei und der Groß/Kleinschreibung die Bezeichnung des Programms, welche mit Obigem übereinstimmen muss!


"m3makefile"
import("libm3")
implementation("Main")
program("Main")

Diesmal bitte keine Kommas nach den Anweisungen schreiben! Die Groß- und Kleinschreibung der Schlüsselwörter import, implementation und program ist hier egal.

Compiliert wird das Ganze durch den Aufruf von m3build in dem Verzeichnis, in welchem Main.m3 und das m3makefile liegt! Um das Programm jetzt zu starten wechseln sie in das darüberliegende Verzeichnis LINUXLIBC6 und geben ./Main ein. Für UNIX-Benutzer gibt es das Programm m3run, unter Windows habe ich keine Ahnung, aber es wird sicherlich auch irgendwo ein Binary mit dem gleichen Namen erzeugt =).

Möchte man einen anderen Namen für sein Programm verwenden als Main.m3, wird es etwas komplizierter ;-) - das ganze sieht dann so aus :
 
 Das Hauptprogramm 


"Name.m3"


MODULE Name EXPORTS Main; 
IMPORT IO;

BEGIN

END Name.


 Das Makefile 


m3makefile


import("libm3")
implementation("Name")
program("Main")


Das Binary steht im gleichen Verzeichnis (LINUXLIBC6) und heisst immernoch Main, da nach EXPORTS im Hauptprogramm und im makefile als program("") Main angegeben wurde =). Das Import kann man auch weglassen, wenn man keine Bibliothek braucht, was aber eher selten vorkommt ;).

Variablen


Variablen sind eines der Grundbestandteile von Programmiersprachen. Sie können Werte speichern, auf die man bliebig oft im Laufe des Programmes zugreifen und manipulieren kann. Dabei unterscheidet Variablen nachdem was sie Speichern können, ob nur Zahlen (ganze oder mit Komma), Zeichen, Text oder gar nur 1 oder 0 (wahr oder falsch). Entsprechend ihrem Typ kann man unterschiedliche Operationen auf sie anwenden. Zahlen kann man Addieren oder Subtrahieren, Texte kann man zerstückeln oder zusammenfügen. Auch der unterschiedliche Anspruch an Speicherplatz unterscheidet sie voneinander, aber das soll uns vorerst nicht weiter interessieren.

Wichtig ist, das Variablen prinzipiell einen Namen besitzen, über den man auf den Wert zugreifen kann. Man deklariert sie wiefolgt :
VAR variablenname : typ;
Der Name kann nahezu beliebig gewählt werden. Verboten sind Sonderzeichen (ausser "_") und Leerzeichen, sowie Zahlen am Anfang des Namens.
VAR  
name : typ; (* OK *) geht_auch : typ; (* OK *) name2 : typ; (* OK *)
12hallo : typ; (* Falsch! - Zahl am Anfang *) _name : typ; (* Falsch! - Sonderzeichen am Anfang *) hallo du : typ; (* Falsch! - Leerzeichen nicht erlaubt *) name! : typ; (* Falsch! - unerlaubtes Sonderzeichen *)
(* Zuweisung von Werten *)
name := wert;
Wenn man zwei Variablen vergleichen will, dann muss man beachten, dass beide Variablen vom gleichen Typ sind.
Es gibt folgende Standarttypen :

Typ
Beschreibung
CARDINAL
natürliche Zahlen

INTEGER
ganze Zahlen
REAL
Gleitkommazahlen (Zahl mit Komma)
LONGREAL
Gleitkommazahlen (Zahl mit Komma)
EXTENDED
Gleitkommazahlen (Zahl mit Komma)
CHAR
ein Zeichen
TEXT
Zeichenkette
BOOLEAN
Wahrheitswert(TRUE, FALSE)

Konstanten


Konstanten sind im Prinzip nicht anderes als Platzhalter für irgendwelche Werte. Es gibt 2 Arten von Konstanten, die in Modula jedoch nicht unterschieden werden. Erwähnt sei nur das Konstanten viel Schreib- und Wartungsarbeit sparen können, wenn man sie richtig und konsequent einsetzt.
Konstanten definiert man mit dem Schlüsselwort CONST
CONST
const_int = 2;
const_real = 2.0;

const_text = "Hallo";
Der Name der Konstanten ist beliebig, solange er den Regeln für Variablennamen entspricht.
Manchmal ist es erforderlich den Typ der Variablen explizit mit anzugeben. Das ist im Prinzip ganz einfach und funktioniert genauso wie bei den Variablen :
CONST
const_int : INTEGER = 2;
const_real : REAL = 2.0;
const_ext : LONGREAL = 2.0; (* ist etwas anderes als const_real! *)

const_text : TEXT = "Hallo";
Beispiele : const.m3

Variablentypen und Umwandlungen



CARDINAL, INTEGER

CARDINAL und INTEGER Zahlen sind die beiden ganzzahligen Typen in Modula 3 - etwas anderes gibt es nicht. Der einzige Unterschied liegt im Wertebereich, ansonsten funktionieren sie nahezu identisch. ABER : es sind unterschiedliche Typen!

Typ
FIRST()
 LAST()

CARDINAL
0
2.147.483.647

INTEGER
-2.147.483.648
2.147.483.647

VAR  
Variable : CARDINAL; (* Deklaration *)
Variable2 : CARDINAL := 5; (* Deklaration mit default wert *)
VAR Variable : INTEGER; (* Deklaration *) Variable2 : INTEGER := 5; (* Deklaration mit default Wert *)
(* Ein- und Ausgabe *)  
variable := IO.GetInt(); (* einlesen; Modul IO *)
IO.PutInt(variable; (* ausgaben; Modul IO *)
beachten sie dabei, das Fehler nicht abgefangen werden! Wenn man eine negative Zahl, einen Buchstaben oder eine Zahl ausserhalb des Wertebereiches eingibt, bricht das Programm mit einem Laufzeitfehler ab. (siehe Exceptions) Sie werden sie durch einen Hinweis vom Compiler auf dieses  Problem hingewiesen.
Folgende Umwandlungen sind möglich :
  
(* CARDINAL in INTEGER *)
integer := cardinal; (* Modul IO *)
(* CARDINAL in TEXT *) text := Fmt.Int(cardinal); (* Modul Fmt *)
(* INTEGER in CARDINAL *) integer := ABS(cardinal); (* Modul IO *)
(* CARDINAL in TEXT *) text := Fmt.Int(integer); (* Modul Fmt *)
REAL, LONGREAL, EXTENDED

Das sind die Gleitkommatypen in Modula 3, die Unterschiede liegen nur in den Wertebereichen.

Typ
FIRST()
LAST()

REAL
1.17549435E-38
3.40282347E+38

LONGREAL
2.2250738585072014D-308
1.7976931148623157D+308
EXTENDED
2.2250738585072014X-308 1.7976931148623157X+308

Definition wie gewohnt. Für die Ein- und Ausgabe gibt es folgende Prozeduren :
VAR
real : REAL [ := 5.0 ]; (* Deklaration mit default Wert - ACHTUNG! *)
longreal : LONGREAL [ := 5.0 ]; (* bei Gleitkommavariablen muss die Nach- *)
extended : EXTENDED [ := 5.0 ]; (* kommastelle immer mit angegeben werden! *)
(* Eingabe *)
real := IO.GetReal(); (* Modul IO *) longreal := ?; (* mir unbekannt *) extended := ?. (* mir unbekannt *)
(* Ausgabe *)

IO.PutReal(real); (* Modul IO *)
folgende Umwandlungen sind interessant :
(* INTEGER in REAL, LONGREAL, EXTENDED  *)
real := FLOAT(integer); (* Modul IO *)
longreal := FLOAT(integer, LONGREAL); (* Modul IO *)
extended := FLOAT(integer, EXTENDED); (* Modul IO *)
(* REAL in INTEGER, LONGREAL, EXTENDED *) integer := TRUNC(real); (* schneidet die Nachkommastellen ab *) integer := ROUND(real); (* rundet auf die entsprechende ganze Zahl *) integer := FLOOR(real); (* ergibt die kleinere ganze Zahl *) integer := CEILING(real); (* ergibt die grössere ganze Zahl *)
longreal := FLOAT(integer, LONGREAL); extended := FLOAT(integer, EXTENDED);
(* für LONGREAL und EXTENDED sind die Umwandlungen entsprechend *)
CHAR, TEXT

Variablen dieser beiden Typen können neben Zahlen auch Buchstaben beinhalten, wobei CHAR auf ein Zeichen beschränkt ist, TEXT hingegen  eine Zeichenkette enthalten kann. Die maximale Länge einer Zeichenkette ist abgängig vom Compiler, dürfte aber für die meisten Anforderungen ausreichen.
VAR
zeichen := CHAR;
string := TEXT;
(* einlesen *) zeichen := IO.GetChar(); (* Modul IO *) text := IO.GetLine();
(* ausgeben *) IO.PutChar(zeichen); IO.Put(text);
interessante Umwandlungen sind :
(* CHAR in TEXT, INTEGER *)
text := Text.FromChar(zeichen); (* Modul Text *)
integer := ORD(zeichen); (* wandelt zeichen in den zugehöringen ASCII-Code *)
zeichen := VAL(integer); (* Umkehrfunktion dazu z.b. VAL(66) = 'B' *)
(* TEXT in CHAR *) zeichen := Text.GetChar(text, pos); (* gibt das Zeichen an der Stelle pos aus text zurück *)
Beispiele : zahlen.m3, text.m3

BOOLEAN

Der Typ BOOLEAN kann nur zwei Zustände haben TRUE oder FALSE, eine Variable von diesem Typ behinhaltet also einen Wahrheitswert.
VAR
boolean : BOOLEAN; (* TRUE oder FALSE *)
(* einlesen - selber schreiben =/ *) get_boolean(); (* selbst verfasst *)
(* ausgben durch Umwandlung in TEXT *) IO.Put(Fmt.Bool(boolean)); (* gibt "TRUE" oder "FALSE" aus, Modul Fmt *)
(* BOOLEAN in TEXT *) text := Fmt.Bool(boolean); (* Modul Fmt - siehe oben *)
Für weitere Funktionen lesen sie bitte die Befehlsreferenz. Dort sind einige nützliche Funktionen beschrieben, die man des öfteren zur Umwandlung oder Bearbeitung von Variablen gebrauchen kann.

Typen


Typen sind eigentlich nichts anderes als ein Name für eine Zusammensetzung von Variablentypen in beliebiger Weise. Eingeleitet wird eine Typendefinition durch das Schlüsselwort TYPE. Dies gilt solange, bis ein anderes Schlüsselwort (VAR oder CONST) benutzt wird oder ein BEGIN im Sourcecode steht.
Für einen komplizierten Ausdruck :
VAR matrix3d : ARRAY OF ARRAY OF ARRAY OF INTEGER;
kann man auch übersichtlicher und damit besser lesbar schreiben :
TYPE
int_array : ARRAY OF INTEGER;
int_matrix : ARRAY OF int_array;
int_matrix3d : ARRAY OF int_matrix;
VAR matrix3d : int_matrix;
Wenn sie jetzt sagen : "ist ja schön und gut, aber ist das alles?", bleibt nur ja als Antwort. Aber sie werden diese Möglichkeit bald zu schätzen wissen, vor allem wenn man solche Konstrukte als Parameter übergibt, wird man es bald leid, ständig die vollständige Bezeichnung aufzuschreiben und es gibt durchaus auch geringfügig kompliziertere Konstrukte als das Beispiel =).
Beispiele : simple_type.m3, array_type.m3

Records


Recodrs dienen vorzugsweise dazu, Variablen zu Gruppen zusammenzufassen. Sie bilden nun zusammen eine Einheit, die auch einen eigenen Namen bekommt. Die allgemeine Definition sieht so aus :
TYPE
name = RECORD
[ Variable1 = INTEGER; ]
[ ... ]
END;
Dabei können die Variablen in einem Record von einem beliebigen Typ, also auch wiederum RECORDS sein.
Hat man nun einen solchen Typen deklariert, verwendet man ihn wie folgt :
TYPE
person = RECORD
name : TEXT;
alter : INTEGER;
END;
VAR 
ich = person;
du = person;
BEGIN ich.name := "daniel"; (* zugriff erfolgt mittels *) ich.alter := 235; (* "." und bezeichener *)
du := ich; (* alle elemente von ich werden in du kopiert *) du.name := ich.name; (* hi bruder =) - kopiere nur ein element *) ... END.
Wichtig ist nur das man die einzelnen Elemente mit "Variablenname.Element" ansprechen kann. Benutzt man mehrere Records ineinanderverschachtelt, so funktioniert das genauso :
TYPE
person = RECORD
name : TEXT;
alter : INTEGER;
END;
student = RECORD typ : person; (* verwende personen-typ *) fach : TEXT; (* und noch was neues =) *) END;

VAR ich : student; (* jetzt bin ich student *) BEGIN ich.typ.name := "daniel"; (* hier greift man über "typ" *) (* auf name zu (personen - record) *) ich.typ.alter := 235; (* hier genauso *) ich.fach := "überlebenskunst"; (* fach im typ (record) student *) ... END.

Beispiele : koord.m3, pos_array.m3

Zeiger - Dynamische Speicherverwaltung


Zeiger sind eine spezielle Form von Variablen, die nun nicht mehr direkt den Wert enthalten, sondern auf den Speicherplatz, wo der Wert steht, zeigen. Zeiger haben den Vorteil, das sie zum einen für alle Datentypen gleich gross sind, als Paramter viel Kopierarbeit sparen und generell leicht manipulierbar sind. Einen Zeiger auf einen Typ deklariert man folgendermassen :
   TYPE zeiger_typ = REF Datentyp;		(* Zeiger auf Datentyp *)
Eine Zeigervarible legt man nun wie gewohnt an, achten sie darauf, sie möglichst gleich zu initialisieren!
   VAR zeiger : zeigertyp := NIL;
Hat ein Zeiger den Wert NIL (not in list), so heißt das, das er auf keine Adresse im Speicher zeigt, er hat sozusagen keinen Wert. Benutzt man zeiger, ohne ihm vorher eine gültige Adresse zuzuweisen, so zeigt er auf eine zufällige Adresse im Speicher. Weist man ihm dann einen Wert zu, so überschreibt er diese, was bei manchen Betriebssystemen schlimmstenfalls zu Abstürzen führen kann, bzw. können sie so ungewollt andere Variablen ihres Programmes überschreiben oder verfälschen. Hier ist also Vorsicht geboten (!) haben sie aber keine Scheu, Zeiger zu benutzten - sie machen vieles einfacher.

Mit Hilfe der Zeiger kann man nun den Speicherplatzinhalt (vormals Variable) manipulieren, in dem man hinter den Zeigernamen ein "^" (sog. carret, oder Dach) hängt.
TYPE int_ptr : REF INTEGER;     (* zeiger auf integer 	    	  *)
VAR int : INTEGER; (* variable vom typ integer *) VAR ptr1, ptr1 : int_ptr; (* 2 zeiger auf integer-variablen *)
(* vermeiden sie soetwas : *) ptr1^ := 2131; (* Achtung! Adresse des Zeigers noch unklar *)
(* das ist ok *) ptr1 := NEW(INTEGER); (* ptr bekommt eine Adresse auf einen freien *) (* Speicherplatz für eine Integer-Zahl zugewiesen *) ptr1^ := 2131; (* ok, ptr zeigt auf gültigen Speicherplatz *) ptr2 := ptr1; (* Zeiger 2 zeigt nun auf den gleichen Speicherplatz *)
ptr2^ := 23; IO.PutInt(ptr1^); (* gibt "23" aus, da beide Zeiger auf den gleichen *) (* Speicherplatz zeigen *) ptr1 := NIL; (* Zeiger 1 zeigt nirgendwohin *) ptr2 := NIL; (* Zeiger 2 zeigt nirgendwohin *) (* auf den Speicherplatz mit der 23 kann nun nicht mehr zugegriffen werden! *)
Sie sehen also, man kann Zeiger in vielfältiger Weise einsetzten. Nun nochmal kurz die Syntax von NEW :
  zeiger := NEW(REF Datentyp [, [Dim1] [, Dim2] ]);
NEW gibt immer einen Zeiger auf Datentyp zurück. REF kann man weglassen, wenn Datentyp bereits ein "Zeiger-auf" ist :
   TYPE zeiger_auf_int : REF INTEGER;
VAR zeiger := NEW(zeiger_auf_int);
Die Parameter Dim1, Dim2, ... kommen zum Einsatz, wenn man Zeiger auf Arrays verwenden will. Dim1 bezeichnet dann die Grösse der 1. Dimension, Dim2 die der Zweiten und so weiter :
   TYPE zeiger_auf_array_array_int : REF ARRAY OF ARRAY OF INTEGER;
VAR zeiger := NEW(zeiger_auf_array_array_int, 10,15);
(* Zeiger auf ARRAY[0..9] OF ARRAY[0..14] OF INTEGER *)
Wie man nun einem Zeiger die Adresse einer Variablen zuweisst, habe ich noch nicht herausgefunden ( &Variable, wie in C++ funktioniert nicht), aber man kann sich funktionen schreiben, die soetwas leisten (siehe Beispiele).
Interessant ist dann das Zugreifen auf Records oder Arrays, welches ich hier mal kurz zeigen möchte :
TYPE 
person_ptr = REF person; (* zeiger auf personen-record *)
person = RECORD (* das record selber *)
name : TEXT;
alter : INTEGER;
END;
TYPE array_int : ARRAY OF ARRAY OF INTEGER; array_ptr : REF array_int;
VAR ich : person_ptr; (* ich ist jetzt ein zeiger *) du : person_ptr; (* du auch =) *)
VAR fld : array_ptr; (* zeiger auf 2d-integer array *)
BEGIN ich := NEW(person_ptr); (* reserviere speicher für eine variable vom *) (* typ person und gibt die addresse zurück *) ich^.name := "daniel"; (* ^ (Carret) bedeudet, ich greife auf die Variable zurück *) (* . weil die Variable ein record (sprich zusammengesetzt) ist *) du := ich; (* du zeigt nun auf die gleiche speicherstelle *) du^.name := "neu"; (* ist das gleicher wie : *) ich^.name := "alt"; (* das hier! *)
du^.name := "daniel"; (* da "du" und "ich" auf die gleiche "stelle" zeigen *) IO.Put(ich^.name & "\n"); (* wird hier "daniel" ausgegeben *)
du := NEW(person_ptr); (* du zeigt auf ein neues element *) du^ := ich^; (* ^ greift auf elemente zu -> kopiert "ich" in "du" *)
fld := NEW(array_ptr, 10,10); (* zeiger auf auf 10x10 feld mit integer einträgen*) fld^[0,0] := 12; (* erstes element ist jetzt 0 *) (*

folgendes beispiel setzt alle felder auf "23" - für ein beliebig grosses feld
dabei ist zu beachten, wie "spalte" und "zeile" bestimmt wird! nicht vertauschen,
da sonst chaos raus kommen kann bzw. man über die grenzen schreiben könnte
*) FOR spalte := FIRST(fld^) TO LAST(fld^) DO FOR zeile := FIRST(fld^[FIRST(fld^)]) TO LAST(fld^[FIRST(fld^)]) DO fld^[spalte, zeile] := 23; END; END;
END.
Wenn Modula auch in vielen Bereichen meiner Meinung nach umständlich und unzureichend ist, so ist es doch auch möglich, Zeiger auf Zeiger sowie Zeiger auf Funktionen zu programmieren. Damit kann man dann auch sehr schön dynamisch den Programmlauf ändern, indem man mit Feldern von Zeigern oder Feldern von Funktionen arbeitet. Das zu erklären, würde hier allerdings zu lang dauern und zugegeben, es fällt mir im Moment auch kein gutes Beispiel dazu ein  ;). Aber die Variante möchte ich trotzdem hier kurz mit angeben.
TYPE
funktion = PROCEDURE(); (* hier sind auch parameter und rückgabewerte möglich *)
funk_ptr = REF funktion; (* das ist ein zeiger auf funktionen des typs funktion *)

ptr = REF INTEGER; (* zeiger auf ein integer *)
ptr_ = REF ptr; (* zeiger auf einen "zeiger auf integer" *)
Eine kleine Spielerei damit kann man sich in double_ptr.m3 anschauen (siehe Beispiele) - lasst eurer kreativität freien Lauf ! =)

Beispiele : int_pointer.m3, matrix_ptr.m3, double_ptr.m3, func_ptr.m3

Schleifen und Wiederhohlungen


FOR
(* Syntax *
FOR Variable := Anfangswert TO Endwert [BY Schrittweite] DO
[ Anweisung; ]
[ ... ]
END;
Beachten sie bitte, das Variable vorher nicht deklariert werden muss. Anfangswert und Endwert können selbstverständlich auch Variablen sein (ebenso Schrittweite).

WHILE
(* Syntax *)
WHILE (Variable [= Bedingung]) DO
[ Anweisung ]
[ ... ]
END;
Hier ist es nun notwendig, Variable vorher zu deklarieren. Sie muss BOOLEAN sein, wenn man keine weitere Bedingung angibt, ansonsten ist fast jeder beliebige Typ erlaubt, solange auf einen Wahrheitswert geprüft wird. Möglich wären auch mehrere Bedingungen wie zum Beispiel :

   WHILE ((Variable1 = Bedingung1) OR (Variable2 # Bedingung2)) DO
Anweisung1;
Anweisung2;
...
END;

REPEAT
(* Syntax *)
REPEAT
[ Anweisung ]
[ ... ]
UNTIL (Variable [= Bedingung]);
Auch hier kann man die Bedingung nur weglassen, wenn man Variable vom Typ BOOLEANdeklariert hat. Auch hier kann man mehrere Abbruchbedingungen angeben :

   REPEAT
Anweisung1;
Anweisung2;
...
UNTIL ((Variable1 = Bedinung1) OR (Variable2 = Bedingung2));
Achten sie darauf sinnvoll zu Klammern! Im obrigen Beispiel könnte man die äusseren Klammern beispielsweise auch weglassen, zur Übersichtlichkeit und besseren Lesbarkeit, setzte ich sie jedoch meistens.

Beispielprogramme : for.m3, while.m3, repeat.m3

Vergleiche und Bedingungen


CASE
(* Syntax *)
CASE Variable OF
|Ausdruck => Anweisung1;
|Ausdruck2 => Anweisung2;
Anweisung3;
[ ... ]
[|Ausdruck3 => Anweisung10;]
ELSE
[ Anweisung_Else1; ]
[ Anweisung_Else2; ]
[ ... ]
END;
Wichtig ist hier zum einen, das Ausdruck und Ausdruck2 konstant sein müssen, das heißt sie dürfen hier keine Variablen erwenden. Desweiteren kann man den ELSE-Zweig weglassen :
   CASE Variable OF
[ ... ]
END;
Bekommt dafür aber eine Wahrnung vom Compiler, das nicht alle möglichen Zustände von Variable betrachtet wurden.
Dieser Hinweis ist nicht unbedingt sinnlos, da unter Umständen unbehandelte Möglichkeiten zu unvorhersehbaren Effekten im Programmlauf führen können (siehe Beispiele).

Vermeiden sie folgende syntaktisch korrekte aber nicht sinnvolle Ausdrücke :
   CASE Variable OF
ELSE
END;
IF-THEN-ELSE

Diese Abfrage wird gerne verwendet, wenn Variablen auf einen bestimmten Wert oder Status geprüft werden müssen. Diese Wenn-Dann-Bedingung benutzt man, wenn eine (oder mehrere) Vorraussetzungen erfüllt sein müssen, ehe man etwas tut. Umgangssprachlich formuliert heißt das dann : "Wenn die Bedingung erfüllt ist. dann bearbeite folgende Anweisungen (ansonsten tue nichts)"
(* syntax *)
IF Bedingung THEN
[ Anweisung; ]
[ ... ]
END;
Wenn man auch den Fall betrachten will, wen eine Bedingung nicht erfüllt ist, dann benutzt man üblicherweise die Wenn-Dann-Sonst-Bedingung. Umgangssprachlich formuliert : "Wenn die Bedingung erfüllt ist. dann bearbeite folgende Anweisungen sonst mache folgendes ..."
Und die normale IF-THEN-ELSE Anweisung sieht so aus :
   IF Bedingung THEN
[ Anweisung; ]
[ ... ] (* kein END vor ELSE ! *)
ELSE
[ Anweisung; ]
[ ... ]
END; (* abschliessendes END *)
Wie auch sonst kann man die Bedingung komplexer und ausführlicher Schreiben und mehrere Variablen auf ihren Zustand prüfen. Achten sie auf sinnvolle Bedinungen!Folgende Variante ist auch erlaubt, aber wenig sinnvoll :
   IF 2 = 2 THEN 
       [   Anweisung;   ]
   END;
Beispiele : ifthen.m3, case.m3

Prozeduren


Prozeduren erleichtern das Programmieren ungemein, da sich der Code in Teilaufgaben zerlegen lässt. Solche Code-Blöcke kann man zum einen wiederverwenden und zum anderen allgemeingültig schreiben.
  PROCEDURE name([parameter : Typ]) [ : Rückgabewert] = 
[ Variablendeklaration ] BEGIN [ Anweisung; ] [ Anweisung; ] [ ... ] END name;
Allgemein besitzen Prozeduren auf jeden Fall einen Namen name und einen Anweisungsblock (das zwischen BEGIN
und END name;). Somit sieht der einfachste Typ (ohne alles) so aus :
   PROCEDURE name() = 
BEGIN
[ Anweisung; ]
[ Anweisung; ]
[ ... ]
END name;
Eine Prozedur ohne Anweisung ist denkbar, hat aber wenig Sinn. Um Prozeduren sinnvoller nutzen zu können und sie vor allem allgemeingültig zu schreiben, gibt es die Möglichkeit Parameter zu übergeben und zwar auf drei Art und Weisen. Erstens, man übergibt eine Kopie der Variable, zweitens man übergibt die Variable als Referenz (dann wird direkt mit der Variable gearbeitet, ohne eine Kopie davon im Speicher anzulegen) oder man übergibt sie als konstanten Parameter, damit darf man sie innerhalb der Prozedur nicht mehr verändern. Sinnvoll wird die Übergabe per Wert vorallem dann, wenn man besonders grosse Objekte als Parameter übergeben muss, da sonst für die Kopie zuerst neuer Speicher reserviert und anschliessend das Objekt noch kopiert werden muss, was viel Zeit beansprucht. Eine Alternative währe da die Übergabe eines Zeigers auf das entsprechende Objekt.

Schlüsselwort
Bedeutung
Erklärung
VAR Übergabe per Referenz
es wird direkt mit der Variablen gearbeitet und nur ein Alias eingeführt
VALUE Übergabe per Wert
man arbeitet mit einer Kopie der Variablen, Änderungen haben keinen Einfluss auf die Variable selbst
READONLY
Übergabe per Referenz, aber nur lesbar
man arbeitet direkt mit der Variablen, darf diese aber nicht verändern

Beispiele :

Um die Palette noch zu vervollständigen, wäre es nicht schlecht, wenn man auch einen Wert zurückbekommen könnte. Beispielsweise übergebe ich eine Hausnummer und eine Strasse an die Prozedur und die sagt mir, wer dort wohnt. Oder ich übergebe 2 Summanden und hätte gerne die Summe zurück. Dafür gibt es Rückgabewerte, die von einem beliebigen Typ sein können. Eine Prozedur sieht dann so aus :
   PROCEDURE name([parameter : Typ]) : Rückgabewert =
[ Variablendeklaration ]
BEGIN
[ Anweisung; ]
[ Anweisung; ]
[ ... ]
END name;
Ob mit oder ohne Parameter bleibt ihnen überlassen =).

Beispiele : procedure.m3, f_param_1.m3, f_param_2.m3

Was sind rekursive Prozeduren ?


Rekursiv nennt man Prozeduren dann, wenn sie selbst Bestandteil in ihrem Anweisungsblock sind und sich sozusagen selbst aufrufen. Dabei wird jedoch nur eine Kopie davon erstellt. Der Vorteil darin liegt, das in der Kopie die Werte sozusagen 'zwischengespeichert' werden und man somit schnell Reihen oder Folgen (bspw. bei mathematischen Berechnungen) abarbeiten kann.

Beispiel (Fakultät) :

   PROCEDURE fakultaet(n : INTEGER) : INTEGER =
BEGIN
IF (n <= 1) THEN RETURN(n); (* 1! = 1 und 0! = 0, andere Fälle *)
(* (für negative Zahlen) werden nicht behandelt! *)
ELSE RETURN(n*fakultaet(n-1)); (* 2! = 2x1!; 3! = 3x2! da 2! = 2*1! und *)
(* 1! = 1 => 3! = 3*2*1 = 6 *)
END fakultaet;

Module (Bibliotheken, Librarys, Units)


Module, Bibliotheken, Librarys, Units oder wie auch immer man dazu sagt sind eine Sammlung von Funktionen (Prozeduren), die einen möglichen Problembereich bearbeiten können. Es gibt Module, die eine Funktionssammlung zur Berechnung mathematischer Probleme beinhalten (quadrieren, Sinus oder Cosinus berechnen, potentieren, logarithmieren u.s.w.), andere Stellen Funktionen zur Ausgabe einer Zeichenkette, Zahl oder ähnlichem zur Verfügung.
Damit wir solche Funktionen nicht ständig neu schreiben müssen oder immer wieder in unser Programm reinkopieren, lagert man sie in Module (Bibliotheken) aus. Bindet man diese Bibliotheken in sein Programm ein, kann man alle darin vorkommenden Funktionen verwenden.

Ein Modul besteht aus folgenden Teilen :

Dateiname
Struktur
Beschreibung

Lib.i3
INTERFACE Lib; 
[ ]
END Lib.
Interface - hier definiert man alle benötigten Schnittstellen, wie Prozeduren, diverse Typen, kurz alles, was man von aussen verwenden können soll und muss

Lib.m3

MODULE Lib EXPORTS Lib;
[ ]
BEGIN
[ ]
END Lib.
Implementation - hier werden die Prozeduren bzw. die Anweisungen codiert bzw. programmiert.
Main.m3

MODULE Main;
IMPORT Lib;
[ ]
BEGIN
[ ]
END Main.
Hauptprogramm - hier wird das Modul eingebunden, ansonsten alles wie gewohnt
(Achtung! : kein EXPORTS nach dem Main )

m3makefile

import("libm3")
module("Lib")
implementation("Main")
program("Main")
Makefile - auch hier müssen wir das Modul angeben, da die Implementation auch übersetzt werden muss
ACHTUNG! "libm3" hat nichts mit unserem Modul Lib zu tun, sondern muss immer mit eingebunden werden.

INTEFACE

Alle hier aufgeführten Definitionen von Prozeduren und Typen ect. sind öffentlich und können vom Benutzer der Bibliothek verwendet werden

IMPLEMENTATION

Dieser Teil ist für den Benutzer der Bibliothek eher unerheblich, da ihn die genaue implementation der Lösung nicht interessiert. Er muss nur wissen, welche Funktionen ihm zur Verfügung stehen. Achtung! : alle Funktionen, die nicht im Interface benannt werden, aber man zusätzlich implementiert und benutzt sind nicht öffentlich, d.h. die Prozeduren im Modul können diese zur Problemlösung verwenden, der Benutzer des Moduls jedoch nicht.

Als Beispiel könnte man folgendes nehmen (das m3makefile sieht genauso aus wie oben angegeben) :

"Lib.i3"

INTERFACE Lib;
TYPE
lib_rec = RECORD
anf, end : INTEGER;
END;
PROCEDURE test_lib(); PROCEDURE test_lib2(parameter : INTEGER) : INTEGER;
END Lib.


"Lib.m3"

MODULE Lib EXPORTS Lib;
IMPORT IO; (* verwendete bibliothek *)
(* hilfsprozedur - nicht im interface definiert *) PROCEDURE ausgabe(text : TEXT; zahl : INTEGER) = BEGIN

IO.Put(text); IO.Put(zahl); IO.Put("\n");
END ausgabe;
PROCEDURE test_lib() = BEGIN IO.Put("Das ist Prozedure test_lib aus Modul Lib\n"); END test_lib;
PROCEDURE test_lib2(parameter : INTEGER) : INTEGER = BEGIN (* ok, prozedur ist bekannt *) ausgabe("test_lib2, Parameter ",parameter, ""); END test_lib2;
BEGIN (* Initialisierung *) END Lib.


"Main.m3"

MODULE Main;
IMPORT IO, Lib;
VAR test_rec : Lib.lib_rec; (* ok, record mit 2 integern *)
BEGIN IO.Put("Das Hauptprogramm\n"); Lib.test_lib(); (* ok, ruft funktion test_lib auf *) Lib.test_lib2(5); (* ok, ruft funktion test_lib2 auf *) Lib.ausgabe(); (* fehler, ausgabe nicht im interface angegeben *) END Main.


FAQ zu Fehlern beim compilieren :

TimeStamp
speichern sie einfach alle Dateinen nocheinmal nacheinander ab

Multiple Link
hier löschen sie am besten alle compilierten Dateien (unter Linux das LINUXLIBC6-Verzeichnis)

Objekte


Objekte sind die Verbindung von Datentyp und Prozedur. Die Variablen werden hier aber Eigenschaft(en) des Objekts und die Prozeduren als die Methoden bezeichnet.
Objekte sind überaus hilfreich und erleichtern einem die Arbeit enorm, vorrausgesetzt, sie werden auch einigermassen vernünftig unterstützt - was ich bei Modlua 3 leider überhaupt nicht sagen kann. Das ist eher murks. Deshalb ignoriert das besser - es ist einfach nicht Beispielhaft.
Trotzdem hier eine einfache Implementierung. (mehr konnte ich mir nicht zusammenreihmen)
TYPE	
simple = OBJECT
int : INTEGER; (* die eigenschaften des objekts *)
METHODS
Init() := init; (* die methoden *)
Print() := print;
Add(i : INTEGER) := add;
END;

PROCEDURE init(obj : simple) =
BEGIN
obj.int := 0;
END init;

PROCEDURE print(obj : simple) =
BEGIN
IO.Put(Fmt.Int(obj.int) & "\n");
END print;

PROCEDURE add(obj : simple; i : INTEGER) =
BEGIN
obj.int := obj.int + i;
END add;

VAR objekt := simple;
BEGIN
objekt := NEW(simple); (* neues objekt anlegen *)
objekt.Init(); (* int mit 0 initialisieren *)
objekt.Add(5);
objekt.Print();
END Main.
Ich habe mich nicht verschrieben - die Methoden des Objekts sind die gross geschriebenen Prozeduren, das klein Geschriebene ist die Implementation der entsprechenden Methode. Sie erhält immer als ersten Parameter vom Typ des Objekts, der beim Aufruf aber nicht mit angegeben werden muss. Esexistiert leider kein standartmäßiger *this - Zeiger wie in C/C++ (Java glaub ich auch). Wer das braucht, arbeitet am Besten mit Zeigern und übergibt die Adresse des Objekts bei der Initialisierung.


Threads (ein kleines Multithreading Progamm in Modula 3)


Threads sind eine schöne Sache, um mehrere Aufgaben parallel abarbeiten zu können und dabei die Ressourcen effektiv ausnutzen. Zudem sind (gut programmierte) Multithread-Programme jederzeit mit Performancegewinn lauffähig auf Multiprozessor-Systemen. Hier ist ein kleines Programm welches den Einstieg in die Thread-Programmierung unter Modlua 3 etwas vereinfachen sollte. Es ist wirklich nur das notwendigste, dafür aber besser verständlich. (als m3akefile kann das aus der Einführung verwendet werden)
MODULE Main;
IMPORT Thread, IO;
TYPE Closure = Thread.Closure OBJECT OVERRIDES apply := Printld; END;
VAR cl := NEW(Closure); (* neues thread objekt *) thread : Thread.T; (* variable fuer rueckgabewert *)
PROCEDURE Printld(c : Closure) : REFANY = (* c : Closure muss sein, da apply so definiert ist *) BEGIN FOR i := 1 TO 100000000 DO IF (i MOD 100000) = 0 THEN IO.Put("T"); END; END; RETURN(NIL); END Printld;
BEGIN thread := Thread.Fork(cl); (* thread starten, rückgabewerte speichern *) (* main prozess *) FOR d := 1 TO 100050000 DO IF (d MOD 100000) = 0 THEN IO.Put("M"); (* M für main ausgeben *) END; END; EVAL Thread.Join(thread); (* auf thread warten *) IO.Put("\n"); END Main.
Ein weiters Beispiel wäre (mit 2 Threads konkurierend, wobei beide die selbe Funktion benutzen)
MODULE Main;
IMPORT Thread, IO;
TYPE Closure = Thread.Closure OBJECT number : INTEGER; (* fuer die identifizierung *) OVERRIDES (* default ueberschreiben *) apply := Printld; (* neue aufgabenfunktion *) END;
VAR th_cl_1 := NEW(Closure, number := 1); (* objekt fuer Thread 1 *) th_cl_2 := NEW(Closure, number := 2); (* objekt fuer Thread 2 *) thread1, thread2 : Thread.T; (* fuer rueckgabewerte *)
PROCEDURE Printld(c : Closure) : REFANY = BEGIN FOR i := 1 TO 100000000 DO IF (i MOD 100000) = 0 THEN IF c.number = 1 THEN IO.Put("1"); (* bearbeitet von Thread 1 *) ELSIF c.number = 2 THEN IO.Put("2"); (* bearbeitet von Thread 2 *) END; END; END; RETURN(NIL); (* just nothin =) *) END Printld;
BEGIN thread1 := Thread.Fork(th_cl_1); (* Thread 1 starten *) thread2 := Thread.Fork(th_cl_2); (* Thread 2 starten *)
(* irgendwas machen *) FOR d := 1 TO 100050000 DO IF (d MOD 100000) = 0 THEN IO.Put("M"); END; END; (* fertig mit irgendwas =) *)
EVAL Thread.Join(thread1); (* auf Thread 1 warten *) EVAL Thread.Join(thread2); (* auf Thread 2 warten *)
IO.Put("\n"); END Main.

Wer Lust und Zeit hatte, weitere Beispiele dazu zu programmieren, den würde ich bitten, mir den Source dazu zu schicken, besonders, wenn er noch mit Synchronisation und oder Shared-Memory gearbeitet hat.

Exceptions (Ausnahmen abfangen und werfen)


Exceptions werden vor allem dazu verwendet, Informationen an den Benutzer über eine fehlgeschlagene Aktion zu übermitteln. Damit spart man sich die Rückgabe von komplexen Typen, die neben dem Rückgabewert auch noch die Fehlerinformation beinhalten. Des weiteren ist dem Benutzer überlassen, wie er mit dem Fehler umgeht. Nicht immer will man eine Fehlermeldung auf dem Bildschirm haben.

Exceptions werden zum Beispiel von Funktionen wie IO.GetInt() oder Scan.Int(TEXT) geworfen. Man fängt Exeptions folgendermaßen ab :
   TRY
int := IO.GetInt(); (* kritische operation *)
EXCEPT
IO.Error => IO.Put("Fehler!\n"); (* Fehlerbehandlung für IO.Error *)
END;
im Allgemeinen sieht das etwas so aus :
   TRY
Anweisung;
[ Anweisung2; ]
EXCEPT
|Fehler1 => Anweisung;
[ ... ]
[|Fehler2 => Anweisung ]
END;
Will man selber Exceptions werfen, macht man folgendes :
EXCEPTION fehler;        (* deklaration einer exception *)
EXCEPTION input_error; (* das gleiche *)
PROCEDURE test() : INTEGER RAISES {fehler} = (* prozedur wirft eventuell fehler *) BEGIN [ ... ] (* hier steht irgendwas *) IF Bedingung THEN RAIS fehler; END; (* hier wird die exception aktiv *) [ ... ] RETURN(0); END test;
PROCEDURE print() RAISES {fehler, input_error} = (* mehr sind auch erlaubt *) BEGIN [... ] (* wirft irgendwo fehler und input_error *) END print;
BEGIN TRY print(); (* verwende kritische anweisung *) EXCEPT |fehler => Anweisung; (* fehler ist aufgetreten => tu was *) |input_error => Anweisung2; (* input_error ist aufgetreten *) END; END Main.
Abschließend kann man sagen, das Exceptions dazu verwendet werden können um den Benutzer zu sagen, das Fehler aufgetreten sind und ihm überlässt, was er jetzt machen will.

Beispiele :

Befehlsreferenz


Diese Referenz ist bei weitem nicht vollständig, aber sie umfasst die wichtigsten Operationen um mit Variablen zu hantieren. Ich werde versuchen sie ständig zu erweiten - entsprechend dem was ich benutzt habe und folglich weiss was es macht. Die Referenz, die mir zur Verfügung steht ist in dieser Beziehung mehr als mangelhaft. Über Mithilfe würde ich mich natürlich sehr freuen.

Rechts neben dem Interface stehen die Module, die das Modul benutzt. Das ist wichtig, wenn man Exceptions abfangen will, welche von Prozeduren aus den entsprechenden Submodulen geworfen werden. (Bsp. : Das Abfangen de Lex.Errors erfordert das Einbinden des Modlus Lex)

Interface IO
Rd, Wr


PROCEDURE Put (txt  :  TEXT;  wr  :  Wr.T := NIL);
   (* gibt txt auf dem Bildschirm aus - wr : ?


*)

PROCEDURE PutChar (ch  :  CHAR;  wr  :  Wr.T := NIL);
   (* gibt ch auf dem Bildschirm aus - wr : ?


*)

PROCEDURE PutInt (n  :  INTEGER;  wr  :  Wr.T := NIL);
   (* gibt n mittels Fmt.Int(n) auf dem Bildschirm aus - wr : ?


*)

PROCEDURE PutReal (r  :  REAL;  wr  :  Wr.T := NIL);
   (* gibt r mittels Fmt.Real(r)auf dem Bildschirm aus - wr : ?


*)

PROCEDURE GetLine (rd  :  Rd.T := NIL)  :  TEXT;
   (*  ließt eine Zeile von der tastatur und gibt diese als TEXT zurück
RAISES {Error}
*)

PROCEDURE GetInt (rd  :  Rd.T := NIL)  :  INTEGER;
  (* ließt eine Dezimalzahl von der Tastatur und gibt diese als INTEGER zurück
RAISES {Error}
*)

PROCEDURE GetReal (rd  :  Rd.T := NIL)  :  REAL;
   (* ließt eine nummer von der Tastatur und gibt diese als REAL zurück
RAISES {Error}
*)


Interface Word



PROCEDURE  Divide (x,  y  :  INTEGER)  :  INTEGER;
   (* entspricht x DIV y

*)
PROCEDURE  Mod (x,  y  :  INTEGER)  :  INTEGER;
   (* entspicht x MOD y

*)
PROCEDURE  LT (x,  y  :  INTEGER)  :  BOOLEAN;
   (* "lower then" (kleiner als) entspricht x < y

*)
PROCEDURE  LE (x,  y  :  INTEGER)  :  BOOLEAN;
   (* "lower equal" (kleiner gleich) entspricht x <= y

*)
PROCEDURE  GT (x,  y  :  INTEGER)  :  BOOLEAN;
   (* "greater then" (grösser als) entspricht x > y

*)
PROCEDURE  GE (x,  y  :  INTEGER)  :  BOOLEAN;
   (* "greater equal" (grösser gleich) entspricht x >= y

*)
PROCEDURE  And (x,  y  :  INTEGER)  :  INTEGER;
   (* bitweises UND von x und y

*)
PROCEDURE  Or (x,  y  :  INTEGER)  :  INTEGER;
   (* bitweises ODER von x und y

*)
PROCEDURE  Xor (x,  y  :  INTEGER)  :  INTEGER;
   (* bitweises XOR von x und y

*)
PROCEDURE  Not (x,  y  :  INTEGER)  :  INTEGER;
   (* bitweise Negation von x und y

*)


Interface Text
Word


PROCEDURE Equal (word1,  word2  :  TEXT)  :  BOOLEAN;
   (*  gibt TRUE zurück, wenn word1 und word2 Übereinstimmen (gleiche Länge beachten !)

*)
PROCEDURE Length (text  :  TEXT)  :  CARDINAL;
   (*  gibt die Länge von text zurück (fängt bei 0 an zu Zählen)

*)
PROCEDURE Cat (word1,  word2  :  TEXT)  :  TEXT;
   (*  gibt die Verknüpfung von word1 und word2 zurück

*)
PROCEDURE Sub (text  :  TEXT;  pos,  anzahl  :  CARDINAL)  :  TEXT;
   (*  gibt anzahl Zeichen aus text Angefangen bei pos zurück.

*)
PROCEDURE Empty (text  :  TEXT)  :  BOOLEAN;
   (*  gibt TRUE zurück, wenn textleer ("") ist, äquivalent zu Text.Equal(text, "")

*)
PROCEDURE FromChar (char  :  CHAR)  :  TEXT
   (* wandelt ein Zeichen char in den Typ TEXT um

*)
PROCEDURE GetChar (text  :  TEXT;  pos  :  CARDINAL)  :  CHAR;
   (*  gibt das Zeichen an der Stelle pos in text zurück (Laufzeitfehler, wenn pos > Text.Length(pos) ist)

*)
PROCEDURE FindChar (text  :  TEXT;  zeichen  :  CHAR;  start  :  INTEGER := 0)  :  INTEGER;
   (* gibt die kleinste Position zwischen start und Length(text) zurück, an der zeichen in text auftritt, -1 wenn es nicht vorkommt

*)
PROCEDURE FindCharR (text  :  TEXT;  zeichen  :  CHAR;  end  :  INTEGER := LAST(INTEGER))  :  INTEGER;
   (* gibt die grösste Position zwischen 0 und end zurück, an der zeichen in text auftritt, -1 wenn es nicht vorkommt

*)
PROCEDURE SetChars (VAR  array  :  ARRAY  OF  CHAR;  text  :  TEXT);
   (* schreibt den Text aus text in ein Char-Array (als Folge von Chars) - bedenken sie, dass das array gross genug sein muss

*)
PROCEDURE FromChars (READONLY  array  :  ARRAY  OF  CHAR)  :  TEXT;
   (* wandelt das Char-Array in einen Text um und gibt diesen zurück, das Array wird nicht verändert

*)


Interface Fmt



PROCEDURE Bool (bool :   BOOLEAN) :   TEXT;
   (* gib "TRUE" oder "FALSE" vom Typ TEXT zurück

*)

PROCEDURE Int (int :   INTEGER; base :   Base := 10) :   TEXT;
   (* wandelt die Zahl int interpretiert zu base im Format TEXT zurück (default ist Basis 10, Base = {2..16}

*)

PROCEDURE Char (char :   CHAR) :   TEXT;
   (* gibt das Zeichen char im Format TEXT zurück

*)

PROCEDURE Addr (n :   ADRESS;  base :   Base := 16) :   TEXT;
   (* gibt "NIL" oder den Wert von n zur Basis base im Format TEXT zurück, Base = {2..16}

*)

PROCEDURE Ref (n :   REFANY;  base :   Base := 16) :   TEXT;
   (* gibt oder den Wert von n zur Basis base im Format TEXT zurück, Base = {2..16}

*)

PROCEDURE Real (real :   REAL;  prec :   CARDINAL := 6;  style :   Style := Style.Mix) :   TEXT;
   (* gibt real mit prec Nachkommastellen formatiert mit style vom Typ TEXT zurück, Style = {Flo, AltFlo, Sci, AltSci, Mix}

*)

PROCEDURE LongReal (longreal :   LONGREAL;  prec :   CARDINAL := 6;  style :   Style := Style.Mix) :   TEXT;
   (* gibt longreal mit prec Nachkommastellen formatiert mit style vom Typ TEXT zurück, Style = {Flo, AltFlo, Sci, AltSci, Mix}

*)

PROCEDURE Extended (ext :   EXTENDED;  prec :   CARDINAL := 6;  style : Style := Style.Mix) :   TEXT;
   (* gibt ext mit prec Nachkommastellen formatiert mit style vom Typ TEXT zurück, Style = {Flo, AltFlo, Sci, AltSci, Mix}

*)

PROCEDURE Pad (text :   TEXT;  length :   CARDINAL;  char :   CHAR := '';  align :   Align := Align.Right) :   TEXT;
   (* gibt text bis zu length aufgefüllt mit char (entprechend align, dh. rechts oder linksseitig) im Format TEXT zurück

*)


Interface Scan
Word, Lex, FloatMode


PROCEDURE Bool (text :   TEXT) :   BOOLEAN;
   (* versucht text in BOOLEAN umzuwandeln
RAISES {Lex.Error}

*)
PROCEDURE Int (text :   TEXT) :   INTEGER;
  (* versucht text in ein INTEGER umzuwandeln
RAISES {Lex.Error, FloatMode.Trap}

*)
PROCEDURE Unsigned (text :   TEXT) :   Word.T;
   (* versucht text in den Typ Word.T umzuwandeln
RAISES {Lex.Error, FloatMode.Trap}

*)
PROCEDURE Real (text :   TEXT) :   REAL;
   (* versuche text in eine Real-Zahl umzuwandeln
RAISES {Lex.Error, FloatMode.Trap}

*)
PROCEDURE LongReal (text :   TEXT) :   LONGREAL;
   (* versucht text in LONGREAL unzuwandeln
RAISES {Lex.Error, FloatMode.Trap}

*)
PROCEDURE Extended (text :   TEXT) :   EXTENDED;
   (* versucht text in EXTENDED umzuwandeln
RAISES {Lex.Error, FloatMode.Trap}

*)
PROCEDURE Char (text :   TEXT) :   CHAR;
   (* versuche text in ein CHAR umzuwandeln
RAISES {Lex.Error}

*)

Interface Math


CONST
Pi = 3.1415926535897932384626433833;
LogPi = 1.1447298858494001741434273514;
SqrtPi = 1.7724538509055160272981674833;
E = 2.7182818284590452353602874714;
Degree = 0.017453292519943295769236907684; (* One degree in radians *)

PROCEDURE exp (x :   LONGREAL) :   LONGREAL;
   (* gibt E hoch x zurück


*)
PROCEDURE log (x :   LONGREAL) :   LONGREAL;
   (* gibt den natürlichen Logarithmus von x zurück (zur Basis E)

*)
PROCEDURE log10 (x :   LONGREAL) :   LONGREAL;
   (* gibt den Logarithmus zur Basis 10 zurück

*)
PROCEDURE log1p (x :   LONGREAL) :   LONGREAL;
   (* gibt log(x + 1) zurück, auch für kleine x

*)
PROCEDURE pow (x,  y :   LONGREAL) :   LONGREAL;
   (* gibt x hoch y zurück

*)
PROCEDURE sqrt (x :   LONGREAL) :   LONGREAL;
   (* gibt die Quadratwurzel von x zurück

*)
PROCEDURE cos (x :   LONGREAL) :   LONGREAL;
   (* gibt den Cosinus von x zurück (x im Bogenmass)

*)
PROCEDURE sin (x :   LONGREAL) :   LONGREAL;
   (* gibt den Sinus von x zurück (x im Bogenmass)

*)
PROCEDURE tan (x :   LONGREAL) :   LONGREAL;
   (* gibt den Tangens von x zurück (x im Bogenmass)

*)
PROCEDURE acos (x :   LONGREAL) :   LONGREAL;
   (* gibt den arc Cosinus von x zurück (x im Bogenmass)

*)
PROCEDURE asin (x :   LONGREAL) :   LONGREAL;
   (* gibt den arc Sinus von x zurück (x im Bogenmass)

*)
PROCEDURE atan (x :   LONGREAL) :   LONGREAL;
   (* gibt den arc Tangens von x zurück (x im Bogenmass)

*)
PROCEDURE atan2 (x,  y :   LONGREAL) :   LONGREAL;
   (* gibt den arc Tangens x/y zurück (x,y im Bogenmass)

*)
PROCEDURE ldexp (x  :   LONGREAL; exp :   INTEGER) :   LONGREAL;
   (* gibt x mal 2 hoch exp zurück

*)

Interface Stack <by daniel>


TYPE
stack_ptr = REF stack;
stack = RECORD
addr : INTEGER;
value : REFANY;
next,back : stack_ptr;
END;

TYPE
sort = PROCEDURE(x,y : REFANY) : BOOLEAN; (* Funktion um "groesser gleich" zweier Einträge zu bestimmen *)
show = PROCEDURE(x : REFANY); (* Funktion für die Ausgabe eines Elements*)

PROCEDURE Push(VAR stack : stack_ptr; x : REFANY);
    (* legt ein neues Element x auf den Stapel stack


*)

PROCEDURE Pop (VAR stack : stack_ptr) : REFANY;
    (* nimmt das oberste Element vom Stapel stack und gibt es zurück (Element wird dabei gelöscht!)
RAISES {StackEmpty}

*)

PROCEDURE Drop(VAR stack : stack_ptr);
    (* löscht das oberste Element vom Stapel stack
RAISES {StackEmpty)

*)

PROCEDURE Swap(VAR stack : stack_ptr);
    (* vertauscht das oberste Element mit dem darunter liegenden Element des Stapels stack
RAISES {StackEmpty}

*)

PROCEDURE Dub (VAR stack : stack_ptr);
    (* kopiert das oberste Element des Stapels stack und legt es oben drauf
RAISES {StackEmpty}

*)

PROCEDURE Sort(VAR stack : stack_ptr; check : sort);
    (* sortiert die Elemente des Stapels stack nach den Kriterien in check
RAISES {StackEmpty}

*)

PROCEDURE Top (stack : stack_ptr) : REFANY;
    (* gibt das oberste Element des Stapels stack zurück, ohne es zu löschen
RAISES {StackEmpty}

*)

PROCEDURE Size(stack : stack_ptr) : CARDINAL;
    (* gibt die Anzahl der Elemente zurück, die auf den Stapel stack liegen


*)

PROCEDURE Show(stack : stack_ptr; print : show);
    (* gibt alle Elemente des Stapels stack mittels der Funktion print aus
RAISES {StackEmpty}

*)


Interface Input <by daniel>


TYPE
setofchar = SET OF CHAR;

PROCEDURE get_int (text : TEXT; min : INTEGER := FIRST(INTEGER); max : INTEGER := LAST(INTEGER)) : INTEGER;
    (* liesst einen Intwert in den Grenzen [min,max] ein, text enthält die Eingabeaufforderung

*)

PROCEDURE get_abs_int (text : TEXT; min : INTEGER := 0;  max : INTEGER := LAST(INTEGER)) : INTEGER;
   (* liesst einen Intwert in den Grenzen [min,max] ein und bildet den Betrag davon, text enthält die Eingabeaufforderung

*)

PROCEDURE get_card (text : TEXT; min : INTEGER := 0;  max : INTEGER := LAST(INTEGER)) : CARDINAL;
   (* liesst eine Cardinalzahl in den Grenzen [min,max] ein, text enthält die Eingabeaufforderung

*)

PROCEDURE get_real (text : TEXT; min : REAL := FIRST(REAL); ;max : REAL := LAST(REAL) ) : REAL;
   (* liesst einen Realwert in den Grenzen [min,max] ein, text enthält die Eingabeaufforderung

*)

PROCEDURE get_longreal (text : TEXT; min : LONGREAL := FIRST(LONGREAL); max : LONGREAL := LAST(LONGREAL)) : LONGREAL;
   (* liesst einen Longrealwert in den Grenzen [min,max] ein, text enthält die Eingabeaufforderung

*)

PROCEDURE get_extended (text : TEXT; min : EXTENDED := FIRST(EXTENDED); max : EXTENDED := LAST(EXTENDED)) : EXTENDED;
   (* liesst einen Extendedwert in den Grenzen [min,max] ein, text enthält die Eingabeaufforderung

*)

PROCEDURE get_char (text : TEXT) : CHAR;
   (* liesst ein Zeichen  ein, text enthält die Eingabeaufforderung

*)

PROCEDURE get_achar (text : TEXT; allowed : setofchar) : CHAR;
   (* liesst ein Zeichen aus der Menge allowed ein, text enthält die Eingabeaufforderung

*)

PROCEDURE get_line (text : TEXT) : TEXT;
   (* liesst eine Zeile beliebiger Zeichen ein, text enthält die Eingabeaufforderung

*)

PROCEDURE get_aline (text : TEXT; allowed : setofchar; error_pos : BOOLEAN := FALSE) : TEXT;
   (* liesst eine Zeile Text ein, wobei nur Zeichen aus allowed zugelassen sind, text enthält die Eingabeaufforderung, error_pos bestimmt, ob die Position des Fehlers in der Eingabe mit angegeben wird

*)


Interface Format <by daniel>


TYPE
Side = {left, right};
setofchar = SET OF CHAR;

PROCEDURE fill_space   (text : TEXT; length : INTEGER; sign : CHAR; side : Side) : TEXT;
   (* füllt text mit Zeichen sign auf, bis der Text die Länge length hat, side bestimmt ob die Zeichen sign rechts oder links angehängt werden

*)

PROCEDURE cut_spaces   (text : TEXT) : TEXT;
   (* entfernt alle Leerzeichen, die links und rechts von text vorkommen

*)

PROCEDURE remove_char  (text : TEXT; pos : INTEGER) : TEXT;
   (* entfernt das Zeichen an der Stelle pos aus dem String text

*)

PROCEDURE cut_char_all (text : TEXT; sign : CHAR) : TEXT;
   (* entfernt alle Zeichen sign aus dem String text

*)

PROCEDURE cut_all_char (text : TEXT; remove : setofchar) : TEXT;
   (* entfernt alle Zeichen aus text, die in der Menge remove angegeben werden

*)

PROCEDURE insert_char (text : TEXT; pos : INTEGER; char : CHAR) : TEXT;
   (* fügt an der Stelle pos [0..Length(text)+1] in text das Zeichen char ein, der Rest wird nach Rechts geschoben


PROCEDURE change_char (text : TEXT; pos : INTEGER; char : CHAR) : TEXT;
   (* ersetzt im String text das Zeichen an der Stelle pos durch das Zeichen char


PROCEDURE change_all (text : TEXT; char, new : CHAR) : TEXT;
   (* ersetzt alle Zeichen char in text durch das Zeichen new




Download



File
Size
Date
Beschreibung


   examples.tar.gz
4132 bytes
17-Nov-2003
  alle Beispiele aus der Dokumentation


   stack.tar.gz
1746 bytes
17-Nov-2003
  Modul für die Simulation eines Stacks mit einem beliebigen Variablen-Typ (siehe Referenz)


   input.tar.gz
1682 bytes
18-Nov-2003
  Modul zum einfachen Einlesen von Typen mit ein paar Features =) (siehe Referenz)


   format.tar.gz
882 bytes
18-Nov-2003
  Modulerweiterung zu Text - Features für Zeichen löschenn, anhängen ect. (siehe Referenz)



Links


Ein paar nützliche Links zu Seiten mit Modlua 3 Stuff.


Link
Beschreibung


  http://www.m3.org/
  die Seite für Modula 3


  http://www1.cs.columbia.edu/graphics/modula3/tutorial/www/m3_toc.html
  eine Umfangreiche Referenz für Modula, allerdings nicht sehr übersichtlich


  http://wwwold.ifi.uni-klu.ac.at/Modula-3/m3buch/beispiele.html
  Programmierbeispiele für Modula - einfach immer gut =)


  http://research.compaq.com/SRC/modula-3/html/srcm3.html
  SRC-Modula 3 (gute Seite, aber viel zu klicken)



EOF


>binaervarianz >projects >Modula-3