master e84e5a5bf33c cached
76 files
799.6 KB
195.8k tokens
666 symbols
1 requests
Download .txt
Showing preview only (830K chars total). Download the full file or copy to clipboard to get everything.
Repository: lotabout/Let-s-build-a-compiler
Branch: master
Commit: e84e5a5bf33c
Files: 76
Total size: 799.6 KB

Directory structure:
gitextract_t5qe27_o/

├── .gitignore
├── 1/
│   ├── cradle.c
│   ├── cradle.h
│   └── tutor1.txt
├── 10/
│   ├── Makefile
│   ├── cradle.c
│   ├── cradle.h
│   ├── main.c
│   ├── prog.txt
│   └── tutor10.txt
├── 11/
│   ├── Makefile
│   ├── cradle.c
│   ├── cradle.h
│   ├── main.c
│   ├── prog.txt
│   └── tutor11.txt
├── 12/
│   ├── Makefile
│   ├── cradle.c
│   ├── cradle.h
│   ├── main.c
│   ├── prog.txt
│   └── tutor12.txt
├── 13/
│   ├── Makefile
│   ├── cradle.c
│   ├── cradle.h
│   ├── main.c
│   ├── prog.txt
│   └── tutor13.txt
├── 14/
│   ├── Makefile
│   ├── README.md
│   ├── cradle.c
│   ├── cradle.h
│   ├── main.c
│   ├── prog.txt
│   └── tutor14.txt
├── 15/
│   └── tutor15.txt
├── 16/
│   └── tutor16.txt
├── 2/
│   ├── Makefile
│   ├── cradle.c
│   ├── cradle.h
│   ├── main.c
│   └── tutor2.txt
├── 3/
│   ├── Makefile
│   ├── cradle.c
│   ├── cradle.h
│   ├── main.c
│   └── tutor3.txt
├── 4/
│   ├── Makefile
│   ├── cradle.c
│   ├── cradle.h
│   ├── main.c
│   └── tutor4.txt
├── 5/
│   ├── Makefile
│   ├── README.md
│   ├── cradle.c
│   ├── cradle.h
│   ├── main.c
│   └── tutor5.txt
├── 6/
│   ├── Makefile
│   ├── cradle.c
│   ├── cradle.h
│   ├── main.c
│   └── tutor6.txt
├── 7/
│   ├── Makefile
│   ├── cradle.c
│   ├── cradle.h
│   ├── main.c
│   └── tutor7.txt
├── 8/
│   └── tutor8.txt
├── 9/
│   ├── Makefile
│   ├── cradle.c
│   ├── cradle.h
│   ├── main.c
│   └── tutor9.txt
├── README.md
└── get_chapters.sh

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
*.s
*.o
*.so
.~
*.swp
*.out
*.pdf
*~


================================================
FILE: 1/cradle.c
================================================
#include "cradle.h"
#include <stdio.h>
#include <stdlib.h>


void GetChar() 
{
    Look = getchar();
}


void Error(char *s)
{
    printf("\nError: %s.", s);
}

void Abort(char *s)
{
    Error(s);
    exit(1);
}


void Expected(char *s)
{
    sprintf(tmp, "%s Expected", s);
    Abort(tmp);
}


void Match(char x)
{
    if(Look == x) {
        GetChar();
    } else {
        sprintf(tmp, "' %c ' ",  x);
        Expected(tmp);
    }
}


int IsAlpha(char c)
{
    return (UPCASE(c) >= 'A') && (UPCASE(c) <= 'Z');
} 

int IsDigit(char c)
{
    return (c >= '0') && (c <= '9');
}


char GetName()
{
    char c = Look;

    if( !IsAlpha(Look)) {
        sprintf(tmp, "Name");
        Expected(tmp);
    }

    GetChar();

    return UPCASE(c);
}


char GetNum()
{
    char c = Look;

    if( !IsDigit(Look)) {
        sprintf(tmp, "Integer");
        Expected(tmp);
    }

    GetChar();

    return c;
}

void Emit(char *s)
{
    printf("\t%s", s);
}

void EmitLn(char *s)
{
    Emit(s);
    printf("\n");
}

void Init()
{
    GetChar();
}



================================================
FILE: 1/cradle.h
================================================
#ifndef _CRADLE_H
#define _CRADLE_H

#define UPCASE(C) (~(1<<5) & (C))
#define MAX_BUF 100

static char tmp[MAX_BUF];

char Look;

void GetChar();

void Error(char *s);
void Abort(char *s);
void Expected(char *s);
void Match(char x);

int IsAlpha(char c);
int IsDigit(char c);

char GetName();
char GetNum();

void Emit(char *s);
void EmitLn(char *s);

void Init();

#endif


================================================
FILE: 1/tutor1.txt
================================================



























                            LET'S BUILD A COMPILER!

                                       By

                            Jack W. Crenshaw, Ph.D.

                                  24 July 1988


                              Part I: INTRODUCTION


*****************************************************************
*                                                               *
*                        COPYRIGHT NOTICE                       *
*                                                               *
*   Copyright (C) 1988 Jack W. Crenshaw. All rights reserved.   *
*                                                               *
*****************************************************************


INTRODUCTION


This series of articles is a tutorial on the theory  and practice
of  developing language parsers and compilers.    Before  we  are
finished,  we  will  have  covered  every   aspect   of  compiler
construction, designed a new programming  language,  and  built a
working compiler.

Though I am not a computer scientist by education (my Ph.D. is in
a different  field, Physics), I have been interested in compilers
for many years.  I have  bought  and tried to digest the contents
of virtually every  book  on  the  subject ever written.  I don't
mind  telling you that it was slow going.    Compiler  texts  are
written for Computer  Science  majors, and are tough sledding for
the rest of us.  But over the years a bit of it began to seep in.
What really caused it to jell was when I began  to  branch off on
my own and begin to try things on my own computer.  Now I plan to
share with you what I have  learned.    At the end of this series
you will by no means be  a  computer scientist, nor will you know
all the esoterics of  compiler  theory.    I intend to completely
ignore the more theoretical  aspects  of  the  subject.  What you
_WILL_ know is all  the  practical aspects that one needs to know
to build a working system.

This is a "learn-by-doing" series.  In the course of the series I
will be performing  experiments  on  a  computer.    You  will be
expected to follow along,  repeating  the  experiments that I do,
and  performing  some  on your own.  I will be using Turbo Pascal
4.0 on a PC  clone.   I will periodically insert examples written
in TP.  These will be executable code, which you will be expected
to copy into your own computer and run.  If you don't have a copy
of  Turbo,  you  will be severely limited in how well you will be
able to follow what's going on.  If you don't have a copy, I urge
you to get one.  After  all,  it's an excellent product, good for
many other uses!

Some articles on compilers show you examples, or show you  (as in
the case of Small-C) a finished product, which you can  then copy
and  use without a whole lot of understanding of how it works.  I
hope to do much more  than  that.    I  hope to teach you HOW the
things get done,  so that you can go off on your own and not only
reproduce what I have done, but improve on it.
                              
This is admittedly an ambitious undertaking, and it won't be done
in  one page.  I expect to do it in the course  of  a  number  of
articles.    Each  article will cover a single aspect of compiler
theory,  and  will  pretty  much  stand  alone.   If  all  you're
interested in at a given time is one  aspect,  then  you  need to
look only at that one article.  Each article will be  uploaded as
it  is complete, so you will have to wait for the last one before
you can consider yourself finished.  Please be patient.



The average text on  compiler  theory covers a lot of ground that
we won't be covering here.  The typical sequence is:

 o An introductory chapter describing what a compiler is.

 o A chapter or two on syntax equations, using Backus-Naur Form
   (BNF).

 o A chapter or two on lexical scanning, with emphasis on
   deterministic and non-deterministic finite automata.

 o Several chapters on parsing theory, beginning with top-down
   recursive descent, and ending with LALR parsers.

 o A chapter on intermediate languages, with emphasis on P-code
   and similar reverse polish representations.

 o Many chapters on alternative ways to handle subroutines and
   parameter passing, type declarations, and such.

 o A chapter toward the end on code generation, usually for some
   imaginary CPU with a simple instruction set.  Most readers
   (and in fact, most college classes) never make it this far.

 o A final chapter or two on optimization. This chapter often
   goes unread, too.


I'll  be taking a much different approach in  this  series.    To
begin  with,  I  won't dwell long on options.  I'll be giving you
_A_ way that works.  If you want  to  explore  options,  well and
good ...  I  encourage  you  to do so ... but I'll be sticking to
what I know.   I also will skip over most of the theory that puts
people  to  sleep.  Don't get me  wrong:  I  don't  belittle  the
theory, and it's vitally important  when it comes to dealing with
the more tricky  parts  of  a  given  language.  But I believe in
putting first things first.    Here we'll be dealing with the 95%
of compiler techniques that don't need a lot of theory to handle.

I  also  will  discuss only one approach  to  parsing:  top-down,
recursive descent parsing, which is the  _ONLY_  technique that's
at  all   amenable  to  hand-crafting  a  compiler.    The  other
approaches are only useful if you have a tool like YACC, and also
don't care how much memory space the final product uses.
                              
I  also take a page from the work of Ron Cain, the author of  the
original Small C.  Whereas almost all other compiler authors have
historically  used  an  intermediate  language  like  P-code  and
divided  the  compiler  into two parts (a front end that produces
P-code,  and   a  back  end  that  processes  P-code  to  produce
executable   object  code),  Ron  showed  us   that   it   is   a
straightforward  matter  to  make  a  compiler  directly  produce
executable  object  code,  in  the  form  of  assembler  language
statements.  The code will _NOT_ be the world's tightest code ...
producing optimized code is  a  much  more  difficult job. But it
will work, and work reasonably well.  Just so that I  don't leave
you with the impression that our end product will be worthless, I
_DO_ intend to show you how  to  "soup up" the compiler with some
optimization.



Finally, I'll be  using  some  tricks  that I've found to be most
helpful in letting  me  understand what's going on without wading
through a lot of boiler plate.  Chief among these  is  the use of
single-character tokens, with no embedded spaces,  for  the early
design work.  I figure that  if  I  can get a parser to recognize
and deal with I-T-L, I can  get  it  to do the same with IF-THEN-
ELSE.  And I can.  In the second "lesson,"   I'll  show  you just
how easy it  is  to  extend  a  simple parser to handle tokens of
arbitrary length.  As another  trick,  I  completely  ignore file
I/O, figuring that  if  I  can  read source from the keyboard and
output object to the screen, I can also do it from/to disk files.
Experience  has  proven  that  once  a   translator   is  working
correctly, it's a  straightforward  matter to redirect the I/O to
files.    The last trick is that I make no attempt  to  do  error
correction/recovery.   The   programs   we'll  be  building  will
RECOGNIZE errors, and will not CRASH, but they  will  simply stop
on the first error ... just like good ol' Turbo does.  There will
be  other tricks that you'll see as you go. Most of them can't be
found in any compiler textbook, but they work.

A word about style and efficiency.    As  you will see, I tend to
write programs in  _VERY_  small, easily understood pieces.  None
of the procedures we'll  be  working with will be more than about
15-20 lines long.  I'm a fervent devotee  of  the  KISS  (Keep It
Simple, Sidney) school of software development.  I  try  to never
do something tricky or  complex,  when  something simple will do.
Inefficient?  Perhaps, but you'll like the  results.    As  Brian
Kernighan has said,  FIRST  make  it  run, THEN make it run fast.
If, later on,  you want to go back and tighten up the code in one
of  our products, you'll be able to do so, since the code will be
quite understandable. If you  do  so, however, I urge you to wait
until the program is doing everything you want it to.

I  also  have  a  tendency  to  delay  building  a module until I
discover that I need  it.    Trying  to anticipate every possible
future contingency can  drive  you  crazy,  and  you'll generally
guess wrong anyway.    In  this  modern day of screen editors and
fast compilers, I don't hesitate to change a module when I feel I
need a more powerful one.  Until then,  I'll  write  only  what I
need.

One final caveat: One of the principles we'll be sticking to here
is that we don't  fool  around with P-code or imaginary CPUs, but
that we will start out on day one  producing  working, executable
object code, at least in the form of  assembler  language source.
However, you may not  like  my  choice  of assembler language ...
it's 68000 code, which is what works on my system (under SK*DOS).
I  think  you'll  find, though, that the translation to any other
CPU such as the 80x86 will  be  quite obvious, though, so I don't
see  a problem here.  In fact, I hope someone out there who knows
the '86 language better than I do will offer  us  the  equivalent
object code fragments as we need them.


THE CRADLE

Every program needs some boiler  plate  ...  I/O  routines, error
message routines, etc.   The  programs we develop here will be no
exceptions.    I've  tried to hold  this  stuff  to  an  absolute
minimum, however, so that we  can  concentrate  on  the important
stuff without losing it  among  the  trees.  The code given below
represents about the minimum that we need to  get  anything done.
It consists of some I/O routines, an error-handling routine and a
skeleton, null main program.   I  call  it  our  cradle.    As we
develop other routines, we'll add them to the cradle, and add the
calls to them as we  need to.  Make a copy of the cradle and save
it, because we'll be using it more than once.

There are many different ways to organize the scanning activities
of  a  parser.   In Unix systems, authors tend to  use  getc  and
ungetc.  I've had very good luck with the  approach  shown  here,
which is to use  a  single, global, lookahead character.  Part of
the initialization procedure  (the  only part, so far!) serves to
"prime  the  pump"  by reading the first character from the input
stream.  No other special  techniques are required with Turbo 4.0
... each successive call to  GetChar will read the next character
in the stream.


{--------------------------------------------------------------}
program Cradle;

{--------------------------------------------------------------}
{ Constant Declarations }

const TAB = ^I;

{--------------------------------------------------------------}
{ Variable Declarations }

var Look: char;              { Lookahead Character }
                              
{--------------------------------------------------------------}
{ Read New Character From Input Stream }

procedure GetChar;
begin
   Read(Look);
end;

{--------------------------------------------------------------}
{ Report an Error }

procedure Error(s: string);
begin
   WriteLn;
   WriteLn(^G, 'Error: ', s, '.');
end;


{--------------------------------------------------------------}
{ Report Error and Halt }

procedure Abort(s: string);
begin
   Error(s);
   Halt;
end;


{--------------------------------------------------------------}
{ Report What Was Expected }

procedure Expected(s: string);
begin
   Abort(s + ' Expected');
end;

{--------------------------------------------------------------}
{ Match a Specific Input Character }

procedure Match(x: char);
begin
   if Look = x then GetChar
   else Expected('''' + x + '''');
end;


{--------------------------------------------------------------}
{ Recognize an Alpha Character }

function IsAlpha(c: char): boolean;
begin
   IsAlpha := upcase(c) in ['A'..'Z'];
end;
                              

{--------------------------------------------------------------}

{ Recognize a Decimal Digit }

function IsDigit(c: char): boolean;
begin
   IsDigit := c in ['0'..'9'];
end;


{--------------------------------------------------------------}
{ Get an Identifier }

function GetName: char;
begin
   if not IsAlpha(Look) then Expected('Name');
   GetName := UpCase(Look);
   GetChar;
end;


{--------------------------------------------------------------}
{ Get a Number }

function GetNum: char;
begin
   if not IsDigit(Look) then Expected('Integer');
   GetNum := Look;
   GetChar;
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;

{--------------------------------------------------------------}
{ Initialize }

procedure Init;
begin
   GetChar;
end;


{--------------------------------------------------------------}
{ Main Program }

begin
   Init;
end.
{--------------------------------------------------------------}


That's it for this introduction.  Copy the code above into TP and
compile it.  Make sure that it compiles and runs  correctly. Then
proceed to the first lesson, which is on expression parsing.


*****************************************************************
*                                                               *
*                        COPYRIGHT NOTICE                       *
*                                                               *
*   Copyright (C) 1988 Jack W. Crenshaw. All rights reserved.   *
*                                                               *
*****************************************************************






================================================
FILE: 10/Makefile
================================================
IN=main.c cradle.c
OUT=main
FLAGS=-Wall -Werror

all:
	gcc -o $(OUT) $(IN) $(FLAGS)

run:
	./$(OUT)

.PHONY: clean
clean:
	rm $(OUT)


================================================
FILE: 10/cradle.c
================================================
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

#include "cradle.h"
#include <malloc.h>

#define MaxEntry 100
#define MAX_SYMBOL_LENGTH 10
static int LCount = 0;
static char labelName[MAX_BUF];
char tmp[MAX_BUF];

/*char ST[TABLE_SIZE];*/
static int NEntry = 0;
const char *ST[MaxEntry];
char SType[MaxEntry];


/* Keywords symbol table */
const char const *KWList[] = {
    "IF",
    "ELSE",
    "ENDIF",
    "WHILE",
    "ENDWHILE",
    "VAR",
    "BEGIN",
    "END",
    "PROGRAM"
};
const char KWCode[] = "xilewevbep";
const int KWNum = sizeof(KWList)/sizeof(*KWList);

char Token;             /* current token */
char Value[MAX_BUF];    /* string token of Look */

/* Helper Functions */
char uppercase(char c)
{
    if (IsAlpha(c)) {
        return (c & 0xDF);
    } else {
        return c;
    }
}

/* Table Lookup
 * If the input string matches a table entry, return the entry index, else
 * return -1.
 * *n* is the size of the table */
int Lookup(const char const *table[], const char *string, int n)
{
    int i;
    bool found = false;

    for (i = 0; i < n; ++i) {
        if (strcmp(table[i], string) == 0) {
            found = true;
            break;
        }
    }
    return found ? i : -1;
}

/* Add a new entry to symbol table */
void AddEntry(char *symbol, char type)
{
    if (InTable(symbol)) {
        sprintf(tmp, "Duplicate Identifier %s", symbol);
        Abort(tmp);
    }
    if (NEntry == MaxEntry) {
        Abort("Symbol Table Full");
    }

    char *new_entry = (char *)malloc((strlen(symbol)+1)*sizeof(*new_entry));
    if (new_entry == NULL) {
        Abort("AddEntry: not enough memory allocating new_entry.");
    }
    strcpy(new_entry, symbol);
    ST[NEntry] = new_entry;
    SType[NEntry] = type;

    NEntry++;
}

/* Get an Identifier and Scan it for keywords */
void Scan()
{
    GetName();
    int index = Lookup(KWList, Value, KWNum);
    Token = KWCode[index+1];
}

void MatchString(char *str)
{
    if (strcmp(Value, str) != 0) {
        sprintf(tmp, "\"%s\"", Value);
        Expected(tmp);
    }
}

void GetChar()
{
    Look = getchar();
    /* printf("Getchar: %c\n", Look); */
}


void Error(char *s)
{
    printf("\nError: %s.", s);
}

void Abort(char *s)
{
    Error(s);
    exit(1);
}


void Expected(char *s)
{
    sprintf(tmp, "%s Expected", s);
    Abort(tmp);
}


void Match(char x)
{
    NewLine();
    if(Look == x) {
        GetChar();
    } else {
        sprintf(tmp, "' %c ' ",  x);
        Expected(tmp);
    }
    SkipWhite();
}

int IsAlpha(char c)
{
    return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}

int IsDigit(char c)
{
    return (c >= '0') && (c <= '9');
}

int IsAddop(char c)
{
    return (c == '+') || (c == '-');
}

int IsMulop(char c)
{
    return (c == '*') || (c == '/');
}

int IsOrOp(char c)
{
    return strchr("|~", c) != NULL;
}

int IsRelop(char c)
{
    return strchr("=#<>", c) != NULL;
}

int IsWhite(char c)
{
    return strchr(" \t\r\n", c) != NULL;
}

int IsAlNum(char c)
{
    return IsAlpha(c) || IsDigit(c);
}

void GetName()
{
    NewLine();
    if( !IsAlpha(Look)) {
        Expected("Name");
    }

    char *p = Value;
    while(IsAlNum(Look)) {
        *p++ = uppercase(Look);
        GetChar();
    }
    *p = '\0';
    SkipWhite();
}


int GetNum()
{
    NewLine();
    int value = 0;
    if( !IsDigit(Look)) {
        sprintf(tmp, "Integer");
        Expected(tmp);
    }

    while (IsDigit(Look)) {
        value = value * 10 + Look - '0';
        GetChar();
    }

    SkipWhite();

    return value;
}

void Emit(char *s)
{
    printf("\t%s", s);
}

void EmitLn(char *s)
{
    Emit(s);
    printf("\n");
}

void Init()
{
    LCount = 0;

    InitTable();
    GetChar();
    Scan();
    SkipWhite();
}

void InitTable()
{
    int i;
    for (i = 0; i < MaxEntry; i++) {
        ST[i] = NULL;
        SType[i] = ' ';
    }

}

bool InTable(char *name)
{
    return Lookup(ST, name, NEntry) != -1;
}

char *NewLabel()
{
    sprintf(labelName, "L%02d", LCount);
    LCount ++;
    return labelName;
}

void PostLabel(char *label)
{
    printf("%s:\n", label);
}

void SkipWhite()
{
    while (IsWhite(Look)) {
        GetChar();
    }
}

/* Skip over an End-of-Line */
void NewLine()
{
    while(Look == '\n') {
        GetChar();
        if (Look == '\r') {
            GetChar();
        }
        SkipWhite();
    }
}

/* re-targetable routines */
void Clear()
{
    EmitLn("xor %eax, %eax");
}

void Negate()
{
    EmitLn("neg %eax");
}

void LoadConst(int n)
{
    sprintf(tmp, "movl $%d, %%eax", n);
    EmitLn(tmp);
}

/* Load a variable to primary register */
void LoadVar(char *name)
{
    if (!InTable(name)) {
        char name_string[MAX_BUF];
        Undefined(name_string);
    }
    sprintf(tmp, "movl %s, %%eax", name);
    EmitLn(tmp);
}


/* Push Primary onto stack */
void Push()
{
    EmitLn("pushl %eax");
}

/* Add Top of Stack to primary */
void PopAdd()
{
    EmitLn("addl (%esp), %eax");
    EmitLn("addl $4, %esp");
}

/* Subtract Primary from Top of Stack */
void PopSub()
{
    EmitLn("subl (%esp), %eax");
    EmitLn("neg %eax");
    EmitLn("addl $4, %esp");
}

/* multiply top of stack by primary */
void PopMul()
{
    EmitLn("imull (%esp), %eax");
    EmitLn("addl $4, %esp");
}

/* divide top of stack by primary */
void PopDiv()
{
    /* for a expersion like a/b we have eax=b and %(esp)=a
     * but we need eax=a, and b on the stack
     */
    EmitLn("movl (%esp), %edx");
    EmitLn("addl $4, %esp");
    EmitLn("pushl %eax");
    EmitLn("movl %edx, %eax");

    /* sign extesnion */
    EmitLn("sarl $31, %edx");
    EmitLn("idivl (%esp)");
    EmitLn("addl $4, %esp");
}

/* store primary to variable */
void Store(char *name)
{
    if (!InTable(name)) {
        char name_string[MAX_BUF];
        Undefined(name_string);
    }
    sprintf(tmp, "movl %%eax, %s", name);
    EmitLn(tmp);
}

void Undefined(char *name)
{
    sprintf(tmp, "Undefined Identifier: %s", name);
    Abort(tmp);
}

/* Complement the primary register */
void NotIt()
{
    EmitLn("not %eax");
}

/* AND top of Stack with primary */
void PopAnd()
{
    EmitLn("and (%esp), %eax");
    EmitLn("addl $4, %esp");
}

/* OR top of Stack with primary */
void PopOr()
{
    EmitLn("or (%esp), %eax");
    EmitLn("addl $4, %esp");
}

/* XOR top of Stack with primary */
void PopXor()
{
    EmitLn("xor (%esp), %eax");
    EmitLn("addl $4, %esp");
}

/* Compare top of Stack with primary */
void PopCompare()
{
    EmitLn("addl $4, %esp");
    EmitLn("cmp -4(%esp), %eax");
}

/* set %eax if Compare was = */
void SetEqual()
{
    EmitLn("sete %al");
    EmitLn("movsx %al, %eax");
}

/* set %eax if Compare was != */
void SetNEqual()
{
    EmitLn("setne %al");
    EmitLn("movsx %al, %eax");
}

/* set %eax if Compare was > */
void SetGreater()
{
    EmitLn("setl %al");
    EmitLn("movsx %al, %eax");
}

/* set %eax if Compare was >= */
void SetGreaterOrEqual()
{
    EmitLn("setle %al");
    EmitLn("movsx %al, %eax");
}

/* set %eax if Compare was < */
void SetLess()
{
    EmitLn("setg %al");
    EmitLn("movsx %al, %eax");
}

/* set %eax if Compare was <= */
void SetLessOrEqual()
{
    EmitLn("setge %al");
    EmitLn("movsx %al, %eax");
}

/* Branch unconditional */
void Branch(char *label)
{
    sprintf(tmp, "jmp %s", label);
    EmitLn(tmp);
}

/* Branch False */
void BranchFalse(char *label)
{
    EmitLn("test $1, %eax");
    sprintf(tmp, "jz %s", label);
    EmitLn(tmp);
}


================================================
FILE: 10/cradle.h
================================================
#ifndef _CRADLE_H
#define _CRADLE_H
#include <stdbool.h>

#define MAX_BUF 100
#define MaxEntry 100
extern char tmp[MAX_BUF];
extern const char *ST[];
extern char SType[];
extern char Token;
extern char Value[MAX_BUF];
char Look;

void GetChar();

void Error(char *s);
void Abort(char *s);
void Expected(char *s);
void Match(char x);
void MatchString(char *str);

int IsAlpha(char c);
int IsDigit(char c);
int IsAddop(char c);
int IsMulop(char c);
int IsOrOp(char c);
int IsRelop(char c);
int IsWhite(char c);
int IsAlNum(char c);

void GetName();
int GetNum();

void Emit(char *s);
void EmitLn(char *s);

void Init();
void InitTable();
bool InTable(char *name);
void AddEntry(char *symbol, char type);

char *NewLabel();
void PostLabel(char *label);
void SkipWhite();
void NewLine();
void Scan();

/* re-targetable routines */
void Clear();
void Negate();
void LoadConst(int n);
void LoadVar(char *name);
void Push();
void PopAdd();
void PopSub();
void PopMul();
void PopDiv();
void Store(char *name);
void Undefined(char *name);
void NotIt();
void PopAnd();
void PopOr();
void PopXor();
void PopCompare();
void SetEqual();
void SetNEqual();
void SetGreater();
void SetGreaterOrEqual();
void SetLess();
void SetLessOrEqual();
void Branch(char *label);
void BranchFalse(char *label);

#endif


================================================
FILE: 10/main.c
================================================
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#include "cradle.h"

#ifdef DEBUG
#define dprint(fmt, ...) printf(fmt, __VA_ARGS__);
#else
#define dprint(fmt, ...)
#endif


void Prog();
void Prolog();
void Epilog();
void Header();
void Main();
void Decl();
void TopDecls();
void Alloc(char *);
void Block();
void Assignment();

void Factor();
void NegFactor();
void Expression();
void Subtract();
void FirstTerm();
void Term();
void Term1();
void Divide();
void Multiply();
void FirstFactor();
void Add();
void Equals();
void NotEquals();
void Less();
void LessOrEqual();
void Greater();
void Relation();
void NotFactor();
void BoolTerm();
void BoolOr();
void BoolXor();
void BoolExpression();
void DoIf();
void DoWhile();

void Prog()
{
    MatchString("PROGRAM");
    Header();
    TopDecls();
    Main();
    Match('.');
}

void Header()
{
    EmitLn(".global _start");
}

void Prolog()
{
    EmitLn(".section .text");
    EmitLn("_start:");
}

void Epilog()
{
    EmitLn("movl %eax, %ebx");
    EmitLn("movl $1, %eax");
    EmitLn("int $0x80");
}

void Main()
{
    MatchString("BEGIN");
    Prolog();
    Block();
    MatchString("END");
    Epilog();
}

void TopDecls()
{
    NewLine();
    Scan();
    while(Token != 'b') {
        switch(Token) {
            case 'v':
                Decl();
                break;
            default:
                sprintf(tmp, "Unrecognized Keyword '%c'", Look);
                Abort(tmp);
                break;
        }
        Scan();
        NewLine();
    }
}

void Decl()
{
    NewLine();
    EmitLn(".section .data"); /* in case that the variable and function
                                 declarations are mixed */
    GetName();
    Alloc(Value);
    while(Look == ',') {
        Match(',');
        GetName();
        Alloc(Value);
        NewLine();
    }
}

void Alloc(char *name)
{
    if (InTable(name)) {
        sprintf(tmp, "Duplicate Variable Name: %s", name);
        Abort(tmp);
    }
    AddEntry(name, 'v');
    sprintf(tmp, "%s: .int ", name);
    Emit(tmp);
    if (Look == '=') {
        Match('=');
        if (Look == '-') {
            Emit("-");
            Match('-');
        } else {
            Emit("");
        }
        printf("%d\n", GetNum());
    } else {
        EmitLn("0");
    }
}

/* Parse and Translate a Block of Statements 
 * <block> ::= ( <statement> )*
 * <statement> ::= <if> | <while> | <assignment>
 * */
void Block()
{
    Scan();
    NewLine();
    while(strchr("el", Token) == NULL) {
        switch (Token) {
            case 'i':
                DoIf();
                break;
            case 'w':
                DoWhile();
                break;
            default:
                Assignment();
                break;
        }
        Scan();
        NewLine();
    }
}

void Assignment()
{
    char name[MAX_BUF];
    sprintf(name, Value);
    Match('=');
    BoolExpression();
    Store(name);
}

void Factor()
{
    if (Look == '(') {
        Match('(');
        BoolExpression();
        Match(')');
    } else if (IsAlpha(Look)) {
        GetName();
        LoadVar(Value);
    } else {
        LoadConst(GetNum());
    }
}

void NegFactor()
{
    Match('-');
    if (IsDigit(Look)) {
        LoadConst(-GetNum());
    } else {
        Factor();
        Negate();
    }
}

/* Parse and Translate a Leading Factor */
void FirstFactor()
{
    switch (Look) {
        case '+':
            Match('+');
            Factor();
            break;
        case '-':
            NegFactor();
            break;
        default:
            Factor();
    }
}

void Multiply()
{
    Match('*');
    Factor();
    PopMul();
}

void Divide()
{
    Match('/');
    Factor();
    PopDiv();
}

void Term1()
{
    NewLine();
    while(IsMulop(Look)) {
        Push();
        switch(Look) {
            case '*':
                Multiply();
                break;
            case '/':
                Divide();
                break;
            default:
                break;
        }
        NewLine();
    }
}

void Term()
{
    Factor();
    Term1();
}

void FirstTerm()
{
    FirstFactor();
    Term1();
}

void Add()
{
    Match('+');
    Term();
    PopAdd();
}

void Subtract()
{
    Match('-');
    Term();
    PopSub();
}

void Expression()
{
    NewLine();
    FirstTerm();
    while(IsAddop(Look)) {
        Push();
        switch(Look) {
            case '+':
                Add();
                break;
            case '-':
                Subtract();
                break;
            default:
                break;
        }
        NewLine();
    }
}

/* Recognize and Translate a Relational "Equals" */
void Equals()
{
    Match('=');
    Expression();
    PopCompare();
    SetEqual();
}

/* Recognize and Translate a Relational "Not Equals" */
void NotEquals()
{
    Match('>');
    Expression();
    PopCompare();
    SetNEqual();
}

/* Recognize and Translate a Relational "Less Than" */
void Less()
{
    Match('<');
    switch(Look) {
        case '=':
            LessOrEqual();
            break;
        case '>':
            NotEquals();
            break;
        default:
            Expression();
            PopCompare();
            SetLess();
            break;
    }
}

/* Recognize and Translate a Relational "Less or Equal" */
void LessOrEqual()
{
    Match('=');
    Expression();
    PopCompare();
    SetLessOrEqual();
}

/* Recognize and Translate a Relational "Greater Than" */
void Greater()
{
    Match('>');
    if (Look == '=') {
        Match('=');
        Expression();
        PopCompare();
        SetGreaterOrEqual();
    } else {
        Expression();
        PopCompare();
        SetGreater();
    }
}

/* Parse and Translate a Relation */
void Relation()
{
    Expression();
    if (IsRelop(Look)) {
        Push();
        switch (Look) {
            case '=':
                Equals();
                break;
            case '#':
                NotEquals();
                break;
            case '<':
                Less();
                break;
            case '>':
                Greater();
                break;
            default:
                break;
        }
    }
}

/* Parse and Translate a Boolean Factor with Leading NOT */
void NotFactor()
{
    if (Look == '!') {
        Match('!');
        Relation();
        NotIt();
    } else {
        Relation();
    }
}

/* Parse and Translate a Boolean Term 
 * <bool_term> ::= <not_factor> ( and_op <not_factor )*
 * */
void BoolTerm()
{
    NewLine();
    NotFactor();
    while(Look == '&') {
        Push();
        Match('&');
        NotFactor();
        PopAnd();
        NewLine();
    }
}

/* Recognize and Translate a Boolean OR */
void BoolOr()
{
    Match('|');
    BoolTerm();
    PopOr();
}

/* Recognize and Translate a Boolean XOR */
void BoolXor()
{
    Match('~');
    BoolTerm();
    PopXor();
}

/* Parse and Translate a Boolean Expression 
 * <bool_expression> ::= <bool_term> ( or_op <bool_term> )* */
void BoolExpression()
{
    NewLine();
    BoolTerm();
    while(IsOrOp(Look)) {
        Push();
        switch(Look) {
            case '|':
                BoolOr();
                break;
            case '~':
                BoolXor();
                break;
            default:
                break;
        }
        NewLine();
    }
}

/* Recognize and Translate an IF construct */
void DoIf()
{
    char L1[MAX_BUF];
    char L2[MAX_BUF];
    sprintf(L1, NewLabel());
    sprintf(L2, L1);
    BoolExpression();
    BranchFalse(L1);
    Block();
    if (Token == 'l') {
        sprintf(L2, NewLabel());
        Branch(L2);
        PostLabel(L1);
        Block();
    }
    PostLabel(L2);
    MatchString("ENDIF");
}

void DoWhile()
{
    char L1[MAX_BUF];
    char L2[MAX_BUF];
    sprintf(L1, NewLabel());
    sprintf(L2, NewLabel());
    PostLabel(L1);
    BoolExpression();
    BranchFalse(L2);
    Block();
    MatchString("ENDWHILE");
    Branch(L1);
    PostLabel(L2);
}


int main()
{
    Init();
    Prog();
    if (Look != '\n') {
        Abort("Unexpected data after '.'");
    }
    return 0;
}


================================================
FILE: 10/prog.txt
================================================
PROGRAM
VAR xx,
yy=1,
zz=10
BEGIN
  WHILE yy <= zz
    IF yy <> 5 
      xx=xx+yy
    ELSE
      xx=xx+5
    ENDIF
  yy=yy+1
  ENDWHILE
END.



================================================
FILE: 10/tutor10.txt
================================================



























                     LET'S BUILD A COMPILER!

                                By

                     Jack W. Crenshaw, Ph.D.

                           21 May 1989


                    Part X: INTRODUCING "TINY"


*****************************************************************
*                                                               *
*                        COPYRIGHT NOTICE                       *
*                                                               *
*   Copyright (C) 1989 Jack W. Crenshaw. All rights reserved.   *
*                                                               *
*****************************************************************


INTRODUCTION

In the last installment, I showed you the general  idea  for  the
top-down development of  a  compiler.    I gave you the first few
steps  of  the process for compilers for  Pascal  and  C,  but  I
stopped  far  short  of  pushing  it through to completion.   The
reason was simple: if we're going to produce  a  real, functional
compiler  for  any  language, I'd rather  do  it  for  KISS,  the
language that I've been defining in this tutorial series.

In this installment, we're going to do just that, for a subset of
KISS which I've chosen to call TINY.

The process  will be essentially that outlined in Installment IX,
except  for  one  notable  difference.   In that  installment,  I
suggested  that  you  begin  with  a full BNF description of  the
language.  That's fine for something like Pascal or C,  for which
the language definition is  firm.   In the case of TINY, however,
we don't yet have a full  description  ... we seem to be defining
the language as we go.  That's OK.    In  fact,  it's preferable,
since we can tailor the language  slightly  as we go, to keep the
parsing easy.

So in the development  that  follows,  we'll  actually be doing a
top-down development of BOTH the  language and its compiler.  The
BNF description will grow along with the compiler.

In this process, there will be a number of decisions to  be made,
each of which will influence the BNF and therefore the  nature of
the language.   At  each  decision  point I'll try to remember to
explain  the  decision  and the rationale behind my choice.  That
way, if you happen to hold a different opinion and would prefer a
different option, you can choose it instead.  You  now  have  the
background  to  do  that.  I guess the important thing to note is
that  nothing  we  do  here  is  cast  in  concrete.  When YOU'RE
designing YOUR language, you should feel free to do it YOUR way.

Many of you may be asking at this point: Why bother starting over
from  scratch?  We had a working subset of KISS as the outcome of
Installment  VII  (lexical  scanning).  Why not just extend it as
needed?  The  answer  is  threefold.    First of all, I have been
making  a  number  of changes to further simplify the program ...
changes  like  encapsulating  the  code generation procedures, so
that  we  can  convert to a different target machine more easily.
Second, I want you to see how the development can indeed  be done
from the top down as outlined in the last installment.   Finally,
we both need the practice.  Each time I go through this exercise,
I get a little better at it, and you will, also.


GETTING STARTED

Many  years  ago  there were languages called  Tiny  BASIC,  Tiny
Pascal, and Tiny C, each of which was a subset of its parent full
language.  Tiny BASIC,  for  example,  had  only single-character
variable names and global variables.   It supported only a single
data type.  Sound familiar?  At this point we have almost all the
tools we need to build a compiler like that.

Yet a language called Tiny-anything  still  carries  some baggage
inherited from its parent language.   I've often wondered if this
is a  good  idea.    Granted,  a  language based upon some parent
language will have the  advantage  of  familiarity, but there may
also  be  some  peculiar syntax carried over from the parent that
may tend  to add unnecessary complexity to the compiler. (Nowhere
is this more true than in Small C.)

I've wondered just how small and simple a compiler could  be made
and  still  be  useful, if it were designed from the outset to be
both easy to use and to  parse.    Let's find out.  This language
will just be called "TINY," period.  It's a subset of KISS, which
I  also  haven't  fully  defined,  so  that  at  least  makes  us
consistent (!).  I suppose you could call it TINY KISS.  But that
opens  up a whole can of worms involving  cuter  and  cuter  (and
perhaps more risque) names, so let's just stick with TINY.

The main limitations  of  TINY  will  be because of the things we
haven't yet covered, such as data types.  Like its cousins Tiny C
and Tiny BASIC,  TINY  will  have  only one data type, the 16-bit
integer.    The  first  version  we  develop  will also  have  no
procedure  calls  and  will  use single-character variable names,
although as you will see we can remove these restrictions without
much effort.

The language I have in mind will share some of the  good features
of  Pascal,  C,  and Ada.  Taking a lesson from the comparison of
the Pascal and  C  compilers in the previous installment, though,
TINY will have a decided Pascal flavor.  Wherever  feasible,    a
language structure will  be  bracketed by keywords or symbols, so
that  the parser will know where it's  going  without  having  to
guess.

One other ground rule:  As we go, I'd like  to  keep the compiler
producing real, executable code.  Even though it may not  DO much
at the beginning, it will at least do it correctly.

Finally,  I'll  use  a couple of Pascal  restrictions  that  make
sense:  All data and procedures must be declared before  they are
used.  That makes good sense,  even  though for now the only data
type we'll use  is a word.  This rule in turn means that the only
reasonable place to put the  executable code for the main program
is at the end of the listing.

The top-level definition will be similar to Pascal:


     <program> ::= PROGRAM <top-level decl> <main> '.'


Already, we've reached a decision point.  My first thought was to
make the main block optional.   It  doesn't seem to make sense to
write a "program" with no main program, but it does make sense if
we're  allowing  for  multiple modules, linked together.    As  a
matter of fact,  I intend to allow for this in KISS.  But then we
begin  to open up a can of worms that I'd rather leave closed for
now.  For example, the  term "PROGRAM" really becomes a misnomer.
The MODULE of Modula-2 or the Unit of Turbo Pascal would  be more
appropriate.  Second,  what  about  scope  rules?    We'd  need a
convention for  dealing  with  name  visibility  across  modules.
Better  for  now  to  just  keep  it  simple  and ignore the idea
altogether.

There's also a decision in choosing to require  the  main program
to  be  last.    I  toyed  with  the idea of making its  position
optional,  as  in  C.  The nature of SK*DOS, the OS I'm compiling
for, make this very easy to do.   But  this  doesn't  really make
much sense in view of the Pascal-like requirement  that  all data
and procedures  be declared before they're referenced.  Since the
main  program can only call procedures  that  have  already  been
declared, the only position that makes sense is at the end,  a la
Pascal.

Given  the  BNF  above, let's write a parser that just recognizes
the brackets:


{--------------------------------------------------------------}
{  Parse and Translate a Program }

procedure Prog;
begin
   Match('p');
   Header;
   Prolog;
   Match('.');
   Epilog;
end;
{--------------------------------------------------------------}


The procedure Header just emits  the startup code required by the
assembler:
                              

{--------------------------------------------------------------}
{ Write Header Info }

procedure Header;
begin
   WriteLn('WARMST', TAB, 'EQU $A01E');
end;
{--------------------------------------------------------------}


The procedures Prolog and  Epilog  emit  the code for identifying
the main program, and for returning to the OS:


{--------------------------------------------------------------}
{ Write the Prolog }

procedure Prolog;
begin
   PostLabel('MAIN');
end;


{--------------------------------------------------------------}
{ Write the Epilog }

procedure Epilog;
begin
   EmitLn('DC WARMST');
   EmitLn('END MAIN');
end;
{--------------------------------------------------------------}


The  main program just calls Prog, and then  looks  for  a  clean
ending:


{--------------------------------------------------------------}
{ Main Program }

begin
   Init;
   Prog;
   if Look <> CR then Abort('Unexpected data after ''.''');
end.
{--------------------------------------------------------------}


At this point, TINY  will  accept  only  one input "program," the
null program:


     PROGRAM .   (or 'p.' in our shorthand.)

Note, though, that the  compiler  DOES  generate correct code for
this program.  It will run, and do  what  you'd  expect  the null
program to do, that is, nothing but return gracefully to the OS.

As a matter of interest, one of my  favorite  compiler benchmarks
is to compile, link,  and  execute  the  null program in whatever
language   is   involved.     You  can  learn  a  lot  about  the
implementation by measuring  the  overhead  in  time  required to
compile what should be a trivial case.  It's also  interesting to
measure the amount of code produced.  In many compilers, the code
can be fairly large, because they always include  the  whole run-
time  library whether they need it or not.    Early  versions  of
Turbo Pascal produced a 12K object file for  this  case.    VAX C
generates 50K!

The  smallest  null  programs  I've  seen are those  produced  by
Modula-2 compilers, and they run about 200-800 bytes.

In the case of TINY, we HAVE no run-time library  as  yet, so the
object code is indeed tiny:  two  bytes.    That's  got  to  be a
record, and it's  likely  to  remain  one since it is the minimum
size required by the OS.

The  next step is to process the code for the main program.  I'll
use the Pascal BEGIN-block:


     <main> ::= BEGIN <block> END


Here,  again,  we  have made a decision.  We could have chosen to
require a "PROCEDURE MAIN" sort of declaration, similar to C.   I
must  admit  that  this  is  not  a bad idea at all ...  I  don't
particularly  like  the  Pascal  approach  since I tend  to  have
trouble locating the main  program  in a Pascal listing.  But the
alternative is a little awkward, too, since you have to deal with
the  error condition where the user omits  the  main  program  or
misspells its name.  Here I'm taking the easy way out.

Another solution to the "where is the main program" problem might
be to require a name for  the  program, and then bracket the main
by


     BEGIN <name>
     END <name>


similar to the convention of  Modula  2.    This  adds  a  bit of
"syntactic sugar" to the language.  Things like this are  easy to
add or change to your liking, if the language is your own design.

To parse this definition of a main block,  change  procedure Prog
to read:

{--------------------------------------------------------------}
{  Parse and Translate a Program }

procedure Prog;
begin
   Match('p');
   Header;
   Main;
   Match('.');
end;
{--------------------------------------------------------------}


and add the new procedure:


{--------------------------------------------------------------}
{ Parse and Translate a Main Program }

procedure Main;
begin
   Match('b');
   Prolog;
   Match('e');
   Epilog;
end;
{--------------------------------------------------------------}


Now, the only legal program is:


     PROGRAM BEGIN END . (or 'pbe.')


Aren't we making progress???  Well, as usual it gets better.  You
might try some deliberate errors here, like omitting  the  'b' or
the 'e', and see what happens.  As always,  the  compiler  should
flag all illegal inputs.


DECLARATIONS

The obvious next step is to decide what we mean by a declaration.
My  intent  here  is to have two kinds of declarations: variables
and  procedures/functions.    At  the  top  level,   only  global
declarations are allowed, just as in C.

For now, there  can  only be variable declarations, identified by
the keyword VAR (abbreviated 'v'):


     <top-level decls> ::= ( <data declaration> )*

     <data declaration> ::= VAR <var-list>


Note that since there is only one variable type, there is no need
to  declare the type.  Later on, for full KISS, we can easily add
a type description.

The procedure Prog becomes:


{--------------------------------------------------------------}
{  Parse and Translate a Program }

procedure Prog;
begin
   Match('p');
   Header;
   TopDecls;
   Main;
   Match('.');
end;
{--------------------------------------------------------------}


Now, add the two new procedures:


{--------------------------------------------------------------}
{ Process a Data Declaration }

procedure Decl;
begin
   Match('v');
   GetChar;
end;


{--------------------------------------------------------------}
{ Parse and Translate Global Declarations }

procedure TopDecls;
begin
   while Look <> 'b' do
      case Look of
        'v': Decl;
      else Abort('Unrecognized Keyword ''' + Look + '''');
      end;
end;
{--------------------------------------------------------------}


Note that at this point, Decl  is  just  a stub.  It generates no
code, and it doesn't process a list ... every variable must occur
in a separate VAR statement.

OK,  now  we  can have any  number  of  data  declarations,  each
starting with a 'v' for VAR,  before  the BEGIN-block.  Try a few
cases and see what happens.


DECLARATIONS AND SYMBOLS

That looks pretty good, but  we're still only generating the null
program  for  output.    A  real compiler would  issue  assembler
directives to allocate storage for  the  variables.    It's about
time we actually produced some code.

With  a  little  extra  code,  that's  an  easy  thing to do from
procedure Decl.  Modify it as follows:


{--------------------------------------------------------------}
{ Parse and Translate a Data Declaration }

procedure Decl;
var Name: char;
begin
   Match('v');
   Alloc(GetName);
end;
{--------------------------------------------------------------}


The procedure Alloc just  issues  a  command  to the assembler to
allocate storage:


{--------------------------------------------------------------}
{ Allocate Storage for a Variable }

procedure Alloc(N: char);
begin
   WriteLn(N, ':', TAB, 'DC 0');
end;
{--------------------------------------------------------------}


Give  this  one  a  whirl.    Try  an  input  that declares  some
variables, such as:

     pvxvyvzbe.

See how the storage is allocated?    Simple, huh?  Note also that
the entry point, "MAIN," comes out in the right place.

For the record, a "real" compiler would also have a  symbol table
to record the variables being used.  Normally,  the  symbol table
is necessary to record the type  of  each variable.  But since in
this case  all  variables  have  the  same  type, we don't need a
symbol  table  for  that reason.  As it turns out, we're going to
find a symbol  necessary  even without different types, but let's
postpone that need until it arises.

Of course, we haven't really parsed the correct syntax for a data
declaration, since it involves a variable list.  Our version only
permits a single variable.  That's easy to fix, too.

The BNF for <var-list> is


     <var-list> ::= <ident> (, <ident>)*


Adding this syntax to Decl gives this new version:


{--------------------------------------------------------------}
{ Parse and Translate a Data Declaration }

procedure Decl;
var Name: char;
begin
   Match('v');
   Alloc(GetName);
   while Look = ',' do begin
      GetChar;
      Alloc(GetName);
   end;
end;
{--------------------------------------------------------------}


OK, now compile this code and give it  a  try.    Try a number of
lines of VAR declarations, try a list of several variables on one
line, and try combinations of the two.  Does it work?


INITIALIZERS

As long as we're dealing with data declarations, one thing that's
always  bothered  me  about  Pascal  is  that  it  doesn't  allow
initializing  data items in the declaration.    That  feature  is
admittedly sort of a frill, and it  may  be  out  of  place  in a
language that purports to  be  a minimal language.  But it's also
SO easy to add that it seems a shame not  to  do  so.    The  BNF
becomes:


     <var-list> ::= <var> ( <var> )*

     <var> ::= <ident> [ = <integer> ]

Change Alloc as follows:


{--------------------------------------------------------------}
{ Allocate Storage for a Variable }

procedure Alloc(N: char);
begin
   Write(N, ':', TAB, 'DC ');
   if Look = '=' then begin
      Match('=');
      WriteLn(GetNum);
      end
   else
      WriteLn('0');
end;
{--------------------------------------------------------------}


There you are: an initializer with six added lines of Pascal.

OK, try this  version  of  TINY  and verify that you can, indeed,
give the variables initial values.

By golly, this thing is starting to look  real!    Of  course, it
still doesn't DO anything, but it looks good, doesn't it?

Before leaving this section, I should point out  that  we've used
two versions of function GetNum.  One, the earlier one, returns a
character value, a single digit.  The other accepts a multi-digit
integer and returns an integer value.  Either one will work here,
since WriteLn will handle either type.  But there's no  reason to
limit ourselves  to  single-digit  values  here,  so  the correct
version to use is the one that returns an integer.  Here it is:


{--------------------------------------------------------------}
{ Get a Number }

function GetNum: integer;
var Val: integer;
begin
   Val := 0;
   if not IsDigit(Look) then Expected('Integer');
   while IsDigit(Look) do begin
      Val := 10 * Val + Ord(Look) - Ord('0');
      GetChar;
   end;
   GetNum := Val;
end;
{--------------------------------------------------------------}

As a matter  of  fact,  strictly  speaking  we  should  allow for
expressions in the data field of the initializer, or at  the very
least  for  negative  values.  For  now,  let's  just  allow  for
negative values by changing the code for Alloc as follows:


{--------------------------------------------------------------}
{ Allocate Storage for a Variable }

procedure Alloc(N: char);
begin
   if InTable(N) then Abort('Duplicate Variable Name ' + N);
   ST[N] := 'v';
   Write(N, ':', TAB, 'DC ');
   if Look = '=' then begin
      Match('=');
      If Look = '-' then begin
         Write(Look);
         Match('-');
      end;
      WriteLn(GetNum);
      end
   else
      WriteLn('0');
end;
{--------------------------------------------------------------}


Now  you should be able to  initialize  variables  with  negative
and/or multi-digit values.


THE SYMBOL TABLE

There's one problem  with  the  compiler  as it stands so far: it
doesn't do anything to record a variable when we declare it.   So
the compiler is perfectly content to allocate storage for several
variables with the same name.  You can easily verify this with an
input like


     pvavavabe.


Here we've declared the variable A three times.  As you  can see,
the compiler will  cheerfully  accept  that,  and  generate three
identical labels.  Not good.

Later on,  when we start referencing variables, the compiler will
also let us reference variables  that don't exist.  The assembler
will  catch  both  of these error conditions, but it doesn't seem
friendly at all to pass such errors along to the assembler.   The
compiler should catch such things at the source language level.

So even though we don't need a symbol table to record data types,
we ought to install  one  just to check for these two conditions.
Since at this  point  we are still restricted to single-character
variable names, the symbol table can be trivial.  To  provide for
it, first add the following  declaration at the beginning of your
program:


     var ST: array['A'..'Z'] of char;


and insert the following function:


{--------------------------------------------------------------}
{ Look for Symbol in Table }

function InTable(n: char): Boolean;
begin
   InTable := ST[n] <> ' ';
end;
{--------------------------------------------------------------}


We  also  need  to initialize the  table  to  all  blanks.    The
following lines in Init will do the job:


var i: char;
begin
   for i := 'A' to 'Z' do
      ST[i] := ' ';
   ...


Finally,  insert  the  following two lines at  the  beginning  of
Alloc:


   if InTable(N) then Abort('Duplicate Variable Name ' + N);
   ST[N] := 'v';


That  should  do  it.  The  compiler  will  now  catch  duplicate
declarations.  Later, we can  also  use  InTable  when generating
references to the variables.


EXECUTABLE STATEMENTS

At this point, we can generate a null program that has  some data
variables  declared  and  possibly initialized.  But  so  far  we
haven't arranged to generate the first line of executable code.

Believe  it or not, though, we almost  have  a  usable  language!
What's missing is the executable code that must go into  the main
program.  But that code is just assignment statements and control
statements ... all stuff we have done before.   So  it  shouldn't
take us long to provide for them, as well.

The BNF definition given earlier  for the main program included a
statement block, which we have so far ignored:


     <main> ::= BEGIN <block> END


For now,  we  can  just  consider  a  block  to  be  a  series of
assignment statements:


     <block> ::= (Assignment)*


Let's start things off by adding  a  parser for the block.  We'll
begin with a stub for the assignment statement:


{--------------------------------------------------------------}
{ Parse and Translate an Assignment Statement }

procedure Assignment;
begin
   GetChar;
end;


{--------------------------------------------------------------}
{ Parse and Translate a Block of Statements }

procedure Block;
begin
   while Look <> 'e' do
      Assignment;
end;
{--------------------------------------------------------------}


Modify procedure Main to call Block as shown below:


{--------------------------------------------------------------}
{ Parse and Translate a Main Program }

procedure Main;
begin
   Match('b');
   Prolog;
   Block;
   Match('e');
   Epilog;
end;
{--------------------------------------------------------------}


This version still won't generate any code for  the   "assignment
statements" ... all it does is to eat characters  until  it  sees
the 'e' for 'END.'  But it sets the stage for what is to follow.

The  next  step,  of  course,  is  to  flesh out the code for  an
assignment statement.  This  is  something  we've done many times
before,  so  I  won't belabor it.  This time, though, I'd like to
deal with the code generation a little differently.  Up till now,
we've always just inserted the Emits that generate output code in
line with  the parsing routines.  A little unstructured, perhaps,
but it seemed the most straightforward approach, and made it easy
to see what kind of code would be emitted for each construct.

However, I realize that most of you are using an  80x86 computer,
so  the 68000 code generated is of little use to you.  Several of
you have asked me if the CPU-dependent code couldn't be collected
into one spot  where  it  would  be easier to retarget to another
CPU.  The answer, of course, is yes.

To  accomplish  this,  insert  the  following  "code  generation"
routines:


{---------------------------------------------------------------}
{ Clear the Primary Register }

procedure Clear;
begin
   EmitLn('CLR D0');
end;


{---------------------------------------------------------------}
{ Negate the Primary Register }

procedure Negate;
begin
   EmitLn('NEG D0');
end;


{---------------------------------------------------------------}
{ Load a Constant Value to Primary Register }

procedure LoadConst(n: integer);
begin
   Emit('MOVE #');
   WriteLn(n, ',D0');
end;


{---------------------------------------------------------------}
{ Load a Variable to Primary Register }

procedure LoadVar(Name: char);
begin
   if not InTable(Name) then Undefined(Name);
   EmitLn('MOVE ' + Name + '(PC),D0');
end;


{---------------------------------------------------------------}
{ Push Primary onto Stack }

procedure Push;
begin
   EmitLn('MOVE D0,-(SP)');
end;


{---------------------------------------------------------------}
{ Add Top of Stack to Primary }

procedure PopAdd;
begin
   EmitLn('ADD (SP)+,D0');
end;


{---------------------------------------------------------------}
{ Subtract Primary from Top of Stack }

procedure PopSub;
begin
   EmitLn('SUB (SP)+,D0');
   EmitLn('NEG D0');
end;


{---------------------------------------------------------------}
{ Multiply Top of Stack by Primary }

procedure PopMul;
begin
   EmitLn('MULS (SP)+,D0');
end;


{---------------------------------------------------------------}
{ Divide Top of Stack by Primary }

procedure PopDiv;
begin
   EmitLn('MOVE (SP)+,D7');
   EmitLn('EXT.L D7');
   EmitLn('DIVS D0,D7');
   EmitLn('MOVE D7,D0');
end;


{---------------------------------------------------------------}
{ Store Primary to Variable }

procedure Store(Name: char);
begin
   if not InTable(Name) then Undefined(Name);
   EmitLn('LEA ' + Name + '(PC),A0');
   EmitLn('MOVE D0,(A0)')
end;
{---------------------------------------------------------------}


The  nice  part  of  this  approach,  of  course,  is that we can
retarget  the compiler to a new CPU  simply  by  rewriting  these
"code generator" procedures.  In  addition,  we  will  find later
that we can improve the code quality by tweaking these routines a
bit, without having to modify the compiler proper.

Note that both LoadVar  and  Store check the symbol table to make
sure that the variable is defined.  The  error  handler Undefined
simply calls Abort:


{--------------------------------------------------------------}
{ Report an Undefined Identifier }

procedure Undefined(n: string);
begin
   Abort('Undefined Identifier ' + n);
end;
{--------------------------------------------------------------}


OK, we are now finally ready to begin processing executable code.
We'll  do  that  by  replacing  the  stub  version  of  procedure
Assignment.

We've been down this  road  many times before, so this should all
be familiar to you.    In fact, except for the changes associated
with the code generation, we  could just copy the procedures from
Part  VII.    Since we are making some changes, I won't just copy
them, but we will go a little faster than usual.

The BNF for the assignment statement is:

     <assignment> ::= <ident> = <expression>

     <expression> ::= <first term> ( <addop> <term> )*

     <first term> ::= <first factor> <rest>

     <term> ::= <factor> <rest>

     <rest> ::= ( <mulop> <factor> )*

     <first factor> ::= [ <addop> ] <factor>

     <factor> ::= <var> | <number> | ( <expression> )


This version of the BNF is  also  a bit different than we've used
before ... yet another "variation on the theme of an expression."
This particular version  has  what  I  consider  to  be  the best
treatment  of  the  unary minus.  As you'll see later, it lets us
handle   negative  constant  values  efficiently.    It's   worth
mentioning  here  that  we  have  often  seen  the advantages  of
"tweaking"  the  BNF  as we go, to help make the language easy to
parse.    What  you're looking at here is a bit different:  we've
tweaked  the  BNF  to make the CODE  GENERATION  more  efficient!
That's a first for this series.

Anyhow, the following code implements the BNF:


{---------------------------------------------------------------}
{ Parse and Translate a Math Factor }

procedure Expression; Forward;

procedure Factor;
begin
   if Look = '(' then begin
      Match('(');
      Expression;
      Match(')');
      end
   else if IsAlpha(Look) then
      LoadVar(GetName)
   else
      LoadConst(GetNum);
end;


{--------------------------------------------------------------}
{ Parse and Translate a Negative Factor }

procedure NegFactor;
begin
   Match('-');
   if IsDigit(Look) then
      LoadConst(-GetNum)
   else begin
      Factor;
      Negate;
   end;
end;


{--------------------------------------------------------------}
{ Parse and Translate a Leading Factor }

procedure FirstFactor;
begin
   case Look of
     '+': begin
             Match('+');
             Factor;
          end;
     '-': NegFactor;
   else  Factor;
   end;
end;


{--------------------------------------------------------------}
{ Recognize and Translate a Multiply }

procedure Multiply;
begin
   Match('*');
   Factor;
   PopMul;
end;


{-------------------------------------------------------------}
{ Recognize and Translate a Divide }

procedure Divide;
begin
   Match('/');
   Factor;
   PopDiv;
end;


{---------------------------------------------------------------}
{ Common Code Used by Term and FirstTerm }

procedure Term1;
begin
   while IsMulop(Look) do begin
      Push;
      case Look of
       '*': Multiply;
       '/': Divide;
      end;
   end;
end;


{---------------------------------------------------------------}
{ Parse and Translate a Math Term }

procedure Term;
begin
   Factor;
   Term1;
end;


{---------------------------------------------------------------}
{ Parse and Translate a Leading Term }

procedure FirstTerm;
begin
   FirstFactor;
   Term1;
end;


{--------------------------------------------------------------}
{ Recognize and Translate an Add }

procedure Add;
begin
   Match('+');
   Term;
   PopAdd;
end;


{-------------------------------------------------------------}
{ Recognize and Translate a Subtract }

procedure Subtract;
begin
   Match('-');
   Term;
   PopSub;
end;


{---------------------------------------------------------------}
{ Parse and Translate an Expression }

procedure Expression;
begin
   FirstTerm;
   while IsAddop(Look) do begin
      Push;
      case Look of
       '+': Add;
       '-': Subtract;
      end;
   end;
end;


{--------------------------------------------------------------}
{ Parse and Translate an Assignment Statement }

procedure Assignment;
var Name: char;
begin
   Name := GetName;
   Match('=');
   Expression;
   Store(Name);
end;
{--------------------------------------------------------------}


OK, if you've  got  all  this  code inserted, then compile it and
check  it out.  You should  be  seeing  reasonable-looking  code,
representing a complete program that will  assemble  and execute.
We have a compiler!


BOOLEANS

The next step should also  be  familiar  to  you.    We  must add
Boolean  expressions  and relational operations.    Again,  since
we've already dealt with them more than once,  I  won't elaborate
much on them, except  where  they  are  different from what we've
done before.  Again, we won't just copy from other  files because
I've changed a few things just a bit.  Most  of  the changes just
involve encapsulating the machine-dependent parts as  we  did for
the   arithmetic  operations.    I've  also  modified   procedure
NotFactor  somewhat,  to  parallel  the structure of FirstFactor.
Finally,  I  corrected  an  error  in  the  object code  for  the
relational operators:  The Scc instruction I used  only  sets the
low 8 bits of D0.  We want all 16 bits set for a logical true, so
I've added an instruction to sign-extend the low byte.

To begin, we're going to need some more recognizers:


{--------------------------------------------------------------}
{ Recognize a Boolean Orop }

function IsOrop(c: char): boolean;
begin
   IsOrop := c in ['|', '~'];
end;


{--------------------------------------------------------------}
{ Recognize a Relop }

function IsRelop(c: char): boolean;
begin
   IsRelop := c in ['=', '#', '<', '>'];
end;
{--------------------------------------------------------------}


Also, we're going to need some more code generation routines:


{---------------------------------------------------------------}
{ Complement the Primary Register }

procedure NotIt;
begin
   EmitLn('NOT D0');
end;
{---------------------------------------------------------------}
.
.
.
{---------------------------------------------------------------}
{ AND Top of Stack with Primary }

procedure PopAnd;
begin
   EmitLn('AND (SP)+,D0');
end;


{---------------------------------------------------------------}
{ OR Top of Stack with Primary }

procedure PopOr;
begin
   EmitLn('OR (SP)+,D0');
end;


{---------------------------------------------------------------}
{ XOR Top of Stack with Primary }

procedure PopXor;
begin
   EmitLn('EOR (SP)+,D0');
end;


{---------------------------------------------------------------}
{ Compare Top of Stack with Primary }

procedure PopCompare;
begin
   EmitLn('CMP (SP)+,D0');
end;


{---------------------------------------------------------------}
{ Set D0 If Compare was = }

procedure SetEqual;
begin
   EmitLn('SEQ D0');
   EmitLn('EXT D0');
end;


{---------------------------------------------------------------}
{ Set D0 If Compare was != }

procedure SetNEqual;
begin
   EmitLn('SNE D0');
   EmitLn('EXT D0');
end;


{---------------------------------------------------------------}
{ Set D0 If Compare was > }

procedure SetGreater;
begin
   EmitLn('SLT D0');
   EmitLn('EXT D0');
end;


{---------------------------------------------------------------}
{ Set D0 If Compare was < }

procedure SetLess;
begin
   EmitLn('SGT D0');
   EmitLn('EXT D0');
end;
{---------------------------------------------------------------}

All of this  gives us the tools we need.  The BNF for the Boolean
expressions is:


     <bool-expr> ::= <bool-term> ( <orop> <bool-term> )*

     <bool-term> ::= <not-factor> ( <andop> <not-factor> )*

     <not-factor> ::= [ '!' ] <relation>

     <relation> ::= <expression> [ <relop> <expression> ]


Sharp-eyed readers might  note  that this syntax does not include
the non-terminal  "bool-factor" used in earlier versions.  It was
needed then because I also allowed for the Boolean constants TRUE
and FALSE.   But  remember  that  in TINY there is no distinction
made between Boolean and arithmetic  types ... they can be freely
intermixed.   So there is really no  need  for  these  predefined
values ... we can just use -1 and 0, respectively.

In C terminology, we could always use the defines:


     #define TRUE -1
     #define FALSE 0


(That is, if TINY had a  preprocessor.)   Later on, when we allow
for  declarations  of  constants,  these  two   values   will  be
predefined by the language.

The reason that I'm harping on this is that  I've  already  tried
the alternative, which is to  include TRUE and FALSE as keywords.
The problem with that approach is that it  then  requires lexical
scanning for EVERY variable name  in every expression.  If you'll
recall,  I pointed out in Installment VII  that  this  slows  the
compiler  down considerably.  As long as  keywords  can't  be  in
expressions, we need to do the scanning only at the  beginning of
every  new  statement  ...  quite  an improvement.  So using  the
syntax above not only simplifies the parsing, but  speeds  up the
scanning as well.

OK, given that we're  all  satisfied  with  the syntax above, the
corresponding code is shown below:


{---------------------------------------------------------------}
{ Recognize and Translate a Relational "Equals" }

procedure Equals;
begin
   Match('=');
   Expression;
   PopCompare;
   SetEqual;
end;


{---------------------------------------------------------------}
{ Recognize and Translate a Relational "Not Equals" }

procedure NotEquals;
begin
   Match('#');
   Expression;
   PopCompare;
   SetNEqual;
end;


{---------------------------------------------------------------}
{ Recognize and Translate a Relational "Less Than" }

procedure Less;
begin
   Match('<');
   Expression;
   PopCompare;
   SetLess;
end;


{---------------------------------------------------------------}
{ Recognize and Translate a Relational "Greater Than" }

procedure Greater;
begin
   Match('>');
   Expression;
   PopCompare;
   SetGreater;
end;


{---------------------------------------------------------------}
{ Parse and Translate a Relation }


procedure Relation;
begin
   Expression;
   if IsRelop(Look) then begin
      Push;
      case Look of
       '=': Equals;
       '#': NotEquals;
       '<': Less;
       '>': Greater;
      end;
   end;
end;


{---------------------------------------------------------------}
{ Parse and Translate a Boolean Factor with Leading NOT }

procedure NotFactor;
begin
   if Look = '!' then begin
      Match('!');
      Relation;
      NotIt;
      end
   else
      Relation;
end;


{---------------------------------------------------------------}
{ Parse and Translate a Boolean Term }

procedure BoolTerm;
begin
   NotFactor;
   while Look = '&' do begin
      Push;
      Match('&');
      NotFactor;
      PopAnd;
   end;
end;


{--------------------------------------------------------------}
{ Recognize and Translate a Boolean OR }

procedure BoolOr;
begin
   Match('|');
   BoolTerm;
   PopOr;
end;


{--------------------------------------------------------------}
{ Recognize and Translate an Exclusive Or }

procedure BoolXor;
begin
   Match('~');
   BoolTerm;
   PopXor;
end;


{---------------------------------------------------------------}
{ Parse and Translate a Boolean Expression }

procedure BoolExpression;
begin
   BoolTerm;
   while IsOrOp(Look) do begin
      Push;
      case Look of
       '|': BoolOr;
       '~': BoolXor;
      end;
   end;
end;
{--------------------------------------------------------------}


To tie it all together, don't forget to change the  references to
Expression in  procedures Factor and Assignment so that they call
BoolExpression instead.

OK, if  you've  got  all  that typed in, compile it and give it a
whirl.    First,  make  sure  you  can  still parse  an  ordinary
arithmetic expression.  Then, try a Boolean one.    Finally, make
sure  that you can assign the results of  relations.    Try,  for
example:

     pvx,y,zbx=z>ye.

which stands for:

     PROGRAM
     VAR X,Y,Z
     BEGIN
     X = Z > Y
     END.


See how this assigns a Boolean value to X?

CONTROL STRUCTURES

We're almost home.   With  Boolean  expressions  in place, it's a
simple  matter  to  add control structures.  For TINY, we'll only
allow two kinds of them, the IF and the WHILE:


     <if> ::= IF <bool-expression> <block> [ ELSE <block>] ENDIF

     <while> ::= WHILE <bool-expression> <block> ENDWHILE

Once  again,  let  me  spell  out the decisions implicit in  this
syntax, which departs strongly from that of C or Pascal.  In both
of those languages, the "body" of an IF or WHILE is regarded as a
single  statement.  If you intend to use a block of more than one
statement, you have to build a compound statement using BEGIN-END
(in Pascal) or  '{}' (in C).  In TINY (and KISS) there is no such
thing as a compound statement  ... single or multiple they're all
just blocks to these languages.

In KISS, all the control structures will have explicit and unique
keywords  bracketing  the  statement block, so there  can  be  no
confusion as to where things begin  and  end.  This is the modern
approach, used in such respected languages as Ada  and  Modula 2,
and it completely eliminates the problem of the "dangling else."

Note  that I could have chosen to use the same keyword END to end
all  the constructs, as is done in Pascal.  (The closing '}' in C
serves the same purpose.)  But this has always led  to confusion,
which is why Pascal programmers tend to write things like


     end { loop }

or   end { if }


As I explained in  Part  V,  using  unique terminal keywords does
increase  the  size  of the keyword list and therefore slows down
the  scanning, but in this case it seems a small price to pay for
the added insurance.   Better  to find the errors at compile time
rather than run time.

One last thought:  The two constructs above each  have  the  non-
terminals


      <bool-expression> and <block>


juxtaposed with no separating keyword.  In Pascal we would expect
the keywords THEN and DO in these locations.

I have no problem with leaving out these keywords, and the parser
has no trouble either, ON CONDITION that we make no errors in the
bool-expression part.  On  the  other hand, if we were to include
these extra keywords we would get yet one more level of insurance
at very little  cost,  and  I  have no problem with that, either.
Use your best judgment as to which way to go.

OK, with that bit of explanation let's proceed.  As  usual, we're
going to need some new  code generation routines.  These generate
the code for conditional and unconditional branches:

{---------------------------------------------------------------}
{ Branch Unconditional  }

procedure Branch(L: string);
begin
   EmitLn('BRA ' + L);
end;


{---------------------------------------------------------------}
{ Branch False }

procedure BranchFalse(L: string);
begin
   EmitLn('TST D0');
   EmitLn('BEQ ' + L);
end;
{--------------------------------------------------------------}


Except for the encapsulation of  the code generation, the code to
parse the control constructs is the same as you've seen before:


{---------------------------------------------------------------}
{ Recognize and Translate an IF Construct }

procedure Block; Forward;


procedure DoIf;
var L1, L2: string;
begin
   Match('i');
   BoolExpression;
   L1 := NewLabel;
   L2 := L1;
   BranchFalse(L1);
   Block;
   if Look = 'l' then begin
      Match('l');
      L2 := NewLabel;
      Branch(L2);
      PostLabel(L1);
      Block;
   end;
   PostLabel(L2);
   Match('e');
end;


{--------------------------------------------------------------}
{ Parse and Translate a WHILE Statement }

procedure DoWhile;
var L1, L2: string;
begin
   Match('w');
   L1 := NewLabel;
   L2 := NewLabel;
   PostLabel(L1);
   BoolExpression;
   BranchFalse(L2);
   Block;
   Match('e');
   Branch(L1);
   PostLabel(L2);
end;
{--------------------------------------------------------------}


To tie everything  together,  we need only modify procedure Block
to recognize the "keywords" for the  IF  and WHILE.  As usual, we
expand the definition of a block like so:


     <block> ::= ( <statement> )*


where


     <statement> ::= <if> | <while> | <assignment>


The corresponding code is:


{--------------------------------------------------------------}
{ Parse and Translate a Block of Statements }

procedure Block;
begin
   while not(Look in ['e', 'l']) do begin
      case Look of
       'i': DoIf;
       'w': DoWhile;
      else Assignment;
      end;
   end;
end;
{--------------------------------------------------------------}


OK,  add the routines I've given, compile and  test  them.    You
should be able to parse the single-character versions  of  any of
the control constructs.  It's looking pretty good!

As a matter  of  fact, except for the single-character limitation
we've got a virtually complete version of TINY.  I call  it, with
tongue planted firmly in cheek, TINY Version 0.1.


LEXICAL SCANNING

Of course, you know what's next:  We have to convert  the program
so that  it can deal with multi-character keywords, newlines, and
whitespace.   We have just gone through all  that  in  Part  VII.
We'll use the distributed scanner  technique that I showed you in
that  installment.    The  actual  implementation  is   a  little
different because the way I'm handling newlines is different.

To begin with, let's simply  allow for whitespace.  This involves
only adding calls to SkipWhite at the end of the  three routines,
GetName, GetNum, and Match.    A call to SkipWhite in Init primes
the pump in case there are leading spaces.

Next, we need to deal with  newlines.   This is really a two-step
process,  since  the  treatment  of  the  newlines  with  single-
character tokens is different from that for multi-character ones.
We can eliminate some work by doing both  steps  at  once,  but I
feel safer taking things one step at a time.

Insert the new procedure:


{--------------------------------------------------------------}
{ Skip Over an End-of-Line }

procedure NewLine;
begin
   while Look = CR do begin
      GetChar;
      if Look = LF then GetChar;
      SkipWhite;
   end;
end;
{--------------------------------------------------------------}


Note that  we  have  seen  this  procedure  before in the form of
Procedure Fin.  I've changed the name since this  new  one  seems
more descriptive of the actual function.  I've  also  changed the
code  to  allow  for multiple newlines and lines with nothing but
white space.

The next step is to insert calls to NewLine wherever we  decide a
newline is permissible.  As I've pointed out before, this  can be
very different in different languages.   In TINY, I've decided to
allow them virtually anywhere.  This means that we need  calls to
NewLine at the BEGINNING (not the end, as with SkipWhite)  of the
procedures GetName, GetNum, and Match.

For procedures that have while loops, such as TopDecl, we  need a
call  to NewLine at the beginning of the  procedure  AND  at  the
bottom  of  each  loop.  That way, we can be assured that NewLine
has just been called at the beginning of each  pass  through  the
loop.

If you've got all this done, try the program out and  verify that
it will indeed handle white space and newlines.

If it does, then we're  ready to deal with multi-character tokens
and keywords.   To begin, add the additional declarations (copied
almost verbatim from Part VII):


{--------------------------------------------------------------}
{ Type Declarations }

type Symbol = string[8];

     SymTab = array[1..1000] of Symbol;

     TabPtr = ^SymTab;


{--------------------------------------------------------------}
{ Variable Declarations }

var Look : char;             { Lookahead Character }
    Token: char;             { Encoded Token       }
    Value: string[16];       { Unencoded Token     }

    ST: Array['A'..'Z'] of char;

{--------------------------------------------------------------}
{ Definition of Keywords and Token Types }

const NKW =   9;
      NKW1 = 10;

const KWlist: array[1..NKW] of Symbol =
              ('IF', 'ELSE', 'ENDIF', 'WHILE', 'ENDWHILE',
               'VAR', 'BEGIN', 'END', 'PROGRAM');

const KWcode: string[NKW1] = 'xilewevbep';
{--------------------------------------------------------------}


Next, add the three procedures, also from Part VII:


{--------------------------------------------------------------}
{ Table Lookup }

function Lookup(T: TabPtr; s: string; n: integer): integer;
var i: integer;
    found: Boolean;
begin
   found := false;
   i := n;
   while (i > 0) and not found do
      if s = T^[i] then
         found := true
      else
         dec(i);
   Lookup := i;
end;
{--------------------------------------------------------------}
.
.
{--------------------------------------------------------------}
{ Get an Identifier and Scan it for Keywords }

procedure Scan;
begin
   GetName;
   Token := KWcode[Lookup(Addr(KWlist), Value, NKW) + 1];
end;
{--------------------------------------------------------------}
.
.
{--------------------------------------------------------------}
{ Match a Specific Input String }

procedure MatchString(x: string);
begin
   if Value <> x then Expected('''' + x + '''');
end;
{--------------------------------------------------------------}


Now, we have to make a  fairly  large number of subtle changes to
the remaining procedures.  First,  we  must  change  the function
GetName to a procedure, again as we did in Part VII:


{--------------------------------------------------------------}
{ Get an Identifier }

procedure GetName;
begin
   NewLine;
   if not IsAlpha(Look) then Expected('Name');
   Value := '';
   while IsAlNum(Look) do begin
      Value := Value + UpCase(Look);
      GetChar;
   end;
   SkipWhite;
end;
{--------------------------------------------------------------}


Note that this procedure leaves its result in  the  global string
Value.

Next, we have to change every reference to GetName to reflect its
new form. These occur in Factor, Assignment, and Decl:


{---------------------------------------------------------------}
{ Parse and Translate a Math Factor }

procedure BoolExpression; Forward;

procedure Factor;
begin
   if Look = '(' then begin
      Match('(');
      BoolExpression;
      Match(')');
      end
   else if IsAlpha(Look) then begin
      GetName;
      LoadVar(Value[1]);
      end
   else
      LoadConst(GetNum);
end;
{--------------------------------------------------------------}
.
.
{--------------------------------------------------------------}
{ Parse and Translate an Assignment Statement }

procedure Assignment;
var Name: char;
begin
   Name := Value[1];
   Match('=');
   BoolExpression;
   Store(Name);
end;
{---------------------------------------------------------------}
.
.
{--------------------------------------------------------------}
{ Parse and Translate a Data Declaration }

procedure Decl;
begin
   GetName;
   Alloc(Value[1]);
   while Look = ',' do begin
      Match(',');
      GetName;
      Alloc(Value[1]);
   end;
end;
{--------------------------------------------------------------}


(Note that we're still  only  allowing  single-character variable
names,  so we take the easy way out here and simply use the first
character of the string.)

Finally, we must make the changes to use Token instead of Look as
the  test  character  and to call Scan at the appropriate places.
Mostly, this  involves  deleting  calls  to  Match,  occasionally
replacing calls to  Match  by calls to MatchString, and Replacing
calls  to  NewLine  by  calls  to  Scan.    Here are the affected
routines:

{---------------------------------------------------------------}
{ Recognize and Translate an IF Construct }

procedure Block; Forward;


procedure DoIf;
var L1, L2: string;
begin
   BoolExpression;
   L1 := NewLabel;
   L2 := L1;
   BranchFalse(L1);
   Block;
   if Token = 'l' then begin
      L2 := NewLabel;
      Branch(L2);
      PostLabel(L1);
      Block;
   end;
   PostLabel(L2);
   MatchString('ENDIF');
end;


{--------------------------------------------------------------}
{ Parse and Translate a WHILE Statement }

procedure DoWhile;
var L1, L2: string;
begin
   L1 := NewLabel;
   L2 := NewLabel;
   PostLabel(L1);
   BoolExpression;
   BranchFalse(L2);
   Block;
   MatchString('ENDWHILE');
   Branch(L1);
   PostLabel(L2);
end;


{--------------------------------------------------------------}
{ Parse and Translate a Block of Statements }

procedure Block;
begin
   Scan;
   while not(Token in ['e', 'l']) do begin
      case Token of
       'i': DoIf;
       'w': DoWhile;
      else Assignment;
      end;
      Scan;
   end;
end;


{--------------------------------------------------------------}
{ Parse and Translate Global Declarations }

procedure TopDecls;
begin
   Scan;
   while Token <> 'b' do begin
      case Token of
        'v': Decl;
      else Abort('Unrecognized Keyword ' + Value);
      end;
      Scan;
   end;
end;


{--------------------------------------------------------------}
{ Parse and Translate a Main Program }

procedure Main;
begin
   MatchString('BEGIN');
   Prolog;
   Block;
   MatchString('END');
   Epilog;
end;

{--------------------------------------------------------------}
{  Parse and Translate a Program }

procedure Prog;
begin
   MatchString('PROGRAM');
   Header;
   TopDecls;
   Main;
   Match('.');
end;


{--------------------------------------------------------------}
{ Initialize }

procedure Init;
var i: char;
begin
   for i := 'A' to 'Z' do
      ST[i] := ' ';
   GetChar;
   Scan;
end;
{--------------------------------------------------------------}


That should do  it.    If  all  the changes got in correctly, you
should now be parsing programs that look like programs.   (If you
didn't  make  it  through all the  changes,  don't  despair.    A
complete listing of the final form is given later.)

Did it work?  If so, then we're just about home.  In fact, with a
few minor  exceptions we've already got a compiler that's usable.
There are still a few areas that need improvement.


MULTI-CHARACTER VARIABLE NAMES

One of those is  the  restriction  that  we still have, requiring
single-character variable names.    Now that we can handle multi-
character keywords, this one  begins  to  look  very much like an
arbitrary  and  unnecessary  limitation.    And  indeed   it  is.
Basically, its only virtue is  that it permits a trivially simple
implementation  of  the   symbol   table.    But  that's  just  a
convenience to the compiler writers, and needs to be eliminated.

We've done this step before.  This time, as usual, I'm doing it a
little differently.  I think  the approach used here keeps things
just about as simple as possible.

The natural  way  to  implement  a  symbol  table in Pascal is by
declaring a record type, and making the symbol table an  array of
such records.  Here, though, we don't really need  a  type  field
yet  (there is only one kind of entry allowed so far), so we only
need an array of symbols.  This has the advantage that we can use
the existing procedure Lookup to  search the symbol table as well
as the  keyword  list.    As it turns out, even when we need more
fields we can still use the same approach, simply by  storing the
other fields in separate arrays.

OK, here are the changes that  need  to  be made.  First, add the
new typed constant:


      NEntry: integer = 0;


Then change the definition of the symbol table as follows:


const MaxEntry = 100;

var ST   : array[1..MaxEntry] of Symbol;


(Note that ST is _NOT_ declared as a SymTab.  That declaration is
a phony one to get Lookup to work.  A SymTab  would  take  up too
much RAM space, and so one is never actually allocated.)

Next, we need to replace InTable:


{--------------------------------------------------------------}
{ Look for Symbol in Table }

function InTable(n: Symbol): Boolean;
begin
   InTable := Lookup(@ST, n, MaxEntry) <> 0;
end;
{--------------------------------------------------------------}


We also need a new procedure, AddEntry, that adds a new  entry to
the table:


{--------------------------------------------------------------}
{ Add a New Entry to Symbol Table }

procedure AddEntry(N: Symbol; T: char);
begin
   if InTable(N) then Abort('Duplicate Identifier ' + N);
   if NEntry = MaxEntry then Abort('Symbol Table Full');
   Inc(NEntry);
   ST[NEntry] := N;
   SType[NEntry] := T;
end;
{--------------------------------------------------------------}


This procedure is called by Alloc:


{--------------------------------------------------------------}
{ Allocate Storage for a Variable }

procedure Alloc(N: Symbol);
begin
   if InTable(N) then Abort('Duplicate Variable Name ' + N);
   AddEntry(N, 'v');
.
.
.
{--------------------------------------------------------------}


Finally, we must change all the routines that currently treat the
variable name as a single character.  These include   LoadVar and
Store (just change the  type  from  char  to string), and Factor,
Assignment, and Decl (just change Value[1] to Value).

One  last  thing:  change  procedure  Init to clear the array  as
shown:


{--------------------------------------------------------------}
{ Initialize }

procedure Init;
var i: integer;
begin
   for i := 1 to MaxEntry do begin
      ST[i] := '';
      SType[i] := ' ';
   end;
   GetChar;
   Scan;
end;
{--------------------------------------------------------------}


That should do it.  Try it out and verify  that  you can, indeed,
use multi-character variable names.


MORE RELOPS

We still have one remaining single-character restriction: the one
on relops.  Some of the relops are indeed single  characters, but
others  require two.  These are '<=' and '>='.  I also prefer the
Pascal '<>' for "not equals,"  instead of '#'.

If you'll recall, in Part VII I pointed out that the conventional
way  to  deal  with  relops  is  to  include them in the list  of
keywords, and let the  lexical  scanner  find  them.  But, again,
this requires scanning throughout the expression parsing process,
whereas so far we've been able to limit the use of the scanner to
the beginning of a statement.

I mentioned then that we can still get away with this,  since the
multi-character relops are so few  and so limited in their usage.
It's easy to just treat them as special cases and handle  them in
an ad hoc manner.

The changes required affect only the code generation routines and
procedures Relation and friends.   First, we're going to need two
more code generation routines:


{---------------------------------------------------------------}
{ Set D0 If Compare was <= }

procedure SetLessOrEqual;
begin
   EmitLn('SGE D0');
   EmitLn('EXT D0');
end;


{---------------------------------------------------------------}
{ Set D0 If Compare was >= }

procedure SetGreaterOrEqual;
begin
   EmitLn('SLE D0');
   EmitLn('EXT D0');
end;
{---------------------------------------------------------------}


Then, modify the relation parsing routines as shown below:


{---------------------------------------------------------------}
{ Recognize and Translate a Relational "Less Than or Equal" }

procedure LessOrEqual;
begin
   Match('=');
   Expression;
   PopCompare;
   SetLessOrEqual;
end;


{---------------------------------------------------------------}
{ Recognize and Translate a Relational "Not Equals" }

procedure NotEqual;
begin
   Match('>');
   Expression;
   PopCompare;
   SetNEqual;
end;


{---------------------------------------------------------------}
{ Recognize and Translate a Relational "Less Than" }

procedure Less;
begin
   Match('<');
   case Look of
     '=': LessOrEqual;
     '>': NotEqual;
   else begin
           Expression;
           PopCompare;
           SetLess;
        end;
   end;
end;


{---------------------------------------------------------------}
{ Recognize and Translate a Relational "Greater Than" }

procedure Greater;
begin
   Match('>');
   if Look = '=' then begin
      Match('=');
      Expression;
      PopCompare;
      SetGreaterOrEqual;
      end
   else begin
      Expression;
      PopCompare;
      SetGreater;
   end;
end;
{---------------------------------------------------------------}


That's all it takes.  Now  you  can  process all the relops.  Try
it.


INPUT/OUTPUT

We  now  have  a complete, working language, except for one minor
embarassment: we have no way to get data in or out.  We need some
I/O.

Now, the convention these days, established in C and continued in
Ada and Modula 2, is to leave I/O statements out of  the language
itself,  and  just  include them in the subroutine library.  That
would  be  fine, except that so far  we  have  no  provision  for
subroutines.  Anyhow, with this approach you run into the problem
of variable-length argument lists.  In Pascal, the I/O statements
are built into the language because they are the  only  ones  for
which  the  argument  list can have a variable number of entries.
In C, we settle for kludges like scanf and printf, and  must pass
the argument count to the called procedure.  In Ada and  Modula 2
we must use the  awkward  (and SLOW!) approach of a separate call
for each argument.

So I think I prefer the  Pascal  approach of building the I/O in,
even though we don't need to.

As  usual,  for  this we need some more code generation routines.
These turn out  to be the easiest of all, because all we do is to
call library procedures to do the work:


{---------------------------------------------------------------}
{ Read Variable to Primary Register }

procedure ReadVar;
begin
   EmitLn('BSR READ');
   Store(Value);
end;


{---------------------------------------------------------------}
{ Write Variable from Primary Register }

procedure WriteVar;
begin
   EmitLn('BSR WRITE');
end;
{--------------------------------------------------------------}


The idea is that READ loads the value from input  to  the D0, and
WRITE outputs it from there.

These two procedures represent  our  first  encounter with a need
for library procedures ... the components of a  Run  Time Library
(RTL).    Of  course, someone (namely  us)  has  to  write  these
routines, but they're not  part  of the compiler itself.  I won't
even bother  showing the routines here, since these are obviously
very much OS-dependent.   I  _WILL_  simply  say that for SK*DOS,
they  are  particularly  simple ... almost trivial.  One reason I
won't show them here is that  you  can add all kinds of fanciness
to the things, for  example  by prompting in READ for the inputs,
and by giving the user a chance to reenter a bad input.

But that is really separate from compiler design, so for now I'll
just assume that a library call TINYLIB.LIB exists.  Since we now
need  it  loaded,  we need to add a statement to  include  it  in
procedure Header:


{--------------------------------------------------------------}
{ Write Header Info }

procedure Header;
begin

   WriteLn('WARMST', TAB, 'EQU $A01E');
   EmitLn('LIB TINYLIB');
end;
{--------------------------------------------------------------}

That takes care of that part.  Now, we also need to recognize the
read  and  write  commands.  We can do this by  adding  two  more
keywords to our list:


{--------------------------------------------------------------}
{ Definition of Keywords and Token Types }

const NKW =   11;
      NKW1 = 12;

const KWlist: array[1..NKW] of Symbol =
              ('IF', 'ELSE', 'ENDIF', 'WHILE', 'ENDWHILE',
               'READ',    'WRITE',    'VAR',    'BEGIN',   'END',
'PROGRAM');

const KWcode: string[NKW1] = 'xileweRWvbep';
{--------------------------------------------------------------}


(Note how I'm using upper case codes here to avoid  conflict with
the 'w' of WHILE.)

Next, we need procedures for processing the  read/write statement
and its argument list:


{--------------------------------------------------------------}
{ Process a Read Statement }
procedure DoRead;
begin
   Match('(');
   GetName;
   ReadVar;
   while Look = ',' do begin
      Match(',');
      GetName;
      ReadVar;
   end;
   Match(')');
end;


{--------------------------------------------------------------}
{ Process a Write Statement }

procedure DoWrite;
begin
   Match('(');
   Expression;
   WriteVar;
   while Look = ',' do begin
      Match(',');
      Expression;
      WriteVar;
   end;
   Match(')');
end;
{--------------------------------------------------------------}


Finally,  we  must  expand  procedure  Block  to  handle the  new
statement types:


{--------------------------------------------------------------}
{ Parse and Translate a Block of Statements }

procedure Block;
begin
   Scan;
   while not(Token in ['e', 'l']) do begin
      case Token of
       'i': DoIf;
       'w': DoWhile;
       'R': DoRead;
       'W': DoWrite;
      else Assignment;
      end;
      Scan;
   end;
end;
{--------------------------------------------------------------}

That's all there is to it.  _NOW_ we have a language!


CONCLUSION

At this point we have TINY completely defined.  It's not much ...
actually a toy  compiler.    TINY  has  only one data type and no
subroutines  ... but it's a complete,  usable  language.    While
you're not likely to be able to write another compiler in  it, or
do anything else very seriously, you could write programs to read
some input, perform calculations,  and  output  the results.  Not
too bad for a toy.

Most importantly, we have a firm base upon which to build further
extensions.  I know you'll be glad to hear this: this is the last
time  I'll  start  over in building a parser ... from  now  on  I
intend to just add features to  TINY  until it becomes KISS.  Oh,
there'll be other times we will  need  to try things out with new
copies  of  the  Cradle, but once we've found out how to do those
things they'll be incorporated into TINY.

What  will  those  features  be?    Well,  for starters  we  need
subroutines and functions.    Then  we  need to be able to handle
different types, including arrays, strings, and other structures.
Then we need to deal with the idea of pointers.  All this will be
upcoming in future installments.

See you then.

For references purposes, the complete listing of TINY Version 1.0
is shown below:


{--------------------------------------------------------------}
program Tiny10;

{--------------------------------------------------------------}
{ Constant Declarations }

const TAB = ^I;
      CR  = ^M;
      LF  = ^J;

      LCount: integer = 0;
      NEntry: integer = 0;


{--------------------------------------------------------------}
{ Type Declarations }

type Symbol = string[8];

     SymTab = array[1..1000] of Symbol;
     TabPtr = ^SymTab;


{--------------------------------------------------------------}
{ Variable Declarations }

var Look : char;             { Lookahead Character }
    Token: char;             { Encoded Token       }
    Value: string[16];       { Unencoded Token     }


const MaxEntry = 100;

var ST   : array[1..MaxEntry] of Symbol;
    SType: array[1..MaxEntry] of char;


{--------------------------------------------------------------}
{ Definition of Keywords and Token Types }

const NKW =   11;
      NKW1 = 12;

const KWlist: array[1..NKW] of Symbol =
              ('IF', 'ELSE', 'ENDIF', 'WHILE', 'ENDWHILE',
               'READ',    'WRITE',    'VAR',    'BEGIN',   'END',
'PROGRAM');

const KWcode: string[NKW1] = 'xileweRWvbep';


{--------------------------------------------------------------}
{ Read New Character From Input Stream }

procedure GetChar;
begin
   Read(Look);
end;

{--------------------------------------------------------------}
{ Report an Error }

procedure Error(s: string);
begin
   WriteLn;
   WriteLn(^G, 'Error: ', s, '.');
end;


{--------------------------------------------------------------}
{ Report Error and Halt }

procedure Abort(s: string);
begin
   Error(s);
   Halt;
end;


{--------------------------------------------------------------}
{ Report What Was Expected }

procedure Expected(s: string);
begin
   Abort(s + ' Expected');
end;

{--------------------------------------------------------------}
{ Report an Undefined Identifier }

procedure Undefined(n: string);
begin
   Abort('Undefined Identifier ' + n);
end;


{--------------------------------------------------------------}
{ Recognize an Alpha Character }

function IsAlpha(c: char): boolean;
begin
   IsAlpha := UpCase(c) in ['A'..'Z'];
end;


{--------------------------------------------------------------}
{ Recognize a Decimal Digit }

function IsDigit(c: char): boolean;
begin
   IsDigit := c in ['0'..'9'];
end;


{--------------------------------------------------------------}
{ Recognize an AlphaNumeric Character }

function IsAlNum(c: char): boolean;
begin
   IsAlNum := IsAlpha(c) or IsDigit(c);
end;


{--------------------------------------------------------------}
{ Recognize an Addop }

function IsAddop(c: char): boolean;
begin
   IsAddop := c in ['+', '-'];
end;


{--------------------------------------------------------------}
{ Recognize a Mulop }

function IsMulop(c: char): boolean;
begin
   IsMulop := c in ['*', '/'];
end;


{--------------------------------------------------------------}
{ Recognize a Boolean Orop }

function IsOrop(c: char): boolean;
begin
   IsOrop := c in ['|', '~'];
end;


{--------------------------------------------------------------}
{ Recognize a Relop }

function IsRelop(c: char): boolean;
begin
   IsRelop := c in ['=', '#', '<', '>'];
end;


{--------------------------------------------------------------}
{ Recognize White Space }

function IsWhite(c: char): boolean;
begin
   IsWhite := c in [' ', TAB];
end;


{--------------------------------------------------------------}
{ Skip Over Leading White Space }

procedure SkipWhite;
begin
   while IsWhite(Look) do
      GetChar;
end;


{--------------------------------------------------------------}
{ Skip Over an End-of-Line }

procedure NewLine;
begin
   while Look = CR do begin
      GetChar;
      if Look = LF then GetChar;
      SkipWhite;
   end;
end;


{--------------------------------------------------------------}
{ Match a Specific Input Character }

procedure Match(x: char);
begin
   NewLine;
   if Look = x then GetChar
   else Expected('''' + x + '''');
   SkipWhite;
end;


{--------------------------------------------------------------}
{ Table Lookup }

function Lookup(T: TabPtr; s: string; n: integer): integer;
var i: integer;
    found: Boolean;
begin
   found := false;
   i := n;
   while (i > 0) and not found do
      if s = T^[i] then
         found := true
      else
         dec(i);
   Lookup := i;
end;


{--------------------------------------------------------------}
{ Locate a Symbol in Table }
{ Returns the index of the entry.  Zero if not present. }

function Locate(N: Symbol): integer;
begin
   Locate := Lookup(@ST, n, MaxEntry);
end;


{--------------------------------------------------------------}
{ Look for Symbol in Table }

function InTable(n: Symbol): Boolean;
begin
   InTable := Lookup(@ST, n, MaxEntry) <> 0;
end;


{--------------------------------------------------------------}
{ Add a New Entry to Symbol Table }

procedure AddEntry(N: Symbol; T: char);
begin
   if InTable(N) then Abort('Duplicate Identifier ' + N);
   if NEntry = MaxEntry then Abort('Symbol Table Full');
   Inc(NEntry);
   ST[NEntry] := N;
   SType[NEntry] := T;
end;


{--------------------------------------------------------------}
{ Get an Identifier }

procedure GetName;
begin
   NewLine;
   if not IsAlpha(Look) then Expected('Name');
   Value := '';
   while IsAlNum(Look) do begin
      Value := Value + UpCase(Look);
      GetChar;
   end;
   SkipWhite;
end;


{--------------------------------------------------------------}
{ Get a Number }

function GetNum: integer;
var Val: integer;
begin
   NewLine;
   if not IsDigit(Look) then Expected('Integer');
   Val := 0;
   while IsDigit(Look) do begin
      Val := 10 * Val + Ord(Look) - Ord('0');
      GetChar;
   end;
   GetNum := Val;
   SkipWhite;
end;


{--------------------------------------------------------------}
{ Get an Identifier and Scan it for Keywords }

procedure Scan;
begin
   GetName;
   Token := KWcode[Lookup(Addr(KWlist), Value, NKW) + 1];
end;


{--------------------------------------------------------------}
{ Match a Specific Input String }

procedure MatchString(x: string);
begin
   if Value <> x then Expected('''' + x + '''');
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;


{--------------------------------------------------------------}
{ Generate a Unique Label }

function NewLabel: string;
var S: string;
begin
   Str(LCount, S);
   NewLabel := 'L' + S;
   Inc(LCount);
end;


{--------------------------------------------------------------}
{ Post a Label To Output }

procedure PostLabel(L: string);
begin
   WriteLn(L, ':');
end;


{---------------------------------------------------------------}
{ Clear the Primary Register }

procedure Clear;
begin
   EmitLn('CLR D0');
end;


{---------------------------------------------------------------}
{ Negate the Primary Register }

procedure Negate;
begin
   EmitLn('NEG D0');
end;


{---------------------------------------------------------------}
{ Complement the Primary Register }

procedure NotIt;
begin
   EmitLn('NOT D0');
end;


{---------------------------------------------------------------}
{ Load a Constant Value to Primary Register }

procedure LoadConst(n: integer);
begin
   Emit('MOVE #');
   WriteLn(n, ',D0');
end;


{---------------------------------------------------------------}
{ Load a Variable to Primary Register }

procedure LoadVar(Name: string);
begin
   if not InTable(Name) then Undefined(Name);
   EmitLn('MOVE ' + Name + '(PC),D0');
end;


{---------------------------------------------------------------}
{ Push Primary onto Stack }

procedure Push;
begin
   EmitLn('MOVE D0,-(SP)');
end;


{---------------------------------------------------------------}
{ Add Top of Stack to Primary }

procedure PopAdd;
begin
   EmitLn('ADD (SP)+,D0');
end;


{---------------------------------------------------------------}
{ Subtract Primary from Top of Stack }

procedure PopSub;
begin
   EmitLn('SUB (SP)+,D0');
   EmitLn('NEG D0');
end;


{---------------------------------------------------------------}
{ Multiply Top of Stack by Primary }

procedure PopMul;
begin
   EmitLn('MULS (SP)+,D0');
end;


{---------------------------------------------------------------}
{ Divide Top of Stack by Primary }

procedure PopDiv;
begin
   EmitLn('MOVE (SP)+,D7');
   EmitLn('EXT.L D7');
   EmitLn('DIVS D0,D7');
   EmitLn('MOVE D7,D0');
end;


{---------------------------------------------------------------}
{ AND Top of Stack with Primary }

procedure PopAnd;
begin
   EmitLn('AND (SP)+,D0');
end;


{---------------------------------------------------------------}
{ OR Top of Stack with Primary }

procedure PopOr;
begin
   EmitLn('OR (SP)+,D0');
end;


{---------------------------------------------------------------}
{ XOR Top of Stack with Primary }

procedure PopXor;
begin
   EmitLn('EOR (SP)+,D0');
end;


{---------------------------------------------------------------}
{ Compare Top of Stack with Primary }

procedure PopCompare;
begin
   EmitLn('CMP (SP)+,D0');
end;


{---------------------------------------------------------------}
{ Set D0 If Compare was = }

procedure SetEqual;
begin
   EmitLn('SEQ D0');
   EmitLn('EXT D0');
end;


{---------------------------------------------------------------}
{ Set D0 If Compare was != }

procedure SetNEqual;
begin
   EmitLn('SNE D0');
   EmitLn('EXT D0');
end;


{---------------------------------------------------------------}
{ Set D0 If Compare was > }

procedure SetGreater;
begin
   EmitLn('SLT D0');
   EmitLn('EXT D0');
end;


{---------------------------------------------------------------}
{ Set D0 If Compare was < }

procedure SetLess;
begin
   EmitLn('SGT D0');
   EmitLn('EXT D0');
end;


{---------------------------------------------------------------}
{ Set D0 If Compare was <= }

procedure SetLessOrEqual;
begin
   EmitLn('SGE D0');
   EmitLn('EXT D0');
end;


{---------------------------------------------------------------}
{ Set D0 If Compare was >= }

procedure SetGreaterOrEqual;
begin
   EmitLn('SLE D0');
   EmitLn('EXT D0');
end;


{---------------------------------------------------------------}
{ Store Primary to Variable }

procedure Store(Name: string);
begin
   if not InTable(Name) then Undefined(Name);
   EmitLn('LEA ' + Name + '(PC),A0');
   EmitLn('MOVE D0,(A0)')
end;


{---------------------------------------------------------------}
{ Branch Unconditional  }

procedure Branch(L: string);
begin
   EmitLn('BRA ' + L);
end;


{---------------------------------------------------------------}
{ Branch False }

procedure BranchFalse(L: string);
begin
   EmitLn('TST D0');
   EmitLn('BEQ ' + L);
end;


{---------------------------------------------------------------}
{ Read Variable to Primary Register }

procedure ReadVar;
begin
   EmitLn('BSR READ');
   Store(Value[1]);
end;


{ Write Variable from Primary Register }

procedure WriteVar;
begin
   EmitLn('BSR WRITE');
end;


{--------------------------------------------------------------}
{ Write Header Info }

procedure Header;
begin
   WriteLn('WARMST', TAB, 'EQU $A01E');
end;


{--------------------------------------------------------------}
{ Write the Prolog }

procedure Prolog;
begin
   PostLabel('MAIN');
end;


{--------------------------------------------------------------}
{ Write the Epilog }

procedure Epilog;
begin
   EmitLn('DC WARMST');
   EmitLn('END MAIN');
end;


{---------------------------------------------------------------}
{ Parse and Translate a Math Factor }

procedure BoolExpression; Forward;

procedure Factor;
begin
   if Look = '(' then begin
      Match('(');
      BoolExpression;
      Match(')');
      end
   else if IsAlpha(Look) then begin
      GetName;
      LoadVar(Value);
      end
   else
      LoadConst(GetNum);
end;


{--------------------------------------------------------------}
{ Parse and Translate a Negative Factor }

procedure NegFactor;
begin
   Match('-');
   if IsDigit(Look) then
      LoadConst(-GetNum)
   else begin
      Factor;
      Negate;
   end;
end;


{--------------------------------------------------------------}
{ Parse and Translate a Leading Factor }

procedure FirstFactor;
begin
   case Look of
     '+': begin
             Match('+');
             Factor;
          end;
     '-': NegFactor;
   else  Factor;
   end;
end;


{--------------------------------------------------------------}
{ Recognize and Translate a Multiply }

procedure Multiply;
begin
   Match('*');
   Factor;
   PopMul;
end;


{-------------------------------------------------------------}
{ Recognize and Translate a Divide }

procedure Divide;
begin
   Match('/');
   Factor;
   PopDiv;
end;


{---------------------------------------------------------------}
{ Common Code Used by Term and FirstTerm }

procedure Term1;
begin
   while IsMulop(Look) do begin
      Push;
      case Look of
       '*': Multiply;
       '/': Divide;
      end;
   end;
end;


{---------------------------------------------------------------}
{ Parse and Translate a Math Term }

procedure Term;
begin
   Factor;
   Term1;
end;


{---------------------------------------------------------------}
{ Parse and Translate a Leading Term }

procedure FirstTerm;
begin
   FirstFactor;
   Term1;
end;


{--------------------------------------------------------------}
{ Recognize and Translate an Add }

procedure Add;
begin
   Match('+');
   Term;
   PopAdd;
end;


{-------------------------------------------------------------}
{ Recognize and Translate a Subtract }

procedure Subtract;
begin
   Match('-');
   Term;
   PopSub;
end;


{---------------------------------------------------------------}
{ Parse and Translate an Expression }

procedure Expression;
begin
   FirstTerm;
   while IsAddop(Look) do begin
      Push;
      case Look of
       '+': Add;
       '-': Subtract;
      end;
   end;
end;


{---------------------------------------------------------------}
{ Recognize and Translate a Relational "Equals" }

procedure Equal;
begin
   Match('=');
   Expression;
   PopCompare;
   SetEqual;
end;


{---------------------------------------------------------------}
{ Recognize and Translate a Relational "Less Than or Equal" }

procedure LessOrEqual;
begin
   Match('=');
   Expression;
   PopCompare;
   SetLessOrEqual;
end;


{---------------------------------------------------------------}
{ Recognize and Translate a Relational "Not Equals" }

procedure NotEqual;
begin
   Match('>');
   Expression;
   PopCompare;
   SetNEqual;
end;


{---------------------------------------------------------------}
{ Recognize and Translate a Relational "Less Than" }

procedure Less;
begin
   Match('<');
   case Look of
     '=': LessOrEqual;
     '>': NotEqual;
   else begin
           Expression;
           PopCompare;
           SetLess;
        end;
   end;
end;


{---------------------------------------------------------------}
{ Recognize and Translate a Relational "Greater Than" }

procedure Greater;
begin
   Match('>');
   if Look = '=' then begin
      Match('=');
      Expression;
      PopCompare;
      SetGreaterOrEqual;
      end
   else begin
      Expression;
      PopCompare;
      SetGreater;
   end;
end;


{---------------------------------------------------------------}
{ Parse and Translate a Relation }


procedure Relation;
begin
   Expression;
   if IsRelop(Look) then begin
      Push;
      case Look of
       '=': Equal;
       '<': Less;
       '>': Greater;
      end;
   end;
end;


{---------------------------------------------------------------}
{ Parse and Translate a Boolean Factor with Leading NOT }

procedure NotFactor;
begin
   if Look = '!' then begin
      Match('!');
      Relation;
      NotIt;
      end
   else
      Relation;
end;


{---------------------------------------------------------------}
{ Parse and Translate a Boolean Term }

procedure BoolTerm;
begin
   NotFactor;
   while Look = '&' do begin
      Push;
      Match('&');
      NotFactor;
      PopAnd;
   end;
end;


{--------------------------------------------------------------}
{ Recognize and Translate a Boolean OR }

procedure BoolOr;
begin
   Match('|');
   BoolTerm;
   PopOr;
end;


{--------------------------------------------------------------}
{ Recognize and Translate an Exclusive Or }

procedure BoolXor;
begin
   Match('~');
   BoolTerm;
   PopXor;
end;


{---------------------------------------------------------------}
{ Parse and Translate a Boolean Expression }

procedure BoolExpression;
begin
   BoolTerm;
   while IsOrOp(Look) do begin
      Push;
      case Look of
       '|': BoolOr;
       '~': BoolXor;
      end;
   end;
end;


{--------------------------------------------------------------}
{ Parse and Translate an Assignment Statement }

procedure Assignment;
var Name: string;
begin
   Name := Value;
   Match('=');
   BoolExpression;
   Store(Name);
end;


{---------------------------------------------------------------}
{ Recognize and Translate an IF Construct }

procedure Block; Forward;


procedure DoIf;
var L1, L2: string;
begin
   BoolExpression;
   L1 := NewLabel;
   L2 := L1;
   BranchFalse(L1);
   Block;
   if Token = 'l' then begin
      L2 := NewLabel;
      Branch(L2);
      PostLabel(L1);
      Block;
   end;
   PostLabel(L2);
   MatchString('ENDIF');
end;


{--------------------------------------------------------------}
{ Parse and Translate a WHILE Statement }

procedure DoWhile;
var L1, L2: string;
begin
   L1 := NewLabel;
   L2 := NewLabel;
   PostLabel(L1);
   BoolExpression;
   BranchFalse(L2);
   Block;
   MatchString('ENDWHILE');
   Branch(L1);
   PostLabel(L2);
end;


{--------------------------------------------------------------}
{ Process a Read Statement }

procedure DoRead;
begin
   Match('(');
   GetName;
   ReadVar;
   while Look = ',' do begin
      Match(',');
      GetName;
      ReadVar;
   end;
   Match(')');
end;


{--------------------------------------------------------------}
{ Process a Write Statement }

procedure DoWrite;
begin
   Match('(');
   Expression;
   WriteVar;
   while Look = ',' do begin
      Match(',');
      Expression;
      WriteVar;
   end;
   Match(')');
end;


{--------------------------------------------------------------}
{ Parse and Translate a Block of Statements }

procedure Block;
begin
   Scan;
   while not(Token in ['e', 'l']) do begin
      case Token of
       'i': DoIf;
       'w': DoWhile;
       'R': DoRead;
       'W': DoWrite;
      else Assignment;
      end;
      Scan;
   end;
end;


{--------------------------------------------------------------}
{ Allocate Storage for a Variable }

procedure Alloc(N: Symbol);
begin
   if InTable(N) then Abort('Duplicate Variable Name ' + N);
   AddEntry(N, 'v');
   Write(N, ':', TAB, 'DC ');
   if Look = '=' then begin
      Match('=');
      If Look = '-' then begin
         Write(Look);
         Match('-');
      end;
      WriteLn(GetNum);
      end
   else
      WriteLn('0');
end;


{--------------------------------------------------------------}
{ Parse and Translate a Data Declaration }

procedure Decl;
begin
   GetName;
   Alloc(Value);
   while Look = ',' do begin
      Match(',');
      GetName;
      Alloc(Value);
   end;
end;


{--------------------------------------------------------------}
{ Parse and Translate Global Declarations }

procedure TopDecls;
begin
   Scan;
   while Token <> 'b' do begin
      case Token of
        'v': Decl;
      else Abort('Unrecognized Keyword ' + Value);
      end;
      Scan;
   end;
end;


{--------------------------------------------------------------}
{ Parse and Translate a Main Program }

procedure Main;
begin
   MatchString('BEGIN');
   Prolog;
   Block;
   MatchString('END');
   Epilog;
end;


{--------------------------------------------------------------}
{  Parse and Translate a Program }

procedure Prog;
begin
   MatchString('PROGRAM');
   Header;
   TopDecls;
   Main;
   Match('.');
end;


{--------------------------------------------------------------}
{ Initialize }

procedure Init;
var i: integer;
begin
   for i := 1 to MaxEntry do begin
      ST[i] := '';
      SType[i] := ' ';
   end;
   GetChar;
   Scan;
end;


{--------------------------------------------------------------}
{ Main Program }

begin
   Init;
   Prog;
   if Look <> CR then Abort('Unexpected data after ''.''');
end.
{--------------------------------------------------------------}



*****************************************************************
*                                                               *
*                        COPYRIGHT NOTICE                       *
*                                                               *
*   Copyright (C) 1989 Jack W. Crenshaw. All rights reserved.   *
*                                                               *
*****************************************************************



================================================
FILE: 11/Makefile
================================================
IN=main.c cradle.c
OUT=main
FLAGS=-Wall -Werror

all:
	gcc -o $(OUT) $(IN) $(FLAGS)

run:
	./$(OUT)

.PHONY: clean
clean:
	rm $(OUT)


================================================
FILE: 11/cradle.c
================================================
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

#include "cradle.h"
#include <malloc.h>

#define MaxEntry 100
#define MAX_SYMBOL_LENGTH 10
static int LCount = 0;
static char labelName[MAX_BUF];
char tmp[MAX_BUF];

/*char ST[TABLE_SIZE];*/
static int NEntry = 0;
const char *ST[MaxEntry];
char SType[MaxEntry];


/* Keywords symbol table */
const char const *KWList[] = {
    "IF",
    "ELSE",
    "ENDIF",
    "WHILE",
    "ENDWHILE",
    "VAR",
    "END",
};
const char KWCode[] = "xileweve";
const int KWNum = sizeof(KWList)/sizeof(*KWList);

char Token;             /* current token */
char Value[MAX_BUF];    /* string token of Look */

/* Helper Functions */
char uppercase(char c)
{
    if (IsAlpha(c)) {
        return (c & 0xDF);
    } else {
        return c;
    }
}

/* Table Lookup
 * If the input string matches a table entry, return the entry index, else
 * return -1.
 * *n* is the size of the table */
int Lookup(const char const *table[], const char *string, int n)
{
    int i;
    bool found = false;

    for (i = 0; i < n; ++i) {
        if (strcmp(table[i], string) == 0) {
            found = true;
            break;
        }
    }
    return found ? i : -1;
}

int Locate(char *symbol)
{
    return Lookup(ST, symbol, NEntry);
}

/* Add a new entry to symbol table */
void AddEntry(char *symbol, char type)
{
    CheckDup(symbol);
    if (NEntry == MaxEntry) {
        Abort("Symbol Table Full");
    }

    char *new_entry = (char *)malloc((strlen(symbol)+1)*sizeof(*new_entry));
    if (new_entry == NULL) {
        Abort("AddEntry: not enough memory allocating new_entry.");
    }
    strcpy(new_entry, symbol);
    ST[NEntry] = new_entry;
    SType[NEntry] = type;

    NEntry++;
}

/* Get an Identifier and Scan it for keywords */
void Scan()
{
    if (Token == 'x') {
        int index = Lookup(KWList, Value, KWNum);
        Token = KWCode[index+1];
    }
}

void MatchString(char *str)
{
    if (strcmp(Value, str) != 0) {
        sprintf(tmp, "\"%s\"", str);
        Expected(tmp);
    }
    Next();
}

void GetChar()
{
    Look = getchar();
    /* printf("Getchar: %c\n", Look); */
}


void Error(char *s)
{
    printf("\nError: %s.", s);
}

void Abort(char *s)
{
    Error(s);
    exit(1);
}


void Expected(char *s)
{
    sprintf(tmp, "%s Expected", s);
    Abort(tmp);
}


void Match(char x)
{
    NewLine();
    if(Look == x) {
        GetChar();
    } else {
        sprintf(tmp, "' %c ' ",  x);
        Expected(tmp);
    }
    SkipWhite();
}

int IsAlpha(char c)
{
    return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}

int IsDigit(char c)
{
    return (c >= '0') && (c <= '9');
}

int IsAddop(char c)
{
    return (c == '+') || (c == '-');
}

int IsMulop(char c)
{
    return (c == '*') || (c == '/');
}

int IsOrOp(char c)
{
    return strchr("|~", c) != NULL;
}

int IsRelop(char c)
{
    return strchr("=#<>", c) != NULL;
}

int IsWhite(char c)
{
    return strchr(" \t\r\n", c) != NULL;
}

int IsAlNum(char c)
{
    return IsAlpha(c) || IsDigit(c);
}

void GetName()
{
    SkipWhite();
    if( !IsAlpha(Look)) {
        Expected("Name");
    }

    Token = 'x';
    char *p = Value;
    do {
        *p++ = uppercase(Look);
        GetChar();
    } while(IsAlNum(Look)) ;
    *p = '\0';
}

void GetNum()
{
    SkipWhite();
    if( !IsDigit(Look)) {
        Expected("Integer");
    }

    Token = '#';
    char *p = Value;
    do {
        *p++ = Look;
        GetChar();
    } while (IsDigit(Look));
    *p = '\0';
}

/* Get an operator */
void GetOp()
{
    SkipWhite();
    Token = Look;
    Value[0] = Look;
    Value[1] = '\0';
    GetChar();
}

/* Get the next input token */
void Next()
{
    SkipWhite();
    if (IsAlpha(Look)) {
        GetName();
    } else if (IsDigit(Look)) {
        GetNum();
    } else {
        GetOp();
    }
}

void Emit(char *s)
{
    printf("\t%s", s);
}

void EmitLn(char *s)
{
    Emit(s);
    printf("\n");
}

void Init()
{
    LCount = 0;

    InitTable();
    GetChar();
    Next();
}

void InitTable()
{
    int i;
    for (i = 0; i < MaxEntry; i++) {
        ST[i] = NULL;
        SType[i] = ' ';
    }

}

/* look for symbol in table */
bool InTable(char *symbol)
{
    return Locate(symbol) != -1;
}

/* Check to see if an identifier is in the symbol table,
 * report an error if it's not */
void CheckTable(char *symbol)
{
    if (! InTable(symbol)) {
        Undefined(symbol);
    }
}

void CheckDup(char *symbol)
{
    if (InTable(symbol)) {
        Duplicate(symbol);
    }
}

char *NewLabel()
{
    sprintf(labelName, "L%02d", LCount);
    LCount ++;
    return labelName;
}

void PostLabel(char *label)
{
    printf("%s:\n", label);
}

void SkipWhite()
{
    while (IsWhite(Look)) {
        GetChar();
    }
}

/* Skip over an End-of-Line */
void NewLine()
{
    while(Look == '\n') {
        GetChar();
        if (Look == '\r') {
            GetChar();
        }
        SkipWhite();
    }
}

/* re-targetable routines */
void Clear()
{
    EmitLn("xor %eax, %eax");
}

void Negate()
{
    EmitLn("neg %eax");
}

void LoadConst(char *value)
{
    sprintf(tmp, "movl $%s, %%eax", value);
    EmitLn(tmp);
}

/* Load a variable to primary register */
void LoadVar(char *name)
{
    if (!InTable(name)) {
        char name_string[MAX_BUF];
        Undefined(name_string);
    }
    sprintf(tmp, "movl %s, %%eax", name);
    EmitLn(tmp);
}


/* Push Primary onto stack */
void Push()
{
    EmitLn("pushl %eax");
}

/* Add Top of Stack to primary */
void PopAdd()
{
    EmitLn("addl (%esp), %eax");
    EmitLn("addl $4, %esp");
}

/* Subtract Primary from Top of Stack */
void PopSub()
{
    EmitLn("subl (%esp), %eax");
    EmitLn("neg %eax");
    EmitLn("addl $4, %esp");
}

/* multiply top of stack by primary */
void PopMul()
{
    EmitLn("imull (%esp), %eax");
    EmitLn("addl $4, %esp");
}

/* divide top of stack by primary */
void PopDiv()
{
    /* for a expersion like a/b we have eax=b and %(esp)=a
     * but we need eax=a, and b on the stack
     */
    EmitLn("movl (%esp), %edx");
    EmitLn("addl $4, %esp");
    EmitLn("pushl %eax");
    EmitLn("movl %edx, %eax");

    /* sign extesnion */
    EmitLn("sarl $31, %edx");
    EmitLn("idivl (%esp)");
    EmitLn("addl $4, %esp");
}

/* store primary to variable */
void Store(char *name)
{
    if (!InTable(name)) {
        char name_string[MAX_BUF];
        Undefined(name_string);
    }
    sprintf(tmp, "movl %%eax, %s", name);
    EmitLn(tmp);
}

void Undefined(char *name)
{
    sprintf(tmp, "Undefined Identifier: %s", name);
    Abort(tmp);
}

void Duplicate(char *name)
{
    sprintf(tmp, "Duplicate Identifier: %s", name);
    Abort(tmp);
}

/* Complement the primary register */
void NotIt()
{
    EmitLn("not %eax");
}

/* AND top of Stack with primary */
void PopAnd()
{
    EmitLn("and (%esp), %eax");
    EmitLn("addl $4, %esp");
}

/* OR top of Stack with primary */
void PopOr()
{
    EmitLn("or (%esp), %eax");
    EmitLn("addl $4, %esp");
}

/* XOR top of Stack with primary */
void PopXor()
{
    EmitLn("xor (%esp), %eax");
    EmitLn("addl $4, %esp");
}

/* Compare top of Stack with primary */
void PopCompare()
{
    EmitLn("addl $4, %esp");
    EmitLn("cmp -4(%esp), %eax");
}

/* set %eax if Compare was = */
void SetEqual()
{
    EmitLn("sete %al");
    EmitLn("movsx %al, %eax");
}

/* set %eax if Compare was != */
void SetNEqual()
{
    EmitLn("setne %al");
    EmitLn("movsx %al, %eax");
}

/* set %eax if Compare was > */
void SetGreater()
{
    EmitLn("setl %al");
    EmitLn("movsx %al, %eax");
}

/* set %eax if Compare was >= */
void SetGreaterOrEqual()
{
    EmitLn("setle %al");
    EmitLn("movsx %al, %eax");
}

/* set %eax if Compare was < */
void SetLess()
{
    EmitLn("setg %al");
    EmitLn("movsx %al, %eax");
}

/* set %eax if Compare was <= */
void SetLessOrEqual()
{
    EmitLn("setge %al");
    EmitLn("movsx %al, %eax");
}

/* Branch unconditional */
void Branch(char *label)
{
    sprintf(tmp, "jmp %s", label);
    EmitLn(tmp);
}

/* Branch False */
void BranchFalse(char *label)
{
    EmitLn("test $1, %eax");
    sprintf(tmp, "jz %s", label);
    EmitLn(tmp);
}


================================================
FILE: 11/cradle.h
================================================
#ifndef _CRADLE_H
#define _CRADLE_H
#include <stdbool.h>

#define MAX_BUF 100
#define MaxEntry 100
extern char tmp[MAX_BUF];
extern const char *ST[];
extern char SType[];
extern char Token;
extern char Value[MAX_BUF];
char Look;

void GetChar();

void Error(char *s);
void Abort(char *s);
void Expected(char *s);
void Match(char x);
void MatchString(char *str);

int IsAlpha(char c);
int IsDigit(char c);
int IsAddop(char c);
int IsMulop(char c);
int IsOrOp(char c);
int IsRelop(char c);
int IsWhite(char c);
int IsAlNum(char c);

void GetName();
void GetNum();
void GetOp();
void Next();

void Emit(char *s);
void EmitLn(char *s);

void Init();
void InitTable();
int Locate(char *symbol);
bool InTable(char *symbol);
void CheckTable(char *symbol);
void CheckDup(char *symbol);
void AddEntry(char *symbol, char type);

char *NewLabel();
void PostLabel(char *label);
void SkipWhite();
void NewLine();
void Scan();

/* re-targetable routines */
void Clear();
void Negate();
void LoadConst(char *value);
void LoadVar(char *name);
void Push();
void PopAdd();
void PopSub();
void PopMul();
void PopDiv();
void Store(char *name);
void Undefined(char *name);
void Duplicate(char *name);
void NotIt();
void PopAnd();
void PopOr();
void PopXor();
void PopCompare();
void SetEqual();
void SetNEqual();
void SetGreater();
void SetGreaterOrEqual();
void SetLess();
void SetLessOrEqual();
void Branch(char *label);
void BranchFalse(char *label);

#endif


================================================
FILE: 11/main.c
================================================
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#include "cradle.h"

#ifdef DEBUG
#define dprint(fmt, ...) printf(fmt, __VA_ARGS__);
#else
#define dprint(fmt, ...)
#endif


void TopDecls();
void Allocate(char *name, char *value);
void Alloc();
void Block();
void Assignment();

void Factor();
void Expression();
void Subtract();
void Term();
void Divide();
void Multiply();
void FirstFactor();
void Add();
void Equals();
void NotEqual();
void Less();
void LessOrEqual();
void Greater();
void Relation();
void NotFactor();
void BoolTerm();
void BoolOr();
void BoolXor();
void BoolExpression();
void DoIf();
void DoWhile();
void CompareExpression();
void NextExpression();

void Header()
{
    EmitLn(".global _start");
}

void Prolog()
{
    EmitLn(".section .text");
    EmitLn("_start:");
}

void Epilog()
{
    EmitLn("movl %eax, %ebx");
    EmitLn("movl $1, %eax");
    EmitLn("int $0x80");
}

void TopDecls()
{
    Scan();
    while(Token == 'v') {
        EmitLn(".section .data"); /* in case that the variable and function
                                     declarations are mixed */
        Alloc();
        while(Token == ',') {
            Alloc();
        }
    }
}

/* Allocate Storage for a static variable */
void Allocate(char *name, char *value)
{
    sprintf(tmp, "%s: .int %s", name, value);
    EmitLn(tmp);
}

void Alloc()
{
    char name[MAX_BUF];
    Next();
    if (Token != 'x') {
        Expected("Variable Name");
    }
    CheckDup(Value);

    sprintf(name, Value);
    AddEntry(name, 'v');
    Next();
    if (Token == '=') {
        Next();
        if (Token != '#') {
            Expected("Integer");
        }
        Allocate(name, Value);
        Next();
    } else {
        Allocate(name, "0");
    }
}

/* Parse and Translate a Block of Statements 
 * <block> ::= ( <statement> )*
 * <statement> ::= <if> | <while> | <assignment>
 * */
void Block()
{
    Scan();
    while(strchr("el", Token) == NULL) {
        switch (Token) {
            case 'i':
                DoIf();
                break;
            case 'w':
                DoWhile();
                break;
            default:
                Assignment();
                break;
        }
        Scan();
    }
}

void Assignment()
{
    char name[MAX_BUF];
    sprintf(name, Value);
    Next();
    MatchString("=");
    BoolExpression();
    Store(name);
}

void Factor()
{
    if (Token == '(') {
        Next();
        BoolExpression();
        MatchString(")");
    } else {
        if (Token == 'x') {
            LoadVar(Value);
        } else if (Token == '#') {
            LoadConst(Value);
        } else {
            Expected("Math Factor");
        }
        Next();
    }
}


void Multiply()
{
    Next();
    Factor();
    PopMul();
}

void Divide()
{
    Next();
    Factor();
    PopDiv();
}

void Term()
{
    Factor();
    while(IsMulop(Token)) {
        Push();
        switch(Token) {
            case '*':
                Multiply();
                break;
            case '/':
                Divide();
                break;
            default:
                break;
        }
    }
}

void Add()
{
    Next();
    Term();
    PopAdd();
}

void Subtract()
{
    Next();
    Term();
    PopSub();
}

void Expression()
{
    if (IsAddop(Token)) {
        Clear();
    } else {
        Term();
    }

    while(IsAddop(Token)) {
        Push();
        switch(Token) {
            case '+':
                Add();
                break;
            case '-':
                Subtract();
                break;
            default:
                break;
        }
    }
}

/* Get another expression and compare */
void CompareExpression()
{
    Expression();
    PopCompare();
}

/* Get the next expression and compare */
void NextExpression()
{
    Next();
    CompareExpression();
}

/* Recognize and Translate a Relational "Equals" */
void Equals()
{
    NextExpression();
    SetEqual();
}

/* Recognize and Translate a Relational "Not Equals" */
void NotEqual()
{
    NextExpression();
    SetNEqual();
}

/* Recognize and Translate a Relational "Less Than" */
void Less()
{
    Next();
    switch(Token) {
        case '=':
            LessOrEqual();
            break;
        case '>':
            NotEqual();
            break;
        default:
            CompareExpression();
            SetLess();
            break;
    }
}

/* Recognize and Translate a Relational "Less or Equal" */
void LessOrEqual()
{
    NextExpression();
    SetLessOrEqual();
}

/* Recognize and Translate a Relational "Greater Than" */
void Greater()
{
    Next();
    if (Token == '=') {
        NextExpression();
        SetGreaterOrEqual();
    } else {
        CompareExpression();
        SetGreater();
    }
}

/* Parse and Translate a Relation */
void Relation()
{
    Expression();
    if (IsRelop(Token)) {
        Push();
        switch (Token) {
            case '=':
                Equals();
                break;
            case '<':
                Less();
                break;
            case '>':
                Greater();
                break;
            default:
                break;
        }
    }
}

/* Parse and Translate a Boolean Factor with Leading NOT */
void NotFactor()
{
    if (Token == '!') {
        Next();
        Relation();
        NotIt();
    } else {
        Relation();
    }
}

/* Parse and Translate a Boolean Term 
 * <bool_term> ::= <not_factor> ( and_op <not_factor )*
 * */
void BoolTerm()
{
    NotFactor();
    while(Token == '&') {
        Push();
        Next();
        NotFactor();
        PopAnd();
    }
}

/* Recognize and Translate a Boolean OR */
void BoolOr()
{
    Next();
    BoolTerm();
    PopOr();
}

/* Recognize and Translate a Boolean XOR */
void BoolXor()
{
    Next();
    BoolTerm();
    PopXor();
}

/* Parse and Translate a Boolean Expression 
 * <bool_expression> ::= <bool_term> ( or_op <bool_term> )* */
void BoolExpression()
{
    BoolTerm();
    while(IsOrOp(Token)) {
        Push();
        switch(Look) {
            case '|':
                BoolOr();
                break;
            case '~':
                BoolXor();
                break;
            default:
                break;
        }
    }
}

/* Recognize and Translate an IF construct */
void DoIf()
{
    Next();
    char L1[MAX_BUF];
    char L2[MAX_BUF];
    sprintf(L1, NewLabel());
    sprintf(L2, L1);
    BoolExpression();
    BranchFalse(L1);
    Block();
    if (Token == 'l') {
        Next();
        sprintf(L2, NewLabel());
        Branch(L2);
        PostLabel(L1);
        Block();
    }
    PostLabel(L2);
    MatchString("ENDIF");
}

void DoWhile()
{
    Next();
    char L1[MAX_BUF];
    char L2[MAX_BUF];
    sprintf(L1, NewLabel());
    sprintf(L2, NewLabel());
    PostLabel(L1);
    BoolExpression();
    BranchFalse(L2);
    Block();
    MatchString("ENDWHILE");
    Branch(L1);
    PostLabel(L2);
}


int main()
{
    Init();
    MatchString("PROGRAM");
    Header();
    TopDecls();
    MatchString("BEGIN");
    Prolog();
    Block();
    MatchString("END");
    Epilog();

    return 0;
}


================================================
FILE: 11/prog.txt
================================================
PROGRAM
VAR xx,
yy=1,
zz=10
BEGIN
  WHILE yy <= zz
    IF yy <> 5 
      xx=xx+yy
    ELSE
      xx=xx+5
    ENDIF
  yy=yy+1
  ENDWHILE
END.



================================================
FILE: 11/tutor11.txt
================================================



























                     LET'S BUILD A COMPILER!

                                By

                     Jack W. Crenshaw, Ph.D.

                           3 June 1989


                 Part XI: LEXICAL SCAN REVISITED


*****************************************************************
*                                                               *
*                        COPYRIGHT NOTICE                       *
*                                                               *
*   Copyright (C) 1989 Jack W. Crenshaw. All rights reserved.   *
*                                                               *
*****************************************************************


INTRODUCTION

I've got some  good news and some bad news.  The bad news is that
this installment is  not  the  one  I promised last time.  What's
more, the one after this one won't be, either.

The good news is the reason for this installment:  I've  found  a
way  to simplify and improve the lexical  scanning  part  of  the
compiler.  Let me explain.


BACKGROUND

If  you'll remember, we talked at length  about  the  subject  of
lexical  scanners in Part VII, and I left you with a design for a
distributed scanner that I felt was about as simple  as  I  could
make it ... more than most that I've  seen  elsewhere.    We used
that idea in Part X.  The compiler structure  that  resulted  was
simple, and it got the job done.

Recently, though, I've begun  to  have  problems, and they're the
kind that send a message that you might be doing something wrong.

The  whole thing came to a head when I tried to address the issue
of  semicolons.  Several people have asked  me  about  them,  and
whether or not KISS will have them separating the statements.  My
intention has been NOT to  use semicolons, simply because I don't
like them and, as you can see, they have not proved necessary.

But I know that many of you, like me, have  gotten  used to them,
and so  I  set  out  to write a short installment to show you how
they could easily be added, if you were so inclined.

Well, it  turned  out  that  they weren't easy to add at all.  In
fact it was darned difficult.

I guess I should have  realized that something was wrong, because
of the issue  of  newlines.    In the last couple of installments
we've addressed that issue,  and  I've shown you how to deal with
newlines with a  procedure called, appropriately enough, NewLine.
In  TINY  Version  1.0,  I  sprinkled calls to this procedure  in
strategic spots in the code.

It  seems  that  every time I've addressed the issue of newlines,
though,  I've found it to be tricky,  and  the  resulting  parser
turned out to be quite fragile ... one addition or  deletion here
or  there and things tended to go to pot.  Looking back on it,  I
realize that  there  was  a  message  in  this that I just wasn't
paying attention to.

When I tried to add semicolons  on  top of the newlines, that was
the last straw.   I ended up with much too complex a solution.  I
began to realize that something fundamental had to change.

So,  in  a  way this installment will cause us to backtrack a bit
and revisit the issue of scanning all over again.    Sorry  about
that.  That's the price you pay for watching me  do  this in real
time.  But the new version is definitely an improvement, and will
serve us well for what is to come.

As  I said, the scanner we used in Part X was about as simple  as
one can get.  But anything can be improved.   The  new scanner is
more like the classical  scanner,  and  not  as simple as before.
But the overall  compiler  structure is even simpler than before.
It's also more robust, and easier to add  to  and/or  modify.   I
think that's worth the time spent in this digression.  So in this
installment, I'll be showing  you  the  new  structure.  No doubt
you'll  be  happy  to  know  that, while the changes affect  many
procedures, they aren't very profound  and so we lose very little
of what's been done so far.

Ironically, the new scanner  is  much  more conventional than the
old one, and is very much like the more generic scanner  I showed
you  earlier  in  Part VII.  Then I started trying to get clever,
and I almost clevered myself clean out of business.   You'd think
one day I'd learn: K-I-S-S!


THE PROBLEM

The problem begins to show  itself in procedure Block, which I've
reproduced below:


{--------------------------------------------------------------}
{ Parse and Translate a Block of Statements }

procedure Block;
begin
   Scan;
   while not(Token in ['e', 'l']) do begin
      case Token of
       'i': DoIf;
       'w': DoWhile;
       'R': DoRead;
       'W': DoWrite;
      else Assignment;
      end;
      Scan;
   end;
end;
{--------------------------------------------------------------}


As  you   can  see,  Block  is  oriented  to  individual  program
statements.  At each pass through  the  loop, we know that we are
at  the beginning of a statement.  We exit the block when we have
scanned an END or an ELSE.

But suppose that we see a semicolon instead.   The  procedure  as
it's shown above  can't  handle that, because procedure Scan only
expects and can only accept tokens that begin with a letter.

I  tinkered  around for quite awhile to come up with a  fix.    I
found many possible approaches, but none were very satisfying.  I
finally figured out the reason.

Recall that when we started with our single-character parsers, we
adopted a convention that the lookahead character would always be
prefetched.    That   is,   we  would  have  the  character  that
corresponds to our  current  position in the input stream fetched
into the global character Look, so that we could  examine  it  as
many  times  as  needed.    The  rule  we  adopted was that EVERY
recognizer, if it found its target token, would  advance  Look to
the next character in the input stream.

That simple and fixed convention served us very well when  we had
single-character tokens, and it still does.  It would make  a lot
of sense to apply the same rule to multi-character tokens.

But when we got into lexical scanning, I began  to  violate  that
simple rule.  The scanner of Part X  did  indeed  advance  to the
next token if it found an identifier or keyword, but it DIDN'T do
that if it found a carriage return, a whitespace character, or an
operator.

Now, that sort of mixed-mode  operation gets us into deep trouble
in procedure Block, because whether or not the  input  stream has
been advanced depends upon the kind of token we  encounter.    If
it's  a keyword or the target of  an  assignment  statement,  the
"cursor," as defined by the contents of Look,  has  been advanced
to  the next token OR to the beginning of whitespace.  If, on the
other  hand,  the  token  is  a  semicolon,  or if we have hit  a
carriage return, the cursor has NOT advanced.

Needless to say, we can add enough logic  to  keep  us  on track.
But it's tricky, and makes the whole parser very fragile.

There's a much  better  way,  and  that's just to adopt that same
rule that's worked so well before, to apply to TOKENS as  well as
single characters.  In other words, we'll prefetch tokens just as
we've always done for  characters.   It seems so obvious once you
think about it that way.

Interestingly enough, if we do things this way  the  problem that
we've had with newline characters goes away.  We  can  just  lump
them in as  whitespace  characters, which means that the handling
of  newlines  becomes  very trivial, and MUCH less prone to error
than we've had to deal with in the past.


THE SOLUTION

Let's  begin  to  fix  the  problem  by  re-introducing  the  two
procedures:

{--------------------------------------------------------------}
{ Get an Identifier }

procedure GetName;
begin
   SkipWhite;
   if Not IsAlpha(Look) then Expected('Identifier');
   Token := 'x';
   Value := '';
   repeat
      Value := Value + UpCase(Look);
      GetChar;
   until not IsAlNum(Look);
end;


{--------------------------------------------------------------}
{ Get a Number }

procedure GetNum;
begin
   SkipWhite;
   if not IsDigit(Look) then Expected('Number');
   Token := '#';
   Value := '';
   repeat
      Value := Value + Look;
      GetChar;
   until not IsDigit(Look);
end;
{--------------------------------------------------------------}


These two procedures are  functionally  almost  identical  to the
ones  I  showed  you in Part VII.  They each  fetch  the  current
token, either an identifier or a number, into  the  global string
Value.    They  also  set  the  encoded  version, Token,  to  the
appropriate code.  The input  stream is left with Look containing
the first character NOT part of the token.

We  can do the same thing  for  operators,  even  multi-character
operators, with a procedure such as:


{--------------------------------------------------------------}
{ Get an Operator }

procedure GetOp;
begin
   Token := Look;
   Value := '';
   repeat
      Value := Value + Look;
      GetChar;
   until IsAlpha(Look) or IsDigit(Look) or IsWhite(Look);
end;
{--------------------------------------------------------------}

Note  that  GetOp  returns,  as  its  encoded  token,  the  FIRST
character of the operator.  This is important,  because  it means
that we can now use that single character to  drive  the  parser,
instead of the lookahead character.

We need to tie these  procedures together into a single procedure
that can handle all three  cases.  The  following  procedure will
read any one of the token types and always leave the input stream
advanced beyond it:


{--------------------------------------------------------------}
{ Get the Next Input Token }

procedure Next;
begin
   SkipWhite;
   if IsAlpha(Look) then GetName
   else if IsDigit(Look) then GetNum
   else GetOp;
end;
{--------------------------------------------------------------}


***NOTE  that  here  I have put SkipWhite BEFORE the calls rather
than after.  This means that, in general, the variable  Look will
NOT have a meaningful value in it, and therefore  we  should  NOT
use it as a test value for parsing, as we have been doing so far.
That's the big departure from our normal approach.

Now, remember that before I was careful not to treat the carriage
return (CR) and line  feed  (LF) characters as white space.  This
was  because,  with  SkipWhite  called  as the last thing in  the
scanner, the encounter with  LF  would  trigger a read statement.
If we were on the last line of the program,  we  couldn't get out
until we input another line with a non-white  character.   That's
why I needed the second procedure, NewLine, to handle the CRLF's.

But now, with the call  to SkipWhite coming first, that's exactly
the behavior we want.    The  compiler  must know there's another
token coming or it wouldn't be calling Next.  In other words,  it
hasn't found the terminating  END  yet.  So we're going to insist
on more data until we find something.

All this means that we can greatly simplify both the  program and
the concepts, by treating CR and LF as whitespace characters, and
eliminating NewLine.  You  can  do  that  simply by modifying the
function IsWhite:


{--------------------------------------------------------------}
{ Recognize White Space }

function IsWhite(c: char): boolean;
begin
   IsWhite := c in [' ', TAB, CR, LF];
end;
{--------------------------------------------------------------}


We've already tried similar routines in Part VII,  but  you might
as well try these new ones out.  Add them to a copy of the Cradle
and call Next with the following main program:


{--------------------------------------------------------------}
{ Main Program }

begin
   Init;
   repeat
      Next;
      WriteLn(Token, ' ', Value);
   until Token = '.';
end.
{--------------------------------------------------------------}


Compile  it and verify that you can separate  a  program  into  a
series of tokens, and that you get the right  encoding  for  each
token.

This ALMOST works,  but  not  quite.    There  are  two potential
problems:    First,  in KISS/TINY almost all of our operators are
single-character operators.  The only exceptions  are  the relops
>=, <=, and <>.  It seems  a  shame  to  treat  all  operators as
strings and do a  string  compare,  when  only a single character
compare  will  almost  always  suffice.   Second, and  much  more
important, the  thing  doesn't  WORK  when  two  operators appear
together, as in (a+b)*(c+d).  Here the string following 'b' would
be interpreted as a single operator ")*(."

It's possible to fix that problem.  For example,  we  could  just
give GetOp a  list  of  legal  characters, and we could treat the
parentheses as different operator types  than  the  others.   But
this begins to get messy.

Fortunately, there's a  better  way that solves all the problems.
Since almost  all the operators are single characters, let's just
treat  them  that  way, and let GetOp get only one character at a
time.  This not only simplifies GetOp, but also speeds  things up
quite a  bit.    We  still have the problem of the relops, but we
were treating them as special cases anyway.

So here's the final version of GetOp:


{--------------------------------------------------------------}
{ Get an Operator }

procedure GetOp;
begin
   SkipWhite;
   Token := Look;
   Value := Look;
   GetChar;
end;
{--------------------------------------------------------------}


Note that I still give the string Value a value.  If you're truly
concerned about efficiency, you could leave this out.  When we're
expecting an operator, we will only be testing  Token  anyhow, so
the  value of the string won't matter.  But to me it seems to  be
good practice to give the thing a value just in case.

Try  this  new  version with some realistic-looking  code.    You
should  be  able  to  separate  any program into  its  individual
tokens, with the  caveat  that the two-character relops will scan
into two separate tokens.  That's OK ... we'll  parse  them  that
way.

Now, in Part VII the function of Next was combined with procedure
Scan,  which  also  checked every identifier against  a  list  of
keywords and encoded each one that was found.  As I  mentioned at
the time, the last thing we would want  to  do  is  to use such a
procedure in places where keywords  should not appear, such as in
expressions.  If we  did  that, the keyword list would be scanned
for every identifier appearing in the code.  Not good.

The  right  way  to  deal  with  that  is  to simply separate the
functions  of  fetching  tokens and looking for  keywords.    The
version of Scan shown below  does NOTHING but check for keywords.
Notice that it operates on the current token and does NOT advance
the input stream.


{--------------------------------------------------------------}
{ Scan the Current Identifier for Keywords }

procedure Scan;
begin
   if Token = 'x' then
      Token := KWcode[Lookup(Addr(KWlist), Value, NKW) + 1];
end;
{--------------------------------------------------------------}


There is one last detail.  In the compiler there are a few places
that we must  actually  check  the  string  value  of  the token.
Mainly, this  is done to distinguish between the different END's,
but there are a couple  of  other  places.    (I  should  note in
passing that we could always  eliminate the need for matching END
characters by encoding each one  to a different character.  Right
now we are definitely taking the lazy man's route.)

The  following  version  of MatchString takes the  place  of  the
character-oriented Match.  Note that, like Match, it DOES advance
the input stream.


{--------------------------------------------------------------}
{ Match a Specific Input String }

procedure MatchString(x: string);
begin
   if Value <> x then Expected('''' + x + '''');
   Next;
end;
{--------------------------------------------------------------}


FIXING UP THE COMPILER

Armed with these new scanner procedures, we can now begin  to fix
the compiler to  use  them  properly.   The changes are all quite
minor,  but  there  are quite a  few  places  where  changes  are
necessary.  Rather than  showing  you each place, I will give you
the general idea and then just give the finished product.


First of all, the code for procedure Block doesn't change, though
its function does:


{--------------------------------------------------------------}
{ Parse and Translate a Block of Statements }

procedure Block;
begin
   Scan;
   while not(Token in ['e', 'l']) do begin
      case Token of
       'i': DoIf;
       'w': DoWhile;
       'R': DoRead;
       'W': DoWrite;
      else Assignment;
      end;
      Scan;
   end;
end;
{--------------------------------------------------------------}


Remember that the new version of Scan doesn't  advance  the input
stream, it only  scans  for  keywords.   The input stream must be
advanced by each procedure that Block calls.

In general, we have to replace every test on Look with  a similar
test on Token.  For example:


{---------------------------------------------------------------}
{ Parse and Translate a Boolean Expression }

procedure BoolExpression;
begin
   BoolTerm;
   while IsOrOp(Token) do begin
      Push;
      case Token of
       '|': BoolOr;
       '~': BoolXor;
      end;
   end;
end;
{--------------------------------------------------------------}


In procedures like Add, we don't  have  to use Match anymore.  We
need only call Next to advance the input stream:


{--------------------------------------------------------------}
{ Recognize and Translate an Add }

procedure Add;
begin
   Next;
   Term;
   PopAdd;
end;
{-------------------------------------------------------------}


Control  structures  are  actually simpler.  We just call Next to
advance over the control keywords:


{---------------------------------------------------------------}
{ Recognize and Translate an IF Construct }

procedure Block; Forward;

procedure DoIf;
var L1, L2: string;
begin
   Next;
   BoolExpression;
   L1 := NewLabel;
   L2 := L1;
   BranchFalse(L1);
   Block;
   if Token = 'l' then begin
      Next;
      L2 := NewLabel;
      Branch(L2);
      PostLabel(L1);
      Block;
   end;
   PostLabel(L2);
   MatchString('ENDIF');
end;
{--------------------------------------------------------------}


That's about the extent of the REQUIRED changes.  In  the listing
of TINY  Version  1.1  below,  I've  also  made a number of other
"improvements" that  aren't really required.  Let me explain them
briefly:

 (1)  I've deleted the two procedures Prog and Main, and combined
      their functions into the main program.  They didn't seem to
      add  to program clarity ... in fact  they  seemed  to  just
      muddy things up a little.

 (2)  I've  deleted  the  keywords  PROGRAM  and  BEGIN  from the
      keyword list.  Each  one  only occurs in one place, so it's
      not necessary to search for it.

 (3)  Having been  bitten  by  an  overdose  of  cleverness, I've
      reminded myself that TINY  is  supposed  to be a minimalist
      program.  Therefore I've  replaced  the  fancy  handling of
      unary minus with the dumbest one I could think of.  A giant
      step backwards in code quality, but a  great simplification
      of the compiler.  KISS is the right place to use  the other
      version.

 (4)  I've added some  error-checking routines such as CheckTable
      and CheckDup, and  replaced  in-line code by calls to them.
      This cleans up a number of routines.

 (5)  I've  taken  the  error  checking  out  of  code generation
      routines  like Store, and put it in  the  parser  where  it
      belongs.  See Assignment, for example.

 (6)  There was an error in InTable and Locate  that  caused them
      to search all locations  instead  of  only those with valid
      data  in them.  They now search only  valid  cells.    This
      allows us to eliminate  the  initialization  of  the symbol
      table, which was done in Init.

 (7)  Procedure AddEntry now has two  arguments,  which  helps to
      make things a bit more modular.

 (8)  I've cleaned up the  code  for  the relational operators by
      the addition of the  new  procedures  CompareExpression and
      NextExpression.

 (9)  I fixed an error in the Read routine ... the  earlier value
      did not check for a valid variable name.


 CONCLUSION

The resulting compiler for  TINY  is given below.  Other than the
removal  of  the  keyword PROGRAM, it parses the same language as
before.    It's  just  a  bit cleaner, and more importantly  it's
considerably more robust.  I feel good about it.

The next installment will be another  digression:  the discussion
of  semicolons  and  such that got me into this mess in the first
place.  THEN we'll press on  into  procedures and types.  Hang in
there with me.  The addition of those features will go a long way
towards removing KISS from  the  "toy  language" category.  We're
getting very close to being able to write a serious compiler.


TINY VERSION 1.1


{--------------------------------------------------------------}
program Tiny11;

{--------------------------------------------------------------}
{ Constant Declarations }

const TAB = ^I;
      CR  = ^M;
      LF  = ^J;

      LCount: integer = 0;
      NEntry: integer = 0;


{--------------------------------------------------------------}
{ Type Declarations }

type Symbol = string[8];

     SymTab = array[1..1000] of Symbol;

     TabPtr = ^SymTab;


{--------------------------------------------------------------}
{ Variable Declarations }

var Look : char;             { Lookahead Character }
    Token: char;             { Encoded Token       }
    Value: string[16];       { Unencoded Token     }


const MaxEntry = 100;

var ST   : array[1..MaxEntry] of Symbol;
    SType: array[1..MaxEntry] of char;


{--------------------------------------------------------------}
{ Definition of Keywords and Token Types }

const NKW =   9;
      NKW1 = 10;

const KWlist: array[1..NKW] of Symbol =
              ('IF', 'ELSE', 'ENDIF', 'WHILE', 'ENDWHILE',
               'READ', 'WRITE', 'VAR', 'END');

const KWcode: string[NKW1] = 'xileweRWve';


{--------------------------------------------------------------}
{ Read New Character From Input Stream }

procedure GetChar;
begin
   Read(Look);
end;

{--------------------------------------------------------------}
{ Report an Error }

procedure Error(s: string);
begin
   WriteLn;
   WriteLn(^G, 'Error: ', s, '.');
end;


{--------------------------------------------------------------}
{ Report Error and Halt }

procedure Abort(s: string);
begin
   Error(s);
   Halt;
end;


{--------------------------------------------------------------}
{ Report What Was Expected }

procedure Expected(s: string);
begin
   Abort(s + ' Expected');
end;

{--------------------------------------------------------------}
{ Report an Undefined Identifier }

procedure Undefined(n: string);
begin
   Abort('Undefined Identifier ' + n);
end;


{--------------------------------------------------------------}
{ Report a Duplicate Identifier }

procedure Duplicate(n: string);
begin
   Abort('Duplicate Identifier ' + n);
end;


{--------------------------------------------------------------}
{ Check to Make Sure the Current Token is an Identifier }

procedure CheckIdent;
begin
   if Token <> 'x' then Expected('Identifier');
end;


{--------------------------------------------------------------}
{ Recognize an Alpha Character }

function IsAlpha(c: char): boolean;
begin
   IsAlpha := UpCase(c) in ['A'..'Z'];
end;


{--------------------------------------------------------------}
{ Recognize a Decimal Digit }

function IsDigit(c: char): boolean;
begin
   IsDigit := c in ['0'..'9'];
end;


{--------------------------------------------------------------}
{ Recognize an AlphaNumeric Character }

function IsAlNum(c: char): boolean;
begin
   IsAlNum := IsAlpha(c) or IsDigit(c);
end;


{--------------------------------------------------------------}
{ Recognize an Addop }

function IsAddop(c: char): boolean;
begin
   IsAddop := c in ['+', '-'];
end;


{--------------------------------------------------------------}
{ Recognize a Mulop }

function IsMulop(c: char): boolean;
begin
   IsMulop := c in ['*', '/'];
end;


{--------------------------------------------------------------}
{ Recognize a Boolean Orop }

function IsOrop(c: char): boolean;
begin
   IsOrop := c in ['|', '~'];
end;


{--------------------------------------------------------------}
{ Recognize a Relop }

function IsRelop(c: char): boolean;
begin
   IsRelop := c in ['=', '#', '<', '>'];
end;


{--------------------------------------------------------------}
{ Recognize White Space }

function IsWhite(c: char): boolean;
begin
   IsWhite := c in [' ', TAB, CR, LF];
end;


{--------------------------------------------------------------}
{ Skip Over Leading White Space }

procedure SkipWhite;
begin
   while IsWhite(Look) do
      GetChar;
end;


{--------------------------------------------------------------}
{ Table Lookup }

function Lookup(T: TabPtr; s: string; n: integer): integer;
var i: integer;
    found: Boolean;
begin
   found := false;
   i := n;
   while (i > 0) and not found do
      if s = T^[i] then
         found := true
      else
         dec(i);
   Lookup := i;
end;


{--------------------------------------------------------------}
{ Locate a Symbol in Table }
{ Returns the index of the entry.  Zero if not present. }

function Locate(N: Symbol): integer;
begin
   Locate := Lookup(@ST, n, NEntry);
end;


{--------------------------------------------------------------}
{ Look for Symbol in Table }

function InTable(n: Symbol): Boolean;
begin
   InTable := Lookup(@ST, n, NEntry) <> 0;
end;


{--------------------------------------------------------------}
{ Check to See if an Identifier is in the Symbol Table         }
{ Report an error if it's not. }


procedure CheckTable(N: Symbol);
begin
   if not InTable(N) then Undefined(N);
end;


{--------------------------------------------------------------}
{ Check the Symbol Table for a Duplicate Identifier }
{ Report an error if identifier is already in table. }


procedure CheckDup(N: Symbol);
begin
   if InTable(N) then Duplicate(N);
end;


{--------------------------------------------------------------}
{ Add a New Entry to Symbol Table }

procedure AddEntry(N: Symbol; T: char);
begin
   CheckDup(N);
   if NEntry = MaxEntry then Abort('Symbol Table Full');
   Inc(NEntry);
   ST[NEntry] := N;
   SType[NEntry] := T;
end;


{--------------------------------------------------------------}
{ Get an Identifier }

procedure GetName;
begin
   SkipWhite;
   if Not IsAlpha(Look) then Expected('Identifier');
   Token := 'x';
   Value := '';
   repeat
      Value := Value + UpCase(Look);
      GetChar;
   until not IsAlNum(Look);
end;


{--------------------------------------------------------------}
{ Get a Number }

procedure GetNum;
begin
   SkipWhite;
   if not IsDigit(Look) then Expected('Number');
   Token := '#';
   Value := '';
   repeat
      Value := Value + Look;
      GetChar;
   until not IsDigit(Look);
end;


{--------------------------------------------------------------}
{ Get an Operator }

procedure GetOp;
begin
   SkipWhite;
   Token := Look;
   Value := Look;
   GetChar;
end;


{--------------------------------------------------------------}
{ Get the Next Input Token }

procedure Next;
begin
   SkipWhite;
   if IsAlpha(Look) then GetName
   else if IsDigit(Look) then GetNum
   else GetOp;
end;


{--------------------------------------------------------------}
{ Scan the Current Identifier for Keywords }

procedure Scan;
begin
   if Token = 'x' then
      Token := KWcode[Lookup(Addr(KWlist), Value, NKW) + 1];
end;


{--------------------------------------------------------------}
{ Match a Specific Input String }

procedure MatchString(x: string);
begin
   if Value <> x then Expected('''' + x + '''');
   Next;
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;


{--------------------------------------------------------------}
{ Generate a Unique Label }

function NewLabel: string;
var S: string;
begin
   Str(LCount, S);
   NewLabel := 'L' + S;
   Inc(LCount);
end;


{--------------------------------------------------------------}
{ Post a Label To Output }

procedure PostLabel(L: string);
begin
   WriteLn(L, ':');
end;


{---------------------------------------------------------------}
{ Clear the Primary Register }

procedure Clear;
begin
   EmitLn('CLR D0');
end;


{---------------------------------------------------------------}
{ Negate the Primary Register }

procedure Negate;
begin
   EmitLn('NEG D0');
end;


{---------------------------------------------------------------}
{ Complement the Primary Register }

procedure NotIt;
begin
   EmitLn('NOT D0');
end;


{---------------------------------------------------------------}
{ Load a Constant Value to Primary Register }

procedure LoadConst(n: string);
begin
   Emit('MOVE #');
   WriteLn(n, ',D0');
end;


{---------------------------------------------------------------}
{ Load a Variable to Primary Register }

procedure LoadVar(Name: string);
begin
   if not InTable(Name) then Undefined(Name);
   EmitLn('MOVE ' + Name + '(PC),D0');
end;


{---------------------------------------------------------------}
{ Push Primary onto Stack }

procedure Push;
begin
   EmitLn('MOVE D0,-(SP)');
end;


{---------------------------------------------------------------}
{ Add Top of Stack to Primary }

procedure PopAdd;
begin
   EmitLn('ADD (SP)+,D0');
end;


{---------------------------------------------------------------}
{ Subtract Primary from Top of Stack }

procedure PopSub;
begin
   EmitLn('SUB (SP)+,D0');
   EmitLn('NEG D0');
end;


{---------------------------------------------------------------}
{ Multiply Top of Stack by Primary }

procedure PopMul;
begin
   EmitLn('MULS (SP)+,D0');
end;


{---------------------------------------------------------------}
{ Divide Top of Stack by Primary }

procedure PopDiv;
begin
   EmitLn('MOVE (SP)+,D7');
   EmitLn('EXT.L D7');
   EmitLn('DIVS D0,D7');
   EmitLn('MOVE D7,D0');
end;


{---------------------------------------------------------------}
{ AND Top of Stack with Primary }

procedure PopAnd;
begin
   EmitLn('AND (SP)+,D0');
end;


{---------------------------------------------------------------}
{ OR Top of Stack with Primary }

procedure PopOr;
begin
   EmitLn('OR (SP)+,D0');
end;


{---------------------------------------------------------------}
{ XOR Top of Stack with Primary }

procedure PopXor;
begin
   EmitLn('EOR (SP)+,D0');
end;


{---------------------------------------------------------------}
{ Compare Top of Stack with Primary }

procedure PopCompare;
begin
   EmitLn('CMP (SP)+,D0');
end;


{---------------------------------------------------------------}
{ Set D0 If Compare was = }

procedure SetEqual;
begin
   EmitLn('SEQ D0');
   EmitLn('EXT D0');
end;


{---------------------------------------------------------------}
{ Set D0 If Compare was != }

procedure SetNEqual;
begin
   EmitLn('SNE D0');
   EmitLn('EXT D0');
end;


{---------------------------------------------------------------}
{ Set D0 If Compare was > }

procedure SetGreater;
begin
   EmitLn('SLT D0');
   EmitLn('EXT D0');
end;


{---------------------------------------------------------------}
{ Set D0 If Compare was < }

procedure SetLess;
begin
   EmitLn('SGT D0');
   EmitLn('EXT D0');
end;


{---------------------------------------------------------------}
{ Set D0 If Compare was <= }

procedure SetLessOrEqual;
begin
   EmitLn('SGE D0');
   EmitLn('EXT D0');
end;


{---------------------------------------------------------------}
{ Set D0 If Compare was >= }

procedure SetGreaterOrEqual;
begin
   EmitLn('SLE D0');
   EmitLn('EXT D0');
end;


{---------------------------------------------------------------}
{ Store Primary to Variable }

procedure Store(Name: string);
begin
   EmitLn('LEA ' + Name + '(PC),A0');
   EmitLn('MOVE D0,(A0)')
end;


{---------------------------------------------------------------}
{ Branch Unconditional  }

procedure Branch(L: string);
begin
   EmitLn('BRA ' + L);
end;


{---------------------------------------------------------------}
{ Branch False }

procedure BranchFalse(L: string);
begin
   EmitLn('TST D0');
   EmitLn('BEQ ' + L);
end;


{---------------------------------------------------------------}
{ Read Variable to Primary Register }

procedure ReadIt(Name: string);
begin
   EmitLn('BSR READ');
   Store(Name);
end;


{ Write from Primary Register }

procedure WriteIt;
begin
   EmitLn('BSR WRITE');
end;


{--------------------------------------------------------------}
{ Write Header Info }

procedure Header;
begin
   WriteLn('WARMST', TAB, 'EQU $A01E');
end;


{--------------------------------------------------------------}
{ Write the Prolog }

procedure Prolog;
begin
   PostLabel('MAIN');
end;


{--------------------------------------------------------------}
{ Write the Epilog }

procedure Epilog;
begin
   EmitLn('DC WARMST');
   EmitLn('END MAIN');
end;


{---------------------------------------------------------------}
{ Allocate Storage for a Static Variable }

procedure Allocate(Name, Val: string);
begin
   WriteLn(Name, ':', TAB, 'DC ', Val);
end;


{---------------------------------------------------------------}
{ Parse and Translate a Math Factor }

procedure BoolExpression; Forward;

procedure Factor;
begin
   if Token = '(' then begin
      Next;
      BoolExpression;
      MatchString(')');
      end
   else begin
      if Token = 'x' then
         LoadVar(Value)
      else if Token = '#' then
         LoadConst(Value)
      else Expected('Math Factor');
      Next;
   end;
end;


{--------------------------------------------------------------}
{ Recognize and Translate a Multiply }

procedure Multiply;
begin
   Next;
   Factor;
   PopMul;
end;


{-------------------------------------------------------------}
{ Recognize and Translate a Divide }

procedure Divide;
begin
   Next;
   Factor;
   PopDiv;
end;


{---------------------------------------------------------------}
{ Parse and Translate a Math Term }

procedure Term;
begin
   Factor;
   while IsMulop(Token) do begin
      Push;
      case Token of
       '*': Multiply;
       '/': Divide;
      end;
   end;
end;


{--------------------------------------------------------------}
{ Recognize and Translate an Add }

procedure Add;
begin
   Next;
   Term;
   PopAdd;
end;


{-------------------------------------------------------------}
{ Recognize and Translate a Subtract }

procedure Subtract;
begin
   Next;
   Term;
   PopSub;
end;


{---------------------------------------------------------------}
{ Parse and Translate an Expression }

procedure Expression;
begin
   if IsAddop(Token) then
      Clear
   else
      Term;
   while IsAddop(Token) do begin
      Push;
      case Token of
       '+': Add;
       '-': Subtract;
      end;
   end;
end;


{---------------------------------------------------------------}
{ Get Another Expression and Compare }

procedure CompareExpression;
begin
   Expression;
   PopCompare;
end;


{---------------------------------------------------------------}
{ Get The Next Expression and Compare }

procedure NextExpression;
begin
   Next;
   CompareExpression;
end;


{---------------------------------------------------------------}
{ Recognize and Translate a Relational "Equals" }

procedure Equal;
begin
   NextExpression;
   SetEqual;
end;


{---------------------------------------------------------------}
{ Recognize and Translate a Relational "Less Than or Equal" }

procedure LessOrEqual;
begin
   NextExpression;
   SetLessOrEqual;
end;


{---------------------------------------------------------------}
{ Recognize and Translate a Relational "Not Equals" }

procedure NotEqual;
begin
   NextExpression;
   SetNEqual;
end;


{---------------------------------------------------------------}
{ Recognize and Translate a Relational "Less Than" }

procedure Less;
begin
   Next;
   case Token of
     '=': LessOrEqual;
     '>': NotEqual;
   else begin
           CompareExpression;
           SetLess;
        end;
   end;
end;


{---------------------------------------------------------------}
{ Recognize and Translate a Relational "Greater Than" }

procedure Greater;
begin
   Next;
   if Token = '=' then begin
      NextExpression;
      SetGreaterOrEqual;
      end
   else begin
      CompareExpression;
      SetGreater;
   end;
end;


{---------------------------------------------------------------}
{ Parse and Translate a Relation }


procedure Relation;
begin
   Expression;
   if IsRelop(Token) then begin
      Push;
      case Token of
       '=': Equal;
       '<': Less;
       '>': Greater;
      end;
   end;
end;


{---------------------------------------------------------------}
{ Parse and Translate a Boolean Factor with Leading NOT }

procedure NotFactor;
begin
   if Token = '!' then begin
      Next;
      Relation;
      NotIt;
      end
   else
      Relation;
end;


{---------------------------------------------------------------}
{ Parse and Translate a Boolean Term }

procedure BoolTerm;
begin
   NotFactor;
   while Token = '&' do begin
      Push;
      Next;
      NotFactor;
      PopAnd;
   end;
end;


{--------------------------------------------------------------}
{ Recognize and Translate a Boolean OR }

procedure BoolOr;
begin
   Next;
   BoolTerm;
   PopOr;
end;


{--------------------------------------------------------------}
{ Recognize and Translate an Exclusive Or }

procedure BoolXor;
begin
   Next;
   BoolTerm;
   PopXor;
end;


{---------------------------------------------------------------}
{ Parse and Translate a Boolean Expression }

procedure BoolExpression;
begin
   BoolTerm;
   while IsOrOp(Token) do begin
      Push;
      case Token of
       '|': BoolOr;
       '~': BoolXor;
      end;
   end;
end;


{--------------------------------------------------------------}
{ Parse and Translate an Assignment Statement }

procedure Assignment;
var Name: string;
begin
   CheckTable(Value);
   Name := Value;
   Next;
   MatchString('=');
   BoolExpression;
   Store(Name);
end;


{---------------------------------------------------------------}
{ Recognize and Translate an IF Construct }

procedure Block; Forward;

procedure DoIf;
var L1, L2: string;
begin
   Next;
   BoolExpression;
   L1 := NewLabel;
   L2 := L1;
   BranchFalse(L1);
   Block;
   if Token = 'l' then begin
      Next;
      L2 := NewLabel;
      Branch(L2);
      PostLabel(L1);
      Block;
   end;
   PostLabel(L2);
   MatchString('ENDIF');
end;


{--------------------------------------------------------------}
{ Parse and Translate a WHILE Statement }

procedure DoWhile;
var L1, L2: string;
begin
   Next;
   L1 := NewLabel;
   L2 := NewLabel;
   PostLabel(L1);
   BoolExpression;
   BranchFalse(L2);
   Block;
   MatchString('ENDWHILE');
   Branch(L1);
   PostLabel(L2);
end;


{--------------------------------------------------------------}
{ Read a Single Variable }

procedure ReadVar;
begin
   CheckIdent;
   CheckTable(Value);
   ReadIt(Value);
   Next;
end;


{--------------------------------------------------------------}
{ Process a Read Statement }

procedure DoRead;
begin
   Next;
   MatchString('(');
   ReadVar;
   while Token = ',' do begin
      Next;
      ReadVar;
   end;
   MatchString(')');
end;


{--------------------------------------------------------------}
{ Process a Write Statement }

procedure DoWrite;
begin
   Next;
   MatchString('(');
   Expression;
   WriteIt;
   while Token = ',' do begin
      Next;
      Expression;
      WriteIt;
   end;
   MatchString(')');
end;


{--------------------------------------------------------------}
{ Parse and Translate a Block of Statements }

procedure Block;
begin
   Scan;
   while not(Token in ['e', 'l']) do begin
      case Token of
       'i': DoIf;
       'w': DoWhile;
       'R': DoRead;
       'W': DoWrite;
      else Assignment;
      end;
      Scan;
   end;
end;


{--------------------------------------------------------------}
{ Allocate Storage for a Variable }

procedure Alloc;
begin
   Next;
   if Token <> 'x' then Expected('Variable Name');
   CheckDup(Value);
   AddEntry(Value, 'v');
   Allocate(Value, '0');
   Next;
end;


{--------------------------------------------------------------}
{ Parse and Translate Global Declarations }

procedure TopDecls;
begin
   Scan;
   while Token = 'v' do
      Alloc;
      while Token = ',' do
         Alloc;
end;


{--------------------------------------------------------------}
{ Initialize }

procedure Init;
begin
   GetChar;
   Next;
end;


{--------------------------------------------------------------}
{ Main Program }

begin
   Init;
   MatchString('PROGRAM');
   Header;
   TopDecls;
   MatchString('BEGIN');
   Prolog;
   Block;
   MatchString('END');
   Epilog;
end.
{--------------------------------------------------------------}
*****************************************************************
*                                                               *
*                        COPYRIGHT NOTICE                       *
*                                                               *
*   Copyright (C) 1989 Jack W. Crenshaw. All rights reserved.   *
*                                                               *
*****************************************************************



================================================
FILE: 12/Makefile
================================================
IN=main.c cradle.c
OUT=main
FLAGS=-Wall -Werror

all:
	gcc -o $(OUT) $(IN) $(FLAGS)

run:
	./$(OUT)

.PHONY: clean
clean:
	rm $(OUT)


================================================
FILE: 12/cradle.c
================================================
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

#include "cradle.h"
#include <malloc.h>

#define MaxEntry 100
#define MAX_SYMBOL_LENGTH 10
static int LCount = 0;
static char labelName[MAX_BUF];
char tmp[MAX_BUF];
char TempChar;

/*char ST[TABLE_SIZE];*/
static int NEntry = 0;
const char *ST[MaxEntry];
char SType[MaxEntry];


/* Keywords symbol table */
const char const *KWList[] = {
    "IF",
    "ELSE",
    "ENDIF",
    "WHILE",
    "ENDWHILE",
    "VAR",
    "END",
};
const char KWCode[] = "xileweve";
const int KWNum = sizeof(KWList)/sizeof(*KWList);

char Token;             /* current token */
char Value[MAX_BUF];    /* string token of Look */

/* Helper Functions */
char uppercase(char c)
{
    if (IsAlpha(c)) {
        return (c & 0xDF);
    } else {
        return c;
    }
}

/* Table Lookup
 * If the input string matches a table entry, return the entry index, else
 * return -1.
 * *n* is the size of the table */
int Lookup(const char const *table[], const char *string, int n)
{
    int i;
    bool found = false;

    for (i = 0; i < n; ++i) {
        if (strcmp(table[i], string) == 0) {
            found = true;
            break;
        }
    }
    return found ? i : -1;
}

int Locate(char *symbol)
{
    return Lookup(ST, symbol, NEntry);
}

/* Add a new entry to symbol table */
void AddEntry(char *symbol, char type)
{
    CheckDup(symbol);
    if (NEntry == MaxEntry) {
        Abort("Symbol Table Full");
    }

    char *new_entry = (char *)malloc((strlen(symbol)+1)*sizeof(*new_entry));
    if (new_entry == NULL) {
        Abort("AddEntry: not enough memory allocating new_entry.");
    }
    strcpy(new_entry, symbol);
    ST[NEntry] = new_entry;
    SType[NEntry] = type;

    NEntry++;
}

/* Get an Identifier and Scan it for keywords */
void Scan()
{
    if (Token == 'x') {
        int index = Lookup(KWList, Value, KWNum);
        Token = KWCode[index+1];
    }
}

void MatchString(char *str)
{
    if (strcmp(Value, str) != 0) {
        sprintf(tmp, "\"%s\"", str);
        Expected(tmp);
    }
    Next();
}

void GetCharX()
{
    Look = getchar();
    /* printf("Getchar: %c\n", Look); */
}

void GetChar()
{
    if (TempChar != ' ') {
        Look = TempChar;
        TempChar = ' ';
    } else {
        GetCharX();
        if (Look == '/') {
            TempChar = getchar();
            if (TempChar == '*') {
                Look = '{';
                TempChar = ' ';
            }
        }
    }
}

void Error(char *s)
{
    printf("\nError: %s.", s);
}

void Abort(char *s)
{
    Error(s);
    exit(1);
}


void Expected(char *s)
{
    sprintf(tmp, "%s Expected", s);
    Abort(tmp);
}


void Match(char x)
{
    NewLine();
    if(Look == x) {
        GetChar();
    } else {
        sprintf(tmp, "' %c ' ",  x);
        Expected(tmp);
    }
    SkipWhite();
}

int IsAlpha(char c)
{
    return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}

int IsDigit(char c)
{
    return (c >= '0') && (c <= '9');
}

int IsAddop(char c)
{
    return (c == '+') || (c == '-');
}

int IsMulop(char c)
{
    return (c == '*') || (c == '/');
}

int IsOrOp(char c)
{
    return strchr("|~", c) != NULL;
}

int IsRelop(char c)
{
    return strchr("=#<>", c) != NULL;
}

int IsWhite(char c)
{
    return strchr(" \t\r\n{", c) != NULL;
}

int IsAlNum(char c)
{
    return IsAlpha(c) || IsDigit(c);
}

void GetName()
{
    SkipWhite();
    if( !IsAlpha(Look)) {
        Expected("Name");
    }

    Token = 'x';
    char *p = Value;
    do {
        *p++ = uppercase(Look);
        GetChar();
    } while(IsAlNum(Look)) ;
    *p = '\0';
}

void GetNum()
{
    SkipWhite();
    if( !IsDigit(Look)) {
        Expected("Integer");
    }

    Token = '#';
    char *p = Value;
    do {
        *p++ = Look;
        GetChar();
    } while (IsDigit(Look));
    *p = '\0';
}

/* Get an operator */
void GetOp()
{
    SkipWhite();
    Token = Look;
    Value[0] = Look;
    Value[1] = '\0';
    GetChar();
}

/* Get the next input token */
void Next()
{
    SkipWhite();
    if (IsAlpha(Look)) {
        GetName();
    } else if (IsDigit(Look)) {
        GetNum();
    } else {
        GetOp();
    }
}

void Emit(char *s)
{
    printf("\t%s", s);
}

void EmitLn(char *s)
{
    Emit(s);
    printf("\n");
}

void Init()
{
    LCount = 0;

    InitTable();
    GetChar();
    Next();
}

void InitTable()
{
    int i;
    for (i = 0; i < MaxEntry; i++) {
        ST[i] = NULL;
        SType[i] = ' ';
    }

}

/* look for symbol in table */
bool InTable(char *symbol)
{
    return Locate(symbol) != -1;
}

/* Check to see if an identifier is in the symbol table,
 * report an error if it's not */
void CheckTable(char *symbol)
{
    if (! InTable(symbol)) {
        Undefined(symbol);
    }
}

void CheckDup(char *symbol)
{
    if (InTable(symbol)) {
        Duplicate(symbol);
    }
}

char *NewLabel()
{
    sprintf(labelName, "L%02d", LCount);
    LCount ++;
    return labelName;
}

void PostLabel(char *label)
{
    printf("%s:\n", label);
}

void SkipWhite()
{
    while (IsWhite(Look)) {
        if (Look == '{') {
            SkipComment();
        } else {
            GetChar();
        }
    }
}

void SkipComment()
{
    do {
        do {
            GetChar();
            if (Look == '{') {
                SkipComment();
            }
        } while(Look != '*');
        GetCharX();
    } while(Look != '/');
    GetChar();
}

/* Skip over an End-of-Line */
void NewLine()
{
    while(Look == '\n') {
        GetChar();
        if (Look == '\r') {
            GetChar();
        }
        SkipWhite();
    }
}

/* re-targetable routines */
void Clear()
{
    EmitLn("xor %eax, %eax");
}

void Negate()
{
    EmitLn("neg %eax");
}

void LoadConst(char *value)
{
    sprintf(tmp, "movl $%s, %%eax", value);
    EmitLn(tmp);
}

/* Load a variable to primary register */
void LoadVar(char *name)
{
    if (!InTable(name)) {
        char name_string[MAX_BUF];
        Undefined(name_string);
    }
    sprintf(tmp, "movl %s, %%eax", name);
    EmitLn(tmp);
}


/* Push Primary onto stack */
void Push()
{
    EmitLn("pushl %eax");
}

/* Add Top of Stack to primary */
void PopAdd()
{
    EmitLn("addl (%esp), %eax");
    EmitLn("addl $4, %esp");
}

/* Subtract Primary from Top of Stack */
void PopSub()
{
    EmitLn("subl (%esp), %eax");
    EmitLn("neg %eax");
    EmitLn("addl $4, %esp");
}

/* multiply top of stack by primary */
void PopMul()
{
    EmitLn("imull (%esp), %eax");
    EmitLn("addl $4, %esp");
}

/* divide top of stack by primary */
void PopDiv()
{
    /* for a expersion like a/b we have eax=b and %(esp)=a
     * but we need eax=a, and b on the stack
     */
    EmitLn("movl (%esp), %edx");
    EmitLn("addl $4, %esp");
    EmitLn("pushl %eax");
    EmitLn("movl %edx, %eax");

    /* sign extesnion */
    EmitLn("sarl $31, %edx");
    EmitLn("idivl (%esp)");
    EmitLn("addl $4, %esp");
}

/* store primary to variable */
void Store(char *name)
{
    if (!InTable(name)) {
        char name_string[MAX_BUF];
        Undefined(name_string);
    }
    sprintf(tmp, "movl %%eax, %s", name);
    EmitLn(tmp);
}

void Undefined(char *name)
{
    sprintf(tmp, "Undefined Identifier: %s", name);
    Abort(tmp);
}

void Duplicate(char *name)
{
    sprintf(tmp, "Duplicate Identifier: %s", name);
    Abort(tmp);
}

/* Complement the primary register */
void NotIt()
{
    EmitLn("not %eax");
}

/* AND top of Stack with primary */
void PopAnd()
{
    EmitLn("and (%esp), %eax");
    EmitLn("addl $4, %esp");
}

/* OR top of Stack with primary */
void PopOr()
{
    EmitLn("or (%esp), %eax");
    EmitLn("addl $4, %esp");
}

/* XOR top of Stack with primary */
void PopXor()
{
    EmitLn("xor (%esp), %eax");
    EmitLn("addl $4, %esp");
}

/* Compare top of Stack with primary */
void PopCompare()
{
    EmitLn("addl $4, %esp");
    EmitLn("cmp -4(%esp), %eax");
}

/* set %eax if Compare was = */
void SetEqual()
{
    EmitLn("sete %al");
    EmitLn("movsx %al, %eax");
}

/* set %eax if Compare was != */
void SetNEqual()
{
    EmitLn("setne %al");
    EmitLn("movsx %al, %eax");
}

/* set %eax if Compare was > */
void SetGreater()
{
    EmitLn("setl %al");
    EmitLn("movsx %al, %eax");
}

/* set %eax if Compare was >= */
void SetGreaterOrEqual()
{
    EmitLn("setle %al");
    EmitLn("movsx %al, %eax");
}

/* set %eax if Compare was < */
void SetLess()
{
    EmitLn("setg %al");
    EmitLn("movsx %al, %eax");
}

/* set %eax if Compare was <= */
void SetLessOrEqual()
{
    EmitLn("setge %al");
    EmitLn("movsx %al, %eax");
}

/* Branch unconditional */
void Branch(char *label)
{
    sprintf(tmp, "jmp %s", label);
    EmitLn(tmp);
}

/* Branch False */
void BranchFalse(char *label)
{
    EmitLn("test $1, %eax");
    sprintf(tmp, "jz %s", label);
    EmitLn(tmp);
}


================================================
FILE: 12/cradle.h
================================================
#ifndef _CRADLE_H
#define _CRADLE_H
#include <stdbool.h>

#define MAX_BUF 100
#define MaxEntry 100
extern char tmp[MAX_BUF];
extern const char *ST[];
extern char SType[];
extern char Token;
extern char Value[MAX_BUF];
char Look;

void GetChar();

void Error(char *s);
void Abort(char *s);
void Expected(char *s);
void Match(char x);
void MatchString(char *str);

int IsAlpha(char c);
int IsDigit(char c);
int IsAddop(char c);
int IsMulop(char c);
int IsOrOp(char c);
int IsRelop(char c);
int IsWhite(char c);
int IsAlNum(char c);

void GetName();
void GetNum();
void GetOp();
void Next();

void Emit(char *s);
void EmitLn(char *s);

void Init();
void InitTable();
int Locate(char *symbol);
bool InTable(char *symbol);
void CheckTable(char *symbol);
void CheckDup(char *symbol);
void AddEntry(char *symbol, char type);

char *NewLabel();
void PostLabel(char *label);
void SkipWhite();
void SkipComment();
void NewLine();
void Scan();

/* re-targetable routines */
void Clear();
void Negate();
void LoadConst(char *value);
void LoadVar(char *name);
void Push();
void PopAdd();
void PopSub();
void PopMul();
void PopDiv();
void Store(char *name);
void Undefined(char *name);
void Duplicate(char *name);
void NotIt();
void PopAnd();
void PopOr();
void PopXor();
void PopCompare();
void SetEqual();
void SetNEqual();
void SetGreater();
void SetGreaterOrEqual();
void SetLess();
void SetLessOrEqual();
void Branch(char *label);
void BranchFalse(char *label);

#endif


================================================
FILE: 12/main.c
================================================
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#include "cradle.h"

#ifdef DEBUG
#define dprint(fmt, ...) printf(fmt, __VA_ARGS__);
#else
#define dprint(fmt, ...)
#endif


void TopDecls();
void Allocate(char *name, char *value);
void Alloc();
void Block();
void Assignment();

void Factor();
void Expression();
void Subtract();
void Term();
void Divide();
void Multiply();
void FirstFactor();
void Add();
void Equals();
void NotEqual();
void Less();
void LessOrEqual();
void Greater();
void Relation();
void NotFactor();
void BoolTerm();
void BoolOr();
void BoolXor();
void BoolExpression();
void DoIf();
void DoWhile();
void CompareExpression();
void NextExpression();

void Semi();

void Header()
{
    EmitLn(".global _start");
}

void Prolog()
{
    EmitLn(".section .text");
    EmitLn("_start:");
}

void Epilog()
{
    EmitLn("movl %eax, %ebx");
    EmitLn("movl $1, %eax");
    EmitLn("int $0x80");
}

void TopDecls()
{
    Scan();
    while(Token == 'v') {
        EmitLn(".section .data"); /* in case that the variable and function
                                     declarations are mixed */
        Alloc();
        while(Token == ',') {
            Alloc();
        }
        Semi();
    }
}

/* Allocate Storage for a static variable */
void Allocate(char *name, char *value)
{
    sprintf(tmp, "%s: .int %s", name, value);
    EmitLn(tmp);
}

void Alloc()
{
    char name[MAX_BUF];
    Next();
    if (Token != 'x') {
        Expected("Variable Name");
    }
    CheckDup(Value);

    sprintf(name, Value);
    AddEntry(name, 'v');
    Next();
    if (Token == '=') {
        Next();
        if (Token != '#') {
            Expected("Integer");
        }
        Allocate(name, Value);
        Next();
    } else {
        Allocate(name, "0");
    }
}

/* Parse and Translate a Block of Statements 
 * <block> ::= ( <statement> )*
 * <statement> ::= <if> | <while> | <assignment>
 * */
void Block()
{
    Scan();
    while(strchr("el", Token) == NULL) {
        switch (Token) {
            case 'i':
                DoIf();
                break;
            case 'w':
                DoWhile();
                break;
            case 'x':
                Assignment();
                break;
            default:
                break;
        }
        Semi();
        Scan();
    }
}

void Assignment()
{
    char name[MAX_BUF];
    sprintf(name, Value);
    Next();
    MatchString("=");
    BoolExpression();
    Store(name);
}

void Factor()
{
    if (Token == '(') {
        Next();
        BoolExpression();
        MatchString(")");
    } else {
        if (Token == 'x') {
            LoadVar(Value);
        } else if (Token == '#') {
            LoadConst(Value);
        } else {
            Expected("Math Factor");
        }
        Next();
    }
}


void Multiply()
{
    Next();
    Factor();
    PopMul();
}

void Divide()
{
    Next();
    Factor();
    PopDiv();
}

void Term()
{
    Factor();
    while(IsMulop(Token)) {
        Push();
        switch(Token) {
            case '*':
                Multiply();
                break;
            case '/':
                Divide();
                break;
            default:
                break;
        }
    }
}

void Add()
{
    Next();
    Term();
    PopAdd();
}

void Subtract()
{
    Next();
    Term();
    PopSub();
}

void Expression()
{
    if (IsAddop(Token)) {
        Clear();
    } else {
        Term();
    }

    while(IsAddop(Token)) {
        Push();
        switch(Token) {
            case '+':
                Add();
                break;
            case '-':
                Subtract();
                break;
            default:
                break;
        }
    }
}

/* Get another expression and compare */
void CompareExpression()
{
    Expression();
    PopCompare();
}

/* Get the next expression and compare */
void NextExpression()
{
    Next();
    CompareExpression();
}

/* Recognize and Translate a Relational "Equals" */
void Equals()
{
    NextExpression();
    SetEqual();
}

/* Recognize and Translate a Relational "Not Equals" */
void NotEqual()
{
    NextExpression();
    SetNEqual();
}

/* Recognize and Translate a Relational "Less Than" */
void Less()
{
    Next();
    switch(Token) {
        case '=':
            LessOrEqual();
            break;
        case '>':
            NotEqual();
            break;
        default:
            CompareExpression();
            SetLess();
            break;
    }
}

/* Recognize and Translate a Relational "Less or Equal" */
void LessOrEqual()
{
    NextExpression();
    SetLessOrEqual();
}

/* Recognize and Translate a Relational "Greater Than" */
void Greater()
{
    Next();
    if (Token == '=') {
        NextExpression();
        SetGreaterOrEqual();
    } else {
        CompareExpression();
        SetGreater();
    }
}

/* Parse and Translate a Relation */
void Relation()
{
    Expression();
    if (IsRelop(Token)) {
        Push();
        switch (Token) {
            case '=':
                Equals();
                break;
            case '<':
                Less();
                break;
            case '>':
                Greater();
                break;
            default:
                break;
        }
    }
}

/* Parse and Translate a Boolean Factor with Leading NOT */
void NotFactor()
{
    if (Token == '!') {
        Next();
        Relation();
        NotIt();
    } else {
        Relation();
    }
}

/* Parse and Translate a Boolean Term 
 * <bool_term> ::= <not_factor> ( and_op <not_factor )*
 * */
void BoolTerm()
{
    NotFactor();
    while(Token == '&') {
        Push();
        Next();
        NotFactor();
        PopAnd();
    }
}

/* Recognize and Translate a Boolean OR */
void BoolOr()
{
    Next();
    BoolTerm();
    PopOr();
}

/* Recognize and Translate a Boolean XOR */
void BoolXor()
{
    Next();
    BoolTerm();
    PopXor();
}

/* Parse and Translate a Boolean Expression 
 * <bool_expression> ::= <bool_term> ( or_op <bool_term> )* */
void BoolExpression()
{
    BoolTerm();
    while(IsOrOp(Token)) {
        Push();
        switch(Look) {
            case '|':
                BoolOr();
                break;
            case '~':
                BoolXor();
                break;
            default:
                break;
        }
    }
}

/* Recognize and Translate an IF construct */
void DoIf()
{
    Next();
    char L1[MAX_BUF];
    char L2[MAX_BUF];
    sprintf(L1, NewLabel());
    sprintf(L2, L1);
    BoolExpression();
    BranchFalse(L1);
    Block();
    if (Token == 'l') {
        Next();
        sprintf(L2, NewLabel());
        Branch(L2);
        PostLabel(L1);
        Block();
    }
    PostLabel(L2);
    MatchString("ENDIF");
}

void DoWhile()
{
    Next();
    char L1[MAX_BUF];
    char L2[MAX_BUF];
    sprintf(L1, NewLabel());
    sprintf(L2, NewLabel());
    PostLabel(L1);
    BoolExpression();
    BranchFalse(L2);
    Block();
    MatchString("ENDWHILE");
    Branch(L1);
    PostLabel(L2);
}

void Semi()
{
    /* make a semicolon optional */
    if (Token == ';') {
        Next();
    }
}

int main()
{
    Init();
    MatchString("PROGRAM");
    Semi();
    Header();
    TopDecls();
    MatchString("BEGIN");
    Prolog();
    Block();
    MatchString("END");
    Epilog();

    return 0;
}


================================================
FILE: 12/prog.txt
================================================
PROGRAM;
/* 
    calculate 1+2+...+10 
    /* nested comment */
*/
VAR xx, yy=1, zz=10;
BEGIN
  WHILE yy <= zz
    IF yy <> 5 
      xx=xx+yy;
    ELSE
      xx=xx+5;
    ENDIF;
  yy=yy+1;
  ENDWHILE;
END.



================================================
FILE: 12/tutor12.txt
================================================



























                     LET'S BUILD A COMPILER!

                                By

                     Jack W. Crenshaw, Ph.D.

                           5 June 1989


                       Part XII: MISCELLANY


*****************************************************************
*                                                               *
*                        COPYRIGHT NOTICE                       *
*                                                               *
*   Copyright (C) 1989 Jack W. Crenshaw. All rights reserved.   *
*                                                               *
*****************************************************************


INTRODUCTION

This installment is another one  of  those  excursions  into side
alleys  that  don't  seem to fit  into  the  mainstream  of  this
tutorial  series.    As I mentioned last time, it was while I was
writing this installment that I realized some changes  had  to be
made  to  the  compiler structure.  So I had to digress from this
digression long enough to develop the new structure  and  show it
to you.

Now that that's behind us, I can tell you what I  set  out  to in
the first place.  This shouldn't  take  long, and then we can get
back into the mainstream.

Several people have asked  me  about  things that other languages
provide, but so far I haven't addressed in this series.   The two
biggies are semicolons and  comments.    Perhaps  you've wondered
about them, too, and  wondered  how things would change if we had
to  deal with them.  Just so you can proceed with what's to come,
without being  bothered by that nagging feeling that something is
missing, we'll address such issues here.


SEMICOLONS

Ever since the introduction o
Download .txt
gitextract_t5qe27_o/

├── .gitignore
├── 1/
│   ├── cradle.c
│   ├── cradle.h
│   └── tutor1.txt
├── 10/
│   ├── Makefile
│   ├── cradle.c
│   ├── cradle.h
│   ├── main.c
│   ├── prog.txt
│   └── tutor10.txt
├── 11/
│   ├── Makefile
│   ├── cradle.c
│   ├── cradle.h
│   ├── main.c
│   ├── prog.txt
│   └── tutor11.txt
├── 12/
│   ├── Makefile
│   ├── cradle.c
│   ├── cradle.h
│   ├── main.c
│   ├── prog.txt
│   └── tutor12.txt
├── 13/
│   ├── Makefile
│   ├── cradle.c
│   ├── cradle.h
│   ├── main.c
│   ├── prog.txt
│   └── tutor13.txt
├── 14/
│   ├── Makefile
│   ├── README.md
│   ├── cradle.c
│   ├── cradle.h
│   ├── main.c
│   ├── prog.txt
│   └── tutor14.txt
├── 15/
│   └── tutor15.txt
├── 16/
│   └── tutor16.txt
├── 2/
│   ├── Makefile
│   ├── cradle.c
│   ├── cradle.h
│   ├── main.c
│   └── tutor2.txt
├── 3/
│   ├── Makefile
│   ├── cradle.c
│   ├── cradle.h
│   ├── main.c
│   └── tutor3.txt
├── 4/
│   ├── Makefile
│   ├── cradle.c
│   ├── cradle.h
│   ├── main.c
│   └── tutor4.txt
├── 5/
│   ├── Makefile
│   ├── README.md
│   ├── cradle.c
│   ├── cradle.h
│   ├── main.c
│   └── tutor5.txt
├── 6/
│   ├── Makefile
│   ├── cradle.c
│   ├── cradle.h
│   ├── main.c
│   └── tutor6.txt
├── 7/
│   ├── Makefile
│   ├── cradle.c
│   ├── cradle.h
│   ├── main.c
│   └── tutor7.txt
├── 8/
│   └── tutor8.txt
├── 9/
│   ├── Makefile
│   ├── cradle.c
│   ├── cradle.h
│   ├── main.c
│   └── tutor9.txt
├── README.md
└── get_chapters.sh
Download .txt
SYMBOL INDEX (666 symbols across 25 files)

FILE: 1/cradle.c
  function GetChar (line 6) | void GetChar()
  function Error (line 12) | void Error(char *s)
  function Abort (line 17) | void Abort(char *s)
  function Expected (line 24) | void Expected(char *s)
  function Match (line 31) | void Match(char x)
  function IsAlpha (line 42) | int IsAlpha(char c)
  function IsDigit (line 47) | int IsDigit(char c)
  function GetName (line 53) | char GetName()
  function GetNum (line 68) | char GetNum()
  function Emit (line 82) | void Emit(char *s)
  function EmitLn (line 87) | void EmitLn(char *s)
  function Init (line 93) | void Init()

FILE: 10/cradle.c
  function uppercase (line 40) | char uppercase(char c)
  function Lookup (line 53) | int Lookup(const char const *table[], const char *string, int n)
  function AddEntry (line 68) | void AddEntry(char *symbol, char type)
  function Scan (line 90) | void Scan()
  function MatchString (line 97) | void MatchString(char *str)
  function GetChar (line 105) | void GetChar()
  function Error (line 112) | void Error(char *s)
  function Abort (line 117) | void Abort(char *s)
  function Expected (line 124) | void Expected(char *s)
  function Match (line 131) | void Match(char x)
  function IsAlpha (line 143) | int IsAlpha(char c)
  function IsDigit (line 148) | int IsDigit(char c)
  function IsAddop (line 153) | int IsAddop(char c)
  function IsMulop (line 158) | int IsMulop(char c)
  function IsOrOp (line 163) | int IsOrOp(char c)
  function IsRelop (line 168) | int IsRelop(char c)
  function IsWhite (line 173) | int IsWhite(char c)
  function IsAlNum (line 178) | int IsAlNum(char c)
  function GetName (line 183) | void GetName()
  function GetNum (line 200) | int GetNum()
  function Emit (line 219) | void Emit(char *s)
  function EmitLn (line 224) | void EmitLn(char *s)
  function Init (line 230) | void Init()
  function InitTable (line 240) | void InitTable()
  function InTable (line 250) | bool InTable(char *name)
  function PostLabel (line 262) | void PostLabel(char *label)
  function SkipWhite (line 267) | void SkipWhite()
  function NewLine (line 275) | void NewLine()
  function Clear (line 287) | void Clear()
  function Negate (line 292) | void Negate()
  function LoadConst (line 297) | void LoadConst(int n)
  function LoadVar (line 304) | void LoadVar(char *name)
  function Push (line 316) | void Push()
  function PopAdd (line 322) | void PopAdd()
  function PopSub (line 329) | void PopSub()
  function PopMul (line 337) | void PopMul()
  function PopDiv (line 344) | void PopDiv()
  function Store (line 361) | void Store(char *name)
  function Undefined (line 371) | void Undefined(char *name)
  function NotIt (line 378) | void NotIt()
  function PopAnd (line 384) | void PopAnd()
  function PopOr (line 391) | void PopOr()
  function PopXor (line 398) | void PopXor()
  function PopCompare (line 405) | void PopCompare()
  function SetEqual (line 412) | void SetEqual()
  function SetNEqual (line 419) | void SetNEqual()
  function SetGreater (line 426) | void SetGreater()
  function SetGreaterOrEqual (line 433) | void SetGreaterOrEqual()
  function SetLess (line 440) | void SetLess()
  function SetLessOrEqual (line 447) | void SetLessOrEqual()
  function Branch (line 454) | void Branch(char *label)
  function BranchFalse (line 461) | void BranchFalse(char *label)

FILE: 10/main.c
  function Prog (line 51) | void Prog()
  function Header (line 60) | void Header()
  function Prolog (line 65) | void Prolog()
  function Epilog (line 71) | void Epilog()
  function Main (line 78) | void Main()
  function TopDecls (line 87) | void TopDecls()
  function Decl (line 106) | void Decl()
  function Alloc (line 121) | void Alloc(char *name)
  function Block (line 148) | void Block()
  function Assignment (line 169) | void Assignment()
  function Factor (line 178) | void Factor()
  function NegFactor (line 192) | void NegFactor()
  function FirstFactor (line 204) | void FirstFactor()
  function Multiply (line 219) | void Multiply()
  function Divide (line 226) | void Divide()
  function Term1 (line 233) | void Term1()
  function Term (line 252) | void Term()
  function FirstTerm (line 258) | void FirstTerm()
  function Add (line 264) | void Add()
  function Subtract (line 271) | void Subtract()
  function Expression (line 278) | void Expression()
  function Equals (line 299) | void Equals()
  function NotEquals (line 308) | void NotEquals()
  function Less (line 317) | void Less()
  function LessOrEqual (line 336) | void LessOrEqual()
  function Greater (line 345) | void Greater()
  function Relation (line 361) | void Relation()
  function NotFactor (line 386) | void NotFactor()
  function BoolTerm (line 400) | void BoolTerm()
  function BoolOr (line 414) | void BoolOr()
  function BoolXor (line 422) | void BoolXor()
  function BoolExpression (line 431) | void BoolExpression()
  function DoIf (line 452) | void DoIf()
  function DoWhile (line 471) | void DoWhile()
  function main (line 487) | int main()

FILE: 11/cradle.c
  function uppercase (line 38) | char uppercase(char c)
  function Lookup (line 51) | int Lookup(const char const *table[], const char *string, int n)
  function Locate (line 65) | int Locate(char *symbol)
  function AddEntry (line 71) | void AddEntry(char *symbol, char type)
  function Scan (line 90) | void Scan()
  function MatchString (line 98) | void MatchString(char *str)
  function GetChar (line 107) | void GetChar()
  function Error (line 114) | void Error(char *s)
  function Abort (line 119) | void Abort(char *s)
  function Expected (line 126) | void Expected(char *s)
  function Match (line 133) | void Match(char x)
  function IsAlpha (line 145) | int IsAlpha(char c)
  function IsDigit (line 150) | int IsDigit(char c)
  function IsAddop (line 155) | int IsAddop(char c)
  function IsMulop (line 160) | int IsMulop(char c)
  function IsOrOp (line 165) | int IsOrOp(char c)
  function IsRelop (line 170) | int IsRelop(char c)
  function IsWhite (line 175) | int IsWhite(char c)
  function IsAlNum (line 180) | int IsAlNum(char c)
  function GetName (line 185) | void GetName()
  function GetNum (line 201) | void GetNum()
  function GetOp (line 218) | void GetOp()
  function Next (line 228) | void Next()
  function Emit (line 240) | void Emit(char *s)
  function EmitLn (line 245) | void EmitLn(char *s)
  function Init (line 251) | void Init()
  function InitTable (line 260) | void InitTable()
  function InTable (line 271) | bool InTable(char *symbol)
  function CheckTable (line 278) | void CheckTable(char *symbol)
  function CheckDup (line 285) | void CheckDup(char *symbol)
  function PostLabel (line 299) | void PostLabel(char *label)
  function SkipWhite (line 304) | void SkipWhite()
  function NewLine (line 312) | void NewLine()
  function Clear (line 324) | void Clear()
  function Negate (line 329) | void Negate()
  function LoadConst (line 334) | void LoadConst(char *value)
  function LoadVar (line 341) | void LoadVar(char *name)
  function Push (line 353) | void Push()
  function PopAdd (line 359) | void PopAdd()
  function PopSub (line 366) | void PopSub()
  function PopMul (line 374) | void PopMul()
  function PopDiv (line 381) | void PopDiv()
  function Store (line 398) | void Store(char *name)
  function Undefined (line 408) | void Undefined(char *name)
  function Duplicate (line 414) | void Duplicate(char *name)
  function NotIt (line 421) | void NotIt()
  function PopAnd (line 427) | void PopAnd()
  function PopOr (line 434) | void PopOr()
  function PopXor (line 441) | void PopXor()
  function PopCompare (line 448) | void PopCompare()
  function SetEqual (line 455) | void SetEqual()
  function SetNEqual (line 462) | void SetNEqual()
  function SetGreater (line 469) | void SetGreater()
  function SetGreaterOrEqual (line 476) | void SetGreaterOrEqual()
  function SetLess (line 483) | void SetLess()
  function SetLessOrEqual (line 490) | void SetLessOrEqual()
  function Branch (line 497) | void Branch(char *label)
  function BranchFalse (line 504) | void BranchFalse(char *label)

FILE: 11/main.c
  function Header (line 45) | void Header()
  function Prolog (line 50) | void Prolog()
  function Epilog (line 56) | void Epilog()
  function TopDecls (line 63) | void TopDecls()
  function Allocate (line 77) | void Allocate(char *name, char *value)
  function Alloc (line 83) | void Alloc()
  function Block (line 111) | void Block()
  function Assignment (line 130) | void Assignment()
  function Factor (line 140) | void Factor()
  function Multiply (line 159) | void Multiply()
  function Divide (line 166) | void Divide()
  function Term (line 173) | void Term()
  function Add (line 191) | void Add()
  function Subtract (line 198) | void Subtract()
  function Expression (line 205) | void Expression()
  function CompareExpression (line 229) | void CompareExpression()
  function NextExpression (line 236) | void NextExpression()
  function Equals (line 243) | void Equals()
  function NotEqual (line 250) | void NotEqual()
  function Less (line 257) | void Less()
  function LessOrEqual (line 275) | void LessOrEqual()
  function Greater (line 282) | void Greater()
  function Relation (line 295) | void Relation()
  function NotFactor (line 317) | void NotFactor()
  function BoolTerm (line 331) | void BoolTerm()
  function BoolOr (line 343) | void BoolOr()
  function BoolXor (line 351) | void BoolXor()
  function BoolExpression (line 360) | void BoolExpression()
  function DoIf (line 379) | void DoIf()
  function DoWhile (line 400) | void DoWhile()
  function main (line 417) | int main()

FILE: 12/cradle.c
  function uppercase (line 39) | char uppercase(char c)
  function Lookup (line 52) | int Lookup(const char const *table[], const char *string, int n)
  function Locate (line 66) | int Locate(char *symbol)
  function AddEntry (line 72) | void AddEntry(char *symbol, char type)
  function Scan (line 91) | void Scan()
  function MatchString (line 99) | void MatchString(char *str)
  function GetCharX (line 108) | void GetCharX()
  function GetChar (line 114) | void GetChar()
  function Error (line 131) | void Error(char *s)
  function Abort (line 136) | void Abort(char *s)
  function Expected (line 143) | void Expected(char *s)
  function Match (line 150) | void Match(char x)
  function IsAlpha (line 162) | int IsAlpha(char c)
  function IsDigit (line 167) | int IsDigit(char c)
  function IsAddop (line 172) | int IsAddop(char c)
  function IsMulop (line 177) | int IsMulop(char c)
  function IsOrOp (line 182) | int IsOrOp(char c)
  function IsRelop (line 187) | int IsRelop(char c)
  function IsWhite (line 192) | int IsWhite(char c)
  function IsAlNum (line 197) | int IsAlNum(char c)
  function GetName (line 202) | void GetName()
  function GetNum (line 218) | void GetNum()
  function GetOp (line 235) | void GetOp()
  function Next (line 245) | void Next()
  function Emit (line 257) | void Emit(char *s)
  function EmitLn (line 262) | void EmitLn(char *s)
  function Init (line 268) | void Init()
  function InitTable (line 277) | void InitTable()
  function InTable (line 288) | bool InTable(char *symbol)
  function CheckTable (line 295) | void CheckTable(char *symbol)
  function CheckDup (line 302) | void CheckDup(char *symbol)
  function PostLabel (line 316) | void PostLabel(char *label)
  function SkipWhite (line 321) | void SkipWhite()
  function SkipComment (line 332) | void SkipComment()
  function NewLine (line 347) | void NewLine()
  function Clear (line 359) | void Clear()
  function Negate (line 364) | void Negate()
  function LoadConst (line 369) | void LoadConst(char *value)
  function LoadVar (line 376) | void LoadVar(char *name)
  function Push (line 388) | void Push()
  function PopAdd (line 394) | void PopAdd()
  function PopSub (line 401) | void PopSub()
  function PopMul (line 409) | void PopMul()
  function PopDiv (line 416) | void PopDiv()
  function Store (line 433) | void Store(char *name)
  function Undefined (line 443) | void Undefined(char *name)
  function Duplicate (line 449) | void Duplicate(char *name)
  function NotIt (line 456) | void NotIt()
  function PopAnd (line 462) | void PopAnd()
  function PopOr (line 469) | void PopOr()
  function PopXor (line 476) | void PopXor()
  function PopCompare (line 483) | void PopCompare()
  function SetEqual (line 490) | void SetEqual()
  function SetNEqual (line 497) | void SetNEqual()
  function SetGreater (line 504) | void SetGreater()
  function SetGreaterOrEqual (line 511) | void SetGreaterOrEqual()
  function SetLess (line 518) | void SetLess()
  function SetLessOrEqual (line 525) | void SetLessOrEqual()
  function Branch (line 532) | void Branch(char *label)
  function BranchFalse (line 539) | void BranchFalse(char *label)

FILE: 12/main.c
  function Header (line 47) | void Header()
  function Prolog (line 52) | void Prolog()
  function Epilog (line 58) | void Epilog()
  function TopDecls (line 65) | void TopDecls()
  function Allocate (line 80) | void Allocate(char *name, char *value)
  function Alloc (line 86) | void Alloc()
  function Block (line 114) | void Block()
  function Assignment (line 136) | void Assignment()
  function Factor (line 146) | void Factor()
  function Multiply (line 165) | void Multiply()
  function Divide (line 172) | void Divide()
  function Term (line 179) | void Term()
  function Add (line 197) | void Add()
  function Subtract (line 204) | void Subtract()
  function Expression (line 211) | void Expression()
  function CompareExpression (line 235) | void CompareExpression()
  function NextExpression (line 242) | void NextExpression()
  function Equals (line 249) | void Equals()
  function NotEqual (line 256) | void NotEqual()
  function Less (line 263) | void Less()
  function LessOrEqual (line 281) | void LessOrEqual()
  function Greater (line 288) | void Greater()
  function Relation (line 301) | void Relation()
  function NotFactor (line 323) | void NotFactor()
  function BoolTerm (line 337) | void BoolTerm()
  function BoolOr (line 349) | void BoolOr()
  function BoolXor (line 357) | void BoolXor()
  function BoolExpression (line 366) | void BoolExpression()
  function DoIf (line 385) | void DoIf()
  function DoWhile (line 406) | void DoWhile()
  function Semi (line 422) | void Semi()
  function main (line 430) | int main()

FILE: 13/cradle.c
  function GetChar (line 22) | void GetChar()
  function Error (line 28) | void Error(char *str)
  function Abort (line 35) | void Abort(char *str)
  function Expected (line 42) | void Expected(char *str)
  function Undefined (line 49) | void Undefined(char symbol)
  function Duplicate (line 56) | void Duplicate(char symbol)
  function TypeOf (line 63) | char TypeOf(char symbol)
  function InTable (line 73) | bool InTable(char symbol)
  function AddEntry (line 79) | void AddEntry(char symbol, char type)
  function CheckVar (line 88) | void CheckVar(char name)
  function upcase (line 101) | char upcase(char c)
  function IsAlpha (line 106) | bool IsAlpha(char c)
  function IsDigit (line 112) | bool IsDigit(char c)
  function IsAlNum (line 117) | bool IsAlNum(char c)
  function IsAddop (line 122) | bool IsAddop(char c)
  function IsMulop (line 127) | bool IsMulop(char c)
  function IsRelop (line 132) | bool IsRelop(char c)
  function IsWhite (line 137) | bool IsWhite(char c)
  function SkipWhite (line 143) | void SkipWhite(void)
  function Fin (line 151) | void Fin(void)
  function Match (line 164) | void Match(char c)
  function GetName (line 177) | char GetName(void)
  function GetNum (line 189) | char GetNum(void)
  function Emit (line 201) | void Emit(char *str)
  function EmitLn (line 207) | void EmitLn(char *str)
  function PostLabel (line 214) | void PostLabel(char label)
  function LoadVar (line 220) | void LoadVar(char name)
  function StoreVar (line 228) | void StoreVar(char name)
  function LoadParam (line 236) | void LoadParam(int n)
  function StoreParam (line 244) | void StoreParam(int n)
  function Push (line 252) | void Push()
  function CleanStack (line 258) | void CleanStack(int bytes)
  function InitTable (line 267) | void InitTable(void)
  function ClearParams (line 276) | void ClearParams()
  function ParamNumber (line 286) | int ParamNumber(char name)
  function IsParam (line 292) | bool IsParam(char name)
  function AddParam (line 298) | void AddParam(char name)
  function Init (line 308) | void Init()

FILE: 13/main.c
  function Expression (line 37) | void Expression()
  function AssignOrProc (line 48) | void AssignOrProc()
  function Assignment (line 71) | void Assignment(char name)
  function DoBlock (line 83) | void DoBlock()
  function CallProc (line 91) | void CallProc(char name)
  function Call (line 99) | void Call(char name)
  function BeginBlock (line 107) | void BeginBlock()
  function Alloc (line 117) | void Alloc(char name)
  function Decl (line 127) | void Decl(void)
  function TopDecls (line 135) | void TopDecls(void)
  function Header (line 158) | void Header()
  function Prolog (line 163) | void Prolog()
  function Epilog (line 169) | void Epilog()
  function DoProc (line 176) | void DoProc(void)
  function Return (line 193) | void Return()
  function DoMain (line 201) | void DoMain(void)
  function FormalList (line 214) | void FormalList()
  function FormalParam (line 231) | void FormalParam()
  function Param (line 237) | void Param()
  function ParamList (line 244) | int ParamList()
  function ProcProlog (line 262) | void ProcProlog(char name, int num_local_params)
  function ProcEpilog (line 273) | void ProcEpilog()
  function LocDecl (line 281) | void LocDecl()
  function LocDecls (line 289) | int LocDecls()
  function main (line 299) | int main(int argc, char *argv[])

FILE: 14/cradle.c
  function GetChar (line 19) | void GetChar()
  function Error (line 25) | void Error(char *str)
  function Abort (line 32) | void Abort(char *str)
  function Expected (line 39) | void Expected(char *str)
  function Undefined (line 46) | void Undefined(char name)
  function Duplicate (line 53) | void Duplicate(char name)
  function TypeOf (line 60) | char TypeOf(char symbol)
  function InTable (line 66) | bool InTable(char symbol)
  function AddEntry (line 72) | void AddEntry(char symbol, char type)
  function CheckVar (line 79) | void CheckVar(char name)
  function CheckDup (line 92) | void CheckDup(char name)
  function upcase (line 100) | char upcase(char c)
  function IsAlpha (line 105) | bool IsAlpha(char c)
  function IsDigit (line 111) | bool IsDigit(char c)
  function IsAlNum (line 116) | bool IsAlNum(char c)
  function IsAddop (line 121) | bool IsAddop(char c)
  function IsMulop (line 126) | bool IsMulop(char c)
  function IsRelop (line 131) | bool IsRelop(char c)
  function IsWhite (line 136) | bool IsWhite(char c)
  function IsVarType (line 141) | bool IsVarType(char c)
  function VarType (line 147) | char VarType(char name)
  function SkipWhite (line 157) | void SkipWhite(void)
  function Fin (line 165) | void Fin(void)
  function Match (line 178) | void Match(char c)
  function GetName (line 191) | char GetName(void)
  function GetNum (line 203) | int GetNum(void)
  function LoadNum (line 218) | char LoadNum(int val)
  function Emit (line 233) | void Emit(char *str)
  function EmitLn (line 239) | void EmitLn(char *str)
  function PostLabel (line 246) | void PostLabel(char *label)
  function LoadVar (line 252) | void LoadVar(char name, char type)
  function Move (line 275) | void Move(char size, char *src, char *dest)
  function StoreVar (line 282) | void StoreVar(char name, char type)
  function Load (line 306) | char Load(char name)
  function LoadConst (line 314) | void LoadConst(int val, char type)
  function Store (line 338) | void Store(char name, char src_type)
  function Convert (line 346) | void Convert(char src, char dst, char reg)
  function Promote (line 369) | char Promote(char src_type, char dst_type, char reg)
  function SameType (line 382) | char SameType(char src_type, char dst_type)
  function InitTable (line 389) | void InitTable(void)
  function DumpTable (line 398) | void DumpTable()
  function Init (line 409) | void Init()
  function Clear (line 416) | void Clear()
  function Push (line 422) | void Push(char type)
  function Pop (line 437) | void Pop(char type)
  function PopAdd (line 453) | char PopAdd(char src_type, char dst_type)
  function PopSub (line 465) | char PopSub(char src_type, char dst_type)
  function GenAdd (line 478) | void GenAdd(char type)
  function GenSub (line 497) | void GenSub(char type)
  function GenMul (line 520) | void GenMul()
  function GenLongMul (line 526) | void GenLongMul()
  function GenDiv (line 531) | void GenDiv()
  function GenLongDiv (line 536) | void GenLongDiv()
  function PopMul (line 542) | char PopMul(char src_type, char dst_type)
  function PopDiv (line 563) | char PopDiv(char src_type, char dst_type)

FILE: 14/main.c
  function Expression (line 30) | char Expression()
  function Term (line 55) | char Term()
  function Factor (line 75) | char Factor()
  function Unop (line 91) | char Unop()
  function Add (line 97) | char Add(char type)
  function Subtract (line 103) | char Subtract(char type)
  function Multiply (line 109) | char Multiply(char type)
  function Divide (line 115) | char Divide(char type)
  function Assignment (line 123) | void Assignment()
  function Block (line 130) | void Block()
  function DoBlock (line 139) | void DoBlock()
  function BeginBlock (line 148) | void BeginBlock()
  function AllocVar (line 159) | void AllocVar(char name, char type)
  function Alloc (line 179) | void Alloc(char name, char type)
  function Decl (line 186) | void Decl(void)
  function TopDecls (line 193) | void TopDecls(void)
  function Header (line 213) | void Header()
  function Prolog (line 218) | void Prolog()
  function Epilog (line 224) | void Epilog()
  function main (line 231) | int main(int argc, char *argv[])

FILE: 2/cradle.c
  function GetChar (line 6) | void GetChar()
  function Error (line 12) | void Error(char *s)
  function Abort (line 17) | void Abort(char *s)
  function Expected (line 24) | void Expected(char *s)
  function Match (line 31) | void Match(char x)
  function IsAlpha (line 42) | int IsAlpha(char c)
  function IsDigit (line 47) | int IsDigit(char c)
  function IsAddop (line 52) | int IsAddop(char c)
  function GetName (line 57) | char GetName()
  function GetNum (line 72) | char GetNum()
  function Emit (line 86) | void Emit(char *s)
  function EmitLn (line 91) | void EmitLn(char *s)
  function Init (line 97) | void Init()

FILE: 2/main.c
  function Multiply (line 14) | void Multiply()
  function Divide (line 23) | void Divide()
  function Factor (line 45) | void Factor()
  function Term (line 67) | void Term()
  function Expression (line 88) | void Expression()
  function Add (line 114) | void Add()
  function Substract (line 124) | void Substract()
  function main (line 134) | int main()

FILE: 3/cradle.c
  function GetChar (line 5) | void GetChar()
  function Error (line 11) | void Error(char *s)
  function Abort (line 16) | void Abort(char *s)
  function Expected (line 23) | void Expected(char *s)
  function Match (line 30) | void Match(char x)
  function IsAlpha (line 42) | int IsAlpha(char c)
  function IsDigit (line 47) | int IsDigit(char c)
  function IsAlNum (line 52) | int IsAlNum(char c)
  function IsAddop (line 57) | int IsAddop(char c)
  function IsWhite (line 62) | int IsWhite(char c)
  function SkipWhite (line 108) | void SkipWhite()
  function Emit (line 115) | void Emit(char *s)
  function EmitLn (line 120) | void EmitLn(char *s)
  function Init (line 126) | void Init()

FILE: 3/main.c
  function Multiply (line 16) | void Multiply()
  function Divide (line 25) | void Divide()
  function Ident (line 47) | void Ident()
  function Factor (line 61) | void Factor()
  function Term (line 80) | void Term()
  function Expression (line 101) | void Expression()
  function Add (line 127) | void Add()
  function Substract (line 137) | void Substract()
  function Assignment (line 146) | void Assignment()
  function main (line 156) | int main()

FILE: 4/cradle.c
  function uppercase (line 6) | char uppercase(char c)
  function GetChar (line 11) | void GetChar()
  function Error (line 18) | void Error(char *s)
  function Abort (line 23) | void Abort(char *s)
  function Expected (line 30) | void Expected(char *s)
  function Match (line 37) | void Match(char x)
  function Newline (line 47) | void Newline()
  function IsAlpha (line 59) | int IsAlpha(char c)
  function IsDigit (line 64) | int IsDigit(char c)
  function IsAddop (line 69) | int IsAddop(char c)
  function GetName (line 74) | char GetName()
  function GetNum (line 89) | int GetNum()
  function Emit (line 105) | void Emit(char *s)
  function EmitLn (line 110) | void EmitLn(char *s)
  function Init (line 116) | void Init()
  function InitTable (line 122) | void InitTable()

FILE: 4/main.c
  function Multiply (line 17) | void Multiply()
  function Divide (line 27) | void Divide()
  function Ident (line 49) | void Ident()
  function Factor (line 63) | int Factor()
  function Term (line 79) | int Term()
  function Expression (line 101) | int Expression()
  function Add (line 130) | void Add()
  function Substract (line 141) | void Substract()
  function Assignment (line 150) | void Assignment()
  function Input (line 160) | void Input()
  function Output (line 168) | void Output()
  function main (line 175) | int main()

FILE: 5/cradle.c
  function uppercase (line 12) | char uppercase(char c)
  function GetChar (line 17) | void GetChar()
  function Error (line 24) | void Error(char *s)
  function Abort (line 29) | void Abort(char *s)
  function Expected (line 36) | void Expected(char *s)
  function Match (line 43) | void Match(char x)
  function Newline (line 53) | void Newline()
  function IsAlpha (line 65) | int IsAlpha(char c)
  function IsDigit (line 70) | int IsDigit(char c)
  function IsAddop (line 75) | int IsAddop(char c)
  function GetName (line 80) | char GetName()
  function GetNum (line 95) | int GetNum()
  function Emit (line 111) | void Emit(char *s)
  function EmitLn (line 116) | void EmitLn(char *s)
  function Init (line 122) | void Init()
  function InitTable (line 130) | void InitTable()
  function PostLabel (line 146) | void PostLabel(char *label)

FILE: 5/main.c
  function Other (line 26) | void Other()
  function Block (line 32) | void Block(char *L)
  function Condition (line 67) | void Condition()
  function DoProgram (line 72) | void DoProgram()
  function DoIf (line 81) | void DoIf(char *L)
  function DoWhile (line 114) | void DoWhile()
  function DoLoop (line 133) | void DoLoop()
  function DoRepeat (line 148) | void DoRepeat()
  function DoFor (line 167) | void DoFor()
  function Expression (line 201) | void Expression()
  function DoDo (line 206) | void DoDo()
  function DoBreak (line 227) | void DoBreak(char *L)
  function main (line 238) | int main()

FILE: 6/cradle.c
  function uppercase (line 13) | char uppercase(char c)
  function GetChar (line 18) | void GetChar()
  function Error (line 24) | void Error(char *s)
  function Abort (line 29) | void Abort(char *s)
  function Expected (line 36) | void Expected(char *s)
  function Match (line 43) | void Match(char x)
  function Newline (line 53) | void Newline()
  function IsAlpha (line 65) | int IsAlpha(char c)
  function IsDigit (line 70) | int IsDigit(char c)
  function IsAddop (line 75) | int IsAddop(char c)
  function IsBoolean (line 80) | int IsBoolean(char c)
  function GetName (line 85) | char GetName()
  function GetNum (line 100) | int GetNum()
  function GetBoolean (line 116) | int GetBoolean()
  function IsOrop (line 126) | int IsOrop(char c)
  function IsRelop (line 131) | int IsRelop(char c)
  function Emit (line 136) | void Emit(char *s)
  function EmitLn (line 141) | void EmitLn(char *s)
  function Init (line 147) | void Init()
  function InitTable (line 155) | void InitTable()
  function PostLabel (line 171) | void PostLabel(char *label)
  function Fin (line 176) | void Fin()

FILE: 6/main.c
  function Other (line 49) | void Other()
  function Block (line 55) | void Block(char *L)
  function DoProgram (line 91) | void DoProgram()
  function DoIf (line 100) | void DoIf(char *L)
  function DoWhile (line 133) | void DoWhile()
  function DoLoop (line 152) | void DoLoop()
  function DoRepeat (line 167) | void DoRepeat()
  function DoFor (line 186) | void DoFor()
  function DoDo (line 220) | void DoDo()
  function DoBreak (line 241) | void DoBreak(char *L)
  function BoolFactor (line 252) | void BoolFactor()
  function Relation (line 265) | void Relation()
  function NotFactor (line 288) | void NotFactor()
  function BoolTerm (line 299) | void BoolTerm()
  function BoolExpression (line 311) | void BoolExpression()
  function BoolOr (line 329) | void BoolOr()
  function BoolXor (line 337) | void BoolXor()
  function Equals (line 345) | void Equals()
  function NotEquals (line 356) | void NotEquals()
  function Less (line 365) | void Less()
  function Greater (line 374) | void Greater()
  function Ident (line 383) | void Ident()
  function Factor (line 397) | void Factor()
  function SignedFactor (line 411) | void SignedFactor()
  function Multiply (line 430) | void Multiply()
  function Divide (line 439) | void Divide()
  function Term (line 461) | void Term()
  function Add (line 480) | void Add()
  function Subtract (line 489) | void Subtract()
  function Expression (line 498) | void Expression()
  function Assignment (line 516) | void Assignment()
  function main (line 526) | int main()

FILE: 7/cradle.c
  function Lookup (line 32) | int Lookup(const char  * const table[], const char *string, int n)
  function Scan (line 47) | void Scan()
  function uppercase (line 61) | char uppercase(char c)
  function GetChar (line 70) | void GetChar()
  function Error (line 76) | void Error(char *s)
  function Abort (line 81) | void Abort(char *s)
  function Expected (line 88) | void Expected(char *s)
  function Match (line 95) | void Match(char x)
  function MatchString (line 105) | void MatchString(char *str)
  function Newline (line 113) | void Newline()
  function IsWhite (line 125) | int IsWhite(char c)
  function IsOp (line 130) | int IsOp(char c)
  function IsAlpha (line 135) | int IsAlpha(char c)
  function IsDigit (line 140) | int IsDigit(char c)
  function IsAddop (line 145) | int IsAddop(char c)
  function IsBoolean (line 150) | int IsBoolean(char c)
  function IsAlNum (line 155) | int IsAlNum(char c)
  function GetName (line 160) | void GetName()
  function GetNum (line 179) | void GetNum()
  function GetBoolean (line 195) | int GetBoolean()
  function IsOrop (line 205) | int IsOrop(char c)
  function IsRelop (line 210) | int IsRelop(char c)
  function Emit (line 215) | void Emit(char *s)
  function EmitLn (line 220) | void EmitLn(char *s)
  function Init (line 226) | void Init()
  function SkipWhite (line 234) | void SkipWhite()
  function InitTable (line 241) | void InitTable()
  function PostLabel (line 257) | void PostLabel(char *label)
  function Fin (line 262) | void Fin()

FILE: 7/main.c
  function Block (line 53) | void Block()
  function DoProgram (line 75) | void DoProgram()
  function DoIf (line 82) | void DoIf()
  function DoWhile (line 111) | void DoWhile()
  function DoLoop (line 130) | void DoLoop()
  function DoRepeat (line 145) | void DoRepeat()
  function DoBreak (line 163) | void DoBreak(char *L)
  function BoolFactor (line 174) | void BoolFactor()
  function Relation (line 187) | void Relation()
  function NotFactor (line 210) | void NotFactor()
  function BoolTerm (line 221) | void BoolTerm()
  function BoolExpression (line 233) | void BoolExpression()
  function BoolOr (line 251) | void BoolOr()
  function BoolXor (line 259) | void BoolXor()
  function Equals (line 267) | void Equals()
  function NotEquals (line 278) | void NotEquals()
  function Less (line 287) | void Less()
  function Greater (line 296) | void Greater()
  function Ident (line 305) | void Ident()
  function Factor (line 319) | void Factor()
  function SignedFactor (line 334) | void SignedFactor()
  function Multiply (line 350) | void Multiply()
  function Divide (line 359) | void Divide()
  function Term1 (line 381) | void Term1()
  function Term (line 399) | void Term()
  function FirstTerm (line 405) | void FirstTerm()
  function Add (line 411) | void Add()
  function Subtract (line 420) | void Subtract()
  function Expression (line 429) | void Expression()
  function Condition (line 448) | void Condition()
  function Assignment (line 453) | void Assignment()
  function main (line 464) | int main()

FILE: 9/cradle.c
  function uppercase (line 13) | char uppercase(char c)
  function GetChar (line 18) | void GetChar()
  function Error (line 25) | void Error(char *s)
  function Abort (line 30) | void Abort(char *s)
  function Expected (line 37) | void Expected(char *s)
  function Match (line 44) | void Match(char x)
  function Newline (line 54) | void Newline()
  function IsAlpha (line 66) | int IsAlpha(char c)
  function IsDigit (line 71) | int IsDigit(char c)
  function IsAddop (line 76) | int IsAddop(char c)
  function GetName (line 81) | char GetName()
  function GetNum (line 96) | int GetNum()
  function Emit (line 112) | void Emit(char *s)
  function EmitLn (line 117) | void EmitLn(char *s)
  function Init (line 123) | void Init()
  function InitTable (line 131) | void InitTable()
  function PostLabel (line 147) | void PostLabel(char *label)

FILE: 9/main.c
  function Prog (line 26) | void Prog()
  function Prolog (line 36) | void Prolog(char name)
  function Epilog (line 43) | void Epilog(char name)
  function DoBlock (line 49) | void DoBlock(char name)
  function Declarations (line 57) | void Declarations()
  function Labels (line 84) | void Labels()
  function Constants (line 89) | void Constants()
  function Types (line 94) | void Types()
  function Variables (line 99) | void Variables()
  function DoProcedure (line 104) | void DoProcedure()
  function DoFunction (line 109) | void DoFunction()
  function Statements (line 114) | void Statements()
  function main (line 123) | int main()
Condensed preview — 76 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (855K chars).
[
  {
    "path": ".gitignore",
    "chars": 37,
    "preview": "*.s\n*.o\n*.so\n.~\n*.swp\n*.out\n*.pdf\n*~\n"
  },
  {
    "path": "1/cradle.c",
    "chars": 1039,
    "preview": "#include \"cradle.h\"\n#include <stdio.h>\n#include <stdlib.h>\n\n\nvoid GetChar() \n{\n    Look = getchar();\n}\n\n\nvoid Error(char"
  },
  {
    "path": "1/cradle.h",
    "chars": 374,
    "preview": "#ifndef _CRADLE_H\n#define _CRADLE_H\n\n#define UPCASE(C) (~(1<<5) & (C))\n#define MAX_BUF 100\n\nstatic char tmp[MAX_BUF];\n\nc"
  },
  {
    "path": "1/tutor1.txt",
    "chars": 14162,
    "preview": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n                            LET'S BUILD A COMPILER!\n\n                                       B"
  },
  {
    "path": "10/Makefile",
    "chars": 133,
    "preview": "IN=main.c cradle.c\nOUT=main\nFLAGS=-Wall -Werror\n\nall:\n\tgcc -o $(OUT) $(IN) $(FLAGS)\n\nrun:\n\t./$(OUT)\n\n.PHONY: clean\nclean"
  },
  {
    "path": "10/cradle.c",
    "chars": 7448,
    "preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdbool.h>\n#include <string.h>\n\n#include \"cradle.h\"\n#include <malloc.h>"
  },
  {
    "path": "10/cradle.h",
    "chars": 1291,
    "preview": "#ifndef _CRADLE_H\n#define _CRADLE_H\n#include <stdbool.h>\n\n#define MAX_BUF 100\n#define MaxEntry 100\nextern char tmp[MAX_B"
  },
  {
    "path": "10/main.c",
    "chars": 8109,
    "preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdbool.h>\n\n#include \"cradle.h\"\n\n#ifdef DEBUG\n#defi"
  },
  {
    "path": "10/prog.txt",
    "chars": 142,
    "preview": "PROGRAM\nVAR xx,\nyy=1,\nzz=10\nBEGIN\n  WHILE yy <= zz\n    IF yy <> 5 \n      xx=xx+yy\n    ELSE\n      xx=xx+5\n    ENDIF\n  yy="
  },
  {
    "path": "10/tutor10.txt",
    "chars": 86120,
    "preview": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n                     LET'S BUILD A COMPILER!\n\n                                By\n\n           "
  },
  {
    "path": "11/Makefile",
    "chars": 133,
    "preview": "IN=main.c cradle.c\nOUT=main\nFLAGS=-Wall -Werror\n\nall:\n\tgcc -o $(OUT) $(IN) $(FLAGS)\n\nrun:\n\t./$(OUT)\n\n.PHONY: clean\nclean"
  },
  {
    "path": "11/cradle.c",
    "chars": 8120,
    "preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdbool.h>\n#include <string.h>\n\n#include \"cradle.h\"\n#include <malloc.h>"
  },
  {
    "path": "11/cradle.h",
    "chars": 1441,
    "preview": "#ifndef _CRADLE_H\n#define _CRADLE_H\n#include <stdbool.h>\n\n#define MAX_BUF 100\n#define MaxEntry 100\nextern char tmp[MAX_B"
  },
  {
    "path": "11/main.c",
    "chars": 7124,
    "preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdbool.h>\n\n#include \"cradle.h\"\n\n#ifdef DEBUG\n#defi"
  },
  {
    "path": "11/prog.txt",
    "chars": 142,
    "preview": "PROGRAM\nVAR xx,\nyy=1,\nzz=10\nBEGIN\n  WHILE yy <= zz\n    IF yy <> 5 \n      xx=xx+yy\n    ELSE\n      xx=xx+5\n    ENDIF\n  yy="
  },
  {
    "path": "11/tutor11.txt",
    "chars": 41928,
    "preview": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n                     LET'S BUILD A COMPILER!\n\n                                By\n\n           "
  },
  {
    "path": "12/Makefile",
    "chars": 133,
    "preview": "IN=main.c cradle.c\nOUT=main\nFLAGS=-Wall -Werror\n\nall:\n\tgcc -o $(OUT) $(IN) $(FLAGS)\n\nrun:\n\t./$(OUT)\n\n.PHONY: clean\nclean"
  },
  {
    "path": "12/cradle.c",
    "chars": 8772,
    "preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdbool.h>\n#include <string.h>\n\n#include \"cradle.h\"\n#include <malloc.h>"
  },
  {
    "path": "12/cradle.h",
    "chars": 1461,
    "preview": "#ifndef _CRADLE_H\n#define _CRADLE_H\n#include <stdbool.h>\n\n#define MAX_BUF 100\n#define MaxEntry 100\nextern char tmp[MAX_B"
  },
  {
    "path": "12/main.c",
    "chars": 7325,
    "preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdbool.h>\n\n#include \"cradle.h\"\n\n#ifdef DEBUG\n#defi"
  },
  {
    "path": "12/prog.txt",
    "chars": 207,
    "preview": "PROGRAM;\n/* \n    calculate 1+2+...+10 \n    /* nested comment */\n*/\nVAR xx, yy=1, zz=10;\nBEGIN\n  WHILE yy <= zz\n    IF yy"
  },
  {
    "path": "12/tutor12.txt",
    "chars": 27678,
    "preview": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n                     LET'S BUILD A COMPILER!\n\n                                By\n\n           "
  },
  {
    "path": "13/Makefile",
    "chars": 133,
    "preview": "IN=main.c cradle.c\nOUT=main\nFLAGS=-Wall -Werror\n\nall:\n\tgcc -o $(OUT) $(IN) $(FLAGS)\n\nrun:\n\t./$(OUT)\n\n.PHONY: clean\nclean"
  },
  {
    "path": "13/cradle.c",
    "chars": 5145,
    "preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"cradle.h\"\n\n\n#define MaxEntry 26\nconst char TAB = '"
  },
  {
    "path": "13/cradle.h",
    "chars": 2285,
    "preview": "#ifndef _CRADLE_H\n#define _CRADLE_H\n\n#include <stdbool.h>\n\n#define MAX_BUF 100\nextern const char TAB;\nextern const char "
  },
  {
    "path": "13/main.c",
    "chars": 5375,
    "preview": "#include <stdio.h>\n#include <string.h>\n\n#include \"cradle.h\"\n\n\nvoid Expression();\nvoid AssignOrProc();\nvoid Assignment(ch"
  },
  {
    "path": "13/prog.txt",
    "chars": 50,
    "preview": "vx\nvy\nvz\npm(a,b,c)\nvt\nb\nt=a\nx=t\ne\nPtb\nm(z,z,z)\ne.\n"
  },
  {
    "path": "13/tutor13.txt",
    "chars": 73827,
    "preview": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n                     LET'S BUILD A COMPILER!\n\n                                By\n\n         "
  },
  {
    "path": "14/Makefile",
    "chars": 133,
    "preview": "IN=main.c cradle.c\nOUT=main\nFLAGS=-Wall -Werror\n\nall:\n\tgcc -o $(OUT) $(IN) $(FLAGS)\n\nrun:\n\t./$(OUT)\n\n.PHONY: clean\nclean"
  },
  {
    "path": "14/README.md",
    "chars": 530,
    "preview": "# Notes\n\nx86 instructions is different to 68000 in some ways, the generated code is\na trying to simulate the author's 68"
  },
  {
    "path": "14/cradle.c",
    "chars": 10563,
    "preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"cradle.h\"\n\n\n#define MaxEntry 26\nconst char TAB = '"
  },
  {
    "path": "14/cradle.h",
    "chars": 2753,
    "preview": "#ifndef _CRADLE_H\n#define _CRADLE_H\n\n#include <stdbool.h>\n\n#define MAX_BUF 100\nextern const char TAB;\nextern const char "
  },
  {
    "path": "14/main.c",
    "chars": 3948,
    "preview": "#include <stdio.h>\n#include <string.h>\n\n#include \"cradle.h\"\n\nchar Term();\nchar Expression();\nvoid Assignment();\nchar Fac"
  },
  {
    "path": "14/prog.txt",
    "chars": 42,
    "preview": "ba\nwb\nlc\nB \na=10\nb=70000\nc=a+b\na=c\nb=c\n.\n\n"
  },
  {
    "path": "14/tutor14.txt",
    "chars": 74254,
    "preview": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n                     LET'S BUILD A COMPILER!\n\n                                By\n\n      "
  },
  {
    "path": "15/tutor15.txt",
    "chars": 43576,
    "preview": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n                     LET'S BUILD A COMPILER!\n\n                                By\n\n      "
  },
  {
    "path": "16/tutor16.txt",
    "chars": 44027,
    "preview": " \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n                       LET'S BUILD A COMPILER! \n \n      "
  },
  {
    "path": "2/Makefile",
    "chars": 133,
    "preview": "IN=main.c cradle.c\nOUT=main\nFLAGS=-Wall -Werror\n\nall:\n\tgcc -o $(OUT) $(IN) $(FLAGS)\n\nrun:\n\t./$(OUT)\n\n.PHONY: clean\nclean"
  },
  {
    "path": "2/cradle.c",
    "chars": 1100,
    "preview": "#include \"cradle.h\"\n#include <stdio.h>\n#include <stdlib.h>\n\n\nvoid GetChar() \n{\n    Look = getchar();\n}\n\n\nvoid Error(char"
  },
  {
    "path": "2/cradle.h",
    "chars": 385,
    "preview": "#ifndef _CRADLE_H\n#define _CRADLE_H\n#define UPCASE(C) ((1<<6)| (C))\n\n#define MAX_BUF 100\nchar tmp[MAX_BUF];\n\nchar Look;\n"
  },
  {
    "path": "2/main.c",
    "chars": 2308,
    "preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"cradle.h\"\n\nvoid Term();\nvoid Expression();\nvoid Ad"
  },
  {
    "path": "2/tutor2.txt",
    "chars": 27507,
    "preview": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n                     LET'S BUILD A COMPILER!\n\n                                By\n\n         "
  },
  {
    "path": "3/Makefile",
    "chars": 133,
    "preview": "IN=main.c cradle.c\nOUT=main\nFLAGS=-Wall -Werror\n\nall:\n\tgcc -o $(OUT) $(IN) $(FLAGS)\n\nrun:\n\t./$(OUT)\n\n.PHONY: clean\nclean"
  },
  {
    "path": "3/cradle.c",
    "chars": 1551,
    "preview": "#include \"cradle.h\"\n#include <stdio.h>\n#include <stdlib.h>\n\nvoid GetChar() \n{\n    Look = getchar();\n}\n\n\nvoid Error(char "
  },
  {
    "path": "3/cradle.h",
    "chars": 441,
    "preview": "#ifndef _CRADLE_H\n#define _CRADLE_H\n\n#define MAX_BUF 100\nchar tmp[MAX_BUF];\nchar token_buf[MAX_BUF];\n\nchar Look;\n\nvoid G"
  },
  {
    "path": "3/main.c",
    "chars": 2899,
    "preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"cradle.h\"\n\nvoid Term();\nvoid Expression();\nvoid Ad"
  },
  {
    "path": "3/tutor3.txt",
    "chars": 27094,
    "preview": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n                     LET'S BUILD A COMPILER!\n\n                                By\n\n          "
  },
  {
    "path": "4/Makefile",
    "chars": 133,
    "preview": "IN=main.c cradle.c\nOUT=main\nFLAGS=-Wall -Werror\n\nall:\n\tgcc -o $(OUT) $(IN) $(FLAGS)\n\nrun:\n\t./$(OUT)\n\n.PHONY: clean\nclean"
  },
  {
    "path": "4/cradle.c",
    "chars": 1602,
    "preview": "#include \"cradle.h\"\n#include <stdio.h>\n#include <stdlib.h>\n\n/* Helper Functions */\nchar uppercase(char c)\n{\n    return ("
  },
  {
    "path": "4/cradle.h",
    "chars": 432,
    "preview": "#ifndef _CRADLE_H\n#define _CRADLE_H\n\n#define MAX_BUF 100\n#define TABLE_SIZE 26\nchar tmp[MAX_BUF];\n\nchar Look;\nint Table["
  },
  {
    "path": "4/main.c",
    "chars": 3251,
    "preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"cradle.h\"\n\nint Term();\nint Expression();\nvoid Add("
  },
  {
    "path": "4/tutor4.txt",
    "chars": 26003,
    "preview": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n                     LET'S BUILD A COMPILER!\n\n                                By\n\n          "
  },
  {
    "path": "5/Makefile",
    "chars": 133,
    "preview": "IN=main.c cradle.c\nOUT=main\nFLAGS=-Wall -Werror\n\nall:\n\tgcc -o $(OUT) $(IN) $(FLAGS)\n\nrun:\n\t./$(OUT)\n\n.PHONY: clean\nclean"
  },
  {
    "path": "5/README.md",
    "chars": 2020,
    "preview": "# Some Notes on the original Article\n\n## The IF statement\nThe BNF shown on this chapter had some bugs. The author said t"
  },
  {
    "path": "5/cradle.c",
    "chars": 1889,
    "preview": "#include \"cradle.h\"\n#include <stdio.h>\n#include <stdlib.h>\n\n#define TABLE_SIZE 26\nstatic int LCount = 0;\nstatic char lab"
  },
  {
    "path": "5/cradle.h",
    "chars": 440,
    "preview": "#ifndef _CRADLE_H\n#define _CRADLE_H\n\n#define MAX_BUF 100\nstatic char tmp[MAX_BUF];\nchar Look;\n\nvoid GetChar();\n\nvoid Err"
  },
  {
    "path": "5/main.c",
    "chars": 4390,
    "preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"cradle.h\"\n\n#ifdef DEBUG\n#define dprint(fmt, ...) p"
  },
  {
    "path": "5/tutor5.txt",
    "chars": 41754,
    "preview": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n                     LET'S BUILD A COMPILER!\n\n                                By\n\n          "
  },
  {
    "path": "6/Makefile",
    "chars": 133,
    "preview": "IN=main.c cradle.c\nOUT=main\nFLAGS=-Wall -Werror\n\nall:\n\tgcc -o $(OUT) $(IN) $(FLAGS)\n\nrun:\n\t./$(OUT)\n\n.PHONY: clean\nclean"
  },
  {
    "path": "6/cradle.c",
    "chars": 2342,
    "preview": "#include \"cradle.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#define TABLE_SIZE 26\nstatic int LCount "
  },
  {
    "path": "6/cradle.h",
    "chars": 535,
    "preview": "#ifndef _CRADLE_H\n#define _CRADLE_H\n\n#define MAX_BUF 100\nstatic char tmp[MAX_BUF];\nchar Look;\n\nvoid GetChar();\n\nvoid Err"
  },
  {
    "path": "6/main.c",
    "chars": 9590,
    "preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"cradle.h\"\n\n#ifdef DEBUG\n#define dprint(fmt, ...) p"
  },
  {
    "path": "6/tutor6.txt",
    "chars": 36173,
    "preview": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n                     LET'S BUILD A COMPILER!\n\n                                By\n\n          "
  },
  {
    "path": "7/Makefile",
    "chars": 133,
    "preview": "IN=main.c cradle.c\nOUT=main\nFLAGS=-Wall -Werror\n\nall:\n\tgcc -o $(OUT) $(IN) $(FLAGS)\n\nrun:\n\t./$(OUT)\n\n.PHONY: clean\nclean"
  },
  {
    "path": "7/cradle.c",
    "chars": 3901,
    "preview": "#include \"cradle.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdbool.h>\n\n#define TABLE_SIZE "
  },
  {
    "path": "7/cradle.h",
    "chars": 775,
    "preview": "#ifndef _CRADLE_H\n#define _CRADLE_H\n\n#define MAX_BUF 100\nchar Look;\nextern char Token;      /* current token */\nextern c"
  },
  {
    "path": "7/main.c",
    "chars": 7869,
    "preview": "/* not that only IF statement is supported for merging lexer and parser in\n * chapter 7.\n */\n#include <stdio.h>\n#include"
  },
  {
    "path": "7/tutor7.txt",
    "chars": 64189,
    "preview": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n                     LET'S BUILD A COMPILER!\n\n                                By\n\n          "
  },
  {
    "path": "8/tutor8.txt",
    "chars": 23133,
    "preview": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n                     LET'S BUILD A COMPILER!\n\n                                By\n\n          "
  },
  {
    "path": "9/Makefile",
    "chars": 133,
    "preview": "IN=main.c cradle.c\nOUT=main\nFLAGS=-Wall -Werror\n\nall:\n\tgcc -o $(OUT) $(IN) $(FLAGS)\n\nrun:\n\t./$(OUT)\n\n.PHONY: clean\nclean"
  },
  {
    "path": "9/cradle.c",
    "chars": 1908,
    "preview": "#include \"cradle.h\"\n#include <stdio.h>\n#include <stdlib.h>\n\n#define TABLE_SIZE 26\nstatic int LCount = 0;\nstatic char lab"
  },
  {
    "path": "9/cradle.h",
    "chars": 440,
    "preview": "#ifndef _CRADLE_H\n#define _CRADLE_H\n\n#define MAX_BUF 100\nextern char tmp[MAX_BUF];\nchar Look;\n\nvoid GetChar();\n\nvoid Err"
  },
  {
    "path": "9/main.c",
    "chars": 1866,
    "preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"cradle.h\"\n\n#ifdef DEBUG\n#define dprint(fmt, ...) p"
  },
  {
    "path": "9/tutor9.txt",
    "chars": 26763,
    "preview": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n                     LET'S BUILD A COMPILER!\n\n                                By\n\n          "
  },
  {
    "path": "README.md",
    "chars": 3100,
    "preview": "# Information\nA C version of the \"Let's Build a Compiler\" by Jack Crenshaw \nhttp://compilers.iecc.com/crenshaw/\n\nThis re"
  },
  {
    "path": "get_chapters.sh",
    "chars": 204,
    "preview": "#!/bin/bash\nset -ex\n\nURL=\"http://compilers.iecc.com/crenshaw/\"\n\nfor i in $(seq 1 16); do  \n\n\tfile=\"tutor\"$i\".txt\"\n\tmkdir"
  }
]

About this extraction

This page contains the full source code of the lotabout/Let-s-build-a-compiler GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 76 files (799.6 KB), approximately 195.8k tokens, and a symbol index with 666 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!