Oberon for microprocessors and controllers.
As explained in the main page, I got entangled in Oberon. And now I want to use the techniques described in Compiler construction (by Niklaus Wirth) to generate an Oberon-0 compiler for small microprocessors and controllers. I am going to use the sources (in real Oberon) as they are described in the afore mentioned book.
The first task is to transform the Oberon sourcecode into Mocka Modula-2 format and syntax. I will mention the steps in this section, possibly divided over several files.
OSS: the Scanner
Below is the source of what used to be the OSS.Mod module. It was in Oberon format, but I changed it into Mocka Modula-2 format. The file is now named OSS.mi. It compiles to an object file! We start out with the DEFINITION MODULE Scanner.md:
DEFINITION MODULE OSS; (* NW 19.9.93 / 17.11.94*)
(* Adapted for Mocka Modula-2 by Jan Verhoeven, Feb 2006.
E-mail: fruttenboel@gmail.com *)
CONST IdLen = 16; (* symbols *)
times = 1; div = 3; mod = 4; and = 5;
plus = 6; minus = 7; or = 8; eql = 9;
neq = 10; lss = 11; geq = 12; leq = 13;
gtr = 14; period = 18; comma = 19; colon = 20;
rparen = 22; rbrak = 23; of = 25; then = 26;
do = 27; lparen = 29; lbrak = 30; not = 32;
becomes = 33; number = 34; ident = 37; semicolon = 38;
end = 40; else = 41; elsif = 42; if = 44;
while = 46; array = 54; record = 55; const = 57;
type = 58; var = 59; procedure = 60;
begin = 61; module = 63;
TYPE Ident = ARRAY [0..IdLen - 1] OF CHAR;
VAR val : LONGINT;
id : Ident;
error : BOOLEAN;
PROCEDURE Mark (msg : ARRAY OF CHAR);
PROCEDURE Get (VAR sym : INTEGER);
PROCEDURE Init (Name : ARRAY OF CHAR);
END OSS.
Scanner IMPLEMENTATION MODULE
Below is the source code for the IMPLEMENTATION MODULE OSS.mi which belongs to OSS.md. The main differences with the Oberon equivalent are:
IMPLEMENTATION MODULE OSS; (* NW 19.9.93 / 17.11.94*)
(* Adapted for Mocka Modula-2 by Jan Verhoeven, Feb 2006.
E-mail: fruttenboel@gmail.com *)
IMPORT ASCII, InOut, Strings, TextIO;
CONST KW = 34; (* symbols *)
null = 0;
eof = 64;
VAR ch : CHAR;
nkw, pos, Line, Column,
errpos : INTEGER;
inFile : TextIO.File;
ProgramEnd : BOOLEAN;
keyTab : ARRAY [0 .. KW-1] OF RECORD
sym : INTEGER;
id : ARRAY [0..11] OF CHAR
END;
PROCEDURE ReadChar (VAR chr : CHAR);
BEGIN
TextIO.GetChar (inFile, chr);
IF TextIO.EOF (inFile) = TRUE THEN ProgramEnd := TRUE END;
INC (pos);
INC (Column);
IF chr = ASCII.LF THEN
INC (Line);
Column := 1
END
END ReadChar;
PROCEDURE Mark (msg: ARRAY OF CHAR);
VAR p : INTEGER;
BEGIN
p := pos - 1;
IF p > errpos THEN
InOut.WriteString ("Error "); InOut.WriteString (msg);
InOut.WriteString ("at pos"); InOut.WriteInt (p, 6);
InOut.WriteString (", line ="); InOut.WriteInt (Line, 5);
InOut.WriteString (", col ="); InOut.WriteInt (Column, 4);
InOut.Write ("."); InOut.WriteLn
END;
errpos := p;
error := TRUE
END Mark;
PROCEDURE Get (VAR sym: INTEGER);
PROCEDURE Ident;
VAR i, k : INTEGER;
BEGIN
i := 0;
REPEAT
IF i < IdLen THEN id[i] := ch; INC (i) END;
ReadChar (ch)
UNTIL (ch < "0") OR (ch > "9") & (CAP (ch) < "A") OR (CAP (ch) > "Z");
id [i] := 0C;
k := 0;
WHILE (k < nkw) & Strings.StrEq (id, keyTab [k].id) DO INC (k) END;
IF k < nkw THEN
sym := keyTab[k].sym
ELSE
sym := ident
END
END Ident;
PROCEDURE Number;
BEGIN
val := 0; sym := number;
REPEAT
IF val <= (MAX (LONGINT) - INTEGER (ORD (ch) + ORD ("0"))) DIV 10 THEN
val := 10 * val + INTEGER ((ORD (ch) - ORD ("0")))
ELSE
Mark ("number too large");
val := 0
END;
ReadChar (ch)
UNTIL (ch < "0") OR (ch > "9")
END Number;
PROCEDURE comment;
BEGIN
ReadChar (ch);
LOOP
LOOP
WHILE ch = "(" DO
ReadChar (ch);
IF ch = "*" THEN comment END
END;
IF ch = "*" THEN ReadChar (ch); EXIT END;
IF ProgramEnd THEN EXIT END;
ReadChar (ch)
END;
IF ch = ")" THEN ReadChar (ch); EXIT END;
IF ProgramEnd THEN
Mark ("comment not terminated");
EXIT
END
END
END comment;
BEGIN
WHILE (NOT ProgramEnd) & (ch <= " ") DO ReadChar (ch) END;
IF ProgramEnd THEN
sym := eof
ELSE
CASE ch OF
"&" : ReadChar (ch); sym := and
| "*" : ReadChar (ch); sym := times
| "+" : ReadChar (ch); sym := plus
| "-" : ReadChar (ch); sym := minus
| "=" : ReadChar (ch); sym := eql
| "#" : ReadChar (ch); sym := neq
| "<" : ReadChar (ch);
IF ch = "=" THEN ReadChar (ch); sym := leq ELSE sym := lss END
| ">" : ReadChar (ch);
IF ch = "=" THEN ReadChar (ch); sym := geq ELSE sym := gtr END
| ";" : ReadChar (ch); sym := semicolon
| "," : ReadChar (ch); sym := comma
| ":" : ReadChar (ch);
IF ch = "=" THEN ReadChar (ch); sym := becomes ELSE sym := colon END
| "." : ReadChar (ch); sym := period
| "(" : ReadChar (ch);
IF ch = "*" THEN comment; Get (sym) ELSE sym := lparen END
| ")" : ReadChar (ch); sym := rparen
| "[" : ReadChar (ch); sym := lbrak
| "]" : ReadChar (ch); sym := rbrak
| "0".."9" : Number;
| "A" .. "Z",
"a".."z" : Ident
| "~" : ReadChar (ch); sym := not
ELSE
ReadChar (ch); sym := null
END
END
END Get;
PROCEDURE Init (Name : ARRAY OF CHAR);
BEGIN
pos := 0; ProgramEnd := FALSE;
error := FALSE; errpos := pos;
TextIO.OpenInput (inFile, Name);
ReadChar (ch)
END Init;
PROCEDURE EnterKW (sym : INTEGER; name : ARRAY OF CHAR);
BEGIN
keyTab [nkw].sym := sym;
Strings.Assign (name, keyTab [nkw].id);
INC (nkw)
END EnterKW;
BEGIN
error := TRUE;
nkw := 0;
EnterKW (null, "BY");
EnterKW (do, "DO");
EnterKW (if, "IF");
EnterKW (null, "IN");
EnterKW (null, "IS");
EnterKW (of, "OF");
EnterKW (or, "OR");
EnterKW (null, "TO");
EnterKW (end, "END");
EnterKW (null, "FOR");
EnterKW (mod, "MOD");
EnterKW (null, "NIL");
EnterKW (var, "VAR");
EnterKW (null, "CASE");
EnterKW (else, "ELSE");
EnterKW (null, "EXIT");
EnterKW (then, "THEN");
EnterKW (type, "TYPE");
EnterKW (null, "WITH");
EnterKW (array, "ARRAY");
EnterKW (begin, "BEGIN");
EnterKW (const, "CONST");
EnterKW (elsif, "ELSIF");
EnterKW (null, "IMPORT");
EnterKW (null, "UNTIL");
EnterKW (while, "WHILE");
EnterKW (record, "RECORD");
EnterKW (null, "REPEAT");
EnterKW (null, "RETURN");
EnterKW (null, "POINTER");
EnterKW (procedure, "PROCEDURE");
EnterKW (div, "DIV");
EnterKW (null, "LOOP");
EnterKW (module, "MODULE");
END OSS.
There is something funny with this KeyTable since it contains keywords which are unknown to Oberon0. I
mention:
Anyway, as we can see below, the units compile without errors:
bash-2.05$ MC Mocka 9903 >> s OSS >> c OSS .. Compiling Implementation of OSS I/0009 II/0009 >> ls -l OSS* -rw-r--r-- 1 jan users 942 Feb 3 00:21 OSS.d -rw-r--r-- 1 jan users 1200 Feb 3 00:21 OSS.md -rw-r--r-- 1 jan users 5539 Feb 3 00:40 OSS.mi -rw-r--r-- 1 jan users 13388 Feb 3 00:48 OSS.o -rw-r--r-- 1 jan users 70 Feb 3 00:48 OSS.r >> q bash-2.05$
OSP and OSG: the big push.
I started work on the OSG and OSP modules but this is quite a work. Lots of qualified imports going back and
forth. I tried to dig myself through the OSP module, but got stuck several times. Remember: I'm not a genius.
I need time.
Yesterday, I had a bright moment (or rather: THE bright moment of March) and decided to merge OSP and OSG into
one file and call that file Oberon0.mi. This removes the majority of qualified imports and makes the transfer
from an Oberon controlled task into a Linux command line executable. See how it works out...
Page created February 2006,
Page equipped with FroogleBuster technology