ack/lang/pc/pem/pem.p
1988-08-31 09:24:28 +00:00

3402 lines
101 KiB
OpenEdge ABL

#include <em_spec.h>
#include <em_pseu.h>
#include <em_mnem.h>
#include <em_mes.h>
#include <em_reg.h>
#include <pc_size.h>
{
(c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands.
This product is part of the Amsterdam Compiler Kit.
Permission to use, sell, duplicate or disclose this software must be
obtained in writing. Requests for such permissions may be sent to
Dr. Andrew S. Tanenbaum
Wiskundig Seminarium
Vrije Universiteit
Postbox 7161
1007 MC Amsterdam
The Netherlands
}
{if next line is included the compiler itself is written in standard pascal}
{#define STANDARD 1}
{if next line is included, the compiler won't generate static exchanges}
{#define NO_EXC 1}
{Author: Johan Stevenson Version: 32}
{$l- : no source line numbers}
{$r- : no subrange checking}
{$a- : no assertion checking}
#ifdef STANDARD
{$s+ : test conformancy to standard}
#endif
program pem(input,em,errors);
{/*
This Pascal compiler produces EM code as described in
- A.S.Tanenbaum, J.W.Stevenson & H. van Staveren,
"Description of a machine architecture for use with
block structured languages" Informatika rapport 81.
NOTE: this version is modified to produce the modified EM code of
januari 1981. it is not possible, using this compiler, to
generate code for machines with 1 byte wordsize.
NOTE: this version is modified by Kees Visser in such a way that
the compiler can now run on 2 and 4 byte machines. It is also
able to generate em-code for a 2 bytes machine while running
on a 4-bytes machine. Cross-compilation from a 2 bytes to a
four bytes machine is also possible with the following
exception: large integers that don't fit in an integer of
the compiler are treated like longs and are thus not allowed
in types.
A description of Pascal is given in
- K.Jensen & N.Wirth, "PASCAL user manual and report", Springer-Verlag.
Several options may be given in the normal pascal way. Moreover,
a positive number may be used instead of + and -. The options are:
a: interpret assertions (+)
c: C-type strings allowed (-)
d: type long may be used (-)
i: controls the number of elements in integer sets
default: (wordsize in bits)
l: insert code to keep track of source lines (+)
o: optimize (+)
r: check subranges (+)
s: accept only standard pascal programs (-)
t: trace procedure entry and exit (-)
u: treat '_' as letter (-)
*/}
{===================================================================}
#ifdef STANDARD
label 9999;
#endif
const
{fundamental constants}
MB1 = 7;
NB1 = 8;
MI1 = 127;
NI1 = 128;
MI2 = 32767;
MU1 = 255;
NU1 = 256;
{string constants}
imax = 10;
max2bytes = '0000032767';
max4bytes = '2147483647';
#if EM_WSIZE == 4
{this can only be compiled with a compiler that has integer size 4}
MU2 = 65535;
NU2 = 65536;
{characteristics of the machine on which the compiler will run}
{wordsize and integer size are 4}
MI = 2147483647;
maxcompintstring = max4bytes;
#endif
#if EM_WSIZE == 2
MU2 = 0; {not used}
NU2 = 0; {not used}
MI = MI2;
maxcompintstring = max2bytes;
#endif
#if EM_WSIZE != 2 && EM_WSIZE != 4
Something wrong here!
#endif
{maximal indices}
idmax = 8;
fnmax = 14;
smax = 72;
{opt values}
off = 0;
on = 1;
{for push and pop: }
global = false;
local = true;
{for sizeof and posaddr: }
wordmult = false;
wordpart = true;
{ASCII characters}
ascht = 9;
ascnl = 10;
ascvt = 11;
ascff = 12;
asccr = 13;
{miscellaneous}
maxcharord = 127; {maximal ordinal number of chars}
maxargc = 13; {maximal index in argv}
rwlim = 34; {number of reserved words}
spaces = ' ';
{-------------------------------------------------------------------}
type
{scalar types}
symbol= (comma,semicolon,colon1,colon2,notsy,lbrack,ident,
intcst,charcst,realcst,longcst,stringcst,nilcst,minsy,
plussy,lparent,arrow,arraysy,recordsy,setsy,filesy,
packedsy,progsy,labelsy,constsy,typesy,varsy,procsy,
funcsy,beginsy,gotosy,ifsy,whilesy,repeatsy,forsy,
withsy,casesy,becomes,starsy,divsy,modsy,slashsy,
andsy,orsy,eqsy,nesy,gtsy,gesy,ltsy,
lesy,insy,endsy,elsesy,untilsy,ofsy,dosy,
downtosy,tosy,thensy,rbrack,rparent,period
); {the order is important}
chartype= (lower,upper,digit,layout,tabch,
quotech,dquotech,colonch,periodch,lessch,
greaterch,lparentch,lbracech,
{different entries}
rparentch,lbrackch,rbrackch,commach,semich,arrowch,
plusch,minch,slash,star,equal,
{also symbols}
others
);
standpf= (pread,preadln,pwrite,pwriteln,pput,pget,
preset,prewrite,pnew,pdispose,ppack,punpack,
pmark,prelease,ppage,phalt,
{all procedures}
feof,feoln,fabs,fsqr,ford,fchr,fpred,fsucc,fodd,
ftrunc,fround,fsin,fcos,fexp,fsqt,flog,fatn
{all functions}
); {the order is important}
libmnem= (ELN ,EFL ,CLS ,WDW , {input and output}
OPN ,GETX,RDI ,RDC ,RDR ,RDL ,RLN ,
{on inputfiles}
CRE ,PUTX,WRI ,WSI ,WRC ,WSC ,WRS ,WSS ,WRB ,
WSB ,WRR ,WSR ,WRL, WSL, WRF ,WRZ ,WSZ ,WLN ,PAG ,
{on outputfiles, order important}
ABR ,RND ,SINX,COSX,EXPX,SQT ,LOG ,ATN ,
{floating point}
ABI ,ABL ,BCP ,BTS ,NEWX,SAV ,RST ,INI ,HLT ,
ASS ,GTO ,PAC ,UNP, DIS, ASZ, MDI, MDL
{miscellaneous}
);
structform= (scalar,subrange,pointer,power,files,arrays,carray,
records,variant,tag); {order important}
structflag= (spack,withfile);
identflag= (refer,used,assigned,noreg,loopvar,samesect);
idclass= (types,konst,vars,field,carrbnd,proc,func);
kindofpf= (standard,formal,actual,extern,varargs,forward);
where= (blck,rec,wrec);
attrkind= (cst,fixed,pfixed,loaded,ploaded,indexed);
twostruct= (eq,subeq,ir,ri,il,li,lr,rl,es,se,noteq); {order important}
{subrange types}
rwrange= 0..rwlim;
byte= 0..MU1;
{pointer types}
sp= ^structure;
ip= ^identifier;
lp= ^labl;
bp= ^blockinfo;
np= ^nameinfo;
#ifdef NO_EXC
mp= ^mmark;
op= ^outrec;
#endif NO_EXC
{set types}
sos= set of symbol;
setofids= set of idclass;
formset= set of structform;
sflagset= set of structflag;
iflagset= set of identflag;
{array types}
idarr=packed array[1..idmax] of char;
fnarr=packed array[1..fnmax] of char;
{record types}
position=record {the addr info of certain variable}
ad:integer; {for locals it is the byte offset}
lv:integer; {the level of the beast}
end;
{records of type attr are used to remember qualities of
expression parts to delay the loading of them.
Reasons to delay the loading of one word constants:
- bound checking
- set building.
Reasons to delay the loading of direct accessible objects:
- efficient handling of read/write
- efficient handling of the with statement.
}
attr=record
asp:sp; {type of expression}
packbit:boolean; {true for part of packed structure}
ak:attrkind; {access method}
pos:position; {lv and ad}
{If ak=cst then the value is stored in ad}
end;
nameinfo=record {one for each separate name space}
nlink:np; {one deeper}
fname:ip; {first name: root of tree}
case occur:where of
blck:();
rec: ();
wrec:(wa:attr) {name space opened by with statement}
end;
blockinfo=record {all info of the current procedure}
nextbp:bp; {pointer to blockinfo of surrounding proc}
reglb:integer; {data location counter (from begin of proc) }
minlb:integer; {keeps track of minimum of reglb}
ilbno:integer; {number of last local label}
forwcount:integer; {number of not yet specified forward procs}
lchain:lp; {first label: header of chain}
end;
structure=record
size:integer; {size of structure in bytes}
sflag:sflagset; {flag bits}
case form:structform of
scalar :(scalno:integer; {number of range descriptor}
fconst:ip {names of constants}
);
subrange:(min,max:integer; {lower and upper bound}
rangetype:sp; {type of bounds}
subrno:integer {number of subr descriptor}
);
pointer :(eltype:sp); {type of pointed object}
power :(elset:sp); {type of set elements}
files :(filtype:sp); {type of file elements}
arrays,carray:
(aeltype:sp; {type of array elements}
inxtype:sp; {type of array index}
arpos:position {position of array descriptor}
);
records :(fstfld:ip; {points to first field}
tagsp:sp {points to tag if present}
);
variant :(varval:integer; {tag value for this variant}
nxtvar:sp; {next equilevel variant}
subtsp:sp {points to tag for sub-case}
);
tag :(fstvar:sp; {first variant of case}
tfldsp:sp {type of tag}
)
end;
identifier=record
idtype:sp; {type of identifier}
name:idarr; {name of identifier}
llink,rlink:ip; {see enterid,searchid}
next:ip; {used to make several chains}
iflag:iflagset; {several flag bits}
case klass:idclass of
types :();
konst :(value:integer); {for integers the value is
computed and stored in this field.
For strings and reals an assembler constant is
defined labeled '.1', '.2', ... This '.' number is then
stored in value. For reals value may be negated to
indicate that the opposite of the assembler constant
is needed. }
vars :(vpos:position); {position of var}
field :(foffset:integer); {offset to begin of record}
carrbnd :(); {idtype points to carray struct}
proc,func:
(case pfkind:kindofpf of
standard:(key:standpf); {identification}
formal,actual,forward,extern,varargs:
(pfpos:position; {lv gives declaration level.
ad is relevant for formal pf s and for
functions (no conflict!!).
for functions: ad is the result address.
for formal pf s: ad is the address of the
descriptor }
pfno:integer; {unique pf number}
maxlb:integer; {bytes of parameters}
parhead:ip {head of parameter list}
)
)
end;
labl=record
nextlp:lp; {chain of labels}
seen:boolean;
labval:integer; {label number given by the programmer}
labname:integer; {label number given by the compiler}
labdlb:integer {zero means only locally used,
otherwise dlbno of label information}
end;
#ifdef NO_EXC
outrec=record
next:op; {chain of records}
bytes:array[1..16] of byte;
cnt:0..16;
end;
mmark=record
next:mp; {chain of marks}
count,where:integer;
end;
#endif NO_EXC
{-------------------------------------------------------------------}
var {the most frequent used externals are declared first}
sy:symbol; {last symbol}
a:attr; {type,access method,position,value of expr}
{returned by insym}
ch:char; {last character}
chsy:chartype; {type of ch, used by insym}
val:integer; {if last symbol is an constant }
ix:integer; {string length}
eol:boolean; {true of current ch is a space, replacing a newline}
zerostring:boolean; {true for strings in " "}
id:idarr; {if last symbol is an identifier}
{some counters}
lino:integer; {line number on code file (1..n) }
dlbno:integer; {number of last global number}
holeb:integer; {size of hol-area}
level:integer; {current static level}
argc:integer; {index in argv}
lastpfno:integer; {unique pf number counter}
copt:integer; {C-type strings allowed if on}
dopt:integer; {longs allowed if on}
iopt:integer; {number of bits in sets with base integer}
sopt:integer; {standard option}
srcchno:integer; {column count for errors}
srclino:integer; {source line number after preprocessing}
srcorig:integer; {source line number before preprocessing}
fildlb:integer; {label number of source string}
{pointers pointing to standard types}
realptr,intptr,textptr,nullset,boolptr:sp;
charptr,nilptr,zeroptr,procptr,longptr:sp;
{flags}
giveline:boolean; {give source line number at next statement}
including:boolean; {no LINs for included code}
eofexpected:boolean; {quit without error if true (nextch) }
main:boolean; {complete programme or a module}
intypedec:boolean; {true if nested in typedefinition}
fltused:boolean; {true if floating point instructions are used}
seconddot:boolean; {indicates the second dot of '..'}
{pointers}
fwptr:ip; {head of chain of forward reference pointers}
progp:ip; {program identifier}
currproc:ip; {current procedure/function ip (see selector)}
top:np; {pointer to the most recent name space}
lastnp:np; {pointer to nameinfo of last searched ident }
{records}
b:blockinfo; {all info to be stacked at pfdeclaration}
fa:attr; {attr for current file name}
{arrays}
sizes:array[0 .. sz_last] of integer;
maxintstring,maxlongstring:packed array[1..imax] of char;
strbuf:array[1..smax] of char;
rw:array[rwrange] of idarr;
{reserved words}
frw:array[0..idmax] of integer;
{indices in rw}
rsy:array[rwrange] of symbol;
{symbol for reserved words}
cs:array[char] of chartype;
{chartype of a character}
csy:array[rparentch..equal] of symbol;
{symbol for single character symbols}
lmn:array[libmnem] of packed array[1..4] of char;
{mnemonics of pascal library routines}
opt:array['a'..'z'] of integer;
forceopt:array['a'..'z'] of boolean;
{26 different options}
undefip:array[idclass] of ip;
{used in searchid}
iop:array[boolean] of ip;
{false:standard input, true:standard output}
argv:array[0..maxargc] of
record name:idarr; ad:integer end;
{save here the external heading names}
{files}
em:file of byte; {the EM code}
errors:text; {the compilation errors}
source:fnarr;
#ifdef NO_EXC
ohead: op; {head of outrec list}
mhead: mp; {head of marks list}
bcnt: integer;
#define newmark setmark
#define relmark(xx) freemark(xx)
#else not NO_EXC
#define newmark lino
#define relmark(xx)
#endif NO_EXC
{===================================================================}
procedure initpos(var p:position);
begin p.lv:=level; p.ad:=0; end;
procedure inita(fsp:sp; fad:integer);
begin with a do begin
asp:=fsp; packbit:=false; ak:=fixed; pos.ad:=fad; pos.lv:=level;
end end;
function newip(kl:idclass; n:idarr; idt:sp; nxt:ip):ip;
var p:ip; f:iflagset;
begin f:=[];
case kl of
types,carrbnd: {similar structure}
new(p,types);
konst:
begin new(p,konst); p^.value:=0 end;
vars:
begin new(p,vars); f:=[used,assigned]; initpos(p^.vpos) end;
field:
begin new(p,field); p^.foffset:=0 end;
proc,func: {same structure}
begin new(p,proc,actual); p^.pfkind:=actual;
initpos(p^.pfpos); p^.pfno:=0; p^.maxlb:=0; p^.parhead:=nil;
end
end;
p^.name:=n; p^.klass:=kl; p^.idtype:=idt; p^.next:=nxt;
p^.llink:=nil; p^.rlink:=nil; p^.iflag:=f; newip:=p
end;
function newsp(sf:structform; sz:integer):sp;
var p:sp; sflag:sflagset;
begin sflag:=[];
case sf of
scalar:
begin new(p,scalar); p^.scalno:=0; p^.fconst:=nil end;
subrange:
new(p,subrange);
pointer:
begin new(p,pointer); p^.eltype:=nil end;
power:
new(p,power);
files:
begin new(p,files); sflag:=[withfile] end;
arrays,carray: {same structure}
new(p,arrays);
records:
new(p,records);
variant:
new(p,variant);
tag:
new(p,tag);
end;
p^.form:=sf; p^.size:=sz; p^.sflag:=sflag; newsp:=p;
end;
function sizeof(fsp:sp; partword:boolean):integer;
var s:integer;
begin if fsp=nil then s:=0 else s:=fsp^.size;
if s<>0 then
if partword and (s<sz_word) then
while sz_word mod s <> 0 do s:=s+1
else
while s mod sz_word <> 0 do s:=s+1;
sizeof:=s
end;
function formof(fsp:sp; forms:formset):boolean;
begin if fsp=nil then formof:=false else formof:=fsp^.form in forms end;
{===================================================================}
#ifdef NO_EXC
procedure newoutrec;
var p:op;
begin
new(p);
bcnt := bcnt+1;
with p^ do begin cnt := 0; next := ohead end;
ohead := p
end;
procedure put1(b:byte);
begin
if mhead = nil then write(em,b)
else begin
if ohead^.cnt = 16 then newoutrec;
with ohead^ do
begin cnt := cnt + 1; bytes[cnt] := b end
end
end;
#else not NO_EXC
procedure put1(b:byte);
begin write(em,b) end;
#endif NO_EXC
procedure put2(i:integer);
var i1,i2:byte;
begin
if i<0 then
begin i:=-(i+1); i1:=MU1 - i mod NU1; i2:=MU1 - i div NU1 end
else
begin i1:=i mod NU1; i2:=i div NU1 end;
put1(i1); put1(i2)
end;
#if EM_WSIZE == 4
procedure put4(i:integer);
var i1,i2:integer;
begin
if i<0 then
begin i:=-(i+1); i1:=MU2 - i mod NU2; i2:=MU2 - i div NU2 end
else
begin i1:=i mod NU2; i2:=i div NU2 end;
put1(i1 mod NU1); put1(i1 div NU1);
put1(i2 mod NU1); put1(i2 div NU1)
end;
#endif
procedure argend;
begin put1(sp_cend) end;
procedure argcst(i:integer);
begin
if (i >= -sp_zcst0) and (i < sp_ncst0-sp_zcst0) then
put1(i + sp_zcst0 + sp_fcst0)
else
#if EM_WSIZE == 4
if (i >= -MI2-1) and (i <= MI2) then
#endif
begin put1(sp_cst2); put2(i) end
#if EM_WSIZE == 4
else begin put1(sp_cst4); put4(i) end
#endif
end;
procedure argnil;
begin put1(sp_icon); argcst(sz_addr); argcst(1); put1(ord('0')) end;
procedure argilb(i:integer);
begin
if i<=MU1 then
begin put1(sp_ilb1); put1(i) end
else
begin put1(sp_ilb2); put2(i) end
end;
procedure argdlb(i:integer);
begin
if i<=MU1 then
begin put1(sp_dlb1); put1(i) end
else
begin put1(sp_dlb2); put2(i) end
end;
procedure argident(var a:idarr);
var i,j:integer;
begin i:=idmax;
while (a[i]=' ') and (i>1) do i:=i-1;
put1(sp_pnam); argcst(i);
for j:=1 to i do put1(ord(a[j]))
end;
procedure genop(b:byte);
begin put1(b); lino:=lino+1 end;
procedure gencst(b:byte; i:integer);
begin genop(b); argcst(i) end;
procedure gensp(m:libmnem; s:integer);
var i:integer;
begin genop(op_cal); put1(sp_pnam); argcst(4);
for i:=1 to 4 do put1(ord(lmn[m][i]));
gencst(op_asp,s)
end;
procedure genpnam(b:byte; fip:ip);
var n:idarr; i,j:integer;
begin
if fip^.pfpos.lv<=1 then n:=fip^.name else
begin n:='_ '; j:=1; i:=fip^.pfno;
while i<>0 do
begin j:=j+1; n[j]:=chr(i mod 10 + ord('0')); i:=i div 10 end;
end;
genop(b); argident(n)
end;
procedure genasp(m:byte);
begin gencst(m,sizeof(a.asp,wordmult)) end;
procedure genlin;
begin giveline:=false;
if opt['l']<>off then if main then gencst(op_lin,srcorig)
end;
procedure genreg(sz,ad,regval:integer);
begin gencst(ps_mes,ms_reg);
argcst(ad); argcst(sz); argcst(regval); argend
end;
procedure laedlb(d:integer);
begin genop(op_lae); argdlb(d) end;
#ifdef NO_EXC
procedure reloutrec;
var i,j,k:integer;
q, r, p:op;
m : mp;
begin p := ohead; q := p;
if mhead <> nil then
begin
m := mhead; while m^.next <> nil do m := m^.next;
k := (bcnt - m^.where) + 1
end
else begin k := 0; ohead := nil; bcnt := 0 end;
for i := 1 to k do begin q := p; p := p^.next end;
if q <> p then q^.next := nil;
if p <> nil then
begin r := nil;
while p <> nil do
begin q := p^.next; p^.next := r; r := p; p := q end;
while r <> nil do with r^ do
begin
for j := 1 to cnt do write(em, bytes[j]);
r := next
end
end
end;
function setmark:integer;
var p:mp; nm:boolean;
begin nm := false;
if mhead <> nil then with mhead^ do
if (where = bcnt) and (ohead^.cnt = 0) then
begin count := count + 1; nm := true end;
if not nm then
begin new(p); newoutrec;
with p^ do
begin where := bcnt; count := 1; next := mhead end;
mhead := p;
end;
setmark := bcnt
end;
procedure freemark(m : integer);
var p, q : mp;
begin assert(mhead <> nil); p := mhead; q := p;
while p^.where <> m do
begin q := p; p := p^.next; assert(p <> nil) end;
with p^ do
begin assert(count > 0); count := count - 1; if count = 0 then
begin
if p = mhead then begin mhead := next; reloutrec end
else q^.next := next
end
end end;
procedure exchange(n,m:integer);
var i:integer;
p,q,r:op;
begin assert(m >= n);
if n <> m then
begin
p := ohead;
for i := bcnt downto m+1 do p := p^.next;
q := p;
for i := m downto n+1 do q := q^.next;
r := ohead; ohead := p^.next; p^.next := q^.next; q^.next := r
end
end;
#else not NO_EXC
procedure exchange(l1,l2:integer);
var d1,d2:integer;
begin d1:=l2-l1; d2:=lino-l2;
if (d1<>0) and (d2<>0) then
begin gencst(ps_exc,d1); argcst(d2) end
end;
#endif NO_EXC
procedure newilb(i:integer);
begin lino:=lino+1;
if i<sp_nilb0 then put1(i+sp_filb0) else argilb(i)
end;
function newdlb:integer;
begin lino:=lino+1; dlbno:=dlbno+1; argdlb(dlbno); newdlb:=dlbno end;
function romstr(typ:byte; siz:integer):integer;
var i:integer;
begin romstr:=newdlb; genop(ps_rom);
put1(typ); if typ<>sp_scon then argcst(siz); argcst(ix);
for i:=1 to ix do put1(ord(strbuf[i])); argend
end;
{===================================================================}
procedure error(err:integer);
{as you will notice, all error numbers are preceded by '+' and '0' to
ease their renumbering in case of new errornumbers.
}
begin writeln(errors,err,srclino,srcchno);
if err>0 then begin gencst(ps_mes,ms_err); argend end
end;
procedure errid(err:integer; var id:idarr);
begin write(errors,'''',id); error(err) end;
procedure errint(err:integer; i:integer);
begin write(errors,i:1); error(err) end;
procedure errasp(err:integer);
begin if a.asp<>nil then begin error(err); a.asp:=nil end end;
procedure teststandard;
begin if sopt<>off then error(-(+01)) end;
procedure enterid(fip: ip);
{enter id pointed at by fip into the name-table,
which on each declaration level is organised as
an unbalanced binary tree}
var nam:idarr; lip,lip1:ip; lleft,again:boolean;
begin nam:=fip^.name; again:=false; assert nam[1]<>' ';
lip:=top^.fname;
if lip=nil then top^.fname:=fip else
begin
repeat lip1:=lip;
if lip^.name>nam then
begin lip:=lip^.llink; lleft:=true end
else
begin if lip^.name=nam then again:=true; {name conflict}
lip:=lip^.rlink; lleft:=false;
end;
until lip=nil;
if lleft then lip1^.llink:=fip else lip1^.rlink:=fip
end;
fip^.llink:=nil; fip^.rlink:=nil;
if again then errid(+02,nam);
end;
{===================================================================}
procedure trace(tname:idarr; fip:ip; var namdlb:integer);
var i:integer;
begin
if opt['t']<>off then
begin
if namdlb=0 then
begin namdlb:=newdlb; genop(ps_rom); put1(sp_scon); argcst(8);
for i:=1 to 8 do put1(ord(fip^.name[i])); argend;
end;
laedlb(namdlb); genop(op_cal); argident(tname);
gencst(op_asp,sz_addr);
end;
end;
procedure expandnullset(fsp:sp);
var s:integer;
begin s:=sizeof(fsp,wordmult)-sz_word;
if s<>0 then gencst(op_zer,s); a.asp:=fsp
end;
procedure push(local:boolean; ad:integer; sz:integer);
begin assert sz mod sz_word = 0;
if sz=sz_word then
if local then gencst(op_lol,ad) else gencst(op_loe,ad)
else if sz=2*sz_word then
if local then gencst(op_ldl,ad) else gencst(op_lde,ad)
else
begin if local then gencst(op_lal,ad) else gencst(op_lae,ad);
gencst(op_loi,sz)
end
end;
procedure pop(local:boolean; ad:integer; sz:integer);
begin assert sz mod sz_word = 0;
if sz=sz_word then
if local then gencst(op_stl,ad) else gencst(op_ste,ad)
else if sz=2*sz_word then
if local then gencst(op_sdl,ad) else gencst(op_sde,ad)
else
begin if local then gencst(op_lal,ad) else gencst(op_lae,ad);
gencst(op_sti,sz)
end
end;
procedure lexaddr(lv:integer; ad:integer);
begin assert level>=lv;
if ad>=0 then gencst(op_lxa,level-lv) else gencst(op_lxl,level-lv);
gencst(op_adp,ad)
end;
procedure loadpos(var p:position; sz:integer);
begin with p do
if lv<=0 then push(global,ad,sz) else
if lv=level then push(local,ad,sz) else
begin lexaddr(lv,ad); gencst(op_loi,sz) end;
end;
procedure descraddr(var p:position);
begin if p.lv=0 then laedlb(p.ad) else loadpos(p,sz_addr) end;
procedure loadaddr;
begin with a,pos do begin
case ak of
fixed:
if lv<=0 then gencst(op_lae,ad) else
if lv=level then gencst(op_lal,ad) else lexaddr(lv,ad);
pfixed:
loadpos(pos,sz_addr);
ploaded:
;
indexed:
gencst(op_aar,sz_word);
end; {case}
ak:=ploaded;
end end;
procedure load;
var sz:integer;
begin with a do begin sz:=sizeof(asp,packbit);
if asp<>nil then begin
case ak of
cst:
gencst(op_loc,pos.ad); {only one-word scalars}
fixed:
loadpos(pos,sz);
pfixed:
begin loadpos(pos,sz_addr); gencst(op_loi,sz) end;
loaded:
;
ploaded:
gencst(op_loi,sz);
indexed:
gencst(op_lar,sz_word);
end; {case}
if asp^.form = subrange then
if sz < sz_word then
if asp^.min < 0 then
{ do sign extension }
begin gencst(op_loc, sz); gencst(op_loc, sz_word); genop(op_cii) end;
end;
ak:=loaded;
end end;
procedure store;
var sz:integer;
begin with a,pos do begin sz:=sizeof(asp,packbit);
if asp<>nil then
case ak of
fixed:
if lv<=0 then pop(global,ad,sz) else
if level=lv then pop(local,ad,sz) else
begin lexaddr(lv,ad); gencst(op_sti,sz) end;
pfixed:
begin loadpos(pos,sz_addr); gencst(op_sti,sz) end;
ploaded:
gencst(op_sti,sz);
indexed:
gencst(op_sar,sz_word);
end; {case}
end end;
procedure fieldaddr(off:integer);
begin with a do
if (ak=fixed) and not packbit then pos.ad:=pos.ad+off else
begin loadaddr; gencst(op_adp,off) end
end;
procedure loadcheap;
begin if formof(a.asp,[arrays..records]) then loadaddr else load end;
{===================================================================}
procedure nextch;
begin
eol:=eoln(input); read(input,ch); chsy:=cs[ch];
if chsy <> tabch then srcchno:=srcchno+1
end;
procedure nextln;
begin
if eof(input) then
begin
if not eofexpected then error(+03) else
if fltused then begin gencst(ps_mes,ms_flt); argend end;
#ifdef STANDARD
goto 9999
#else
halt
#endif
end;
srcchno:=0; srclino:=srclino+1;
if not including then
begin srcorig:=srcorig+1; giveline:=true end;
end;
procedure options(normal:boolean);
var ci:char; i:integer;
procedure getc;
begin if normal then nextch else read(errors,ch) end;
begin
repeat getc;
if (ch>='a') and (ch<='z') then
begin ci:=ch; getc; i:=0;
if ch='+' then begin i:=1; getc end else
if ch='-' then getc else
if cs[ch]=digit then
repeat i:=i*10 + ord(ch) - ord('0'); getc;
until cs[ch]<>digit
else i:=-1;
if i>=0 then
if not normal then
begin forceopt[ci]:=true; opt[ci]:=i end
else
if not forceopt[ci] then opt[ci]:=i;
end;
until ch<>',';
end;
procedure linedirective;
var i:integer; fname:fnarr;
begin
repeat nextch until (ch='"') or eol;
if eol then error(+04) else
begin nextch; i:=0;
while (ch<>'"') and not eol do
begin
if ch='/' then i:=0 else
begin i:=i+1; if i<=fnmax then fname[i]:=ch end;
nextch
end;
while i<fnmax do begin i:=i+1; fname[i]:=' ' end;
including:=fname<>source; while not eol do nextch
end;
end;
procedure putdig;
begin ix:=ix+1; if ix<=smax then strbuf[ix]:=ch; nextch end;
procedure inident;
label 1;
var i,k:integer;
begin k:=0; id:=spaces;
repeat
if chsy=upper then ch:=chr(ord(ch)-ord('A')+ord('a'));
if k<idmax then begin k:=k+1; id[k]:=ch end;
nextch
until chsy>digit;
{lower=0,upper=1,digit=2. ugly but fast}
for i:=frw[k-1] to frw[k] - 1 do
if rw[i]=id then
begin sy:=rsy[i]; goto 1 end;
sy:=ident;
1:
end;
procedure innumber;
label 1;
var i,j:integer;
is:packed array[1..imax] of char;
begin ix:=0; sy:=intcst; val:=0;
repeat putdig until chsy<>digit;
if (ch='.') or (ch='e') or (ch='E') then
begin
if ch='.' then
begin putdig;
if ch='.' then
begin seconddot:=true; ix:=ix-1; goto 1 end;
if chsy<>digit then error(+05) else
repeat putdig until chsy<>digit;
end;
if (ch='e') or (ch='E') then
begin putdig;
if (ch='+') or (ch='-') then putdig;
if chsy<>digit then error(+06) else
repeat putdig until chsy<>digit;
end;
if ix>smax then begin error(+07); ix:=smax end;
sy:=realcst; fltused:=true; val:=romstr(sp_fcon,sz_real);
end;
1:if (chsy=lower) or (chsy=upper) then teststandard;
if sy=intcst then
if ix>imax then error(+08) else
begin is:='0000000000'; i:=ix; j:=imax;
repeat is[j]:=strbuf[i]; j:=j-1; i:=i-1 until i=0;
if (is<=maxintstring) and (is<=maxcompintstring) then
repeat j:=j+1; val:=val*10 - ord('0') + ord(is[j]) until j=imax
else if (is<=maxlongstring) and (dopt<>off) then
begin sy:=longcst; val:=romstr(sp_icon,sz_long) end
else error(+09)
end
end;
procedure instring(qc:char);
begin ix:=0; zerostring:=qc='"';
repeat
repeat nextch; ix:=ix+1; if ix<=smax then strbuf[ix]:=ch;
until (ch=qc) or eol;
if ch=qc then nextch else error(+010);
until ch<>qc;
if not zerostring then
begin ix:=ix-1; if ix=0 then error(+011) end
else
begin strbuf[ix]:=chr(0); if copt=off then error(+012) end;
if (ix=1) and not zerostring then
begin sy:=charcst; val:=ord(strbuf[1]) end
else
begin if ix>smax then begin error(+013); ix:=smax end;
sy:=stringcst; val:=romstr(sp_scon,0);
end
end;
procedure incomment;
var stopc:char;
begin nextch; stopc:='}';
if ch='$' then options(true);
while (ch<>'}') and (ch<>stopc) do
begin stopc:='}'; if ch='*' then stopc:=')';
if eol then nextln; nextch
end;
if ch<>'}' then teststandard;
nextch
end;
procedure insym;
{read next basic symbol of source program and return its
description in the global variables sy, op, id, val and ix}
label 1;
begin
1:case chsy of
tabch:
begin srcchno:=srcchno - srcchno mod 8 + 8; nextch; goto 1 end;
layout:
begin if eol then nextln; nextch; goto 1 end;
lower,upper: inident;
digit: innumber;
quotech,dquotech:
instring(ch);
colonch:
begin nextch;
if ch='=' then begin sy:=becomes; nextch end else sy:=colon1
end;
periodch:
begin nextch;
if seconddot then begin seconddot:=false; sy:=colon2 end else
if ch='.' then begin sy:=colon2; nextch end else sy:=period
end;
lessch:
begin nextch;
if ch='=' then begin sy:=lesy; nextch end else
if ch='>' then begin sy:=nesy; nextch end else sy:=ltsy
end;
greaterch:
begin nextch;
if ch='=' then begin sy:=gesy; nextch end else sy:=gtsy
end;
lparentch:
begin nextch;
if ch<>'*' then sy:=lparent else
begin teststandard; incomment; goto 1 end;
end;
lbracech:
begin incomment; goto 1 end;
rparentch,lbrackch,rbrackch,commach,semich,arrowch,
plusch,minch,slash,star,equal:
begin sy:=csy[chsy]; nextch end;
others:
begin
if (ch='#') and (srcchno=1) then linedirective else
begin error(+014); nextch end;
goto 1
end;
end {case}
end;
procedure nextif(fsy:symbol; err:integer);
begin if sy=fsy then insym else error(-err) end;
function find1(sys1,sys2:sos; err:integer):boolean;
{symbol of sys1 expected. return true if sy in sys1}
begin
if not (sy in sys1) then
begin error(err); while not (sy in sys1+sys2) do insym end;
find1:=sy in sys1
end;
function find2(sys1,sys2:sos; err:integer):boolean;
{symbol of sys1+sys2 expected. return true if sy in sys1}
begin
if not (sy in sys1+sys2) then
begin error(err); repeat insym until sy in sys1+sys2 end;
find2:=sy in sys1
end;
function find3(sy1:symbol; sys2:sos; err:integer):boolean;
{symbol sy1 or one of sys2 expected. return true if sy1 found and skip it}
begin find3:=true;
if not (sy in [sy1]+sys2) then
begin error(err); repeat insym until sy in [sy1]+sys2 end;
if sy=sy1 then insym else find3:=false
end;
function endofloop(sys1,sys2:sos; sy3:symbol; err:integer):boolean;
begin endofloop:=false;
if find2(sys2+[sy3],sys1,err) then nextif(sy3,err+1)
else endofloop:=true;
end;
function lastsemicolon(sys1,sys2:sos; err:integer):boolean;
begin lastsemicolon:=true;
if not endofloop(sys1,sys2,semicolon,err) then
if find2(sys2,sys1,err+2) then lastsemicolon:=false
end;
{===================================================================}
function searchid(fidcls: setofids):ip;
{search for current identifier symbol in the name table}
label 1;
var lip:ip; ic:idclass;
begin lastnp:=top;
while lastnp<>nil do
begin lip:=lastnp^.fname;
while lip<>nil do
if lip^.name=id then
if lip^.klass in fidcls then
begin
if lip^.klass=vars then if lip^.vpos.lv<>level then
lip^.iflag:=lip^.iflag+[noreg];
goto 1
end
else lip:=lip^.rlink
else
if lip^.name< id then lip:=lip^.rlink else lip:=lip^.llink;
lastnp:=lastnp^.nlink;
end;
errid(+015,id);
if types in fidcls then ic:=types else
if vars in fidcls then ic:=vars else
if konst in fidcls then ic:=konst else
if proc in fidcls then ic:=proc else
if func in fidcls then ic:=func else ic:=field;
lip:=undefip[ic];
1:
searchid:=lip
end;
function searchsection(fip: ip):ip;
{to find record fields and forward declared procedure identifiers
-->procedure pfdeclaration
-->procedure selector}
label 1;
begin
while fip<>nil do
if fip^.name=id then goto 1 else
if fip^.name< id then fip:=fip^.rlink else fip:=fip^.llink;
1: searchsection:=fip
end;
function searchlab(flp:lp; val:integer):lp;
label 1;
begin
while flp<>nil do
if flp^.labval=val then goto 1 else flp:=flp^.nextlp;
1:searchlab:=flp
end;
procedure opconvert(ts:twostruct);
var op:integer;
begin with a do begin genasp(op_loc);
case ts of
ir, lr: begin asp:=realptr; op:=op_cif; fltused:=true end;
ri: begin asp:=intptr ; op:=op_cfi; fltused:=true end;
rl: begin asp:=longptr; op:=op_cfi; fltused:=true end;
li: begin asp:=intptr ; op:=op_cii end;
il: begin asp:=longptr; op:=op_cii end;
end;
genasp(op_loc); genop(op)
end end;
procedure negate;
begin if a.asp=realptr then genasp(op_ngf) else genasp(op_ngi) end;
function desub(fsp:sp):sp;
begin if formof(fsp,[subrange]) then fsp:=fsp^.rangetype; desub:=fsp end;
function nicescalar(fsp:sp):boolean;
begin
if fsp=nil then nicescalar:=true else
nicescalar:=(fsp^.form=scalar) and (fsp<>realptr) and (fsp<>longptr)
end;
function bounded(fsp:sp):boolean;
begin bounded:=false;
if fsp<>nil then
if fsp^.form=subrange then bounded:=true else
if fsp^.form=scalar then bounded:=fsp^.fconst<>nil
end;
procedure bounds(fsp:sp; var fmin,fmax:integer);
begin
if fsp=nil then
begin fmin:=0; fmax:=0 end
else
case fsp^.form of
subrange:
begin fmin:=fsp^.min; fmax:=fsp^.max end;
scalar:
begin fmin:=0; fmax:=fsp^.fconst^.value end
end
end;
procedure genrck(fsp:sp);
var min,max,sno:integer;
begin
if opt['r']<>off then if bounded(fsp) then
begin
if fsp^.form=scalar then sno:=fsp^.scalno else sno:=fsp^.subrno;
if sno=0 then
begin bounds(fsp,min,max); sno:=newdlb;
gencst(ps_rom,min); argcst(max); argend;
if fsp^.form=scalar then fsp^.scalno:=sno else fsp^.subrno:=sno
end;
laedlb(sno); gencst(op_rck,sz_word);
end
end;
procedure checkbnds(fsp:sp);
var min1,max1,min2,max2:integer;
begin
if bounded(fsp) then
if not bounded(a.asp) then genrck(fsp) else
begin bounds(fsp,min1,max1); bounds(a.asp,min2,max2);
if (min2<min1) or (max2>max1) then
genrck(fsp);
end;
a.asp:=fsp;
end;
function eqstruct(p,q:sp):boolean;
begin eqstruct:=(p=q) or (p=nil) or (q=nil) end;
function string(fsp:sp):boolean;
var lsp:sp;
begin string:=false;
if formof(fsp,[arrays]) then
if eqstruct(fsp^.aeltype,charptr) then
if spack in fsp^.sflag then
begin lsp:=fsp^.inxtype;
if lsp=nil then string:=true else
if lsp^.form=subrange then
if lsp^.rangetype=intptr then
if lsp^.min=1 then
string:=true
end
end;
function compat(p,q:sp):twostruct;
begin compat:=noteq;
if eqstruct(p,q) then compat:=eq else
begin p:=desub(p); q:=desub(q);
if eqstruct(p,q) then compat:=subeq else
if p^.form=q^.form then
case p^.form of
scalar:
if (p=intptr) and (q=realptr) then compat:=ir else
if (p=realptr) and (q=intptr) then compat:=ri else
if (p=intptr) and (q=longptr) then compat:=il else
if (p=longptr) and (q=intptr) then compat:=li else
if (p=longptr) and (q=realptr) then compat:=lr else
if (p=realptr) and (q=longptr) then compat:=rl else
;
pointer:
if (p=nilptr) or (q=nilptr) then compat:=eq;
power:
if p=nullset then compat:=es else
if q=nullset then compat:=se else
if compat(p^.elset,q^.elset) <= subeq then
if p^.sflag=q^.sflag then compat:=eq;
arrays:
if string(p) and string(q) and (p^.size=q^.size) then compat:=eq;
files,carray,records: ;
end;
end
end;
procedure checkasp(fsp:sp; err:integer);
var ts:twostruct;
begin
ts:=compat(a.asp,fsp);
case ts of
eq:
if fsp<>nil then if withfile in fsp^.sflag then errasp(err);
subeq:
checkbnds(fsp);
li:
begin opconvert(ts); checkasp(fsp,err) end;
il,rl,lr,ir:
opconvert(ts);
es:
expandnullset(fsp);
noteq,ri,se:
errasp(err);
end
end;
procedure force(fsp:sp; err:integer);
begin load; checkasp(fsp,err) end;
function newident(kl:idclass; idt:sp; nxt:ip; err:integer):ip;
begin newident:=nil;
if sy<>ident then error(err) else
begin newident:=newip(kl,id,idt,nxt); insym end
end;
function stringstruct:sp;
var lsp:sp;
begin {only used when ix and zerostring are still valid}
if zerostring then lsp:=zeroptr else
begin lsp:=newsp(arrays,ix*sz_char); lsp^.sflag:=[spack];
lsp^.aeltype:=charptr; lsp^.inxtype:=nil;
end;
stringstruct:=lsp;
end;
function posaddr(var lb:integer; fsp:sp; partword:boolean):integer;
var sz:integer;
begin sz:=sizeof(fsp,partword);
if sz_int = 2 then
if lb >= MI2-sz-sz_word then begin error(+016); lb:=0 end;
if not partword or (sz>=sz_word) then
while lb mod sz_word <> 0 do lb:=lb+1;
posaddr:=lb;
lb:=lb+sz
end;
function negaddr(fsp:sp):integer;
var sz:integer;
begin with b do begin
sz:=sizeof(fsp,wordmult);
if sz_int = 2 then
if reglb <= -MI2+sz+sz_word then begin error(+017); reglb:=0 end;
reglb:=reglb-sz;
while reglb mod sz_word <> 0 do reglb:=reglb-1;
if reglb < minlb then minlb:=reglb;
negaddr:=reglb
end end;
procedure temporary(fsp:sp;r:integer);
begin inita(fsp,negaddr(fsp));
if r>=0 then genreg(sizeof(fsp,wordmult),a.pos.ad,r)
end;
procedure genhol;
var sz: integer;
begin sz := posaddr(holeb,nil,false); if sz = 0 then sz := sz_word;
gencst(ps_hol,sz);
if sz_word = 4 then begin put1(sp_cst4); put1(0); put1(0); end
else put1(sp_cst2);
put1(0); put1(128); { 1000000000000000 pattern}
argcst(0); level:=1
end;
function arraysize(fsp:sp; pack:boolean):integer;
var sz,min,max,tot,n:integer;
begin sz:=sizeof(fsp^.aeltype,pack);
bounds(fsp^.inxtype,min,max);
fsp^.arpos.lv:=0; fsp^.arpos.ad:=newdlb;
gencst(ps_rom,min); argcst(max-min); argcst(sz); argend;
n:=max-min+1; tot:=sz*n;
if sz<>0 then if tot div sz <> n then begin error(+018); tot:=0 end;
arraysize:=tot
end;
procedure treewalk(fip:ip);
var lsp:sp; i,sz:integer;
begin
if fip<>nil then
begin treewalk(fip^.llink); treewalk(fip^.rlink);
if fip^.klass=vars then
begin if not (used in fip^.iflag) then errid(-(+019),fip^.name);
if not (assigned in fip^.iflag) then errid(-(+020),fip^.name);
lsp:=fip^.idtype;
if level<>1 then
if (refer in fip^.iflag) or not (noreg in fip^.iflag) then
if (refer in fip^.iflag) or formof(lsp,[pointer]) then
genreg(sz_addr,fip^.vpos.ad,reg_pointer)
else
begin sz:=sizeof(lsp,wordmult);
if loopvar in fip^.iflag then
genreg(sz,fip^.vpos.ad,reg_loop)
else if lsp=realptr then
genreg(sz,fip^.vpos.ad,reg_float)
else
genreg(sz,fip^.vpos.ad,reg_any);
end;
if lsp<>nil then if withfile in lsp^.sflag then
if lsp^.form=files then
if level=1 then
begin
for i:=2 to argc do with argv[i] do
if name=fip^.name then ad:=fip^.vpos.ad
end
else
begin
if not (refer in fip^.iflag) then
begin gencst(op_lal,fip^.vpos.ad); gensp(CLS,sz_addr)
end
end
else
if level<>1 then errid(-(+021),fip^.name)
end
end
end;
procedure constant(fsys:sos; var fsp:sp; var fval:integer);
var signed,min:boolean; lip:ip;
begin signed:=(sy=plussy) or (sy=minsy);
if signed then begin min:=sy=minsy; insym end else min:=false;
if find1([ident..stringcst],fsys,+022) then
begin fval:=val;
case sy of
stringcst: fsp:=stringstruct;
charcst: fsp:=charptr;
intcst: fsp:=intptr;
realcst: fsp:=realptr;
longcst: fsp:=longptr;
ident:
begin lip:=searchid([konst]);
fsp:=lip^.idtype; fval:=lip^.value;
end
end; {case}
if signed then
if (fsp<>intptr) and (fsp<>realptr) and (fsp<>longptr) then
error(+023)
else if min then fval:= -fval;
{note: negating the v-number for reals and longs}
insym;
end
else begin fsp:=nil; fval:=0 end;
end;
function cstinteger(fsys:sos; fsp:sp; err:integer):integer;
var lsp:sp; lval,min,max:integer;
begin constant(fsys,lsp,lval);
if fsp<>lsp then
if not eqstruct(desub(fsp),lsp) then
begin error(err); lval:=0 end
else if bounded(fsp) then
begin bounds(fsp,min,max);
if (lval<min) or (lval>max) then error(+024)
end;
cstinteger:=lval
end;
{===================================================================}
function typid(err:integer):sp;
var lip:ip; lsp:sp;
begin lsp:=nil;
if sy<>ident then error(err) else
begin lip:=searchid([types]); lsp:=lip^.idtype; insym end;
typid:=lsp
end;
function simpletyp(fsys:sos):sp;
var lsp,lsp1:sp; lip,hip:ip; min,max:integer; lnp:np;
newsubrange:boolean;
begin lsp:=nil;
if find1([ident..lparent],fsys,+025) then
if sy=lparent then
begin insym; lnp:=top; {decl. consts local to innermost block}
while top^.occur<>blck do top:=top^.nlink;
lsp:=newsp(scalar,sz_word); hip:=nil; max:=0;
repeat lip:=newident(konst,lsp,hip,+026);
if lip<>nil then
begin enterid(lip); hip:=lip; lip^.value:=max; max:=max+1 end;
until endofloop(fsys+[rparent],[ident],comma,+027); {+028}
if max<=MU1 then lsp^.size:=sz_byte
#if EM_WSIZE == 4
else if max <= MU2 then lsp^.size := 2*sz_byte
#endif
;
lsp^.fconst:=hip; top:=lnp; nextif(rparent,+029);
end
else
begin newsubrange:=true;
if sy=ident then
begin lip:=searchid([types,konst]); insym;
if lip^.klass=types then
begin lsp:=lip^.idtype; newsubrange:=false end
else
begin lsp1:=lip^.idtype; min:=lip^.value end
end
else constant(fsys+[colon2,ident..plussy],lsp1,min);
if newsubrange then
begin lsp:=newsp(subrange,sz_word); lsp^.subrno:=0;
if not nicescalar(lsp1) then
begin error(+030); lsp1:=nil; min:=0 end;
lsp^.rangetype:=lsp1;
nextif(colon2,+031); max:=cstinteger(fsys,lsp1,+032);
if min>max then begin error(+033); max:=min end;
if ((min>=0) and (max<=MU1)) or ((min>=-NI1) and (max<=MI1)) then
lsp^.size:=sz_byte
#if EM_WSIZE == 4
else if ((min>=0) and (max<=MU2)) or ((min>=-MI2-1) and (max<=MI2)) then
lsp^.size := 2*sz_byte
#endif
;
lsp^.min:=min; lsp^.max:=max
end
end;
simpletyp:=lsp
end;
function arraytyp(fsys:sos;
artyp:structform;
sflag:sflagset;
function element(fsys:sos):sp
):sp;
var lsp,lsp1,hsp:sp; ok:boolean; sepsy:symbol; lip:ip;
oksys:sos;
begin insym; nextif(lbrack,+034); hsp:=nil;
repeat lsp:=newsp(artyp,0); initpos(lsp^.arpos);
lsp^.aeltype:=hsp; hsp:=lsp; {link reversed}
if artyp=carray then
begin sepsy:=semicolon; oksys:=[ident];
lip:=newident(carrbnd,lsp,nil,+035); if lip<>nil then enterid(lip);
nextif(colon2,+036);
lip:=newident(carrbnd,lsp,lip,+037); if lip<>nil then enterid(lip);
nextif(colon1,+038); lsp1:=typid(+039);
ok:=nicescalar(desub(lsp1));
end
else
begin sepsy:=comma; oksys:=[ident..lparent];
lsp1:=simpletyp(fsys+[comma,rbrack,ofsy,ident..packedsy]);
ok:=bounded(lsp1)
end;
if not ok then begin error(+040); lsp1:=nil end;
lsp^.inxtype:=lsp1
until endofloop(fsys+[rbrack,ofsy,ident..packedsy],oksys,
sepsy,+041); {+042}
nextif(rbrack,+043); nextif(ofsy,+044);
lsp:=element(fsys);
if lsp<>nil then sflag:=sflag + lsp^.sflag * [withfile];
repeat {reverse links and compute size}
lsp1:=hsp^.aeltype; hsp^.aeltype:=lsp; hsp^.sflag:=sflag;
if artyp=arrays then hsp^.size:=arraysize(hsp,spack in sflag);
lsp:=hsp; hsp:=lsp1
until hsp=nil; {lsp points to array with highest dimension}
arraytyp:=lsp
end;
function typ(fsys:sos):sp;
var lsp,lsp1:sp; off,sz,min,errno:integer;
sflag:sflagset; lnp:np;
function fldlist(fsys:sos):sp;
{level 2: << typ}
var fip,hip,lip:ip; lsp:sp;
function varpart(fsys:sos):sp;
{level 3: << fldlist << typ}
var tip,lip:ip; lsp,headsp,hsp,vsp,tsp,tsp1,tfsp:sp;
minoff,maxoff,int,nvar:integer; lid:idarr;
begin insym; tip:=nil; lip:=nil;
tsp:=newsp(tag,0);
if sy<>ident then error(+045) else
begin lid:=id; insym;
if sy=colon1 then
begin tip:=newip(field,lid,nil,nil); enterid(tip); insym;
if sy<>ident then error(+046) else
begin lid:=id; insym end;
end;
if sy=ofsy then {otherwise you may destroy id}
begin id:=lid; lip:=searchid([types]) end;
end;
if lip=nil then tfsp:=nil else tfsp:=lip^.idtype;
if bounded(tfsp) then
begin bounds(tfsp,int,nvar); nvar:=nvar-int+1 end
else
begin nvar:=0; if tfsp<>nil then begin error(+047); tfsp:=nil end end;
tsp^.tfldsp:=tfsp;
if tip<>nil then {explicit tag}
begin tip^.idtype:=tfsp;
tip^.foffset:=posaddr(off,tfsp,spack in sflag)
end;
nextif(ofsy,+048); minoff:=off; maxoff:=minoff; headsp:=nil;
repeat hsp:=nil; {for each caselabel list}
repeat nvar:=nvar-1;
int:=cstinteger(fsys+[ident..plussy,comma,colon1,lparent,
semicolon,casesy,rparent],tfsp,+049);
lsp:=headsp; {each label may occur only once}
while lsp<>nil do
begin if lsp^.varval=int then error(+050);
lsp:=lsp^.nxtvar
end;
vsp:=newsp(variant,0); vsp^.varval:=int;
vsp^.nxtvar:=headsp; headsp:=vsp; {chain of case labels}
vsp^.subtsp:=hsp; hsp:=vsp;
{use this field to link labels with same variant}
until endofloop(fsys+[colon1,lparent,semicolon,casesy,rparent],
[ident..plussy],comma,+051); {+052}
nextif(colon1,+053); nextif(lparent,+054);
tsp1:=fldlist(fsys+[rparent,semicolon,ident..plussy]);
if off>maxoff then maxoff:=off;
while vsp<>nil do
begin vsp^.size:=off; hsp:=vsp^.subtsp;
vsp^.subtsp:=tsp1; vsp:=hsp
end;
nextif(rparent,+055);
off:=minoff;
until lastsemicolon(fsys,[ident..plussy],+056); {+057 +058}
if nvar>0 then error(-(+059));
tsp^.fstvar:=headsp; tsp^.size:=minoff; off:=maxoff; varpart:=tsp;
end;
begin {fldlist}
if find2([ident],fsys+[casesy],+060) then
repeat lip:=nil; hip:=nil;
repeat fip:=newident(field,nil,nil,+061);
if fip<>nil then
begin enterid(fip);
if lip=nil then hip:=fip else lip^.next:=fip; lip:=fip;
end;
until endofloop(fsys+[colon1,ident..packedsy,semicolon,casesy],
[ident],comma,+062); {+063}
nextif(colon1,+064);
lsp:=typ(fsys+[casesy,semicolon]);
if lsp<>nil then if withfile in lsp^.sflag then
sflag:=sflag+[withfile];
while hip<>nil do
begin hip^.idtype:=lsp;
hip^.foffset:=posaddr(off,lsp,spack in sflag);
hip:=hip^.next
end;
until lastsemicolon(fsys+[casesy],[ident],+065); {+066 +067}
if sy=casesy then fldlist:=varpart(fsys) else fldlist:=nil;
end;
begin {typ}
sflag:=[]; lsp:=nil;
if sy=packedsy then begin sflag:=[spack]; insym end;
if find1([ident..filesy],fsys,+068) then
if sy in [ident..arrow] then
begin if spack in sflag then error(+069);
if sy=arrow then
begin lsp:=newsp(pointer,sz_addr); insym;
if not intypedec then lsp^.eltype:=typid(+070) else
if sy<>ident then error(+071) else
begin fwptr:=newip(types,id,lsp,fwptr); insym end
end
else lsp:=simpletyp(fsys);
end
else
case sy of
{<<<<<<<<<<<<}
arraysy:
lsp:=arraytyp(fsys,arrays,sflag,typ);
recordsy:
begin insym;
new(lnp,rec); lnp^.occur:=rec; lnp^.nlink:=top; lnp^.fname:=nil; top:=lnp;
off:=0; lsp1:=fldlist(fsys+[endsy]); {fldlist updates off}
lsp:=newsp(records,off); lsp^.tagsp:=lsp1;
lsp^.fstfld:=top^.fname; lsp^.sflag:=sflag;
top:=top^.nlink; nextif(endsy,+072)
end;
setsy:
begin insym; nextif(ofsy,+073);
lsp:=simpletyp(fsys); lsp1:=desub(lsp); errno:=0;
if bounded(lsp1) then
begin bounds(lsp1,min,sz);
if sz div NB1>=sz_mset then errno:=+074
end
else if bounded(lsp) then {subrange of integer}
begin bounds(lsp,min,sz);
if (min<0) or (sz>=iopt) then errno:=+075;
sz:=iopt-1
end
else if lsp=intptr then
begin sz:=iopt-1; errno:=-(+076) end
else
errno:=+077;
if errno<>0 then
begin error(errno); if errno>0 then begin lsp1:=nil; sz:=0 end end;
lsp:=newsp(power,sz div NB1 +1); lsp^.elset:=lsp1;
end;
filesy:
begin insym; nextif(ofsy,+078); lsp1:=typ(fsys);
if lsp1<>nil then if withfile in lsp1^.sflag then error(-(+079));
sz:=sizeof(lsp1,wordpart); if sz<sz_buff then sz:=sz_buff;
lsp:=newsp(files,sz+sz_head); lsp^.filtype:=lsp1;
end;
{>>>>>>>>>>>>}
end; {case}
typ:=lsp;
end;
function vpartyp(fsys:sos):sp;
begin
if find2([arraysy],fsys+[ident],+080) then
vpartyp:=arraytyp(fsys,carray,[],vpartyp)
else
vpartyp:=typid(+081)
end;
{===================================================================}
procedure block(fsys:sos; fip:ip); forward;
{pfdeclaration calls block. With a more obscure lexical
structure this forward declaration can be avoided}
procedure labeldeclaration(fsys:sos);
var llp:lp;
begin with b do begin
repeat
if sy<>intcst then error(+082) else
begin
if searchlab(lchain,val)<>nil then errint(+083,val) else
begin new(llp); llp^.labval:=val;
if val>9999 then teststandard;
ilbno:=ilbno+1; llp^.labname:=ilbno; llp^.labdlb:=0;
llp^.seen:=false; llp^.nextlp:=lchain; lchain:=llp;
end;
insym
end
until endofloop(fsys+[semicolon],[intcst],comma,+084); {+085}
nextif(semicolon,+086)
end end;
procedure constdefinition(fsys:sos);
var lip:ip;
begin
repeat lip:=newident(konst,nil,nil,+087);
if lip<>nil then
begin nextif(eqsy,+088);
constant(fsys+[semicolon,ident],lip^.idtype,lip^.value);
nextif(semicolon,+089); enterid(lip);
end;
until not find2([ident],fsys,+090);
end;
procedure typedefinition(fsys:sos);
var lip:ip;
begin fwptr:=nil; intypedec:=true;
repeat lip:=newident(types,nil,nil,+091);
if lip<>nil then
begin nextif(eqsy,+092);
lip^.idtype:=typ(fsys+[semicolon,ident]);
nextif(semicolon,+093); enterid(lip);
end;
until not find2([ident],fsys,+094);
assert sy<>ident;
while fwptr<>nil do
begin
id:=fwptr^.name; lip:=searchid([types]);
fwptr^.idtype^.eltype:=lip^.idtype; fwptr:=fwptr^.next
end;
intypedec:=false;
end;
procedure vardeclaration(fsys:sos);
var lip,hip,vip:ip; lsp:sp;
begin
repeat hip:=nil; lip:=nil;
repeat vip:=newident(vars,nil,nil,+095);
if vip<>nil then
begin enterid(vip); vip^.iflag:=[];
if lip=nil then hip:=vip else lip^.next:=vip; lip:=vip;
end;
until endofloop(fsys+[colon1,ident..packedsy],[ident],comma,+096); {+097}
nextif(colon1,+098);
lsp:=typ(fsys+[semicolon,ident]);
while hip<>nil do
begin hip^.idtype:=lsp;
if level<=1 then
hip^.vpos.ad:=posaddr(holeb,lsp,false)
else
hip^.vpos.ad:=negaddr(lsp);
hip:=hip^.next
end;
nextif(semicolon,+099);
until not find2([ident],fsys,+0100);
end;
procedure pfhead(fsys:sos;var fip:ip;var again:boolean;param:boolean);
forward;
procedure parlist(fsys:sos; slink:boolean; var tip:ip; var maxlb:integer);
var lastip,hip,lip,pip:ip; lsp,tsp:sp; iflag:iflagset; again:boolean;
begin tip:=nil; lastip:=nil;
maxlb:=0; if slink then maxlb:=sz_addr;
repeat {once for each formal-parameter-section}
if find1([ident,varsy,procsy,funcsy],fsys+[semicolon],+0101) then
begin
if (sy=procsy) or (sy=funcsy) then
begin
pfhead(fsys+[semicolon,ident,varsy,procsy,funcsy],hip,again,true);
hip^.pfpos.ad:=posaddr(maxlb,procptr,false);
hip^.pfkind:=formal; lip:=hip;
top:=top^.nlink; level:=level-1
end
else
begin hip:=nil; lip:=nil; iflag:=[assigned];
if sy=varsy then
begin iflag:=[refer,assigned,used]; insym end;
repeat pip:=newident(vars,nil,nil,+0102);
if pip<>nil then
begin enterid(pip); pip^.iflag:=iflag;
if lip=nil then hip:=pip else lip^.next:=pip; lip:=pip;
end;
iflag:=iflag+[samesect];
until endofloop(fsys+[semicolon,colon1],[ident],comma,+0103);
{+0104}
nextif(colon1,+0105);
if refer in iflag then
begin lsp:=vpartyp(fsys+[semicolon]); tsp:=lsp;
while formof(tsp,[carray]) do
begin tsp^.arpos.ad:=posaddr(maxlb,nilptr,false);
tsp:=tsp^.aeltype
end;
tsp:=nilptr;
end
else
begin lsp:=typid(+0106); tsp:=lsp end;
pip:=hip;
while pip<>nil do
begin pip^.vpos.ad:=posaddr(maxlb,tsp,false); pip^.idtype:=lsp;
pip:=pip^.next
end;
end;
if lastip=nil then tip:=hip else lastip^.next:=hip; lastip:=lip;
end;
until endofloop(fsys,[ident,varsy,procsy,funcsy],semicolon,+0107); {+0108}
end;
procedure pfhead; {forward declared}
var lip:ip; lsp:sp; lnp:np; kl:idclass;
begin lip:=nil; again:=false;
if sy=procsy then kl:=proc else
begin kl:=func; fsys:=fsys+[colon1,ident] end;
insym;
if sy<>ident then begin error(+0109); id:=spaces end;
if not param then lip:=searchsection(top^.fname);
if lip<>nil then
if (lip^.klass<>kl) or (lip^.pfkind<>forward) then errid(+0110,id) else
begin b.forwcount:=b.forwcount-1; again:=true end;
if again then insym else
begin lip:=newip(kl,id,nil,nil);
if sy=ident then begin enterid(lip); insym end;
lastpfno:=lastpfno+1; lip^.pfno:=lastpfno;
end;
level:=level+1;
new(lnp,blck); lnp^.occur:=blck; lnp^.nlink:=top; top:=lnp;
if again then lnp^.fname:=lip^.parhead else
begin lnp^.fname:=nil;
if find3(lparent,fsys,+0111) then
begin parlist(fsys+[rparent],lip^.pfpos.lv>1,lip^.parhead,lip^.maxlb);
nextif(rparent,+0112)
end;
end;
if (kl=func) and not again then
begin nextif(colon1,+0113); lsp:=typid(+0114);
if formof(lsp,[power..tag]) then
begin error(+0115); lsp:=nil end;
lip^.idtype:=lsp;
end;
fip:=lip;
end;
procedure pfdeclaration(fsys:sos);
var lip:ip; again,headonly:boolean; markp:^integer; lbp:bp; kind:kindofpf;
begin with b do begin
pfhead(fsys+[ident,semicolon,labelsy..beginsy],lip,again,false);
nextif(semicolon,+0116);
if find1([ident,labelsy..beginsy],fsys+[semicolon],+0117) then
begin headonly:=sy=ident;
if headonly then
begin kind:=standard;
if id='forward ' then kind:=forward else
if id='extern ' then kind:=extern else
if id='varargs ' then kind:=varargs else errid(+0118,id);
if kind<>standard then
begin insym; lip^.pfkind:=kind;
if kind=forward then
if again then errid(+0119,lip^.name) else
forwcount:=forwcount+1
else
begin lip^.pfpos.lv:=1; teststandard end
end;
end;
if not again then
if lip^.pfpos.lv<=1 then genpnam(ps_exp,lip) else genpnam(ps_inp,lip);
if not headonly then
begin lip^.pfkind:=actual;
#ifndef STANDARD
mark(markp);
#endif
new(lbp); lbp^:=b; nextbp:=lbp;
reglb:=0; minlb:=0; ilbno:=0; forwcount:=0; lchain:=nil;
block(fsys+[semicolon],lip);
b:=nextbp^;
#ifndef STANDARD
release(markp);
#endif
end;
end;
if not main then eofexpected:=forwcount=0;
nextif(semicolon,+0120);
level:=level-1; top:=top^.nlink;
end end;
{===================================================================}
procedure expression(fsys:sos); forward;
{this forward declaration cannot be avoided}
procedure selectarrayelement(fsys:sos);
var isp,lsp:sp;
begin
repeat loadaddr; isp:=nil;
if formof(a.asp,[arrays,carray]) then isp:=a.asp^.inxtype else
errasp(+0121);
lsp:=a.asp;
expression(fsys+[comma]); force(desub(isp),+0122);
{no range check}
if lsp<>nil then
begin a.packbit:=spack in lsp^.sflag;
descraddr(lsp^.arpos); lsp:=lsp^.aeltype
end;
a.asp:=lsp; a.ak:=indexed;
until endofloop(fsys,[notsy..lparent],comma,+0123); {+0124}
end;
procedure selector(fsys: sos; fip:ip; iflag:iflagset);
{selector computes the address of any kind of variable.
Four possibilities:
1.for direct accessable variables (fixed), a contains offset and level,
2.for indirect accessable variables (ploaded), the address is on the stack.
3.for array elements, the top of stack gives the index (one word).
The address of the array is beneath it.
4.for variables with address in direct accessible pointer variable (pfixed),
the offset and level of the pointer is stored in a.
If a.asp=nil then an error occurred else a.asp gives
the type of the variable.
}
var lip:ip;
begin inita(fip^.idtype,0);
case fip^.klass of
vars: with a do
begin pos:=fip^.vpos; if refer in fip^.iflag then ak:=pfixed end;
field:
begin a:=lastnp^.wa; fieldaddr(fip^.foffset); a.asp:=fip^.idtype end;
func: with a do
if fip^.pfkind=standard then errasp(+0125) else
if (fip^.pfpos.lv>=level-1) and (fip<>currproc) then error(+0126) else
if fip^.pfkind<>actual then error(+0127) else
begin pos:=fip^.pfpos; pos.lv:=pos.lv+1;
if sy=arrow then error(+0128);
end
end; {case}
if (sy=lbrack) or (sy=period) then iflag:=iflag+[noreg];
while find2([lbrack,period,arrow],fsys,+0129) do with a do
if sy=lbrack then
begin insym; selectarrayelement(fsys+[rbrack,lbrack,period,arrow]);
nextif(rbrack,+0130);
end else
if sy=period then
begin insym;
if sy<>ident then error(+0131) else
begin
if not formof(asp,[records]) then errasp(+0132) else
begin lip:=searchsection(asp^.fstfld);
if lip=nil then begin errid(+0133,id); asp:=nil end else
begin packbit:=spack in asp^.sflag;
fieldaddr(lip^.foffset); asp:=lip^.idtype
end
end;
insym
end
end
else
begin insym; iflag:=[used];
if asp<>nil then
if asp=zeroptr then errasp(+0134) else
if asp^.form=pointer then
begin
if ak=fixed then ak:=pfixed else
begin load; ak:=ploaded end;
asp:=asp^.eltype
end else
if asp^.form=files then
begin loadaddr; gensp(WDW,sz_addr); gencst(op_lfr,sz_addr);
asp:=asp^.filtype; ak:=ploaded; packbit:=true;
end
else errasp(+0135);
end;
fip^.iflag:=fip^.iflag+iflag;
end;
procedure variable(fsys:sos);
var lip: ip;
begin
if sy=ident then
begin lip:=searchid([vars,field]); insym;
selector(fsys,lip,[used,assigned,noreg])
end
else begin error(+0136); inita(nil,0) end;
end;
{===================================================================}
function plistequal(p1,p2:ip):boolean;
var ok:boolean; q1,q2:sp;
begin plistequal:=eqstruct(p1^.idtype,p2^.idtype);
p1:=p1^.parhead; p2:=p2^.parhead;
while (p1<>nil) and (p2<>nil) do
begin ok:=false;
if p1^.klass=p2^.klass then
if p1^.klass<>vars then ok:=plistequal(p1,p2) else
begin q1:=p1^.idtype; q2:=p2^.idtype; ok:=true;
while ok and formof(q1,[carray]) and formof(q2,[carray]) do
begin ok:=eqstruct(q1^.inxtype,q2^.inxtype);
q1:=q1^.aeltype; q2:=q2^.aeltype;
end;
if not (eqstruct(q1,q2) and
(p1^.iflag*[refer,samesect] = p2^.iflag*[refer,samesect]))
then ok:=false;
end;
if not ok then plistequal:=false;
p1:=p1^.next; p2:=p2^.next
end;
if (p1<>nil) or (p2<>nil) then plistequal:=false
end;
procedure callnonstandard(fsys:sos; moreargs:boolean; fip:ip);
var nxt,lip:ip; l0,l1,l2,l3,sz:integer; lsp,savasp:sp;
begin with a do begin
l0:=newmark; l1:=newmark; sz:=0; nxt:=fip^.parhead;
while moreargs do
begin
if nxt=nil then
begin if fip^.pfkind<>varargs then error(+0137);
expression(fsys); load; sz:=sz+sizeof(asp,wordmult)
end
else
begin lsp:=nxt^.idtype;
if nxt^.klass<>vars then {proc or func}
begin inita(procptr,0); sz:=sz+sz_proc;
if sy<>ident then error(+0138) else
begin lip:=searchid([nxt^.klass]); insym;
if lip^.pfkind=standard then error(+0139) else
if not plistequal(nxt,lip) then error(+0140)
else
begin pos:=lip^.pfpos;
if lip^.pfkind=formal then load else
begin
if lip^.pfpos.lv<=1 then gencst(op_zer,sz_addr) else
gencst(op_lxl,level-lip^.pfpos.lv);
genpnam(op_lpi,lip)
end
end
end
end
else if not (refer in nxt^.iflag) then {call by value}
begin expression(fsys); force(lsp,+0141);
sz:=sz+sizeof(asp,wordmult);
end
else {call by reference}
begin variable(fsys); loadaddr; sz:=sz+sz_addr;
if samesect in nxt^.iflag then lsp:=savasp else
begin savasp:=asp; l2:=newmark;
while formof(lsp,[carray])
and formof(asp,[arrays,carray]) do
if (compat(lsp^.inxtype,asp^.inxtype) > subeq) or
(lsp^.sflag<>asp^.sflag) then errasp(+0142) else
begin l3:=newmark; descraddr(asp^.arpos); exchange(l2,l3);
relmark(l3);
sz:=sz+sz_addr; asp:=asp^.aeltype; lsp:=lsp^.aeltype
end;
relmark(l2)
end;
if not eqstruct(asp,lsp) then errasp(+0143);
if packbit then errasp(+0144);
end;
nxt:=nxt^.next
end;
exchange(l0,l1);
relmark(l1);
l1:=newmark; moreargs:=find3(comma,fsys,+0145)
end;
relmark(l0); relmark(l1);
if nxt<>nil then error(+0146);
inita(procptr,0); pos:=fip^.pfpos;
if fip^.pfkind=formal then
with b do
begin load; ilbno:=ilbno+2;
gencst(op_exg,sz_addr);
gencst(op_dup,sz_addr);
gencst(op_zer,sz_addr);
genop(op_cmp);
gencst(op_zeq,ilbno-1);
gencst(op_exg,sz_addr);
genop(op_cai);
gencst(op_asp,sz_addr);
gencst(op_bra,ilbno);
newilb(ilbno-1);
gencst(op_asp,sz_addr);
genop(op_cai);
newilb(ilbno);
end
else
begin
if pos.lv>1 then
begin gencst(op_lxl,level-pos.lv); sz:=sz+sz_addr end;
genpnam(op_cal,fip)
end;
if sz<>0 then gencst(op_asp,sz);
asp:=fip^.idtype;
if asp<>nil then genasp(op_lfr);
if (fip^.pfkind = extern) and (opt['l'] <> off) then
begin genop(op_fil); argdlb(fildlb) end;
genlin
end end;
procedure fileaddr;
var la:attr;
begin la:=a; a:=fa; loadaddr; a:=la end;
procedure callr(l1,l2:integer);
var la:attr; m:libmnem;
begin with a do begin
la:=a; asp:=desub(asp); fileaddr; m:=RDI;
if asp<>intptr then
if asp=charptr then m:=RDC else
if asp=realptr then m:=RDR else
if asp=longptr then m:=RDL else errasp(+0147);
gensp(m,sz_addr); genasp(op_lfr);
if asp<>la.asp then checkbnds(la.asp);
a:=la; exchange(l1,l2); store;
end end;
procedure callw(fsys:sos; l1,l2:integer);
var m:libmnem; s:integer;
begin with a do begin
fileaddr; exchange(l1,l2); loadcheap; asp:=desub(asp);
if string(asp) then
begin gencst(op_loc,asp^.size); m:=WRS; s:=sz_addr+sz_word end
else
begin m:=WRI; s:=sizeof(asp,wordmult);
if asp<>intptr then
if asp=charptr then m:=WRC else
if asp=realptr then m:=WRR else
if asp=boolptr then m:=WRB else
if asp=zeroptr then m:=WRZ else
if asp=longptr then m:=WRL else errasp(+0148);
end;
if find3(colon1,fsys,+0149) then
begin expression(fsys+[colon1]); force(intptr,+0150);
m:=succ(m); s:=s+sz_int
end;
if find3(colon1,fsys,+0151) then
begin expression(fsys); force(intptr,+0152); s:=s+sz_int;
if m<>WSR then error(+0153) else m:=WRF;
end;
gensp(m,s+sz_addr);
end end;
procedure callrw(fsys:sos; lpar,w,ln:boolean);
var l1,l2,errno:integer; ftype,lsp,fsp:sp; (* savlb:integer;*) m:libmnem;
begin with b do begin (* savlb:=reglb; *) ftype:=textptr;
inita(textptr,argv[ord(w)].ad); a.pos.lv:=0; fa:=a;
if lpar then
begin l1:=newmark; if w then expression(fsys+[colon1]) else variable(fsys);
l2:=newmark;
if formof(a.asp,[files]) then
begin ftype:=a.asp;
if (a.ak<>fixed) and (a.ak<>pfixed) then
begin loadaddr; temporary(nilptr,reg_pointer);
store; a.ak:=pfixed
end;
fa:=a; {store does not change a}
if (sy<>comma) and not ln then error(+0154);
end
else
begin if iop[w]=nil then error(+0155);
if w then callw(fsys,l1,l2) else callr(l1,l2)
end;
relmark(l1); relmark(l2);
while find3(comma,fsys,+0156) do with a do
begin l1:=newmark;
if w then expression(fsys+[colon1]) else variable(fsys);
l2:=newmark;
if ftype=textptr then
if w then callw(fsys,l1,l2) else callr(l1,l2)
else
begin errno:=+0157; fsp:=ftype^.filtype;
if w then force(fsp,errno) else
begin store; lsp:=asp; relmark(l2); l2 := newmark end;
fileaddr; gensp(WDW,sz_addr); gencst(op_lfr,sz_addr);
ak:=ploaded; packbit:=true; asp:=fsp;
if w then store else
begin force(lsp,errno); exchange(l1,l2) end;
fileaddr; if w then m:=PUTX else m:=GETX; gensp(m,sz_addr)
end;
relmark(l1); relmark(l2);
end;
end
else
if not ln then error(+0158) else
if iop[w]=nil then error(+0159);
if ln then
begin if ftype<>textptr then error(+0160);
fileaddr; if w then m:=WLN else m:=RLN; gensp(m,sz_addr)
end;
(* reglb:=savlb *)
end end;
procedure callnd(fsys:sos);
label 1;
var lsp:sp; int:integer;
begin with a do begin
if asp=zeroptr then errasp(+0161) else asp:=asp^.eltype;
while find3(comma,fsys,+0162) do
begin
if asp<>nil then {asp of form record or variant}
if asp^.form=records then asp:=asp^.tagsp else
if asp^.form=variant then asp:=asp^.subtsp else errasp(+0163);
if asp=nil then constant(fsys,lsp,int) else
begin assert asp^.form=tag;
int:=cstinteger(fsys,asp^.tfldsp,+0164); lsp:=asp^.fstvar;
while lsp<>nil do
if lsp^.varval<>int then lsp:=lsp^.nxtvar else
begin asp:=lsp; goto 1 end;
end;
1: end;
genasp(op_loc)
end end;
procedure call(fsys: sos; fip: ip);
var lkey: standpf; lpar:boolean; lsp,sp1,sp2:sp;
m:libmnem; s:integer; b:byte;
begin with a do begin fsys:=fsys+[comma];
lpar:=find3(lparent,fsys,+0165); if lpar then fsys:=fsys+[rparent];
if fip^.pfkind<>standard then callnonstandard(fsys,lpar,fip) else
begin lkey:=fip^.key; m:=CLS; lsp:=nil;
if not lpar then
if lkey in [pput..prelease,fabs..fatn] then error(+0166);
if lkey in [pput..ppage,feof,feoln] then
begin s:=sz_addr;
if lpar then
begin variable(fsys); loadaddr end
else
begin asp:=textptr;
if iop[lkey=ppage]=nil then errasp(+0167) else
gencst(op_lae,argv[ord(lkey=ppage)].ad)
end;
if lkey in [pput..prewrite,ppage,feof,feoln] then
if not formof(asp,[files]) then
begin error(+0168); asp:=textptr end;
if lkey in [pnew,pdispose,pmark,prelease] then
if not formof(asp,[pointer]) then
begin error(+0169); asp:=nilptr end;
end;
case lkey of
pread, preadln, pwrite, pwriteln: {0,1,2,3 resp}
callrw(fsys,lpar,lkey>=pwrite,odd(ord(lkey)));
pput: m:=PUTX;
pget: m:=GETX;
ppage: m:=PAG;
preset: m:=OPN;
prewrite: m:=CRE;
pnew: m:=NEWX;
pdispose: m:=DIS;
ppack:
begin sp2:=asp; nextif(comma,+0170); expression(fsys); load;
lsp:=asp; nextif(comma,+0171); variable(fsys); loadaddr;
sp1:=asp; asp:=lsp; m:=PAC
end;
punpack:
begin sp1:=asp; nextif(comma,+0172); variable(fsys); loadaddr;
sp2:=asp; nextif(comma,+0173); expression(fsys); load;
m:=UNP
end;
pmark: m:=SAV;
prelease: m:=RST;
phalt:
begin m:=HLT; teststandard;
if lpar then lsp:=intptr else gencst(op_loc,0);
end;
feof: m:=EFL;
feoln: m:=ELN;
fodd, fchr: lsp:=intptr;
fpred: b:=op_dec;
fsucc: b:=op_inc;
fround: m:=RND;
fsin, fcos, fexp, fsqt, flog, fatn: lsp:=realptr;
fabs, fsqr, ford, ftrunc: ;
end;
if lpar then if lkey in [phalt,fabs..fatn] then
begin expression(fsys);
force(lsp,+0174); s:=sizeof(asp,wordmult)
end;
if lkey in [ppack,punpack,fabs..fodd] then
asp:=desub(asp);
case lkey of
ppage, feoln:
begin if asp<>textptr then error(+0175); asp:=boolptr end;
preset, prewrite:
begin s:=sz_addr+sz_word;
if asp=textptr then gencst(op_loc,0) else
gencst(op_loc,sizeof(asp^.filtype,wordpart));
end;
pnew, pdispose:
begin callnd(fsys); s:=sz_addr+sz_word end;
ppack, punpack:
begin s:=2*sz_addr+sz_int;
if formof(sp1,[arrays,carray])
and formof(sp2,[arrays,carray]) then
if (spack in (sp1^.sflag - sp2^.sflag)) and
eqstruct(sp1^.aeltype,sp2^.aeltype) and
eqstruct(desub(sp1^.inxtype),asp) and
eqstruct(desub(sp2^.inxtype),asp) then
begin descraddr(sp1^.arpos); descraddr(sp2^.arpos) end
else error(+0176)
else error(+0177)
end;
pmark, prelease: teststandard;
feof: asp:=boolptr;
fabs:
if asp=intptr then m:=ABI else
if asp=longptr then m:=ABL else
if asp=realptr then m:=ABR else errasp(+0178);
fsqr:
begin
if (asp=intptr) or (asp=longptr) then b:=op_mli else
if asp=realptr then begin b:=op_mlf; fltused:=true end
else errasp(+0179);
genasp(op_dup); genasp(b)
end;
ford:
begin if not nicescalar(asp) then errasp(+0180); asp:=intptr end;
fchr: checkbnds(charptr);
fpred, fsucc:
begin genop(b);
if nicescalar(asp) then genrck(asp) else errasp(+0181)
end;
fodd:
begin gencst(op_loc,1); asp:=boolptr; genasp(op_and) end;
ftrunc, fround: if asp<>realptr then errasp(+0182);
fsin: m:=SINX;
fcos: m:=COSX;
fexp: m:=EXPX;
fsqt: m:=SQT;
flog: m:=LOG;
fatn: m:=ATN;
phalt:s:=0;
pread, preadln, pwrite, pwriteln, pput, pget: ;
end;
if m<>CLS then
begin gensp(m,s);
if lkey>=feof then genasp(op_lfr)
end;
if (lkey=fround) or (lkey=ftrunc) then
opconvert(ri);
end;
if lpar then nextif(rparent,+0183);
end end;
{===================================================================}
procedure convert(fsp:sp; l1:integer);
{Convert tries to make the operands of some operator of the same type.
The operand types are given by fsp and a.asp. The resulting type
is put in a.asp.
l1 gives the lino of the first instruction of the right operand.
}
var l2:integer; ts:twostruct; lsp:sp;
begin with a do begin asp:=desub(asp);
ts:=compat(asp,fsp);
case ts of
eq,subeq:
;
il,ir,lr:
opconvert(ts);
es:
expandnullset(fsp);
li,ri,rl,se:
begin l2:=newmark; lsp:=asp; asp:=fsp;
convert(lsp,l1); exchange(l1,l2); relmark(l2); asp:=lsp
end;
noteq:
errasp(+0184);
end;
if asp=realptr then fltused:=true
end end;
procedure buildset(fsys:sos);
{This is a bad construct in pascal. Two objections:
- expr..expr very difficult to implement on most machines
- this construct makes it hard to implement sets of different size
}
const ncsb = 32; {tunable}
type byteset = set of 0..MB1;
var i,j,val1,val2,ncst,l1,l2,sz:integer;
cst1,cst2,cst12,varpart:boolean;
cstpart:array[1..ncsb] of byteset;
procedure genconstset(sz:integer);
{level 2: << buildset}
var i,j:integer;
function setcode(s:byteset):byte;
{level 3: << buildset}
var b,i,w:byte;
begin i:=0; w:=0; b:=1;
for i:=0 to MB1 do
begin if i in s then w:=w+b; b:=b+b end;
setcode := w;
end;
begin
i:=sz;
repeat
genop(op_loc); j:=i; i:=i-sz_word;
{the bytes of the next word to be loaded on the stack}
{are in cstpart[i+1] .. cstpart[j]}
while (cstpart[j] = []) and (j > i+1) do j:=j-1;
if j = i+1 then argcst(setcode(cstpart[j]))
else
begin
if (j = i+2) and ((sz_word <= 2) or not (MB1 in cstpart[j])) then put1(sp_cst2)
else begin j:=i+4; put1(sp_cst4) end;
for j:=i+1 to j do put1(setcode(cstpart[j]))
end;
until i = 0;
end;
procedure setexpr(fsys:sos; var c:boolean; var v:integer);
{level 2: << buildset}
var min:integer; lsp:sp;
begin with a do begin c:=false; v:=0; lsp:=asp;
expression(fsys); asp:=desub(asp);
if not eqstruct(asp,lsp^.elset) then
begin error(+0185); lsp:=nullset end;
if lsp=nullset then
begin
if bounded(asp) then bounds(asp,min,sz) else
if asp=intptr then sz:=iopt-1 else begin errasp(+0186); sz:=0 end;
sz:=sz div NB1 + 1; while sz mod sz_word <> 0 do sz:=sz+1;
if sz>sz_mset then errasp(+0187);
lsp:=newsp(power,sz); lsp^.elset:=asp
end;
if asp<>nil then if ak=cst then
if (pos.ad<0) or (pos.ad div NB1>=sizeof(lsp,wordmult)) then
error(+0188)
else if sz<=ncsb*sz_byte then
begin c:=true; v:=pos.ad end;
if not c then load; asp:=lsp
end end;
begin with a do begin {buildset}
varpart:=false; ncst:=0; asp:=nullset;
for i:=1 to ncsb do cstpart[i]:=[];
if find2([notsy..lparent],fsys,+0189) then
repeat l1:=newmark;
setexpr(fsys+[colon2,comma],cst1,val1); cst12:=cst1;
if find3(colon2,fsys+[comma,notsy..lparent],+0190) then
begin setexpr(fsys+[comma,notsy..lparent],cst2,val2);
cst12:=cst12 and cst2;
if not cst12 then
begin
if cst2 then gencst(op_loc,val2);
if cst1 then
begin l2:=newmark; gencst(op_loc,val1); exchange(l1,l2);
relmark(l2);
end;
l2:=newmark; genasp(op_zer); exchange(l1,l2);
relmark(l2);
genasp(op_loc); gensp(BTS,3*sz_word)
end;
end
else
if cst12 then val2:=val1 else genasp(op_set);
if cst12 then
for i:=val1 to val2 do
begin j:=i div NB1 + 1; ncst:=ncst+1;
cstpart[j]:=cstpart[j] + [i mod NB1]
end
else
if varpart then genasp(op_ior) else varpart:=true;
relmark(l1);
until endofloop(fsys,[notsy..lparent],comma,+0191); {+0192}
ak:=loaded;
if ncst>0 then
begin
genconstset(sizeof(asp,wordmult));
if varpart then genasp(op_ior);
end
else
if not varpart then genasp(op_zer); {empty set}
end end;
procedure factor(fsys: sos);
var lip:ip; lsp:sp;
begin with a do begin
asp:=nil; packbit:=false; ak:=loaded;
if find1([notsy..nilcst,lparent],fsys,+0193) then
case sy of
ident:
begin lip:=searchid([konst,vars,field,func,carrbnd]); insym;
case lip^.klass of
func: {call moves result to top stack}
begin call(fsys,lip); ak:=loaded; packbit:=false end;
konst:
begin asp:=lip^.idtype;
if nicescalar(asp) then {including asp=nil}
begin ak:=cst; pos.ad:=lip^.value end
else
begin ak:=ploaded; laedlb(abs(lip^.value));
if asp^.form=scalar then
begin load; if lip^.value<0 then negate end
else
if asp=zeroptr then ak:=loaded
end
end;
field,vars:
selector(fsys,lip,[used]);
carrbnd:
begin lsp:=lip^.idtype; assert formof(lsp,[carray]);
descraddr(lsp^.arpos); lsp:=lsp^.inxtype; asp:=desub(lsp);
if lip^.next=nil then ak:=ploaded {low bound} else
begin gencst(op_loi,2*sz_int); genasp(op_adi) end;
load; checkbnds(lsp);
end;
end {case}
end;
intcst:
begin asp:=intptr; ak:=cst; pos.ad:=val; insym end;
realcst:
begin asp:=realptr; ak:=ploaded; laedlb(val); insym end;
longcst:
begin asp:=longptr; ak:=ploaded; laedlb(val); insym end;
charcst:
begin asp:=charptr; ak:=cst; pos.ad:=val; insym end;
stringcst:
begin asp:=stringstruct; laedlb(val); insym;
if asp<>zeroptr then ak:=ploaded;
end;
nilcst:
begin insym; asp:=nilptr; genasp(op_zer); end;
lparent:
begin insym; expression(fsys+[rparent]); nextif(rparent,+0194) end;
notsy:
begin insym; factor(fsys); load; genop(op_teq); asp:=desub(asp);
if asp<>boolptr then errasp(+0195)
end;
lbrack:
begin insym; buildset(fsys+[rbrack]); nextif(rbrack,+0196) end;
end
end end;
procedure term(fsys:sos);
var lsy:symbol; lsp:sp; l1:integer; first:boolean;
begin with a,b do begin first:=true;
factor(fsys+[starsy..andsy]);
while find2([starsy..andsy],fsys,+0197) do
begin if first then begin load; first:=false end;
lsy:=sy; insym; l1:=newmark; lsp:=asp;
factor(fsys+[starsy..andsy]); load; convert(lsp,l1);
if asp<>nil then
case lsy of
starsy:
if (asp=intptr) or (asp=longptr) then genasp(op_mli) else
if asp=realptr then genasp(op_mlf) else
if asp^.form=power then genasp(op_and) else errasp(+0198);
slashsy:
begin
if (asp=intptr) or (asp=longptr) then
begin lsp:=asp;
convert(realptr,l1); {make real of right operand}
convert(lsp,l1); {make real of left operand}
end;
if asp=realptr then genasp(op_dvf) else errasp(+0199);
end;
divsy:
if (asp=intptr) or (asp=longptr) then genasp(op_dvi) else
errasp(+0200);
modsy:
begin
if asp=intptr then gensp(MDI,2*sz_int) else
if asp=longptr then gensp(MDL,2*sz_long) else errasp(+0201);
genasp(op_lfr);
end;
andsy:
if asp=boolptr then genasp(op_and) else errasp(+0202);
end; {case}
relmark(l1)
end {while}
end end;
procedure simpleexpression(fsys:sos);
var lsy:symbol; lsp:sp; l1:integer; signed,min,first:boolean;
begin with a do begin first:=true;
signed:=(sy=plussy) or (sy=minsy);
if signed then begin min:=sy=minsy; insym end else min:=false;
term(fsys + [minsy,plussy,orsy]); lsp:=desub(asp);
if signed then
if (lsp<>intptr) and (lsp<>realptr) and (lsp<>longptr) then
errasp(+0203)
else if min then
begin load; first:=false; asp:=lsp; negate end;
while find2([plussy,minsy,orsy],fsys,+0204) do
begin if first then begin load; first:=false end;
lsy:=sy; insym; l1:=newmark; lsp:=asp;
term(fsys+[minsy,plussy,orsy]); load; convert(lsp,l1);
if asp<>nil then
case lsy of
plussy:
if (asp=intptr) or (asp=longptr) then genasp(op_adi) else
if asp=realptr then genasp(op_adf) else
if asp^.form=power then genasp(op_ior) else errasp(+0205);
minsy:
if (asp=intptr) or (asp=longptr) then genasp(op_sbi) else
if asp=realptr then genasp(op_sbf) else
if asp^.form=power then begin genasp(op_com); genasp(op_and) end
else errasp(+0206);
orsy:
if asp=boolptr then genasp(op_ior) else errasp(+0207);
end; {case}
relmark(l1)
end {while}
end end;
procedure expression; { fsys:sos }
var lsy:symbol; lsp:sp; l1,l2:integer;
begin with a do begin l1:=newmark;
simpleexpression(fsys+[eqsy..insy]);
if find2([eqsy..insy],fsys,+0208) then
begin lsy:=sy; insym; lsp:=asp; loadcheap; l2:=newmark;
simpleexpression(fsys); loadcheap;
if lsy=insy then
begin
if not formof(asp,[power]) then errasp(+0209) else
if asp=nullset then genasp(op_and) else
{this effectively replaces the word on top of the
stack by the result of the 'in' operator: false }
if not (compat(lsp,asp^.elset) <= subeq) then errasp(+0210) else
begin exchange(l1,l2); genasp(op_inn) end
end
else
begin convert(lsp,l2);
if asp<>nil then
case asp^.form of
scalar:
if asp=realptr then genasp(op_cmf) else genasp(op_cmi);
pointer:
if (lsy=eqsy) or (lsy=nesy) then genop(op_cmp) else
errasp(+0211);
power:
case lsy of
eqsy,nesy: genasp(op_cms);
ltsy,gtsy: errasp(+0212);
lesy: {'a<=b' equivalent to 'a-b=[]'}
begin genasp(op_com); genasp(op_and); genasp(op_zer);
genasp(op_cms); lsy:=eqsy
end;
gesy: {'a>=b' equivalent to 'a=a+b'}
begin gencst(op_dup,2*sizeof(asp,wordmult));
genasp(op_asp); genasp(op_ior);
genasp(op_cms); lsy:=eqsy
end
end; {case}
arrays:
if string(asp) then
begin gencst(op_loc,asp^.size);
gensp(BCP,2*sz_addr+sz_word);
gencst(op_lfr,sz_word)
end
else errasp(+0213);
records: errasp(+0214);
files: errasp(+0215)
end; { case }
case lsy of
ltsy: genop(op_tlt);
lesy: genop(op_tle);
gtsy: genop(op_tgt);
gesy: genop(op_tge);
nesy: genop(op_tne);
eqsy: genop(op_teq)
end
end;
relmark(l2);
asp:=boolptr; ak:=loaded
end;
relmark(l1)
end end;
{===================================================================}
procedure statement(fsys:sos); forward;
{this forward declaration can be avoided}
procedure assignment(fsys:sos; fip:ip);
var la:attr; l1,l2:integer;
begin
l1:=newmark; selector(fsys+[becomes],fip,[assigned]); l2:=newmark;
la:=a; nextif(becomes,+0216);
expression(fsys); loadcheap; checkasp(la.asp,+0217);
exchange(l1,l2); a:=la;
relmark(l1); relmark(l2);
if not formof(la.asp,[arrays..records]) then store else
begin loadaddr;
if la.asp^.form<>carray then genasp(op_blm) else
begin descraddr(la.asp^.arpos); gensp(ASZ,sz_addr);
gencst(op_lfr,sz_word); gencst(op_bls,sz_word)
end;
end;
end;
procedure gotostatement;
{jumps into structured statements can give strange results. }
label 1;
var llp:lp; lbp:bp; diff:integer;
begin
if sy<>intcst then error(+0218) else
begin llp:=searchlab(b.lchain,val);
if llp<>nil then gencst(op_bra,llp^.labname) else
begin lbp:=b.nextbp; diff:=1;
while lbp<>nil do
begin llp:=searchlab(lbp^.lchain,val);
if llp<>nil then goto 1;
lbp:=lbp^.nextbp; diff:=diff+1;
end;
1: if llp=nil then errint(+0219,val) else
begin
if llp^.labdlb=0 then
begin dlbno:=dlbno+1; llp^.labdlb:=dlbno;
genop(ps_ina); argdlb(dlbno); {forward data reference}
end;
laedlb(llp^.labdlb);
if diff=level-1 then gencst(op_zer,sz_addr) else
gencst(op_lxl,diff);
gensp(GTO,2*sz_addr);
end;
end;
insym;
end
end;
procedure compoundstatement(fsys:sos; err:integer);
begin
repeat statement(fsys+[semicolon])
until endofloop(fsys,[beginsy..casesy],semicolon,err)
end;
procedure ifstatement(fsys:sos);
var lb1,lb2:integer;
begin with b do begin
expression(fsys+[thensy,elsesy]);
force(boolptr,+0220); ilbno:=ilbno+1; lb1:=ilbno; gencst(op_zeq,lb1);
nextif(thensy,+0221); statement(fsys+[elsesy]);
if find3(elsesy,fsys,+0222) then
begin ilbno:=ilbno+1; lb2:=ilbno; gencst(op_bra,lb2);
newilb(lb1); statement(fsys); newilb(lb2)
end
else newilb(lb1);
end end;
procedure casestatement(fsys:sos);
label 1;
type cip=^caseinfo;
caseinfo=record
next: cip;
csstart: integer;
cslab: integer
end;
var lsp:sp; head,p,q,r:cip; l0,l1:integer;
ilb1,ilb2,dlb,i,n,m,min,max:integer;
begin with b do begin
expression(fsys+[ofsy,semicolon,ident..plussy]); lsp:=a.asp; load;
if not nicescalar(desub(lsp)) then begin error(+0223); lsp:=nil end;
l0:=newmark; ilbno:=ilbno+1; ilb1:=ilbno;
nextif(ofsy,+0224); head:=nil; max:=-MI; min:=MI; n:=0;
repeat ilbno:=ilbno+1; ilb2:=ilbno; {label of current case}
repeat i:=cstinteger(fsys+[comma,colon1,semicolon],lsp,+0225);
if i>max then max:=i; if i<min then min:=i; n:=n+1;
q:=head; r:=nil; new(p);
while q<>nil do
begin {chain all cases in ascending order}
if q^.cslab>=i then
begin if q^.cslab=i then error(+0226); goto 1 end;
r:=q; q:=q^.next
end;
1: p^.next:=q; p^.cslab:=i; p^.csstart:=ilb2;
if r=nil then head:=p else r^.next:=p;
until endofloop(fsys+[colon1,semicolon],[ident..plussy],comma,+0227);
{+0228}
nextif(colon1,+0229); newilb(ilb2); statement(fsys+[semicolon]);
gencst(op_bra,ilb1);
until lastsemicolon(fsys,[ident..plussy],+0230); {+0231 +0232}
assert n<>0; newilb(ilb1); l1:=newmark;
dlb:=newdlb; genop(ps_rom); argnil;
if (max div 3) - (min div 3) < n then
begin argcst(min); argcst(max-min);
m:=op_csa;
while head<>nil do
begin
while head^.cslab>min do
begin argnil; min:=min+1 end;
argilb(head^.csstart); min:=min+1; head:=head^.next
end;
end
else
begin argcst(n); m:=op_csb;
while head<>nil do
begin argcst(head^.cslab);argilb(head^.csstart);head:=head^.next end;
end;
argend; laedlb(dlb); gencst(m,sz_word); exchange(l0,l1);
relmark(l0); relmark(l1)
end end;
procedure repeatstatement(fsys:sos);
var lb1: integer;
begin with b do begin
ilbno:=ilbno+1; lb1:=ilbno; newilb(lb1);
compoundstatement(fsys+[untilsy],+0233); {+0234}
nextif(untilsy,+0235); genlin;
expression(fsys); force(boolptr,+0236); gencst(op_zeq,lb1);
end end;
procedure whilestatement(fsys:sos);
var lb1,lb2: integer;
begin with b do begin
ilbno:=ilbno+1; lb1:=ilbno; newilb(lb1);
ilbno:=ilbno+1; lb2:=ilbno;
genlin; expression(fsys+[dosy]);
force(boolptr,+0237); gencst(op_zeq,lb2);
nextif(dosy,+0238); statement(fsys);
gencst(op_bra,lb1); newilb(lb2)
end end;
procedure forstatement(fsys:sos);
var lip:ip; tosym:boolean; endlab,looplab(* ,savlb *):integer;
av,at1,at2:attr; lsp:sp;
procedure forbound(fsys:sos; var fa:attr; fsp:sp);
begin
expression(fsys); fa:=a; force(fsp,+0239);
if fa.ak<>cst then
begin temporary(fsp,reg_any);
genasp(op_dup); fa:=a; store
end
end;
begin with b do begin (* savlb:=reglb; *) tosym:=false;
ilbno:=ilbno+1; looplab:=ilbno; ilbno:=ilbno+1; endlab:=ilbno;
inita(nil,0);
if sy<>ident then error(+0240) else
begin lip:=searchid([vars]); insym;
a.asp:=lip^.idtype; a.pos:=lip^.vpos;
lip^.iflag:=lip^.iflag+[used,assigned,loopvar];
if level>1 then
if (a.pos.ad>=0) or (a.pos.lv<>level) then
error(+0241);
end;
lsp:=desub(a.asp);
if not nicescalar(lsp) then begin errasp(+0242); lsp:=nil end;
av:=a; nextif(becomes,+0243);
forbound(fsys+[tosy,downtosy,notsy..lparent,dosy],at1,lsp);
if find1([tosy,downtosy],fsys+[notsy..lparent,dosy],+0244) then
begin tosym:=sy=tosy; insym end;
forbound(fsys+[dosy],at2,lsp);
if tosym then gencst(op_bgt,endlab) else gencst(op_blt,endlab);
a:=at1; force(av.asp,+0245); a:=av; store; newilb(looplab);
nextif(dosy,+0246); statement(fsys);
a:=av; load; a:=at2; load; gencst(op_beq,endlab);
a:=av; load; if tosym then genop(op_inc) else genop(op_dec);
a.asp:=lsp; checkbnds(av.asp); a:=av; store;
gencst(op_bra,looplab); newilb(endlab);
(* reglb:=savlb *)
end end;
procedure withstatement(fsys:sos);
var lnp,savtop:np; (* savlb:integer; *) pbit:boolean;
begin with b do begin
(* savlb:=reglb;*) savtop:=top;
repeat variable(fsys+[comma,dosy]);
if not formof(a.asp,[records]) then errasp(+0247) else
begin pbit:=spack in a.asp^.sflag;
new(lnp,wrec); lnp^.occur:=wrec; lnp^.fname:=a.asp^.fstfld;
if a.ak<>fixed then
begin loadaddr; temporary(nilptr,reg_pointer); store;
a.ak:=pfixed;
end;
a.packbit:=pbit; lnp^.wa:=a; lnp^.nlink:=top; top:=lnp;
end;
until endofloop(fsys+[dosy],[ident],comma,+0248); {+0249}
nextif(dosy,+0250); statement(fsys);
top:=savtop; (* reglb:=savlb; *)
end end;
procedure assertion(fsys:sos);
begin teststandard;
if opt['a']=off then
while not (sy in fsys) do insym
else
begin expression(fsys); force(boolptr,+0251);
gencst(op_loc,srcorig); gensp(ASS,2*sz_word);
end
end;
procedure statement; {fsys: sos}
var lip:ip; llp:lp; lsy:symbol;
begin
assert [labelsy..casesy,endsy] <= fsys;
assert [ident,intcst] * fsys = [];
if find2([intcst],fsys+[ident],+0252) then
begin llp:=searchlab(b.lchain,val);
if llp=nil then errint(+0253,val) else
begin if llp^.seen then errint(+0254,val) else llp^.seen:=true;
newilb(llp^.labname)
end;
insym; nextif(colon1,+0255);
end;
if find2([ident,beginsy..casesy],fsys,+0256) then
begin if giveline then if sy<>whilesy then genlin;
if sy=ident then
if id='assert ' then
begin insym; assertion(fsys) end
else
begin lip:=searchid([vars,field,func,proc]); insym;
if lip^.klass=proc then call(fsys,lip) else assignment(fsys,lip)
end
else
begin lsy:=sy; insym;
case lsy of
beginsy:
begin compoundstatement(fsys,+0257); {+0258}
nextif(endsy,+0259)
end;
gotosy:
gotostatement;
ifsy:
ifstatement(fsys);
casesy:
begin casestatement(fsys); nextif(endsy,+0260) end;
whilesy:
whilestatement(fsys);
repeatsy:
repeatstatement(fsys);
forsy:
forstatement(fsys);
withsy:
withstatement(fsys);
end
end;
end
end;
{===================================================================}
procedure body(fsys:sos; fip:ip);
var i,dlb,l0,l1,ssp:integer; llp:lp; spset:boolean;
begin with b do begin
{produce PRO}
genpnam(ps_pro,fip); argend;
gencst(ps_mes,ms_par);argcst(fip^.maxlb); argend;
l0:=newmark; dlb:=0; trace('procentr',fip,dlb);
{global labels}
llp:=lchain; spset:=false;
while llp<>nil do
begin
if llp^.labdlb<>0 then
begin
if not spset then
begin spset:=true;
gencst(ps_mes,ms_gto); argend;
temporary(nilptr,-1); ssp:=a.pos.ad;
gencst(op_lor,1); store
end;
argdlb(llp^.labdlb); lino:=lino+1; genop(ps_rom);
argilb(llp^.labname); argcst(ssp); argend;
end;
llp:=llp^.nextlp
end;
{the body itself}
currproc:=fip;
compoundstatement(fsys,+0261); {+0262}
trace('procexit',fip,dlb);
{undefined labels}
llp:=lchain;
while llp<>nil do
begin if not llp^.seen then errint(+0263,llp^.labval);
llp:=llp^.nextlp
end;
{finish and close files}
treewalk(top^.fname);
if level=1 then
begin l1:=newmark;
genop(op_fil); argdlb(fildlb); {temporarily}
dlb:=newdlb; gencst(ps_con,argc+1);
for i:=0 to argc do with argv[i] do
begin argcst(ad);
if (ad=-1) and (i>1) then errid(+0264,name)
end;
argend; gencst(op_lxl,0); laedlb(dlb); gencst(op_lae,0);
gencst(op_lxa,0); gensp(INI,4*sz_addr);
exchange(l0,l1); relmark(l1); gencst(op_loc,0); gensp(HLT,0)
end
else
begin inita(fip^.idtype,fip^.pfpos.ad);
if fip^.klass=func then
begin load;
if not (assigned in fip^.iflag) then
errid(-(+0265),fip^.name)
end;
genasp(op_ret)
end;
relmark(l0);
gencst(ps_end,-minlb)
end end;
{===================================================================}
procedure block; {forward declared}
begin with b do begin
assert [labelsy..withsy] <= fsys;
assert [ident,intcst,casesy,endsy,period] * fsys = [];
if find3(labelsy,fsys,+0266) then labeldeclaration(fsys);
if find3(constsy,fsys,+0267) then constdefinition(fsys);
if find3(typesy,fsys,+0268) then typedefinition(fsys);
if find3(varsy,fsys,+0269) then vardeclaration(fsys);
if fip=progp then
begin
if iop[true]<>nil then
begin argv[1].ad:=posaddr(holeb,textptr,false);
iop[true]^.vpos.ad:=argv[1].ad
end;
if iop[false]<>nil then
begin argv[0].ad:=posaddr(holeb,textptr,false);
iop[false]^.vpos.ad:=argv[0].ad
end;
genhol; genpnam(ps_exp,fip);
end; {externals are also extern for the main body}
fip^.pfpos.ad:=negaddr(fip^.idtype); {function result area}
while find2([procsy,funcsy],fsys,+0270) do pfdeclaration(fsys);
if forwcount<>0 then error(+0271); {forw proc not specified}
nextif(beginsy,+0272);
body(fsys+[casesy,endsy],fip);
nextif(endsy,+0273);
end end;
{===================================================================}
procedure programme(fsys:sos);
var stdin,stdout:boolean; p:ip;
begin
nextif(progsy,+0274); nextif(ident,+0275);
if find3(lparent,fsys+[semicolon],+0276) then
begin
repeat
if sy<>ident then error(+0277) else
begin stdin:=id='input '; stdout:=id='output ';
if stdin or stdout then
begin p:=newip(vars,id,textptr,nil);
enterid(p); iop[stdout]:=p;
end
else
if argc<maxargc then
begin
argc:=argc+1; argv[argc].name:=id; argv[argc].ad:=-1
end;
insym
end
until endofloop(fsys+[rparent,semicolon],[ident],comma,+0278); {+0279}
if argc>maxargc then
begin error(+0280); argc:=maxargc end;
nextif(rparent,+0281);
end;
nextif(semicolon,+0282);
block(fsys,progp);
if opt['l']<>off then
begin gencst(ps_mes,ms_src); argcst(srcorig); argend end;
eofexpected:=true; nextif(period,+0283);
end;
procedure compile;
var lsys:sos;
begin lsys:=[progsy,labelsy..withsy];
repeat eofexpected:=false;
main:=find2([progsy,labelsy,beginsy..withsy],lsys,+0284);
if main then programme(lsys) else
begin
if find3(constsy,lsys,+0285) then constdefinition(lsys);
if find3(typesy,lsys,+0286) then typedefinition(lsys);
if find3(varsy,lsys,+0287) then vardeclaration(lsys);
genhol;
while find2([procsy,funcsy],lsys,+0288) do pfdeclaration(lsys);
end;
error(+0289);
until false; { the only way out is the halt in nextln on eof }
end;
{===================================================================}
procedure init1;
var c:char;
begin
{reserved words}
rw[ 0]:='if '; rw[ 1]:='do '; rw[ 2]:='of ';
rw[ 3]:='to '; rw[ 4]:='in '; rw[ 5]:='or ';
rw[ 6]:='end '; rw[ 7]:='for '; rw[ 8]:='nil ';
rw[ 9]:='var '; rw[10]:='div '; rw[11]:='mod ';
rw[12]:='set '; rw[13]:='and '; rw[14]:='not ';
rw[15]:='then '; rw[16]:='else '; rw[17]:='with ';
rw[18]:='case '; rw[19]:='type '; rw[20]:='goto ';
rw[21]:='file '; rw[22]:='begin '; rw[23]:='until ';
rw[24]:='while '; rw[25]:='array '; rw[26]:='const ';
rw[27]:='label '; rw[28]:='repeat '; rw[29]:='record ';
rw[30]:='downto '; rw[31]:='packed '; rw[32]:='program ';
rw[33]:='function'; rw[34]:='procedur';
{corresponding symbols}
rsy[ 0]:=ifsy; rsy[ 1]:=dosy; rsy[ 2]:=ofsy;
rsy[ 3]:=tosy; rsy[ 4]:=insy; rsy[ 5]:=orsy;
rsy[ 6]:=endsy; rsy[ 7]:=forsy; rsy[ 8]:=nilcst;
rsy[ 9]:=varsy; rsy[10]:=divsy; rsy[11]:=modsy;
rsy[12]:=setsy; rsy[13]:=andsy; rsy[14]:=notsy;
rsy[15]:=thensy; rsy[16]:=elsesy; rsy[17]:=withsy;
rsy[18]:=casesy; rsy[19]:=typesy; rsy[20]:=gotosy;
rsy[21]:=filesy; rsy[22]:=beginsy; rsy[23]:=untilsy;
rsy[24]:=whilesy; rsy[25]:=arraysy; rsy[26]:=constsy;
rsy[27]:=labelsy; rsy[28]:=repeatsy; rsy[29]:=recordsy;
rsy[30]:=downtosy; rsy[31]:=packedsy; rsy[32]:=progsy;
rsy[33]:=funcsy; rsy[34]:=procsy;
{indices into rw to find reserved words fast}
frw[0]:= 0; frw[1]:= 0; frw[2]:= 6; frw[3]:=15; frw[4]:=22;
frw[5]:=28; frw[6]:=32; frw[7]:=33; frw[8]:=35;
{char types}
for c:=chr(0) to chr(maxcharord) do cs[c]:=others;
for c:='0' to '9' do cs[c]:=digit;
for c:='A' to 'Z' do cs[c]:=upper;
for c:='a' to 'z' do cs[c]:=lower;
cs[chr(ascnl)]:=layout;
cs[chr(ascvt)]:=layout;
cs[chr(ascff)]:=layout;
cs[chr(asccr)]:=layout;
{characters with corresponding chartype in ASCII order}
cs[chr(ascht)]:=tabch;
cs[' ']:=layout; cs['"']:=dquotech; cs['''']:=quotech;
cs['(']:=lparentch; cs[')']:=rparentch; cs['*']:=star;
cs['+']:=plusch; cs[',']:=commach; cs['-']:=minch;
cs['.']:=periodch; cs['/']:=slash; cs[':']:=colonch;
cs[';']:=semich; cs['<']:=lessch; cs['=']:=equal;
cs['>']:=greaterch; cs['[']:=lbrackch; cs[']']:=rbrackch;
cs['^']:=arrowch; cs['{']:=lbracech;
{single character symbols in chartype order}
csy[rparentch]:=rparent; csy[lbrackch]:=lbrack;
csy[rbrackch]:=rbrack; csy[commach]:=comma;
csy[semich]:=semicolon; csy[arrowch]:=arrow;
csy[plusch]:=plussy; csy[minch]:=minsy;
csy[slash]:=slashsy; csy[star]:=starsy;
csy[equal]:=eqsy;
{pascal library mnemonics}
lmn[ELN ]:='_eln'; lmn[EFL ]:='_efl'; lmn[CLS ]:='_cls';
lmn[WDW ]:='_wdw';
lmn[OPN ]:='_opn'; lmn[GETX]:='_get'; lmn[RDI ]:='_rdi';
lmn[RDC ]:='_rdc'; lmn[RDR ]:='_rdr'; lmn[RDL ]:='_rdl';
lmn[RLN ]:='_rln';
lmn[CRE ]:='_cre'; lmn[PUTX]:='_put'; lmn[WRI ]:='_wri';
lmn[WSI ]:='_wsi'; lmn[WRC ]:='_wrc'; lmn[WSC ]:='_wsc';
lmn[WRS ]:='_wrs'; lmn[WSS ]:='_wss'; lmn[WRB ]:='_wrb';
lmn[WSB ]:='_wsb'; lmn[WRR ]:='_wrr'; lmn[WSR ]:='_wsr';
lmn[WRL ]:='_wrl'; lmn[WSL ]:='_wsl';
lmn[WRF ]:='_wrf'; lmn[WRZ ]:='_wrz'; lmn[WSZ ]:='_wsz';
lmn[WLN ]:='_wln'; lmn[PAG ]:='_pag';
lmn[ABR ]:='_abr'; lmn[RND ]:='_rnd'; lmn[SINX]:='_sin';
lmn[COSX]:='_cos'; lmn[EXPX]:='_exp'; lmn[SQT ]:='_sqt';
lmn[LOG ]:='_log'; lmn[ATN ]:='_atn'; lmn[ABI ]:='_abi';
lmn[ABL ]:='_abl';
lmn[BCP ]:='_bcp'; lmn[BTS ]:='_bts'; lmn[NEWX]:='_new';
lmn[SAV ]:='_sav'; lmn[RST ]:='_rst'; lmn[INI ]:='_ini';
lmn[HLT ]:='_hlt'; lmn[ASS ]:='_ass'; lmn[GTO ]:='_gto';
lmn[PAC ]:='_pac'; lmn[UNP ]:='_unp'; lmn[DIS ]:='_dis';
lmn[ASZ ]:='_asz'; lmn[MDI ]:='_mdi'; lmn[MDL ]:='_mdl';
{scalar variables}
b.nextbp:=nil;
b.reglb:=0;
b.minlb:=0;
b.ilbno:=0;
b.forwcount:=0;
b.lchain:=nil;
srcchno:=0;
srclino:=1;
srcorig:=1;
lino:=0;
dlbno:=0;
holeb:=0;
argc:=1;
lastpfno:=0;
giveline:=true;
including:=false;
eofexpected:=false;
intypedec:=false;
fltused:=false;
seconddot:=false;
iop[false]:=nil;
iop[true]:=nil;
argv[0].ad:=-1;
argv[1].ad:=-1;
#ifdef NO_EXC
ohead := nil;
bcnt := 0;
mhead := nil;
#endif NO_EXC
end;
procedure init2;
var p:ip; k:idclass; j:standpf;
pfn:array[standpf] of idarr;
begin
{initialize the first name space}
new(top,blck); top^.occur:=blck; top^.nlink:=nil; top^.fname:=nil;
level:=0;
{undefined identifier pointers used by searchid}
for k:=types to func do
undefip[k]:=newip(k,spaces,nil,nil);
{names of standard procedures/functions}
pfn[pread ]:='read '; pfn[preadln ]:='readln ';
pfn[pwrite ]:='write '; pfn[pwriteln ]:='writeln ';
pfn[pput ]:='put '; pfn[pget ]:='get ';
pfn[ppage ]:='page '; pfn[preset ]:='reset ';
pfn[prewrite ]:='rewrite '; pfn[pnew ]:='new ';
pfn[pdispose ]:='dispose '; pfn[ppack ]:='pack ';
pfn[punpack ]:='unpack '; pfn[pmark ]:='mark ';
pfn[prelease ]:='release '; pfn[phalt ]:='halt ';
pfn[feof ]:='eof '; pfn[feoln ]:='eoln ';
pfn[fabs ]:='abs '; pfn[fsqr ]:='sqr ';
pfn[ford ]:='ord '; pfn[fchr ]:='chr ';
pfn[fpred ]:='pred '; pfn[fsucc ]:='succ ';
pfn[fodd ]:='odd '; pfn[ftrunc ]:='trunc ';
pfn[fround ]:='round '; pfn[fsin ]:='sin ';
pfn[fcos ]:='cos '; pfn[fexp ]:='exp ';
pfn[fsqt ]:='sqrt '; pfn[flog ]:='ln ';
pfn[fatn ]:='arctan ';
{standard procedure/function identifiers}
for j:=pread to phalt do
begin new(p,proc,standard); p^.klass:=proc;
p^.name:=pfn[j]; p^.pfkind:=standard; p^.key:=j; enterid(p);
end;
for j:=feof to fatn do
begin new(p,func,standard); p^.klass:=func; p^.idtype:=nil;
p^.name:=pfn[j]; p^.pfkind:=standard; p^.key:=j; enterid(p);
end;
{program identifier}
progp:=newip(proc,'m_a_i_n ',nil,nil);
end;
procedure init3;
var n:np; p,q:ip; i:integer; c:char;
#if EM_WSIZE == 2
is:packed array[1..imax] of char;
#endif
begin
for i:=0 to sz_last do readln(errors,sizes[i]);
if sz_int = 2 then maxintstring := max2bytes
else maxintstring := max4bytes;
if sz_long = 2 then maxlongstring := max2bytes
else maxlongstring := max4bytes;
gencst(ps_mes,ms_emx); argcst(sz_word); argcst(sz_addr); argend;
ix:=1;
while not eoln(errors) do
begin read(errors,c);
if ix<smax then begin strbuf[ix]:=c; ix:=ix+1 end
end;
readln(errors); strbuf[ix]:=chr(0);
for i:=1 to fnmax do
if i<ix then source[i]:=strbuf[i] else source[i]:=' ';
fildlb:=romstr(sp_scon,0);
{standard type pointers}
intptr :=newsp(scalar,sz_int);
realptr:=newsp(scalar,sz_real);
longptr:=newsp(scalar,sz_long);
charptr:=newsp(scalar,sz_char);
boolptr:=newsp(scalar,sz_bool);
nilptr :=newsp(pointer,sz_addr);
zeroptr:=newsp(pointer,sz_addr);
procptr:=newsp(records,sz_proc);
nullset:=newsp(power,sz_word); nullset^.elset:=nil;
textptr:=newsp(files,sz_head+sz_buff); textptr^.filtype:=charptr;
{standard type names}
enterid(newip(types,'integer ',intptr,nil));
enterid(newip(types,'real ',realptr,nil));
enterid(newip(types,'char ',charptr,nil));
enterid(newip(types,'boolean ',boolptr,nil));
enterid(newip(types,'text ',textptr,nil));
{standard constant names}
q:=nil; p:=newip(konst,'false ',boolptr,q); enterid(p);
q:=p; p:=newip(konst,'true ',boolptr,q); p^.value:=1; enterid(p);
boolptr^.fconst:=p;
{maxint of the target machine}
p:=newip(konst,'maxint ',intptr,nil);
if sz_int = 2 then p^.value:=MI2
else
#if EM_WSIZE == 4
p^.value := MI;
#else
{EM_WSIZE = 2, sz_int = 4}
begin p^.idtype:=longptr; ix:=imax; is:=max4bytes;
for i:=1 to ix do strbuf[i]:=is[i];
p^.value:=romstr(sp_icon,sz_int);
end;
#endif
enterid(p);
p:=newip(konst,spaces,charptr,nil); p^.value:=maxcharord;
charptr^.fconst:=p;
{new name space for user externals}
new(n,blck); n^.occur:=blck; n^.nlink:=top; n^.fname:=nil; top:=n;
{options}
for c:='a' to 'z' do begin opt[c]:=0; forceopt[c]:=false end;
opt['a']:=on;
opt['i']:=NB1*sz_iset;
opt['l']:=on;
opt['o']:=on;
opt['r']:=on;
sopt:=off;
end;
procedure init4;
begin
copt:=opt['c'];
dopt:=opt['d']; if EM_WSIZE < sz_int then dopt:=on;
iopt:=opt['i'];
sopt:=opt['s'];
if sopt<>off then begin copt:=off; dopt:=off end
else if opt['u']<>off then cs['_']:=lower;
if copt<>off then enterid(newip(types,'string ',zeroptr,nil));
if dopt<>off then enterid(newip(types,'long ',longptr,nil));
if opt['o']=off then begin gencst(ps_mes,ms_opt); argend end;
if dopt<>off then fltused:=true; {temporary kludge}
end;
begin {main body of pcompiler}
init1; {initialize tables and scalars}
init2; {initialize heap objects}
rewrite(em); put2(sp_magic); reset(errors);
init3; {size dependent initialization}
while not eof(errors) do
begin options(false); readln(errors) end;
rewrite(errors);
if not eof(input) then
begin nextch; insym;
init4; {option dependent initialization}
compile
end;
#ifdef STANDARD
9999: ;
#endif
end. {pcompiler}