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