program pascal_pass1; {N B S P a s c a l C o m p i l e r -- P a s s 1} { *Authors: Brian G. Lucas * Justin C. Walker *Address comments to: * Justin C. Walker * Interactive Systems Corp * 1050 17th Street, N.W. * Suite 580 * Washington, D.C. 20036 * This software has been developed at the National Bureau of Standards. * As a product of the U.S. Government, it is in the public domain, and * should not be sold or otherwise used for profit. * RSX-11 version developed by John R. Barr and J. Bill Heidebrecht. * RT-11 version developed by John R. Barr through a grant from * Lawrence Livermore Laboratories. * Address comments on RSX-11 and RT-11 versions to: * John R. Barr * Department of Computer Science * University of Montana * Missoula, Montana 59812 } (*DEC OS versions maintained by DECUS Pascal SIG: Bill Heidebrecht - TRW DSSG John R. Barr - University of Montana *) {constants} {*********} const NL = chr(10); {ascii new line (line feed)} HT = chr(9); {ascii horz tab} FF = chr(12); {ascii form feed} alfaleng = 15; {max length of identifier} strlen = 80; {max length of string} filsiz = 522; {size of file variable} pagesize = 58; (* length of printer page *) compiler_version = ' Pascal-NBS V1.5l 1 Dec 79'; {addressing characteristics} {**************************} const maxlevel = 15; {maximum lex level} type lltype = 0..maxlevel; addrrange = integer; {address type} {value information} {*****************} type stndset = set of 0..15; { ***TEMP Restriction*** } realoverlay = array[0..3] of integer; cstclass = (lit,data,reel,setc); valu = record case kind: cstclass of lit: (ival: integer); data: (daddr: integer); reel: (case boolean of { caution: equivalence } false: (rval: @longreal); true: (xval: @realoverlay)); setc: (sval: @stndset) end; {lexical information} {*******************} {basic symbols} {*************} type symbol = (ident,intconst,realconst,stringconst,notsy,mulop,addop, relop,lparen,rparen,lbrack,rbrack,comma,semicolon, period,atsign,colon,becomes,constsy,typesy,varsy, programsy,proceduresy,functionsy,setsy,packedsy,arraysy, recordsy,filesy,forwardsy,beginsy,ifsy,casesy,repeatsy, whilesy,nilsy,forsy,withsy,loopsy,gotosy,exitsy,endsy, elsesy,untilsy,ofsy,dosy,tosy,downtosy,thensy,externalsy, labelsy,eofsy,othersy,ceesy,fortransy); operator = (mul,andop,idiv,imod,plus,minus,orop,ltop,leop, geop,gtop,neop,eqop,inop,maxop,minop,ceilop, floorop,noop); symtype = record sy: symbol; op: operator end; idtype = record l: char; {length of identifier} s: array[1..alfaleng] of char end; {returned by insymbol} {********************} var sym: symtype; {symbol type and classification} val: valu; {value of constant} lgth: integer; {length of string} string: array[0..strlen-1] of char; {value of string} id: idtype; {last identifier} ch: char; {last character} chcnt: 0..75; {character counter} linenr: integer; {line counter} pageno: integer; (* page count *) linesleft: integer; (* lines left on page *) {option switches:} {****************} option: array ['A'..'Z'] of boolean; {files} {*****} var src, {source} lst, {listing} int, {intermediate code} dat: text; {intermediate data} {error messages:} {***************} const not_yet_impl = 398; {error msg number} var errtot: integer; {total number of errors} errinx: 0..7; {number of errors in current line} errlist: array [1..7] of record pos: 1..75; nmr: 1..999 end; {type and identifier information} {*******************************} type itp = @idents; {pointer to identifier information} stp = @struct; {pointer to type information} {form of types:} {**************} forms = (scalar, booleant, chart, integert, longintt, realt, longrealt, pointer, sett, arrayt, recordt, filet, tagfield, variant); {type information:} {*****************} struct = record size: integer; marked: boolean; {used by printtables} case form: forms of scalar, booleant, chart, integert, longintt, realt, longrealt: ( maxconst: itp; case subrange: boolean of true: (maxvalue, minvalue: integer)); pointer: ( eltype: stp); sett: ( settyp: stp); arrayt: ( aeltyp, inxtyp: stp); recordt: ( fstfld: itp; recvar: stp); filet: ( filtyp: stp); tagfield: ( fstvar: stp; tagfld: itp; tagtyp: stp); variant: ( varval: integer; nxtvar, subvar: stp) end; {pointers to builtin types:} {**************************} var boolptr, charptr, intptr, realptr, textptr, nilptr: stp; {identifier classes:} {*******************} type classes = (types, konst, vars, field, proc); {kinds of variables:} {*******************} varkinds = (local, param, formal); {kinds of procedures:} {********************} pkinds = (decl, stnd, forw, extn, cee, fort); {identifier information:} {***********************} idents = record name: @idtype; {address of identifier string} llink, rlink: itp; {pointers to build binary tree} itype: stp; {pointer to type information} next: itp; {used to build lists of identifiers} case class: classes of types: ( ); konst: (value: valu); vars:( vkind: varkinds; vlev: lltype; vaddr: addrrange); field:( case ispacked: boolean of false: (fdisp: addrrange); true: (bdisp: integer)); proc:( case pkind: pkinds of decl: ( plev: lltype; paddr: addrrange); stnd: ( psinx: integer); extn,cee,fort: ( pxinx: integer)) end; {dummy identifiers for undeclareds:} {**********************************} var udptrs: array[classes] of itp; {pointers to names of input and output defaults} {**********************************************} inptr, outptr: itp; {lex level display:} {******************} const maxdis = 32; {maximum depth of display (lexlev + with)} type disprange = 0..maxdis-1; {display index range} dtype = (blck, vrec, crec); {display entry type} var display: array[disprange] of record fname: itp; {root of identifier tree} case occur: dtype of crec: ( dlev: lltype; daddr: addrrange); vrec: ( tnum: integer) end; top, disx, level: disprange; {indices into display} pin, {procedure number} maxpin, {highest number procedure seen so far} ac, {parameter address counter} dc, {fixed data address counter} lc, {local variable address counter} tc: integer; {temporary (with ...) variable counter} prterr: boolean; {print error if ident is undefined} type attributestates = (cst, ref, exp); accessmodes = (direct, byvalue, offset, indirect, indexed); attr = record { attributes of expressions } atype: stp; case akind: attributestates of cst: ( avalue: valu); ref: ( access: accessmodes; alevel: lltype; addr: integer) end; var gattr: attr; { attributes of current expression } lcp: itp; { points to main program ident structure } c: char; function match(var s1: array[1..255] of char; l1: integer; var s2: array[1..255] of char; l2: integer): integer; var i, n: integer; begin {match} n := min(l1, l2); i := 1; while (i <= n) and (s1[i] = s2[i]) do i := succ(i); if i > n then match := l1 - l2 else match := ord(s1[i]) - ord(s2[i]) end {match}; procedure error(n: integer); begin {error} if errinx < 7 then begin errinx := succ(errinx); with errlist[errinx] do begin pos := chcnt; nmr := n end end end {error}; procedure warning (n: integer); begin {warning} if option['W'] then error(n) end {warning}; procedure newpage; begin if option['L'] then begin pageno := pageno + 1; writeln(lst, FF, compiler_version, HT, HT, 'Page ', pageno:5 ); writeln( lst ); linesleft := pagesize end end (* newpage *); procedure beginline; begin if option['L'] then write(lst, linenr:10, level:4, ' ') end (*beginline *); procedure endofline; var k: integer; begin {endofline} writeln(lst); linesleft := linesleft - 1; if errinx > 0 then begin for k := 1 to errinx do with errlist[k] do writeln(lst,' ****',nmr:4,HT,' ':pos,'^'); errtot := errtot + errinx; errinx := 0; linesleft := linesleft - 1 end; if linesleft <= 0 then newpage end {endofline}; procedure insymbol; type chartype = (ctl,oth,dig,let,quo,db0,db1,db2,db3,eos,s00,s01,s02,s03, s04,s05,s06,s07,s08,s09,s10,s11,s12,s13); chartabtype = array[chr(0)..chr(127)] of chartype; const chartab = chartabtype( eos,ctl,ctl,ctl,ctl,ctl,ctl,ctl,ctl,ctl,ctl,ctl,ctl,ctl,ctl,ctl, ctl,ctl,ctl,ctl,ctl,ctl,ctl,ctl,ctl,ctl,ctl,ctl,ctl,ctl,ctl,ctl, oth,oth,quo,s00,oth,oth,oth,quo,s01,s02,s03,s04,s05,s06,db3,s07, dig,dig,dig,dig,dig,dig,dig,dig,dig,dig,db0,s08,db1,s09,db2,oth, s10,let,let,let,let,let,let,let,let,let,let,let,let,let,let,let, let,let,let,let,let,let,let,let,let,let,let,s11,oth,s12,s13,let, oth,let,let,let,let,let,let,let,let,let,let,let,let,let,let,let, let,let,let,let,let,let,let,let,let,let,let,oth,oth,oth,oth,ctl); type chartoktab = array[s00..s13] of symtype; const chartok = chartoktab( (relop,neop), {s00: '#'} (lparen,noop), {s01: '('} (rparen,noop), {s02: ')'} (mulop,mul), {s03: '*'} (addop,plus), {s04: '+'} (comma,noop), {s05: ','} (addop,minus), {s06: '-'} (mulop,idiv), {s07: '/'} (semicolon,noop), {s08: ';'} (relop,eqop), {s09: '='} (atsign,noop), {s10: '@'} (lbrack,noop), {s11: '['} (rbrack,noop), {s12: ']'} (atsign,noop)); {s13: '^'} const NrKeywords = 41; type keywords = array[1..NrKeywords] of record id: idtype; sym: symtype end; const keyword = keywords( ((chr(3),'end '),(endsy,noop)), ((chr(5),'begin '),(beginsy,noop)), ((chr(2),'if '),(ifsy,noop)), ((chr(4),'then '),(thensy,noop)), ((chr(4),'else '),(elsesy,noop)), ((chr(3),'div '),(mulop,idiv)), ((chr(3),'mod '),(mulop,imod)), ((chr(2),'do '),(dosy,noop)), ((chr(5),'while '),(whilesy,noop)), ((chr(6),'repeat '),(repeatsy,noop)), ((chr(5),'until '),(untilsy,noop)), ((chr(4),'with '),(withsy,noop)), ((chr(4),'case '),(casesy,noop)), ((chr(4),'loop '),(loopsy,noop)), ((chr(4),'exit '),(exitsy,noop)), ((chr(3),'not '),(notsy,noop)), ((chr(2),'or '),(addop,orop)), ((chr(3),'and '),(mulop,andop)), ((chr(2),'to '),(tosy,noop)), ((chr(2),'in '),(relop,inop)), ((chr(3),'nil '),(nilsy,noop)), ((chr(3),'for '),(forsy,noop)), ((chr(2),'of '),(ofsy,noop)), ((chr(5),'array '),(arraysy,noop)), ((chr(5),'const '),(constsy,noop)), ((chr(4),'file '),(filesy,noop)), ((chr(6),'packed '),(packedsy,noop)), ((chr(6),'record '),(recordsy,noop)), ((chr(3),'set '),(setsy,noop)), ((chr(4),'type '),(typesy,noop)), ((chr(3),'var '),(varsy,noop)), ((chr(6),'downto '),(downtosy,noop)), ((chr(9),'procedure '),(proceduresy,noop)), ((chr(8),'function '),(functionsy,noop)), ((chr(7),'forward '),(forwardsy,noop)), ((chr(8),'external '),(externalsy,noop)), ((chr(4),'cext '),(ceesy,noop)), ((chr(7),'fortran '),(fortransy,noop)), ((chr(7),'program '),(programsy,noop)), ((chr(4),'goto '),(gotosy,noop)), ((chr(5),'label '),(labelsy,noop)) ); const digmax = 9; var i, j, k, n, scale, radix: integer; r, sf, fac: longreal; digits: array[1..digmax] of 0..9; terminator: char; getnuchar, maxstr, found, sign, useful: boolean; procedure nextch; begin {nextch} if not eoln(src) and not eof(src) then begin read( src, ch ); if ((ch>=' ') or(ch=HT)) and option['L'] then begin write( lst, ch ); chcnt := succ(chcnt); if ch=HT then while chcnt mod 8 = 0 do chcnt:=succ(chcnt) end else if ch=FF then newpage else ch := chr(0) end else if eoln(src) then begin endofline; linenr := succ(linenr); beginline; chcnt := 0; readln( src ); ch := NL end else ch := chr(0) end {nextch}; procedure options; var lch: char; begin {options} repeat nextch; if (ch >= 'a') and (ch <= 'z') then ch := chr(ord(ch)-32); {convert l.c. option to u.c.} if (ch >= 'A') and (ch <= 'Z') then begin lch := ch; nextch; option[lch] := (ch = '+'); nextch end until ch <> ',' end {options}; begin {insymbol} repeat loop while (ch <= ' ') and (ch <> chr(0)) do {skip over blanks and controls} nextch; exit if ch <> '{'; nextch; if ch = '$' then options; while (ch <> '}') and (ch <> chr(0)) do { gobble up comment } nextch; nextch end; getnuchar := true; useful := true; case chartab[ch] of dig:begin sym.sy := intconst; {assume integer until shown otherwise} i := 0; repeat i := i + 1; if i <= digmax then digits[i] := ord(ch) - ord('0'); nextch until chartab[ch] <> dig; if i > digmax then begin error(203); i := digmax end; n := 0; radix := 10; if (ch = 'b') or (ch = 'B') then begin nextch; radix := 8 end else begin scale := 0; if ch = '.' then begin nextch; if ch = '.' then ch := ':' else begin sym.sy := realconst; while chartab[ch] = dig do begin i := i + 1; if i <= digmax then digits[i] := ord(ch) - ord('0'); scale := scale - 1; nextch end end end; if (ch = 'e') or (ch = 'E') then begin sym.sy := realconst; nextch; sign := false; if ch = '+' then nextch else if ch = '-' then begin sign := true; nextch end; while chartab[ch] = dig do begin n := n*10 + (ord(ch) - ord('0')); nextch end; if sign then scale := scale - n else scale := scale + n end; end; if sym.sy = intconst then begin for k := 1 to i do n := n*radix + digits[k]; val.kind := lit; val.ival := n end else begin { realconst } r := 0.0; for k := 1 to i do r := r * 10.0 + float(digits[k]); sf := 1.0; fac := 10.0; if scale < 0 then begin scale := -scale; fac := 1.0/fac; end; while scale > 0 do begin if odd(scale) then sf := sf*fac; fac := fac*fac; scale := scale div 2 end; val.kind := reel; new(val.rval); val.rval@ := r * sf end; getnuchar := false end; let: begin k := 0; repeat if k < alfaleng then begin {Map to lower case in keywords and identifiers} if (ch <= 'Z') and (ch >= 'A') then ch := chr(ord(ch)+32); k := succ(k); id.s[k] := ch; end; nextch until (chartab[ch] <> let) and (chartab[ch] <> dig); id.l := chr(k); j := 0; repeat found := true; j := succ(j); if keyword[j].id.l = id.l then begin i := 1; while found and (i <= k) do if keyword[j].id.s[i] <> id.s[i] then found := false else i := succ(i) end else found := false until found or (j >= NrKeywords); if found then sym := keyword[j].sym else sym := symtype(ident,noop); getnuchar := false end; quo: begin terminator := ch; sym.sy := stringconst; k := 0; maxstr := false; repeat repeat nextch; if not maxstr then if k < strlen then begin string[k] := ch; k := succ(k) end else begin error(205); maxstr := true end until (ch = terminator) or (ch = chr(0)); nextch until ch <> terminator; if terminator = '"' then begin string[k-1] := chr(0); lgth := k end else lgth := pred(k); getnuchar := false; end; db0: begin {':' or ':='} nextch; if ch = '=' then sym := symtype(becomes,noop) else begin sym := symtype(colon,noop); getnuchar := false end end; db1: begin {'<' or '<=' or '<>'} nextch; if ch = '=' then sym := symtype(relop,leop) else if ch = '>' then sym := symtype(relop,neop) else begin sym := symtype(relop,ltop); getnuchar := false end end; db2: begin {'>' or '>='} nextch; if ch = '=' then sym := symtype(relop,geop) else begin sym := symtype(relop,gtop); getnuchar := false end end; db3: begin {'..' or '.'; '..' is changed to ':'} nextch; if ch = '.' then sym := symtype(colon,noop) else begin sym := symtype(period,noop); getnuchar := false end end; s00,s02,s03,s04,s05,s06,s07,s08,s09,s10,s11,s12,s13: sym := chartok[chartab[ch]]; s01: begin {'(', check for (* *) comment} nextch; if ch <> '*' then begin sym := chartok[s01]; getnuchar := false end else begin nextch; if ch = '$' then options; repeat while (ch <> '*') and (ch <> chr(0)) do nextch; nextch until (ch = ')') or (ch = chr(0)); useful := false end; end; oth: begin error(396); sym := symtype(othersy, noop) end; eos: begin sym := symtype(eofsy,noop); getnuchar := false; useful := true end end {case}; if getnuchar then nextch; { write(output,'sym.sy=',ord(sym.sy),NL); break(output) {***DEBUG***} until useful end {insymbol}; procedure skip(tosymbol: symbol); begin {skip} while (sym.sy <> tosymbol) and (sym.sy <> eofsy) do insymbol end {skip}; function searchlevel(fp: itp): itp; var p: itp; i: integer; found: boolean; begin {searchlevel} p := fp; found := false; while not found and (p <> nil) do with p@.name@ do begin i := match(s, ord(l), id.s, ord(id.l)); if i = 0 then found := true else if i > 0 then p := p@.llink else p := p@.rlink end; searchlevel := p end {searchlevel}; function searchid(fs: set of classes): itp; var p: itp; begin {searchid} disx := top; repeat p := searchlevel(display[disx].fname); if p <> nil then begin if not (p@.class in fs) then begin if prterr then error(103); p := nil; disx := pred(disx) end end else disx := pred(disx) until (p <> nil) or (disx < 0); if (p = nil) and prterr then begin error(104); p := udptrs[any(fs)] end; searchid := p end {searchid}; procedure newid(fc: classes; fq: stp; fn: itp; var fp: itp); var c: classes; p, p1, p2: itp; i: integer; lleft: boolean; begin {newid} case fc of types: new(p,types); konst: new(p,konst); vars: new(p,vars); field: new(p,field); proc: new(p,proc) end; with p@ do begin class := fc; new(name,ord(id.l)); with name@ do begin l := id.l; for i := 1 to id.l do s[i] := id.s[i] end; llink := nil; rlink := nil; itype := fq; next := fn end; p2 := display[top].fname; if p2 = nil then display[top].fname := p else begin repeat p1 := p2; with p2@.name@ do begin i := match(s, ord(l), id.s, ord(id.l)); if i <= 0 then begin if i = 0 then error(101); p2 := p2@.rlink; lleft := false end else begin p2 := p2@.llink; lleft := true end end until p2 = nil; if lleft then p1@.llink := p else p1@.rlink := p end; fp := p end {newid}; procedure inittables; {initialize symbol tables with standard and predeclared identifiers and types} const nrbuiltin = 39; {number of builtin procedures and functions} type nametab = array[0..nrbuiltin-1] of idtype; const names = nametab( (chr(3),'get '), (chr(3),'put '), (chr(5),'break '), (chr(4),'seek '), (chr(5),'reset '), (chr(7),'rewrite '), (chr(6),'update '), (chr(4),'read '), (chr(6),'readln '), (chr(5),'write '), (chr(7),'writeln '), (chr(3),'eof '), (chr(4),'eoln '), (chr(3),'new '), (chr(4),'free '), (chr(4),'mark '), (chr(7),'release '), (chr(4),'pred '), (chr(4),'succ '), (chr(3),'any '), (chr(3),'all '), (chr(3),'odd '), (chr(3),'ord '), (chr(3),'chr '), (chr(5),'float '), (chr(5),'trunc '), (chr(5),'round '), (chr(3),'max '), (chr(3),'min '), (chr(4),'ceil '), (chr(5),'floor '), (chr(3),'abs '), (chr(3),'sqr '), (chr(4),'sqrt '), (chr(2),'ln '), (chr(3),'exp '), (chr(3),'sin '), (chr(3),'cos '), (chr(6),'arctan ')); var p, p1: itp; q: stp; i,j,k,l: integer; begin {inittables} {Initialize display} level := 0; top := 0; with display[0] do begin fname := nil; occur := blck end; {***integer***} new(intptr,integert); with intptr@ do begin form := integert; size := 2; subrange := false end; id := idtype(chr(7),'integer '); newid(types,intptr,nil,p); {***real***} new(realptr,longrealt); with realptr@ do begin form := longrealt; size := 8; subrange := false end; id := idtype(chr(8),'longreal '); newid(types,realptr,nil,p); new(realptr,realt); with realptr@ do begin form := realt; size := 4; subrange := false end; id := idtype(chr(4),'real '); newid(types,realptr,nil,p); {***char***} new(charptr,chart); with charptr@ do begin form := chart; size := 1; subrange := false end; id := idtype(chr(4),'char '); newid(types,charptr,nil,p); {***false,true,boolean***} new(boolptr,booleant); with boolptr@ do begin form := booleant; size := 1; subrange := false end; id := idtype(chr(5),'false '); newid(konst,boolptr,nil,p); p@.value.ival := 0; id := idtype(chr(4),'true '); newid(konst,boolptr,p,p); p@.value.ival := 1; boolptr@.maxconst := p; id := idtype(chr(7),'boolean '); newid(types,boolptr,nil,p); {***text***} new(textptr,filet); with textptr@ do begin form := filet; size := 1; filtyp := charptr end; id := idtype(chr(4),'text '); newid(types,textptr,nil,p); {***nil (need nilptr for type)***} new(nilptr,pointer); with nilptr@ do begin form := pointer; size := 2; eltype := nil end; {***maxint***} id := idtype(chr(6),'maxint '); newid(konst,intptr,nil,p); p@.value.ival := 32767; {***builtin procedures and functions***} for i := 0 to nrbuiltin-1 do begin id := names[i]; newid(proc,nil,nil,p); with p@ do begin pkind := stnd; psinx := i end end; {***enter undeclared identifiers***} id.l := chr(3); id.s[1] := '.'; id.s[2] := 'u'; id.s[3] := 't'; newid(types,nil,nil,udptrs[types]); id.s[3] := 'c'; newid(konst,nil,nil,udptrs[konst]); id.s[3] := 'v'; newid(vars,nil,nil,udptrs[vars]); udptrs[vars]@.vkind := local; id.s[3] := 'f'; newid(field,nil,nil,udptrs[field]); id.s[3] := 'p'; newid(proc,nil,nil,udptrs[proc]); udptrs[proc]@.pkind := decl end {inittables}; procedure printtables (fb: boolean); var i,lim: disprange; procedure markctp (fp: itp); forward; procedure markstp (fp: stp); begin {markstp} if fp <> nil then with fp@ do begin marked := true; case form of sett: markstp(settyp); arrayt: begin markstp(aeltyp); markstp(inxtyp) end; recordt: begin markctp(fstfld); markstp(recvar) end; filet: markstp(filtyp); tagfield: markstp(fstvar); variant: begin markstp(nxtvar); markstp(subvar) end end {case} end {with} end {markstp}; procedure markctp { (fp: itp) }; begin {markctp} if fp <> nil then with fp@ do begin { write(output,"c:",fp:8,llink:8,rlink:8,itype:8,NL); break(output); {DEBUG} markctp(llink); markctp(rlink); markstp(itype) end {with} end {markctp}; procedure followctp (fp: itp); forward; procedure followstp (fp: stp); type typenamtab = array[forms] of array[0..7] of char; const typename = typenamtab( 'scalar ','boolean ','char ','integer ', 'longint ','real ','longreal','pointer ', 'set ','array ','record ','file ', 'tagfield','variant '); begin {followstp} if fp <> nil then with fp@ do begin write(lst,' ',typename[form]); if marked then begin marked := false; case form of pointer: begin write(lst,' to'); followstp(eltype) end; sett:begin followstp(settyp) end; arrayt:begin write(lst,' ['); followstp(inxtyp); write(lst,'] of'); followstp(aeltyp) end; recordt:begin write(lst,' of',NL); followctp(fstfld); followstp(recvar) end; filet:begin followstp(filtyp) end; tagfield:begin followstp(fstvar) end; variant:begin followstp(nxtvar); followstp(subvar) end end {case} end {if marked} end {fp <> nil} end {followstp}; procedure followctp { (fp: itp) }; var ch:char; i:integer; begin {followctp} if fp <> nil then with fp@ do begin followctp(rlink); write(lst, ' '); if name@.l <> chr(0) then write(lst, name@.s:ord(name@.l)); write(lst,' ':16-ord(name@.l),llink:8,rlink:8,itype:8,next:8,' ':4); case class of types:write(lst,'type'); konst:begin write(lst,'constant':13); end; vars:begin case vkind of formal: write(lst, 'parm by ref':13); param: write(lst, 'parm by val':13); local: write(lst, 'variable':13) end; write(lst, vlev:4, vaddr:6); if itype <> nil then write(lst, itype@.size:6, ':') else write(lst, ' ':7); end; field:write(lst,'field':13,fdisp:10); proc:begin write(lst,'procedure'); if pkind = extn then write(lst, ' Ext') else if pkind = cee then write(lst, ' Cxt') else if pkind = fort then write(lst, ' Ftn') else write(lst, ' ') end end; {case} followstp(itype); writeln(lst); followctp(llink) end {with} end {followctp}; begin {printtables} writeln(lst); writeln( lst, ' ' ); if fb then lim:=0 else begin lim:=top; write(lst,'Local ') end; writeln(lst,'Tables'); for i:=top downto lim do markctp(display[i].fname); for i:=top downto lim do followctp(display[i].fname); writeln(lst); if ch <> NL then write(lst,' ':chcnt+8) end {printtables}; procedure getbounds(fq: stp; var fmin, fmax: integer); begin {getbounds} fmin := 0; fmax := 0; { until shown otherwise } if fq <> nil then with fq@ do if subrange then begin fmin := minvalue; fmax := maxvalue end else case form of scalar, booleant: fmax := maxconst@.value.ival; chart: fmax := 255 end end {getbounds}; procedure genbyte(fi: integer); begin int@ := chr(fi); put(int) end (*genbyte*); procedure genid(fid: @idtype); var i: integer; begin if fid <> nil then with fid@ do begin genbyte(ord(l)); for i := 1 to ord(l) do genbyte(ord(s[i])) end end (*genid*); procedure block (fp: itp); type blockparts = (headpart, labelpart, constpart, typepart, varblpart, procpart); {************TARGET MACHINE DEPENDENT STUFF FOLLOWS:**************} type sizetables = array[forms] of integer; const bitsau = 8; { bits in addressable unit } auword = 2; { addressable units in a 'preferred' size word } bitswd = bitsau*auword; { bits in a 'preferred' size word } maxlit = bitswd; { bits in largest literal } usize = sizetables( { size of forms in addressable units } 1 {scalar}, 1 {boolean}, 1 {char}, 2 {int}, 4 {longint}, 4 {real}, 8 {longreal}, 2 {pointer}, 0 {set}, 0 {array}, 0 {record}, 522 {file}, 0 {tagfield}, 0 {variant} ); ualign = sizetables( { required alignment of forms in addressable units } 1 {scalar}, 1 {boolean}, 1 {char}, 2 {int}, 2 {longint}, 2 {real}, 2 {longreal}, 2 {pointer}, 0 {set}, 0 {array}, 0 {record}, 2 {file}, 0 {tagfield}, 0 {variant}); type litshfttab = array[0..maxlit div bitsau] of integer; const shfttab = litshfttab( { use to "shift" au's into a literal } 1, 256); var fwptr, varlst, p: itp; q1, q2, q3: stp; rvsize, filecount: integer; declstate: blockparts; function floor(fa, fb: integer): integer; {***THIS SHOULD BE BUILT-IN} begin {floor} floor := fa div fb * fb end {floor}; function ceil(fa, fb: integer): integer; {***ALSO SHOULD BE BUILT-IN?} begin {ceil} ceil := (fa + (fb - 1)) div fb * fb end {ceil}; function typsize(fq: stp): integer; forward; function typalign(fq: stp): integer; begin {typalign} if fq <> nil then with fq@ do case form of scalar, booleant, chart, integert, longintt, realt, longrealt, pointer, filet, tagfield, variant: typalign := ualign[form]; sett, recordt: if typsize(fq) < auword then typalign := 1 else typalign := 2; arrayt: typalign := typalign(aeltyp) end else typalign := 1 end {typalign}; function setsize(fq: stp): integer; begin {setsize} if fq <> nil then with fq@ do if (form < pointer) and subrange then if (minvalue >= 0) and (maxvalue <= 255) then setsize := maxvalue else begin error(115); setsize := 0 end else case form of scalar, booleant: setsize := maxconst@.value.ival + 1; chart: setsize := 128; { why not 256 ? } integert: setsize := 256; { arbitrary decision? } longintt, realt, longrealt, pointer, sett, arrayt, recordt, filet, tagfield, variant: {default} begin error(115); setsize := 0 end end else setsize := 0 end {setsize}; function typsize { (fq: stp): integer }; begin {typsize} if fq <> nil then with fq@ do case form of scalar, booleant, chart, integert, longintt, realt, longrealt, pointer, filet: typsize := usize[form]; sett: if size < bitswd then typsize := (size + (bitsau-1)) div bitsau else typsize := ((size + (bitswd-1)) div bitswd)*auword; arrayt: typsize := ceil(typsize(aeltyp), typalign(aeltyp)) * size; { round-up } recordt, tagfield, variant: typsize := size end else typsize := 0 end {typsize}; procedure genword(fi: integer); begin int@ := chr(fi div 256); put(int); int@ := chr(fi); put(int) end {genword}; procedure genlit(fi: integer); begin genbyte(162); genword(fi) end {genlit}; procedure gendat(var fattr: attr); var i, j, n: integer; begin {gendat} if (fattr.akind = cst) and (fattr.avalue.kind = setc) then if fattr.atype <> nil then with fattr.atype@ do begin if size <= maxlit then begin n := 0; j := 1; for i := 0 to size-1 do begin if i in fattr.avalue.sval@ then n := n + j; j := j * 2 end; fattr.avalue.kind := lit; fattr.avalue.ival := n end else begin if odd(dc) then dc := succ(dc); error( not_yet_impl ); for i := 0 to size div bitswd do { do a gendword on each word of set }; fattr.avalue.kind := data; fattr.avalue.daddr := dc end end end {gendat}; procedure gencon(var fattr: attr); var i: integer; begin {gencon} gendat(fattr); case fattr.avalue.kind of lit: genlit(fattr.avalue.ival); reel: begin genbyte(164 {LITD}); with fattr.avalue do for i:=0 to 3 do genword(xval@[i]) end; data: begin genbyte(163 {RDATA}); genword(fattr.addr) end end; fattr.akind := exp end {gencon}; procedure gendif(fi: integer); begin if fi <> 0 then if fi > 0 then begin genlit(fi); genbyte(32 {IADD}) end else begin genlit(-fi); genbyte(33 {ISUB}) end end {gendif}; procedure gendbyte(fi: integer); begin dat@ := chr(fi); put(dat); dc := succ(dc) end {gendbyte}; procedure gendword(fi: integer); begin dat@ := chr(fi); put(dat); dat@ := chr(fi div 256); put(dat); dc := dc + 2 end {gendword}; function comptypes(fq1, fq2: stp): boolean; var form2: forms; begin {comptypes} comptypes := true; { until shown otherwise } if (fq1 <> fq2) and (fq1 <> nil) and (fq2 <> nil) then begin form2 := fq2@.form; with fq1@ do begin case form of scalar: { to handle subranges, identifier list must be same } {P-compiler note: "indentical scalars declared on different levels are not recognized to be compatible"} comptypes := (form2 = scalar) and (maxconst = fq2@.maxconst); booleant, chart: comptypes := (form2 = form); integert, longintt: comptypes := (form2 = integert) or (form2 = longintt); realt, longrealt: comptypes := (form2 = realt) or (form2 = longrealt); pointer: {P-compiler note: code is much different here} comptypes := (form2 = pointer) and comptypes(eltype, fq2@.eltype); sett: comptypes := (form2 = sett) and comptypes(settyp, fq2@.settyp); arrayt: comptypes := (form2 = arrayt) and comptypes(aeltyp, fq2@.aeltyp); {P-compiler note: sizes must also be equal} {P-compiler note: "alternatives: ... indextype must be compatible. ... lowbounds must be the same."} recordt: comptypes := false; {P-compiler note:"identical records are recognized to be compatible iff no variants occur"} filet: comptypes := (form2 = filet) and comptypes(filtyp, fq2@.filtyp) end end end end {comptypes}; procedure expression; forward; procedure valueexpression; begin {valueexpression} expression; if gattr.akind = cst then begin gencon(gattr); gattr.akind := exp end end {valueexpression}; procedure setcoerce(fq: stp); begin {setcoerce} if (gattr.atype = nil) or (gattr.atype@.form <> sett) or (gattr.atype@.settyp = nil) then gattr.atype := fq else if gattr.atype@.settyp@.subrange then { check for inclusion } end {setcoerce}; procedure selector (fp: itp); var lattr: attr; p: itp; q: stp; loffset: integer; more: boolean; procedure genload; begin {genload} case gattr.akind of cst: case gattr.avalue.kind of data: begin genbyte(163 {RDATA}); genword(gattr.avalue.daddr); gattr.akind := ref end; reel: { coming attraction }; setc: { coming attraction } end; ref: begin case gattr.access of direct: genbyte(176 {VARBL} + gattr.alevel); byvalue: genbyte(192 {PARAM} + gattr.alevel); offset: genbyte(132 {OFSET}); indirect: genbyte(133 {INDIR}); indexed: genbyte(134 {INDEX}) end; genbyte(typsize(gattr.atype)); genword(gattr.addr) end end end {genload}; begin {selector} with fp@ do begin gattr.atype := itype; case class of konst: begin gattr.akind := cst; gattr.avalue := value; end; vars: begin gattr.akind := ref; if vkind = local then gattr.access := direct else gattr.access := byvalue; gattr.alevel := vlev; gattr.addr := vaddr; if vkind = formal then begin genload; gattr.access := indirect; gattr.addr := 0 end end; field: begin with display[disx] do begin gattr.akind := ref; { doesn't work with record structured constants } if occur = crec then begin { direct reference } gattr.access := direct; gattr.alevel := dlev; gattr.addr := daddr end else begin { indirect reference } genbyte(140 {RTEMP}); genbyte(tnum); gattr.access := indirect; gattr.addr := 0 end; gattr.addr := gattr.addr + fdisp end end end {case} end; {with} repeat more := false; if sym.sy = atsign then begin if gattr.atype <> nil then begin if gattr.atype@.form = pointer then gattr.atype := gattr.atype@.eltype else if gattr.atype@.form = filet then gattr.atype := gattr.atype@.filtyp else error(141); genload; gattr.access := indirect; gattr.addr := 0 end; insymbol; more := true end { atsign } else if sym.sy = period then begin if gattr.access = indexed then begin genload; gattr.access := offset; gattr.addr := 0 end; if (gattr.atype <> nil) and (gattr.atype@.form <> recordt) then begin error(140); gattr.atype := nil end; insymbol; if sym.sy = ident then begin if gattr.atype <> nil then begin p := searchlevel(gattr.atype@.fstfld); if p <> nil then begin gattr.atype := p@.itype; gattr.addr := gattr.addr + p@.fdisp end else begin error(152); gattr.atype := nil end end; insymbol end else error(2); more := true end { period } else if sym.sy = lbrack then begin repeat if (gattr.atype <> nil) and (gattr.atype@.form <> arrayt) then begin error(138); gattr.atype := nil end; insymbol; genload; lattr := gattr; expression; if lattr.atype <> nil then with lattr.atype@ do begin if (inxtyp <> nil) and inxtyp@.subrange then loffset := - inxtyp@.minvalue else loffset := 0; if not comptypes(gattr.atype, inxtyp) then error(139); if gattr.akind = cst then begin gattr.avalue.ival := gattr.avalue.ival + loffset; gencon(gattr); gattr.akind := exp end else gendif(loffset); if aeltyp <> nil then if aeltyp@.form = arrayt then gattr.addr := aeltyp@.size { index multiplier } else gattr.addr := ceil(typsize(aeltyp), typalign(aeltyp)); gattr.atype := aeltyp; gattr.akind := lattr.akind; gattr.access := indexed end until sym.sy <> comma; if sym.sy = rbrack then insymbol else error(12); more := true end { lbrack }; if sym.sy = eofsy then more := false until not more; genload end {selector}; procedure binop(fop: operator; var fattr: attr); type binoptab = array[forms, operator] of 0..255; const binopcodes = binoptab ( { * and / mod + - or < <= >= > <> = in max min cei flo nop } ( 0, 0, 0, 0, 16, 17, 0, 29, 27, 28, 26, 25, 24, 0, 30, 31, 0, 0, 0),{ scalar } ( 0,111, 0, 0, 0, 0,110,109,107,108,106,105,104, 0, 0, 0, 0, 0, 0),{ booleant } ( 0, 0, 0, 0, 16, 17, 0, 29, 27, 28, 26, 25, 24, 0, 30, 31, 0, 0, 0),{ chart } ( 34, 0, 35, 36, 32, 33, 0, 61, 59, 60, 58, 57, 56, 0, 62, 63, 44, 45, 0),{ integert } ( 34, 0, 35, 36, 32, 33, 0, 61, 59, 60, 58, 57, 56, 0, 62, 63, 44, 45, 0),{ longintt } ( 66, 0, 67, 0, 64, 65, 0, 93, 91, 92, 90, 89, 88, 0, 94, 95, 0, 0, 0),{ realt } ( 66, 0, 67, 0, 64, 65, 0, 93, 91, 92, 90, 89, 88, 0, 94, 95, 0, 0, 0),{ longrealt } ( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 24, 0, 0, 0, 0, 0, 0),{ pointer } (114, 0, 0, 0,113,115, 0, 0,123,124, 0,121,120,126, 0, 0, 0, 0, 0),{ sett } ( 0, 0, 0, 0, 0, 0, 0,173,171,172,170,169,168, 0, 0, 0, 0, 0, 0),{ arrayt } ( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),{ recordt } ( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),{ filet } ( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),{ tagfield } ( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));{ variant } var q: stp; s: @stndset; r: @longreal; opcode: 0..255; begin {binop} if (fop = inop) and (gattr.atype <> nil) then if gattr.atype@.form = sett then q := gattr.atype@.settyp else begin error(134); q := nil end else q := gattr.atype; if not comptypes(q, fattr.atype) then error(134); if gattr.atype <> nil then begin opcode := binopcodes[gattr.atype@.form][fop]; if opcode = 0 then error(134) else if gattr.atype@.form = arrayt then begin if not comptypes(gattr.atype@.aeltyp, charptr) then error(134); if fattr.akind = cst then gencon(fattr); if gattr.akind = cst then gencon(gattr) else if fattr.akind = cst then genbyte(1 {XCH}); genbyte(opcode); genbyte(1); genword(min(gattr.atype@.size, fattr.atype@.size)) end else begin if fattr.akind <> cst then begin { ?? } if gattr.akind = cst then { } gencon(gattr); genbyte(opcode) end else { ?? } if gattr.akind <> cst then begin { } gencon(fattr); genbyte(1 {XCH}); genbyte(opcode) end else { } case gattr.atype@.form of scalar, booleant, chart, integert: case fop of plus: gattr.avalue.ival := fattr.avalue.ival + gattr.avalue.ival; minus: gattr.avalue.ival := fattr.avalue.ival - gattr.avalue.ival; mul: gattr.avalue.ival := fattr.avalue.ival * gattr.avalue.ival; idiv: gattr.avalue.ival := fattr.avalue.ival div gattr.avalue.ival; imod: gattr.avalue.ival := fattr.avalue.ival mod gattr.avalue.ival; ltop: gattr.avalue.ival := ord(fattr.avalue.ival < gattr.avalue.ival); leop: gattr.avalue.ival := ord(fattr.avalue.ival <= gattr.avalue.ival); geop: gattr.avalue.ival := ord(fattr.avalue.ival >= gattr.avalue.ival); gtop: gattr.avalue.ival := ord(fattr.avalue.ival > gattr.avalue.ival); neop: gattr.avalue.ival := ord(fattr.avalue.ival <> gattr.avalue.ival); eqop: gattr.avalue.ival := ord(fattr.avalue.ival = gattr.avalue.ival); maxop: if fattr.avalue.ival > gattr.avalue.ival then gattr.avalue.ival := fattr.avalue.ival; minop: if fattr.avalue.ival < gattr.avalue.ival then gattr.avalue.ival := fattr.avalue.ival; ceilop, floorop: error(not_yet_impl); andop: if (fattr.avalue.ival <> 0) and (gattr.avalue.ival <> 0) then gattr.avalue.ival := 1 else gattr.avalue.ival := 0; orop: if (fattr.avalue.ival <> 0) or (gattr.avalue.ival <> 0) then gattr.avalue.ival := 1 else gattr.avalue.ival := 0 end; {case fop} realt, longrealt: begin if fop <= minus then begin new(r); r@ := gattr.avalue.rval@; gattr.avalue.rval := r end; case fop of plus: gattr.avalue.rval@ := fattr.avalue.rval@ + gattr.avalue.rval@; minus: gattr.avalue.rval@ := fattr.avalue.rval@ - gattr.avalue.rval@; mul: gattr.avalue.rval@ := fattr.avalue.rval@ * gattr.avalue.rval@; idiv: gattr.avalue.rval@ := fattr.avalue.rval@ / gattr.avalue.rval@ end end; sett: begin if fop <= orop then begin new(s); s@ := gattr.avalue.sval@; gattr.avalue.sval := s end; case fop of plus: gattr.avalue.sval@ := fattr.avalue.sval@ + gattr.avalue.sval@; minus: gattr.avalue.sval@ := fattr.avalue.sval@ - gattr.avalue.sval@; mul: gattr.avalue.sval@ := fattr.avalue.sval@ * gattr.avalue.sval@; inop: gattr.avalue.ival := ord(fattr.avalue.ival in gattr.avalue.sval@); ltop, leop, geop, gtop, neop, eqop: error(not_yet_impl) end end end {case form} end {else opcode <> 0} end {not nil} end {binop}; procedure call (fp: itp); var lattr: attr; i: integer; const nrbuiltin = 39; type doargtab = array[0..nrbuiltin-1] of set of (hasarg, getarg, dfltarg); {hasarg: will be arg list} {getarg: parse arg list here} {dfltarg: arg list may be incomplete or missing} const doarg = doargtab( [hasarg], [hasarg], [hasarg], [hasarg], { get - seek } [hasarg], [hasarg], [hasarg], { reset - update } [dfltarg], [dfltarg], [dfltarg], [dfltarg], { read - writeln } [hasarg], [hasarg], { eof - eoln } [hasarg,getarg], [hasarg,getarg], [], [], { new - release } [hasarg,getarg], [hasarg,getarg], { pred - succ } [hasarg,getarg], [hasarg,getarg], { any - all } [hasarg,getarg], [hasarg,getarg], { odd - ord } [hasarg,getarg], [hasarg,getarg], { chr - float } [hasarg,getarg], [hasarg,getarg], { trunc - round } [hasarg,getarg], [hasarg,getarg], { max - min } [hasarg,getarg], [hasarg,getarg], { ceil - floor } [hasarg,getarg], { abs } [hasarg,getarg], [hasarg,getarg], { sqr - sqrt } [hasarg,getarg], [hasarg,getarg], { ln - exp } [hasarg,getarg], [hasarg,getarg], { sin - cos } [hasarg,getarg]); { arctan } procedure calluser (fp: itp); var p: itp; q: stp; nrofparm, retvsize: integer; begin {calluser} nrofparm := 0; p := fp@.next; { head of formal parameter list } if fp@.itype <> nil then begin { its a function } retvsize := typsize(p@.itype); p := p@.next { skip over returned value dummy } end else retvsize := 0; if sym.sy = lparen then begin { parse actual parameter list } repeat insymbol; valueexpression; if p <> nil then begin q := p@.itype; { type of formal parameter } if q <> nil then if not comptypes(gattr.atype, q) then error(142) else if q@.form = sett then setcoerce(q); { make empty sets behave } if p@.vkind = formal then begin if gattr.akind <> ref then error(154); genbyte(9 {REFER}) end; p := p@.next end else error(126); { nr actuals > nr formals } nrofparm := succ(nrofparm) until sym.sy <> comma; if sym.sy = rparen then insymbol else error(4) end; if p <> nil then error(126); { nr formals > nr actuals } if fp@.paddr = 0 then error(401); { illegal call on main program } genbyte(208 + fp@.plev {CALL}); genbyte(retvsize); genbyte(fp@.paddr); genbyte(nrofparm); gattr.atype := fp@.itype end {calluser}; procedure callnew; var q, q1: stp; n, m: integer; hasvariablepart: boolean; begin {callnew} q := nil; n := 0; hasvariablepart := false; if gattr.atype <> nil then if gattr.atype@.form = pointer then begin q1 := gattr.atype@.eltype; if q1 <> nil then begin n := typsize(q1); if q1@.form = recordt then q := q1@.recvar end end else error(116); while sym.sy = comma do begin insymbol; expression; if q<> nil then begin if q@.form = tagfield then begin if not comptypes(gattr.atype, q@.tagtyp) then error(116); n := typsize(q); if gattr.akind = cst then begin q1 := q@.fstvar; while (q1 <> nil) and (q1@.varval <> gattr.avalue.ival) do q1 := q1@.nxtvar; if q1 <> nil then begin n := q1@.size; q := q1@.subvar end end else error(50) end else if q@.form = arrayt then begin m := typsize(q@.aeltyp); n := n - (m * q@.size); if gattr.akind = cst then n := n + (gattr.avalue.ival * m) else begin genlit(m); genbyte(34 {IMUL}); hasvariablepart := true end; q := nil end end end; genlit(n); if hasvariablepart then genbyte(32 {IADD}); genbyte(138 {INVOK}); genbyte(1); genbyte(4 {new}); genbyte(10 {STOL}) end {callnew}; procedure fileproc (apsinx:integer; candefault:boolean); { call builtin file procedures } var noarg,defaulted:boolean; p,fileptr: itp; filetype: stp; {to retain type of file} procedure readwrite(doline:boolean); {implements the read, readln, write, and writeln calls. doline tells whether to add the final call to complete a line} type rwtab = array[forms] of integer; {for read/write proc codes} const readcode = rwtab( 0, 0,24,30, 0,34,36, 0, 0, 0, 0, 0, 0, 0); writecode = rwtab( 0,29,25,31, 0,35,37,39, 0,27, 0, 0, 0, 0); var code, nrcalls, nrparm: integer; arglist: boolean; begin {readwrite} genbyte(177 {VARBL}); genbyte(2); genword(0); genbyte(1 {XCH}); genbyte(10 {STOL}); nrcalls := 0; arglist := false; {process arg list if present} if (not noarg) and (defaulted or not (sym.sy = rparen)) then begin {has arg list} arglist := true; if apsinx < 9 then {read/readln} loop nrcalls := succ(nrcalls); expression; {compute varble} if gattr.atype <> nil then begin code := readcode[gattr.atype@.form]; if code = 0 then error(116) end else code := 0; genbyte(9 {REFER}); genbyte(138 {INVOK}); genbyte(2); genbyte(code); exit if sym.sy <> comma; insymbol; genbyte(177 {VARBL}); genbyte(2); genword(0) end {loop} else {write/writeln} loop nrcalls := succ(nrcalls); nrparm := 3; {until otherwise proven} valueexpression; if gattr.atype <> nil then begin code := writecode[gattr.atype@.form]; if code = 27 {array} then if gattr.atype@.aeltyp = charptr then begin genbyte(9 {REFER}); genlit(gattr.atype@.size); nrparm := 4 end else code := 0; if code = 0 then error(116) end else code := 0; if sym.sy = colon then begin {field width} if not comptypes(filetype,textptr) then error(169); {must be text-like} insymbol; valueexpression; if not comptypes(gattr.atype, intptr) then error(116); if (code=35) or (code=37) then {reals} if sym.sy = colon then begin {fraction width} insymbol; valueexpression; if not comptypes(gattr.atype, intptr) then error(116); nrparm := 4 end else code := code+6; {use e-stype output} end else begin genlit(0); {default field width} if (code = 35) or (code = 37) then begin {default for reals} genlit(0); nrparm := 4; end end; genbyte(138 {INVOK}); genbyte(nrparm); genbyte(code); exit if sym.sy <> comma; insymbol; genbyte(177 {VARBL}); genbyte(2); genword(0) end{loop} end{arglist}; if doline then begin {readln/writeln} code := 44; {readln until proven otherwise} if apsinx = 10 then code := succ(code); if nrcalls <> 0 then begin genbyte(177 {VARBLE}); genbyte(2); genword(0) end; genbyte(138 {INVOK}); genbyte(1); genbyte(code); nrcalls := succ(nrcalls) end{doline}; genbyte(152 {SEQ}); genbyte(pred(nrcalls)) end {readwrite}; begin {fileproc} {First, determine the state of the argument list and set up the environment so that subsequent processing will see things uniformly} noarg := false; {Assume there is an arg list} defaulted := false; {Assume there is a file name} filetype := textptr; {an initial assumption} if candefault and (sym.sy <> lparen) then begin {No arglist} noarg := true; defaulted := true; if odd(apsinx) {read or write} then error(20) end else begin {arglist present} if candefault then insymbol; {gobble up '('} if sym.sy = ident then begin {may be file spec} p := searchid([types,konst,vars,field,proc]); if p@.itype <> nil then if (p@.itype@.form <> filet) or ((ch = '^') or (ch = '@')) {KLUDGE!!!} then defaulted := true end else defaulted := true end; if defaulted and not candefault then error(116) else if not defaulted then {must be a file name} begin expression; filetype := gattr.atype end else begin {defaulted file name} fileptr := inptr; {Assume input} if apsinx > 8 then fileptr := outptr; {It's output} selector(fileptr) {Make the file available} end; {now, implement the specified procedure} if apsinx >= 11 then begin {eof, eoln} if apsinx = 11 then begin {eof} genbyte(132 {OFSET}); genbyte(1 {size}); genword(2 {offset to eof byte}); end else begin {eoln} {For DEC OSs, this should be "filetype<>textptr"} {if not comptypes(filetype,textptr) then error(169); (UNIX) } if filetype <> textptr then error(169); { DEC OS } {Fake a genload on fileptr@; no need to change gattr} genbyte(133 {INDIR}); genbyte(1 {char size}); genword(0 {offset}); genlit(10{Ascii NL}); genbyte(24 {UCEQ}) end; gattr.atype := boolptr end else begin genbyte(9 {REFER}); {Remaining procs REFER to fileblock} case apsinx of 0,1: begin {get - put} genlit(typsize(gattr.atype@.filtyp)); genbyte(138 {INVOK}); genbyte(2); genbyte(apsinx+21) end; 2: begin {break} genbyte(138 {INVOK}); genbyte(1); genbyte(23) end; 3: begin { seek } warning( 413 ); if sym.sy = comma then begin insymbol; valueexpression; genbyte( 138 ); genbyte( 2 ); genbyte( 18 ) end end; 4, 5, 6: begin {reset, rewrite, update} {Rewritten to permit "anonymous" files, according to standard} if apsinx = 6 {update} then warning( 413 ); if sym.sy = comma then begin {Assume filename} insymbol; {gobble up ','} valueexpression; {parse it} if (gattr.atype <> nil) and ((gattr.atype@.form <> arrayt) or (gattr.atype@.aeltyp <> charptr)) then error(116); genbyte(9 {REFER}); { For DEC OS, check for 3rd arg, file attribute (integer): } if sym.sy = comma then begin insymbol; valueexpression end else genlit(0 {default file attr}) end else genlit(0 {nil}); genlit(apsinx-4); {0=>reset, 1=>rewrite, 2=>update} genbyte(138 {INVOK}); genbyte(4); genbyte(17) end; 7, 9: begin {read, write} if not defaulted then if sym.sy = comma then insymbol else error(20); readwrite(false) end; 8, 10: begin {readln, writeln} if not comptypes(filetype,textptr) then error(169); if (not defaulted) and (sym.sy = comma) then insymbol; readwrite(true) end end {case} end {else case}; if not noarg then if sym.sy = rparen then insymbol else error(4) end {fileproc}; begin {call} with fp@ do begin if pkind <> stnd then calluser(fp) else begin { pkind = stnd } if hasarg in doarg[psinx] then begin if sym.sy = lparen then begin insymbol; if getarg in doarg[psinx] then expression end else error(9) end; case psinx of 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12: { get - eoln } fileproc(psinx, dfltarg in doarg[psinx]); 13: callnew; { new } 15, 16: begin { mark - release } genbyte(138 {INVOK}); genbyte(0 {nr of args}); genbyte(psinx-9) end; 17: begin { pred } if (gattr.atype <> nil) and (gattr.atype@.form > integert) then error(125); if gattr.akind = cst then gattr.avalue.ival := pred(gattr.avalue.ival) else genbyte(17 {PRED}) end; 18: begin { succ } if (gattr.atype <> nil) and (gattr.atype@.form > integert) then error(125); if gattr.akind = cst then gattr.avalue.ival := succ(gattr.avalue.ival) else genbyte(16 {SUCC}) end; 19: { any } if gattr.atype@.form = sett then begin if gattr.akind = cst then gattr.avalue.ival := any(gattr.avalue.sval@) else genbyte(127 {SANY}); gattr.atype := gattr.atype@.settyp end else error(125); 21: begin { odd } if comptypes(gattr.atype, intptr) then if gattr.akind = cst then gattr.avalue.ival := ord(odd(gattr.avalue.ival)) else genbyte(42 {IODD}) else error(125); gattr.atype := boolptr end; 22: begin { ord } if (gattr.atype <> nil) and (gattr.atype@.form > integert) then error(125); gattr.atype := intptr end; 23: begin { chr } if not comptypes(gattr.atype, intptr) then error(125); gattr.atype := charptr end; 24: begin { float } if not comptypes(gattr.atype, intptr) then error(125); if gattr.akind = cst then begin i := gattr.avalue.ival; new(gattr.avalue.rval); gattr.avalue.rval@ := float(i); gattr.avalue.kind := reel end else genbyte(74 {FLOAT}); gattr.atype := realptr end; 25: begin { trunc } if not comptypes(gattr.atype, realptr) then error(125); if gattr.akind = cst then begin gattr.avalue.ival := trunc(gattr.avalue.rval@); gattr.avalue.kind := lit end else genbyte(75 {TRUNC}); gattr.atype := intptr end; 26: begin { round } if not comptypes(gattr.atype, realptr) then error(125); if gattr.akind = cst then begin gattr.avalue.ival := round(gattr.avalue.rval@); gattr.avalue.kind := lit end else genbyte(76 {ROUND}); gattr.atype := intptr end; 27, 28: begin { max - min } lattr := gattr; if sym.sy = comma then insymbol else error(20); expression; if psinx = 27 then binop(maxop, lattr) else binop(minop, lattr) end; 31: { abs } if gattr.atype <> nil then if comptypes(gattr.atype, intptr) then if gattr.akind = cst then gattr.avalue.ival := abs(gattr.avalue.ival) else genbyte(41 {IABS}) else if comptypes(gattr.atype, realptr) then if gattr.akind = cst then gattr.avalue.rval@ := abs(gattr.avalue.rval@) else genbyte(73 {FABS}) else error(125); 14, { free } 20, { all } 29, { ceil } 30: error(not_yet_impl); { floor } 32: { sqr } if gattr.atype <> nil then if comptypes(gattr.atype, intptr) then if gattr.akind = cst then gattr.avalue.ival := sqr(gattr.avalue.ival) else genbyte(37 {ISQR}) else if comptypes(gattr.atype, realptr) then if gattr.akind = cst then gattr.avalue.rval@ := sqr(gattr.avalue.rval@) else genbyte(69 {FSQR}) else error(125); 33, { sqrt } 34, { ln } 35, { exp } 36, { sin } 37, { cos } 38: { arctan } begin if gattr.akind = cst then gencon(gattr); if not comptypes(gattr.atype, realptr) then if comptypes(gattr.atype, intptr) then genbyte(74 {FLOAT}) else error(125); genbyte(138 {INVOK}); genbyte(1 {nr of args}); genbyte(psinx + 68); gattr.atype := realptr end end {case}; if getarg in doarg[psinx] then begin if sym.sy = rparen then insymbol else error(4) end end {pkind = stnd} end {with} end {call}; procedure expression; var lattr: attr; lop: operator; procedure simpleexpression; var lattr: attr; lop: operator; issigned: boolean; procedure term; var lattr: attr; lop: operator; procedure factor; var coffset: addrrange; cvalue: integer; p: itp; q, q1: stp; n: integer; s: @stndset; cstpart, varpart: boolean; procedure structconst(fq: stp); var nxtfld: itp; caddr, loffset: addrrange; nrelts, eltsiz: integer; begin {structconst} if fq <> nil then begin case fq@.form of scalar, booleant, chart, integert, longintt, realt, longrealt, sett: begin expression; if not comptypes(gattr.atype, fq) then error(134) end; arrayt: begin if coffset > 0 then begin { flush out accumulated literal } if coffset = 1 then gendbyte(cvalue) else { never use this else ? } gendword(cvalue); coffset := 0; cvalue := 0 end; nrelts := fq@.size; { nr elts in array } eltsiz := typsize(fq@.aeltyp); { size of array elt } if odd(dc) and (typalign(fq) > 1) then gendbyte(0); caddr := dc; if sym.sy = lparen then begin repeat insymbol; structconst(fq@.aeltyp); if nrelts > 0 then begin gendat(gattr); if gattr.avalue.kind = lit then if eltsiz = 1 then gendbyte(gattr.avalue.ival) else gendword(gattr.avalue.ival); nrelts := pred(nrelts) end until sym.sy <> comma; if sym.sy = rparen then insymbol else error(4); gattr.atype := fq; gattr.akind := cst; gattr.avalue.kind := data; gattr.avalue.daddr := caddr end else if sym.sy = stringconst then begin expression; if (fq@.aeltyp <> nil) and (fq@.aeltyp@.form <> chart) then error(134) end else error(9) end; recordt: begin eltsiz := typsize(fq); if odd(dc) and (eltsiz >= auword) then gendbyte(0); caddr := dc; nxtfld := fq@.fstfld; if sym.sy = lparen then begin repeat insymbol; if nxtfld <> nil then begin loffset := nxtfld@.fdisp mod auword; {proposed offset into literal} if loffset < coffset then begin gendword(cvalue); { flush literal } cvalue := 0; coffset := 0 end; structconst(nxtfld@.itype); gendat(gattr); if gattr.avalue.kind = lit then begin cvalue := cvalue + (gattr.avalue.ival * shfttab[loffset]); coffset := loffset + 1 end else coffset := 0; nxtfld := nxtfld@.next end until sym.sy <> comma; if sym.sy = rparen then insymbol else error(4); gattr.atype := fq; gattr.akind := cst; if eltsiz > auword then begin if coffset > 0 then gendword(cvalue); { flush accumulated literal } gattr.avalue.kind := data; gattr.avalue.daddr := caddr end else begin { carry along as literal } gattr.avalue.kind := lit; gattr.avalue.ival := cvalue end; coffset := 0; cvalue := 0 end else error(9) end end end end {structconst}; begin {factor} case sym.sy of ident: begin p := searchid([types,konst,vars,field,proc]); insymbol; if p = udptrs[types] then { ident was not declared } if (sym.sy = lparen) or (sym.sy = semicolon) then p := udptrs[proc] { just for better error recovery } else p := udptrs[vars]; case p@.class of types: begin { structured constant } {if sym.sy = lparen then insymbol else error(9);} if sym.sy <> lparen then error(9); warning(414); coffset := 0; cvalue := 0; structconst(p@.itype); {if sym.sy = rparen then insymbol else error(4);} gattr.akind := cst; gattr.atype := p@.itype end; vars, field, konst: selector(p); proc: call(p) end end; intconst: begin gattr.akind := cst; gattr.atype := intptr; gattr.avalue.kind := lit; gattr.avalue.ival := val.ival; insymbol end; realconst: begin gattr.akind := cst; gattr.atype := realptr; gattr.avalue.kind := reel; gattr.avalue.rval := val.rval; insymbol end; stringconst: begin gattr.akind := cst; if lgth = 1 then begin {character constant} gattr.atype := charptr; gattr.avalue.kind := lit; gattr.avalue.ival := ord(string[0]) end else begin {string constant} new(gattr.atype, arrayt); with gattr.atype@ do begin form := arrayt; size := lgth; aeltyp := charptr; inxtyp := nil end; gattr.avalue.kind := data; gattr.avalue.daddr := dc; for n := 0 to lgth-1 do gendbyte(ord(string[n])) end; insymbol end; lparen: begin insymbol; expression; if sym.sy = rparen then insymbol else error(4) end; nilsy: begin {nil is reserved word; set up its value} gattr.akind := cst; gattr.atype := nilptr; gattr.avalue.kind := lit; gattr.avalue.ival := 0; insymbol end; notsy: begin insymbol; factor; if gattr.atype = boolptr then if gattr.akind = cst then begin (* not *) if gattr.avalue.ival = 0 (* false *) then gattr.avalue.ival := 1 (* true *) else gattr.avalue.ival := 0 (* false *) end else genbyte(96 {NOT}) else error(134) end; lbrack: begin insymbol; q := nil; n := 0; varpart := false; cstpart := false; new(s); s@ := []; { place to store constant part } if sym.sy <> rbrack then begin loop expression; if gattr.atype <> nil then begin if comptypes(gattr.atype, q) then begin n := setsize(gattr.atype); if n > 0 then begin if gattr.akind = cst then { constant element } if (gattr.avalue.ival >= 0) and (gattr.avalue.ival <= 255) then begin s@ := s@ + [gattr.avalue.ival]; cstpart := true end else error(137) else { variable element } if varpart then genbyte(118 {SADEL}) else begin genbyte(117 {SGENS}); varpart := true end; q := gattr.atype end else error(136) end else error(137) end; exit if sym.sy <> comma; insymbol end end; if sym.sy = rbrack then insymbol else error(12); new(gattr.atype, sett); with gattr.atype@ do begin form := sett; size := n; settyp := q end; gattr.akind := cst; gattr.avalue.kind := setc; gattr.avalue.sval := s; if varpart then begin if cstpart then begin gencon(gattr); genbyte(113 {UNION}) end; gattr.akind := exp end end { lbrack }; mulop,addop,relop,rparen,rbrack,comma,semicolon,period, atsign,colon,becomes,constsy,typesy,varsy,programsy, proceduresy,functionsy,setsy,packedsy,arraysy,recordsy, filesy,forwardsy,beginsy,ifsy,casesy,repeatsy,whilesy, forsy,withsy,loopsy,gotosy,exitsy,endsy,elsesy,untilsy, ofsy,dosy,tosy,downtosy,thensy,externalsy, ceesy,fortransy,labelsy,eofsy,othersy: begin error(58); gattr.atype := nil end end {case} end {factor}; begin {term} factor; while sym.sy = mulop do begin lattr := gattr; lop := sym.op; insymbol; factor; binop(lop, lattr) end end {term}; begin {simpleexpression} issigned := false; if (sym.sy = addop) and ((sym.op = plus) or (sym.op = minus)) then begin issigned := sym.op = minus; insymbol end; term; if issigned and (gattr.atype <> nil) then case gattr.atype@.form of integert: if gattr.akind = cst then gattr.avalue.ival := - gattr.avalue.ival else genbyte(40 {INEG}); longintt: if gattr.akind = cst then error(not_yet_impl) { coming attraction } else genbyte(40 {INEG}); realt: if gattr.akind = cst then error(not_yet_impl) { coming attraction } else genbyte(72 {FNEG}); longrealt: if gattr.akind = cst then error(not_yet_impl) { coming attraction } else genbyte(72 {FNEG}); scalar, booleant, chart, pointer, sett, arrayt, recordt, filet: error(134) end; while sym.sy = addop do begin lattr := gattr; lop := sym.op; insymbol; term; binop(lop, lattr) end end {simpleexpression}; begin {expression} simpleexpression; if sym.sy = relop then begin lattr := gattr; lop := sym.op; insymbol; simpleexpression; binop(lop, lattr); gattr.atype := boolptr end end {expression}; procedure typ (var fq: stp); { parse type definitions } var fldoffset: integer; p: itp; q, q1, q2 : stp; oldtop, n, lmin, lmax: integer; procedure subrange(var fq: stp); var q, q1: stp; lmin: integer; begin {subrange} expression; if gattr.akind <> cst then error(106); q1 := gattr.atype; lmin := gattr.avalue.ival; if sym.sy = colon then insymbol else error(5); expression; if gattr.akind <> cst then error(106); if gattr.avalue.ival < lmin then error(102); q := nil; if (q1 <> nil) and (gattr.atype <> nil) then begin if comptypes(q1, gattr.atype) then begin case gattr.atype@.form of scalar: begin new(q, scalar, true); q@.maxconst := gattr.atype@.maxconst end; booleant: new(q, booleant, true); chart: new(q, chart, true); integert: new(q, integert, true); longintt: new(q, longintt, true) end; if q <> nil then with q@ do begin size := typsize(q1); form := q1@.form; subrange := true; maxvalue := gattr.avalue.ival; minvalue := lmin end else error(148) end else error(107) end; fq := q end {subrange}; procedure fieldlist (var fq: stp); var p, p1, p2, p3: itp; q, q1, q2, q3, q4, q5: stp; maxsize, minsize: integer; lid: idtype; procedure fieldaddr(fp: itp); begin {fieldaddr} if fp <> nil then with fp@ do begin fldoffset := ceil(fldoffset , typalign(itype)); fdisp := fldoffset; fldoffset := fldoffset + typsize(itype) end end {fieldaddr}; begin {fieldlist} p3 := nil; while sym.sy = ident do begin p2 := p3; loop if sym.sy = ident then begin newid(field,nil,p2,p2); insymbol end else error(2); exit if sym.sy <> comma; insymbol end; if sym.sy = colon then insymbol else error(5); typ(q); p := p2; while p2 <> p3 do begin p2@.itype := q; p2 := p2@.next end; p3 := p; if sym.sy = semicolon then insymbol end; p2 := nil; while p3 <> nil do begin { reverse links } p := p3@.next; p3@.next := p2; p2 := p3; p3 := p end; p := p2; while p <> nil do begin { assign offsets of fields } fieldaddr(p); p := p@.next end; if sym.sy = casesy then begin insymbol; new(q, tagfield); with q@ do begin form := tagfield; fstvar := nil; tagfld := nil; tagtyp := nil; if sym.sy = ident then begin lid := id; insymbol; if sym.sy = colon then begin newid(field, nil, nil, p); tagfld := p; insymbol; if sym.sy = ident then begin lid := id; insymbol end else error(2) end else p := nil; id := lid; p1 := searchid([types]); q5 := p1@.itype; if q5@.form > longintt then begin error(110); q5 := nil end; tagtyp := q5 end else error(2); if p <> nil then begin p@.itype := q5; fieldaddr(p) end; size := fldoffset { min size of variants } end; if sym.sy = ofsy then insymbol else error(8); q1 := nil; minsize := fldoffset; maxsize := fldoffset; loop { parse variants } q2 := nil; loop expression; if not comptypes(gattr.atype, q5) then error(115); new(q3, variant); with q3@ do begin form := variant; nxtvar := q1; subvar := q2; varval := gattr.avalue.ival end; q1 := q3; q2 := q3; exit if sym.sy <> comma; insymbol end; if sym.sy = colon then insymbol else error(5); if sym.sy = lparen then insymbol else error(9); fieldlist(q2); if fldoffset > maxsize then maxsize := fldoffset; while q3 <> nil do begin q4 := q3@.subvar; q3@.subvar := q2; q3@.size := fldoffset; q3 := q4 end; if sym.sy = rparen then insymbol else error(4); exit if sym.sy <> semicolon; insymbol; fldoffset := minsize end; fldoffset := maxsize; q@.fstvar := q1; fq := q end else if (q <> nil) and (q@.form = arrayt) then fq := q else fq := nil end {fieldlist}; begin {typ} if sym.sy = packedsy then insymbol; { 'packed' is ignored } case sym.sy of ident: begin p := searchid([types,konst,proc]); if p@.class = types then begin q := p@.itype; insymbol end else subrange(q) end { ident }; addop, intconst, realconst, stringconst: subrange(q); lparen: begin oldtop := top; top := level; new(q, scalar); with q@ do begin size := usize[scalar]; form := scalar; subrange := false; p := nil; n := 0; repeat insymbol; { gobble up leading '(' or ',' } if sym.sy = ident then begin newid(konst,q,p,p); with p@ do begin value.kind := lit; value.ival := n end; n := succ(n); insymbol { gobble up ident } end else error(2) until sym.sy <> comma; maxconst := p end; top := oldtop; if sym.sy = rparen then insymbol else error(4) end { lparen }; atsign: begin insymbol; { gobble up '@' } new(q, pointer); with q@ do begin size := usize[pointer]; form := pointer; eltype := nil; if sym.sy = ident then begin prterr := false; { suppress error for forward declaration } p := searchid([types]); prterr := true; if p = nil then { referenced before declared } newid(types,q,fwptr,fwptr) else begin eltype := p@.itype; if (eltype <> nil) and (eltype@.form = filet) then error(108) end; insymbol { gobble up ident } end else error(2) end end { atsign }; arraysy: begin insymbol; { gobble up 'array' } if sym.sy = lbrack then insymbol else error(11); q1 := nil; loop new(q, arrayt); with q@ do begin form := arrayt; aeltyp := q1; inxtyp := nil end; q1 := q; typ(q2); if q2 <> nil then with q2@ do if form > longrealt then error(113) else if (form=realt) or (form=longrealt) then error(109) else if ((form=integert) or (form=longintt)) and not subrange then error(149) else q@.inxtyp := q2 { valid index type }; exit if sym.sy <> comma; insymbol { gobble up ',' } end; {loop} if sym.sy = rbrack then insymbol else error(12); if sym.sy = ofsy then insymbol else error(8); typ(q); { parse base type of array } repeat with q1@ do begin q2 := aeltyp; aeltyp := q; getbounds(inxtyp, lmin, lmax); size := lmax - lmin + 1 end; q := q1; q1 := q2 until q1 = nil end { arraysy }; recordsy: begin insymbol; { gobble up 'record' } oldtop := top; if top < maxdis then begin top := succ(top); display[top].fname := nil end else error(250); fldoffset := 0; fieldlist(q1); new(q, recordt); with q@ do begin size := fldoffset; { maximum size of record } form := recordt; fstfld := display[top].fname; recvar := q1 end; top := oldtop; if sym.sy = endsy then insymbol else error(13) end { recordsy }; setsy: begin insymbol; { gobble up 'set' } if sym.sy = ofsy then insymbol else error(8); { gobble up 'of' } typ(q1); new(q, sett); with q@ do begin size := setsize(q1); form := sett; settyp := q1 end end { setsy }; filesy: begin insymbol; { gobble up 'file' } if sym.sy = ofsy then insymbol else error(8); { gobble up 'of' } typ(q1); if q1 <> nil then if q1@.form > recordt then begin error(108); q1 := nil end; new(q, filet); with q@ do begin size := 0; {??} form := filet; filtyp := q1 end end { filesy }; notsy, mulop, relop, rparen, lbrack, rbrack, comma, semicolon, period, colon, becomes, constsy, typesy, varsy, programsy, proceduresy, functionsy, forwardsy, beginsy, ifsy, casesy, repeatsy, whilesy, forsy, withsy, loopsy, gotosy, exitsy, endsy, elsesy, untilsy, ofsy, dosy, tosy, downtosy, thensy, externalsy, ceesy, fortransy, othersy, packedsy, nilsy, eofsy, labelsy: {... in other words - default } begin error(10); q := nil end end {case sym.sy}; fq := q end {typ}; procedure constdecl; var p: itp; begin {constdecl} while sym.sy = ident do begin newid(konst,nil,nil,p); insymbol; if (sym.sy = relop) and (sym.op = eqop) then insymbol else error(16); expression; if (gattr.akind <> cst) then error(106); with p@ do begin itype := gattr.atype; value := gattr.avalue end; if sym.sy = semicolon then insymbol else error(14) end end {constdecl}; procedure labeldecl; begin {labeldecl} error(not_yet_impl); loop if sym.sy = intconst then begin insymbol end else error(15); exit if sym.sy <> comma; insymbol end; if sym.sy = semicolon then insymbol else error(14) end {labeldecl}; procedure typedecl; var p1, p2, p3: itp; q1, q2: stp; begin {typedecl} while sym.sy = ident do begin p1 := fwptr; p3 := nil; { p3 will point to a forward pointer id (if any) } while p1 <> nil do begin { search current forward list } if match(p1@.name@.s, ord(p1@.name@.l), id.s, ord(id.l)) = 0 then begin p3 := p1; { got one, save it in p3 } if p1 = fwptr then fwptr := fwptr@.next else p2@.next := p1@.next end; p2 := p1; p1 := p1@.next end; if p3 = nil then begin { if not a forward pointer, then enter it } newid(types,nil,nil,p3); q1 := nil end else q1 := p3@.itype; { q1 is the type of the forward pointer } insymbol; { gobble up the ident } if (sym.sy = relop) and (sym.op = eqop) then insymbol else error(16); typ(q2); { parse type field and return pointer to struct in q2 } p3@.itype := q2; if q1 <> nil then q1@.eltype := q2; { resolve forward pointer } if sym.sy = semicolon then insymbol else error(14) end end {typedecl}; procedure vardecl; var p1, p2, p3, p4: itp; q: stp; procedure varaddr(fp: itp); begin {varaddr} with fp@ do begin if level = 1 then begin lc := ceil(lc, typalign(itype)); {round-up} vaddr := lc; lc := lc + typsize(itype) end else begin lc := lc - typsize(itype); lc := -ceil(-lc, typalign(itype)); {round-down} vaddr := lc end end end {varaddr}; begin {vardecl} p3 := nil; while sym.sy = ident do begin loop if sym.sy = ident then begin newid(vars,nil,p3,p3); with p3@ do begin vkind := local; vlev := level end; insymbol { gobble up the ident } end else error(2); exit if sym.sy <> comma; insymbol end; if sym.sy = colon then insymbol else error(5); typ(q); { parse type } if p3 <> nil then begin p4 := p3; p2 := nil; { after reversing, p4 is tail, p2 is head } repeat { assign type and reverse list } p3@.itype := q; p1 := p3@.next; p3@.next := p2; p2 := p3; p3 := p1 until p3 = nil; p3 := p2; repeat { assign addresses } varaddr(p3); p3 := p3@.next until p3 = nil; p4@.next := varlst; varlst := p2; { add to list of all variables } end; if sym.sy = semicolon then insymbol else error(14) end end {vardecl}; procedure procdecl (isfunction: boolean); var p1, p2, p3, p4: itp; oldfwptr, oldvarlst: itp; oldac, olddc, oldlc, oldpin: integer; rvsize: integer; oldlevel, oldtop: disprange; wasforward: boolean; procedure parmlist(var fp: itp); var p1, p2, p3, p4: itp; q: stp; lvkind: varkinds; begin {parmlist} p1 := nil; if sym.sy = lparen then begin insymbol; { gobble up '(' } loop if sym.sy = varsy then begin insymbol; lvkind := formal end else lvkind := param; p2 := nil; loop if sym.sy = ident then begin newid(vars,nil,p2,p4); with p4@ do begin vkind := lvkind; vlev := level end; p2 := p4; insymbol end else error(2); exit if sym.sy <> comma; insymbol end; if sym.sy = colon then begin insymbol; typ(q); { parse type } p3 := p2; while p2 <> nil do begin { assign type } p2@.itype := q; p4 := p2; p2 := p2@.next end; p4@.next := p1; p1 := p3 end else error(5); exit if sym.sy <> semicolon; insymbol end; if sym.sy = rparen then insymbol else error(4) end; fp := p1 end {parmlist}; procedure parmaddr(fp: itp); begin {parmaddr} if odd(ac) then ac := succ(ac); with fp@ do begin vaddr := ac; if vkind = formal then ac := ac + usize[pointer] else ac := ac + typsize(itype) end end {parmaddr}; procedure externs(fp: itp; langcode:pkinds); var rvsize: integer; begin {externs} genbyte(6 {proc}); genbyte(ord(langcode)-ord(extn)+1); genbyte(5 {ident}); genid( fp@.name ); genbyte(7 {end}); genbyte( pin {proc nr}); rvsize := typsize( fp@.itype ); genbyte( rvsize ); genword( lc ); genword( ac-rvsize ); genword( dc ); insymbol {gobble up 'external','cext',or 'fortran'} end {externs}; begin {procdecl} { preserve state of current procedure } oldfwptr := fwptr; oldvarlst := varlst; oldlc := lc; lc := 0; oldac := ac; ac := 0; { olddc := dc; dc := 0; ***anticipating version 3 int code*** } oldpin := pin; p4 := nil; p2 := nil; p3 := nil; wasforward := false; { assume its not until shown otherwise } if sym.sy = ident then begin p4 := searchlevel(display[top].fname); if p4 <> nil then begin { check to see if previous declared forward } if (p4@.class = proc) and (p4@.pkind = forw) then wasforward := true else error(160) { ident has already been used } end; if not wasforward then begin { enter ident } newid(proc,nil,nil,p4); maxpin := succ(maxpin); pin := maxpin; with p4@ do begin plev := level; paddr := pin end end else begin { must restore parameter list to p2 } p1 := p4@.next; if (p4@.itype <> nil) then p2 := p1@.next { skip over dummy variable for returned value } else p2 := p1 end; insymbol { gobble up the ident } end else error(2); oldlevel := level; if level < maxlevel then level := succ(level) else error(251); oldtop := top; if top < maxdis then begin top := succ(top); with display[top] do begin occur := blck; fname := p2 end end else error(250); if wasforward then begin { param list and func type already avaiable } p3 := p4@.next; p1 := nil; while p3 <> nil do begin { reverse links } p2 := p3@.next; p3@.next := p1; p1 := p3; p3 := p2 end end else parmlist(p1); { must parse paramter list and function type } p3 := nil; while p1 <> nil do begin { reverse order and assign addresses } p2 := p1@.next; p1@.next := p3; parmaddr(p1); p3 := p1; p1 := p2 end; if not wasforward then begin p4@.next := p3; { in a proc, next points to parameter list } p4@.itype := nil; { assume its not a function } if isfunction then begin { parse function type } if sym.sy = colon then begin insymbol; { gobble up the colon } typ(p4@.itype) { parse the function type } end else error(5); if p4@.itype <> nil then begin if p4@.itype@.form > pointer then begin error(120); p4@.itype := nil end; id := idtype(chr(3),'.rv'); { enter dummy ident for returned value } newid(vars, p4@.itype, p3, p2); with p2@ do begin vkind := param; vlev := level end; p4@.next := p2; parmaddr(p2) end else error(123) end end; if sym.sy = semicolon then insymbol else error(14); if sym.sy = forwardsy then begin if wasforward then error(161) else p4@.pkind := forw; insymbol { gobble up 'forward' } end else if (sym.sy = externalsy) then begin p4@.pkind := extn; externs( p4, extn ) end else if sym.sy = ceesy then begin p4@.pkind := cee; externs( p4, cee ) end else if sym.sy = fortransy then begin p4@.pkind := fort; externs( p4, fort ) end else begin { parse procedure definition } p4@.pkind := decl; genbyte(6 (*proc*) ); genbyte(0 (*not extern*) ); block(p4) end; if sym.sy = semicolon then insymbol else error(14); level := oldlevel; top := oldtop; lc := oldlc; ac := oldac; {dc := olddc;} pin := oldpin; fwptr := oldfwptr; varlst := oldvarlst end {procdecl}; procedure body; procedure statelist (stopper: symbol); var scnt: integer; procedure statement; var p: itp; procedure assignment(fp: itp); var q: stp; begin {assignment} if (fp@.class = proc) and (fp@.itype <> nil) then fp := fp@.next; { dummy variable for returned value } selector(fp); q := gattr.atype; if sym.sy = becomes then begin insymbol; valueexpression; if q <> nil then with q@ do begin if not comptypes(gattr.atype, q) then if comptypes(q, realptr) and comptypes(gattr.atype, intptr) then genbyte(74 {FLOAT}) else error(129) else if form = sett then setcoerce(q); { to make empty sets behave } if form = arrayt then begin genbyte(135 {MOVEM}); if typsize(aeltyp) < 2 then genbyte(1) else genbyte(2); genword(size) end else if form = recordt then begin if size <= 2 then genbyte(10 {STOL}) else begin genbyte(135 {MOVEM}); if odd(size) then begin genbyte(1); genword(size) end else begin genbyte(2); genword(size div 2) end end end else if (form = realt) or (form = longrealt) then genbyte(12 {STOF}) else genbyte(10 {STOL}) end end else error(51) end {assignment}; procedure gotostatement; begin {gotostatement} error(not_yet_impl); genbyte(8 {NULL}); { temporary, in place of goto op } if sym.sy = intconst then begin insymbol end else error(15) end {gotostatement}; procedure ifstatement; begin {ifstatement} valueexpression; if not comptypes(gattr.atype, boolptr) then error(135); if sym.sy = thensy then insymbol else error(52); statement; if sym.sy = elsesy then begin insymbol; statement end else genbyte(8 {NULL}); genbyte(144 {IF}) end {ifstatement}; procedure casestatement; var q: stp; lmin, lmax, nrent, nrval: integer; cslab, elm, ind: integer; usedlabels: array [0..15] of stndset; { Note: elm, ind, and the above array are artifacts of the small set size. This will be changed when set of 0..255 is implemented. } begin {casestatement} for ind := 0 to 15 do usedlabels[ind] := []; valueexpression; q := gattr.atype; getbounds(q, lmin, lmax); if sym.sy = ofsy then insymbol else error(8); nrent := 1; loop nrval := 0; loop expression; if gattr.akind = cst then begin if q <> nil then begin if not comptypes(gattr.atype, q) then error(147); cslab := gattr.avalue.ival - lmin; if (cslab>=0) and (cslab<=255) then begin ind := cslab div 16; { temp } elm := cslab mod 16; { temp } if elm in usedlabels[ind] then error(156) else usedlabels[ind] := [elm] + usedlabels[ind] end; genlit(cslab); lmax := max(lmax, gattr.avalue.ival); nrval := succ(nrval) end end else error(106); exit if sym.sy <> comma; insymbol end {loop}; if sym.sy = colon then insymbol else error(5); statement; genbyte(146 {ENTRY}); genbyte(nrval); nrent := succ(nrent); exit if sym.sy <> semicolon; insymbol end {loop}; if sym.sy = endsy then insymbol else error(13); lmax := lmax - lmin; if lmax > 255 then error(173); genbyte(145 {CASE}); genbyte(nrent); genbyte(lmax) end {casestatement}; procedure repeatstatement; begin {repeatstatement} statelist(untilsy); if sym.sy = untilsy then begin insymbol; valueexpression; if not comptypes(gattr.atype, boolptr) then error(135); genbyte(8 {NULL}); { no exit stub } genbyte(148 {EXIT}) end else error(53); genbyte(8 {NULL}); { no code after exit } genbyte(147 {LOOP}); genbyte(2) { arg count - 2 } end {repeatstatement}; procedure whilestatement; begin {whilestatement} genbyte(8 {NULL}); { no code before exit } valueexpression; if not comptypes(gattr.atype, boolptr) then error(135); if sym.sy = dosy then insymbol else error(54); genbyte(96 {NOT}); genbyte(8 {NULL}); { no exit stub } genbyte(148 {EXIT}); statement; genbyte(147 {LOOP}); genbyte(2) { arg count - 2 } end {whilestatement}; procedure loopstatement; var nrexits: integer; begin {loopstatement} warning(411); nrexits := 0; loop statelist(exitsy); exit if (sym.sy = endsy) or (sym.sy = eofsy); if sym.sy = exitsy then begin warning(412); insymbol; if sym.sy = ifsy then insymbol else error(56); valueexpression; if not comptypes(gattr.atype, boolptr) then error(135); if sym.sy = thensy then begin insymbol; statement end else genbyte(8 {NULL}); { no exit stub } genbyte(148 {EXIT}); nrexits := succ(nrexits); if sym.sy = semicolon then insymbol else error(14) end else error(57) end; if sym.sy = endsy then insymbol else error(13); genbyte(147 {LOOP}); genbyte(nrexits*2) { builds bad tree if exit missing! } end {loopstatement}; procedure forstatement; var p: itp; lsy: symbol; begin {forstatement} if sym.sy = ident then begin p := searchid([vars]); selector(p); insymbol end else error(2); if sym.sy = becomes then begin insymbol; valueexpression end else error(51); if (sym.sy = tosy) or (sym.sy = downtosy) then begin lsy := sym.sy; insymbol; valueexpression; if lsy = tosy then genlit(1) else genlit(-1) end else error(55); if sym.sy = dosy then insymbol else error(54); statement; genbyte(149 {FOR}) end {forstatement}; procedure withstatement; var p: itp; nrwiths: integer; begin {withstatement} nrwiths := 0; loop if sym.sy = ident then begin p := searchid([vars,field,konst]); insymbol end else begin error(2); p := udptrs[vars] end; selector(p); if gattr.atype <> nil then if gattr.atype@.form = recordt then begin if top < maxdis then begin top := succ(top); nrwiths := succ(nrwiths); with display[top] do begin fname := gattr.atype@.fstfld; if gattr.access = direct then begin occur := crec; dlev := gattr.alevel; daddr := gattr.addr; genbyte(2 {DEL}) { don't need the reference we already gen'ed } end else begin occur := vrec; tc := succ(tc); { need new temp to store 'with' pointer } tnum := tc; genbyte(9 {REFER}) end end { of with } end else error(250) end else error(140); exit if sym.sy <> comma; insymbol end; if sym.sy = dosy then insymbol else error(54); statement; for nrwiths := nrwiths downto 1 do begin if display[top].occur = vrec then begin genbyte(141 {DTEMP}); genbyte(tc); tc := pred(tc) end; top := pred(top) end end {withstatement}; begin {statement} { check for labels and put out error message: } if sym.sy = intconst then begin error(not_yet_impl); insymbol; if sym.sy = colon then insymbol else error(5) end; case sym.sy of ident: begin p := searchid([vars,field,proc]); insymbol; if (p@.class = proc) and (p@.itype = nil) then call(p) { procedure call } else assignment(p) end; beginsy: begin insymbol; body end; ifsy: begin insymbol; ifstatement end; casesy: begin insymbol; casestatement end; whilesy: begin insymbol; whilestatement end; repeatsy: begin insymbol; repeatstatement end; loopsy: begin insymbol; loopstatement end; forsy: begin insymbol; forstatement end; withsy: begin insymbol; withstatement end; gotosy: begin insymbol; gotostatement end; semicolon, endsy, elsesy, exitsy, untilsy: genbyte(8 {NULL}); intconst, realconst, stringconst, notsy, mulop, addop, relop, lparen, rparen, lbrack, rbrack, comma, period, atsign, colon, becomes, constsy, typesy, varsy, programsy, proceduresy, functionsy, setsy, packedsy, arraysy, recordsy, filesy, forwardsy, nilsy, labelsy, ofsy, dosy, tosy, downtosy, thensy, externalsy, ceesy, fortransy, othersy: begin error(6); insymbol end end {case} end {statement}; begin {statelist} scnt := 0; statement; while (sym.sy <> stopper) and (sym.sy <> endsy) and (sym.sy <> eofsy) do if sym.sy = semicolon then begin insymbol; { gobble up ';' } if scnt >= 255 then begin { already 256 statements, do a SEQ } genbyte(152 {SEQ}); genbyte(255); scnt := 0 end; statement; scnt := succ(scnt) end else begin error(14); skip(stopper) end; genbyte(152 {SEQ}); genbyte(scnt) end {statelist}; begin {body} statelist(endsy); if sym.sy = endsy then insymbol { gobble up 'end' } else error(13) end {body}; procedure checkfiles(isclose: boolean; var filecount: integer); var p: itp; n: integer; begin {checkfiles} n := 0; p := varlst; while p <> nil do with p@ do begin if itype <> nil then with itype@ do begin if form = filet then begin n := succ(n); genbyte(176 + vlev {VARBL}); genbyte(typsize(itype)); genword(vaddr); genbyte(9 {REFER}); if isclose then begin genbyte(138 {INVOK}); genbyte(1); genbyte(20) {close} end else begin genlit(typsize(itype@.filtyp)); genbyte(138 {INVOK}); genbyte(2); genbyte(16) {finit} end end end; p := p@.next end; filecount := n end {checkfiles}; begin {block} mark; lc := 0; tc := 0; if level = 1 then begin { declare implicit globals } genbyte(6 (*proc*) ); genbyte(0 (*not extern*) ); lc := 2; id := idtype(chr(6),'output'); newid(vars,textptr,nil,p); outptr := p; {retain in case of defaulted filename} with p@ do begin vkind := local; vlev := 1; vaddr := lc end; lc := lc + filsiz; id := idtype(chr(5),'input'); newid(vars,textptr,nil,p); inptr := p; {retain in case of defaulted filename} with p@ do begin vkind := local; vlev := 1; vaddr := lc end; lc := lc + filsiz; { definition of argc and argv: } new(q1, integert); with q1@ do begin size := 2; form := integert; subrange := true; minvalue := 0; maxvalue := 255 end; new(q2, arrayt); with q2@ do begin size := 256; form := arrayt; aeltyp := charptr; inxtyp := q1 end; new(q3, pointer); with q3@ do begin size := 2; form := pointer; eltype := q2 end; new(q2, arrayt); with q2@ do begin size := 256; form := arrayt; aeltyp := q3; inxtyp := q1 end; id := idtype(chr(4),'argv'); newid(vars,q2,nil,p); with p@ do begin vkind := formal; vlev := 1; vaddr := 0 end; id := idtype(chr(4),'argc'); newid(vars,intptr,nil,p); with p@ do begin vkind := param; vlev := 1; vaddr := 2 end; ac := 4 end; fwptr := nil; varlst := nil; declstate := headpart; while (sym.sy <> beginsy) and (sym.sy <> eofsy) do if sym.sy = labelsy then begin if declstate > labelpart then warning(413); declstate := labelpart; insymbol; labeldecl end else if sym.sy = constsy then begin if declstate > constpart then warning(413); declstate := constpart; insymbol; constdecl end else if sym.sy = typesy then begin if declstate > typepart then warning(413); declstate := typepart; insymbol; typedecl end else if sym.sy = varsy then begin if declstate > varblpart then warning(413); declstate := varblpart; insymbol; vardecl end else if sym.sy = proceduresy then begin declstate := procpart; insymbol; procdecl(false) end else if sym.sy = functionsy then begin declstate := procpart; insymbol; procdecl(true) end else begin error(18); skip(semicolon); insymbol end; if ((level>1) or not option['X']) and (sym.sy = beginsy) then begin insymbol; { gobble the 'begin' } genbyte(5 {ident}); genid(fp@.name); checkfiles(false, filecount); body; { parse the body of this block } checkfiles(true, filecount); if filecount > 0 then begin genbyte(152 {seq}); genbyte(filecount*2) end; if odd(dc) then gendbyte(0); { round dc to word boundary } genbyte(7 {end}); genbyte(fp@.paddr {proc nr}); rvsize := typsize(fp@.itype); genbyte(rvsize); genword(lc); genword(ac - rvsize); genword(dc); if option['T'] then printtables(false); end; release end {block}; procedure openfiles; begin reset (src, argv[2]@, 2); rewrite (int, argv[3]@); rewrite (dat, argv[4]@); rewrite (lst, argv[5]@, 2) end {openfiles}; begin {pass 1} writeln(output, NL, compiler_version); break(output); openfiles; pageno := 1; writeln(lst, compiler_version, HT, HT, 'Page ', pageno:5 ); writeln( lst ); linesleft := pagesize; for c := 'A' to 'Z' do option[c] := false; option['L'] := true; { get the listing for now } option['W'] := false; { turn off warning option } prterr := true; errtot := 0; errinx := 0; linenr := 1; maxpin := 0; dc := 0; chcnt := 0; ch := NL; c := NL; level := 0; top := 0; pin := 0; gattr.atype := nil; gattr.akind := cst; inittables; lcp := nil; beginline; insymbol; if sym.sy = programsy then begin insymbol; if sym.sy = ident then begin newid(proc,nil,nil,lcp); insymbol end else error(2); if sym.sy = lparen then begin { ignore program parameters } skip(rparen); insymbol end; if sym.sy = semicolon then insymbol else error(14) end; if lcp = nil then begin id := idtype(chr(6),'.main.'); newid(proc,nil,nil,lcp) end; with lcp@ do begin pkind := decl; plev := 0; paddr := 0 end; level := 1; top := 1; genbyte(5 (*ident*) ); genid( lcp@.name); with display[1] do begin fname := nil; occur := blck end; block(lcp); if not option['X'] then if sym.sy<>period then error(21); if ch <> NL then writeln(lst); if errtot > 0 then writeln(output, NL, 'Pass1 errors: ', errtot) end {pass 1}.