Let's build a compiler! (4) --- Part 3: Reproduction
Gum character
Before this chapter, let's discuss the problem of blank characters. Now this version of the analyzer will stop in a place to read a blank character. This is quite unfriendly behavior. So let us eliminate this last restriction, so that the performance of the analyzer has the taste of commercial products.
The key to handling blank characters is to develop a rule that specifies how the analyzer changes how to handle the blank characters to be entered and follow it in the entire analyzer. So far, blank characters are still not allowed, we can assume that the first character look contains meaningful characters after each analytical action, and then verify this. Our way to design is based on the above principles.
Specifically, each process that needs to be read first, must ignore a blank character, and only non-blank characters can only be retained in the last Look variable. Fortunately, we have completed most of the input operations with GetName, GetNum and Match, so only three processes (plus init processes) need to be modified.
First, define a new process of identifying blank characters.
{------------------------------------- -------------} {Recognize White Space}
Function iswhite (c: char): boolean; begin iswhite: = c in ['', tab]; end; {---------------------------------------------------------------------------------------------------- --------------------------------------}
There is also a process "Eat" blank character until you meet a non-empty character.
{------------------------------------- -------------} {Skip over Leading White Space}
Procedure Skipwhite; Begin While Iswhite (LOOK) Do GetChar; End; {------------------------------------ ---------------------------}
Now, call SkipWhite during match, getname, and getnum:
{------------------------------------- -------------} {match a specific input character}
Procedure match (x: char); Begin if Look <> x THEN EXPECTED ('' ' x ' '') else begin getchar; skipwhite; end;
{------------------------------------- -------------} {Get An Identifier}
Function getname: string; var token: string; begin token: = '; if not isalpha (look) THEN EXPECTED (' Name '); While Isalnum (LOOK) Do Begin token: = Token Upcase (LOOK); GetChar; Getname: = token; skipwhite;
{------------------------------------- -------------} {Get a Number} Function GetNum: String; Value: String; Begin Value: = '; if Not isdigit (Look) THEN EXPECTED (' Integer '); While Isdigit (Look) Do Begin Value: = Value Look; getChar; End; GetNum: = Value; Skipwhite; End; {-------------------------------------------------------------------------------------------------- ----------------------------------------}
(Note, I have reorganized the Match process, but did not change its function.)
Finally, it is necessary to slightly skew the blank character at the beginning of the file.
{------------------------------------- -------------} {initialize}
Skipwhite; skipwhite; end; {---------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------}
If you override the program and compile it, pay attention to put the match process behind the SkipWhite, otherwise it is completed. Test this new version to ensure that it can run normally.
Since we have made a lot of modifications to the original procedures, now I will list the modified full programs below:
{------------------------------------- -------------} Program Parse;
{------------------------------------- -------------} {Constant Declarations}
Const tab = ^ i; cr = ^ m;
{------------------------------------- -------------} {Variable Declarations}
Var Look: char; {lookahead character}
{------------------------------------- -------------} {read new character from input stream}
Procedure getchar; begin read (look);
{------------------------------------- -------------} {report an error}
Procedure error (s: string); begin writeln; Writeln (^ g, 'error:', s, '.');
{------------------------------------- -------------} {report error and halt} procedure Abort; begin error (s); halt; end;
{------------------------------------- -------------} {report what was spected} procedure spected; begin Abort (s 'expected');
{------------------------------------- -------------} {recognize an alpha character}
Function isalpha (c: char): boolean; begin isalpha: = Upcase (c) in ['a' .. 'z'];
{------------------------------------- -------------} {Recognize a decimal Digit}
Function isdigit (c: char): boolean; begin isdigit: = c in ['0' .. '9'];
{------------------------------------- -------------} {recognize an alphaumeric}
Function isalnum (c: char): boolean; begin isalynum: = isalpha (c) or isdigit (c);
{------------------------------------- -------------} {recognize an addop}
Function isaddop (C: char): boolean; begin isaddop: = C in [' ', '-']; end;
{------------------------------------- -------------} {recognize white space} Function iswhite (c: char): boolean; begin iswhite: = c in ['', tab];
{------------------------------------- -------------} {Skip over Leading White Space}
Procedure Skipwhite; Begin While Iswhite (LOOK) Do GetChar; End;
{------------------------------------- -------------} {match a specific input character}
Procedure match (x: char); Begin if Look <> x THEN EXPECTED ('' ' x ' '') else begin getchar; skipwhite; end;
{------------------------------------- -------------} {Get An Identifier}
Function getname: string; var token: string; begin token: = '; if not isalpha (look) THEN EXPECTED (' Name '); While Isalnum (LOOK) Do Begin token: = Token Upcase (LOOK); GetChar; End; getname: = token; skipwhite; end; {------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ------------------------} {Get a Number}
Function GetNum: string; var value: string; begin value: = '; if not isdigit (LOOK); While Isdigit (Look) Do Begin Value: = Value Look; GetChar; End; GetNum = Value; Skipwhite; end;
{------------------------------------- -------------} {Output A String with Tab}
Procedure Emit (S: String); Begin Write (Tab, S); END;
{------------------------------------- -------------} {Output A String with Tab and CRLF}
Procedure Emitln (S: String); Begin Emit (s); Writeln; End;
{------------------------------------- --------------} {Parse and Translate A Identifier}
Procedure Ident; Var name: String [8]; begin name: = getName; if Look = '('); match (')'); ELSE ('BSR' Name); ELSE Emitln ('Move' Name '(PC), D0');
{------------------------------------- --------------} {PARSE AND TRANSLATE A MATH FACTOR}
PROCEDURE Expression; Forward;
Procedure factor; begin if Look = '('); expression; match (')'); Else if Isalpha (LOOK) THEN IDENT ELSE Emitln ('Move #' GetNum , D0 ');
{------------------------------------- -------------} {recognize and translate a multiply} procedure multiply; begin match ('*'); factor; Emitln ('Muls (SP) , D0');
{------------------------------------- ------------} {recognize and translate a divide}
Procedure Divide; Begin Match ('/'); Factor; Emitln ('Move (SP) , D1'); Emitln ('EXS.L D0'); Emitln ('Divs D1, D0');
{------------------------------------- --------------} {PARSE AND TRANSLATE A Math Term}
Procedure Term; Begin Factor; While Look IN ['*', '/'] Do Begin Emitln ('Move D0, - (SP)'); Case Look of '*': Multiply; '/': Divide; End; END;
{------------------------------------- -------------} {recognize and translate an add}
Procedure add; begin match (' '); Term; Emitln ('Add (SP) , D0');
{------------------------------------- ------------} {recognize and translate a Subtract}
Procedure subtract; begin match ('-'); Term; Emitln ('SUB (SP) , D0'); Emitln ('NEG D0');
{------------------------------------- --------------} {PARSE AND TRANSLATE AN Expression}
Procedure expression; begin if isaddop (LOOK) THEN EMITLN ('CLR D0') Else Term; While Isaddop (Look) Do Begin Emitln ('Move D0, - (SP)'); Case Look Of ' ': add; ' - ': subtract; end; end;
{------------------------------------- -------------} {Parse and Translate An Assignment Statement}
Procedure assignment; var name: String [8]; begin name: = getName; match ('='); expression; Emitln ('Lea' Name '(PC), A0'); Emitln ('Move D0, A0) ') End; {----------------------------------------- -------------------} {Initialize} procedure init;
{------------------------------------- -------------} {main program}
BEGIN INIT; if Look <> Cr THEN EXPECTED ('newline'); end. {----------------------------- ---------------------------------}
It is already a complete analyzer. It already has a "row compiler" all features. It is recommended that you save it. Next time we will turn to a new topic, but still talk about a problem. Next chapter, I plan to introduce some knowledge about the interpreter and show how its structure is constrained when we change the action of the analyzer. We will use after we have learned in the next chapter, even if you are not interested in the interpreter. See the next chapter