5.1. The Plan

In what follows, we'll be starting over again with a bare cradle, and as we've done twice before now, we'll build things up one at a time. We'll also be retaining the concept of single-character tokens that has served us so well to date. This means that the “code” will look a little funny, with i for IF, w for WHILE, etc. But it helps us get the concepts down pat without fussing over lexical scanning. Fear not … eventually we'll see something looking like real code.

I also don't want to have us get bogged down in dealing with statements other than branches, such as the assignment statements we've been working on. We've already demonstrated that we can handle them, so there's no point carrying them around as excess baggage during this exercise. So what I'll do instead is to use an anonymous statement, “other”, to take the place of the non-control statements and serve as a place-holder for them. We have to generate some kind of object code for them (we're back into compiling, not interpretation), so for want of anything else I'll just echo the character input.

OK, then, starting with yet another copy of the cradle, let's define the procedure:

{ Recognize and Translate an "Other" }
procedure Other;
begin
   EmitLn(GetName);
end;

Now include a call to it in the main program, thus:

{ Main Program }
begin
   Init;
   Other;
end.

Run the program and see what you get. Not very exciting, is it? But hang in there, it's a start, and things will get better.

The first thing we need is the ability to deal with more than one statement, since a single-line branch is pretty limited. We did that in the last session on interpreting, but this time let's get a little more formal. Consider the following BNF:

<program> ::= <block> END
<block> ::= [ <statement> ]*

This says that, for our purposes here, a program is defined as a block, followed by an END statement. A block, in turn, consists of zero or more statements. We only have one kind of statement, so far.

What signals the end of a block? It's simply any construct that isn't an “other” statement. For now, that means only the END statement.

Armed with these ideas, we can proceed to build up our parser. The code for a program (we have to call it DoProgram, or Pascal will complain, is:

{ Parse and Translate a Program }
procedure DoProgram;
begin
   Block;
   if Look <> 'e' then Expected('End');
   EmitLn('END')
end;

Notice that I've arranged to emit an END command to the assembler, which sort of punctuates the output code, and makes sense considering that we're parsing a complete program here.

The code for Block is:

{ Recognize and Translate a Statement Block }
procedure Block;
begin
   while not(Look in ['e']) do begin
      Other;
   end;
end;

Note

From the form of the procedure, you just know we're going to be adding to it in a bit!

OK, enter these routines into your program. Replace the call to Block in the main program, by a call to DoProgram. Now try it and see how it works. Well, it's still not much, but we're getting closer.