This file comprises two P test programs and the Pascal source of the simple recursive-descent P compiler/interpreter separated by asterisks. Copyright (c) P.C.Capon & P.J.Jinks 1988 May be copied for educational purposes only, with the copyright notice attached. ******************************************************************************* { runs correctly - figure 2.1 } VAR S, N, T; BEGIN S := 0; {sum} N := 0; {number of items} T := READ; WHILE T <> 0 {not end of file} DO BEGIN S := S + T; N := N + 1; T := READ; END; WRITE (N); WRITE (S); IF N <> 0 THEN WRITE (S / N); {average} END. 2 3 4 5 6 7 8 0 ****************************************************************************** { fails at run time - figure 5.2 } VAR S, N, T; BEGIN S := 0; {sum} N := 0; {number of items} T := READ; WHILE T <> 0 {not end of file} DO BEGIN S := S + T; N := N + 1; T := READ; END; WRITE (N); WRITE (S); IF N <> 0 THEN WRITE (S / N); {average} T := 0; T := T / T; END. 2 3 4 5 6 7 8 0 ****************************************************************************** program run (input,output); {Copyright (c) P.C.Capon & P.J.Jinks 1988} {may be copied for educational purposes only,} {with the copyright notice attached.} label 999; const linewidth = 256; namechars = 6; nulop = 0; readproc = 1; writeproc = 2; {call operands} stack = 1; unstack = 2; nooperand = 3; {special operands} maxcode = 500; maxstack = 100; maxvar = 26; type lexemetype = (dot, constant, identifier, comma, assign, semicolon, lbracket, rbracket, addop, mulop, relop, beginlexeme, readlexeme, writelexeme, iflexeme, thenlexeme, whilelexeme, dolexeme, varlexeme, endlexeme, unknown); addvaltype = (add, sub); mulvaltype = (mul, divd); relvaltype = (eq, ne, gt, lt, ge, le); nametype = packed array [1..namechars] of char; lexemevaltype = record case lexemetype of constant: (constval: integer); identifier: (idval: char); addop: (addval: addvaltype); mulop: (mulval: mulvaltype); relop: (relval: relvaltype); end; lexemesettype = set of lexemetype; functiontype = (accload, accstore, stackaccload, accplus, accminus, minusacc, acctimes, accdiv, divacc, stop, call, acccompare, br, breq, brne, brlt, brle, brge, brgt); optypetype = (specialop, constop, varop, labelop); calltype = readproc..writeproc; mode = specialop .. varop; data = integer; address = 0..maxcode; inst = record case funct: functiontype of accload, accstore, stackaccload, accplus, accminus, minusacc, acctimes, accdiv, divacc, acccompare: (accmode: mode; accval: data); br, breq, brne, brlt, brle, brge, brgt: (brval: address); call: (callval: calltype); stop: (); end; var linesize: 0..linewidth; {number of chars in line} linepos: integer; {position of next char in line} line: array[1..linewidth] of char; lexeme, firstlexeme, lastlexeme: lexemetype; lexemeval: lexemevaltype; errors: boolean; lexemes: array [lexemetype] of nametype; startexp, startblock: lexemesettype; variables: array['A'..'Z'] of -1..maxint; {-1=undefined, 0=not used, >0=address} nextvariable: 1..maxint; {address of next variable declared} accinuse: boolean; codepos: 0..maxint; {position to plant next piece of code} forwardadd, reverseadd: array [addvaltype] of functiontype; forwardmul, reversemul: array [mulvaltype] of functiontype; normalskip, reverseskip: array [relvaltype] of functiontype; fnnames: array [functiontype] of nametype; store: array [address] of inst; branchset: set of breq..brgt; machinestack: array [0..maxstack] of data; pc: -1..maxcode; acc: data; sf: -1..maxstack; stoprun: boolean; vars: array [1..maxvar] of record defined: boolean; varval: data; end; { procedures and functions } procedure error (n :integer); begin write('^':linepos - 1); case n of 1 : writeln('end of input encountered'); 2 : writeln('line too long, max width= ', linewidth); 3 : {unexpected symbol}; 4 : writeln('unknown character'); 5 : writeln('= expected after :'); 6 : writeln('unknown keyword'); 7 : writeln('lexeme skipped after error'); 8 : writeln('variable already declared'); 9 : writeln('variable not declared'); 10 : writeln('code overflow'); end; errors := true; end; procedure getlexeme; var ch: char; name: nametype; charno: integer; function nextch:char; begin if linepos>linesize then if eof then begin error(1); {end of file encountered} goto 999; {fatal error} end else begin linesize := 1; while (not eoln) and (linesize lexemes[lexeme]) do lexeme := succ (lexeme); if lexeme > endlexeme then begin lexeme := unknown; error(6); {unknown name} end; end else begin { try to recognise:- , ; := ( ) + - * / <=> } if ch in [',',';',':','(',')','+','-','*','/','<','=','>','.'] then case ch of '.': lexeme := dot; ',': lexeme := comma; ';': lexeme := semicolon; ':': if nextch = '=' then begin ch := getch; lexeme := assign; end else begin error(5); {: not followed by =} lexeme := unknown; end; '(': lexeme := lbracket; ')': lexeme := rbracket; '+': begin lexeme := addop; lexemeval.addval := add end; '-': begin lexeme := addop; lexemeval.addval := sub end; '*': begin lexeme := mulop; lexemeval.mulval := mul end; '/': begin lexeme := mulop; lexemeval.mulval := divd end; '<': begin lexeme := relop; ch := nextch; if ch = '=' then begin ch := getch; lexemeval.relval := le; end else if ch = '>' then begin ch := getch; lexemeval.relval := ne; end else lexemeval.relval := lt; end; '=': begin lexeme := relop; lexemeval.relval := eq end; '>': begin lexeme := relop; if nextch = '=' then begin ch := getch; lexemeval.relval := ge; end else lexemeval.relval := gt; end; end else begin lexeme := unknown; error (4); {unknown characters} end; end; end; {getlexeme} procedure declid; begin if variables[lexemeval.idval] > 0 then error(8) {variable already declared} else begin variables[lexemeval.idval] := nextvariable; nextvariable := nextvariable + 1; end; end; procedure checkid; begin if variables[lexemeval.idval] = 0 then begin error(9); {variable not declared} variables[lexemeval.idval] := -1; { to stop further error messages } end; end; procedure listaline (pos: address); begin with store[pos] do begin write (pos:3,' : ',fnnames[funct],' '); case funct of accload,accstore,stackaccload,accplus,accminus,minusacc, acctimes,accdiv,divacc,acccompare: case accmode of varop :write ('variable ', accval:1); constop :write ('constant ', accval:1); specialop: case accval of stack :write ('stack '); unstack :write ('unstack '); nooperand:; end; end; br, brne, breq, brlt, brle, brge, brgt: write (brval :1); call: if callval = readproc then write ('read') else if callval = writeproc then write ('write') else write (callval :3); stop:; end; end; writeln; end; procedure listing; var i: address; begin writeln ; writeln ('assembly listing of compiled code'); writeln ('================================='); writeln ; for i := 0 to codepos - 1 do listaline (i); writeln; end; procedure plant(fn: functiontype; optype: optypetype; opval: integer); begin if codepos >= maxcode then begin error (10); codepos := 0; {not very satisfactory, but adequate} end; with store [codepos] do begin funct := fn; case optype of specialop: begin accmode := specialop; accval := opval; end; labelop: if fn = call then callval := opval else brval := opval; constop: begin accval := opval; accmode := constop; end; varop: begin accval := opval; accmode := varop; end; end; write ('plant '); listaline (codepos); codepos := codepos + 1; end; end; procedure plantforwardlabel(pos: integer); begin writeln('label: used from ', pos); store[pos].brval := codepos; end; function saveforwardlabel: integer; begin writeln('label used'); saveforwardlabel := codepos; end; function savelabel: integer; begin writeln('label:'); savelabel := codepos; end; procedure plantaccload (optype: optypetype; opval: integer); begin if optype <> specialop then if accinuse then plant (stackaccload, optype, opval) else plant (accload, optype, opval); accinuse := true; end; function checkorskip (okset, stopset: lexemesettype): boolean; var alexeme: lexemetype; orflag: boolean; begin if lexeme in okset then checkorskip := true else begin checkorskip := false; error(3); {okset expected} orflag := false; for alexeme := firstlexeme to lastlexeme do if alexeme in okset then begin if orflag then write(' or '); orflag := true; write(lexemes[alexeme]); end; writeln(' expected'); if stopset <> [] then while not (lexeme in stopset) do begin error(7); {lexeme skipped after error} getlexeme; end; end; end; procedure idlist (stopset: lexemesettype); var ignore, finish: boolean; begin repeat if checkorskip([identifier], stopset + [semicolon, comma]) then begin declid; getlexeme; end; ignore := checkorskip([semicolon, comma], stopset + [comma, identifier]); finish := not (lexeme in [comma, identifier]); if lexeme = comma then getlexeme; until finish; if lexeme = semicolon then getlexeme; end; procedure expression (stopset: lexemesettype; var optype: optypetype; var opval: integer); forward; procedure factor (stopset: lexemesettype; var optype: optypetype; var opval: integer); begin optype := specialop; if checkorskip(startexp, stopset) then if lexeme = identifier then begin checkid; optype := varop; opval := variables[lexemeval.idval]; getlexeme; end else if lexeme = constant then begin optype := constop; opval := lexemeval.constval; getlexeme; end else if lexeme = lbracket then begin getlexeme; expression(stopset + [rbracket], optype, opval); if checkorskip([rbracket], stopset) then getlexeme; end else if lexeme = readlexeme then begin getlexeme; if accinuse then plant(accstore, specialop, stack); plant(call, labelop, readproc); accinuse := true; optype := specialop; end; end; procedure term (stopset: lexemesettype; var optype: optypetype; var opval: integer); var operator: mulvaltype; roptype: optypetype; ropval: integer; begin factor(stopset + [mulop], optype, opval); while lexeme = mulop do begin plantaccload (optype, opval); optype := specialop; operator := lexemeval.mulval; getlexeme; factor(stopset + [mulop], roptype, ropval); if roptype = specialop then plant(reversemul[operator], specialop, unstack) else plant(forwardmul[operator], roptype, ropval); end; end; procedure expression; var operator: addvaltype; roptype: optypetype; ropval: integer; begin term(stopset + [addop], optype, opval); while lexeme = addop do begin plantaccload (optype, opval); optype := specialop; operator := lexemeval.addval; getlexeme; term(stopset + [addop], roptype, ropval); if roptype = specialop then plant(reverseadd[operator], specialop, unstack) else plant(forwardadd[operator], roptype, ropval); end; end; procedure block (stopset: lexemesettype); forward; procedure comparison(stopset: lexemesettype; var iflabel: integer); var compareop: relvaltype; optype: optypetype; opval: integer; begin expression(stopset + [relop], optype, opval); plantaccload (optype, opval); if checkorskip([relop], stopset + startexp) then begin compareop := lexemeval.relval; getlexeme; end else compareop := eq; expression(stopset + startblock - [identifier], optype, opval); if optype <> specialop then begin plant(acccompare, optype, opval); iflabel := saveforwardlabel; plant(normalskip[compareop], labelop, nulop); end else begin plant(acccompare, specialop, unstack); iflabel := saveforwardlabel; plant(reverseskip[compareop], labelop, nulop); end; end; procedure statement (stopset: lexemesettype); var assignto: integer; optype: optypetype; opval, iflabel, whilelabel: integer; begin accinuse := false; if checkorskip(stopset + startblock, stopset) then if lexeme = identifier then begin checkid; assignto := variables[lexemeval.idval]; getlexeme; if checkorskip([assign], stopset + startexp) then getlexeme; expression(stopset, optype, opval); plantaccload (optype, opval); plant(accstore, varop, assignto); end else if lexeme = iflexeme then begin getlexeme; comparison(stopset + [thenlexeme] + startblock - [identifier], iflabel); if checkorskip([thenlexeme], stopset + startblock) then getlexeme; block(stopset); plantforwardlabel(iflabel); end else if lexeme = whilelexeme then begin getlexeme; whilelabel := savelabel; comparison(stopset + [dolexeme] + startblock - [identifier], iflabel); if checkorskip([dolexeme], stopset + startblock) then getlexeme; block(stopset); plant(br, labelop, whilelabel); plantforwardlabel(iflabel); end else if lexeme = writelexeme then begin getlexeme; if checkorskip([lbracket], stopset + [rbracket] + startexp) then getlexeme; expression(stopset + [rbracket], optype, opval); plantaccload (optype, opval); if checkorskip([rbracket], stopset) then getlexeme; plant(accstore, specialop, stack); plant(call, labelop, writeproc); end; end; procedure block; var ignore: boolean; begin ignore := checkorskip(stopset + startblock, stopset + startblock); if lexeme = beginlexeme then begin getlexeme; block(stopset + [semicolon, endlexeme]); ignore := checkorskip([semicolon, endlexeme], stopset + [semicolon] + startblock); while lexeme in ([semicolon] + startblock) do begin if lexeme = semicolon then getlexeme; block(stopset + [semicolon, endlexeme]); ignore := checkorskip([semicolon, endlexeme], stopset + [semicolon] + startblock); end; if lexeme = endlexeme then getlexeme; end else statement(stopset); end; procedure prog; { recognise p program } begin if lexeme = varlexeme then begin getlexeme; idlist([beginlexeme, semicolon, dot]); end; repeat block([dot]); until checkorskip([dot],[]); end; procedure runerror(message: packed array [lo..hi:integer] of char); var i : integer; begin writeln; write ('*** runtime error - '); for i := lo to hi do write (message [i]); writeln (' at address ',pc:1,' ***'); writeln; for i := 1 to maxvar do if vars[i].defined then writeln ('variable ',i:2,' = ',vars[i].varval:1); writeln; writeln ('accumulator = ',acc:1); writeln; writeln ('stack front = ', sf); for i := 0 to sf do writeln ('stack item ', i, ' =', machinestack [i]); goto 999; end; procedure push (d : data); begin if sf = maxstack then runerror ('stack overflow'); sf := sf + 1; machinestack [sf] := d; end; function pop : data; begin if sf = -1 then runerror ('stack underflow'); pop := machinestack [sf]; sf := sf - 1; end; function getop : data; begin with store [pc] do case accmode of specialop: if accval = unstack then getop := pop else runerror ('illegal operand'); constop: getop := accval; varop: if vars[accval].defined then getop := vars[accval].varval else runerror ('undefined variable'); end; { case } end; procedure interpret; { a single instruction } var operand : data; begin pc := pc + 1; with store[pc] do begin case funct of accload: acc := getop; stackaccload: begin push (acc); acc := getop; end; accplus: acc := acc + getop; accminus: acc := acc - getop; minusacc: acc := getop - acc; acctimes: acc := acc * getop; accdiv: begin operand := getop; if operand = 0 then runerror ('division by zero'); acc := acc div operand; end; divacc: begin if acc = 0 then runerror ('division by zero'); acc := getop div acc; end; accstore: with store[pc] do case accmode of constop: runerror('illegal operand'); varop: with vars [accval] do begin defined := true; varval := acc; end; specialop: if accval = stack then push (acc) else runerror ('illegal operand'); end; acccompare: begin operand := getop; if acc = operand then branchset := [breq] else branchset := [brne]; if acc < operand then branchset := branchset + [brlt] else branchset := branchset + [brge]; if acc <= operand then branchset := branchset + [brle] else branchset := branchset + [brgt]; end; brne, breq, brle, brlt, brgt, brge: if funct in branchset then pc := brval - 1; br: pc := brval - 1; call: if callval = readproc then read (acc) else if callval = writeproc then writeln (pop:1) else runerror ('illegal call operand'); stop: stoprun := true; end; { case } end; { with } end; { interpret } procedure init; var i: 0..maxvar; ch : char; begin linesize := 0; linepos := 1; errors := false; firstlexeme := dot; lexemes[dot] := '. '; lexemes[constant] := 'digit '; lexemes[identifier] := 'name '; lexemes[comma] := ', '; lexemes[assign] := ':= '; lexemes[semicolon] := '; '; lexemes[lbracket] := '( '; lexemes[rbracket] := ') '; lexemes[addop] := '+ or -'; lexemes[mulop] := '* or /'; lexemes[relop] := '<=> '; lexemes[beginlexeme] := 'BEGIN '; lexemes[readlexeme] := 'READ '; lexemes[writelexeme] := 'WRITE '; lexemes[iflexeme] := 'IF '; lexemes[thenlexeme] := 'THEN '; lexemes[whilelexeme] := 'WHILE '; lexemes[dolexeme] := 'DO '; lexemes[varlexeme] := 'VAR '; lexemes[endlexeme] := 'END '; lexemes[unknown] := '? '; lastlexeme := unknown; startexp := [lbracket, identifier, constant, readlexeme]; startblock := [beginlexeme, identifier, iflexeme, whilelexeme, writelexeme]; nextvariable := 1; for ch := 'A' to 'Z' do variables[ch] := 0; codepos := 0; forwardadd[add] := accplus; forwardadd[sub] := accminus; reverseadd[add] := accplus; reverseadd[sub] := minusacc; forwardmul[mul] := acctimes; forwardmul[divd] := accdiv; reversemul[mul] := acctimes; reversemul[divd] := divacc; normalskip[eq] := brne; normalskip[ne] := breq; normalskip[gt] := brle; normalskip[lt] := brge; normalskip[ge] := brlt; normalskip[le] := brgt; reverseskip[eq] := brne; reverseskip[ne] := breq; reverseskip[gt] := brge; reverseskip[lt] := brle; reverseskip[ge] := brgt; reverseskip[le] := brlt; fnnames[accload] := 'acc= '; fnnames[accstore] := 'acc=> '; fnnames[stackaccload] := '