external;

{
	Stanprocs.p (of PCQ Pascal)
	Copyright (c) 1989 Patrick Quaid

	This routine implements the various standard procedures,
hence the name.
}

const
{$I "pasconst.i"}

type
{$I "pastype.i"}

var
{$I "pasvar.i"}

	procedure nextsymbol;
	    forward;
	function match(s : integer): boolean;
	    forward;
	procedure error(s : string);
	    forward;
	function expression(): integer;
	    forward;
	function conexpr(var t : integer): integer;
	    forward;
	function typecmp(t1, t2 : integer): boolean;
	    forward;
	function typecheck(t1, t2 : integer): boolean;
	    forward;
	function loadaddress() : integer;
	    forward;
	procedure mismatch;
	    forward;
	procedure needleftparent;
	    forward;
	procedure needrightparent;
	    forward;
	function findid(s : string) : integer;
	    forward;
	procedure savestack(t : integer);
	    forward;
	procedure saveval(v : integer);
	    forward;
	procedure ns;
	    forward;
	function loadvar(v : integer) : integer;
	    forward;
	procedure promotetype(var f : integer; o, r : integer);
	    forward;
	function numbertype(t : integer): boolean;
	    forward;

procedure callwrite(vartype : integer);

{
	This routine calls the appropriate library routine to write
vartype to a text file.
}

var
    elementtype	: integer;
begin
    if numbertype(vartype) then begin
	promotetype(vartype, inttype, 0);
	writeln(output, "\tjsr\t_p%writeint");
    end else if typecmp(vartype, chartype) then
	writeln(output, "\tjsr\t_p%writechar")
    else if typecmp(vartype, booltype) then
	writeln(output, "\tjsr\t_p%writebool")
    else if idents[vartype].offset = varray then begin
	elementtype := idents[vartype].vtype;
	if typecmp(elementtype, chartype) then begin
	    writeln(output, "\tmove.l\t#",
		idents[vartype].upper - idents[vartype].lower + 1, ',d3');
	    writeln(output, "\tjsr\t_p%writecharray");
	end else
	    error("can only write arrays of char");
    end else if typecmp(vartype, stringtype) then
	writeln(output, "\tjsr\t_p%writestring")
    else
	error("can't write that type to text file");
end;

procedure filewrite(vartype : integer);

{
	This routine writes a variable to a 'file of that
variable'.
}

begin
    writeln(output, "\tmove.l\t#", idents[vartype].size, ',d3');
    writeln(output, "\tjsr\t_p%writearb");
end;

procedure dowrite(varindex : integer);

{
	This routine handles all aspects of the write and writeln
statements.
}

var
    filetype	: integer; { file type if there is one }
    exprtype	: integer; { current element type }
    pushed	: boolean; { have pushed the file handle on stack }
    width	: integer; { constant field width }
    widtype     : integer; { type of the above }
begin
    if match(leftparent1) then begin
	filetype := expression();
	pushed := true;
	if idents[filetype].offset = vfile then begin
	    writeln(output, "\tmove.l\td0,a0");
	    writeln(output, "\tmove.l\t(a0),d0");
	    writeln(output, "\tmove.l\td0,-(sp)");
	end else begin
	    writeln(output, "\tmove.l\t_stdout,-(sp)");
	    if match(colon1) then begin
		width := conexpr(widtype);
		if not typecheck(inttype, widtype) then
		    error("Expecting integer value.");
		writeln(output, "\tmove.w\t#", width, ',-(sp)');
	    end else
		writeln(output, "\tmove.w\t#1,-(sp)");
	    callwrite(filetype);
	    writeln(output, "\taddq.l\t#2,sp");
	    filetype := texttype;
	end;
	while not match(rightparent1) do begin
	    if not match(comma1) then
		error("expecting , or )");
	    exprtype := expression();
	    if typecmp(filetype, texttype) then begin
		if match(colon1) then begin
		    width := conexpr(widtype);
		    if not typecheck(inttype, widtype) then
			error("Expecting integer value.");
		    writeln(output, "\tmove.w\t#", width, ',-(sp)');
		end else
		    writeln(output, "\tmove.w\t#1,-(sp)");
		callwrite(exprtype);
		writeln(output, "\taddq.l\t#2,sp");
	    end else begin
		if typecmp(idents[filetype].vtype, exprtype) then
		    filewrite(exprtype)
		else
		    mismatch;
	    end;
	end;
    end else begin
	filetype := texttype;
	pushed := false;
	if idents[varindex].offset = 1 then
	    error("'write' requires arguments.");
    end;
    if idents[varindex].offset = 2 then begin
	if filetype = texttype then begin
	    if pushed then
		writeln(output, "\tjsr\t_p%writeln")
	    else begin
		writeln(output, "\tmove.l\t_stdout,-(sp)");
		writeln(output, "\tjsr\t_p%writeln");
		writeln(output, "\taddq.l\t#4,sp");
	    end;
	end else
	   error("No ...ln for non-text files");
    end;
    if pushed then
	writeln(output, "\taddq.l\t#4,sp");
end;

procedure callread(vartype : integer);

{
	This routine calls the appropriate library routines to read
the vartype from a text file.
}

begin
    if typecmp(vartype, chartype) then
	writeln(output, "\tjsr\t_p%readchar")
    else if typecmp(vartype, inttype) then begin
	writeln(output, "\tjsr\t_p%readint");
	writeln(output, "\tmove.l\td0,(a0)");
    end else if typecmp(vartype, shorttype) then begin
	writeln(output, "\tjsr\t_p%readint");
	writeln(output, "\tmove.w\td0,(a0)");
    end else if idents[vartype].offset = varray then begin
	if typecmp(idents[vartype].vtype, chartype) then begin
	    writeln(output, "\tmove.l\t#",
		idents[vartype].upper - idents[vartype].lower + 1, ',d3');
	    writeln(output, "\tjsr\t_p%readcharray");
	end else
	    error("can only read character arrays");
    end else if typecmp(vartype, stringtype) then
	writeln(output, "\tjsr\t_p%readstring");
    else
	error("cannot read that type from a text file");
end;

procedure doread(varindex : integer);

{
	This handles the read statement.  Note that read(f, var) from a
non-text file really does end up being var := f^; get(f).  Same
goes for text files, but it's all handled within the library.
	Note the difference between this and dowrite(),
specifically the use of expression() up there and loadaddress()
here.
}

var
    filetype	: integer;
    vartype	: integer;
    pushed	: boolean;
begin
    if match(leftparent1) then begin
	filetype := loadaddress();
	pushed := true;
	if idents[filetype].offset = vfile then
	    writeln(output, "\tmove.l\ta0,-(sp)");
	else begin
	    writeln(output, "\tmove.l\t#0,-(sp)");
	    callread(filetype);
	    filetype := texttype;
	end;
	while not match(rightparent1) do begin
	    if not match(comma1) then
		error("expecting , or )");
	    vartype := loadaddress();
	    if typecmp(filetype, texttype) then
		callread(vartype)
	    else begin
		if typecmp(idents[filetype].vtype, vartype) then
		    writeln(output, "\tjsr\t_p%readarb")
		else
		    mismatch;
	    end;
	end;
    end else begin
	filetype := texttype;
	pushed := false;
	if idents[varindex].offset = 3 then
	    error("'read' requires arguments.");
    end;
    if idents[varindex].offset = 4 then begin
	if typecmp(filetype, texttype) then begin
	    if pushed then
		writeln(output, "\tjsr\t_p%readln")
	    else begin
		writeln(output, "\tmove.l\t#0,-(sp)");
		writeln(output, "\tjsr\t_p%readln");
		writeln(output, "\taddq.l\t#4,sp");
	    end;
	end else
	   error("No ...ln for non-text files");
    end;
    if pushed then
	writeln(output, "\taddq.l\t#4,sp");
end;

procedure donew;

{
	This just handles allocation of memory.
}

var
    varindex	: integer;
    vartype	: integer;
    varsize	: integer;
    stackvar	: integer;
begin
    needleftparent;
    varindex := findid(symtext);
    if varindex <> 0 then begin
	stackvar := loadvar(varindex);
	if stackvar <> 0 then begin
	    writeln(output, "\tmove.l\td0,-(sp)");
	    vartype := stackvar;
	end else
	    vartype := idents[varindex].vtype;
	if idents[vartype].offset <> vpointer then
	    error("expecting a pointer type");
	varsize := idents[vartype].vtype;
	varsize := idents[varsize].size;
	writeln(output, "\tmove.l\t#", varsize, ',d0');
	writeln(output, "\tjsr\t_p%new");
	if stackvar <> 0 then
	    savestack(vartype)
	else
	    saveval(varindex);
    end else
	error("Unknown identifier");
    needrightparent;
end;

procedure dodispose;

{
	This routine calls the library routine that disposes of
memory.
}

var
    exprtype	: integer;
begin
    needleftparent;
    exprtype := expression();
    if idents[exprtype].offset <> vpointer then
	error("Expecting a pointer type")
    else
	writeln(output, "\tjsr\t_p%dispose");
    needrightparent;
end;

procedure doclose;

{
	Closes a file.  The difference between this and a normal
DOS close is that this routine must un-link the file from the
program's open file list.
}

var
    exprtype	: integer;
begin
    needleftparent;
    exprtype := expression();
    if idents[exprtype].offset <> vfile then
	error("Expecting a file type")
    else
	writeln(output, "\tjsr\t_p%close");
    needrightparent;
end;

procedure doget;

{
	This implements get.  There is no analogous put(), since
the write statements never needed it.
}

var
    exprtype	: integer;
begin
    needleftparent;
    exprtype := expression();
    if idents[exprtype].offset <> vfile then
	error("Expecting a file type")
    else begin
	writeln(output, "\tmove.l\td0,a0");
	writeln(output, "\tjsr\t_p%readarbbuf");
    end;
    needrightparent;
end;

procedure doexit;

{
	Just calls the routine that allows the graceful shut-down
of the program.
}

var
    exprtype : integer;
begin
    needleftparent;
    exprtype := expression();
    if not typecheck(exprtype, inttype) then
	error("Expecting an integer argument.");
    writeln(output, "\tjsr\t_p%exit");
    needrightparent;
end;

procedure dotrap;

{
	This is just for debugging a program.  Use some trap, and
your debugger will stop at that statement.
}

var
    exprtype,
    trapnum   : integer;
begin
    needleftparent;
    trapnum := conexpr(exprtype);
    writeln(output, "\ttrap\t#", trapnum);
    needrightparent;
end;

procedure stdproc(varindex : integer);

{
	This routine sifts out the proper routine to call.
}

var
    exprtype	: integer;
    pushed	: boolean;
begin
    nextsymbol;
    pushed := false;
    if (idents[varindex].offset = 1) or
	    (idents[varindex].offset = 2) then
	dowrite(varindex)
    else if (idents[varindex].offset = 3) or
	    (idents[varindex].offset = 4) then
	doread(varindex)
    else if idents[varindex].offset = 5 then
	donew
    else if idents[varindex].offset = 6 then
	dodispose
    else if idents[varindex].offset = 7 then
	doclose
    else if idents[varindex].offset = 8 then
	doget
    else if idents[varindex].offset = 9 then
	doexit
    else if idents[varindex].offset = 10 then
	dotrap;
    ns;
end;

