Plov005 : Make it use file I/O and read the command line
Let's first do what we always do on this site (and what is forgotten in all bigger sites): produce some source code. The source is Plov005 and it is identical to Plov004 with the exception that characters are read from file, which' name is supplied on the command line.
MODULE Plov005;
(* Plov is the PL/0 compiler as modified by Verhoeven *)
(* Ver Comment : Date *)
(* ----- ----------------------------------------------------- ------------ *)
(* 0.01 First version: read symbols and store them in a list 01 Oct 2007 *)
(* 0.02 Introduce a limited symbol processor; aborted 02 Oct 2007 *)
(* 0.03 Introduce a recursive descent parser 06 Oct 2007 *)
(* 0.04 Fine tune the parser 18 Oct 2007 *)
(* 0.05 Adapt it for file IO and command line 26 Nov 2007 *)
IMPORT ASCII, InOut, MemPools, NumConv, Strings, SYSTEM, TextIO, Arguments;
TYPE Identifier = ARRAY [0..31] OF CHAR;
Opcode = ARRAY [0..7] OF CHAR;
SymbolType = (Progtype, Proctype, Constype, Vartype, Keytype);
SymbolPtr = POINTER TO SymbolNode;
SymbolNode = RECORD
Name : Identifier;
previous,
next : SymbolPtr;
type : SymbolType;
value,
address : CARDINAL
END;
VAR token : Identifier;
op : Opcode;
LineEnd,
Exhausted : BOOLEAN;
Symbols : MemPools.MemPool;
firstSymbol,
lastSymbol,
thisSymbol : SymbolPtr;
currentType : SymbolType;
LOOPdepth,
PROCdepth,
currentValue : CARDINAL;
inFile, outFile : TextIO.File;
buffer : Arguments.ArgTable;
PROCEDURE ErrorMessage (n : CARDINAL);
BEGIN
CASE n OF
0 : InOut.WriteString ("Error filling Symbol table") |
1 : InOut.WriteString ("Invalid digit in : ");
InOut.WriteString (token) |
2 : InOut.WriteString ("Invalid letter in : ");
InOut.WriteString (token) |
3 : InOut.WriteString ("Program names do not match.") |
4 : InOut.WriteString ("BEGIN expected") |
5 : InOut.WriteString ("Illegal identifier name : ");
InOut.WriteString (token) |
6 : InOut.WriteString ("Duplicate identifier : ");
InOut.WriteString (token) |
7 : InOut.WriteString ("'=' expected") |
8 : InOut.WriteString ("END expected") |
9 : InOut.WriteString ("Undefined symbol : ");
InOut.WriteString (token) |
10 : InOut.WriteString ("Error in StatementSequence") |
11 : InOut.WriteString ("EXIT without LOOP") |
12 : InOut.WriteString ("THEN expected") |
13 : InOut.WriteString ("Illegal comparator : ");
InOut.WriteString (token) |
14 : InOut.WriteString ("Illegal operator in factor : ");
InOut.WriteString (token) |
15 : InOut.WriteString ("Error in factor : ");
InOut.WriteString (token) |
16 : InOut.WriteString ("Missing right parenthesis") |
17 : InOut.WriteString ("Missing assigment operator") |
18 : InOut.WriteString ("PROGRAM expected") |
19 : InOut.WriteString ("Procedure names do not match.") |
33 : InOut.WriteString ("Syntax : Plov infile") |
34 : InOut.WriteString ("Cannot open file '");
InOut.WriteString (buffer^[1]^); InOut.Write ("'")
ELSE
InOut.WriteString ("Unknown error detected.")
END;
InOut.WriteLn;
InOut.WriteBf
END ErrorMessage;
PROCEDURE StoreSymbol (str : Identifier) : BOOLEAN;
VAR thisOne, nextOne : SymbolPtr;
BEGIN
thisOne := firstSymbol;
LOOP
IF Strings.StrEq (thisOne^.Name, str) THEN RETURN FALSE END;
IF thisOne^.next = NIL THEN EXIT END;
thisOne := thisOne^.next
END;
MemPools.PoolAllocate (Symbols, nextOne, SYSTEM.TSIZE (SymbolNode));
thisOne^.next := nextOne;
WITH nextOne^ DO
Name := str;
type := currentType;
next := NIL;
previous := thisOne
END;
lastSymbol := nextOne;
thisSymbol := nextOne;
RETURN TRUE
END StoreSymbol;
PROCEDURE FindSymbol (str : Identifier) : BOOLEAN;
VAR thisOne, nextOne : SymbolPtr;
BEGIN
thisOne := firstSymbol;
LOOP
IF Strings.StrEq (thisOne^.Name, str) THEN
thisSymbol := thisOne;
RETURN TRUE
END;
IF thisOne^.next = NIL THEN EXIT END;
thisOne := thisOne^.next
END;
RETURN FALSE
END FindSymbol;
PROCEDURE PrintNames;
VAR thisOne : SymbolPtr;
BEGIN
thisOne := firstSymbol;
InOut.WriteLn;
LOOP
InOut.WriteString (thisOne^.Name); InOut.Write (11C);
CASE thisOne^.type OF
Progtype : InOut.WriteString ("Program type") |
Proctype : InOut.WriteString ("Procedure type") |
Constype : InOut.WriteString ("Constant type = ");
InOut.WriteCard (thisOne^.value, 5) |
Vartype : InOut.WriteString ("Variable type") |
Keytype : InOut.WriteString ("Keyword type")
END;
InOut.WriteLn;
IF thisOne^.next = NIL THEN
EXIT
ELSE
thisOne := thisOne^.next
END
END
END PrintNames;
PROCEDURE ReadString (str : Identifier);
VAR n, m : CARDINAL;
BEGIN
TextIO.GetString (inFile, str)
END ReadString;
PROCEDURE GetSymbol;
BEGIN
IF TextIO.EOF (inFile) = FALSE THEN
TextIO.GetString (inFile, token)
ELSE
InOut.WriteString ("EOF!!! ");
END;
InOut.WriteString (token); InOut.Write (11C); InOut.WriteBf
END GetSymbol;
PROCEDURE isDigit (chr : CHAR) : BOOLEAN;
BEGIN
IF (chr >= '0') AND (chr <= '9') THEN
RETURN TRUE
END;
RETURN FALSE
END isDigit;
PROCEDURE isAlpha (chr : CHAR) : BOOLEAN;
BEGIN
chr := CAP (chr);
IF (chr >= 'A') AND (chr <= 'Z') THEN
RETURN TRUE
END;
RETURN FALSE
END isAlpha;
PROCEDURE isAlphaNum (chr : CHAR) : BOOLEAN;
BEGIN
RETURN isAlpha (chr) OR isDigit (chr)
END isAlphaNum;
PROCEDURE isNumber (str : Identifier) : BOOLEAN;
VAR i : CARDINAL;
BEGIN
i := 0;
REPEAT
IF isDigit (str [i]) = FALSE THEN RETURN FALSE END;
INC (i)
UNTIL (str [i] = 0C) OR (i > HIGH (str));
RETURN TRUE
END isNumber;
PROCEDURE isIdentifier (str : Identifier) : BOOLEAN;
VAR i : CARDINAL;
BEGIN
IF NOT isAlpha (str [0]) THEN RETURN FALSE END;
IF Strings.Length (str) = 1 THEN RETURN TRUE END;
i := 1;
REPEAT
IF NOT isAlphaNum (str [i]) THEN RETURN FALSE END;
INC (i)
UNTIL (str [i] = 0C) OR (i > HIGH (str));
RETURN TRUE
END isIdentifier;
PROCEDURE Program;
BEGIN
GetSymbol;
IF Strings.StrEq (token, "PROGRAM") THEN
GetSymbol;
IF isIdentifier (token) THEN
WITH firstSymbol^ DO
Name := token;
type := Progtype
END;
Block;
IF Strings.StrEq (token, firstSymbol^.Name) = FALSE THEN
ErrorMessage (3)
END
ELSE
ErrorMessage (5) (* Illegal identifier *)
END
ELSE
ErrorMessage (18) (* 'PROGRAM' expected *)
END
END Program;
PROCEDURE Block;
BEGIN
GetSymbol;
IF Strings.StrEq (token, "CONSTANTS") THEN ConstantDeclaration END;
IF Strings.StrEq (token, "VARIABLES") THEN VariableDeclaration END;
WHILE Strings.StrEq (token, "PROCEDURE") DO ProcedureDeclaration END;
BlockBody;
END Block;
PROCEDURE BlockBody;
BEGIN
IF Strings.StrEq (token, "BEGIN") THEN
GetSymbol;
StatementSequence
ELSE
ErrorMessage (4) (* BEGIN expected *)
END;
IF Strings.StrEq (token, "END") THEN
GetSymbol;
RETURN
ELSE
ErrorMessage (8); (* END expected *)
GetSymbol
END
END BlockBody;
PROCEDURE StatementSequence;
BEGIN
LOOP
IF FindSymbol (token) = TRUE THEN
IF (thisSymbol^.type = Vartype) THEN Assignment
ELSIF thisSymbol^.type = Proctype THEN Procedurecall
ELSIF Strings.StrEq (token, "IF") THEN isIF
ELSIF Strings.StrEq (token, "LOOP") THEN isLOOP
ELSIF Strings.StrEq (token, "RETURN") THEN isRETURN
ELSIF Strings.StrEq (token, "EXIT") THEN isEXIT
ELSIF Strings.StrEq (token, "END") THEN
RETURN
ELSE
ErrorMessage (10); (* Error in StatementSequence *)
END
ELSE
ErrorMessage (9); (* Undefined symbol *)
END;
END;
END StatementSequence;
PROCEDURE ConstantDeclaration;
VAR Val : CARDINAL;
ok : BOOLEAN;
BEGIN
GetSymbol;
REPEAT
currentType := Constype;
IF isIdentifier (token) THEN
IF StoreSymbol (token) = TRUE THEN
GetSymbol;
IF token [0] = '=' THEN
GetSymbol;
NumConv.Str2Num (Val, 10, token, ok);
IF ok THEN
WITH thisSymbol^ DO
value := Val;
type := Constype
END;
ELSE
ErrorMessage (1) (* Invalid digit *)
END
ELSE
ErrorMessage (7); (* '=' expected *)
END
ELSE
ErrorMessage (6); (* Duplicate identifier *)
END
ELSE
ErrorMessage (5); (* Illegal Identifier *)
END;
GetSymbol
UNTIL Strings.StrEq (token, "END");
GetSymbol
END ConstantDeclaration;
PROCEDURE VariableDeclaration;
BEGIN
GetSymbol;
REPEAT
currentType := Vartype;
IF isIdentifier (token) THEN
IF StoreSymbol (token) = FALSE THEN
ErrorMessage (6) (* Duplicate identifier *)
END
ELSE
ErrorMessage (5); (* Illegal Identifier *)
END;
GetSymbol
UNTIL Strings.StrEq (token, "END");
GetSymbol
END VariableDeclaration;
PROCEDURE ProcedureDeclaration;
VAR thisP : SymbolPtr;
BEGIN
GetSymbol;
currentType := Proctype;
IF isIdentifier (token) THEN
IF StoreSymbol (token) = TRUE THEN
thisP := thisSymbol;
INC (PROCdepth);
GetSymbol;
BlockBody;
DEC (PROCdepth);
IF Strings.StrEq (thisP^.Name, token) = FALSE THEN
ErrorMessage (19) (* Names do not match *)
END;
GetSymbol
ELSE
ErrorMessage (6) (* Duplicate identifier *)
END
ELSE
ErrorMessage (5) (* Illegal identifier *)
END
END ProcedureDeclaration;
PROCEDURE Procedurecall;
BEGIN
GetSymbol
END Procedurecall;
PROCEDURE isLOOP;
BEGIN
INC (LOOPdepth);
GetSymbol; StatementSequence;
IF Strings.StrEq (token, "END") THEN
DEC (LOOPdepth)
ELSE
ErrorMessage (8) (* END expected *)
END;
GetSymbol
END isLOOP;
PROCEDURE isEXIT;
BEGIN
IF LOOPdepth = 0 THEN
ErrorMessage (11) (* EXIT without LOOP *)
ELSE
DEC (LOOPdepth)
END;
GetSymbol
END isEXIT;
PROCEDURE isRETURN;
BEGIN
IF PROCdepth = 0 THEN
ErrorMessage (19) (* RETURN without PROCEDURE *)
END;
GetSymbol
END isRETURN;
PROCEDURE isIF;
BEGIN
Condition;
IF Strings.StrEq (token, "THEN") THEN
GetSymbol;
StatementSequence;
WHILE Strings.StrEq (token, "ELSIF") DO
Condition;
IF Strings.StrEq (token, "THEN") THEN
GetSymbol;
StatementSequence
ELSE
ErrorMessage (12) (* THEN expected *)
END
END;
IF Strings.StrEq (token, "ELSE") THEN
GetSymbol;
StatementSequence
END;
IF Strings.StrEq (token, "END") = FALSE THEN
ErrorMessage (8) (* END expected *)
END;
GetSymbol
ELSE
ErrorMessage (12) (* THEN expected *)
END
END isIF;
PROCEDURE Condition;
BEGIN
IF Strings.StrEq (token, "ODD") THEN
Expression
ELSE
GetSymbol; Expression;
Comparison;
GetSymbol; Expression
END
END Condition;
PROCEDURE Comparison;
BEGIN
CASE token [0] OF
'=' : op := 'EQL' |
'<' : CASE token [1] OF
0C : op := 'LESS' |
'>' : op := 'NEQL' |
'=' : op := 'LEQL'
ELSE
ErrorMessage (13) (* Illegal comparator *)
END |
'>' : CASE token [1] OF
0C : op := 'GRTR' |
'=' : op := 'GEQL'
ELSE
ErrorMessage (13) (* Illegal comparator *)
END |
'#' : op := 'NEQL'
ELSE
ErrorMessage (13) (* Illegal comparator *)
END;
END Comparison;
PROCEDURE Assignment;
BEGIN
GetSymbol;
IF Strings.StrEq (token, ':=') THEN
GetSymbol;
Expression
ELSE
ErrorMessage (17); (* Missing ':=' *)
END
END Assignment;
PROCEDURE Expression;
BEGIN
Term;
WHILE (token [0] = '+') OR (token [0] = '-') DO
GetSymbol;
Term
END
END Expression;
PROCEDURE TermOperator () : BOOLEAN;
VAR oper : CHAR;
BEGIN
oper := token [0];
IF Strings.StrEq (token, 'x') OR (oper = '*') OR (oper='.') THEN RETURN TRUE
ELSIF (oper = ':') OR (oper = '/') THEN RETURN TRUE
ELSIF (oper = '%') OR Strings.StrEq (token, 'MOD') THEN RETURN TRUE
ELSE
RETURN FALSE
END
END TermOperator;
PROCEDURE Term;
VAR Operator : CHAR;
BEGIN
Factor;
GetSymbol;
WHILE TermOperator () DO
CASE Operator OF
'.', 'x', '*' : op := 'MUL' |
':', '/' : op := 'DIV'
ELSE
op := 'MOD'
END;
GetSymbol;
Factor;
GetSymbol
END
END Term;
PROCEDURE Factor;
BEGIN
IF FindSymbol (token) = TRUE THEN
IF (thisSymbol^.type = Constype) OR (thisSymbol^.type = Vartype) THEN op := 'PUSH' END
ELSIF isNumber (token) THEN op := 'PUSH'
ELSIF token [0] = '(' THEN
GetSymbol;
Expression;
IF token [0] # ')' THEN ErrorMessage (16) END
ELSE
ErrorMessage (15) (* Error in Factor *)
END
END Factor;
PROCEDURE Init;
VAR count : SHORTCARD;
BEGIN
Arguments.GetArgs (count, buffer);
IF count = 1 THEN
ErrorMessage (33);
HALT
END;
TextIO.OpenInput (inFile, buffer^[1]^);
IF NOT TextIO.Done () THEN
ErrorMessage (34);
HALT
END;
MemPools.NewPool (Symbols);
MemPools.PoolAllocate (Symbols, firstSymbol, SYSTEM.TSIZE (SymbolNode));
WITH firstSymbol^ DO
Name := "|";
next := NIL;
previous := NIL
END;
currentType := Keytype;
IF StoreSymbol ("PROGRAM") = FALSE THEN ErrorMessage (0) END;
IF StoreSymbol ("LOOP") = FALSE THEN ErrorMessage (0) END;
IF StoreSymbol ("EXIT") = FALSE THEN ErrorMessage (0) END;
IF StoreSymbol ("BEGIN") = FALSE THEN ErrorMessage (0) END;
IF StoreSymbol ("END") = FALSE THEN ErrorMessage (0) END;
IF StoreSymbol ("IF") = FALSE THEN ErrorMessage (0) END;
IF StoreSymbol ("THEN") = FALSE THEN ErrorMessage (0) END;
IF StoreSymbol ("ELSIF") = FALSE THEN ErrorMessage (0) END;
IF StoreSymbol ("ELSE") = FALSE THEN ErrorMessage (0) END;
IF StoreSymbol ("ODD") = FALSE THEN ErrorMessage (0) END;
IF StoreSymbol ("OR") = FALSE THEN ErrorMessage (0) END;
IF StoreSymbol ("AND") = FALSE THEN ErrorMessage (0) END;
IF StoreSymbol ("NOT") = FALSE THEN ErrorMessage (0) END;
IF StoreSymbol ("MOD") = FALSE THEN ErrorMessage (0) END;
IF StoreSymbol ("IRQ") = FALSE THEN ErrorMessage (0) END;
IF StoreSymbol ("STATIC") = FALSE THEN ErrorMessage (0) END;
IF StoreSymbol ("RETURN") = FALSE THEN ErrorMessage (0) END;
IF StoreSymbol ("PROCEDURE") = FALSE THEN ErrorMessage (0) END;
IF StoreSymbol ("CONSTANTS") = FALSE THEN ErrorMessage (0) END;
IF StoreSymbol ("VARIABLES") = FALSE THEN ErrorMessage (0) END;
currentType := Constype;
IF StoreSymbol ("TRUE") = FALSE THEN ErrorMessage (0) ELSE thisSymbol^.value := 1 END;
IF StoreSymbol ("FALSE") = FALSE THEN ErrorMessage (0) ELSE thisSymbol^.value := 0 END;
currentType := Progtype;
LOOPdepth := 0;
PROCdepth := 0;
Exhausted := FALSE;
END Init;
PROCEDURE ShutDown;
BEGIN
TextIO.Close (inFile);
InOut.WriteLn; InOut.WriteLn; PrintNames;
MemPools.KillPool (Symbols)
END ShutDown;
BEGIN
Init;
Program;
ShutDown
END Plov005.
The compiler runs all the test programs that were published earlier. Although I did not check all of them.
Page created on 26 November 2007 and
Page equipped with FroogleBuster technology