Skip to content

Latest commit

 

History

History
6029 lines (5680 loc) · 238 KB

pcom.pas.org

File metadata and controls

6029 lines (5680 loc) · 238 KB

pascal p5 assembler / interpreter

preamble

compiler options

(*$c+,t-,d-,l-*)

header comment

{*******************************************************************************
*                                                                              *
*                     Portable Pascal assembler/interpreter                    *
*                     *************************************                    *
*                                                                              *
*                                 Pascal P5                                    *
*                                                                              *
*                                 ETH May 76                                   *
*                                                                              *
* Authors:                                                                     *
*    Urs Ammann                                                                *
*    Kesav Nori                                                                *
*    Christian Jacobi                                                          *
*    K. Jensen                                                                 *
*    N. Wirth                                                                  *
*                                                                              *
*    Address:                                                                  *
*       Institut Fuer Informatik                                               *
*       Eidg. Technische Hochschule                                            *
*       CH-8096 Zuerich                                                        *
*                                                                              *
*  This code is fully documented in the book                                   *
*        "Pascal Implementation"                                               *
*   by Steven Pemberton and Martin Daniels                                     *
* published by Ellis Horwood, Chichester, UK                                   *
*         ISBN: 0-13-653-0311                                                  *
*       (also available in Japanese)                                           *
*                                                                              *
* Steven Pemberton, CWI/AA,                                                    *
* Kruislaan 413, 1098 SJ Amsterdam, NL                                         *
* [email protected]                                                      *
*                                                                              *
* Adaption from P4 to P5 by:                                                   *
*                                                                              *
*    Scott A. Moore                                                            *
*    [email protected]                                                       *
*                                                                              *
*    The comments marked with brackets are mine [sam]                          *
*                                                                              *
* P5 is an extended version of P4 with the following goals:                    *
*                                                                              *
* 1. The remaining unimplemented functions of Pascal are implemented, so that  *
*    P5 is no longer a "subset" of full Pascal. This was done because it is    *
*    no longer necessary to produce a minimum size implementation, and it      *
*    allows any standard program to be used with P5.                           *
*                                                                              *
* 2. The P5 compiler is brought up to ISO 7185 level 0 standards, both in the  *
*    language it compiles for, and the language it is implemented in.          *
*                                                                              *
* 3. The internal storage efficiency is increased. For example, character      *
*    strings no longer take as much space per character as integers and other  *
*    data. Sets are placed in their own space so that the minimum stack size   *
*    not determined by set size.                                               *
*                                                                              *
* 4. The remaining limitations and dependencies on the CDC 6000 version are    *
*    removed. For example, the instruction store no longer is packed 2         *
*    instructions to a 60 bit word.                                            *
*                                                                              *
* 5. General clean up. Longstanding bugs and issues are addressed. Constants   *
*    that were buried in the source (magic numbers) were made constants. The   *
*    type 'alpha' (specific to CDC 6000) was replaced with idstr, etc.         *
*                                                                              *
* The idea of P5 is to obtain a compiler that is ISO 7185 compliant, can       *
* compile itself, can compile any reasonable standard program, and is          *
* efficient enough to be used as a normal compiler for some certain uses.      *
* Finally, it can serve as a starting implementation for native compilers.     *
*                                                                              *
* P5 machine instructions added:                                               *
*                                                                              *
* rnd    round:   expects a float on stack, performs round() and places the    *
*                 result back on the stack as an integer.                      *
*                                                                              *
* pck ln pack:    Expects a packed array on stack top, followed by the         *
*                 starting subscript, then the unpacked array. The parameter   *
*                 contains the length of packed array in elements. Performs    *
*                 pack(upa, ss, pa) and removes all from stack. The starting   *
*                 subscript is zero based and scaled to the element size.      *
*                                                                              *
* upk ln pack:    Expects the starting subscript on stack top, followed by the *
*                 unpacked array, then the packed array. The parameter         *
*                 contains the length of packed array in elements. Performs    *
*                 unpack(pa, upa, ss) and removes all from stack. The starting *
*                 subscript is zero based and scaled to the element size.      *
*                                                                              *
* rgs    set rng: Expects a set range specification on stack, with the last    *
*                 value on the top, and the first value next. The two values   *
*                 are replaced with a set with all of the values between and   *
*                 including the first and last values.                         *
*                                                                              *
* fbv ad buf val: Validates a file buffer variable. Expects a file address on  *
*                 stack. The buffer is "validated" for lazy I/O, which means   *
*                 that if the associated file is in read mode, the delayed     *
*                 read to the buffer variable occurs. The file address remains *
*                 on the stack.                                                *
*                                                                              *
* ipj v l ip jmp: Interprocedure jump. Contains the level of the target        *
*                 procedure, and the label to jump to. The stack is adjusted   *
*                 to remove all nested procedures/functions, then the label is *
*                 unconditionally jumped to.                                   *        
*                                                                              *
* cip p           Call indirect procedure/function. The top of stack has the   *
*                 address of a mp/address pair pushed by lpa. The dl of the    *
*                 current mark is replaced by the mp, and the address replaces *
*                 the current pc. The mp/ad address is removed from stack.     *
*                                                                              *
* lpa p l q       Load procedure address. The current mark pointer is loaded   *
*                 onto the stack, followed by the target procedure or function *
*                 address. This puts enough information on the stack to call   *
*                 it with the callers environment.                             *
*                                                                              *
* lip p q         load procedure function address. Loads a mark/address pair   *
*                 for a procedure or function parameter onto the stack. Used   *
*                 to pass a procedure or function parameter to another         *
*                 procedure or function.                                       *
*                                                                              *
* efb    eof:     Find eof for binary file. The top of stack is a logical file *
*                 number. The eof boolean vale replaces it.                    *
*                                                                              *
* fvb ad buf val: Expects the length of the file buffer on stack, and the file *
*                 address under that. The buffer is "validated" for lazy I/O,  *
*                 which means that if the associated file is in read mode, the *
*                 delayed read to the buffer variable occurs. The buffer       *
*                 length is removed only.                                      *
*                                                                              *
* dmp q           Subtracts the value from the stack top. Used to dump the top *
*                 of the stack.                                                *
*                                                                              *
* swp q           Pulls the second on stack to the top, swapping the top to    *
*                 elements. The size of the second on stack is specified, but  *
*                 the top of the on stack is implied as a pointer.             *
*                                                                              *
* tjp q           Expects a boolean on stack. Jumps to the address if the      *
*                 value is true. Removes the value from the stack.             *
*                                                                              *
* P5 machine built in procedures/functions added:                              *
*                                                                              *
* pag    page:    Expects a logical file number on stack top. Performs page(). *
*                                                                              *
* rsf    reset:   Expects a logical file number on stack top. Performs         *
*                 reset() and sets the file to text mode.                      *
*                                                                              *
* rwf    rewrite: Expects a logical file number on stack top. Performs         *
*                 reset() and sets the file to text mode.                      *
*                                                                              *
* wrb    write:   Expects a field number on stack top, followed by a boolean   *
*                 to print, then the logical file number. The boolean is       *
*                 output as per ISO 7185.                                      *
*                                                                              *
* rgs    set rng: Expects a set range specification on stack, with the last    *
*                 value on the top, and the first value next. The two values   *
*                 are replaced with a set with all of the values between and   *
*                 including the first and last values.                         *
*                                                                              *
* wrf    write:   Expects a logical file number on stack top, followed by a    *
*                 field number, then a fraction, then a real to print. The     *
*                 real is output in r:f:f (fraction) format. All but the file  *
*                 are removed from stack.                                      *
*                                                                              *
* wbf    write:   Expects a file address on stack top, followed by the length  *
*                 of the type to write, then the variable address to write     *
*                 from. Writes binary store to the file.                       *
*                                                                              *
* wbi    write:   Expects a file address on stack top, followed by an integer. *
*                 Writes the integer to the file in binary format.             *
*                                                                              *
* wbr    write:   Expects a file address on stack top, followed by a real.     *
*                 Writes the real to the file in binary format.                *
*                                                                              *
* wbc    write:   Expects a file address on stack top, followed by a           *
*                 character. Writes the character to the file in binary        *
*                 format.                                                      *
*                                                                              *
* wbb    write:   Expects a file address on stack top, followed by a boolean.  *
*                 Writes the boolean to the file in binary format.             *
*                                                                              *
* rbf    read:    Expects a file address on stack top, followed by the length  *
*                 of the type to read, then the variable address to read       *
*                 from. Reads binary store from the file.                      *
*                                                                              *
* rsb    reset:   Expects a logical file number on stack top. Performs         *
*                 reset() and sets the file to binary mode.                    *
*                                                                              *
* rwb    rewrite: Expects a logical file number on stack top. Performs         *
*                 reset() and sets the file to binary mode.                    *
*                                                                              *
* gbf    get:     Get file binary. Expects the length of a file element on     *
*                 stack top, followed by a pointer to the file. The next file  *
*                 element is loaded to the file buffer.                        *
*                                                                              *
* pbf    put:     Put file binary. Expects the length of a file element on     *
*                 stack top, followed by a pointer to the file. Writes the     *
*                 file buffer to thr file.                                     *
*                                                                              *
* Note that the previous version of P4 added some type specified instructions  *
* that used to be unified, typeless instructions.                              *
*                                                                              *
* P5 errors added:                                                             *
*                                                                              *
* 182 identifier too long                                                      *
* 183 For index variable must be local to this block                           *
* 184 Interprocedure goto does not reference outter block of destination       *
* 185 Goto references deeper nested statement                                  *
* 186 Label referenced by goto at lesser statement level                       *
* 187 Goto references label in different nested statement                      *
* 188 Label referenced by goto in different nested statement                   *
* 189 Parameter lists of formal and actual parameters not congruous.           *
*                                                                              *
* P5 instructions modified:                                                    *
*                                                                              *
* lca'string'       '                                                          *
*                                                                              *
* was changed to                                                               *
*                                                                              *
* lca 'string'''                                                               *
*                                                                              *
* That is, lca has a space before the opening quote, no longer pads to the     *
* right, and represents single quotes with a quote image. pint converts quote  *
* images back to single quotes, and pads out strings to their full length.     *
*                                                                              *
* In addition, the way files work was extensively modified. Original P5 could  *
* not represent files as full1y expressed variables, such as within an array   *
* or record, and were effectively treated as constants. To treat them as true  *
* variable accesses, the stacking order of the file in all file subroutines    *
* was changed so that the file is on the bottom. This matches the source       *
* order of the file in write(f, ...) or read(f, ...). Also, the file           *
* operations now leave the file on the stack for the duration of a write or    *
* read, then dump them using a specific new instruction "dmp". This allows     *
* multiparameter writes and reads to be effectively a chain of single          *
* operations using one file reference. Finally, files were tied to the type    *
* ending 'a', because files are now full variable references.                  *
*                                                                              *
*******************************************************************************}

main code

program pascalcompiler(input,output,prr);

label 99; { terminate immediately }

const 

   {

   Program object sizes and characteristics, sync with pint. These define
   the machine specific characteristics of the target. 
   
   This configuration is for a 32 bit machine as follows:

   integer               32  bits
   real                  64  bits
   char                  8   bits
   boolean               8   bits
   set                   256 bits
   pointers              32  bits
   marks                 32  bits
   File logical number   8   bits

   Both endian types are supported. There is no alignment needed, but you
   may wish to use alignment to tune the runtime speed.

   The machine characteristics dependent on byte accessable machines. This
   table is all you should need to adapt to any byte addressable machine.

   }

   intsize     =        4;  { size of integer }
   intal       =        4;  { memory alignment of integer }
   realsize    =        8;  { size of real }
   realal      =        4;  { memory alignment of real }
   charsize    =        1;  { size of char }
   charal      =        1;  { memory alignment of char }
   charmax     =        1;
   boolsize    =        1;  { size of boolean }
   boolal      =        1;  { alignment of boolean }
   ptrsize     =        4;  { size of pointer }
   adrsize     =        4;  { size of address }
   adral       =        4;  { alignment of address }
   setsize     =       32;  { size of set }
   setal       =        1;  { alignment of set }
   filesize    =        1;  { required runtime space for file (lfn) }
   fileidsize  =        1;  { size of the lfn only }
   stackal     =        4;  { alignment of stack }
   stackelsize =        4;  { stack element size }
   maxsize     =       32;  { this is the largest type that can be on the stack }
   heapal      =        4;  { alignment for each heap arena }
   sethigh     =      255;  { Sets are 256 values }
   setlow      =        0;
   ordmaxchar  =      255;  { Characters are 8 bit ISO/IEC 8859-1 }
   ordminchar  =        0;
   maxresult   = realsize;  { maximum size of function result }
   marksize    =       32;  { maxresult+6*ptrsize }
   { Value of nil is 1 because this allows checks for pointers that were
     initialized, which would be zero (since we clear all space to zero).
     In the new unified code/data space scheme, 0 and 1 are always invalid
     addresses, since the startup code is at least that long. }
   nilval      =        1;  { value of 'nil' }

   { end of pcom and pint common parameters }

   displimit   = 300;
   maxlevel    = 255;
   { strglgth used to define the size of all strings in pcom and pint. With the
     string quanta system, string lengths are effectively unlimited, but there
     it still sets the size of some buffers in pcom. }
   strglgth    = 250;
   { maximum number of digits in real, including sign and exponent }
   digmax      = 250;
   { lcaftermarkstack is a very pcom specific way of stating the size of a mark
     in pint. However, it is used frequently in Perberton's documentation, so I
     left it, but equated it to the more portable marksize. }
   lcaftermarkstack = marksize;
   fileal      = charal;
   (* stackelsize = minimum size for 1 stackelement
                  = k*stackal
      stackal     = scm(all other al-constants)
      charmax     = scm(charsize,charal)
                    scm = smallest common multiple
      lcaftermarkstack >= maxresult+3*ptrsize+max(x-size)
                        = k1*stackelsize          *)
   maxstack   =       1;
   parmal     = stackal;
   parmsize   = stackelsize;
   recal      = stackal;
   filebuffer =       4; { number of system defined files }
   maxaddr    =  maxint;
   maxsp      = 39;  { number of standard procedures/functions }
   maxins     = 74;  { maximum number of instructions }
   maxids     = 250; { maximum characters in id string (basically, a full line) }
   maxstd     = 39;  { number of standard identifiers }
   maxres     = 35;  { number of reserved words }
   reslen     = 9;   { maximum length of reserved words }
   varsqt     = 10;  { variable string quanta }
   prtlln     = 10;  { number of label characters to print in dumps }

   { default field sizes for write }
   intdeff    = 11; { default field length for integer }
   reldeff    = 22; { default field length for real }
   chrdeff    = 1;  { default field length for char (usually 1) }
   boldeff    = 5;  { default field length for boolean (usually 5 for 'false' }

   { debug flags }

   dodmplex   = false; { dump lexical }
   doprtryc   = false; { dump recycling tracker counts }

   { version numbers }

   majorver   = 1; { major version number }
   minorver   = 2; { minor version number }

types

type                                                        (*describing:*)
                                                            (*************)

     {marktype= ^integer;}
                                                            (*basic symbols*)
                                                            (***************)

     symbol = (ident,intconst,realconst,stringconst,notsy,mulop,addop,relop,
               lparent,rparent,lbrack,rbrack,comma,semicolon,period,arrow,
               colon,becomes,range,labelsy,constsy,typesy,varsy,funcsy,progsy,
               procsy,setsy,packedsy,arraysy,recordsy,filesy,beginsy,ifsy,
               casesy,repeatsy,whilesy,forsy,withsy,gotosy,endsy,elsesy,untilsy,
               ofsy,dosy,tosy,downtosy,thensy,nilsy,othersy);
     operator = (mul,rdiv,andop,idiv,imod,plus,minus,orop,ltop,leop,geop,gtop,
                 neop,eqop,inop,noop);
     setofsys = set of symbol;
     chtp = (letter,number,special,illegal,
             chstrquo,chcolon,chperiod,chlt,chgt,chlparen,chspace,chlcmt);
     { Here is the variable length string containment to save on space. strings
       strings are only stored in their length rounded to the nearest 10th. }
     strvsp = ^strvs; { pointer to variable length id string }
     strvs = record { id string variable length }
                 str:   packed array [1..varsqt] of char; { data contained }
                 next:  strvsp { next }
               end;

                                                            (*constants*)
                                                            (***********)
     setty = set of setlow..sethigh;
     cstclass = (reel,pset,strg);
     csp = ^ constant;
     constant = record
                       next: csp; { next entry link }
                       case cclass: cstclass of
                         reel: (rval: strvsp);
                         pset: (pval: setty);
                         strg: (slgth: 0..strglgth; sval: strvsp)
                       end;

     valu = record case intval: boolean of  (*intval never set nor tested*)
                     true:  (ival: integer);
                     false: (valp: csp)
                   end;

                                                           (*data structures*)
                                                           (*****************)
     levrange = 0..maxlevel; addrrange = 0..maxaddr;
     structform = (scalar,subrange,pointer,power,arrays,records,files,
                   tagfld,variant);
     declkind = (standard,declared);
     stp = ^ structure; 
     ctp = ^ identifier;

     structure = record
                   next: stp; { next entry link }
                   marked: boolean;   (*for test phase only*)
                   size: addrrange;
                   packing: boolean; { packing status }
                   case form: structform of
                     scalar:   (case scalkind: declkind of
                                  declared: (fconst: ctp); standard: ());
                     subrange: (rangetype: stp; min,max: valu);
                     pointer:  (eltype: stp);
                     power:    (elset: stp; matchpack: boolean);
                     arrays:   (aeltype,inxtype: stp);
                     records:  (fstfld: ctp; recvar: stp; recyc: stp);
                     files:    (filtype: stp);
                     tagfld:   (tagfieldp: ctp; fstvar: stp);
                     variant:  (nxtvar,subvar: stp; varval: valu)
                   end;

                                                            (*names*)
                                                            (*******)

     idclass = (types,konst,vars,field,proc,func);
     setofids = set of idclass;
     idkind = (actual,formal);
     idstr = packed array [1..maxids] of char;
     restr = packed array [1..reslen] of char;
     nmstr = packed array [1..digmax] of char;
     csstr = packed array [1..strglgth] of char;
     identifier = record
                   name: strvsp; llink, rlink: ctp;
                   idtype: stp; next: ctp; keep: boolean;
                   case klass: idclass of
                     types: ();
                     konst: (values: valu);
                     vars:  (vkind: idkind; vlev: levrange; vaddr: addrrange);
                     field: (fldaddr: addrrange);
                     proc, func:  (pfaddr: addrrange; pflist: ctp; { param list }
                                   case pfdeckind: declkind of
                              standard: (key: 1..18);
                              declared: (pflev: levrange; pfname: integer;
                                          case pfkind: idkind of
                                           actual: (forwdecl, externl: boolean);
                                           formal: ()))
                   end;


     disprange = 0..displimit;
     where = (blck,crec,vrec,rec);

                                                            (*expressions*)
                                                            (*************)
     attrkind = (cst,varbl,expr);
     vaccess = (drct,indrct,inxd);

     attr = record typtr: stp;
              case kind: attrkind of
                cst:   (cval: valu);
                varbl: (case access: vaccess of
                          drct: (vlevel: levrange; dplmt: addrrange);
                          indrct: (idplmt: addrrange);
           inxd: ());
      expr: ()
              end;

                                                                 (*labels*)
                                                                 (********)
     lbp = ^ labl;
     labl = record { 'goto' label }
                   nextlab: lbp;      { next list link }
                   defined: boolean;  { label defining point was seen }
                   labval,            { numeric value of label }
                   labname: integer;  { internal sequental name of label }
                   vlevel: levrange;  { procedure level of definition }
                   slevel:  integer;  { statement level of definition }
                   ipcref:  boolean;  { was referenced by another proc/func }
                   minlvl:  integer;  { minimum goto reference statement lvl }
                   bact:    boolean;  { containing block is active }
            end;

     { external file tracking entries }
     extfilep = ^filerec;
     filerec = record filename:idstr; nextfile:extfilep end;

     { case statement tracking entries }
     cip = ^caseinfo;
     caseinfo = record next: cip;
                  csstart: integer;
                  cslab: integer
                end;

(*-------------------------------------------------------------------------*)

global variables

self-compiler thing ??

var
    { !!! remove this statement for self compile }
    {elide}prr: text;{noelide}       { output code file }

scanner state

                                (*returned by source program scanner
                                 insymbol:
                                 **********)

sy: symbol;                     (*last symbol*)
op: operator;                   (*classification of last symbol*)
val: valu;                      (*value of last constant*)
lgth: integer;                  (*length of last string constant*)
id: idstr;                      (*last identifier (possibly truncated)*)
kk: 1..maxids;                  (*nr of chars in last identifier*)
ch: char;                       (*last character*)
eol: boolean;                   (*end of line flag*)

counters

                                (*counters:*)
                                (***********)

chcnt: integer;                 (*character counter*)
lc,ic: addrrange;               (*data location and instruction counter*)
linecount: integer;

switches

                                (*switches:*)
                                (***********)

dp,                             (*declaration part*)
list,prcode,prtables: boolean;  (*output options for
                                    -- source program listing
                                    -- printing symbolic code
                                    -- displaying ident and struct tables
                                    --> procedure option*)
debug: boolean;

pointer-related

                                (*pointers:*)
                                (***********)
parmptr,
intptr,realptr,charptr,
boolptr,nilptr,textptr: stp;    (*pointers to entries of standard ids*)
utypptr,ucstptr,uvarptr,
ufldptr,uprcptr,ufctptr,        (*pointers to entries for undeclared ids*)
fwptr: ctp;                     (*head of chain of forw decl type ids*)
outputptr,inputptr: ctp;        { pointers to default files }
fextfilep: extfilep;            (*head of chain of external files*)

scope-related

                                (*bookkeeping of declaration levels:*)
                                (************************************)

level: levrange;                (*current static level*)
disx,                           (*level of last id searched by searchid*)
top: disprange;                 (*top of display*)

display:                        (*where:   means:*)
  array [disprange] of
    packed record               (*=blck:   id is variable id*)
      fname: ctp; flabel: lbp;  (*=crec:   id is field id in record with*)
      fconst: csp; fstruct: stp;
      case occur: where of      (*   constant address*)
        crec: (clev: levrange;  (*=vrec:   id is field id in record with*)
              cdspl: addrrange);(*   variable address*)
        vrec: (vdspl: addrrange);
        blck: (bname: ctp);     { block id }
        rec: ()
    end;                        (* --> procedure withstatement*)

error messages

                                (*error messages:*)
                                (*****************)

errinx: 0..10;                  (*nr of errors in current source line*)
errlist:
  array [1..10] of
    packed record pos: integer;
                  nmr: 1..500
           end;



                                (*expression compilation:*)
                                (*************************)

gattr: attr;                    (*describes the expr currently compiled*)

‘structured constants’ (lookup tables)

                                (*structured constants:*)
                                (***********************)

constbegsys,simptypebegsys,typebegsys,blockbegsys,selectsys,facbegsys,
statbegsys,typedels: setofsys;
chartp : array[char] of chtp;
rw:  array [1..maxres(*nr. of res. words*)] of restr;
frw: array [1..10] of 1..36(*nr. of res. words + 1*);
rsy: array [1..maxres(*nr. of res. words*)] of symbol;
ssy: array [char] of symbol;
rop: array [1..maxres(*nr. of res. words*)] of operator;
sop: array [char] of operator;
na:  array [1..maxstd] of restr;
mn:  array [0..maxins] of packed array [1..4] of char;
sna: array [1..maxsp] of packed array [1..4] of char;
cdx: array [0..maxins] of -4..+4;
pdx: array [1..maxsp] of -7..+7;
ordint: array [char] of integer;

intlabel,mxint10: integer;
inputhdf: boolean; { 'input' appears in header files }
outputhdf: boolean; { 'output' appears in header files }
errtbl: array [1..500] of boolean; { error occrence tracking }
toterr: integer; { total errors in program }

{ Recycling tracking counters, used to check for new/dispose mismatches. }
strcnt: integer; { strings }
cspcnt: integer; { constants }
stpcnt: integer; { structures }
ctpcnt: integer; { identifiers }
lbpcnt: integer; { label counts }
filcnt: integer; { file tracking counts }
cipcnt: integer; { case entry tracking counts }

f: boolean; { flag for if error number list entries were printed }
i: 1..500; { index for error number tracking array }

procedures and functions

recycling, for…

(*-------------------------------------------------------------------------*)

                           { recycling controls }


(*-------------------------------------------------------------------------*)

…”string quanta”

{ get string quanta }
procedure getstr(var p: strvsp);
begin
   new(p); { get new entry }
   strcnt := strcnt+1 { count }
end;

{ recycle string quanta list }
procedure putstrs(p: strvsp);
var p1: strvsp;
begin
  while p <> nil do begin
    p1 := p; p := p^.next; dispose(p1); strcnt := strcnt-1
  end
end;

… labels…

{ get label entry }
procedure getlab(var p: lbp);
begin
   new(p); { get new entry }
   lbpcnt := lbpcnt+1 { add to count }
end;

{ recycle label entry }
procedure putlab(p: lbp);
begin
   dispose(p); { release entry }
   lbpcnt := lbpcnt-1 { remove from count }
end;

… constants…

{ push constant entry to list }
procedure pshcst(p: csp);
begin
   { push to constant list }
   p^.next := display[top].fconst;
   display[top].fconst := p;
   cspcnt := cspcnt+1 { count entries }
end;

{ recycle constant entry }
procedure putcst(p: csp);
begin
   { recycle string if present }
   if p^.cclass = strg then putstrs(p^.sval)
   else if p^.cclass = reel then putstrs(p^.rval);
   dispose(p); { release entry }
   cspcnt := cspcnt-1 { remove from count }
end;

… structures…

{ push structure entry to list }
procedure pshstc(p: stp);
begin
   { push to structures list }
   p^.next := display[top].fstruct;
   display[top].fstruct := p;
   stpcnt := stpcnt+1 { count entries }
end;

{ recycle structure entry }
procedure putstc(p: stp);
begin
   dispose(p); { release entry }
   stpcnt := stpcnt-1
end;

… identifiers…

{ initialize and register identifier entry }
procedure ininam(p: ctp);
begin
   ctpcnt := ctpcnt+1; { count entry }
   p^.keep := false { clear keepme flag }
end;

{ recycle identifier entry }
procedure putnam(p: ctp);
var p1: ctp;
begin
   if (p^.klass = proc) or (p^.klass = func) then
      while p^.pflist <> nil do begin
      { scavenge the parameter list }
      p1 := p^.pflist; p^.pflist := p1^.next;
      putnam(p1) { release }
   end;
   putstrs(p^.name); { release name string }    
   dispose(p); { release entry }
   ctpcnt := ctpcnt-1 { remove from count }
end;

{ recycle identifier tree }
procedure putnams(p: ctp);
begin
  if p <> nil then begin
    putnams(p^.llink); { release left }
    putnams(p^.rlink); { release right }
    { "keep" means it is a parameter and stays with it's procedure or
      function entry. }
    if not p^.keep then putnam(p) { release the id entry }
  end
end;

… display levels…

{ scrub display level }
procedure putdsp(l: disprange);
   var llp: lbp; lvp: csp; lsp: stp;
   { release substructure }
   procedure putsub(p: stp);
      var p1: stp;
   begin
      { clear record recycle list if record }
      if p^.form = records then begin
         { clear structure list }
         while p^.recyc <> nil do begin
            { remove top of list }
            p1 := p^.recyc; p^.recyc := p1^.next;
            putsub(p1) { release that element }
         end;
         putnams(p^.fstfld) { clear id list }
      end else if p^.form = tagfld then begin
            if p^.tagfieldp <> nil then
               { recycle anonymous tag fields }
               if p^.tagfieldp^.name = nil then putnam(p^.tagfieldp)
      end;
      putstc(p) { release head entry }
   end;
begin { putdsp }
  putnams(display[l].fname); { dispose of identifier tree }
  { dispose of label list }
  while display[l].flabel <> nil do begin
    llp := display[l].flabel; display[l].flabel := llp^.nextlab; putlab(llp)
  end;
  { dispose of constant list }
  while display[l].fconst <> nil do begin
    lvp := display[l].fconst; display[l].fconst := lvp^.next; putcst(lvp)
  end;
  { dispose of structure list }
  while display[l].fstruct <> nil do begin
    { remove top from list }
    lsp := display[l].fstruct; display[l].fstruct := lsp^.next; putsub(lsp)
  end
end; { putdsp }

{ scrub all display levels until given }
procedure putdsps(l: disprange);
var t: disprange;
begin
  if l > top then begin
    writeln('*** Error: Compiler internal error');
    goto 99
  end;
  t := top;
  while t > l do begin
    putdsp(t); t := t-1
  end
end;

… file entries …

{ get external file entry }
procedure getfil(var p: extfilep);
begin
   new(p); { get new entry }
   filcnt := filcnt+1 { count entry }
end;

{ recycle external file entry }
procedure putfil(p: extfilep);
begin
   dispose(p); { release entry }
   filcnt := filcnt-1 { count entry }
end;

… case tracking …

{ get case tracking entry }
procedure getcas(var p: cip);
begin
   new(p); { get new entry }
   cipcnt := cipcnt+1 { count entry }
end;

{ recycle case tracking entry }
procedure putcas(p: cip);
begin
   dispose(p); { release entry }
   cipcnt := cipcnt-1 { count entry }
end;

character and string quanta functions

(*-------------------------------------------------------------------------*)

                { character and string quanta functions }

(*-------------------------------------------------------------------------*)

lower case

{ find lower case of character }
function lcase(c: char): char;
begin
  if c in ['A'..'Z'] then c := chr(ord(c)-ord('A')+ord('a'));
  lcase := c
end { lcase };

{ convert string to lower case }
procedure lcases(var s: idstr);
var i: integer;
begin
  for i := 1 to maxids do s[i] := lcase(s[i]);
end;

compare string to reserved word

{ find reserved word string equal to id string }
function strequri(a: restr; var b: idstr): boolean;
var m: boolean; i: integer;
begin
  m := true;
  for i := 1 to reslen do if lcase(a[i]) <> lcase(b[i]) then m := false;
  for i := reslen+1 to maxids do if b[i] <> ' ' then m := false;
  strequri := m
end { equstr };

identifiers

{ write variable length id string to output }
procedure writev(var f: text; s: strvsp; fl: integer);
var i: integer; c: char;
begin i := 1;
  while fl > 0 do begin
    c := ' '; if s <> nil then begin c := s^.str[i]; i := i+1 end;
    write(f, c); fl := fl-1;
    if i > varsqt then begin s := s^.next; i := 1 end
  end
end;

{ find padded length of variable length id string }
function lenpv(s: strvsp): integer;
var i, l, lc: integer;
begin l := 1; lc := 0;
  while s <> nil do begin
    for i := 1 to varsqt do begin
      if s^.str[i] <> ' ' then lc := l;
      l := l+1; { count characters }
    end;
    s := s^.next
  end;
  lenpv := lc
end;

string assignments

{ assign identifier fixed to variable length string, including allocation }
procedure strassvf(var a: strvsp; var b: idstr);
var i, j, l: integer; p, lp: strvsp;
begin l := maxids; p := nil; a := nil; j := 1;
  while (l > 1) and (b[l] = ' ') do l := l-1; { find length of fixed string }
  if b[l] = ' ' then l := 0;
  for i := 1 to l do begin
    if j > varsqt then p := nil;
    if p = nil then begin 
      getstr(p); p^.next := nil; j := 1; 
      if a = nil then a := p else lp^.next := p; lp := p
    end;
    p^.str[j] := b[i]; j := j+1
  end;
  if p <> nil then for j := j to varsqt do p^.str[j] := ' '
end;

{ assign reserved word fixed to variable length string, including allocation }
procedure strassvr(var a: strvsp; b: restr);
var i, j, l: integer; p, lp: strvsp;
begin l := reslen; p := nil; a := nil; lp := nil; j := 1;
  while (l > 1) and (b[l] = ' ') do l := l-1; { find length of fixed string }
  if b[l] = ' ' then l := 0;
  for i := 1 to l do begin
    if j > varsqt then p := nil;
    if p = nil then begin 
      getstr(p); p^.next := nil; j := 1; 
      if a = nil then a := p else lp^.next := p; lp := p
    end;
    p^.str[j] := b[i]; j := j+1
  end;
  if p <> nil then for j := j to varsqt do p^.str[j] := ' '
end;

{ assign number string fixed to variable length string, including allocation }
procedure strassvd(var a: strvsp; b: nmstr);
var i, j, l: integer; p, lp: strvsp;
begin l := digmax; p := nil; a := nil; lp := nil; j := 1;
  while (l > 1) and (b[l] = ' ') do l := l-1; { find length of fixed string }
  if b[l] = ' ' then l := 0;
  for i := 1 to l do begin
    if j > varsqt then p := nil;
    if p = nil then begin 
      getstr(p); p^.next := nil; j := 1; 
      if a = nil then a := p else lp^.next := p; lp := p
    end;
    p^.str[j] := b[i]; j := j+1
  end;
  if p <> nil then for j := j to varsqt do p^.str[j] := ' '
end;

{ assign constant string fixed to variable length string, including allocation }
procedure strassvc(var a: strvsp; b: csstr; l: integer);
var i, j: integer; p, lp: strvsp;
begin p := nil; a := nil; lp := nil; j := 1;
  for i := 1 to l do begin
    if j > varsqt then p := nil;
    if p = nil then begin 
      getstr(p); p^.next := nil; j := 1; 
      if a = nil then a := p else lp^.next := p; lp := p
    end;
    p^.str[j] := b[i]; j := j+1
  end;
  if p <> nil then for j := j to varsqt do p^.str[j] := ' '
end;

{ assign variable length string to fixed identifier }
procedure strassfv(var a: idstr; b: strvsp);
var i, j: integer;
begin for i := 1 to maxids do a[i] := ' '; i := 1;
   while b <> nil do begin
      for j := 1 to varsqt do begin a[i] := b^.str[j]; i := i+1 end; 
      b := b^.next
   end
end;

string comparisons

{ compare variable length id strings }
function strequvv(a, b: strvsp): boolean;
var m: boolean; i: integer;
begin
  m := true;
  while (a <> nil) and (b <> nil) do begin
    for i := 1 to varsqt do if lcase(a^.str[i]) <> lcase(b^.str[i]) then m := false;
    a := a^.next; b := b^.next
  end;
  if a <> b then m := false;
  strequvv := m
end;

{ compare variable length id strings, a < b }
function strltnvv(a, b: strvsp): boolean;
var i: integer; ca, cb: char;
begin ca := ' '; cb := ' ';
  while (a <> nil) or (b <> nil) do begin
    i := 1;
    while (i <= varsqt) and ((a <> nil) or (b <> nil)) do begin
      if a <> nil then ca := lcase(a^.str[i]) else ca := ' ';
      if b <> nil then cb := lcase(b^.str[i]) else cb := ' ';
      if ca <> cb then begin a := nil; b := nil end;
      i := i+1
    end;
    if a <> nil then a := a^.next; if b <> nil then b := b^.next
  end;
  strltnvv := ca < cb
end;

{ compare variable length id string to fixed }
function strequvf(a: strvsp; var b: idstr): boolean;
var m: boolean; i, j: integer; c: char;
begin
  m := true; j := 1;
  for i := 1 to maxids do begin
    c := ' '; if a <> nil then begin c := a^.str[j]; j := j+1 end;
    if lcase(c) <> lcase(b[i]) then m := false;
    if j > varsqt then begin a := a^.next; j := 1 end
  end;
  strequvf := m
end;

{ compare variable length id string to fixed, a < b }
function strltnvf(a: strvsp; var b: idstr): boolean;
var m: boolean; i, j, f: integer; c: char;
begin
  m := true; i := 1; j := 1;
  while i < maxids do begin
    c := ' '; if a <> nil then begin c := a^.str[j]; j := j+1 end;
    if lcase(c) <> lcase(b[i]) then begin f := i; i := maxids end else i := i+1;
    if j > varsqt then begin a := a^.next; j := 1 end
  end;
  strltnvf := lcase(c) < lcase(b[f])
end;

character accessors

{ get character from variable length string }

function strchr(a: strvsp; x: integer): char;
var c: char; i: integer; q: integer;
begin
   c := ' '; i := 1; q := 1;
   while i < x do begin 
      if q >= varsqt then begin q := 1; if a <> nil then a := a^.next end
      else q := q+1;
      i := i+1
   end;
   if a <> nil then c := a^.str[q];
   strchr := c
 end;

{ put character to variable length string }

procedure strchrass(var a: strvsp; x: integer; c: char);
var i: integer; q: integer; p, l: strvsp;
procedure getsqt;
var y: integer;
begin
   if p = nil then begin getstr(p); for y := 1 to varsqt do p^.str[y] := ' ';
      p^.next := nil; if a = nil then a := p else l^.next := p
   end
end;
begin
   i := 1; q := 1; p := a; l := nil;
   getsqt;
   while i < x do begin 
      if q >= varsqt then begin q := 1; l := p; p := p^.next; getsqt end
      else q := q+1;    
      i := i+1
   end;
   p^.str[q] := c
 end;

dump the display

{ dump the display }
procedure prtdsp;
var i: integer;
procedure prtlnk(p: ctp; f: integer);
var i: integer;
begin
  if p <> nil then begin
    for i := 1 to f do write(' ');
    writev(output, p^.name, 10); writeln;
    if p^.llink <> nil then prtlnk(p^.llink, f+3);
    if p^.rlink <> nil then prtlnk(p^.rlink, f+3)
  end
end;
begin
  writeln;
  writeln('Display:');
  writeln;
  for i := 0 to displimit do if display[i].fname <> nil then begin

     writeln('level ', i:1);
     writeln;
     prtlnk(display[i].fname, 0);
     writeln
 
  end;
  writeln;
end;

end of line

procedure endofline;
  var lastpos,freepos,currpos,currnmr,f,k: integer;
begin
  if errinx > 0 then   (*output error messages*)
    begin write(output,linecount:6,' ****  ':9);
      lastpos := -1; freepos := 1;
      for k := 1 to errinx do
        begin
          with errlist[k] do
            begin currpos := pos; currnmr := nmr end;
          if currpos = lastpos then write(output,',')
          else
            begin
              while freepos < currpos do
                begin write(output,' '); freepos := freepos + 1 end;
              write(output,'^');
              lastpos := currpos
            end;
          if currnmr < 10 then f := 1
          else if currnmr < 100 then f := 2
            else f := 3;
          write(output,currnmr:f);
          freepos := freepos + f + 1
        end;
      writeln(output); errinx := 0
    end;
  linecount := linecount + 1;
  if list and (not eof(input)) then
    begin write(output,linecount:6,'  ':2);
      if dp then write(output,lc:7) else write(output,ic:7);
      write(output,' ')
    end;
  { output line marker in intermediate file }
  if not eof(input) then begin
    writeln(prr, ':', linecount:1);
  end;
  chcnt := 0
end  (*endofline*) ;

error messages

procedure errmsg(ferrnr: integer);
begin case ferrnr of
  1:   write('Error in simple type');
  2:   write('Identifier expected');
  3:   write('''program'' expected');
  4:   write(''')'' expected');
  5:   write(''':'' expected');
  6:   write('Illegal symbol');
  7:   write('Error in parameter list');
  8:   write('''of'' expected');
  9:   write('''('' expected');
  10:  write('Error in type');
  11:  write('''['' expected');
  12:  write(''']'' expected');
  13:  write('''end'' expected');
  14:  write(''';'' expected');
  15:  write('Integer expected');
  16:  write('''='' expected');
  17:  write('''begin'' expected');
  18:  write('Error in declaration part');
  19:  write('Error in field-list');
  20:  write(''','' expected');
  21:  write('''.'' expected');

  50:  write('Error in constant');
  51:  write(''':='' expected');
  52:  write('''then'' expected');
  53:  write('''until'' expected');
  54:  write('''do'' expected');
  55:  write('''to''/''downto'' expected');
  56:  write('''if'' expected');
  57:  write('''file'' expected');
  58:  write('Error in factor');
  59:  write('Error in variable');

  101: write('Identifier declared twice');
  102: write('Low bound exceeds highbound');
  103: write('Identifier is not of appropriate class');
  104: write('Identifier not declared');
  105: write('Sign not allowed');
  106: write('Number expected');
  107: write('Incompatible subrange types');
  109: write('Type must not be real');
  110: write('Tagfield type must be scalar or subrange');
  111: write('Incompatible with tagfield type');
  112: write('Index type must not be real');
  113: write('Index type must be scalar or subrange');
  114: write('Base type must not be real');
  115: write('Base type must be scalar or subrange');
  116: write('Error in type of standard procedure parameter');
  117: write('Unsatisfied forward reference');
  118: write('Forward reference type identifier in variable declaration');
  119: write('Forward declared; repetition of parameter list not allowed');
  120: write('Function result type must be scalar, subrange or point');
  121: write('File value parameter not allowed');
  122: write('Forward declared function; repetition of result type not allowed');
  123: write('Missing result type in function declaration');
  124: write('F-format for real only');
  125: write('Error in type of standard function parameter');
  126: write('Number of parameters does not agree with declaration');
  127: write('Illegal parameter substitution');
  128: write('Result type of parameter function does not agree with declaration');
  129: write('Type conflict of operands');
  130: write('Expression is not of set type');
  131: write('Tests on equality allowed only');
  132: write('Strict inclusion not allowed');
  133: write('File comparison not allowed');
  134: write('Illegal type of operand(s)');
  135: write('Type of operand must be Boolean');
  136: write('Set element type must be scalar nr subrange');
  137: write('Set element types not compatible');
  138: write('Type of variable is not array');
  139: write('Index type is not compatible with declaration');
  140: write('Type of variable is not record');
  141: write('Type of variable must be file or pointer');
  142: write('Illegal parameter substitution');
  143: write('Illegal type of loop control variable');
  144: write('Illegal type of expression');
  145: write('Type conflict');
  146: write('Assignment of files not allowed');
  147: write('Label type incompatible with selecting expression');
  148: write('Subrange bounds must be scalar');
  149: write('Index type must not be integer');
  150: write('Assignment to standard function is not allowed');
  151: write('Assignment to formal function is not allowed');
  152: write('No such field in this record');
  153: write('Type error in read');
  154: write('Actual parameter must be a variable');
  155: write('Control variable must ~ot be declared on intermediate');
  156: write('Multidefined case label');
  157: write('Too many cases in case statement');
  158: write('Missing corresponding variant declaration');
  159: write('Real or string tagfields not allowed');
  160: write('Previous declaration was not forward');
  161: write('Again forward declared');
  162: write('Parameter size must be constant');
  163: write('Missing variant in declaration');
  164: write('Substitution of standard proc/func not allowed');
  165: write('Multidefined label');
  166: write('Multideclared label');
  167: write('Undeclared label');
  168: write('Undefined label');
  169: write('Error in base set');
  170: write('Value parameter expected');
  171: write('Standard file was redeclared');
  172: write('Undeclared external file');
  173: write('Fortran procedure or function expected');
  174: write('Pascal procedure or function expected');
  175: write('Missing file "input" in program heading');
  176: write('Missing file "output" in program heading');
  177: write('Assiqnment to function identifier not allowed here');
  178: write('Multidefined record variant');
  179: write('X-opt of actual proc/funcdoes not match formal declaration');
  180: write('Control variable must not be formal');
  181: write('Constant part of address out of ranqe');
  182: write('identifier too long');
  183: write('For index variable must be local to this block');
  184: write('Interprocedure goto does not reference outter block of destination');
  185: write('Goto references deeper nested statement');
  186: write('Label referenced by goto at lesser statement level');
  187: write('Goto references label in different nested statement');
  188: write('Label referenced by goto in different nested statement');
  189: write('Parameter lists of formal and actual parameters not congruous');
  190: write('File component may not contain other files');
  191: write('Cannot assign from file or component containing files');
  192: write('Assignment to function that is not active');

  201: write('Error in real constant: digit expected');
  202: write('String constant must not exceed source line');
  203: write('Integer constant exceeds range');
  204: write('8 or 9 in octal number');
  205: write('Zero strinq not allowed');
  206: write('Integer part of real constant exceeds ranqe');

  250: write('Too many nestedscopes of identifiers');
  251: write('Too many nested procedures and/or functions');
  252: write('Too many forward references of procedure entries');
  253: write('Procedure too long');
  254: write('Too many long constants in this procedure');
  255: write('Too many errors on this source line');
  256: write('Too many external references');
  257: write('Too many externals');
  258: write('Too many local files');
  259: write('Expression too complicated');
  260: write('Too many exit labels');

  300: write('Division by zero');
  301: write('No case provided for this value');
  302: write('Index expression out of bounds');
  303: write('Value to be assigned is out of bounds');
  304: write('Element expression out of range');

  398: write('Implementation restriction');
  399: write('Feature not implemented');

  400,
  500: write('Compiler internal error');
  end    
end;

error display

procedure error(ferrnr: integer);
begin

  { This diagnostic is here because error buffers error numbers til the end
    of line, and sometimes you need to know exactly where they occurred. }

  {

  writeln('error: ', ferrnr:1); 

  }

  errtbl[ferrnr] := true; { track this error }
  if errinx >= 9 then
    begin errlist[10].nmr := 255; errinx := 10 end
  else
    begin errinx := errinx + 1;
      errlist[errinx].nmr := ferrnr
    end;
  errlist[errinx].pos := chcnt;
  toterr := toterr+1
end (*error*) ;

lexer (procedure insymbol)

declarations

procedure insymbol;
  (*read next basic symbol of source program and return its
  description in the global variables sy, op, id, val and lgth*)
  label 1;
  var i,k,j: integer;
      digit: nmstr; { temp holding for digit string }
      rvalb: nmstr; { temp holding for real string }
      string: csstr;
      lvp: csp; test, ferr: boolean;
      iscmte: boolean;

nextch

procedure nextch;
begin if eol then
  begin if list then writeln(output); endofline
  end;
  if not eof(input) then
   begin eol := eoln(input); read(input,ch);
    if list then write(output,ch);
    chcnt := chcnt + 1
   end
  else
    begin writeln(output,'   *** eof ','encountered');
      test := false
    end
end;

options

procedure options;
begin
  repeat nextch;
    if ch <> '*' then
      begin
        if lcase(ch) = 't' then
          begin nextch; prtables := ch = '+' end
        else
          if lcase(ch) = 'l' then
            begin nextch; list := ch = '+';
              if not list then writeln(output)
            end
          else
         if lcase(ch) = 'd' then
           begin nextch; debug := ch = '+' end
         else
            if lcase(ch) = 'c' then
              begin nextch; prcode := ch = '+' end;
        nextch
      end
  until ch <> ','
end (*options*) ;

(break this down) main code for insymbol

begin (*insymbol*)
1:
  { Skip both spaces and controls. This allows arbitrary formatting characters
    in the source. }
  repeat while (ch <= ' ') and not eol do nextch;
    test := eol;
    if test then nextch
  until not test;
  if chartp[ch] = illegal then
    begin sy := othersy; op := noop;
      error(399); nextch
    end
  else
  case chartp[ch] of
    letter:
      begin k := 0; ferr := true;
        repeat
          if k < maxids then
           begin k := k + 1; id[k] := ch end
          else if ferr then begin error(182); ferr := false end;
          nextch
        until chartp[ch] in [special,illegal,chstrquo,chcolon,
                              chperiod,chlt,chgt,chlparen,chspace,chlcmt];
        if k >= kk then kk := k
        else
          repeat id[kk] := ' '; kk := kk - 1
          until kk = k;
        sy := ident; op := noop;
        if k <= reslen then
          for i := frw[k] to frw[k+1] - 1 do
            if strequri(rw[i], id) then
              begin sy := rsy[i]; op := rop[i] end;
    end;
    number:
      begin op := noop; i := 0;
        repeat i := i+1; if i<= digmax then digit[i] := ch; nextch
        until chartp[ch] <> number;
        if ((ch = '.') and (input^ <> '.') and (input^ <> ')')) or 
           (lcase(ch) = 'e') then
          begin
            k := i;
            if ch = '.' then
              begin k := k+1; if k <= digmax then digit[k] := ch;
                nextch; (*if ch = '.' then begin ch := ':'; goto 3 end;*)
                if chartp[ch] <> number then error(201)
                else
                  repeat k := k + 1;
                    if k <= digmax then digit[k] := ch; nextch
                  until chartp[ch] <>  number
              end;
            if lcase(ch) = 'e' then
              begin k := k+1; if k <= digmax then digit[k] := ch;
                nextch;
                if (ch = '+') or (ch ='-') then
                  begin k := k+1; if k <= digmax then digit[k] := ch;
                    nextch
                  end;
                if chartp[ch] <> number then error(201)
                else
                  repeat k := k+1;
                    if k <= digmax then digit[k] := ch; nextch
                  until chartp[ch] <> number
               end;
             new(lvp,reel); pshcst(lvp); sy:= realconst; 
             lvp^.cclass := reel;
             with lvp^ do
               begin for i := 1 to digmax do rvalb[i] := ' ';
                 if k <= digmax then
                   for i := 2 to k + 1 do rvalb[i] := digit[i-1]
                 else begin error(203); rvalb[2] := '0';
                        rvalb[3] := '.'; rvalb[4] := '0'
                      end;
                 { place buffered real string in constant }
                 strassvd(rval, rvalb)
               end;
             val.valp := lvp
          end
        else
          begin
            if i > digmax then begin error(203); val.ival := 0 end
            else
              with val do
                begin ival := 0;
                  for k := 1 to i do
                    begin
                      if ival <= mxint10 then
                        ival := ival*10+ordint[digit[k]]
                      else begin error(203); ival := 0 end
                    end;
                  sy := intconst
                end
          end
      end;
    chstrquo:
      begin lgth := 0; sy := stringconst;  op := noop;
        for i := 1 to strglgth do string[i] := ' ';
        repeat
          repeat nextch; lgth := lgth + 1;
                 if lgth <= strglgth then string[lgth] := ch
          until (eol) or (ch = '''');
          if eol then error(202) else nextch
        until ch <> '''';
        string[lgth] := ' '; { get rid of trailing quote }
        lgth := lgth - 1;   (*now lgth = nr of chars in string*)
        if lgth = 0 then error(205) else
        if lgth = 1 then val.ival := ord(string[1])
        else
          begin new(lvp,strg); pshcst(lvp);
            lvp^.cclass:=strg;
            if lgth > strglgth then
              begin error(399); lgth := strglgth end;
            with lvp^ do
              begin slgth := lgth; strassvc(sval, string, strglgth) end;
            val.valp := lvp
          end
      end;
    chcolon:
      begin op := noop; nextch;
        if ch = '=' then
          begin sy := becomes; nextch end
        else sy := colon
      end;
    chperiod:
      begin op := noop; nextch;
        if ch = '.' then begin sy := range; nextch end
        else if ch = ')' then begin sy := rbrack; nextch end
        else sy := period
      end;
    chlt:
      begin nextch; sy := relop;
        if ch = '=' then
          begin op := leop; nextch end
        else
          if ch = '>' then
            begin op := neop; nextch end
          else op := ltop
      end;
    chgt:
      begin nextch; sy := relop;
        if ch = '=' then
          begin op := geop; nextch end
        else op := gtop
      end;
    chlparen:
     begin nextch;
       if ch = '*' then
         begin nextch;
           if ch = '$' then options;
           repeat
             while (ch <> '}') and (ch <> '*') and not eof(input) do nextch;
             iscmte := ch = '}'; nextch
           until iscmte or (ch = ')') or eof(input);
           if not iscmte then nextch; goto 1
         end
       else if ch = '.' then begin sy := lbrack; nextch end
       else sy := lparent; 
       op := noop
     end;
    chlcmt:
     begin nextch;
       if ch = '$' then options;
       repeat
          while (ch <> '}') and (ch <> '*') and not eof(input) do nextch;
          iscmte := ch = '}'; nextch
       until iscmte or (ch = ')') or eof(input);
       if not iscmte then nextch; goto 1
     end;
    special:
      begin sy := ssy[ch]; op := sop[ch];
        nextch
      end;
    chspace: sy := othersy
  end; (*case*)

lexical dump

  if dodmplex then begin {  lexical dump }

    writeln;
    write('symbol: ');
    case sy of
       ident:       write('ident: ', id:10);
       intconst:    write('int const: ', val.ival:1);
       realconst:   begin write('real const: '); 
                          writev(output, val.valp^.rval, 9) end;
       stringconst: begin write('string const: '''); 
                          writev(output, val.valp^.sval, val.valp^.slgth) end;
       notsy: write('not'); mulop: write('*'); addop: write('+'); 
       relop: write('<'); lparent: write('('); rparent: write(')');
       lbrack: write('['); rbrack: write(']'); comma: write(',');
       semicolon: write(';'); period: write('.'); arrow: write('^');
       colon: write(':'); becomes: write(':='); range: write('..'); 
       labelsy: write('label'); constsy: write('const'); typesy: write('type');
       varsy: write('var'); funcsy: write('function'); progsy: write('program');
       procsy: write('procedure'); setsy: write('set'); 
       packedsy: write('packed'); arraysy: write('array');
       recordsy: write('record'); filesy: write('file'); 
       beginsy: write('begin'); ifsy: write('if'); casesy: write('case'); 
       repeatsy: write('repeat'); whilesy: write('while'); 
       forsy: write('for'); withsy: write('with'); gotosy: write('goto'); 
       endsy: write('end'); elsesy: write('else'); untilsy: write('until');
       ofsy: write('of'); dosy: write('do'); tosy: write('to'); 
       downtosy: write('downto'); thensy: write('then');
       othersy: write('<other>');
    end;
    writeln

  end

end (*insymbol*) ;

enterid

procedure enterid(fcp: ctp);
  (*enter id pointed at by fcp into the name-table,
   which on each declaration level is organised as
   an unbalanced binary tree*)
  var lcp, lcp1: ctp; lleft: boolean;
begin
  lcp := display[top].fname;
  if lcp = nil then
    display[top].fname := fcp
  else
    begin
      repeat lcp1 := lcp;
        if strequvv(lcp^.name, fcp^.name) then (*name conflict, follow right link*)
          begin error(101); lcp := lcp^.rlink; lleft := false end
        else
          if strltnvv(lcp^.name, fcp^.name) then
            begin lcp := lcp^.rlink; lleft := false end
          else begin lcp := lcp^.llink; lleft := true end
      until lcp = nil;
      if lleft then lcp1^.llink := fcp else lcp1^.rlink := fcp
    end;
  fcp^.llink := nil; fcp^.rlink := nil
end (*enterid*) ;

field and forward lookups

  procedure searchsection(fcp: ctp; var fcp1: ctp);
    (*to find record fields and forward declared procedure id's
     --> procedure proceduredeclaration
     --> procedure selector*)
     label 1;
  begin
    while fcp <> nil do
      if strequvf(fcp^.name, id) then goto 1
      else if strltnvf(fcp^.name, id) then fcp := fcp^.rlink
        else fcp := fcp^.llink;
1:  fcp1 := fcp
  end (*searchsection*) ;

  procedure searchidnenm(fidcls: setofids; var fcp: ctp; var mm: boolean);
    label 1;
    var lcp: ctp;
        disxl: disprange;
  begin
    mm := false;
    for disxl := top downto 0 do
      begin lcp := display[disxl].fname;
        while lcp <> nil do begin
          if strequvf(lcp^.name, id) then
            if lcp^.klass in fidcls then begin disx := disxl; goto 1 end
            else
              begin mm := true;
                lcp := lcp^.rlink
              end
          else
            if strltnvf(lcp^.name, id) then
              lcp := lcp^.rlink
            else lcp := lcp^.llink
        end
      end;
      disx := 0;
      lcp := nil; { make sure this is not found }
1:  fcp := lcp
  end (*searchidne*) ;

  procedure searchidne(fidcls: setofids; var fcp: ctp);
    var mm: boolean;
  begin
    searchidnenm(fidcls, fcp, mm);
    if mm then error(103)
  end (*searchidne*) ;

searchid

  procedure searchid(fidcls: setofids; var fcp: ctp);
    label 1;
    var lcp: ctp;
  begin
    searchidne(fidcls, lcp); { perform no error search }
    if lcp <> nil then goto 1; { found }
    (*search not successful
     --> procedure simpletype*)
      error(104);
      (*to avoid returning nil, reference an entry
       for an undeclared id of appropriate class
       --> procedure enterundecl*)
      if types in fidcls then lcp := utypptr
      else
        if vars in fidcls then lcp := uvarptr
        else
          if field in fidcls then lcp := ufldptr
          else
            if konst in fidcls then lcp := ucstptr
            else
              if proc in fidcls then lcp := uprcptr
              else lcp := ufctptr;
1:  fcp := lcp
  end (*searchid*) ;

getbounds

procedure getbounds(fsp: stp; var fmin,fmax: integer);
  (*get internal bounds of subrange or scalar type*)
  (*assume fsp<>intptr and fsp<>realptr*)
begin
  fmin := 0; fmax := 0;
  if fsp <> nil then
  with fsp^ do
    if form = subrange then
      begin fmin := min.ival; fmax := max.ival end
    else
        if fsp = charptr then
          begin fmin := ordminchar; fmax := ordmaxchar
          end
        else
          if fconst <> nil then
            fmax := fconst^.values.ival
end (*getbounds*) ;

alignquot

{ alignment for general memory placement }
function alignquot(fsp: stp): integer;
begin
  alignquot := 1;
  if fsp <> nil then
    with fsp^ do
      case form of
        scalar:   if fsp=intptr then alignquot := intal
                  else if fsp=boolptr then alignquot := boolal
                  else if scalkind=declared then alignquot := intal
                  else if fsp=charptr then alignquot := charal
                  else if fsp=realptr then alignquot := realal
                  else (*parmptr*) alignquot := parmal;
        subrange: alignquot := alignquot(rangetype);
        pointer:  alignquot := adral;
        power:    alignquot := setal;
        files:    alignquot := fileal;
        arrays:   alignquot := alignquot(aeltype);
        records:  alignquot := recal;
        variant,tagfld: error(501)
      end
end (*alignquot*);

align

procedure align(fsp: stp; var flc: addrrange);
  var k,l: integer;
begin
  k := alignquot(fsp);
  l := flc-1;
  flc := l + k  -  (k+l) mod k
end (*align*);

printttables

declarations

procedure printtables(fb: boolean);
  (*print data structure and name table*)
  (* Added these functions to convert pointers to integers.
    Works on any machine where pointers and integers are the same format.
    The original code was for a processor where "ord" would do this, a
    very nonstandard feature [sam] *)
  const intsize = 11; (* size of printed integer *)

  var i, lim: disprange;

  function stptoint(p: stp): integer;
  var r: record case boolean of false: (p: stp); true: (i: integer) end;
  begin r.p := p; stptoint := r.i end;

  function ctptoint(p: ctp): integer;
  var r: record case boolean of false: (p: ctp); true: (i: integer) end;
  begin r.p := p; ctptoint := r.i end;

marker

procedure marker;
  (*mark data structure entries to avoid multiple printout*)
  var i: integer;

  procedure markctp(fp: ctp); forward;

  procedure markstp(fp: stp);
    (*mark data structures, prevent cycles*)
  begin
    if fp <> nil then
      with fp^ do
        begin marked := true;
          case form of
          scalar:   ;
          subrange: markstp(rangetype);
          pointer:  (*don't mark eltype: cycle possible; will be marked
                    anyway, if fp = true*) ;
          power:    markstp(elset) ;
          arrays:   begin markstp(aeltype); markstp(inxtype) end;
          records:  begin markctp(fstfld); markstp(recvar) end;
          files:    markstp(filtype);
          tagfld:   markstp(fstvar);
          variant:  begin markstp(nxtvar); markstp(subvar) end
          end (*case*)
        end (*with*)
  end (*markstp*);

  procedure markctp;
  begin
    if fp <> nil then
      with fp^ do
        begin markctp(llink); markctp(rlink);
          markstp(idtype)
        end
  end (*markctp*);

begin (*marker*)
  for i := top downto lim do
    markctp(display[i].fname)
end (*marker*);

followctp

procedure followctp(fp: ctp); forward;

followstp

procedure followstp(fp: stp);
begin
  if fp <> nil then
    with fp^ do
      if marked then
        begin marked := false; write(output,' ':4,stptoint(*ord*)(fp):intsize(*6*),size:10);
          case form of
          scalar:   begin write(output,'scalar':10);
                      if scalkind = standard then
                        write(output,'standard':10)
                      else write(output,'declared':10,' ':4,ctptoint(*ord*)(fconst):intsize(*6*));
                      writeln(output)
                    end;
          subrange: begin
                      write(output,'subrange':10,' ':4,stptoint(*ord*)(rangetype):6);
                      if rangetype <> realptr then
                        write(output,min.ival,max.ival)
                      else
                        if (min.valp <> nil) and (max.valp <> nil) then begin
                          write(' '); writev(output, min.valp^.rval, 9);
                          write(' '); writev(output, max.valp^.rval, 9)
                        end;
                      writeln(output); followstp(rangetype);
                    end;
          pointer:  writeln(output,'pointer':10,' ':4,stptoint(*ord*)(eltype):intsize(*6*));
          power:    begin writeln(output,'set':10,' ':4,stptoint(*ord*)(elset):intsize(*6*));
                      followstp(elset)
                    end;
          arrays:   begin
                      writeln(output,'array':10,' ':4,stptoint(*ord*)(aeltype):intsize(*6*),' ':4,
                        stptoint(*ord*)(inxtype):6);
                      followstp(aeltype); followstp(inxtype)
                    end;
          records:  begin
                      writeln(output,'record':10,' ':4,ctptoint(*ord*)(fstfld):intsize(*6*),' ':4,
                        stptoint(*ord*)(recvar):intsize(*6*)); followctp(fstfld);
                      followstp(recvar)
                    end;
          files:    begin write(output,'file':10,' ':4,stptoint(*ord*)(filtype):intsize(*6*));
                      followstp(filtype)
                    end;
          tagfld:   begin writeln(output,'tagfld':10,' ':4,ctptoint(*ord*)(tagfieldp):intsize(*6*),
                        ' ':4,stptoint(*ord*)(fstvar):intsize(*6*));
                      followstp(fstvar)
                    end;
          variant:  begin writeln(output,'variant':10,' ':4,stptoint(*ord*)(nxtvar):intsize(*6*),
                        ' ':4,stptoint(*ord*)(subvar):intsize(*6*),varval.ival);
                      followstp(nxtvar); followstp(subvar)
                    end
          end (*case*)
        end (*if marked*)
end (*followstp*);

followctp

procedure followctp;
begin
  if fp <> nil then
    with fp^ do
      begin write(output,' ':4,ctptoint(*ord*)(fp):intsize(*6*),' '); 
            writev(output, name, 9); write(' ':4,ctptoint(*ord*)(llink):intsize(*6*),
        ' ':4,ctptoint(*ord*)(rlink):intsize(*6*),' ':4,stptoint(*ord*)(idtype):intsize(*6*));
        case klass of
          types: write(output,'type':10);
          konst: begin write(output,'constant':10,' ':4,ctptoint(*ord*)(next):intsize(*6*));
                   if idtype <> nil then
                     if idtype = realptr then
                       begin
                         if values.valp <> nil then begin
                           write(' '); writev(output, values.valp^.rval, 9)
                         end
                       end
                     else
                       if idtype^.form = arrays then  (*stringconst*)
                         begin
                           if values.valp <> nil then
                             begin write(output,' ');
                               with values.valp^ do
                                 writev(output, sval, slgth)
                             end
                         end
                       else write(output,values.ival)
                 end;
          vars:  begin write(output,'variable':10);
                   if vkind = actual then write(output,'actual':10)
                   else write(output,'formal':10);
                   write(output,' ':4,ctptoint(*ord*)(next):intsize(*6*),vlev,' ':4,vaddr:6 );
                 end;
          field: write(output,'field':10,' ':4,ctptoint(*ord*)(next):intsize(*6*),' ':4,fldaddr:6);
          proc,
          func:  begin
                   if klass = proc then write(output,'procedure':10)
                   else write(output,'function':10);
                   if pfdeckind = standard then
                     write(output,'standard':10, key:10)
                   else
                     begin write(output,'declared':10,' ':4,ctptoint(*ord*)(next):intsize(*6*));
                       write(output,pflev,' ':4,pfname:6);
                       if pfkind = actual then
                         begin write(output,'actual':10);
                           if forwdecl then write(output,'forward':10)
                           else write(output,'notforward':10);
                           if externl then write(output,'extern':10)
                           else write(output,'not extern':10);
                         end
                       else write(output,'formal':10)
                     end
                 end
        end (*case*);
        writeln(output);
        followctp(llink); followctp(rlink);
        followstp(idtype)
      end (*with*)
end (*followctp*);

printtables main code

begin (*printtables*)
  writeln(output); writeln(output); writeln(output);
  if fb then lim := 0
  else begin lim := top; write(output,' local') end;
  writeln(output,' tables '); writeln(output);
  marker;
  for i := top downto lim do
    followctp(display[i].fname);
  writeln(output);
  if not eol then write(output,' ':chcnt+16)
end (*printtables*);

genlabel

procedure genlabel(var nxtlab: integer);
begin intlabel := intlabel + 1;
  nxtlab := intlabel
end (*genlabel*);

searchlabel

procedure searchlabel(var llp: lbp; level: disprange);
var fllp: lbp; { found label entry }
begin
  fllp := nil; { set no label found }
  llp := display[level].flabel; { index top of label list }
  while llp <> nil do begin { traverse }
    if llp^.labval = val.ival then begin { found }
      fllp := llp; { set entry found }
      llp := nil { stop }
    end else llp := llp^.nextlab { next in list }
  end;
  llp := fllp { return found entry or nil }
end;

newlabel

procedure newlabel(var llp: lbp);
var lbname: integer;
begin
  with display[top] do
    begin getlab(llp);
      with llp^ do
        begin labval := val.ival; genlabel(lbname);
          defined := false; nextlab := flabel; labname := lbname;
          vlevel := level; slevel := 0; ipcref := false; minlvl := maxint;
          bact := false;
        end;
      flabel := llp
    end
end;

prtlabels

procedure prtlabels;
var llp: lbp; { found label entry }
begin
  writeln;
  writeln('Labels: ');
  writeln;
  llp := display[level].flabel; { index top of label list }
  while llp <> nil do with llp^ do begin { traverse }
    writeln('label: ', labval:1, ' defined: ', defined, 
            ' internal: ', labname:1, ' vlevel: ', vlevel:1, 
            ' slevel: ', slevel:1, ' ipcref: ', ipcref:1, 
            ' minlvl: ', minlvl:1);
    writeln('   bact: ', bact);
    llp := llp^.nextlab { next in list }
  end
end;

block

declarations

procedure block(fsys: setofsys; fsy: symbol; fprocp: ctp);
  var lsy: symbol;
      stalvl: integer; { statement nesting level }

skip

procedure skip(fsys: setofsys);
  (*skip input string until relevant symbol found*)
begin
  if not eof(input) then
    begin while not(sy in fsys) and (not eof(input)) do insymbol;
      if not (sy in fsys) then insymbol
    end
end (*skip*) ;

constant

procedure constant(fsys: setofsys; var fsp: stp; var fvalu: valu);
  var lsp: stp; lcp: ctp; sign: (none,pos,neg);
      lvp: csp; i: 2..strglgth;
begin lsp := nil; fvalu.ival := 0;
  if not(sy in constbegsys) then
    begin error(50); skip(fsys+constbegsys) end;
  if sy in constbegsys then
    begin
      if sy = stringconst then
        begin
          if lgth = 1 then lsp := charptr
          else
            begin
              new(lsp,arrays); pshstc(lsp);
              with lsp^ do
                begin aeltype := charptr; inxtype := nil;
                   size := lgth*charsize; form := arrays;
                   packing := true
                end
            end;
          fvalu := val; insymbol
        end
      else
        begin
          sign := none;
          if (sy = addop) and (op in [plus,minus]) then
            begin if op = plus then sign := pos else sign := neg;
              insymbol
            end;
          if sy = ident then
            begin searchid([konst],lcp);
              with lcp^ do
                begin lsp := idtype; fvalu := values end;
              if sign <> none then
                if lsp = intptr then
                  begin if sign = neg then fvalu.ival := -fvalu.ival end
                else
                  if lsp = realptr then
                    begin
                      if sign = neg then
                        begin new(lvp,reel); pshcst(lvp);
                          if strchr(fvalu.valp^.rval, 1) = '-' then
                            strchrass(lvp^.rval, 1, '+')
                          else strchrass(lvp^.rval, 1, '-');
                          for i := 2 to digmax do
                            strchrass(lvp^.rval, i, strchr(fvalu.valp^.rval, i));
                          fvalu.valp := lvp;
                        end
                      end
                    else error(105);
              insymbol;
            end
          else
            if sy = intconst then
              begin if sign = neg then val.ival := -val.ival;
                lsp := intptr; fvalu := val; insymbol
              end
            else
              if sy = realconst then
                begin if sign = neg then strchrass(val.valp^.rval, 1, '-');
                  lsp := realptr; fvalu := val; insymbol
                end
              else
                begin error(106); skip(fsys) end
        end;
      if not (sy in fsys) then
        begin error(6); skip(fsys) end
      end;
  fsp := lsp
end (*constant*) ;

string (forward)

function string(fsp: stp) : boolean; forward;

comptypes

function comptypes(fsp1,fsp2: stp) : boolean;
  (*decide whether structures pointed at by fsp1 and fsp2 are compatible*)
begin
  comptypes := false; { set default is false }
  { Check equal. Aliases of the same type will also be equal. }
  if fsp1 = fsp2 then comptypes := true
  else
    if (fsp1 <> nil) and (fsp2 <> nil) then
      if fsp1^.form = fsp2^.form then
        case fsp1^.form of
          scalar: ;
          { Subranges are compatible if either type is a subrange of the 
            other, or if the base type is the same. }
          subrange: comptypes := (fsp1^.rangetype = fsp2) or 
                                 (fsp2^.rangetype = fsp1) or
                                 (fsp1^.rangetype = fsp2^.rangetype);
          { Sets are compatible if they have the same base types and packed/
            unpacked status, or one of them is the empty set. The empty set
            is indicated by a nil base type, which is identical to a base
            type in error. Either way, we treat them as compatible.
            
            Set types created for set constants have a flag that disables
            packing matches. This is because set constants can be packed or
            unpacked by context. }
          power: comptypes := (comptypes(fsp1^.elset, fsp2^.elset) and 
                                ((fsp1^.packing = fsp2^.packing) or 
                                 not fsp1^.matchpack or 
                                 not fsp2^.matchpack)) or
                              (fsp1^.elset = nil) or (fsp2^.elset = nil);
          { Arrays are compatible if they are string types and equal in size }
          arrays: comptypes := string(fsp1) and string(fsp2) and 
                               (fsp1^.size = fsp2^.size );
          { Pointers, must either be the same type or aliases of the same
            type, or one must be nil. The nil pointer is indicated by a nil
            base type, which is identical to a base type in error. Either 
            way, we treat them as compatible. }
          pointer: comptypes := (fsp1^.eltype = nil) or (fsp2^.eltype = nil);
          { records and files must either be the same type or aliases of the
            same type }
          records: ;
          files:
        end (*case*)
      else (*fsp1^.form <> fsp2^.form*)
        { subranges of a base type match the base type }
        if fsp1^.form = subrange then
          comptypes := fsp1^.rangetype = fsp2
        else
          if fsp2^.form = subrange then
            comptypes := fsp1 = fsp2^.rangetype
          else comptypes := false
    else comptypes := true { one of the types is in error }
end (*comptypes*) ;

filecomponent

{ check structure is, or contains, a file }
function filecomponent(fsp: stp): boolean;
var f: boolean;
  { tour identifier tree }
  function filecomponentre(lcp: ctp): boolean;
  var f: boolean;
  begin
    f := false; { set not file by default }
    if lcp <> nil then with lcp^ do begin
      if filecomponent(idtype) then f := true;
      if filecomponentre(llink) then f := true;
      if filecomponentre(rlink) then f := true
    end;
    filecomponentre := f
  end;
begin
  f := false; { set not a file by default }
  if fsp <> nil then with fsp^ do case form of
    scalar:   ;
    subrange: ;
    pointer:  ;
    power:    ;
    arrays:   if filecomponent(aeltype) then f := true; 
    records:  if filecomponentre(fstfld) then f := true;
    files:    f := true;
    tagfld:   ;
    variant:  ;
  end;
  filecomponent := f
end;

string

function string;
var fmin, fmax: integer;
begin string := false;
  if fsp <> nil then
    if (fsp^.form = arrays) and fsp^.packing then begin
      { if the index is nil, either the array is a string constant or the
        index type was in error. Either way, we call it a string }
      if fsp^.inxtype = nil then fmin := 1
      else getbounds(fsp^.inxtype,fmin,fmax);
      if comptypes(fsp^.aeltype,charptr) and (fmin = 1) then string := true
    end
end (*string*) ;

resolvep

{ resolve all pointer references in the forward list }
procedure resolvep;
var ids: idstr; lcp1, lcp2: ctp; mm, fe: boolean;
begin
  ids := id;
  fe := true;
  while fwptr <> nil do begin
    lcp1 := fwptr;
    fwptr := lcp1^.next; 
    strassfv(id, lcp1^.name);
    searchidnenm([types], lcp2, mm);
    if lcp2 <> nil then lcp1^.idtype^.eltype := lcp2^.idtype 
    else begin 
      if fe then begin error(117); writeln(output) end;
      write('*** undefined type-id forward reference: '); 
      writev(output, lcp1^.name, prtlln); writeln;
      fe := false
    end;
    putnam(lcp1)
  end;
  id := ids
end;

some kind of giant type-related procedure

procedure typ(fsys: setofsys; var fsp: stp; var fsize: addrrange);
  var lsp,lsp1,lsp2: stp; oldtop: disprange; lcp: ctp;
      lsize,displ: addrrange; lmin,lmax: integer;
      test: boolean; ispacked: boolean;

simpletype

procedure simpletype(fsys:setofsys; var fsp:stp; var fsize:addrrange);
  var lsp,lsp1: stp; lcp,lcp1: ctp; ttop: disprange;
      lcnt: integer; lvalu: valu;
begin fsize := 1;
  if not (sy in simptypebegsys) then
    begin error(1); skip(fsys + simptypebegsys) end;
  if sy in simptypebegsys then
    begin
      if sy = lparent then
        begin ttop := top;   (*decl. consts local to innermost block*)
          while display[top].occur <> blck do top := top - 1;
          new(lsp,scalar,declared); pshstc(lsp);
          with lsp^ do
            begin size := intsize; form := scalar;
              scalkind := declared
            end;
          lcp1 := nil; lcnt := 0;
          repeat insymbol;
            if sy = ident then
              begin new(lcp,konst); ininam(lcp);
                with lcp^ do
                  begin strassvf(name, id); idtype := lsp; next := lcp1;
                    values.ival := lcnt; klass := konst
                  end;
                enterid(lcp);
                lcnt := lcnt + 1;
                lcp1 := lcp; insymbol
              end
            else error(2);
            if not (sy in fsys + [comma,rparent]) then
              begin error(6); skip(fsys + [comma,rparent]) end
          until sy <> comma;
          lsp^.fconst := lcp1; top := ttop;
          if sy = rparent then insymbol else error(4)
        end
      else
        begin
          if sy = ident then
            begin searchid([types,konst],lcp);
              insymbol;
              if lcp^.klass = konst then
                begin new(lsp,subrange); pshstc(lsp);
                  with lsp^, lcp^ do
                    begin rangetype := idtype; form := subrange;
                      if string(rangetype) then
                        begin error(148); rangetype := nil end;
                      min := values; size := intsize
                    end;
                  if sy = range then insymbol else error(5);
                  constant(fsys,lsp1,lvalu);
                  lsp^.max := lvalu;
                  if lsp^.rangetype <> lsp1 then error(107)
                end
              else
                begin lsp := lcp^.idtype;
                  if lsp <> nil then fsize := lsp^.size
                end
            end (*sy = ident*)
          else
            begin new(lsp,subrange); pshstc(lsp);
              lsp^.form := subrange;
              constant(fsys + [range],lsp1,lvalu);
              if string(lsp1) then
                begin error(148); lsp1 := nil end;
              with lsp^ do
                begin rangetype:=lsp1; min:=lvalu; size:=intsize end;
              if sy = range then insymbol else error(5);
              constant(fsys,lsp1,lvalu);
              lsp^.max := lvalu;
              if lsp^.rangetype <> lsp1 then error(107)
            end;
          if lsp <> nil then
            with lsp^ do
              if form = subrange then
                if rangetype <> nil then
                  if rangetype = realptr then error(399)
                  else
                    if min.ival > max.ival then error(102)
        end;
      fsp := lsp;
      if not (sy in fsys) then
        begin error(6); skip(fsys) end
    end
      else fsp := nil
end (*simpletype*) ;

fieldlist

procedure fieldlist(fsys: setofsys; var frecvar: stp);
  var lcp,lcp1,nxt,nxt1: ctp; lsp,lsp1,lsp2,lsp3,lsp4: stp;
      minsize,maxsize,lsize: addrrange; lvalu: valu;
      test: boolean; mm: boolean;
begin nxt1 := nil; lsp := nil;
  if not (sy in (fsys+[ident,casesy])) then
    begin error(19); skip(fsys + [ident,casesy]) end;
  while sy = ident do
    begin nxt := nxt1;
      repeat
        if sy = ident then
          begin new(lcp,field); ininam(lcp);
            with lcp^ do
              begin strassvf(name, id); idtype := nil; next := nxt;
                klass := field
              end;
            nxt := lcp;
            enterid(lcp);
            insymbol
          end
        else error(2);
        if not (sy in [comma,colon]) then
          begin error(6); skip(fsys + [comma,colon,semicolon,casesy])
          end;
        test := sy <> comma;
        if not test  then insymbol
      until test;
      if sy = colon then insymbol else error(5);
      typ(fsys + [casesy,semicolon],lsp,lsize);
      while nxt <> nxt1 do
        with nxt^ do
          begin align(lsp,displ);
            idtype := lsp; fldaddr := displ;
            nxt := next; displ := displ + lsize
          end;
      nxt1 := lcp;
      while sy = semicolon do
        begin insymbol;
          if not (sy in fsys + [ident,casesy,semicolon]) then
            begin error(19); skip(fsys + [ident,casesy]) end
        end
    end (*while*);
  nxt := nil;
  while nxt1 <> nil do
    with nxt1^ do
      begin lcp := next; next := nxt; nxt := nxt1; nxt1 := lcp end;
  if sy = casesy then
    begin new(lsp,tagfld); pshstc(lsp);
      with lsp^ do
        begin tagfieldp := nil; fstvar := nil; form:=tagfld end;
      frecvar := lsp;
      insymbol;
      if sy = ident then 
        begin
          { find possible type first }
          searchidnenm([types],lcp1,mm);
          { now set up as field id }
          new(lcp,field); ininam(lcp);
          with lcp^ do
            begin strassvf(name, id); idtype := nil; klass:=field;
              next := nil; fldaddr := displ
            end;
          insymbol;
          { If type only (undiscriminated variant), kill the id. }
          if sy = colon then begin 
            enterid(lcp); insymbol;
            if sy = ident then begin searchid([types],lcp1); insymbol end
            else begin error(2); skip(fsys + [ofsy,lparent]); lcp1 := nil end
          end else begin
             if mm then error(103);
             putstrs(lcp^.name); { release name string }
             lcp^.name := nil { set no tagfield }
          end;
          if lcp1 <> nil then begin
            lsp1 := lcp1^.idtype;
            if lsp1 <> nil then
              begin align(lsp1,displ);
                lcp^.fldaddr := displ;
                { only allocate field if named }
                if lcp^.name <> nil then displ := displ+lsp1^.size;
                if (lsp1^.form <= subrange) or string(lsp1) then
                  begin if comptypes(realptr,lsp1) then error(109)
                    else if string(lsp1) then error(399);
                    lcp^.idtype := lsp1; lsp^.tagfieldp := lcp;
                  end
                else error(110);
              end
            end
        end
      else begin error(2); skip(fsys + [ofsy,lparent]) end;
      lsp^.size := displ;
      if sy = ofsy then insymbol else error(8);
      lsp1 := nil; minsize := displ; maxsize := displ;
      repeat lsp2 := nil;
        if not (sy in fsys + [semicolon]) then
        begin
          repeat constant(fsys + [comma,colon,lparent],lsp3,lvalu);
            if lsp^.tagfieldp <> nil then
             if not comptypes(lsp^.tagfieldp^.idtype,lsp3)then error(111);
            new(lsp3,variant); pshstc(lsp3);
            with lsp3^ do
              begin nxtvar := lsp1; subvar := lsp2; varval := lvalu;
                form := variant
              end;
            lsp4 := lsp1;
            while lsp4 <> nil do
              with lsp4^ do
                begin
                  if varval.ival = lvalu.ival then error(178);
                  lsp4 := nxtvar
                end;
            lsp1 := lsp3; lsp2 := lsp3;
            test := sy <> comma;
            if not test then insymbol
          until test;
          if sy = colon then insymbol else error(5);
          if sy = lparent then insymbol else error(9);
          fieldlist(fsys + [rparent,semicolon],lsp2);
          if displ > maxsize then maxsize := displ;
          while lsp3 <> nil do
            begin lsp4 := lsp3^.subvar; lsp3^.subvar := lsp2;
              lsp3^.size := displ;
              lsp3 := lsp4
            end;
          if sy = rparent then
            begin insymbol;
              if not (sy in fsys + [semicolon]) then
                begin error(6); skip(fsys + [semicolon]) end
            end
          else error(4);
        end;
        test := sy <> semicolon;
        if not test then
          begin displ := minsize;
                insymbol
          end
      until test;
      displ := maxsize;
      lsp^.fstvar := lsp1;
    end
  else frecvar := nil
end (*fieldlist*) ;

type

begin (*typ*)
  if not (sy in typebegsys) then
     begin error(10); skip(fsys + typebegsys) end;
  if sy in typebegsys then
    begin
      if sy in simptypebegsys then simpletype(fsys,fsp,fsize)
      else
(*^*)     if sy = arrow then
          begin new(lsp,pointer); pshstc(lsp); fsp := lsp;
            with lsp^ do
              begin eltype := nil; size := ptrsize; form:=pointer end;
            insymbol;
            if sy = ident then
              begin { forward reference everything }
                new(lcp,types); ininam(lcp);
                with lcp^ do
                  begin strassvf(name,id); idtype := lsp;
                    next := fwptr; klass := types
                  end;
                fwptr := lcp;
                insymbol;
              end
            else error(2);
          end
        else
          begin
            ispacked := false; { set not packed by default }
            if sy = packedsy then
              begin insymbol; ispacked := true; { packed }
                if not (sy in typedels) then
                  begin
                    error(10); skip(fsys + typedels)
                  end
              end;
(*array*)     if sy = arraysy then
              begin insymbol;
                if sy = lbrack then insymbol else error(11);
                lsp1 := nil;
                repeat new(lsp,arrays); pshstc(lsp);
                  with lsp^ do
                    begin aeltype := lsp1; inxtype := nil; form:=arrays; 
                          packing := ispacked end;
                  lsp1 := lsp;
                  simpletype(fsys + [comma,rbrack,ofsy],lsp2,lsize);
                  lsp1^.size := lsize;
                  if lsp2 <> nil then
                    if lsp2^.form <= subrange then
                      begin
                        if lsp2 = realptr then
                          begin error(109); lsp2 := nil end
                        else
                          if lsp2 = intptr then
                            begin error(149); lsp2 := nil end;
                        lsp^.inxtype := lsp2
                      end
                    else begin error(113); lsp2 := nil end;
                  test := sy <> comma;
                  if not test then insymbol
                until test;
                if sy = rbrack then insymbol else error(12);
                if sy = ofsy then insymbol else error(8);
                typ(fsys,lsp,lsize);
                repeat
                  with lsp1^ do
                    begin lsp2 := aeltype; aeltype := lsp;
                      if inxtype <> nil then
                        begin getbounds(inxtype,lmin,lmax);
                          align(lsp,lsize);
                          lsize := lsize*(lmax - lmin + 1);
                          size := lsize
                        end
                    end;
                  lsp := lsp1; lsp1 := lsp2
                until lsp1 = nil
              end
            else
(*record*)      if sy = recordsy then
                begin insymbol;
                  oldtop := top;
                  if top < displimit then
                    begin top := top + 1;
                      with display[top] do
                        begin fname := nil;
                              flabel := nil;
                              fconst := nil;
                              fstruct := nil;
                              occur := rec
                        end
                    end
                  else error(250);
                  displ := 0;
                  fieldlist(fsys-[semicolon]+[endsy],lsp1);
                  new(lsp,records);
                  with lsp^ do
                    begin fstfld := display[top].fname; 
                      display[top].fname := nil;
                      recvar := lsp1; size := displ; form := records; 
                      packing := ispacked;
                      recyc := display[top].fstruct; 
                      display[top].fstruct := nil
                    end;
                  putdsps(oldtop); top := oldtop;
                  { register the record late because of the purge above }
                  pshstc(lsp);
                  if sy = endsy then insymbol else error(13)
                end
              else
(*set*)        if sy = setsy then
                  begin insymbol;
                    if sy = ofsy then insymbol else error(8);
                    simpletype(fsys,lsp1,lsize);
                    if lsp1 <> nil then
                      if lsp1^.form > subrange then
                        begin error(115); lsp1 := nil end
                      else
                        if lsp1 = realptr then
                          begin error(114); lsp1 := nil end
                        else if lsp1 = intptr then
                          begin error(169); lsp1 := nil end
                        else
                          begin getbounds(lsp1,lmin,lmax);
                            if (lmin < setlow) or (lmax > sethigh)
                              then error(169);
                          end;
                    new(lsp,power); pshstc(lsp);
                    with lsp^ do
                      begin elset:=lsp1; size:=setsize; form:=power; 
                            packing := ispacked; matchpack := true end;
                  end
                else
(*file*)        if sy = filesy then
                      begin insymbol;
                        if sy = ofsy then insymbol else error(8);
                        typ(fsys,lsp1,lsize);
                        if filecomponent(lsp1) then error(190);
                        new(lsp,files); pshstc(lsp);
                        with lsp^ do
                          begin filtype := lsp1; size := filesize+lsize; 
                            form := files; packing := ispacked
                          end
                      end;
            fsp := lsp
          end;
      if not (sy in fsys) then
        begin error(6); skip(fsys) end
    end
  else fsp := nil;
  if fsp = nil then fsize := 1 else fsize := fsp^.size
end (*typ*) ;

labeldeclaration

procedure labeldeclaration;
  var llp: lbp;
      test: boolean;
begin
  repeat
    if sy = intconst then begin
      searchlabel(llp, top); { search preexisting label }
      if llp <> nil then error(166) { multideclared label }
      else newlabel(llp);
      insymbol
    end else error(15);
    if not ( sy in fsys + [comma, semicolon] ) then
      begin error(6); skip(fsys+[comma,semicolon]) end;
    test := sy <> comma;
    if not test then insymbol
  until test;
  if sy = semicolon then insymbol else error(14)
end (* labeldeclaration *) ;

const declaration

procedure constdeclaration;
  var lcp: ctp; lsp: stp; lvalu: valu;
begin
  if sy <> ident then
    begin error(2); skip(fsys + [ident]) end;
  while sy = ident do
    begin new(lcp,konst); ininam(lcp);
      with lcp^ do
        begin strassvf(name, id); idtype := nil; next := nil; klass:=konst end;
      insymbol;
      if (sy = relop) and (op = eqop) then insymbol else error(16);
      constant(fsys + [semicolon],lsp,lvalu);
      enterid(lcp);
      lcp^.idtype := lsp; lcp^.values := lvalu;
      if sy = semicolon then
        begin insymbol;
          if not (sy in fsys + [ident]) then
            begin error(6); skip(fsys + [ident]) end
        end
      else error(14)
    end
end (*constdeclaration*) ;

typedeclaration

procedure typedeclaration;
  var lcp,lcp1,lcp2,lcp3: ctp; lsp: stp; lsize: addrrange;
begin
  if sy <> ident then
    begin error(2); skip(fsys + [ident]) end;
  while sy = ident do
    begin new(lcp,types); ininam(lcp);
      with lcp^ do
        begin strassvf(name, id); idtype := nil; klass := types end;
      insymbol;
      if (sy = relop) and (op = eqop) then insymbol else error(16);
      typ(fsys + [semicolon],lsp,lsize);
      enterid(lcp);
      lcp^.idtype := lsp;
      if sy = semicolon then
        begin insymbol;
          if not (sy in fsys + [ident]) then
            begin error(6); skip(fsys + [ident]) end
        end
      else error(14)
    end;
  resolvep
end (*typedeclaration*) ;

vardeclaration

procedure vardeclaration;
  var lcp,nxt: ctp; lsp: stp; lsize: addrrange;
      test: boolean;
begin nxt := nil;
  repeat
    repeat
      if sy = ident then
        begin new(lcp,vars); ininam(lcp);
          with lcp^ do
           begin strassvf(name, id); next := nxt; klass := vars;
              idtype := nil; vkind := actual; vlev := level
            end;
          enterid(lcp);
          nxt := lcp;
          insymbol;
        end
      else error(2);
      if not (sy in fsys + [comma,colon] + typedels) then
        begin error(6); skip(fsys+[comma,colon,semicolon]+typedels) end;
      test := sy <> comma;
      if not test then insymbol
    until test;
    if sy = colon then insymbol else error(5);
    typ(fsys + [semicolon] + typedels,lsp,lsize);
    while nxt <> nil do
      with  nxt^ do
        begin align(lsp,lc);
          idtype := lsp; vaddr := lc;
          lc := lc + lsize; nxt := next
        end;
    if sy = semicolon then
      begin insymbol;
        if not (sy in fsys + [ident]) then
          begin error(6); skip(fsys + [ident]) end
      end
    else error(14)
  until (sy <> ident) and not (sy in typedels);
  resolvep
end (*vardeclaration*) ;

procdeclaration

procedure procdeclaration(fsy: symbol);
  var oldlev: 0..maxlevel; lcp,lcp1: ctp; lsp: stp;
      forw: boolean; oldtop: disprange;
      llc,lcm: addrrange; lbname: integer; {markp: marktype;}

  procedure pushlvl(forw: boolean; lcp: ctp);
  begin
    if level < maxlevel then level := level + 1 else error(251);
    if top < displimit then
      begin top := top + 1;
        with display[top] do
          begin
            if forw then fname := lcp^.pflist
            else fname := nil;
            flabel := nil; fconst := nil; fstruct := nil;
            occur := blck;
            bname := lcp
          end
      end
    else error(250);
  end;

parameterlist

  procedure parameterlist(fsy: setofsys; var fpar: ctp);
    var lcp,lcp1,lcp2,lcp3: ctp; lsp: stp; lkind: idkind;
      llc,lsize: addrrange; count: integer;
      oldlev: 0..maxlevel; oldtop: disprange;
      lcs: addrrange;
      test: boolean;
  begin lcp1 := nil;
    if not (sy in fsy + [lparent]) then
      begin error(7); skip(fsys + fsy + [lparent]) end;
    if sy = lparent then
      begin if forw then error(119);
        insymbol;
        if not (sy in [ident,varsy,procsy,funcsy]) then
          begin error(7); skip(fsys + [ident,rparent]) end;
        while sy in [ident,varsy,procsy,funcsy] do
          begin
            if sy = procsy then
              begin
                insymbol; lcp := nil;
                if sy = ident then
                  begin new(lcp,proc,declared,formal); ininam(lcp);
                    with lcp^ do
                      begin strassvf(name, id); idtype := nil; next := lcp1;
                        pflev := level (*beware of parameter procedures*);
                        klass:=proc;pfdeckind:=declared;
                        pfkind:=formal; pfaddr := lc; keep := true
                      end;
                    enterid(lcp);
                    lcp1 := lcp;
                    align(parmptr,lc);
                    lc := lc+ptrsize*2; { mp and addr }
                    insymbol
                  end
                else error(2);
                oldlev := level; oldtop := top; pushlvl(false, lcp);
                lcs := lc; parameterlist([semicolon,rparent],lcp2); lc := lcs;
                if lcp <> nil then lcp^.pflist := lcp2;
                if not (sy in fsys+[semicolon,rparent]) then
                  begin error(7);skip(fsys+[semicolon,rparent]) end;
                level := oldlev; putdsps(oldtop); top := oldtop
              end
            else
              begin
                if sy = funcsy then
                  begin lcp2 := nil;
                    insymbol;
                    if sy = ident then
                      begin new(lcp,func,declared,formal); ininam(lcp);
                        with lcp^ do
                          begin strassvf(name, id); idtype := nil; next := lcp1;
                            pflev := level (*beware param funcs*);
                            klass:=func;pfdeckind:=declared;
                            pfkind:=formal; pfaddr:=lc; keep := true
                          end;
                        enterid(lcp);
                        lcp1 := lcp;
                        align(parmptr,lc);
                        lc := lc+ptrsize*2; { mp and addr }
                        insymbol;
                      end
                    else error(2);
                    oldlev := level; oldtop := top; pushlvl(false, lcp);
                    lcs := lc; parameterlist([colon,semicolon,rparent],lcp2); lc := lcs;
                    if lcp <> nil then lcp^.pflist := lcp2;
                    if not (sy in fsys+[colon]) then
                      begin error(7);skip(fsys+[comma,semicolon,rparent]) end;
                    if sy = colon then
                      begin insymbol;
                        if sy = ident then
                          begin searchid([types],lcp2);
                            lsp := lcp2^.idtype;
                            lcp^.idtype := lsp;
                            if lsp <> nil then
                             if not(lsp^.form in[scalar,subrange,pointer])
                                then begin error(120); lsp := nil end;
                            insymbol
                          end
                        else error(2);
                        if not (sy in fsys + [semicolon,rparent]) then
                          begin error(7);skip(fsys+[semicolon,rparent])end
                      end
                    else error(5);
                    level := oldlev; putdsps(oldtop); top := oldtop
                  end
                else
                  begin
                    if sy = varsy then
                      begin lkind := formal; insymbol end
                    else lkind := actual;
                    lcp2 := nil;
                    count := 0;
                    repeat
                      if sy = ident then
                        begin new(lcp,vars); ininam(lcp);
                          with lcp^ do
                            begin strassvf(name,id); idtype:=nil; klass:=vars;
                              vkind := lkind; next := lcp2; vlev := level; 
                              keep := true
                            end;
                          enterid(lcp);
                          lcp2 := lcp; count := count+1;
                          insymbol;
                        end;
                      if not (sy in [comma,colon] + fsys) then
                        begin error(7);skip(fsys+[comma,semicolon,rparent])
                        end;
                      test := sy <> comma;
                      if not test then insymbol
                    until test;
                    if sy = colon then
                      begin insymbol;
                        if sy = ident then
                          begin searchid([types],lcp);
                            lsp := lcp^.idtype;
                            lsize := ptrsize;
                            if lsp <> nil then
                              if lkind=actual then
                                if lsp^.form<=power then lsize := lsp^.size
                                else if lsp^.form=files then error(121);
                            align(parmptr,lsize);
                            lcp3 := lcp2;
                            align(parmptr,lc);
                            lc := lc+count*lsize;
                            llc := lc;
                            while lcp2 <> nil do
                              begin lcp := lcp2;
                                with lcp2^ do
                                  begin idtype := lsp;
                                    llc := llc-lsize;
                                    vaddr := llc;
                                  end;
                                lcp2 := lcp2^.next
                              end;
                            lcp^.next := lcp1; lcp1 := lcp3;
                            insymbol
                          end
                        else error(2);
                        if not (sy in fsys + [semicolon,rparent]) then
                          begin error(7);skip(fsys+[semicolon,rparent])end
                      end
                    else error(5);
                  end;
              end;
            if sy = semicolon then
              begin insymbol;
                if not (sy in fsys + [ident,varsy,procsy,funcsy]) then
                  begin error(7); skip(fsys + [ident,rparent]) end
              end
          end (*while*) ;
        if sy = rparent then
          begin insymbol;
            if not (sy in fsy + fsys) then
              begin error(6); skip(fsy + fsys) end
          end
        else error(4);
        lcp3 := nil;
        (*reverse pointers and reserve local cells for copies of multiple
         values*)
        while lcp1 <> nil do
          with lcp1^ do
            begin lcp2 := next; next := lcp3;
              if klass = vars then
                if idtype <> nil then
                  if (vkind=actual)and(idtype^.form>power) then
                    begin align(idtype,lc);
                      vaddr := lc;
                      lc := lc+idtype^.size;
                    end;
              lcp3 := lcp1; lcp1 := lcp2
            end;
        fpar := lcp3
      end
        else fpar := nil
end (*parameterlist*) ;

procdeclaration

begin (*procdeclaration*)
  llc := lc; lc := lcaftermarkstack; forw := false;
  if sy = ident then
    begin searchsection(display[top].fname,lcp); (*decide whether forw.*)
      if lcp <> nil then
        begin
          if lcp^.klass = proc then
            forw := lcp^.forwdecl and(fsy=procsy)and(lcp^.pfkind=actual)
          else
            if lcp^.klass = func then
              forw:=lcp^.forwdecl and(fsy=funcsy)and(lcp^.pfkind=actual)
            else forw := false;
          if not forw then error(160)
        end;
      if not forw then
        begin
          if fsy = procsy then new(lcp,proc,declared,actual)
          else new(lcp,func,declared,actual); ininam(lcp);
          with lcp^ do
            begin strassvf(name, id); idtype := nil;
              externl := false; pflev := level; genlabel(lbname);
              pfdeckind := declared; pfkind := actual; pfname := lbname;
              if fsy = procsy then klass := proc
              else klass := func
            end;
          enterid(lcp)
        end
      else
        begin lcp1 := lcp^.pflist;
          while lcp1 <> nil do
            begin
              with lcp1^ do
                if klass = vars then
                  if idtype <> nil then
                    begin lcm := vaddr + idtype^.size;
                      if lcm > lc then lc := lcm
                    end;
              lcp1 := lcp1^.next
            end
        end;
      insymbol
    end
  else
    begin error(2); lcp := ufctptr end;
  oldlev := level; oldtop := top;
  pushlvl(forw, lcp);
  if fsy = procsy then
    begin parameterlist([semicolon],lcp1);
      if not forw then lcp^.pflist := lcp1
    end
  else
    begin parameterlist([semicolon,colon],lcp1);
      if not forw then lcp^.pflist := lcp1;
      if sy = colon then
        begin insymbol;
          if sy = ident then
            begin if forw then error(122);
              searchid([types],lcp1);
              lsp := lcp1^.idtype;
              lcp^.idtype := lsp;
              if lsp <> nil then
                if not (lsp^.form in [scalar,subrange,pointer]) then
                  begin error(120); lcp^.idtype := nil end;
              insymbol
            end
          else begin error(2); skip(fsys + [semicolon]) end
        end
      else
        if not forw then error(123)
    end;
  if sy = semicolon then insymbol else error(14);
  if (sy = ident) and strequri('forward  ', id) then
    begin
      if forw then error(161)
      else lcp^.forwdecl := true;
      insymbol;
      if sy = semicolon then insymbol else error(14);
      if not (sy in fsys) then
        begin error(6); skip(fsys) end
    end
  else
    begin lcp^.forwdecl := false;
      { mark(markp); }
      repeat block(fsys,semicolon,lcp);
        if sy = semicolon then
          begin if prtables then printtables(false); insymbol;
            if not (sy in [beginsy,procsy,funcsy]) then
              begin error(6); skip(fsys) end
          end
        else error(14)
      until (sy in [beginsy,procsy,funcsy]) or eof(input);
      { release(markp); } (* return local entries on runtime heap *)
    end;
  level := oldlev; putdsps(oldtop); top := oldtop; lc := llc;
end (*procdeclaration*) ;

body

procedure body(fsys: setofsys);
  const cstoccmax=4000; cixmax=10000;
  type oprange = 0..maxins;
  var
      llcp:ctp; saveid:idstr;
      cstptr: array [1..cstoccmax] of csp;
      cstptrix: 0..cstoccmax;
      (*allows referencing of noninteger constants by an index
       (instead of a pointer), which can be stored in the p2-field
       of the instruction record until writeout.
       --> procedure load, procedure writeout*)
      entname, segsize: integer;
      stacktop, topnew, topmax: integer;
      lcmax,llc1: addrrange; lcp: ctp;
      llp: lbp;
      fp: extfilep;
      test: boolean;

  { add statement level }
  procedure addlvl;
  begin
    stalvl := stalvl+1
  end;

  { remove statement level }
  procedure sublvl;
  var llp: lbp;
  begin
     stalvl := stalvl-1;
     { traverse label list for current block and remove any label from
       active status whose statement block has closed }
     llp := display[top].flabel;
     while llp <> nil do with llp^ do begin
       if slevel > stalvl then bact := false;
       llp := nextlab { link next }
     end
  end;

  procedure mes(i: integer);
  begin topnew := topnew + cdx[i]*maxstack;
    if topnew > topmax then topmax := topnew
  end;

  procedure putic;
  begin if ic mod 10 = 0 then writeln(prr,'i',ic:5) end;

  procedure gen0(fop: oprange);
  begin
    if prcode then begin putic; writeln(prr,mn[fop]:4) end;
    ic := ic + 1; mes(fop)
  end (*gen0*) ;

  procedure gen1(fop: oprange; fp2: integer);
    var k, j: integer; p: strvsp;
  begin
    if prcode then
      begin putic; write(prr,mn[fop]:4);
        if fop = 30 then
          begin writeln(prr,sna[fp2]:12);
            topnew := topnew + pdx[fp2]*maxstack;
            if topnew > topmax then topmax := topnew
          end
        else
          begin
            if fop = 38 then
               begin with cstptr[fp2]^ do begin p := sval; j := 1;
                   write(prr,' ',slgth:4,' ''');
                   for k := 1 to lenpv(p) do begin
                     if p^.str[j] = '''' then write(prr, '''''')
                     else write(prr,p^.str[j]:1);
                     j := j+1; if j > varsqt then begin 
                       p := p^.next; j := 1 
                     end
                   end
                 end;
                 writeln(prr,'''')
               end
            else if fop = 42 then writeln(prr,chr(fp2))
            else if fop = 67 then writeln(prr,fp2:4)
            else writeln(prr,fp2:12);
            mes(fop)
          end
      end;
    ic := ic + 1
  end (*gen1*) ;

gen2

procedure gen2(fop: oprange; fp1,fp2: integer);
  var k : integer;
begin
  if prcode then
    begin putic; write(prr,mn[fop]:4);
      case fop of
        45,50,54,56,74,62,63:
          writeln(prr,' ',fp1:3,fp2:8);
        47,48,49,52,53,55:
          begin write(prr,chr(fp1));
            if chr(fp1) = 'm' then write(prr,' ',fp2:11);
            writeln(prr)
          end;
        51:
          case fp1 of
            1: writeln(prr,'i ',fp2);
            2: begin write(prr,'r ');
                 with cstptr[fp2]^ do writev(prr,rval,lenpv(rval));
                 writeln(prr)
               end;
            3: writeln(prr,'b ',fp2);
            4: writeln(prr,'n');
            6: writeln(prr,'c ''':3,chr(fp2),'''');
            5: begin write(prr,'(');
                 with cstptr[fp2]^ do
                   for k := setlow to sethigh do
                     (* increased for testing [sam] *)
                     if k in pval then write(prr,k:7(*3*));
                 writeln(prr,')')
               end
          end;
      end;
    end;
  ic := ic + 1; mes(fop)
end (*gen2*) ;

gentypindicator

procedure gentypindicator(fsp: stp);
begin
  if fsp<>nil then
    with fsp^ do
      case form of
       scalar: if fsp=intptr then write(prr,'i')
               else
                 if fsp=boolptr then write(prr,'b')
                 else
                   if fsp=charptr then write(prr,'c')
                   else
                     if scalkind = declared then write(prr,'i')
                     else write(prr,'r');
       subrange: gentypindicator(rangetype);
       pointer:  write(prr,'a');
       power:    write(prr,'s');
       records,arrays: write(prr,'m');
       files:    write(prr,'a');
       tagfld,variant: error(500)
      end
end (*typindicator*);

procedure gen0t(fop: oprange; fsp: stp);
begin
  if prcode then
    begin putic;
      write(prr,mn[fop]:4);
      gentypindicator(fsp);
      writeln(prr);
    end;
  ic := ic + 1; mes(fop)
end (*gen0t*);

procedure gen1t(fop: oprange; fp2: integer; fsp: stp);
begin
  if prcode then
    begin putic;
      write(prr,mn[fop]:4);
      gentypindicator(fsp);
      writeln(prr,' ',fp2:11)
    end;
  ic := ic + 1; mes(fop)
end (*gen1t*);

procedure gen2t(fop: oprange; fp1,fp2: integer; fsp: stp);
begin
  if prcode then
    begin putic;
      write(prr,mn[fop]: 4);
      gentypindicator(fsp);
      writeln(prr,' ', fp1:3+5*ord(abs(fp1)>99),fp2:11);
    end;
  ic := ic + 1; mes(fop)
end (*gen2t*);

load

procedure load;
begin
  with gattr do
    if typtr <> nil then
      begin
        case kind of
          cst:   if (typtr^.form = scalar) and (typtr <> realptr) then
                   if typtr = boolptr then gen2(51(*ldc*),3,cval.ival)
                   else
                     if typtr=charptr then
                       gen2(51(*ldc*),6,cval.ival)
                     else gen2(51(*ldc*),1,cval.ival)
                 else
                   if typtr = nilptr then gen2(51(*ldc*),4,0)
                   else
                     if cstptrix >= cstoccmax then error(254)
                     else
                       begin cstptrix := cstptrix + 1;
                         cstptr[cstptrix] := cval.valp;
                         if typtr = realptr then
                           gen2(51(*ldc*),2,cstptrix)
                         else
                           gen2(51(*ldc*),5,cstptrix)
                       end;
          varbl: case access of
                   drct:   if vlevel<=1 then gen1t(39(*ldo*),dplmt,typtr)
                           else gen2t(54(*lod*),level-vlevel,dplmt,typtr);
                   indrct: gen1t(35(*ind*),idplmt,typtr);
                   inxd:   error(400)
                 end;
          expr:
        end;
        kind := expr
      end
end (*load*) ;

store

procedure store(var fattr: attr);
begin
  with fattr do
    if typtr <> nil then
      case access of
        drct:   if vlevel <= 1 then gen1t(43(*sro*),dplmt,typtr)
                else gen2t(56(*str*),level-vlevel,dplmt,typtr);
        indrct: if idplmt <> 0 then error(400)
                else gen0t(26(*sto*),typtr);
        inxd:   error(400)
      end
end (*store*) ;

procedure loadaddress;
begin
  with gattr do
    if typtr <> nil then
      begin
        case kind of
          cst:   if string(typtr) then
                   if cstptrix >= cstoccmax then error(254)
                   else
                     begin cstptrix := cstptrix + 1;
                       cstptr[cstptrix] := cval.valp;
                       gen1(38(*lca*),cstptrix)
                     end
                 else error(400);
          varbl: case access of
                   drct:   if vlevel <= 1 then gen1(37(*lao*),dplmt)
                           else gen2(50(*lda*),level-vlevel,dplmt);
                   indrct: if idplmt <> 0 then
                             gen1t(34(*inc*),idplmt,nilptr);
                   inxd:   error(400)
                 end;
          expr:  error(400)
        end;
        kind := varbl; access := indrct; idplmt := 0
      end
end (*loadaddress*) ;


procedure genfjp(faddr: integer);
begin load;
  if gattr.typtr <> nil then
    if gattr.typtr <> boolptr then error(144);
  if prcode then begin putic; writeln(prr,mn[33]:4,' l':8,faddr:4) end;
  ic := ic + 1; mes(33)
end (*genfjp*) ;

procedure genujpxjp(fop: oprange; fp2: integer);
begin
 if prcode then
    begin putic; writeln(prr, mn[fop]:4, ' l':8,fp2:4) end;
  ic := ic + 1; mes(fop)
end (*genujpxjp*);

procedure genipj(fop: oprange; fp1, fp2: integer);
begin
 if prcode then
    begin putic; writeln(prr, mn[fop]:4,fp1:4,' l':8,fp2:4) end;
  ic := ic + 1; mes(fop)
end (*genujpxjp*);

procedure gencupent(fop: oprange; fp1,fp2: integer);
begin
  if prcode then
    begin putic;
      if fop = 32 then begin { create ents or ente instructions }
        if fp1 = 1 then writeln(prr,mn[fop]:4,'s','l':8,fp2:4)
        else writeln(prr,mn[fop]:4,'e','l':8,fp2:4)
      end else writeln(prr,mn[fop]:4,fp1:4,'l':4,fp2:4)
    end;
  ic := ic + 1; mes(fop)
end;

procedure genlpa(fp1,fp2: integer);
begin
  if prcode then
    begin putic;
      writeln(prr,mn[68]:4,fp2:4,'l':4,fp1:4)
    end;
  ic := ic + 1; mes(68)
end (*genlpa*); 

checkbnds

procedure checkbnds(fsp: stp);
  var lmin,lmax: integer;
begin
  if fsp <> nil then
    if fsp <> intptr then
      if fsp <> realptr then
        if fsp^.form <= subrange then
          begin
            getbounds(fsp,lmin,lmax);
            gen2t(45(*chk*),lmin,lmax,fsp)
          end
end (*checkbnds*);

putlabel

procedure putlabel(labname: integer);
begin if prcode then writeln(prr, 'l', labname:4)
end (*putlabel*);

statement

procedure statement(fsys: setofsys);
  var lcp: ctp; llp: lbp;
  
  procedure expression(fsys: setofsys); forward;

  procedure selector(fsys: setofsys; fcp: ctp);
  var lattr: attr; lcp: ctp; lsize: addrrange; lmin,lmax: integer;
  function schblk(fcp: ctp): boolean;
  var i: disprange; f: boolean;
  begin
     f := false;
     for i := level downto 2 do if display[i].bname = fcp then f := true;
     schblk := f
  end;
  begin
    with fcp^, gattr do
      begin typtr := idtype; kind := varbl;
        case klass of
          vars:
            if vkind = actual then
              begin access := drct; vlevel := vlev;
                dplmt := vaddr
              end
            else
              begin gen2t(54(*lod*),level-vlev,vaddr,nilptr);
                access := indrct; idplmt := 0
              end;
          field:
            with display[disx] do
              if occur = crec then
                begin access := drct; vlevel := clev;
                  dplmt := cdspl + fldaddr
                end
              else
                begin
                  if level = 1 then gen1t(39(*ldo*),vdspl,nilptr)
                  else gen2t(54(*lod*),0,vdspl,nilptr);
                  access := indrct; idplmt := fldaddr
                end;
          func:
            if pfdeckind = standard then
              begin error(150); typtr := nil end
            else
              begin
                if pfkind = formal then error(151)
                else
                  if not schblk(fcp) then error(192);
                  begin access := drct; vlevel := pflev + 1;
                    dplmt := 0   (*impl. relat. addr. of fct. result*)
                  end
              end
        end (*case*)
      end (*with*);
    if not (sy in selectsys + fsys) then
      begin error(59); skip(selectsys + fsys) end;
    while sy in selectsys do
      begin
  (*[*) if sy = lbrack then
          begin
            repeat lattr := gattr;
              with lattr do
                if typtr <> nil then
                  if typtr^.form <> arrays then
                    begin error(138); typtr := nil end;
              loadaddress;
              insymbol; expression(fsys + [comma,rbrack]);
              load;
              if gattr.typtr <> nil then
                if gattr.typtr^.form<>scalar then error(113)
                else if not comptypes(gattr.typtr,intptr) then
                       gen0t(58(*ord*),gattr.typtr);
              if lattr.typtr <> nil then
                with lattr.typtr^ do
                  begin
                    if comptypes(inxtype,gattr.typtr) then
                      begin
                        if inxtype <> nil then
                          begin getbounds(inxtype,lmin,lmax);
                            if debug then
                              gen2t(45(*chk*),lmin,lmax,intptr);
                            if lmin>0 then gen1t(31(*dec*),lmin,intptr)
                            else if lmin<0 then
                              gen1t(34(*inc*),-lmin,intptr);
                            (*or simply gen1(31,lmin)*)
                          end
                      end
                    else error(139);
                    with gattr do
                      begin typtr := aeltype; kind := varbl;
                        access := indrct; idplmt := 0
                      end;
                    if gattr.typtr <> nil then
                      begin
                        lsize := gattr.typtr^.size;
                        align(gattr.typtr,lsize);
                        gen1(36(*ixa*),lsize)
                      end
                  end
            until sy <> comma;
            if sy = rbrack then insymbol else error(12)
          end (*if sy = lbrack*)
        else
  (*.*)   if sy = period then
            begin
              with gattr do
                begin
                  if typtr <> nil then
                    if typtr^.form <> records then
                      begin error(140); typtr := nil end;
                  insymbol;
                  if sy = ident then
                    begin
                      if typtr <> nil then
                        begin searchsection(typtr^.fstfld,lcp);
                          if lcp = nil then
                            begin error(152); typtr := nil end
                          else
                            with lcp^ do
                              begin typtr := idtype;
                                case access of
                                  drct:   dplmt := dplmt + fldaddr;
                                  indrct: idplmt := idplmt + fldaddr;
                                  inxd:   error(400)
                                end
                              end
                        end;
                      insymbol
                    end (*sy = ident*)
                  else error(2)
                end (*with gattr*)
            end (*if sy = period*)
          else
  (*^*)     begin
              if gattr.typtr <> nil then
                with gattr,typtr^ do
                  if form = pointer then
                    begin load; typtr := eltype;
                      if debug then gen2t(45(*chk*),1,maxaddr,nilptr);
                      with gattr do
                        begin kind := varbl; access := indrct;
                          idplmt := 0
                        end
                    end
                  else
                    if form = files then begin loadaddress;
                       { generate buffer validate for file }
                       if typtr = textptr then gen0(65(*fbv*))
                       else begin
                         gen2(51(*ldc*),1,filtype^.size);
                         gen0(70(*fvb*))
                       end;
                       { index buffer }
                       gen1t(34(*inc*),fileidsize,gattr.typtr);
                       typtr := filtype;
                    end else error(141);
              insymbol
            end;
        if not (sy in fsys + selectsys) then
          begin error(6); skip(fsys + selectsys) end
      end (*while*)
  end (*selector*) ;

call procedure

procedure call(fsys: setofsys; fcp: ctp); var lkey: 1..18;

variable

procedure variable(fsys: setofsys); var lcp: ctp; begin if sy = ident then begin searchid([vars,field],lcp); insymbol end else begin error(2); lcp := uvarptr end; selector(fsys,lcp) end (variable) ;

getputresetwriteprocedure

procedure getputresetrewriteprocedure; begin variable(fsys + [rparent]); loadaddress; if gattr.typtr <> nil then if gattr.typtr^.form <> files then error(116); if lkey <= 2 then begin if gattr.typtr = textptr then gen1(30(csp),lkey(get,put)) else begin if gattr.typtr <> nil then gen2(51(ldc),1,gattr.typtr^.filtype^.size); if lkey = 1 then gen1(30(csp),38(gbf)) else gen1(30(csp),39(pbf)) end end else if gattr.typtr = textptr then begin if lkey = 3 then gen1(30(csp),25(reset)) else gen1(30(csp),26(rewrite)) end else begin if lkey = 3 then gen1(30(csp),36(reset)) else gen1(30(csp),37(rewrite)) end end (getputresetrewrite) ;

pageprocedure

procedure pageprocedure; var llev:levrange; begin llev := 1; if sy = lparent then begin insymbol; variable(fsys + [rparent]); loadaddress; if gattr.typtr <> nil then if gattr.typtr <> textptr then error(116); if sy = rparent then insymbol else error(4) end else begin if not outputhdf then error(176); gen2(50(lda),level-outputptr^.vlev,outputptr^.vaddr); end; gen1(30(csp),24(page)) end (page) ;

readprocedure

procedure readprocedure; var lsp : stp; txt: boolean; { is a text file } deffil: boolean; { default file was loaded } test: boolean; begin txt := true; deffil := true; if sy = lparent then begin insymbol; variable(fsys + [comma,rparent]); lsp := gattr.typtr; test := false; if lsp <> nil then if lsp^.form = files then with gattr, lsp^ do begin txt := lsp = textptr; if not txt and (lkey = 11) then error(116); loadaddress; deffil := false; if sy = rparent then begin if lkey = 5 then error(116); test := true end else if sy <> comma then begin error(116); skip(fsys + [comma,rparent]) end; if sy = comma then begin insymbol; variable(fsys + [comma,rparent]) end else test := true end else if not inputhdf then error(175); if not test then repeat loadaddress; if deffil then begin { file was not loaded, we load and swap so that it ends up on the bottom.} gen2(50(lda),level-inputptr^.vlev,inputptr^.vaddr); gen1(72(swp),ptrsize); { note 2nd is always pointer } deffil := false end; if txt then begin if gattr.typtr <> nil then if gattr.typtr^.form <= subrange then if comptypes(intptr,gattr.typtr) then gen1(30(csp),3(rdi)) else if comptypes(realptr,gattr.typtr) then gen1(30(csp),4(rdr)) else if comptypes(charptr,gattr.typtr) then gen1(30(csp),5(rdc)) else error(399) else error(116); end else begin { binary file } if not comptypes(gattr.typtr,lsp^.filtype) then error(129); gen2(51(ldc),1,lsp^.filtype^.size); gen1(30(csp),35(rbf)) end; test := sy <> comma; if not test then begin insymbol; variable(fsys + [comma,rparent]) end until test; if sy = rparent then insymbol else error(4) end else begin if not inputhdf then error(175); if lkey = 5 then error(116); gen2(50(lda),level-inputptr^.vlev,inputptr^.vaddr) end; if lkey = 11 then gen1(30(csp),21(rln)); { remove the file pointer from stack } gen1(71(dmp),ptrsize); end (read) ;

writeprocedure

procedure writeprocedure; var lsp,lsp1: stp; default, default1: boolean; llkey: 1..15; len:addrrange; txt: boolean; { is a text file } deffil: boolean; { default file was loaded } test: boolean; begin llkey := lkey; txt := true; deffil := true; if sy = lparent then begin insymbol; expression(fsys + [comma,colon,rparent]); lsp := gattr.typtr; test := false; if lsp <> nil then if lsp^.form = files then with gattr, lsp^ do begin lsp1 := lsp; txt := lsp = textptr; if not txt and (lkey = 12) then error(116); loadaddress; deffil := false; if sy = rparent then begin if llkey = 6 then error(116); test := true end else if sy <> comma then begin error(116); skip(fsys+[comma,rparent]) end; if sy = comma then begin insymbol; expression(fsys+[comma,colon,rparent]) end else test := true end else if not outputhdf then error(176); if not test then repeat lsp := gattr.typtr; if lsp <> nil then if lsp^.form <= subrange then load else loadaddress; if deffil then begin { file was not loaded, we load and swap so that it ends up on the bottom.} gen2(50(lda),level-outputptr^.vlev,outputptr^.vaddr); if lsp <> nil then if lsp^.form <= subrange then begin if lsp^.size < stackelsize then gen1(72(swp),stackelsize) { size of 2nd is minimum stack } else gen1(72(swp),lsp^.size) { size of 2nd is operand } end else gen1(72(swp),ptrsize); { size of 2nd is pointer } deffil := false end; if txt then begin if sy = colon then begin insymbol; expression(fsys + [comma,colon,rparent]); if gattr.typtr <> nil then if gattr.typtr <> intptr then error(116); load; default := false end else default := true; if sy = colon then begin insymbol; expression(fsys + [comma,rparent]); if gattr.typtr <> nil then if gattr.typtr <> intptr then error(116); if lsp <> realptr then error(124); load; default1 := false end else default1 := true; if lsp = intptr then begin if default then gen2(51(ldc),1,intdeff); gen1(30(csp),6(wri)) end else if lsp = realptr then begin if default1 then begin if default then gen2(51(ldc),1,reldeff); gen1(30(csp),8(wrr)) end else begin if default then gen2(51(ldc),1,reldeff); gen1(30(csp),28(wrf)) end end else if lsp = charptr then begin if default then gen2(51(ldc),1,chrdeff); gen1(30(csp),9(wrc)) end else if lsp = boolptr then begin if default then gen2(51(ldc),1,boldeff); gen1(30(csp),27(wrb)) end else if lsp <> nil then begin if lsp^.form = scalar then error(399) else if string(lsp) then begin len := lsp^.size div charmax; if default then gen2(51(ldc),1,len); gen2(51(ldc),1,len); gen1(30(csp),10(wrs)) end else error(116) end end else begin { binary file } if not comptypes(lsp1^.filtype,lsp) then error(129); if lsp = intptr then gen1(30(csp),31(wbi)) else if lsp = realptr then gen1(30(csp),32(wbr)) else if lsp = charptr then gen1(30(csp),33(wbc)) else if lsp = boolptr then gen1(30(csp),34(wbb)) else if lsp^.form <= subrange then gen1(30(csp),31(wbi)) else if lsp <> nil then begin gen2(51(ldc),1,lsp1^.filtype^.size); gen1(30(csp),30(wbf)) end end; test := sy <> comma; if not test then begin insymbol; expression(fsys + [comma,colon,rparent]) end until test; if sy = rparent then insymbol else error(4) end else begin if not outputhdf then error(176); if lkey = 6 then error(116); gen2(50(lda),level-outputptr^.vlev,outputptr^.vaddr) end; if llkey = 12 then (writeln) gen1(30(csp),22(wln)); { remove the file pointer from stack } gen1(71(dmp),ptrsize); end (write) ;

packprocedure

procedure packprocedure; var lsp,lsp1: stp; lb, bs: integer; lattr: attr; begin variable(fsys + [comma,rparent]); loadaddress; lsp := nil; lsp1 := nil; lb := 1; bs := 1; lattr := gattr; if gattr.typtr <> nil then with gattr.typtr^ do if form = arrays then begin lsp := inxtype; lsp1 := aeltype; if (inxtype = charptr) or (inxtype = boolptr) then lb := 0 else if inxtype^.form = subrange then lb := inxtype^.min.ival; bs := aeltype^.size end else error(116); if sy = comma then insymbol else error(20); expression(fsys + [comma,rparent]); load; if gattr.typtr <> nil then if gattr.typtr^.form <> scalar then error(116) else if not comptypes(lsp,gattr.typtr) then error(116); gen2(51(ldc),1,lb); gen0(21(sbi)); gen2(51(ldc),1,bs); gen0(15(mpi)); if sy = comma then insymbol else error(20); variable(fsys + [rparent]); loadaddress; if gattr.typtr <> nil then with gattr.typtr^ do if form = arrays then begin if not comptypes(aeltype,lsp1) then error(116) end else error(116); if (gattr.typtr <> nil) and (lattr.typtr <> nil) then gen2(62(pck),gattr.typtr^.size,lattr.typtr^.size) end (pack) ;

unpackprocedure

procedure unpackprocedure; var lsp,lsp1: stp; lattr,lattr1: attr; lb, bs: integer; begin variable(fsys + [comma,rparent]); loadaddress; lattr := gattr; lsp := nil; lsp1 := nil; lb := 1; bs := 1; if gattr.typtr <> nil then with gattr.typtr^ do if form = arrays then lsp1 := aeltype else error(116); if sy = comma then insymbol else error(20); variable(fsys + [comma,rparent]); loadaddress; lattr1 := gattr; if gattr.typtr <> nil then with gattr.typtr^ do if form = arrays then begin if not comptypes(aeltype,lsp1) then error(116); if (inxtype = charptr) or (inxtype = boolptr) then lb := 0 else if inxtype^.form = subrange then lb := inxtype^.min.ival; bs := aeltype^.size; lsp := inxtype; end else error(116); if sy = comma then insymbol else error(20); expression(fsys + [rparent]); load; if gattr.typtr <> nil then if gattr.typtr^.form <> scalar then error(116) else if not comptypes(lsp,gattr.typtr) then error(116); gen2(51(ldc),1,lb); gen0(21(sbi)); gen2(51(ldc),1,bs); gen0(15(mpi)); if (gattr.typtr <> nil) and (lattr.typtr <> nil) then gen2(63(upk),lattr.typtr^.size,lattr1.typtr^.size) end (unpack) ;

procedure newdisposeprocedure; label 1; var lsp,lsp1: stp; varts: integer; lsize: addrrange; lval: valu; begin variable(fsys + [comma,rparent]); loadaddress; lsp := nil; varts := 0; lsize := 0; if gattr.typtr <> nil then with gattr.typtr^ do if form = pointer then begin if eltype <> nil then begin lsize := eltype^.size; if eltype^.form = records then lsp := eltype^.recvar end end else error(116); while sy = comma do begin insymbol;constant(fsys + [comma,rparent],lsp1,lval); varts := varts + 1; (check to insert here: is constant in tagfieldtype range) if lsp = nil then error(158) else if lsp^.form <> tagfld then error(162) else if lsp^.tagfieldp <> nil then if string(lsp1) or (lsp1 = realptr) then error(159) else if comptypes(lsp^.tagfieldp^.idtype,lsp1) then begin lsp1 := lsp^.fstvar; while lsp1 <> nil do with lsp1^ do if varval.ival = lval.ival then begin lsize := size; lsp := subvar; goto 1 end else lsp1 := nxtvar; lsize := lsp^.size; lsp := nil; end else error(116); 1: end (while) ; gen2(51(ldc),1,lsize); if lkey = 9 then gen1(30(csp),12(new)) else gen1(30(csp),29(dispose)) end (new) ;

abs

procedure absfunction; begin if gattr.typtr <> nil then if gattr.typtr = intptr then gen0(0(abi)) else if gattr.typtr = realptr then gen0(1(abr)) else begin error(125); gattr.typtr := intptr end end (abs) ;

sqr

procedure sqrfunction; begin if gattr.typtr <> nil then if gattr.typtr = intptr then gen0(24(sqi)) else if gattr.typtr = realptr then gen0(25(sqr)) else begin error(125); gattr.typtr := intptr end end (sqr) ;

trunc

procedure truncfunction; begin if gattr.typtr <> nil then if gattr.typtr <> realptr then error(125); gen0(27(trc)); gattr.typtr := intptr end (trunc) ;

procedure roundfunction; begin if gattr.typtr <> nil then if gattr.typtr <> realptr then error(125); gen0(61(rnd)); gattr.typtr := intptr end (round) ;

procedure oddfunction; begin if gattr.typtr <> nil then if gattr.typtr <> intptr then error(125); gen0(20(odd)); gattr.typtr := boolptr end (odd) ;

ord

procedure ordfunction; begin if gattr.typtr <> nil then if gattr.typtr^.form >= power then error(125); gen0t(58(ord),gattr.typtr); gattr.typtr := intptr end (ord) ;

procedure chrfunction; begin if gattr.typtr <> nil then if gattr.typtr <> intptr then error(125); gen0(59(chr)); gattr.typtr := charptr end (chr) ;

predsuccfunc

procedure predsuccfunction; begin if gattr.typtr <> nil then if gattr.typtr^.form <> scalar then error(125); if lkey = 7 then gen1t(31(dec),1,gattr.typtr) else gen1t(34(inc),1,gattr.typtr) end (predsucc) ;

eofeolnfunction

procedure eofeolnfunction; begin if sy = lparent then begin insymbol; variable(fsys + [rparent]); if sy = rparent then insymbol else error(4) end else begin if not inputhdf then error(175); with gattr do begin typtr := textptr; kind := varbl; access := drct; vlevel := inputptr^.vlev; dplmt := inputptr^.vaddr end end; loadaddress; if gattr.typtr <> nil then if gattr.typtr^.form <> files then error(125) else if (lkey = 10) and (gattr.typtr <> textptr) then error(116); if lkey = 9 then begin if gattr.typtr = textptr then gen0(8(eof)) else gen0(69(efb)) end else gen1(30(csp),14(eln)); gattr.typtr := boolptr end (eof) ;

callnonstandard

procedure callnonstandard; var nxt,lcp: ctp; lsp: stp; lkind: idkind; lb: boolean; locpar, llc: addrrange;

compparam

  procedure compparam(pla, plb: ctp);
  begin
    while (pla <> nil) and (plb <> nil) do begin
      if not comptypes(pla^.idtype,plb^.idtype) then error(189);
      pla := pla^.next; plb := plb^.next
    end;
    if (pla <> nil) or (plb <> nil) then error(189)
  end;

  begin locpar := 0;
    with fcp^ do
      begin nxt := pflist; lkind := pfkind;
        if pfkind = actual then begin { it's a system call }
          if not externl then gen1(41(*mst*),level-pflev)
        end else gen1(41(*mst*),level-pflev) { its an indirect }
      end;
    if sy = lparent then
      begin llc := lc;
        repeat lb := false; (*decide whether proc/func must be passed*)
          if nxt = nil then error(126)
          else lb := nxt^.klass in [proc,func];
          insymbol;
          if lb then   (*pass function or procedure*)
            begin
              if sy <> ident then
                begin error(2); skip(fsys + [comma,rparent]) end
              else if nxt <> nil then
                begin
                  if nxt^.klass = proc then searchid([proc],lcp)
                  else
                    begin searchid([func],lcp);
                      { compare result types }
                      if not comptypes(lcp^.idtype,nxt^.idtype) then
                        error(128)
                    end;
                  { compare parameter lists }
                  if (nxt^.klass in [proc,func]) and 
                     (lcp^.klass in [proc,func]) then
                    compparam(nxt^.pflist, lcp^.pflist);
                  if lcp^.pfkind = actual then genlpa(lcp^.pfname,level-lcp^.pflev)
                  else gen2(74(*lip*),level-lcp^.pflev,lcp^.pfaddr);
                  locpar := locpar+ptrsize*2;
                  insymbol;
                  if not (sy in fsys + [comma,rparent]) then
                    begin error(6); skip(fsys + [comma,rparent]) end
                end
            end (*if lb*)
          else
            begin expression(fsys + [comma,rparent]);
              if gattr.typtr <> nil then
                begin
                  if nxt <> nil then
                    begin lsp := nxt^.idtype;
                      if lsp <> nil then
                        begin
                          if (nxt^.vkind = actual) then
                            if lsp^.form <= power then
                              begin load;
                                if debug then checkbnds(lsp);
                                if comptypes(realptr,lsp)
                                   and (gattr.typtr = intptr) then
                                  begin gen0(10(*flt*));
                                    gattr.typtr := realptr
                                  end;
                                locpar := locpar+lsp^.size;
                                align(parmptr,locpar);
                              end
                            else
                              begin
                                loadaddress;
                                locpar := locpar+ptrsize;
                                align(parmptr,locpar)
                              end
                          else
                            if gattr.kind = varbl then
                              begin loadaddress;
                                locpar := locpar+ptrsize;
                                align(parmptr,locpar);
                              end
                            else error(154);
                          if not comptypes(lsp,gattr.typtr) then
                            error(142)
                        end
                    end
                end
            end;
          if nxt <> nil then nxt := nxt^.next
        until sy <> comma;
        lc := llc;
        if sy = rparent then insymbol else error(4)
      end (*if lparent*);
    if lkind = actual then
      begin if nxt <> nil then error(126);
        with fcp^ do
          begin
            if externl then gen1(30(*csp*),pfname)
            else gencupent(46(*cup*),locpar,pfname);
          end
      end
    else begin { call procedure or function parameter }
      gen2(50(*lda*),level-fcp^.pflev,fcp^.pfaddr);
      gen1(67(*cip*),locpar)
    end;
    gattr.typtr := fcp^.idtype
  end (*callnonstandard*) ;

begin (*call*)
  if fcp^.pfdeckind = standard then
    begin lkey := fcp^.key;
      if fcp^.klass = proc then
        begin
          if not(lkey in [5,6,11,12,17]) then
            if sy = lparent then insymbol else error(9);
          case lkey of
            1,2,
            3,4:   getputresetrewriteprocedure;
            17:    pageprocedure;
            5,11:  readprocedure;
            6,12:  writeprocedure;
            7:     packprocedure;
            8:     unpackprocedure;
            9,18:  newdisposeprocedure;
            10,13: error(399)
          end;
          if not(lkey in [5,6,11,12,17]) then
            if sy = rparent then insymbol else error(4)
        end
      else
        begin
          if (lkey <= 8) or (lkey = 16) then
            begin
              if sy = lparent then insymbol else error(9);
              expression(fsys+[rparent]); load
            end;
          case lkey of
            1:    absfunction;
            2:    sqrfunction;
            3:    truncfunction;
            16:   roundfunction;
            4:    oddfunction;
            5:    ordfunction;
            6:    chrfunction;
            7,8:  predsuccfunction;
            9,10: eofeolnfunction
          end;
          if (lkey <= 8) or (lkey = 16) then
            if sy = rparent then insymbol else error(4)
        end;
    end (*standard procedures and functions*)
  else callnonstandard
end (*call*) ;

expression

procedure expression;
  var lattr: attr; lop: operator; typind: char; lsize: addrrange;

  procedure simpleexpression(fsys: setofsys);
    var lattr: attr; lop: operator; signed: boolean;

    procedure term(fsys: setofsys);
      var lattr: attr; lop: operator;

      procedure factor(fsys: setofsys);
        var lcp: ctp; lvp: csp; varpart: boolean;
            cstpart: setty; lsp: stp;
            tattr, rattr: attr;
            test: boolean;
      begin
        if not (sy in facbegsys) then
          begin error(58); skip(fsys + facbegsys);
            gattr.typtr := nil
          end;
        while sy in facbegsys do
          begin
            case sy of
      (*id*)    ident:
                begin searchid([konst,vars,field,func],lcp);
                  insymbol;
                  if lcp^.klass = func then
                    begin call(fsys,lcp);
                      with gattr do
                        begin kind := expr;
                          if typtr <> nil then
                            if typtr^.form=subrange then
                              typtr := typtr^.rangetype
                        end
                    end
                  else
                    if lcp^.klass = konst then
                      with gattr, lcp^ do
                        begin typtr := idtype; kind := cst;
                          cval := values
                        end
                    else
                      begin selector(fsys,lcp);
                        if gattr.typtr<>nil then(*elim.subr.types to*)
                          with gattr,typtr^ do(*simplify later tests*)
                            if form = subrange then
                              typtr := rangetype
                      end
                end;
      (*cst*)   intconst:
                begin
                  with gattr do
                    begin typtr := intptr; kind := cst;
                      cval := val
                    end;
                  insymbol
                end;
                realconst:
                begin
                  with gattr do
                    begin typtr := realptr; kind := cst;
                      cval := val
                    end;
                  insymbol
                end;
                stringconst:
                begin
                  with gattr do
                    begin
                      if lgth = 1 then typtr := charptr
                      else
                        begin new(lsp,arrays); pshstc(lsp);
                          with lsp^ do
                            begin aeltype := charptr; form:=arrays;
                              packing := true;
                              inxtype := nil; size := lgth*charsize
                            end;
                          typtr := lsp
                        end;
                      kind := cst; cval := val
                    end;
                  insymbol
                end;
      (* ( *)   lparent:
                begin insymbol; expression(fsys + [rparent]);
                  if sy = rparent then insymbol else error(4)
                end;
      (*not*)   notsy:
                begin insymbol; factor(fsys);
                  load; gen0(19(*not*));
                  if gattr.typtr <> nil then
                    if gattr.typtr <> boolptr then
                      begin error(135); gattr.typtr := nil end;
                end;
      (*[*)     lbrack:
                begin insymbol; cstpart := [ ]; varpart := false;
                  new(lsp,power); pshstc(lsp);
                  with lsp^ do
                    begin elset:=nil;size:=setsize;form:=power; 
                          packing := false; matchpack := false end;
                  if sy = rbrack then
                    begin
                      with gattr do
                        begin typtr := lsp; kind := cst end;
                      insymbol
                    end
                  else
                    begin
                      repeat expression(fsys + [comma,range,rbrack]);
                        rattr.typtr := nil;
                        if sy = range then begin insymbol; 
                          { if the left side is not constant, load it
                            and coerce it to integer now }
                          if gattr.kind <> cst then begin
                            load;
                            if not comptypes(gattr.typtr,intptr)
                            then gen0t(58(*ord*),gattr.typtr);
                          end;
                          tattr := gattr; expression(fsys + [comma,rbrack]);
                          rattr := gattr; gattr := tattr;
                        end;
                        if gattr.typtr <> nil then
                          if gattr.typtr^.form <> scalar then
                            begin error(136); gattr.typtr := nil end
                          else
                            if comptypes(lsp^.elset,gattr.typtr) then
                              begin
                                if rattr.typtr <> nil then begin { x..y form }
                                  if rattr.typtr^.form <> scalar then
                                    begin error(136); rattr.typtr := nil end
                                  else
                                    if comptypes(lsp^.elset,rattr.typtr) then
                                      begin
                                        if (gattr.kind = cst) and 
                                           (rattr.kind = cst) then
                                          if (lattr.cval.ival < setlow) or
                                             (lattr.cval.ival > sethigh) or
                                             (gattr.cval.ival < setlow) or
                                             (gattr.cval.ival > sethigh) then
                                            error(304)
                                          else
                                            cstpart := cstpart+
                                              [gattr.cval.ival..rattr.cval.ival]
                                        else
                                          begin 
                                            tattr := gattr; gattr := rattr;
                                            load;
                                            gattr := tattr;
                                            if not comptypes(rattr.typtr,intptr)
                                            then gen0t(58(*ord*),rattr.typtr);
                                            gen0(64(*rgs*));
                                            if varpart then gen0(28(*uni*))
                                            else varpart := true
                                          end
                                      end
                                    else error(137)
                                end else begin
                                  if gattr.kind = cst then
                                    if (gattr.cval.ival < setlow) or
                                      (gattr.cval.ival > sethigh) then
                                      error(304)
                                    else
                                      cstpart := cstpart+[gattr.cval.ival]
                                  else
                                    begin load;
                                      if not comptypes(gattr.typtr,intptr)
                                      then gen0t(58(*ord*),gattr.typtr);
                                      gen0(23(*sgs*));
                                      if varpart then gen0(28(*uni*))
                                      else varpart := true
                                    end
                                end;
                                lsp^.elset := gattr.typtr;
                                gattr.typtr := lsp
                              end
                            else error(137);
                        test := sy <> comma;
                        if not test then insymbol
                      until test;
                      if sy = rbrack then insymbol else error(12)
                    end;
                  if varpart then
                    begin
                      if cstpart <> [ ] then
                        begin new(lvp,pset); pshcst(lvp);
                          lvp^.pval := cstpart;
                          lvp^.cclass := pset;
                          if cstptrix = cstoccmax then error(254)
                          else
                            begin cstptrix := cstptrix + 1;
                              cstptr[cstptrix] := lvp;
                              gen2(51(*ldc*),5,cstptrix);
                              gen0(28(*uni*)); gattr.kind := expr
                            end
                        end
                    end
                  else
                    begin new(lvp,pset); pshcst(lvp);
                      lvp^.pval := cstpart;
                      lvp^.cclass := pset;
                      gattr.cval.valp := lvp
                    end
                end;
      (*nil*)   nilsy: with gattr do
                         begin typtr := nilptr; kind := cst;
                               cval.ival := nilval;
                               insymbol
                         end
            end (*case*) ;
            if not (sy in fsys) then
              begin error(6); skip(fsys + facbegsys) end
          end (*while*)
      end (*factor*) ;

term

begin (*term*)
  factor(fsys + [mulop]);
  while sy = mulop do
    begin load; lattr := gattr; lop := op;
      insymbol; factor(fsys + [mulop]); load;
      if (lattr.typtr <> nil) and (gattr.typtr <> nil) then
        case lop of
(***)     mul:  if (lattr.typtr=intptr)and(gattr.typtr=intptr)
                then gen0(15(*mpi*))
                else
                  begin
                    if lattr.typtr = intptr then
                      begin gen0(9(*flo*));
                        lattr.typtr := realptr
                      end
                    else
                      if gattr.typtr = intptr then
                        begin gen0(10(*flt*));
                          gattr.typtr := realptr
                        end;
                    if (lattr.typtr = realptr)
                      and(gattr.typtr=realptr)then gen0(16(*mpr*))
                    else
                      if(lattr.typtr^.form=power)
                        and comptypes(lattr.typtr,gattr.typtr)then
                        gen0(12(*int*))
                      else begin error(134); gattr.typtr:=nil end
                  end;
(* / *)   rdiv: begin
                  if gattr.typtr = intptr then
                    begin gen0(10(*flt*));
                      gattr.typtr := realptr
                    end;
                  if lattr.typtr = intptr then
                    begin gen0(9(*flo*));
                      lattr.typtr := realptr
                    end;
                  if (lattr.typtr = realptr)
                    and (gattr.typtr=realptr)then gen0(7(*dvr*))
                  else begin error(134); gattr.typtr := nil end
                end;
(*div*)   idiv: if (lattr.typtr = intptr)
                  and (gattr.typtr = intptr) then gen0(6(*dvi*))
                else begin error(134); gattr.typtr := nil end;
(*mod*)   imod: if (lattr.typtr = intptr)
                  and (gattr.typtr = intptr) then gen0(14(*mod*))
                else begin error(134); gattr.typtr := nil end;
(*and*)   andop:if (lattr.typtr = boolptr)
                  and (gattr.typtr = boolptr) then gen0(4(*and*))
                else begin error(134); gattr.typtr := nil end
        end (*case*)
      else gattr.typtr := nil
    end (*while*)
end (*term*) ;

simple expression

begin (*simpleexpression*)
  signed := false;
  if (sy = addop) and (op in [plus,minus]) then
    begin signed := op = minus; insymbol end;
  term(fsys + [addop]);
  if signed then
    begin load;
      if gattr.typtr = intptr then gen0(17(*ngi*))
      else
        if gattr.typtr = realptr then gen0(18(*ngr*))
        else begin error(134); gattr.typtr := nil end
    end;
  while sy = addop do
    begin load; lattr := gattr; lop := op;
      insymbol; term(fsys + [addop]); load;
      if (lattr.typtr <> nil) and (gattr.typtr <> nil) then
        case lop of
(*+*)       plus:
            if (lattr.typtr = intptr)and(gattr.typtr = intptr) then
              gen0(2(*adi*))
            else
              begin
                if lattr.typtr = intptr then
                  begin gen0(9(*flo*));
                    lattr.typtr := realptr
                  end
                else
                  if gattr.typtr = intptr then
                    begin gen0(10(*flt*));
                      gattr.typtr := realptr
                    end;
                if (lattr.typtr = realptr)and(gattr.typtr = realptr)
                  then gen0(3(*adr*))
                else if(lattr.typtr^.form=power)
                       and comptypes(lattr.typtr,gattr.typtr) then
                       gen0(28(*uni*))
                     else begin error(134); gattr.typtr:=nil end
              end;
(*-*)       minus:
            if (lattr.typtr = intptr)and(gattr.typtr = intptr) then
              gen0(21(*sbi*))
            else
              begin
                if lattr.typtr = intptr then
                  begin gen0(9(*flo*));
                    lattr.typtr := realptr
                  end
                else
                  if gattr.typtr = intptr then
                    begin gen0(10(*flt*));
                      gattr.typtr := realptr
                    end;
                if (lattr.typtr = realptr)and(gattr.typtr = realptr)
                  then gen0(22(*sbr*))
                else
                  if (lattr.typtr^.form = power)
                    and comptypes(lattr.typtr,gattr.typtr) then
                    gen0(5(*dif*))
                  else begin error(134); gattr.typtr := nil end
              end;
(*or*)      orop:
            if(lattr.typtr=boolptr)and(gattr.typtr=boolptr)then
              gen0(13(*ior*))
            else begin error(134); gattr.typtr := nil end
        end (*case*)
      else gattr.typtr := nil
    end (*while*)
end (*simpleexpression*) ;

expression

begin (*expression*)
  simpleexpression(fsys + [relop]);
  if sy = relop then
    begin
      if gattr.typtr <> nil then
        if gattr.typtr^.form <= power then load
        else loadaddress;
      lattr := gattr; lop := op;
      if lop = inop then
        if not comptypes(gattr.typtr,intptr) then
          gen0t(58(*ord*),gattr.typtr);
      insymbol; simpleexpression(fsys);
      if gattr.typtr <> nil then
        if gattr.typtr^.form <= power then load
        else loadaddress;
      if (lattr.typtr <> nil) and (gattr.typtr <> nil) then
        if lop = inop then
          if gattr.typtr^.form = power then
            if comptypes(lattr.typtr,gattr.typtr^.elset) then
              gen0(11(*inn*))
            else begin error(129); gattr.typtr := nil end
          else begin error(130); gattr.typtr := nil end
        else
          begin
            if lattr.typtr <> gattr.typtr then
              if lattr.typtr = intptr then
                begin gen0(9(*flo*));
                  lattr.typtr := realptr
                end
              else
                if gattr.typtr = intptr then
                  begin gen0(10(*flt*));
                    gattr.typtr := realptr
                  end;
            if comptypes(lattr.typtr,gattr.typtr) then
              begin lsize := lattr.typtr^.size;
                case lattr.typtr^.form of
                  scalar:
                    if lattr.typtr = realptr then typind := 'r'
                    else
                      if lattr.typtr = boolptr then typind := 'b'
                      else
                        if lattr.typtr = charptr then typind := 'c'
                        else typind := 'i';
                  pointer:
                    begin
                      if lop in [ltop,leop,gtop,geop] then error(131);
                      typind := 'a'
                    end;
                  power:
                    begin if lop in [ltop,gtop] then error(132);
                      typind := 's'
                    end;
                  arrays:
                    begin
                      if not string(lattr.typtr)
                        then error(134);
                      typind := 'm'
                    end;
                  records:
                    begin
                      error(134);
                      typind := 'm'
                    end;
                  files:
                    begin error(133); typind := 'f' end
                end;
                case lop of
                  ltop: gen2(53(*les*),ord(typind),lsize);
                  leop: gen2(52(*leq*),ord(typind),lsize);
                  gtop: gen2(49(*grt*),ord(typind),lsize);
                  geop: gen2(48(*geq*),ord(typind),lsize);
                  neop: gen2(55(*neq*),ord(typind),lsize);
                  eqop: gen2(47(*equ*),ord(typind),lsize)
                end
              end
            else error(129)
          end;
      gattr.typtr := boolptr; gattr.kind := expr
    end (*sy = relop*)
end (*expression*) ;

assignment

        procedure assignment(fcp: ctp);
          var lattr: attr;
        begin selector(fsys + [becomes],fcp);
          if sy = becomes then
            begin
              if gattr.typtr <> nil then
                if (gattr.access<>drct) or (gattr.typtr^.form>power) then
                  loadaddress;
              lattr := gattr;
              insymbol; expression(fsys);
              if gattr.typtr <> nil then
                if gattr.typtr^.form <= power then load
                else loadaddress;
              if (lattr.typtr <> nil) and (gattr.typtr <> nil) then
                begin
                  if comptypes(realptr,lattr.typtr)and(gattr.typtr=intptr)then
                    begin gen0(10(*flt*));
                      gattr.typtr := realptr
                    end;
                  if comptypes(lattr.typtr,gattr.typtr) then begin
                    if filecomponent(gattr.typtr) then error(191);
                    case lattr.typtr^.form of
                      scalar,
                      subrange: begin
                                  if debug then checkbnds(lattr.typtr);
                                  store(lattr)
                                end;
                      pointer: begin
                                 if debug then
                                   gen2t(45(*chk*),0,maxaddr,nilptr);
                                 store(lattr)
                               end;
                      power:   store(lattr);
                      arrays,
                      records: gen1(40(*mov*),lattr.typtr^.size);
                      files: error(146)
                    end
                  end else error(129)
                end
            end (*sy = becomes*)
          else error(51)
        end (*assignment*) ;
goto statement
        procedure gotostatement;
          var llp: lbp; ttop,ttop1: disprange;

        begin
          if sy = intconst then
            begin
              ttop := top;
              while display[ttop].occur <> blck do ttop := ttop - 1;
              ttop1 := ttop;
              repeat
                searchlabel(llp, ttop); { find label }
                if llp <> nil then with llp^ do begin
                  if defined then
                    if slevel > stalvl then { defining point level greater than
                                              present statement level }
                      error(185) { goto references deeper nested statement }
                    else if (slevel > 1) and not bact then 
                      error(187); { Goto references label in different nested
                                    statement }
                  { establish the minimum statement level a goto appeared at }
                  if minlvl > stalvl then minlvl := stalvl;
                  if ttop = ttop1 then
                    genujpxjp(57(*ujp*),labname) 
                  else begin { interprocedural goto }
                    genipj(66(*ipj*),level-vlevel,labname);
                    ipcref := true
                  end
                end;
                ttop := ttop - 1
              until (llp <> nil) or (ttop = 0);
              if llp = nil then begin
                error(167); { undeclared label }
                newlabel(llp) { create dummy label in current context }
              end;
              insymbol
            end
          else error(15)
        end (*gotostatement*) ;

compound statement

procedure compoundstatement;
var test: boolean;
begin
  addlvl;
  repeat
    repeat statement(fsys + [semicolon,endsy])
    until not (sy in statbegsys);
    test := sy <> semicolon;
    if not test then insymbol
  until test;
  if sy = endsy then insymbol else error(13);
  sublvl
end (*compoundstatemenet*) ;

if statement

procedure ifstatement; var lcix1,lcix2: integer; begin expression(fsys + [thensy]); genlabel(lcix1); genfjp(lcix1); if sy = thensy then insymbol else error(52); addlvl; statement(fsys + [elsesy]); sublvl; if sy = elsesy then begin genlabel(lcix2); genujpxjp(57(ujp),lcix2); putlabel(lcix1); insymbol; addlvl; statement(fsys); sublvl; putlabel(lcix2) end else putlabel(lcix1) end (ifstatement) ;

case statement

procedure casestatement;
  label 1;
  var lsp,lsp1: stp; fstptr,lpt1,lpt2,lpt3: cip; lval: valu;
      laddr, lcix, lcix1, lmin, lmax: integer;
      test: boolean;
begin expression(fsys + [ofsy,comma,colon]);
  load; genlabel(lcix);
  lsp := gattr.typtr;
  if lsp <> nil then
    if (lsp^.form <> scalar) or (lsp = realptr) then
      begin error(144); lsp := nil end
    else if not comptypes(lsp,intptr) then gen0t(58(*ord*),lsp);
  genujpxjp(57(*ujp*),lcix);
  if sy = ofsy then insymbol else error(8);
  fstptr := nil; genlabel(laddr);
  repeat
    lpt3 := nil; genlabel(lcix1);
    if not(sy in [semicolon,endsy]) then
      begin
        repeat constant(fsys + [comma,colon],lsp1,lval);
          if lsp <> nil then
            if comptypes(lsp,lsp1) then
              begin lpt1 := fstptr; lpt2 := nil;
                while lpt1 <> nil do
                  with lpt1^ do
                    begin
                      if cslab <= lval.ival then
                        begin if cslab = lval.ival then error(156);
                          goto 1
                        end;
                      lpt2 := lpt1; lpt1 := next
                    end;
    1:    getcas(lpt3);
                with lpt3^ do
                  begin next := lpt1; cslab := lval.ival;
                    csstart := lcix1
                  end;
                if lpt2 = nil then fstptr := lpt3
                else lpt2^.next := lpt3
              end
            else error(147);
          test := sy <> comma;
          if not test then insymbol
        until test;
        if sy = colon then insymbol else error(5);
        putlabel(lcix1);
        repeat 
          addlvl;
          statement(fsys + [semicolon]);
          sublvl
        until not (sy in statbegsys);
        if lpt3 <> nil then
          genujpxjp(57(*ujp*),laddr);
      end;
    test := sy <> semicolon;
    if not test then insymbol
  until test;
  putlabel(lcix);
  if fstptr <> nil then
    begin lmax := fstptr^.cslab;
      (*reverse pointers*)
      lpt1 := fstptr; fstptr := nil;
      repeat lpt2 := lpt1^.next; lpt1^.next := fstptr;
        fstptr := lpt1; lpt1 := lpt2
      until lpt1 = nil;
      lmin := fstptr^.cslab;
      if lmax - lmin < cixmax then
        begin
          gen2t(45(*chk*),lmin,lmax,intptr);
          gen2(51(*ldc*),1,lmin); gen0(21(*sbi*)); genlabel(lcix);
          genujpxjp(44(*xjp*),lcix); putlabel(lcix);
          repeat
            with fstptr^ do
              begin
                while cslab > lmin do
                   begin gen0(60(*ujc error*));
                     lmin := lmin+1
                   end;
                genujpxjp(57(*ujp*),csstart);
                lpt1 := fstptr; fstptr := next; lmin := lmin + 1;
                putcas(lpt1);
              end
          until fstptr = nil;
          putlabel(laddr)
        end
      else begin
        error(157);
        repeat
          with fstptr^ do
            begin
              lpt1 := fstptr; fstptr := next;
              putcas(lpt1);
            end
        until fstptr = nil
      end
    end;
    if sy = endsy then insymbol else error(13)
end (*casestatement*) ;

repeat

procedure repeatstatement;
  var laddr: integer;
begin genlabel(laddr); putlabel(laddr);
  repeat 
    addlvl;
    statement(fsys + [semicolon,untilsy]);
    sublvl;
    if sy in statbegsys then error(14)
  until not(sy in statbegsys);
  while sy = semicolon do
    begin insymbol;
      repeat 
        addlvl;
        statement(fsys + [semicolon,untilsy]);
        if sy in statbegsys then error(14);
        sublvl
      until not (sy in statbegsys);
    end;
  if sy = untilsy then
    begin insymbol; expression(fsys); genfjp(laddr)
    end
  else error(53);
end (*repeatstatement*) ;

while

procedure whilestatement;
  var laddr, lcix: integer;
begin genlabel(laddr); putlabel(laddr);
  expression(fsys + [dosy]); genlabel(lcix); genfjp(lcix);
  if sy = dosy then insymbol else error(54);
  addlvl;
  statement(fsys); 
  sublvl;
  genujpxjp(57(*ujp*),laddr); putlabel(lcix)
end (*whilestatement*) ;

forstmt

procedure forstatement;
  var lattr: attr;  lsy: symbol;
      lcix, laddr: integer;
            llc, lcs: addrrange;
      typind: char; (* added for typing [sam] *)
begin llc := lc;
  with lattr do
    begin typtr := nil; kind := varbl;
      access := drct; vlevel := level; dplmt := 0
    end;
  typind := 'i'; (* default to integer [sam] *)
  if sy = ident then
    begin searchid([vars],lcp);
      with lcp^, lattr do
        begin typtr := idtype; kind := varbl;
          if vkind = actual then
            begin access := drct; vlevel := vlev;
              if vlev <> level then error(183);
              dplmt := vaddr
            end
          else begin error(155); typtr := nil end
        end;
      (* determine type of control variable [sam] *)
      if lattr.typtr = boolptr then typind := 'b'
      else if lattr.typtr = charptr then typind := 'c';
      if lattr.typtr <> nil then
        if (lattr.typtr^.form > subrange)
           or comptypes(realptr,lattr.typtr) then
          begin error(143); lattr.typtr := nil end;
      insymbol
    end
  else
    begin error(2); skip(fsys + [becomes,tosy,downtosy,dosy]) end;
  if sy = becomes then
    begin insymbol; expression(fsys + [tosy,downtosy,dosy]);
      if gattr.typtr <> nil then
          if gattr.typtr^.form <> scalar then error(144)
          else
            if comptypes(lattr.typtr,gattr.typtr) then begin
              load; align(intptr,lc);
              { store start to temp }
              gen2t(56(*str*),0,lc,intptr);
            end else error(145)
    end
  else
    begin error(51); skip(fsys + [tosy,downtosy,dosy]) end;
  if sy in [tosy,downtosy] then
    begin lsy := sy; insymbol; expression(fsys + [dosy]);
      if gattr.typtr <> nil then
      if gattr.typtr^.form <> scalar then error(144)
        else
          if comptypes(lattr.typtr,gattr.typtr) then
            begin
              load; align(intptr,lc);
              if not comptypes(lattr.typtr,intptr) then
                gen0t(58(*ord*),gattr.typtr);
              gen2t(56(*str*),0,lc+intsize,intptr);
              { set initial value of index }
              gen2t(54(*lod*),0,lc,intptr);
              store(lattr);
              genlabel(laddr); putlabel(laddr);
              gattr := lattr; load;
              if not comptypes(gattr.typtr,intptr) then
                gen0t(58(*ord*),gattr.typtr);
              gen2t(54(*lod*),0,lc+intsize,intptr);
              lcs := lc;
              lc := lc + intsize + intsize;
              if lc > lcmax then lcmax := lc;
              if lsy = tosy then gen2(52(*leq*),ord(typind),1)
              else gen2(48(*geq*),ord(typind),1);
            end
          else error(145)
    end
  else begin error(55); skip(fsys + [dosy]) end;
  genlabel(lcix); genujpxjp(33(*fjp*),lcix);
  if sy = dosy then insymbol else error(54);
  addlvl;
  statement(fsys);
  sublvl;
  gattr := lattr; load;
  if not comptypes(gattr.typtr,intptr) then
    gen0t(58(*ord*),gattr.typtr);
  gen2t(54(*lod*),0,lcs+intsize,intptr);
  gen2(47(*equ*),ord(typind),1);
  genujpxjp(73(*tjp*),lcix);
  gattr := lattr; load;
  if lsy=tosy then gen1t(34(*inc*),1,gattr.typtr)
  else  gen1t(31(*dec*),1,gattr.typtr);
  store(lattr); genujpxjp(57(*ujp*),laddr); putlabel(lcix);
  lc := llc;
end (*forstatement*) ;

with

procedure withstatement;
  var lcp: ctp; lcnt1: disprange; llc: addrrange;
      test: boolean;
begin lcnt1 := 0; llc := lc;
 repeat
    if sy = ident then
      begin searchid([vars,field],lcp); insymbol end
    else begin error(2); lcp := uvarptr end;
    selector(fsys + [comma,dosy],lcp);
    if gattr.typtr <> nil then
      if gattr.typtr^.form = records then
        if top < displimit then
          begin top := top + 1; lcnt1 := lcnt1 + 1;
            with display[top] do
              begin fname := gattr.typtr^.fstfld;
                flabel := nil;
                flabel := nil;
                fconst := nil;
                fstruct := nil;
              end;
            if gattr.access = drct then
              with display[top] do
                begin occur := crec; clev := gattr.vlevel;
                  cdspl := gattr.dplmt
                end
            else
              begin loadaddress;
                align(nilptr,lc);
                gen2t(56(*str*),0,lc,nilptr);
                with display[top] do
                  begin occur := vrec; vdspl := lc end;
                lc := lc+ptrsize;
                if lc > lcmax then lcmax := lc
              end
          end
        else error(250)
      else error(140);
    test := sy <> comma;
    if not test then insymbol
  until test;
  if sy = dosy then insymbol else error(54);
  addlvl;
  statement(fsys);
  sublvl;
  { purge display levels }
  while lcnt1 > 0 do begin
     { don't recycle the record context }
     display[top].fname := nil;
     putdsp(top); { purge }
     top := top-1; lcnt1 := lcnt1-1; { count off }
  end;
  lc := llc;
end (*withstatement*) ;

statement main

begin (*statement*)
  if sy = intconst then (*label*)
    begin 
      searchlabel(llp, level); { search label }
      if llp <> nil then with llp^ do begin { found }
        if defined then error(165); { multidefined label }
        bact := true; { set in active block now }
        slevel := stalvl; { establish statement level }
        defined := true; { set defined }
        if ipcref and (stalvl > 1) then 
          error(184) { intraprocedure goto does not reference outter block }
        else if minlvl < stalvl then 
          error(186); { label referenced by goto at lesser statement level }
        putlabel(labname); { output label to intermediate }
      end else begin { not found }
        error(167); { undeclared label }
        newlabel(llp) { create a dummy level }
      end;
      insymbol;
      if sy = colon then insymbol else error(5)
    end;
  if not (sy in fsys + [ident]) then
    begin error(6); skip(fsys) end;
  if sy in statbegsys + [ident] then
    begin
      case sy of
        ident:    begin searchid([vars,field,func,proc],lcp); insymbol;
                    if lcp^.klass = proc then call(fsys,lcp)
                    else assignment(lcp)
                  end;
        beginsy:  begin insymbol; compoundstatement end;
        gotosy:   begin insymbol; gotostatement end;
        ifsy:     begin insymbol; ifstatement end;
        casesy:   begin insymbol; casestatement end;
        whilesy:  begin insymbol; whilestatement end;
        repeatsy: begin insymbol; repeatstatement end;
        forsy:    begin insymbol; forstatement end;
        withsy:   begin insymbol; withstatement end
      end;
      if not (sy in [semicolon,endsy,elsesy,untilsy]) then
        begin error(6); skip(fsys) end
    end
end (*statement*) ;

body main

begin (*body*)
  if fprocp <> nil then entname := fprocp^.pfname
  else genlabel(entname);
  cstptrix := 0; topnew := lcaftermarkstack; topmax := lcaftermarkstack;
  putlabel(entname); genlabel(segsize); genlabel(stacktop);
  gencupent(32(*ent1*),1,segsize); gencupent(32(*ent2*),2,stacktop);
  if fprocp <> nil then (*copy multiple values into local cells*)
    begin llc1 := lcaftermarkstack;
      lcp := fprocp^.pflist;
      while lcp <> nil do
        with lcp^ do
          begin
            align(parmptr,llc1);
            if klass = vars then
              if idtype <> nil then
                if idtype^.form > power then
                  begin
                    if vkind = actual then
                      begin
                        gen2(50(*lda*),0,vaddr);
                        gen2t(54(*lod*),0,llc1,nilptr);
                        gen1(40(*mov*),idtype^.size);
                      end;
                    llc1 := llc1 + ptrsize
                  end
                else llc1 := llc1 + idtype^.size;
            lcp := lcp^.next;
          end;
    end;
  lcmax := lc;
  addlvl;
  repeat
    repeat statement(fsys + [semicolon,endsy])
    until not (sy in statbegsys);
    test := sy <> semicolon;
    if not test then insymbol
  until test;
  sublvl;
  if sy = endsy then insymbol else error(13);
  llp := display[top].flabel; (*test for undefined labels*)
  while llp <> nil do
    with llp^ do
      begin
        if not defined then
          begin error(168);
            writeln(output); writeln(output,' label ',labval);
            write(output,' ':chcnt+16)
          end;
        llp := nextlab
      end;
  if fprocp <> nil then
    begin
      if fprocp^.idtype = nil then gen1(42(*ret*),ord('p'))
      else gen0t(42(*ret*),fprocp^.idtype);
      align(parmptr,lcmax);
      if prcode then
        begin writeln(prr,'l',segsize:4,'=',lcmax);
          writeln(prr,'l',stacktop:4,'=',topmax)
        end
    end
  else
    begin gen1(42(*ret*),ord('p'));
      align(parmptr,lcmax);
      if prcode then
        begin writeln(prr,'l',segsize:4,'=',lcmax);
          writeln(prr,'l',stacktop:4,'=',topmax);
          writeln(prr,'q')
        end;
      ic := 0;
      (*generate call of main program; note that this call must be loaded
        at absolute address zero*)
      gen1(41(*mst*),0); gencupent(46(*cup*),0,entname); gen0(29(*stp*));
      if prcode then
        writeln(prr,'q');
      saveid := id;
      while fextfilep <> nil do
        begin
          with fextfilep^ do
            if not (strequri('input    ', filename) or 
                    strequri('output   ', filename) or
                    strequri('prd      ', filename) or 
                    strequri('prr      ', filename))
            then begin id := filename;
                   { output general error for undefined external file }
                   writeln(output);
                   writeln(output,'**** Error: external file unknown ''',
                                  fextfilep^.filename:8, '''');
                   toterr := toterr+1;
                   { hold the error in case not found, since this error
                     occurs far from the original symbol }
                   searchidne([vars],llcp);
                   if llcp = nil then begin
                     { a header file was never defined in a var statement }
                     writeln(output);
                     writeln(output,'**** Error: Undeclared external file ''',
                                    fextfilep^.filename:8, '''');
                     toterr := toterr+1;
                     llcp := uvarptr
                   end;
                   if llcp^.idtype<>nil then
                     if llcp^.idtype^.form<>files then
                       begin writeln(output);
                         writeln(output,'**** Error: Undeclared external file ''',
                                        fextfilep^.filename:8, '''');
                         toterr := toterr+1
                       end
                 end;
            fp := fextfilep; fextfilep := fextfilep^.nextfile; putfil(fp);
        end;
      id := saveid;
      if prtables then
        begin writeln(output); printtables(true)
        end
    end;
end (*body*) ;

block main

begin (*block*)
  stalvl := 0; { clear statement nesting level }
  dp := true;
  repeat
    if sy = labelsy then
      begin insymbol; labeldeclaration end;
    if sy = constsy then
      begin insymbol; constdeclaration end;
    if sy = typesy then
      begin insymbol; typedeclaration end;
    if sy = varsy then
      begin insymbol; vardeclaration end;
    while sy in [procsy,funcsy] do
      begin lsy := sy; insymbol; procdeclaration(lsy) end;
    if sy <> beginsy then
      begin error(18); skip(fsys) end
  until (sy in statbegsys) or eof(input);
  dp := false;
  if sy = beginsy then insymbol else error(17);
  repeat body(fsys + [casesy]);
    if sy <> fsy then
      begin error(6); skip(fsys) end
  until ((sy = fsy) or (sy in blockbegsys)) or eof(input);
end (*block*) ;

<program>

procedure programme(fsys:setofsys);
  var extfp:extfilep;
begin
  if sy = progsy then
    begin insymbol; if sy <> ident then error(2) else insymbol;
      if not (sy in [lparent,semicolon]) then error(14);
      if sy = lparent  then
        begin
          repeat insymbol;
            if sy = ident then
              begin getfil(extfp);
                with extfp^ do
                  begin filename := id; nextfile := fextfilep end;
                fextfilep := extfp;
                { check 'input' or 'output' appears in header for defaults }
                if strequri('input    ', id) then inputhdf := true
                else if strequri('output   ', id) then outputhdf := true;
                insymbol;
                if not ( sy in [comma,rparent] ) then error(20)
              end
            else error(2)
          until sy <> comma;
          if sy <> rparent then error(4);
          insymbol;
          if sy <> semicolon then error(14)
        end;
      if sy = semicolon then insymbol
    end else error(3);
  repeat block(fsys,period,nil);
    if sy <> period then error(21)
  until (sy = period) or eof(input);
  if list then writeln(output);
  if errinx <> 0 then
    begin list := false; endofline end;
end (*programme*) ;

standard names

procedure stdnames;
begin
  { 'mark' and 'release' were removed and replaced with placeholders }
  na[  1] := 'false    '; na[  2] := 'true     '; na[  3] := 'input    ';
  na[  4] := 'output   '; na[  5] := 'get      '; na[  6] := 'put      ';
  na[  7] := 'reset    '; na[  8] := 'rewrite  '; na[  9] := 'read     ';
  na[ 10] := 'write    '; na[ 11] := 'pack     '; na[ 12] := 'unpack   ';
  na[ 13] := 'new      '; na[ 14] := '---      '; na[ 15] := 'readln   ';
  na[ 16] := 'writeln  ';
  na[ 17] := 'abs      '; na[ 18] := 'sqr      '; na[ 19] := 'trunc    ';
  na[ 20] := 'odd      '; na[ 21] := 'ord      '; na[ 22] := 'chr      ';
  na[ 23] := 'pred     '; na[ 24] := 'succ     '; na[ 25] := 'eof      ';
  na[ 26] := 'eoln     ';
  na[ 27] := 'sin      '; na[ 28] := 'cos      '; na[ 29] := 'exp      ';
  na[ 30] := 'sqrt     '; na[ 31] := 'ln       '; na[ 32] := 'arctan   ';
  na[ 33] := 'prd      '; na[ 34] := 'prr      '; na[ 35] := '---      ';
  na[ 36] := 'maxint   '; na[ 37] := 'round    '; na[ 38] := 'page     ';
  na[ 39] := 'dispose  ';
end (*stdnames*) ;

enter standard types

procedure enterstdtypes;

begin                                                 (*type underlying:*)
                                                      (******************)

  new(intptr,scalar,standard); pshstc(intptr);               (*integer*)
  with intptr^ do
    begin size := intsize; form := scalar; scalkind := standard end;
  new(realptr,scalar,standard); pshstc(realptr);             (*real*)
  with realptr^ do
    begin size := realsize; form := scalar; scalkind := standard end;
  new(charptr,scalar,standard); pshstc(charptr);             (*char*)
  with charptr^ do
    begin size := charsize; form := scalar; scalkind := standard end;
  new(boolptr,scalar,declared); pshstc(boolptr);             (*boolean*)
  with boolptr^ do
    begin size := boolsize; form := scalar; scalkind := declared end;
  new(nilptr,pointer); pshstc(nilptr);                (*nil*)
  with nilptr^ do
    begin eltype := nil; size := ptrsize; form := pointer end;
  (*for alignment of parameters*)
  new(parmptr,scalar,standard); pshstc(parmptr);
  with parmptr^ do
    begin size := parmsize; form := scalar; scalkind := standard end ;
  new(textptr,files); pshstc(textptr);                (*text*)
  with textptr^ do
    begin filtype := charptr; size := filesize+charsize; form := files end
end (*enterstdtypes*) ;

enter standard names

procedure entstdnames;
  var cp,cp1: ctp; i: integer;

begin                                                       (*name:*)
                                                            (*******)

  new(cp,types); ininam(cp);                                (*integer*)
  with cp^ do
    begin strassvr(name, 'integer  '); idtype := intptr; klass := types end;
  enterid(cp);
  new(cp,types); ininam(cp);                                (*real*)
  with cp^ do
    begin strassvr(name, 'real     '); idtype := realptr; klass := types end;
  enterid(cp);
  new(cp,types); ininam(cp);                                (*char*)
  with cp^ do
    begin strassvr(name, 'char     '); idtype := charptr; klass := types end;
  enterid(cp);
  new(cp,types); ininam(cp);                                (*boolean*)
  with cp^ do
    begin strassvr(name, 'boolean  '); idtype := boolptr; klass := types end;
  enterid(cp);
  new(cp,types); ininam(cp);                                (*text*)
  with cp^ do
    begin strassvr(name, 'text     '); idtype := textptr; klass := types end;
  enterid(cp);
  cp1 := nil;
  for i := 1 to 2 do
    begin new(cp,konst); ininam(cp);                        (*false,true*)
      with cp^ do
        begin strassvr(name, na[i]); idtype := boolptr;
          next := cp1; values.ival := i - 1; klass := konst
        end;
      enterid(cp); cp1 := cp
    end;
  boolptr^.fconst := cp;
  for i := 3 to 4 do
    begin new(cp,vars); ininam(cp);                         (*input,output*)
      with cp^ do
        begin strassvr(name, na[i]); idtype := textptr; klass := vars;
          vkind := actual; next := nil; vlev := 1;
          vaddr := lcaftermarkstack+(i-3)*(filesize+charsize);
        end;
      enterid(cp);
      if i = 3 then inputptr := cp else outputptr := cp
    end;
  for i:=33 to 34 do
    begin new(cp,vars); ininam(cp);                         (*prd,prr files*)
       with cp^ do
         begin strassvr(name, na[i]); idtype := textptr; klass := vars;
            vkind := actual; next := nil; vlev := 1;
            vaddr := lcaftermarkstack+(i-31)*(filesize+charsize);
         end;
       enterid(cp)
    end;
  for i := 5 to 16 do if i <> 14 then { no longer doing release }
    begin new(cp,proc,standard); ininam(cp);                 (*get,put,reset*)
      with cp^ do                                            (*rewrite,read*)
        begin strassvr(name, na[i]); idtype := nil;          (*write,pack*)
          pflist := nil; next := nil; key := i - 4;          (*unpack,new*)
          klass := proc; pfdeckind := standard               (*readln,writeln*)
        end;
      enterid(cp)
    end;
  for i := 17 to 26 do
    begin new(cp,func,standard); ininam(cp);                 (*abs,sqr,trunc*)
      with cp^ do                                            (*odd,ord,chr*)
        begin strassvr(name, na[i]); idtype := nil;          (*pred,succ,eof*)
          pflist := nil; next := nil; key := i - 16;
          klass := func; pfdeckind := standard
        end;
      enterid(cp)
    end;
  for i := 27 to 32 do
    begin 
      new(cp,vars); ininam(cp);                                (*parameter of predeclared functions*)
      with cp^ do
        begin strassvr(name, '         '); idtype := realptr; klass := vars;
          vkind := actual; next := nil; vlev := 1; vaddr := 0
        end;
      new(cp1,func,declared,actual); ininam(cp1);            (*sin,cos,exp*)
      with cp1^ do                                           (*sqrt,ln,arctan*)
        begin strassvr(name, na[i]); idtype := realptr; pflist := cp;
          forwdecl := false; externl := true; pflev := 0; pfname := i - 12;
          klass := func; pfdeckind := declared; pfkind := actual
        end;
      enterid(cp1)
    end;
  new(cp,konst); ininam(cp);                                 (*maxint*)
  with cp^ do
    begin strassvr(name, na[36]); idtype := intptr;
      next := nil; values.ival := maxint; klass := konst
    end; enterid(cp);
  new(cp,func,standard); ininam(cp);                         (*round*)
  with cp^ do
    begin strassvr(name, na[37]); idtype := nil;
      pflist := nil; next := nil; key := 16;
      klass := func; pfdeckind := standard
    end; enterid(cp);
  new(cp,proc,standard); ininam(cp);                         (*page*)
  with cp^ do   
    begin strassvr(name, na[38]); idtype := nil; 
      pflist := nil; next := nil; key := 17;
      klass := proc; pfdeckind := standard
    end; enterid(cp);
  new(cp,proc,standard); ininam(cp);                         (*dispose*)
  with cp^ do   
    begin strassvr(name, na[39]); idtype := nil; 
      pflist := nil; next := nil; key := 18;
      klass := proc; pfdeckind := standard
    end; enterid(cp)
end (*entstdnames*) ;

scopes

procedure enterundecl;
begin
  new(utypptr,types); ininam(utypptr);
  with utypptr^ do
    begin strassvr(name, '         '); idtype := nil; klass := types end;
  new(ucstptr,konst); ininam(ucstptr);
  with ucstptr^ do
    begin strassvr(name, '         '); idtype := nil; next := nil;
      values.ival := 0; klass := konst
    end;
  new(uvarptr,vars); ininam(uvarptr);
  with uvarptr^ do
    begin strassvr(name, '         '); idtype := nil; vkind := actual;
      next := nil; vlev := 0; vaddr := 0; klass := vars
    end;
  new(ufldptr,field); ininam(ufldptr);
  with ufldptr^ do
    begin strassvr(name, '         '); idtype := nil; next := nil; fldaddr := 0;
      klass := field
    end;
  new(uprcptr,proc,declared,actual); ininam(uprcptr);
  with uprcptr^ do
    begin strassvr(name, '         '); idtype := nil; forwdecl := false;
      next := nil; externl := false; pflev := 0; genlabel(pfname);
      klass := proc; pflist := nil; pfdeckind := declared; pfkind := actual
    end;
  new(ufctptr,func,declared,actual); ininam(ufctptr);
  with ufctptr^ do
    begin strassvr(name, '         '); idtype := nil; next := nil;
      forwdecl := false; externl := false; pflev := 0; genlabel(pfname);
      klass := func; pflist := nil; pfdeckind := declared; pfkind := actual
    end
end (*enterundecl*) ;

{ tear down storage allocations from enterundecl }
procedure exitundecl;
begin
  putnam(utypptr);
  putnam(ucstptr);                                  
  putnam(uvarptr);                                    
  putnam(ufldptr);                                    
  putnam(uprcptr);
  putnam(ufctptr);                                   
end (*exitundecl*) ;

init scalars

procedure initscalars;
var i: integer;
begin fwptr := nil;
  prtables := false; list := true; prcode := true; debug := true;
  dp := true; errinx := 0;
  intlabel := 0; kk := maxids; fextfilep := nil;
  lc := lcaftermarkstack+filebuffer*(filesize+charsize);
  (* note in the above reservation of buffer store for 2 text files *)
  ic := 3; eol := true; linecount := 0;
  ch := ' '; chcnt := 0;
  mxint10 := maxint div 10;
  inputhdf := false; { set 'input' not in header files }
  outputhdf := false; { set 'output' not in header files }
  for i := 1 to 500 do errtbl[i] := false; { initialize error tracking }
  toterr := 0; { clear error count }
  { clear the recycling tracking counters }
  strcnt := 0; { strings }
  cspcnt := 0; { constants }
  stpcnt := 0; { structures }
  ctpcnt := 0; { identifiers }
  lbpcnt := 0; { label counts }
  filcnt := 0; { file tracking counts }
  cipcnt := 0 { case entry tracking counts }
end (*initscalars*) ;

init sets

procedure initsets;
begin
  constbegsys := [addop,intconst,realconst,stringconst,ident];
  simptypebegsys := [lparent] + constbegsys;
  typebegsys:=[arrow,packedsy,arraysy,recordsy,setsy,filesy]+simptypebegsys;
  typedels := [arraysy,recordsy,setsy,filesy];
  blockbegsys := [labelsy,constsy,typesy,varsy,procsy,funcsy,beginsy];
  selectsys := [arrow,period,lbrack];
  facbegsys := [intconst,realconst,stringconst,ident,lparent,lbrack,notsy,nilsy];
  statbegsys := [beginsy,gotosy,ifsy,whilesy,repeatsy,forsy,withsy,casesy];
end (*initsets*) ;

lookup tables

reserved words

procedure inittables;
  procedure reswords;
  begin
    rw[  1] := 'if       '; rw[  2] := 'do       '; rw[  3] := 'of       ';
    rw[  4] := 'to       '; rw[  5] := 'in       '; rw[  6] := 'or       ';
    rw[  7] := 'end      '; rw[  8] := 'for      '; rw[  9] := 'var      ';
    rw[ 10] := 'div      '; rw[ 11] := 'mod      '; rw[ 12] := 'set      ';
    rw[ 13] := 'and      '; rw[ 14] := 'not      '; rw[ 15] := 'nil      ';
    rw[ 16] := 'then     '; rw[ 17] := 'else     '; rw[ 18] := 'with     '; 
    rw[ 19] := 'goto     '; rw[ 20] := 'case     '; rw[ 21] := 'type     '; 
    rw[ 22] := 'file     '; rw[ 23] := 'begin    '; rw[ 24] := 'until    '; 
    rw[ 25] := 'while    '; rw[ 26] := 'array    '; rw[ 27] := 'const    '; 
    rw[ 28] := 'label    '; rw[ 29] := 'repeat   '; rw[ 30] := 'record   ';
    rw[ 31] := 'downto   '; rw[ 32] := 'packed   '; rw[ 33] := 'program  ';
    rw[ 34] := 'function '; rw[ 35] := 'procedure';
    frw[ 1] :=  1; frw[ 2] :=  1; frw[ 3] :=  7; frw[ 4] := 16; frw[ 5] := 23;
    frw[ 6] := 29; frw[ 7] := 33; frw[ 8] := 34; frw[ 9] := 35; frw[ 10] := 36;
  end (*reswords*) ;

symbols

procedure symbols;
begin
  rsy[  1] := ifsy;      rsy[  2] := dosy;      rsy[  3] := ofsy;
  rsy[  4] := tosy;      rsy[  5] := relop;     rsy[  6] := addop;
  rsy[  7] := endsy;     rsy[  8] := forsy;     rsy[  9] := varsy;
  rsy[ 10] := mulop;     rsy[ 11] := mulop;     rsy[ 12] := setsy;
  rsy[ 13] := mulop;     rsy[ 14] := notsy;     rsy[ 15] := nilsy;
  rsy[ 16] := thensy;    rsy[ 17] := elsesy;    rsy[ 18] := withsy; 
  rsy[ 19] := gotosy;    rsy[ 20] := casesy;    rsy[ 21] := typesy;
  rsy[ 22] := filesy;    rsy[ 23] := beginsy;   rsy[ 24] := untilsy;
  rsy[ 25] := whilesy;   rsy[ 26] := arraysy;   rsy[ 27] := constsy;
  rsy[ 28] := labelsy;   rsy[ 29] := repeatsy;  rsy[ 30] := recordsy;
  rsy[ 31] := downtosy;  rsy[ 32] := packedsy;  rsy[ 33] := progsy;
  rsy[ 34] := funcsy;    rsy[ 35] := procsy;
  ssy[ '+'] := addop ;   ssy[ '-'] := addop;    ssy[ '*'] := mulop;
  ssy[ '/'] := mulop ;   ssy[ '('] := lparent;  ssy[ ')'] := rparent;
  ssy[ '$'] := othersy ; ssy[ '='] := relop;    ssy[ ' '] := othersy;
  ssy[ ','] := comma ;   ssy[ '.'] := period;   ssy[ '''']:= othersy;
  ssy[ '[ '] := lbrack ;  ssy[ ']'] := rbrack;   ssy[ ':'] := colon;
  ssy[ '^'] := arrow ;   ssy[ '<'] := relop;    ssy[ '>'] := relop;
  ssy[ ';'] := semicolon; ssy[ '@'] := arrow;
end (*symbols*) ;

rators

procedure rators;
  var i: integer;
begin
  for i := 1 to maxres (*nr of res words*) do rop[ i] := noop;
  rop[ 5] := inop; rop[ 10] := idiv; rop[ 11] := imod;
  rop[ 6] := orop; rop[ 13] := andop;
  for i := ordminchar to ordmaxchar do sop[ chr(i)] := noop;
  sop[ '+'] := plus; sop[ '-'] := minus; sop[ '*'] := mul; sop[ '/'] := rdiv;
  sop[ '='] := eqop; sop[ '<'] := ltop;  sop[ '>'] := gtop;
end (*rators*) ;

procedure mnemonics

procedure procmnemonics;
begin
  { There are two mnemonics that have no counterpart in the
    assembler/interpreter: wro, pak. I didn't find a generator for them, and
    suspect they are abandoned. }
  sna[  1] :=' get'; sna[  2] :=' put'; sna[  3] :=' rdi'; sna[  4] :=' rdr';
  sna[  5] :=' rdc'; sna[  6] :=' wri'; sna[  7] :=' wro'; sna[  8] :=' wrr';
  sna[  9] :=' wrc'; sna[ 10] :=' wrs'; sna[ 11] :=' pak'; sna[ 12] :=' new';
  sna[ 13] :=' rst'; sna[ 14] :=' eln'; sna[ 15] :=' sin'; sna[ 16] :=' cos';
  sna[ 17] :=' exp'; sna[ 18] :=' sqt'; sna[ 19] :=' log'; sna[ 20] :=' atn';
  sna[ 21] :=' rln'; sna[ 22] :=' wln'; sna[ 23] :=' sav'; 
  { new procedure/function memonics for p5 }
  sna[ 24] :=' pag'; sna[ 25] :=' rsf'; sna[ 26] :=' rwf'; sna[ 27] :=' wrb';
  sna[ 28] :=' wrf'; sna[ 29] :=' dsp'; sna[ 30] :=' wbf'; sna[ 31] :=' wbi';
  sna[ 32] :=' wbr'; sna[ 33] :=' wbc'; sna[ 34] :=' wbb'; sna[ 35] :=' rbf';
  sna[ 36] :=' rsb'; sna[ 37] :=' rwb'; sna[ 38] :=' gbf'; sna[ 39] :=' pbf';

end (*procmnemonics*) ;

instruction mnemonics

procedure instrmnemonics;
begin
  mn[  0] :=' abi'; mn[  1] :=' abr'; mn[  2] :=' adi'; mn[  3] :=' adr';
  mn[  4] :=' and'; mn[  5] :=' dif'; mn[  6] :=' dvi'; mn[  7] :=' dvr';
  mn[  8] :=' eof'; mn[  9] :=' flo'; mn[ 10] :=' flt'; mn[ 11] :=' inn';
  mn[ 12] :=' int'; mn[ 13] :=' ior'; mn[ 14] :=' mod'; mn[ 15] :=' mpi';
  mn[ 16] :=' mpr'; mn[ 17] :=' ngi'; mn[ 18] :=' ngr'; mn[ 19] :=' not';
  mn[ 20] :=' odd'; mn[ 21] :=' sbi'; mn[ 22] :=' sbr'; mn[ 23] :=' sgs';
  mn[ 24] :=' sqi'; mn[ 25] :=' sqr'; mn[ 26] :=' sto'; mn[ 27] :=' trc';
  mn[ 28] :=' uni'; mn[ 29] :=' stp'; mn[ 30] :=' csp'; mn[ 31] :=' dec';
  mn[ 32] :=' ent'; mn[ 33] :=' fjp'; mn[ 34] :=' inc'; mn[ 35] :=' ind';
  mn[ 36] :=' ixa'; mn[ 37] :=' lao'; mn[ 38] :=' lca'; mn[ 39] :=' ldo';
  mn[ 40] :=' mov'; mn[ 41] :=' mst'; mn[ 42] :=' ret'; mn[ 43] :=' sro';
  mn[ 44] :=' xjp'; mn[ 45] :=' chk'; mn[ 46] :=' cup'; mn[ 47] :=' equ';
  mn[ 48] :=' geq'; mn[ 49] :=' grt'; mn[ 50] :=' lda'; mn[ 51] :=' ldc';
  mn[ 52] :=' leq'; mn[ 53] :=' les'; mn[ 54] :=' lod'; mn[ 55] :=' neq';
  mn[ 56] :=' str'; mn[ 57] :=' ujp'; mn[ 58] :=' ord'; mn[ 59] :=' chr';
  mn[ 60] :=' ujc'; 
  { new instruction memonics for p5 }
  mn[ 61] :=' rnd'; mn[ 62] :=' pck'; mn[ 63] :=' upk'; mn[ 64] :=' rgs';
  mn[ 65] :=' fbv'; mn[ 66] :=' ipj'; mn[ 67] :=' cip'; mn[ 68] :=' lpa';
  mn[ 69] :=' efb'; mn[ 70] :=' fvb'; mn[ 71] :=' dmp'; mn[ 72] :=' swp';
  mn[ 73] :=' tjp'; mn[ 74] :=' lip'; 
end (*instrmnemonics*) ;

chartypes

procedure chartypes;
var i : integer;
begin
  for i := ordminchar to ordmaxchar do chartp[chr(i)] := illegal;
  chartp['a'] := letter  ;
  chartp['b'] := letter  ; chartp['c'] := letter  ;
  chartp['d'] := letter  ; chartp['e'] := letter  ;
  chartp['f'] := letter  ; chartp['g'] := letter  ;
  chartp['h'] := letter  ; chartp['i'] := letter  ;
  chartp['j'] := letter  ; chartp['k'] := letter  ;
  chartp['l'] := letter  ; chartp['m'] := letter  ;
  chartp['n'] := letter  ; chartp['o'] := letter  ;
  chartp['p'] := letter  ; chartp['q'] := letter  ;
  chartp['r'] := letter  ; chartp['s'] := letter  ;
  chartp['t'] := letter  ; chartp['u'] := letter  ;
  chartp['v'] := letter  ; chartp['w'] := letter  ;
  chartp['x'] := letter  ; chartp['y'] := letter  ;
  chartp['z'] := letter  ; 
  chartp['A'] := letter  ;
  chartp['B'] := letter  ; chartp['C'] := letter  ;
  chartp['D'] := letter  ; chartp['E'] := letter  ;
  chartp['F'] := letter  ; chartp['G'] := letter  ;
  chartp['H'] := letter  ; chartp['I'] := letter  ;
  chartp['J'] := letter  ; chartp['K'] := letter  ;
  chartp['L'] := letter  ; chartp['M'] := letter  ;
  chartp['N'] := letter  ; chartp['O'] := letter  ;
  chartp['P'] := letter  ; chartp['Q'] := letter  ;
  chartp['R'] := letter  ; chartp['S'] := letter  ;
  chartp['T'] := letter  ; chartp['U'] := letter  ;
  chartp['V'] := letter  ; chartp['W'] := letter  ;
  chartp['X'] := letter  ; chartp['Y'] := letter  ;
  chartp['Z'] := letter  ; 
  chartp['0'] := number  ;
  chartp['1'] := number  ; chartp['2'] := number  ;
  chartp['3'] := number  ; chartp['4'] := number  ;
  chartp['5'] := number  ; chartp['6'] := number  ;
  chartp['7'] := number  ; chartp['8'] := number  ;
  chartp['9'] := number  ; chartp['+'] := special ;
  chartp['-'] := special ; chartp['*'] := special ;
  chartp['/'] := special ; chartp['('] := chlparen;
  chartp[')'] := special ; chartp['$'] := special ;
  chartp['='] := special ; chartp[' '] := chspace ;
  chartp[','] := special ; chartp['.'] := chperiod;
  chartp['''']:= chstrquo; chartp['['] := special ;
  chartp[']'] := special ; chartp[':'] := chcolon ;
  chartp['^'] := special ; chartp[';'] := special ;
  chartp['<'] := chlt    ; chartp['>'] := chgt    ;
  chartp['{'] := chlcmt  ; chartp['}'] := special ;
  chartp['@'] := special ;

  ordint['0'] := 0; ordint['1'] := 1; ordint['2'] := 2;
  ordint['3'] := 3; ordint['4'] := 4; ordint['5'] := 5;
  ordint['6'] := 6; ordint['7'] := 7; ordint['8'] := 8;
  ordint['9'] := 9;
end;

initdx

procedure initdx;
begin
  cdx[  0] :=  0; cdx[  1] :=  0; cdx[  2] := -1; cdx[  3] := -1;
  cdx[  4] := -1; cdx[  5] := -1; cdx[  6] := -1; cdx[  7] := -1;
  cdx[  8] :=  0; cdx[  9] :=  0; cdx[ 10] :=  0; cdx[ 11] := -1;
  cdx[ 12] := -1; cdx[ 13] := -1; cdx[ 14] := -1; cdx[ 15] := -1;
  cdx[ 16] := -1; cdx[ 17] :=  0; cdx[ 18] :=  0; cdx[ 19] :=  0;
  cdx[ 20] :=  0; cdx[ 21] := -1; cdx[ 22] := -1; cdx[ 23] :=  0;
  cdx[ 24] :=  0; cdx[ 25] :=  0; cdx[ 26] := -2; cdx[ 27] :=  0;
  cdx[ 28] := -1; cdx[ 29] :=  0; cdx[ 30] :=  0; cdx[ 31] :=  0;
  cdx[ 32] :=  0; cdx[ 33] := -1; cdx[ 34] :=  0; cdx[ 35] :=  0;
  cdx[ 36] := -1; cdx[ 37] := +1; cdx[ 38] := +1; cdx[ 39] := +1;
  cdx[ 40] := -2; cdx[ 41] :=  0; cdx[ 42] :=  0; cdx[ 43] := -1;
  cdx[ 44] := -1; cdx[ 45] :=  0; cdx[ 46] :=  0; cdx[ 47] := -1;
  cdx[ 48] := -1; cdx[ 49] := -1; cdx[ 50] := +1; cdx[ 51] := +1;
  cdx[ 52] := -1; cdx[ 53] := -1; cdx[ 54] := +1; cdx[ 55] := -1;
  cdx[ 56] := -1; cdx[ 57] :=  0; cdx[ 58] :=  0; cdx[ 59] :=  0;
  cdx[ 60] :=  0; cdx[ 61] :=  0; cdx[ 62] := -3; cdx[ 63] := -3;
  cdx[ 64] := -1; cdx[ 65] :=  0; cdx[ 66] :=  0; cdx[ 67] := -1;
  cdx[ 68] := +2; cdx[ 69] :=  0; cdx[ 70] := -1; cdx[ 71] := -1;
  cdx[ 72] :=  0; cdx[ 73] := -1; cdx[ 74] := +2;

  pdx[  1] := -1; pdx[  2] := -1; pdx[  3] := -1; pdx[  4] := -1;
  pdx[  5] := -1; pdx[  6] := -2; pdx[  7] := -3; pdx[  8] := -2;
  pdx[  9] := -2; pdx[ 10] := -3; pdx[ 11] :=  0; pdx[ 12] := -2;
  pdx[ 13] := -1; pdx[ 14] :=  0; pdx[ 15] :=  0; pdx[ 16] :=  0;
  pdx[ 17] :=  0; pdx[ 18] :=  0; pdx[ 19] :=  0; pdx[ 20] :=  0;
  pdx[ 21] :=  0; pdx[ 22] :=  0; pdx[ 23] := -1; pdx[ 24] := -1;
  pdx[ 25] := -1; pdx[ 26] := -1; pdx[ 27] := -2; pdx[ 28] := -3;
  pdx[ 29] := -2; pdx[ 30] := -2; pdx[ 31] := -1; pdx[ 32] := -1;
  pdx[ 33] := -1; pdx[ 34] := -1; pdx[ 35] := -2; pdx[ 36] := -1; 
  pdx[ 37] := -1; pdx[ 38] := -2; pdx[ 39] := -2;
end;

inittables main

begin (*inittables*)
  reswords; symbols; rators;
  instrmnemonics; procmnemonics;
  chartypes; initdx;
end (*inittables*) ;

main code

initialization

begin

  writeln('P5 Pascal compiler vs. ', majorver:1, '.', minorver:1);
  writeln;

  (*initialize*)
  (************)
  initscalars; initsets; inittables;

standard names and types

(*enter standard names and standard types:*)
(******************************************)
level := 0; top := 0;
with display[0] do
  begin fname := nil; flabel := nil; fconst := nil; fstruct := nil; 
        occur := blck; bname := nil end;
enterstdtypes;   stdnames; entstdnames;   enterundecl;
top := 1; level := 1;
with display[1] do
  begin fname := nil; flabel := nil; fconst := nil; fstruct := nil; 
        occur := blck; bname := nil end;

compile

(*compile:*)
(**********)

{ !!! remove this statement for self compile }
{elide}rewrite(prr);{noelide} { open output file }

{ write generator comment }
writeln(prr, 'i');
writeln(prr, 'i Pascal intermediate file Generated by P5 Pascal compiler vs. ', 
        majorver:1, '.', minorver:1);
writeln(prr, 'i');
insymbol;
programme(blockbegsys+statbegsys-[casesy]);

{ dispose of levels 0 and 1 }
putdsp(1);
putdsp(0);

{ remove undeclared ids }
exitundecl;

writeln;
writeln('Errors in program: ', toterr:1);
{ output error report as required }
f := true;
for i := 1 to 500 do if errtbl[i] then begin
  if f then begin
    writeln;
    writeln('Error numbers in listing:');
    writeln('-------------------------');
    f := false
  end;
  write(i:3, '  '); errmsg(i); writeln
end;
if not f then writeln;

if doprtryc then begin { print recyling tracking counts }

  writeln;
  writeln('Recycling tracking counts:');
  writeln;
  writeln('string quants:              ', strcnt:1);
  writeln('constants:                  ', cspcnt:1);
  writeln('structures:                 ', stpcnt:1);
  writeln('identifiers:                ', ctpcnt:1);
  writeln('label counts:               ', lbpcnt:1);
  writeln('file tracking counts:       ', filcnt:1);
  writeln('case entry tracking counts: ', cipcnt:1);
  writeln;

end;

error messages

{ perform errors for recycling balance }

if strcnt <> 0 then 
   writeln('*** Error: Compiler internal error: string recycle balance: ', 
           strcnt:1);
if cspcnt <> 0 then 
   writeln('*** Error: Compiler internal error: constant recycle balance: ', 
           cspcnt:1);
if stpcnt <> 0 then 
   writeln('*** Error: Compiler internal error: structure recycle balance: ', 
           stpcnt:1);
if ctpcnt <> 0 then 
   writeln('*** Error: Compiler internal error: identifier recycle balance: ',
           ctpcnt:1);
if lbpcnt <> 0 then 
   writeln('*** Error: Compiler internal error: label recycle balance: ', 
           lbpcnt:1);
if filcnt <> 0 then 
   writeln('*** Error: Compiler internal error: file recycle balance: ', 
           filcnt:1);
if cipcnt <> 0 then 
   writeln('*** Error: Compiler internal error: case recycle balance: ', 
           cipcnt:1);

end of program

  99:

end.