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