Plov013 : Enhance the error checker

Previous versions did have some kind of syntax checker. As soon as one popped up, the program would end up in an infinite loop and that was the sign... By this time I got a bit bored of this and decided to give it a closer look. The best thing to do is make a construct similar to what Mocka does. Yet, this would take some more time and there are PLOV issues that I want to settle first. Below is a diff list of the most important changes applied to Plov.

Plov013 : Changes

In order to streamline errorchecking, a new ReadString procedure was necessary. So I started with making my own GetChar function. Nothing fancy, but it makes searching for EOL tokens easier. And it enables me to add a line/column counter for the error indicator.

PROCEDURE GetChar (VAR  ch	: CHAR);

BEGIN
   TextIO.GetChar (inFile, ch);
   IF  ch = 11C  THEN
      INC (Xpos, 8)
   ELSE
      INC (Xpos)
   END;
   IF  DebugMode  THEN  InOut.Write (ch)  END;
   IF  ch = ASCII.LF  THEN
      INC (line);
      Xpos := 0;
      pastEOL := TRUE
   END
END GetChar;
   
DebugMode is a constant defined in the header section. 11C of course is the Modula-2 notation for ASCII.TAB. The rest is rather straighforward. If you came this far on this website I need not explain in detail why I chose these lines of prose.

Here comes the new ReadString:

PROCEDURE ReadString (VAR  str : Identifier);

VAR	n, max		: CARDINAL;
	ch		: CHAR;

BEGIN
   n := 0;		max := HIGH (str);
   pastEOL := FALSE;
   REPEAT  GetChar (ch)  UNTIL  ch > ' ';
   REPEAT
      str [n] := ch;
      INC (n);
      GetChar (ch)
   UNTIL  (ch < '!') OR (n > max);
   lastCH := ch;
   IF  n < max  THEN  str [n] := 0C  END;
END ReadString;
   
It's a lot smaller than the previous one. Tedious linefeed checking has completely been removed. And now that we have a decent character getter, we can also make a lineskipper:
PROCEDURE Skip2LF;

VAR	ch	: CHAR;

BEGIN
   REPEAT  GetChar (ch)  UNTIL  ch = ASCII.LF
END Skip2LF;
   
Here is the new ErrorMessage function. I removed the bulk of the messages since they are just makeup.
CONST	ErrorMax	= 5;

PROCEDURE ErrorMessage (n  : CARDINAL);

BEGIN
   INC (ErrCount);
   InOut.WriteLn;		InOut.WriteString ("@@  -- ");
   CASE  n  OF
      0 : InOut.WriteString ("Error filling Symbol table")		|
      .
      .
      .
     34 : InOut.WriteString ("Cannot open file '");
     	  InOut.WriteString (buffer^[1]^);	InOut.Write ("'")
   ELSE
      InOut.WriteString ("Unknown error detected.")
   END;
   InOut.WriteString (" in line ");	InOut.WriteCard (line, 5);	InOut.WriteLn;
   InOut.WriteBf;
   IF  ErrCount = ErrorMax  THEN  
      ShutDown;
      HALT
   END
END ErrorMessage;
   
Now I can filter until (in this case) five error messages have been issued and then abort. After 5 messages things turn out in a mess most of the times, depending on where the previous error were. If a WHILE has been mis spelled as WHLIE, the corresponding END just comes out of the blue, causing new (but false) error messages so it's not wrong to put the compiler out of its misery.
The newline is added, just like the errormessage prefix string (@@ -- ) for easier finding back in files or on screen. At the end of the message, a line is added with information about the position of the error.

Below is the new StatementSequence procedure:

PROCEDURE StatementSequence;

BEGIN
   GetSymbol;
   LOOP
      CG ("");
      IF     findType (token) = Vartype 	THEN  Assignment
      ELSIF  Strings.StrEq (token, "ELSIF")	THEN  RETURN
      .
      .
      .
      ELSIF  Strings.StrEq (token, "ELSE")	THEN  RETURN
      ELSE
	 Skip2LF;
	 ErrorMessage (10);		(* Error in StatementSequence	*)
	 GetSymbol
      END
   END
END StatementSequence;
   
If an error is found, the rest of the line is skipped, a message is sent out and the new symbol is collected for a new journey around the LOOP. This works quite well.

In 'Assignment' something similar was done. After the errormessage, the rest of the line is skipped. This is the only sensible thing to do, since the Assignment-scanner is a statemachine and it already is in the WRONG state... So I skip the line and get back to 'StatementSequence' (with the RETURN) leaving possible further errors in this line to the next compilerrun.

PROCEDURE Assignment;

BEGIN
   CG ("ADDRESS ");
   IF  weHaveLocals AND (FindLocal (token) = TRUE)  THEN  CG ("LOCAL ")  END;
   CG (token);			CG ("");
   pastEOL := FALSE;
   GetSymbol;
   IF  Strings.StrEq (token, ':=') = FALSE  THEN
      ErrorMessage (17);		(* Missing ':=' 	*)
      Skip2LF;
      GetSymbol;
      RETURN
   END;
   GetSymbol;
   Expression;
   CG ("SAVE");		CG ("")
END Assignment;
   

Plov013 : see it run

Here's a messy source:

PROGRAP testt

CONSTANT a = 1 END

VARIABLE 1 2 ab END

PROCEDURE hhhhhhh

LOCAL aa ss 1a END

BEGIN
  aa := ss + 22
  aa : 0
END hhhhhhh

BEGIN
  aa := a + 1
END testt
   
See how it compiles:
PROGRAP 
@@  -- PROGRAM expected in line     1
   
That was quick. See how things change when I fix this error:
PROGRAM testt

CONSTANT 
@@  -- BEGIN expected in line     3
a = 1 END

@@  -- Undefined symbol or keyword in line     4

VARIABLE 1 2 ab END

@@  -- Undefined symbol or keyword in line     6

PROCEDURE hhhhhhh

@@  -- Undefined symbol or keyword in line     8

LOCAL aa ss 1a END

@@  -- Undefined symbol or keyword in line    10
   
After five error messages the compiler called it quits. Let's fix this error as well. Still, this silly error makes me wonder: would it be feasible to have the compiler hint what the programmer was after? Could Plov find out that this presumably was a misspelled CONSTANTS keyword?
PROGRAM testt

CONSTANTS a = 1 END

VARIABLE 
@@  -- BEGIN expected in line     5
1 2 ab END

@@  -- Undefined symbol or keyword in line     6

PROCEDURE hhhhhhh

@@  -- Undefined symbol or keyword in line     8

LOCAL aa ss 1a END

@@  -- Undefined symbol or keyword in line    10

BEGIN
  aa := ss + 22

@@  -- Undefined symbol or keyword in line    13
   
The statemachine got stateless... Still, after the most silly errors have been removed, the syntax checker behaves rather well. I'm kind of proud about this:
PROGRAM testt

CONSTANTS a = 1 END

VARIABLES 1 
@@  -- Illegal identifier name : 1 in line     5
2 
@@  -- Illegal identifier name : 2 in line     5
ab END

PROCEDURE hhhhhhh

LOCAL aa ss 1a 
@@  -- Illegal identifier name : 1a in line     9
END

BEGIN
  aa := ss + 22
  aa : 
@@  -- Missing assigment operator in line    13
0
END hhhhhhh

BEGIN
  aa := a + 1
END testt
   
It reported five errors and then stopped. Error 6 is still in. Let's see if this will be found as well:
PROGRAM testt

CONSTANTS a = 1 END

VARIABLES  ab END

PROCEDURE hhhhhhh

LOCAL aa ss END

BEGIN
  aa := ss + 22
  aa := 0
END hhhhhhh

BEGIN
  aa := a + 1

@@  -- Undefined symbol or keyword in line    18
END testt
   
Not bad!
PROGRAM testt

CONSTANTS a = 1 END

VARIABLES  ab END

PROCEDURE hhhhhhh

LOCAL aa ss END

BEGIN
  aa := ss + 22
  aa := 0
END hhhhhhh

BEGIN
  ab := a + 1
END testt
   
Yes! No errors anymore!

Page created on 14 September 2008 and

Page equipped with FroogleBuster technology