DataMuseum.dk

Presents historical artifacts from the history of:

RC3500

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about RC3500

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download

⟦295a8a662⟧ TextFileVerbose

    Length: 141312 (0x22800)
    Types: TextFileVerbose
    Names: »pass3txt«

Derivation

└─⟦2c55ea56f⟧ Bits:30001844 SW-save af projekt 1000, Alarm-system
    └─⟦6b41451d2⟧ 
        └─⟦this⟧ »pass3txt« 

TextFileVerbose

program platonpass3 (
input,  (* used when reading pass3errors *)
output, (* used for testoutput *)
pass1file = 'pass1code', (* contains pass1-output *)
pass1labels = 'pass1labels' , (* contains labeldefs from pass1 *)
pass3file = 'pass3code' (* contains output from pass3 *)
);

label 999; (* exit from pass3 main program *)

const

version = '81.04.03';
versionpass3 = 205;  (* 2.05 *)
versionpass1 = 300;  (* pass1 version must be 3.xx *)
own_passno = 3;
(*$T+*)
{
nilname = 0; max_names = 600;
niltype = 601; max_types = 800;
nillabel = 950; max_labels = 999;
nilopand = 1000; max_opands = 1500;
nilstring = 2000; max_strings = 2010;
max_levels = 20;
}

nilname = 0;           (* dummy index in 'names' *)
niltype = 0;           (* dummy index in 'types' *)
nillabel = 0;          (* dummy index in 'labels' *)
nilopand = 0;          (* dummy index in 'opands' *)
nilstring = 0;         (* dummy index in 'strings' *)

max_names = 2000;      (* max index in 'names' *)
max_types = 450;       (* max index in 'types' *)
max_labels = 20;       (* max index in 'labels' *)
max_opands = 1500;     (* max index in 'opands' *)
max_levels = 20;       (* max index in 'namestack' *)
max_strings = 1000;     (* max index in 'strings' *)
max_pushnames = 100;   (* max index in 'pushnamestack' *)
max_nested_calls = 20; (* max index in 'calls' *)

lines_pr_page = 45;    (* used for nice testoutput *)

max_spix = 4095;       (* max value of 'spix' *)
max_labelscopenumber = 100000; (* max value of 'labelscope' *)

lowestlevel = 1;       (* first used namelevel *)
process_block = 0;     (* blocknumber for process variables *)
maxstring = 12;        (* number of chars per string portion *)

compilertest = true;   (* determines amount of 'superflous' testing *)

type

nameptr = nilname .. max_names;
typeptr = niltype .. max_types;
labelptr = nillabel .. max_labels;
opandptr = nilopand .. max_opands;
stringptr = nilstring .. max_strings;
str_index = 1 .. maxstring;

spixrange = 0 .. max_spix;
labelrange = 0 .. max_labelscopenumber;
levelrange = 0 .. max_levels;
pushrange = 0 .. max_pushnames;
callrange = 0 .. max_nested_calls;

(* define codes in pass1file/pass1labels *)
codes =
(
cnocode,
cerror, cerrortext, cerrorno,
coption,
cnewline,
ceom,

cendstandards,
cstandardname,
(* parameters to 'standardname' *)
canonymous, (* first std name *)
cboolean,
cchar,
cshadow,
creference,
csemaphore,
cson,
cprocessrec,
cexception,
cabs,
csucc,
cpred,
cchr,
cord,       (* last std name *)

cinclude, cendinclude,
ccontext, cerrcontext, cendcontext,

cendprefix,

cbeginlevel, cendlevel,
cdeclaration, cdeclare, cdeclarelist,

cexternal, cforward,

cendformallist, cendformal,
cendprocdecl, cendfuncdecl, cendtypedecl,
cendconstantdecl,

cnoinit, cinit, cendvardecl,

cexport,
cexportvalue, cexportsize, cexportdisp, cexportoffset, cexportaddr,

cenddeclarations,

cstartlabelscope, cendlabelscope,

cbegincode, ccodeline, cendcode,

cendblock,

ctypeid,
cendtype,
cwithout_data,

cnewtype,
cendscalar, cendsubrange, cendarray, cendfield, cendrecord, cendset,
cendpool, cendpointer, cendreadonly,
cendexttype,
cpacked,

cbegin, cend,

clabeldef,

ccasestat, ccaseexpr, ccaselabel, ccaselabelrange, ccaselist,
ccaseelement, cotherwise, cendcase,

cforstat, cforvar, cup, cdown, cendfor,

cifstat, cifexpr, celse, cendif,

crepeatstat, cuntil, cendrepeat,

cwhilestat, cwhileexpr, cendwhile,

clockstat, cwithstat, cwithvar,
cnolocaldecl, cendlocaldeclare,
cnolocaltype,
cwithcomma, cdo, cendwith,

cgotostat, cendgoto,

cchannelstat, cchanvar, cendchannel,

cendassign, cendexchange,

ccallprocedure,

(* expression codes *)
cendexpression,
ceq, cne, clt, cle, cgt, cge, cin,
cuplus, cuminus, cplus, cminus, cor, cxor,
cstar, cslash, cdiv, cmod, cand,
cnot,
cendvariable,
cindex, (* <<< used internally by pass3 *)

(* operand codes *)
csetlist, cs_element, cm_element, cendsetlist,
cliteral,
cid,
cskipparam,
cfield,
cuparrow,
crangefirst, (* <<< used internally by pass3 *)
crange,      (* <<< used internally by pass3 *)

cbeginactual, cactualparam, cdoubleparam, cendactual,
(* the following few lines are all for internal pass3-use *)
cfunctrailer, cfunctemp, cerrorarg, cargument,
cfcall, cendproc,
ccallprocess, cprocessargument,
cendprocessparam,
cstrucrecord, cstrucarray, cendstruc,


(* namekinds i.e. parameters for 'declaration' *)
cprocess, cprocedure, cfunction, ctype,
cscalarelem, crecfield, cconstant, cvar,
cvarp, cvaluep, cfuncval (* <<< used internally by pass3 *),
clabel,
cprefix,
cundeclared (* <<< used internally by pass3 *),


(* typekinds *)
calias (* <<< used internally by pass3 *),
(* typekinds i.e. parameters for 'newtype' *)
cerrorkind (* <<< used internally by pass3 *),
cscalar, csubrange, carray, crecord, cset, cpool,
cpointer, creadonly,
cexttype,
(* standard types *)
cinteger, creal, cniltype,
ctext, (* used for string-literals *)

clastcode (* used internally by pass1/pass3 *)

);

codeindex = 0 .. 200; (* number of codes *)

\f





stdname_range = canonymous .. cord;

(* define codes in pass3file *)
pass3codes =
(
xfirstcode,
xerror, xerrortext, xerrorno,
xoption,
xnewline,
xeom,

xendprelude,

xexternal, xforward, xsecprocid, xsecfuncid,

xprocessid, xprocid, xfuncid,
xconstid,

xvarid, xinitconst, xvarlist,

xlabelid,

xundeclid,

xblock, xexception, xinitblock, xendblock,

xbegincode, xcodeline, xendcode,

(* std types *)
xinteger, xreal, xniltype, xerrortype, xstringtype,
xboolean, xshadow, xreference, xsemaphore,
xchar,

(* std functions *)
xsucc, xpred, xord, xchr,

xtype,
xredeftype,
xscalarid, xscalardef,
xgetexpr, xsubdef,
xarraydef, xpackedarraydef,
xpackedrecord, xrecord, xfieldid, xfielddef, xrecdef,
xsetdef, xpooldef, xpointerdef, xfrozendef,
xtypedef,

xexportvalue, xexportsize, xexportdispl, xexportoffset, xexportaddr,
xendexport,

xparamid, xvalueparam, xvarparam,

xname,

xprefix,

xlabel,

xcasestat, xcase, xcaselabel, xcaserange, xotherwise, xendcase,
xendcaselist, xendcasestat,

xforstat, xfor, xup, xdown, xdo, xendfor,

xifstat, xifexpr, xelse, xif,

xrepeat, xuntil, xendrepeat,

xwhile, xwhileexpr, xendwhile,

xwith, xwithvar, xendwith,
xlockstat, xlockvar, xlock, xendlock,

xgoto,

xchannel, xchanvar, xendchannel,

xassignstat, xbecomes, xassign,
xexchangestat, xexchange,

xproccall, xendproc,
xfunccall, xfcall, xendfunccall,
xactual, xendactual,

xprocessparam,
xvarpointer, xtempointer, xarglistsize,

(* expressions *)
xexpr,
xgetvalue,
xne, xeq, xle, xge, xlt, xgt, xin,
xneg, xadd, xsub, xor,
xxor,
xdiv, xmul, xmod, xand,
xnot,

(* conversion operators *)
xrange,

xset, xinclude, xsetexpr, xincluderange, xendset,

xliteral,
xvar,
xstrucconst, xtimes, xnull, xstruc, xendstruc,
xindexexpr, xindex,
xarrow, xfield,

xlastcode

);

pass3codeindex = 0 .. 200; (* number of pass3codes *)


literalkinds = (lit_dummy, lit_integer, lit_string_or_empty, lit_char);

\f



(* end pass 3 codes *)

(* include pass3errors from stdfile *)
pass3errors =
(
noerror,
undeclared,            (* identifier not declared *)
inconsistent_use,      (* identifier used before declaration *)
double_declaration,    (* identifier already declared at this level *)
label_not_declared,    (* label-identifier not declared at all *)
not_label_name,        (* other identifier used as label-name *)
multiple_defined_label, (* label defined several times at this level *)
label_not_locally_declared,  (* label-identifier declared at surrounding level *)
erroneous_label,       (* use of a multiple defined label *)
label_used_from_inner, (* a label-ident has been used in inner routine *)
label_used_outside_scope,    (* goto leading into control-structure *)
label_defined_outside_lock_or_channel, (* goto out of lock- or channel statements *)
label_undefined,       (* label is not defined *) 
not_typename,          (* identifier is not a type-identifier *)
recursive_use_of_type, (* error in record or array etc. *)
recursive_constant_use,(* constant is used its own definition-expression *)
illegal_pool_type,     (* pool ... of <illegal type> *)
pool_cardinality_must_be_integer, (* illegal size'ing of pool type *)
subrange_elems_must_be_enumeraton, (* illegal limit-types in subrange def *)
type_may_only_be_used_at_process_level,  (* nb: semaphore, pool *)
process_only_allowed_at_processlevel, (* processes inside functions/procedures forbidden *)
illegal_formal_type,   (* formal type may not be used in this context *)
illegal_function_type,  (* functiontype may not contain:semaphore etc *)
illegal_scope_of_type_component, (* only variables of surrounding scope allowed *)
paramlist_changed_since_forwarddecl, (* 'new' paramlist may be empty or exact the same *)
forward_not_solved,    (* forward-declared routine not followed with the real body *)
funcval_not_used,      (* function-value has not been defined at all *)
type_has_pointers,     (* locktype contains pointer-types *)
type_has_systemtypes,  (* locktype contains semaphore, reference, shadow, pool *)
opands_incompatible,   (* opands not of same typename *)
for_incompatible,      (* for-variable/startvalue/endvalue not of compatible types *)
case_incompatible,     (* case-expression/caselabels not of compatible types *)
if_type,               (* if-expression must be boolean type *)
repeat_type,           (* until-expression must be boolean type *)
while_type,            (* while-expression must be boolean type *)
with_type,             (* with-variable must be a record *)
lock_type,             (* lock-variable must be reference type *)
channel_type,          (* channel-variable must be reference type *)
not_index_type,        (* type of operand must be enumeration-type *)
not_variable,          (* operand cannot be used as variable *)
field_must_follow_recordtype, (* <variable> in front of <.> is not a record *)
name_not_fieldname,    (* <name> after <.> is not a fieldname of <variable> *)
must_be_pointertype_before_uparrow, (* <variable> in front of <uparrow> is not a pointer *)
mixed_types_in_setlist, (* elements in set-value may not be of mixed types *)
relation_error,        (* illegal mixture of types in relation *)
arithmetic_error,      (* illegal mixture of types in term or factor *)
monadic_error,         (* illegal type for monadic operator *)
real_not_implemented,  (* real occuring in expression *)
real_division_not_implemented,  (* real-division of integers not impl *)
illegal_in_expr,       (* illegal operand kind in expression *)
too_few_parameters,    (* too few actual parameters to routinecall (or strucrecord *)
too_many_actual_params, (* error in routinecall *)
too_many_values_in_record_structure, (* error in structured-record constant *)
type_must_be_record_or_array, (* typename in front of arglist must be... *)
double_param_only_in_struc_const, (* the '***' operator must only occur in structured-array constant *)
subscript_after_nonarray, (* name in front of arglist is not of array-type *)
incompatible_index,     (* index-expression does'nt match array-declaration *)
assign_incompatible,    (* incompatible types in assignment *)
exchange_incompatible,  (* incompatible types in exchange *)
not_procedure_call,     (* the statement is not a procedure-call *)
variable_may_not_be_packed,  (* for-variable or actual var-param is packed *)
not_assignable,          (* operand may not be assigned: sem, pool, ref, shadow, frozen *)
not_exchangeable,        (* operand may not be exchanged: sem, pool, frozen *)
exchange_type,           (* type must be: reference or shadow *)
illegal_var_param_substitution,   (* formal and actual type must match exactly *)
illegal_value_param_substitution, (* actual and formal types are not compatible *)
actual_may_not_be_frozen, (* formal is not frozen, therefor... *)
skipparam_only_in_struc_const,  (* the '?' may only occur in structured constants *)
struc_arr_incompatible,  (* incomp. types in structured array-constant *)
struc_rec_incompatible,  (* incomp. types in structured record-constant *)
var_init_incompatible,   (* incomp. types in var-initialization *)
repetition_type,         (* repeatiton must be integer *)
const_export_only,       (* value-export demands constant *)
var_export_only,         (* offset-export demands variable *)
const_or_var_export_only,(* size-,disp-,addr-export demands constant or variable *)
disp_export_must_have_field, (* disp-export demands 'fielding' *)
not_implemented_on_z80, (* xor, integer-and, and integer-or not implemented *)
lastpass3error);

(* define parameters to procedure 'stop' *)
stopcodes = (
too_many_names,
too_many_types,
too_many_strings,
too_many_labels,
too_many_opands,
too_many_levels,
too_many_pushnames,
too_many_nested_calls);

levelkinds = (
processlevel,
firstwith, nextwith,
firstlock, nextlock,
irrell_level);

(* define pass3 programming error-codes *)
comp_errorcodes =
(unknown_namekind,
unknown_argkind,
unknown_opcode,
unknown_paramkind,
unknown_routinekind,
unknown_spix,
unknown_stdtype_name,
unknown_typekind,
unknown_rectype,
inconsist_alloc,
wrong_version,
unstack_nontype);


(* operator modes *)
opmodes = (unary, binary);

(* components of a type *)
components = (t_simple, t_semaphore, t_pool, t_reference, t_shadow,
t_pointer, t_readonly);

content_set = set of components;

label_states = (outside_scope, inside_scope, hidden_by_lock_or_channel);

(*QQQ*)

namenode = packed record
nextname: nameptr      ; (* used to chain names of same level *)
spix    : spixrange    ; (* spelling index of identifier *)
namekind: codes        ; (* kind of identifier: var, const, function etc *)
typename: nameptr      ; (* points to the namenode of the type (unless namekind = 'ctype' *)
typ     : typeptr      ; (* only relevant if namekind = 'ctype' *)
params  : nameptr      ; (* routine etc: first formal parameter name *)
forward_decl: boolean  ; (* true, when after 'forward' *)
external_decl: boolean ; (* true, when after 'external' *)
func_assigned: boolean; (* true, when funcval is used *)
usedlevel: levelrange  ; (* nillevel, if never used, otherwise some namelevel *)
end;



typenode = packed record
typekind: codes        ; (* kind of type: subrange, array etc *)
emitted:  boolean      ; (* true, when type has been emitted *)
contents: content_set  ; (* describes the components of the type *)
packflag: boolean      ; (* true, if type is packed *)
element: nameptr       ; (* depends on 'typekind':
                         -    scalar: first scalarname
                         -    record: first field name
                         -    array : typename of elements
                         -    set   : typename of elements
                         -    pool  : typename of elements
                         -    pointer: typename of element
                         -    readonly: typename of element
                         -    alias : typename of alias type
                         *)
index:    nameptr      ; (* depends on 'typekind':
                         -    subrange: typename of limits
                         -    array   : typename of index
-    alias   : ( corresponding to the alias type )
                         *)
subexpr:  opandptr     ; (* depends on 'typekind':
                         -    subrange: pointer to 'both' expressions
                         -    pool:     pointer to cardinality-expr
                         *)
end;

(*QQQ*)

labelnode = packed record
prevlabel : labelptr   ; (* used to chain to other label def's *)
labelspix : spixrange  ; (* spelling index of label *)
labelname : nameptr    ; (* points to the label-declaration namenode *)
labelscope: labelrange ; (* scope number of label *)
hiddenscope: labelrange; (* scopenumber of lock-or-channel which hides this label *)
labelstate: label_states; (* used to determine 'visability' *)
multflag  : boolean    ; (* true, if multiple defined *)
used_from_inner_routines: boolean; (* true, if used from inner routines *)
end;



opandnode = packed record
opcode     : codes     ; (* kind of operand/operator *)
op_lineno  : integer   ; (* line number *)
left       : opandptr  ; (* left subtree *)
rigth      : opandptr  ; (* rigth subtree *)
next       : opandptr  ; (* opand to the left of reduced tree *)
op_name    : nameptr   ; (* name of operand, if 'id' *)
op_value   : stringptr ; (* relevant, if 'literal' *)
op_typename: nameptr   ; (* typename of reduced tree *)
op_packflag: boolean   ; (* true, if the opand is packed *)
end;



namestacknode = packed record
firstname : nameptr    ; (* points to first name of a level *)
lastname  : nameptr    ; (* points to last  name of a level *)
namelistkind: levelkinds; (* defines kind of list *)
withname  : nameptr    ; (* holds the name of the pseudo var *)
end;



stringnode = packed record
nextstr  : stringptr   ; (* pointer to next string portion *)
stringlgt: integer     ; (* length of remaining part *)
str: packed array [ str_index ] of char; (* contains the portion *)
end;



callnode = packed record
(* the record holds stacked values of global variables *)
callkind      : codes;      (* holds: 'actkind' *)
callformal    : nameptr;    (* holds: 'curformal' *)
callowner     : nameptr;    (* holds: 'arglist_owner' *)
call_afterfunc: boolean;    (* holds: 'after_funccall' *)
call_functemp : nameptr;     (* holds: 'functempvar' *)
call_countprocess: integer;  (* holds: 'count_processparam' *)
end;

var
cur_lineno: integer;   (* current line number from pass1file *)
lineno    : integer;   (* last read lineno from pass1 *)
cur_opandno: integer;  (* number of operands on current line *)
emit_lineno:integer;   (* current emitted lineno in pass3file *)
curname:    nameptr;   (* current name node *)
cur_routine:nameptr;   (* current routine *)
cur_typename: nameptr; (* namenode of current type *)
cur_typenode: typeptr; (* corresponding typenode *)
cur_blockno: integer;  (* lexicographical level *)
cur_labelscope: labelrange; (* updated by: begin/end label scope *)
cur_namekind: codes;   (* updated by 'declaration' (and 'newtype') *)
rectype: codes;        (* record type of current input-record *)
cur_dyn_var_level:integer; (* highest level allowed for use of variables *)
namelevel: levelrange; (* current name level *)
levelkind: levelkinds; (* kind of (future) namelevel *)
lastgloballevel: levelrange; (* identifies last environment level *)
processparamlevel: levelrange; (* used when searching identifiers *)
pushnameindex: pushrange; (* index in pushnamestack *)
callindex    : callrange; (* index in calls *)

(* the following variables are results from 'searchspix' etc *)
retrieved_level: levelrange; (* undef, if name was'nt found *)
retrieved_name : nameptr;    (* nilname, if name was'nt found *)

(* variables used when processing actual parameter lists *)
curformal     : nameptr; (* points into formal parameter list *)
argkind       : codes;   (* separates into: argument, index, struc etc *)
arglist_owner: nameptr; (* points to the routinename or arraytypename or typename *)
functempvar   : nameptr; (* points to temporary namenode *)
count_processparam: integer;  (* number of process-'calls' in arglist *)

testinput: boolean;    (* determines printing of input-records *)
testoutput: boolean;   (* determines printing of output-records *)
testing:   boolean;    (* = (testinput or testoutput) *)
testlinecnt: integer;  (* used for changing page during testprinting *)

freenames: nameptr;    (* list of free name node *)
freetypename: nameptr; (* list of free type nodes, linked via namenodes *)
freestrings: stringptr; (* list of free string nodes *)
freelabels: labelptr;  (* list of free label nodes *)
freeopands: opandptr;  (* list of free opand nodes *)

listfirst: nameptr;    (* first name in current declaration list *)
listlast:  nameptr;    (* last  name in current declaration list *)
listtop:   nameptr;    (* used when scanning the current decl list *)
lastlabel: labelptr;   (* last name in list of defined labels *)

endpass1:  boolean;    (* false, until 'eom'-record has been read *)
call_pass4: boolean;    (* true, until first error in pass3 *)
lambda_version: boolean; (* true, unless z80-version *)
after_funccall: boolean;  (* flag to determine 'endfunccall' *)
insert_in_front: boolean; (* true: names are inserted in front else in rear *)
before_assign: boolean; (* true: identifier cannot be function or process *)
afterdecl: boolean;    (* flag to determine <end-decl> in a routine *)
expecting_block: boolean; (* flag to determine <first decl> in a routine *)
var_initialization: boolean;  (* true, if initialization in var-decl *)
expr_release: boolean; (* true, if expression must be released after emit *)
const_decl: boolean;   (* true, when emitting expr in const declaration *)
retain_top: boolean;   (* true, if stacktop must be reused *)
start_of_protected: boolean;  (* true when starting channel or lock statements *)
expr_level: integer;   (* controls beginning of whole expression *)

dummy_typenode: typeptr; (* std type entry *)
error_typename: nameptr; (* std name entry *)
error_typenode: typeptr; (* std type entry *)

envir_spix: spixrange; (* working location ? *)
newspix    : spixrange; (* holds spix of fieldname *)

maxstdspix: integer;   (* holds max spix with special interrest *)
anonymous_spix: spixrange; (* initialized during startup *)
exception_spix: spixrange; (* initialized durring startup *)

boolean_typename: nameptr;
integer_typename: nameptr;
char_typename   : nameptr;
shadow_typename   : nameptr;
reference_typename: nameptr;
text_typename     : nameptr; (* internally used for text-literals *)
processrec_typename: nameptr;
son_typename    : nameptr;
nil_typename    : nameptr;

exception_procname: nameptr;
abs_procname    : nameptr;
succ_procname   : nameptr;
pred_procname   : nameptr;
ord_procname    : nameptr;

for_typename: nameptr;  (* holds typename of current for-variable *)
for_basetypename: nameptr; (* holds simplified typename of for-variable *)
case_typename: nameptr; (* holds typename of current case-expression *)
with_typename: nameptr; (* work-location: holds typename of with-variable *)

indexname: nameptr;     (* working location *)
elemname:  nameptr;     (* working location *)

paramkind: codes;       (* used when checking formal parameters *)
not_allowed: content_set; (* holds the illegal parameter contents *)

opand_procname: opandptr; (* holds 'literal'-operand, with routine name *)

for_expr: opandptr;     (* holds the end-value expression of a for-statement *)

stacktop: opandptr;     (* points to current top of opands *)
nexttop:  opandptr;     (* points to 'next' of current top-opand *)

(* the following variables are used when resolving opand-types *)
lefttypename:  nameptr;
leftbasekind:  codes;
leftelement:   nameptr;
rigthtypename: nameptr;
rigthbasekind: codes;
rigthelement:  nameptr;
exprtypename:  nameptr;
exprbasekind:  codes;
exprelement:   nameptr;

errorcode: pass3errors; (* working location *)

no_of_errors: integer;

cur_date: alfa;  (* string holding current date *)
cur_time: alfa;  (* string holding current time *)

names:   array [ nameptr ] of namenode;
types:   array [ typeptr ] of typenode;
labels:  array [ labelptr ] of labelnode;
opands:  array [ opandptr ] of opandnode;
namestack: array [ levelrange ] of namestacknode;
strings: array [ stringptr ] of stringnode;
pushnamestack: array [ pushrange ] of nameptr;
calls: array [ callrange ] of callnode;

convtable: array [ codeindex ] of codes;

stdspix_table: array [ stdname_range ] of integer;

error_bitmap: packed array [ noerror .. lastpass3error ] of boolean;

pass1file: file of integer;
pass1labels: file of integer;
pass3file: file of integer;

(*QQQ*)

(* procedures used for testoutput purposes *)

procedure changeline;
begin
testlinecnt := testlinecnt + 1;
if testlinecnt mod lines_pr_page = 1 then
begin
page (output);
writeln (output);
write (cur_date, '   ', cur_time, '      platon, pass3, version ', version);
end;
writeln (output);
end;



procedure continue_line;
begin
(* used for alligning testoutput on consequtive lines *)
changeline;
write (output, '   ');
end;

(*QQQ*)

(* procedures for reading pass1-code file *)

function passin : integer;
(* delivers the next pass1-code as integer value *)
var i: integer;
begin
read (pass1file, i);
passin := i;
if testinput then
write (output, ' ', i:1, '*');
end;



function password : codes;
(* delivers the next pass1-code as code value *)
var i: integer;
begin
read (pass1file, i);
password := convtable [ i ];
if testinput then
write (output, ' ', i:1, '>');
end;

(*QQQ*)

(* procedures for reading the pass1-label file *)

function labelin : integer;
(* delivers the next pass1-label-value as integer value *)
var i: integer;
begin
read (pass1labels, i);
labelin := i;
if testinput then
write (output, ' ', i:1, '+');
end;

(*QQQ*)

(* procedures for writing the pass3file *)

procedure emitin (i: integer);
(* outputs the value as integer value *)
begin
write (pass3file, i); 
if testoutput then
write (output, ' ', i:1, ' ');
end;



procedure emit (code: pass3codes);
(* outputs the codeword as integer value *)
var i: integer;
begin
i := ord (code);
write (pass3file, i);
if testoutput then
write (output, ' ', i:1, '<');
end;



procedure emitid (xcode: pass3codes; xnix: nameptr);
begin
emit (xcode);
emitin (ord (xnix) );
end;



procedure terminate;
var
err: pass3errors;
ch : char;
begin
(* terminate use of files *)

emit (xeom);
close (pass3file);

close (pass1file);
close (pass1labels);

(* if any errormessages then print the descriptions *)
if no_of_errors > 0 then
begin
(* at least one error, therefor prepare the errorlist *)
changeline;
write (output, 'error   description');
changeline;
open (input, 'pass3errors');
reset (input);
(* find the start of error-table *)
repeat
readln (input, ch);
until ch = '(';

for err := noerror to lastpass3error do
begin
if error_bitmap [ err ] then
begin
write (ord (err) : 4, ' =  ');
while not eoln (input) do
begin
read (input, ch); write (output, ch);
end;
changeline;
end;
readln (input);
end; (* scan when errors *)
end; (* if any errors *)

end;

(*QQQ*)

(* procedures for error-handling etc *)

procedure warning (error: pass3errors);
begin
if (no_of_errors = 0) or testing then changeline;
write (output, '*** pass ', own_passno:1,
' line ', cur_lineno:4, '.', cur_opandno:1,
', error ', ord (error):2);
changeline;
no_of_errors := no_of_errors + 1;
error_bitmap [ error ] := true;
end;



procedure error (err: pass3errors);
begin
call_pass4 := false;  (* avoid calling pass4 automaticly *)
emit (xerror);
emitin (own_passno);
emitin (ord (err));
emit (xerrorno);
emitin (cur_opandno);
warning (err);
error_bitmap [ err ] := true;
end;



procedure suppress_error (kind: codes; err: pass3errors);
(* the error-message will be suppressed, if already after error *)
(* the typename if stacktop will be set to error-type *)
begin
if kind <> cerrorkind then error (err);
with opands [ stacktop ] do
op_typename := error_typename;
end;



procedure break;
(* used when a call-trace is wanted *)
begin
terminate; (* terminate the files etc *)
writeln;
writeln ('***** intentionally error...');
emit (xeom); (* this will provoke an error, which will produce the trace *)
(* the program is terminated *)
end;



procedure stop (cause: stopcodes);
begin
writeln (output);
writeln ('*** error found in pass3, at sourceline: ', cur_lineno:1);

case cause of
too_many_names:
writeln ('max_names exceeded', max_names);
too_many_types:
writeln ('max_types exceeded', max_types);
too_many_strings:
writeln ('max_strings exceeded', max_strings);
too_many_labels:
writeln ('max_labels exceeded', max_labels);
too_many_opands:
writeln ('max_opands exceeded', max_opands);
too_many_levels:
writeln ('max_levels exceeded', max_levels);
too_many_pushnames:
writeln ('max_pushnames exceeded', max_pushnames);
too_many_nested_calls:
writeln ('max_nested_calls exceeded', max_nested_calls);
end
otherwise writeln ('??? stop-cause = ', ord (cause):1);

call_pass4 := false;  (* suppress automatic call of next pass *)

goto 999; (* terminate program *)
end;



procedure comp_error (cause: comp_errorcodes);
begin
writeln;
writeln ('*** program inconsistency in pass3, at sourceline ', cur_lineno:1);
write   ('    cause is: ');
case cause of
unknown_namekind: write ('unknown-namekind');
unknown_argkind: write ('unknown-argkind');
unknown_opcode: write ('unknown-opcode');
unknown_paramkind: write ('unknown-paramkind');
unknown_routinekind: write ('unknown-routinekind');
unknown_spix: write ('unknown-spix');
unknown_stdtype_name: write ('unknown-stdtype-name');
unknown_typekind: write ('unknown-typekind');
unknown_rectype: write ('unknown-rectype');
inconsist_alloc: write ('inconsistent-alloc');
wrong_version: write ('wrong pass1/pass3 combination');
unstack_nontype: write ('unstack-nontype');
end
otherwise write ('??? = ', ord(cause):1 );

break; (* terminate program, with call-trace *)

end; (* procedure comp_error *)



procedure readoption;
(* reads: passno, option number, option value *)
(* if passno is not current pass then emit the option *)

var passno, optno, optvalue: integer;

begin

passno   := passin;
optno    := passin;
optvalue := passin;

if passno <> own_passno then
begin
if passno = 0 then (* version-identification *)
if optno = 1 (* pass 1 *) then
if (optvalue div 100 <> versionpass1 div 100) or
(optvalue mod 100 < versionpass1 mod 100) then
comp_error (wrong_version);
emit   (xoption);
emitin (passno);
emitin (optno);
emitin (optvalue);
end
else
begin
case optno of
1: testinput   := optvalue = 1;
2: testoutput  := optvalue = 1;
3: call_pass4  := optvalue = 1;
4: lambda_version := optvalue = 1;
end otherwise; (* case, otherwise blind *)
end;
testing := testinput or testoutput;

end; (* procedure readoption *)

(*QQQ*)

(* procedures for allocating/releasing structures *)



function alloc_string: stringptr;
(* deliver a stringnode *)

var string: stringptr;

begin

string := freestrings;
if string = nilstring then
stop (too_many_strings);

with strings [ string ] do
begin
alloc_string := string;
freestrings  := nextstr;
nextstr      := nilstring;
end;

end;



procedure release_string (xstring: stringptr);
(* releases a whole list, constituting a literal *)

var string: stringptr;

begin

string := xstring;

(* scan the list to find the termination *)
repeat
with strings [ string ] do
begin
string := nextstr;
if string = nilstring then
nextstr := freestrings; (* insert in front of free nodes *)
end;
until string = nilstring;

freestrings := xstring;  (* first free in the list *)

end;



function alloc_opand: opandptr;
(* delivers an opand node, initialized with cur_lineno *)

var opand: opandptr;

begin

opand := freeopands;
if opand = nilopand then
stop (too_many_opands);

with opands [ opand ] do
begin
alloc_opand := opand;
freeopands  := next;
next        := nilopand;
op_lineno   := cur_lineno;
op_name     := nilname;
op_value    := nilstring;
op_packflag := false;
end;

end;



procedure release_expr (expr: opandptr);
(* works as 'emitexpr', but does only the releasing *)

begin

while expr <> nilopand do
with opands [ expr ] do
begin
if left <> nilopand then
release_expr (left);
if op_value <> nilstring then
release_string (op_value);
next := freeopands;
freeopands := expr;
expr := rigth;
end;

end; (* procedure release-expr *)



function alloc_namenode: nameptr;
(* delivers a namenode, initialized as dummy *)

var name: nameptr;
begin
name := freenames;
if name = nilname then
stop (too_many_names);

(* initialize *)
with names [ name ] do
begin
alloc_namenode := name;
freenames := nextname; (* unlink from free-chain *)
nextname  := nilname;

forward_decl  := false;
external_decl := false;
func_assigned := false;
usedlevel := lowestlevel - 1; (* i.e. not used *)
params := nilname;
typ := dummy_typenode;
end;

end;



procedure release_typenode (xtype: typeptr); forward;

procedure releasenames (xnamelist : nameptr);
(* releases all namenodes in the list *)
(* if a namenode is a typename, the corresponding typenode *)
(*   will be released too *)

var name: nameptr;

begin

while xnamelist <> nilname do
with names [ xnamelist ] do
begin
if params <> nilname then
releasenames (params); (* release paramlists of inner routines *)
if typ <> dummy_typenode then
if namekind <> cundeclared then
release_typenode (typ); (* release typenode of local type *)

(* insert in free-chain *)
name := nextname;
nextname := freenames;
freenames := xnamelist;

xnamelist := name;

end; (* with xnamelist *)

end;



function alloc_typenode : typeptr;
(* delivers a typenode, initialized as dummy *)

var name : nameptr;

begin

name := freetypename;
if name = nilname then
stop (too_many_types);


with names [ name ] do
with types [ typ ] do

begin
alloc_typenode := typ;
freetypename := nextname;
nextname := nilname;
typ := dummy_typenode;
releasenames (name);

(* initialize typenode *)
element := nilname;
index   := nilname;
subexpr := nilopand;
typekind := cnocode;
contents:= [ t_simple ];  (* default *)
packflag:= false;
emitted := false;

end; (* with typenode *)

end;



procedure release_typenode (xtype: typeptr);

var name: nameptr;

begin
with types [ xtype ] do
begin
if typekind = crecord then
releasenames (element); (* release field-list of record *)
if subexpr <> nilopand then
release_expr (subexpr);
end; (* with xtype *)
name := alloc_namenode;
with names [ name ] do
begin
namekind := ctype;
typ := xtype;
nextname := freetypename;
freetypename := name;
end;

end;



function alloc_label: labelptr;
(* delivers a labelnode *)

var lab: labelptr;

begin

lab := freelabels;
if lab = nillabel then
stop (too_many_labels);

with labels [ lab ] do
begin
alloc_label := lab;
freelabels  := prevlabel;
prevlabel   := nillabel;
end;

end;

(*QQQ*)



function readstring: stringptr;
(* reads: length, char [ 1 .. length ] from pass1file *)
(*  and packs into stringnodes *)

var
length: integer;
curstr: stringptr;
i, portion: str_index;

begin

length := passin; (* get length of string *)

curstr := alloc_string;
readstring := curstr; (* result is (first) stringnode *)

portion := maxstring;

while length > 0 do
with strings [ curstr ] do
begin

(* read a portion of the string *)
stringlgt := length;
if length < portion then portion := length;
length := length - portion;
for i := 1 to portion do
str [ i ] := chr (passin);

if length > 0 then
nextstr := alloc_string;

curstr := nextstr;

end; (* with curstr *)

end; (* procedure readstring *)



procedure emitstring (string: stringptr);

var
length: integer;
curstr: stringptr;
i, portion: str_index;

begin

curstr := string;
portion := maxstring;

with strings [ curstr ] do
length := stringlgt;

emitin (length);

if length > 0 then
repeat
with strings [ curstr ] do
begin
if portion > stringlgt then portion := stringlgt;

for i := 1 to portion do
emitin ( ord ( str [ i ] ) );

curstr := nextstr;
end; (* with curstr *)

until curstr = nilstring;

end; (* procedure emitstring *)



procedure emit_extname (opand: opandptr);

begin

emit (xname);
with opands [ opand ] do
emitstring (op_value);
release_expr (opand);

end;



procedure stdspix (xspix: integer; xstdname: stdname_range);

(* inserts the value 'xspix' in stdspix-table *)
(* ... the value -1 is <dummy-value> *)

var i: stdname_range;

begin

stdspix_table [ xstdname ] := xspix;

maxstdspix := -1; (* assume all entries are dummy *)
(* maxstdspix is used when determining a spix to be 'special' *)

for i := canonymous to cord do
if maxstdspix < stdspix_table [ i ] then
maxstdspix := stdspix_table [ i ];

end;



procedure standardname;
(* called from initialization and mainloop *)
(*   it will read: (spix,stdname) , and insert in tabel *)

begin
stdspix (passin (* = spix *), password (* = stdname *) );

anonymous_spix := stdspix_table [ canonymous ];
exception_spix := stdspix_table [ cexception ];
end;



procedure specialspix (xname: nameptr);
(* called from mainloop, when a 'special'-spix has been defined *)
(*   the action depends on the spix *)

label 99;
var i: stdname_range;

begin

with names [ xname ] do    (* namenode of special interest *)
with types [ typ ] do    (* the corresponding typenode (in case of type...) *)
for i := canonymous to cord do
(* scan the stdspix-table to find the rigth spix *)
if stdspix_table [ i ] = spix (* of namenode *) then
begin

(* remove it from table (... optimalization ...) *)
stdspix (-1, i);

case i of

canonymous: ; (* empty *)

cboolean:
begin
boolean_typename := xname;
emitid (xboolean, xname);
end;

cchar:
begin
char_typename    := xname;
emitid (xchar, xname);
end;

cshadow:
begin
shadow_typename := xname;
contents         := [ t_shadow ];  (* force this special content *)
emitid (xshadow, xname);
end;

creference:
begin
reference_typename := xname;
contents         := [ t_reference ];  (* force this special content *)
emitid (xreference, xname);
end;

csemaphore:
begin
contents         := [ t_semaphore ];  (* force this special content *)
emitid (xsemaphore, xname);
end;

cson:
son_typename     := xname;

cprocessrec:
processrec_typename := xname;

cexception:
exception_procname := xname;

cabs:
abs_procname     := xname;

csucc:
begin
succ_procname    := xname;
emitid (xsucc, xname);
end;

cpred:
begin
pred_procname    := xname;
emitid (xpred, xname);
end;

cchr:
emitid (xchr, xname);

cord:
begin
ord_procname     := xname;
emitid (xord, xname);
end;

end
otherwise
if compilertest then
comp_error (unknown_stdtype_name);

goto 99; (* goto found *)

end; (* for i in stdspix_range *)

(* in case the loop terminates: severe error *)
if compilertest then
comp_error (unknown_spix);

99: ; (* found: *)

end; (* procedure specialspix *)



procedure set_error_type;
(* if the current type was an implicit type definition,    *)
(*   it will be released at block-end                      *)

begin
cur_typename := error_typename;
cur_typenode := error_typenode;
end;

(* procedures for inserting/retrieving names *)



procedure searchspix (xspix: spixrange; firstlevel, lastlevel: levelrange);
(* searches among all names in the levels to find 'xspix' *)

(* the global variables 'retrieved_name'/'retrieved_level' *)
(*   describe the result of the procedure: *)

(* xspix not found: 'retrieved_name' = nilname (-level undef) *)
(* xspix found:     'retrieved_name/level' identifies the namenode *)

label 99;
var
name: nameptr;
level: levelrange;

begin

for level := lastlevel downto firstlevel do
with namestack [ level ] do
begin
name := firstname;
while name <> nilname do
with names [ name ] do
begin
if spix (* of name *) = xspix then
goto 99; (* found *)
name := nextname; (* follow chain *)
end;
end; (* for level *)
name := nilname; (* name not found *)

99:; (* found *)
retrieved_level := level; (* undef, if not found *)
retrieved_name  := name;  (* nilname, if not found *)

end; (* procedure searchspix *)



function findname (xspix: spixrange) : nameptr;
(* delivers the namenode with the searched spix *)
(* if the spix was'nt found, the result is 'nilname' *)

begin
searchspix (xspix, processparamlevel, namelevel);
if retrieved_name = nilname then
searchspix (xspix, lowestlevel, lastgloballevel);
findname := retrieved_name;
end;



function newname (xspix: spixrange) : nameptr;
(* a new namenode is inserted at current namelevel *)
(* the namenode is initialized as dummy *)

label 99;
var name : nameptr;

begin

if xspix <> anonymous_spix then
begin
(* explicit spix'es may not exist already at current namelevel *)
(*   ... except forward declared routine-names ... *)

name := findname (xspix);
if name <> nilname then
begin
(* the name already existed at some level *)
(* check the consistency *)
with names [ name ] do  (* namenode of just found name *)
if retrieved_level = namelevel then
begin
(* double-declaration is suppressed, in case the previous decl was forward *)
if forward_decl then
(* it was really forward declared *)
(* now test that it is similar to the new one *)
if namekind = cur_namekind then
goto 99; (* skip initialization etc *)

(* otherwise is will be flagged as error *)
error (double_declaration);
xspix := anonymous_spix; (* i.e. this name can never be retrieved *)

end (* if already on namelevel *)
else
begin
(* test that this (global) name has'nt been used previously on this level *)
if usedlevel (* of name *) = namelevel then
error (inconsistent_use);
end;

end; (* if existing name *)

end; (* if explicit spix *)

(* allocate and initialize a new namenode *)

name := alloc_namenode;

with names [ name ] do
begin
spix     := xspix;
namekind := cur_namekind;
typename := nil_typename;

(* insert in list of names of same level *)
with namestack [ namelevel ] do
if insert_in_front then
begin (* only function-return-value are inserted in front ... *)
with names [ name ] do
nextname := firstname; (* insert in front of existing list *)
if firstname = nilname then
lastname := name;
firstname := name;
end
else
begin
if firstname = nilname then
firstname := name
else
with names [ lastname ] do
nextname := name; (* insert in rear of existing list *)
lastname := name;
end; (* with namelevel *)

end; (* with name *)

99:; (* after init of namenode *)
newname := name;

end; (* procedure newname *)



function new_undecl (xspix: spixrange): nameptr;
(* the routine creates a new namenode with the spix given *)
(* and marks it as undeclared *)

var name: nameptr;

begin

name := newname (xspix);
with names [ name ] do
begin
namekind := cundeclared;
typename := error_typename;
typ      := error_typenode;
end;

(* nbnbnb: make a 'pseudo'-declaration of the name *)
emitid (xundeclid, name);

new_undecl := name;

end; (* procedure new-undecl *)



function usename (xspix: spixrange) : nameptr;
(* the routine will search for the given spix, and mark the namenode *)
(*   as being used on current namelevel *)

(* if the name does'nt exist, a new namenode will be created *)

var name: nameptr;

begin

cur_opandno := cur_opandno + 1;

name := findname (xspix);

if name = nilname then
begin
(* the name did'nt exist, create e new namenode *)
error (undeclared);
name := new_undecl (xspix);

end; (* if undeclared *)

(* test for recursive use of constants, i.e. using before defining *)
with names [ name ] do
if namekind = cconstant then
if usedlevel = lowestlevel - 1 (* i.e. just after declaration *) then
begin
error (recursive_constant_use);
name := new_undecl (xspix);
end;

(* mark the name as being used on current level *)
with names [ name ] do
usedlevel := namelevel;

usename := name; (* deliver the used name node *)

end; (* procedure usename *)



procedure end_level_use (firstlevel, lastlevel: levelrange);
(* all names in the outpointed namelevels are tested for use *)
(* on current namelevel *)

(* if a name was used on current namelevel, it will be flagged *)
(*   as if it had been used on preceding namelevel *)

var
name: nameptr;
level: levelrange;

begin

for level := lastlevel downto firstlevel do
with namestack [ level ] do
begin
name := firstname;
while name <> nilname do
with names [ name ] do
begin
if usedlevel = namelevel then
if namelevel = processparamlevel then
usedlevel := lastgloballevel
else
usedlevel := usedlevel - 1;
name := nextname;  (* follow chain *)
end;
end;

end; (* procedure end_level_use *)



procedure end_global_use;
(* called when terminating a namelevel *)
(* if global variables have been used at this namelevel *)
(*   their uselevel will be decreased as is they were used at *)
(*   preceding namelevel *)

begin
end_level_use (processparamlevel, namelevel - 1);
end_level_use (lowestlevel, lastgloballevel);
end;

(*QQQ*)

(* procedures for handling push/pop of names *)

procedure pushname (xname: nameptr);
begin
pushnamestack [ pushnameindex ] := xname;
if pushnameindex = max_pushnames then
stop (too_many_pushnames);
pushnameindex := pushnameindex + 1;
end;



function popname : nameptr;
begin
pushnameindex := pushnameindex - 1;
popname := pushnamestack [ pushnameindex ];
end;



(* pushlist/poplist are used for handling of nested namelists *)

procedure pushlist;
begin
pushname (listfirst);
pushname (listlast);
listfirst := nilname;
listlast  := nilname;
end;

procedure poplist;
begin
listlast  := popname;
listfirst := popname;
end;



(* pushtype/poptype are used for handling of nested types *)

procedure pushtype;
begin
pushname (cur_typename);
end;

procedure poptype;
begin
cur_typename := popname;
with names [ cur_typename ] do
cur_typenode := typ;

if compilertest then
if cur_typenode = dummy_typenode then
begin
comp_error (unstack_nontype);
set_error_type;
end;
end;



(* procedures for handling of nested procedure calls (or indexing etc) *)

procedure pushcall;
begin
if callindex = max_nested_calls then
stop (too_many_nested_calls);

with calls [ callindex ] do
begin
callkind      := argkind;
callformal    := curformal;
callowner     := arglist_owner;
call_afterfunc:= after_funccall;
call_functemp := functempvar;
call_countprocess := count_processparam;
end;

after_funccall := false; (* assume not function call *)

count_processparam := 0;

callindex := callindex + 1;

end; (* procedure pushcall *)



procedure popcall;
(* works opposite of pushcall *)

begin

callindex := callindex - 1;

with calls [ callindex ] do
begin
argkind       := callkind;
curformal     := callformal;
arglist_owner := callowner;
after_funccall:= call_afterfunc;
functempvar   := call_functemp;
count_processparam := call_countprocess;
end;

end; (* procedure popcall *)


(* procedures for creating new opand-nodes, whether operators or operands *)

procedure oper (mode: opmodes; xopcode: codes; xtypename: nameptr);
(* inserts an opandnode as unary or binary operator *)

var
oldtop: opandptr;

begin

(* move stacktop to another opand, and use the stacktop for the new one *)
(* this makes it possible to 'insert' in the middle of the opand-tree   *)
oldtop := alloc_opand;
opands [ oldtop ] := opands [ stacktop ];

with opands [ stacktop ] do  (* new opand node *)
begin

opcode := xopcode;

if mode = unary then
begin
left   := nilopand;
rigth  := oldtop;
next   := nexttop;
end
else
begin (* binary*)
left   := nexttop;
rigth  := oldtop;
next   := opands [ left ] . next;

nexttop := next;

end;

op_lineno   := 0;  (* dummy *)
op_name     := nilname;
op_value    := nilstring;
op_typename := xtypename;

end; (* with (new) stacktop *)

end; (* procedure oper *)



procedure newopand (xcode: codes; xtypename: nameptr);
(* insert an operand at stacktop *)

var
oldtop: opandptr;

begin

if retain_top then
begin
(* if insertion in middle of list then don't 'move' stacktop *)
oldtop := alloc_opand;
opands [ oldtop ] := opands [ stacktop ];
with opands [ stacktop ] do
begin
op_lineno := 0;
op_name   := nilname;
op_value  := nilstring;
end;
end
else
begin

oldtop := stacktop;
stacktop := alloc_opand;

end;

with opands [ stacktop ] do  (* new opand node *)
begin

opcode      := xcode;
left        := nilopand;
rigth       := nilopand;
next        := oldtop;
op_typename := xtypename;

end;

nexttop := oldtop;

end; (* procedure newopand *)



function take_stacktop : opandptr;
(* used when the top-expression is to be saved a while *)
begin
take_stacktop := stacktop;
stacktop      := nexttop;
nexttop       := opands [ stacktop ] . next;
end;



procedure insert_functype (xtypename: nameptr);
begin
with names [ functempvar ] do
typename := xtypename;
end;



procedure prepare_call (opand: opandptr);
(* used to prepare a routine-call *)
(* nb: 'pushcall' must have been called previously *)

var
saved_stacktop : opandptr;
saved_nexttop  : opandptr;

begin

with opands [ opand ] do   (* opand contains id, holding routine *)
with names [ op_name ] do   (* namenode of routine *)

begin
(* initialize global variables, describing arguments to this routine *)

argkind     := cargument;
curformal   := params;     (* first of formal list *)
arglist_owner := op_name;    (* set this routine as owner of formal list *)

if namekind (* of routine *) = cfunction then
begin
(* save stacktop etc and restore later *)
(* simulate that 'opand' is stacktop *)
saved_stacktop := stacktop;
saved_nexttop  := nexttop;
stacktop       := opand;
nexttop        := opands [ stacktop ] . next;

retain_top := true;

(* reserve temp name, for identifying temp-return-value *)
functempvar := alloc_namenode;
insert_functype (typename (* of function *) );

(* insert opand, pointing at this temp-name *)
oper (unary, cfunctrailer, nilname (* irrell typename *) );
with opands [ stacktop ] do
op_name := functempvar;

(* insert operand, to be used as actual argument for the return-value *)
newopand (cfunctemp, nilname (* irrell typename *) );
with opands [ stacktop ] do
op_name := functempvar;

(* bind this actual-argument onto the function *)
(* ... see also 'actparam' ... *)
oper (binary, argkind, nilname (* irrell typename *) );
with opands [ stacktop ] do
op_name := curformal;  (* let opname point at first formal name, i.e. 'funcval' *)

if compilertest then
if (stacktop <> opand) or (nexttop <> opands [ opand ] . next) then
begin
writeln(stacktop,opand,nexttop, opands[opand].next);
break;
end;

retain_top := false;

stacktop       := saved_stacktop;
nexttop        := saved_nexttop;
end;

end; (* with namenode of routine *)

end; (* procedure prepare-call *)



procedure terminate_call (opand: opandptr);
(* used by 'endactual' to test total match of formal lists etc *)

label 10;

var
saved_stacktop : opandptr;
saved_nexttop  : opandptr;

begin

(* save stacktop etc and restore later *)
(* simulate that 'opand' is stacktop   *)
saved_stacktop := stacktop;
saved_nexttop  := nexttop;
stacktop       := opand;
nexttop        := opands [ stacktop ] . next;

case argkind of

cstrucrecord,
cargument:
begin
(* test that all recfields (respectively all formals) have been matched *)
while curformal <> nilname do
with names [ curformal ] do
case namekind (* of curformal *) of
crecfield,
cvarp,
cvaluep:
begin
error (too_few_parameters);
goto 10;
end;
cfuncval,
cundeclared,  (* undeclared typename *)
cscalarelem,
ctype:
curformal := nextname;  (* i.e. skip in formal-list *)
end
otherwise
if compilertest then
comp_error (unknown_namekind);

10:;
end; (* argkind = strucrecord or argument *)

end (* case argkind *)
otherwise; (* no check *)

(* insert opands, describing the proper end-list *)
case argkind of

cindex: ; (* nothing *)

cstrucarray,
cstrucrecord:
oper (unary, cendstruc, arglist_owner);

cargument:
with names [ arglist_owner ] do
case namekind of
cprocess:
begin
oper (unary, ccallprocess, nilname (* no type *) );
with opands [ stacktop ] do
op_name := nextname (* of process *);  (* remember the sonvar-name, for later use *)
end;
cprocedure,
cfunction:
begin
(* first insert operators for later counting in opand-stack *)
while count_processparam > 0 do
begin
count_processparam := count_processparam - 1;
oper (unary, cendprocessparam, nilname (* irrell typename *) );
end;
case namekind (* of arglist-owner *) of
cprocedure:
oper (unary, cendproc, nilname (* irrell typename *) );
cfunction:
begin
with names [ functempvar ] do  (* namenode, containing function-type *)
oper (unary, cfcall, typename (* of function-type *) );
with calls [ callindex - 1 ] do  (* i.e. determine the value to be unstacked *)
call_afterfunc := true;
end;
end otherwise (* not possible *); (* case procedure or function *)
end;
end otherwise; (* case namekind *)

cerrorarg: ; (* don't care anyway *)

end (* case argkind *)

otherwise
if compilertest then
comp_error (unknown_argkind);

(* unstack nested call *)
popcall;

if compilertest then
if (opand <> stacktop) or (nexttop <> opands[opand].next) then
begin
writeln(stacktop,opand,nexttop,opands[opand].next);
break;
end;
(* restore thereal stacktop and nexttop *)
stacktop := saved_stacktop;
nexttop  := saved_nexttop;

end; (* procedure terminate-call *)



procedure terminate_funccall (opand: opandptr);
(* ensures that the 'endfunccall' is emitted (as late as possible) after a function call *)

var
saved_stacktop : opandptr;
saved_nexttop  : opandptr;

begin
if after_funccall then
begin
(* save stacktop etc and restore later *)
(* simulate that 'opand' is stacktop   *)
saved_stacktop := stacktop;
saved_nexttop  := nexttop;
stacktop       := opand;
nexttop        := opands [ stacktop ] . next;
with opands [ stacktop ] do
oper (unary, cendvariable, op_typename (* continue with op-type *) );
(* unstack from call-stack *)
after_funccall := false;
if compilertest then
if (opand <> stacktop) or (nexttop <> opands[opand].next) then
begin
write(stacktop,opand,nexttop,opands[opand].next);
break;
end;
(* restore the real stacktop and nexttop *)
stacktop := saved_stacktop;
nexttop  := saved_nexttop;
end;
end;



procedure make_call (opand: opandptr);
(* if the operand is a routine-ident, a (parameter-less) call will be made *)

begin
with opands [ opand ] do
if opcode = cid then
with names [ op_name ] do
case namekind of
cprocess,
cprocedure,
cfunction:
begin
pushcall;
prepare_call (opand);
terminate_call (opand);
terminate_funccall (opand);
end;
end otherwise; (* other namekinds are not routine-calls *)

end; (* procedure make-call *)

(*QQQ*)

(* procedures for handling level-change etc *)

procedure newnamelevel;
(* initializes a new namelevel, with an empty namelist *)
begin
if namelevel = max_levels then
stop (too_many_levels);
namelevel := namelevel + 1;
with namestack [ namelevel ] do
begin
firstname := nilname;
namelistkind := levelkind;
withname := nilname;  (* assume normal level *)
if namelistkind = processlevel then
processparamlevel := namelevel;
end;
levelkind := irrell_level;  (* prepare for next level to be normal *)

end;


procedure insert_namelist (first: nameptr);
(* initializes current namelevel with the namelist given *)
begin
with namestack [ namelevel ] do
begin
firstname := first;
lastname := firstname;
while names [ lastname ] . nextname <> nilname do
lastname := names [ lastname ] . nextname;
end;
end;


procedure endnamelevel;
begin
(* the variable 'processparamlevel' must be reinitialized, when *)
(*   leaving a process. *)
if namelevel = processparamlevel then
repeat
processparamlevel := processparamlevel - 1;
until
(namestack [ processparamlevel ] . namelistkind = processlevel)
or
(processparamlevel = lastgloballevel - 1);
end_global_use;
namelevel := namelevel - 1;
end;

procedure test_recursive_type;
(* will test that 'cur_typename' is a defined type *)
begin
with names [ cur_typename ] do
if spix (* of typename *) <> anonymous_spix then
(* recursion only possible with named types *)
with types [ typ ] do
if not emitted then
begin
error (recursive_use_of_type);
set_error_type;
end;
end;

(*QQQ*)



function empty_paramlist (paramlist: nameptr): boolean;
(* returns false, if the paramlist contains 'varparam' or 'valueparam' *)

label 99;

begin
while paramlist <> nilname do
with names [ paramlist ] do
if (namekind = cvarp) or (namekind = cvaluep) then
begin
empty_paramlist := false;
goto 99;
end
else
paramlist := nextname;  (* follow chain *)

(* the list did'nt contain var- or valueparam *)
empty_paramlist := true;
99: ;

end; (* procedure empty-paramlist *)

procedure compare_paramlists;
(* compares the previous paramlist of 'cur_routine' *)
(*   with param-list in current namelevel *)

(* the new paramlist may be empty or exactly the same *)

(* the new paramlist is released and replaced by the original list *)

var
newparam, oldparam: nameptr;
errorflag: boolean;

begin
errorflag := false; (* suppose no errors *)
with namestack [ namelevel ] do
with names [ cur_routine ] do
begin

newparam := firstname; (* first of new parameter list *)

if not empty_paramlist (newparam) then
begin
oldparam := params; (* first of old parameter list (of cur_routine) *)

(* compare the two lists *)
(* they both terminate with nilname *)

while oldparam <> newparam do
with names [ oldparam ] do
begin

if
(* they must have identical names *)
(spix     <> names [ newparam ] . spix)
or
(* both must be 'cvarp' or 'cvaluep' *)
(namekind <> names [ newparam ] . namekind)
or
(* they must have same type *)
(typename <> names [ newparam ] . typename)
then
errorflag := true;

oldparam := nextname;
newparam := names [ newparam ] . nextname;

end; (* with/while more params *)

if errorflag then
error (paramlist_changed_since_forwarddecl);

end; (* if new paramlist not empty *)

(* release the new paramlist and replace it by the old one *)

releasenames (firstname (* of namelevel *) );
insert_namelist (params (* of cur-routine *) );

end; (* with cur_routine *)

end; (* procedure compare_paramlists *)

(*QQQ*)

procedure begin_block;
(* called before first local declaration in a routine *)
begin
expecting_block := false; (* indicate that the procedure has been called *)

with names [ cur_routine ] do
begin
if namekind <> cprocess then
cur_blockno := cur_blockno + 1;
forward_decl := false; (* because here comes the body *)
end;

emit (xblock);
emitin (cur_blockno);

end;

(*QQQ*)

(* procedures for emitting identifiers and types *)



procedure emitexpr (expr: opandptr);
(* the expression is emitted, and maybe released (depending on 'expr-release') *)

procedure emitboth;
begin
with opands [ expr ] do
begin
emitexpr (left);
emitexpr (rigth);
end;
end;

procedure emit_arith (outcode: pass3codes);
begin
with opands [ expr ] do
begin
if left <> nilopand then
begin
emitexpr (left);
emit (xgetvalue);
end;
emitexpr (rigth);
emit (outcode);
end;
end;

begin

if expr <> nilopand then
with opands [ expr ] do
begin

(* note: only 'newopand' inserts proper lineno in opands *)
if op_lineno <> 0 then
if op_lineno <> emit_lineno then
begin
emit (xnewline);
emitin (op_lineno);
emit_lineno := op_lineno;
end;


expr_level := expr_level + 1;
if expr_level = 1 then
begin
emit (xexpr);      (* this is the start of a whole expression *)
if testoutput then continue_line;
end;

if opcode = cnocode then
emitboth
else
case opcode (* of opand *) of

cid:
begin
(* identifier: action depends on namekind *)

with names [ op_name ] do  (* namenode of identifier *)

case namekind (* of namenode *) of

cundeclared,
cscalarelem,
cconstant,
cvar,
cvarp,
cvaluep,
cfuncval:
begin
emitid (xvar, op_name);
emitin ( ord(typename) );
end;

cprocess: ; (* nothing *)
(* start of process param sequence *)

cprocedure:
begin
(* start of procedure call-sequence *)
emitid (xproccall, op_name);
end;

cfunction:
begin
(* start of function call sequence *)
emitid (xfunccall, op_name);
(* the temp-return-value and the type are emitted by 'functrailer' *)
end;

ctype:
begin
(* start of structured constant sequence *)
emitid (xstrucconst, op_name (* name of type itself *));
end;

end (* case namekind *)
otherwise
if true then write (output, '*** id: namekind = ', ord(namekind):1)
else
if compilertest then
comp_error (unknown_namekind);

end; (* opcode = 'id' *)

cliteral:
begin
emit (xliteral);
with names [ op_typename ] do    (* namenode of opand-type *)
with types [ typ ] do          (* corresponding typenode *)
if typekind (* of opand-typenode *) = cinteger then
emitin ( ord(lit_integer) )
else
with strings [ op_value ] do
if stringlgt = 1 then
emitin ( ord(lit_char) )
else
emitin ( ord(lit_string_or_empty) );
emitstring (op_value);
end;


cskipparam:
emit (xnull);


cfunctrailer:
begin
emitexpr (rigth);  (* emit funccall etc *)
emitin (ord (op_name) );  (* name of temp-return-value *)
with names [ op_name ] do  (* namenode of functemp var *)
emitin (ord (typename) );  (* typename of function *)
end;


cfunctemp:
begin
(* this is almost an 'id', but the namenode does'nt contain typename *)
(* used for actual argment, to match 'funcval' *)
emitid (xvar, op_name);
with names [ op_name ] do  (* namenode of functemp var *)
emitin ( ord (typename) );
if expr_release then
releasenames (op_name); (* because it was a temporary *)
end;


cfcall:
begin
emitexpr (rigth);  (* note: it is an unary operator *)
emit (xfcall);
emitin (count_processparam); count_processparam := 0;
(* note: the 'endfunccall' is emitted after any indexing,uparrow etc *)
end;


cendproc:
begin
emitexpr (rigth);  (* note: it is an unary operator *)
emit (xendproc);
emitin (count_processparam); count_processparam := 0;
end;


ccallprocess:
emitexpr (rigth);  (* note: it is an unary operator *)


cendprocessparam:
begin
emitexpr (rigth);  (* note: it is an unary operator *)
count_processparam := count_processparam + 1;
end;


(* emission of set_values *)
csetlist:
emit (xset);
cs_element:
begin
emitboth;
emit (xinclude);
end;
cm_element:
begin
emitexpr (left);
emit (xsetexpr);
emitexpr (rigth);
emit (xincluderange);
end;
cendsetlist:
begin
emitexpr (rigth);  (* note: unary operator *)
(* note: pass4 will generate its own type description, using this type-nix *)
emitid (xendset, op_typename);
if expr_release then
if not const_decl then  (* dont't release, when const-declaration *)
releasenames (op_typename); (* because it was a temporary *)
end;


cfield:
begin
emitexpr (rigth);  (* nb: field is unary operator *)
emit (xfield);
emitin (ord (op_name) );     (* field name *)
emitin (ord (op_typename) ); (* field type name *)
end;


cuparrow:
begin
emitexpr (rigth); (* nb: uparrow is unary operator *)
emitid (xarrow, op_typename);
end;

crange:
begin
emitexpr (rigth);  (* note: unary operator *)
emitid (xrange, op_typename);
end;


cerrorarg, (* midlertidig ************************************** *)
cindex:
begin
emitexpr (left); (* array name etc *)
emit (xindexexpr);
emitexpr (rigth); (* index expression *)
emit (xindex);
emitin (ord (op_name) );     (* array type name *)
emitin (ord (op_typename) ); (* element type name *)
end;


cprocessargument:
begin
(* parameter, describing a process-'call' *)
(* emit the argument-expression at once (i.e. the process-params) *)
(* and then later simulate a normal argument *)
emit (xprocessparam);
emit (xvarpointer);
emitin ( ord (op_name) );  (* son-var of process *)
emitin ( ord (op_typename) );  (* temp var, for addressing the 'processrec' *)
emit (xtempointer);
emitexpr (rigth);  (* emit the process-params *)

emit (xarglistsize);
emitexpr (left);  (* previous routine-ident and arguments *)
end;


cargument:
begin
(* argument-list of routine call *)
emitexpr (left);  (* previous routine-ident and arguments *)
emitid (xactual, op_name);
emitexpr (rigth); (* argument expression *)
emit (xendactual);
end;


cdoubleparam:
begin
(* used for repetition in structured array-values *)
emitexpr (left);  (* emit repeat-value *)
emit (xtimes);
emitexpr (rigth); (* emit the struc-value *)
end;


cendvariable:
begin (* made by 'cendvariable', if after function call *)
emitexpr (rigth);  (* note: unary operator *)
emit (xendfunccall);
end;


cstrucarray,
cstrucrecord:
begin
emitboth;  (* emit previous struc-elements and value component *)
emitid (xstruc, op_typename);
end;


cendstruc:
begin
emitexpr (rigth);  (* note: it is an unary operator *)
emit (xendstruc);
end;

cendassign:
begin
emit (xassignstat);
emitexpr (left);   (* variable *)
emit (xbecomes);
emitexpr (rigth);  (* expression *)
emit (xassign);
end;

cendexchange:
begin
emit (xexchangestat);
emitexpr (left);   (* lefthand variable *)
emit (xbecomes);
emitexpr (rigth);  (* rigthhand variable *)
emit (xexchange);
end;

crangefirst:
begin
emitexpr (left);  (* first range *)
emit (xgetexpr);
emitexpr (rigth); (* last range *)
end;

cplus:   emit_arith (xadd);
cminus:  emit_arith (xsub);
cstar:   emit_arith (xmul);
cslash,                    (* until further same as div... *)
cdiv:    emit_arith (xdiv);
cmod:    emit_arith (xmod);
ceq:     emit_arith (xeq);
cne:     emit_arith (xne);
clt:     emit_arith (xlt);
cle:     emit_arith (xle);
cgt:     emit_arith (xgt);
cge:     emit_arith (xge);
cin:     emit_arith (xin);
cor:     emit_arith (xor);
cxor:    begin
if not lambda_version then error( not_implemented_on_z80 );
emit_arith (xxor);
end;
cand:    emit_arith (xand);
cnot:    emit_arith (xnot);
cuminus: emit_arith (xneg);
cuplus:  emitexpr (rigth);  (* blind operator *)


end (* case opcode of opand *)
otherwise
if compilertest then
comp_error (unknown_opcode);

if testoutput then continue_line;

if expr_release then
begin
if op_value <> nilstring then
release_string (op_value);
if expr = stacktop then
stacktop := next;
next := freeopands;
freeopands := expr;
end;

expr_level := expr_level - 1;

end; (* with expr *)

end; (* procedure emitexpr *)



procedure emit_and_release (expr: opandptr);
(* emits - and releases - the expression *)
begin
expr_release := true;
emitexpr (expr);
expr_release := false;

(* in case stacktop has been updated, now update nexttop *)
nexttop := opands [ stacktop ] . next;

end;



procedure prepare_statement;
(* the procedure will emit all pending expressions  *)
(* .... but maybe it should rather test for pending expressions ? *)
(**)
(* furthermore it will set a flag, used to determine how *)
(*   a function-identifier should be interpreted         *)

begin
while stacktop <> nilopand do
emit_and_release (stacktop);
before_assign := true;
end;



procedure prepare_expression;
(* works in coorporation with prepare-statement *)

begin
before_assign := false;
end;



procedure emittype (xtypename: nameptr);
label 10; (* loop in cscalar *)
var name: nameptr;
begin

with names [ xtypename ] do
with types [ typ ] do
begin 

if emitted then
emitid (xtype, xtypename)
else
begin
(* this is the first time the typename is emitted *)
(*   emit the whole definition *)
emitted := true;

case typekind of

calias:
begin
(* output:  <type>  redeftype(nix) *)
emittype (element);
emitid (xredeftype, xtypename);
end;



cscalar:
begin
(* output:  ( scalarid(nix) )*  scalardef(nix)   *)
name := element; (* first scalar name of scalartype *)

10: (* loop *)
with names [ name ] do
if namekind = cscalarelem then
(* the scalar names are in the common namelist *)
if typename = xtypename then
begin
emitid (xscalarid, name);
name := nextname;  (* follow chain *)
if testoutput then continue_line;
goto 10;  (* goto loop *)
end;

emitid (xscalardef, xtypename);
end;



csubrange:
begin
(* output:  <expr> <expr> subdef(typenix, elementnix) *)
emit_and_release (subexpr);  (* emit both expressions *)
subexpr := nilopand;
emitid (xsubdef, xtypename);
emitin (ord (index) );  (* nix of element type *)
end;



cpointer:
begin
(* output:  <type>  pointerdef(nix)  *)
emittype (element);
emitid (xpointerdef, xtypename);
end;



creadonly:
begin
(* output:  <type>  frozendef(nix)  *)
emittype (element);
emitid (xfrozendef, xtypename);
end;



carray:
begin
(* output:  <indextype> <elementtype> (packed) arraydef(nix)  *)
emittype (index);
emittype (element);
if packflag (* of array type *) then
emitid (xpackedarraydef, xtypename)
else
emitid (xarraydef, xtypename);
end;



crecord:
begin
(* output:                                   *)
(*   packedrecord/record                     *)
(*     ( fieldid(nix)  <type>  fielddef )*   *)
(*   recdef(nix)                             *)
if packflag (* of record type *) then
emit (xpackedrecord)
else
emit (xrecord);

name := element;  (* first name in field list *)
while name <> nilname do
with names [ name ] do
begin
if namekind (* of fieldname/local typedef *) = crecfield then
begin
if testoutput then continue_line;
emitid (xfieldid, name);
emittype (typename);
emit (xfielddef);
end;
name := nextname;  (* follow chain *)
end;

emitid (xrecdef, xtypename);

end;



cset:
begin
(* output:  <type>  setdef(nix)  *)
emittype (element);
emitid (xsetdef, xtypename);
end;



cpool:
begin
(* output:  <expr> <type> pooldef(nix)  *)
emit_and_release (subexpr);
subexpr := nilopand;
emittype (element);
emitid (xpooldef, xtypename);
end;



cinteger:
emitid (xinteger, xtypename);



creal:
emitid (xreal, xtypename);



cniltype:
emitid (xniltype,xtypename);



cerrorkind:
emitid (xerrortype, xtypename);



ctext:
emitid (xstringtype, xtypename);



end (* case typekind *)
otherwise
if true then write(' unknown typekind = ', ord (typekind)) else
comp_error(unknown_typekind);

end; (* if not emitted yet *)

end; (* with typ/typename *)

end; (* procedure emittype *)

(*QQQ*)

procedure emitformal (plist: nameptr);
(* output:  ( paramid(nix) <type> cparam/vparam )*  *)
procedure emit_one_formal (xparamkind: pass3codes);
begin
if testoutput then continue_line;
emitid (xparamid, plist);
emittype ( names [ plist ] . typename );
emit (xparamkind);
end;
begin

while plist <> nilname do
with names [ plist ] do (* namenode of current parameter *)
begin
case namekind of
cfuncval: emit_one_formal (xvarparam);     (* function value *)
cvaluep:  emit_one_formal (xvalueparam);      (* value parameter *)
cvarp:    emit_one_formal (xvarparam);        (* var parameter *)
ctype,        (* implicit type decls *)
cundeclared,  (* undeclared typenames *)
cscalarelem:  (* scalar-elems of implicit typedecls *)
; (* no action *)
end
otherwise
comp_error(unknown_paramkind);

plist := nextname;  (* follow chain *)
end;  (* while/with plist *)

end;



procedure emitroutinehead (xname: nameptr);
(* 'xname' is the namenode of the routine *)

(* if this is second emit of routinehead, i.e. earlier was 'forward' *)
(*    then emit short description,
(*    otherwise emit the complete routine head *)

begin

with names [ xname ] do (* namenode of routine *)
if forward_decl then
begin
case namekind of
cprocess,
cprocedure: emitid (xsecprocid, xname);
cfunction:  emitid (xsecfuncid, xname);
end
otherwise
if compilertest then comp_error (unknown_namekind);
emit_extname (opand_procname);  (* emit the name once again... *)
end
else
begin

case namekind of
cprocess:   emitid (xprocessid, xname);
cprocedure: emitid (xprocid, xname);
cfunction:  emitid (xfuncid, xname);
end
otherwise
comp_error(unknown_routinekind);

if namekind = cprocess then
begin
(* emit the son var nix *)
emitin (ord (nextname) ); (* 'sonvar' is next to 'processname' *)
emitin ( ord (son_typename) );  (* type-nix of 'sonvariable' *)
end;

(* emit external name of the routine *)
emit_extname (opand_procname);

(* emit formallist *)
emitformal (params);

end; (* if first emission of this routine head *)

(* set flag to look for first local declaration *)
expecting_block := true;

end; (* procedure emitroutinehead *)



function testprocessparam (xtypename: nameptr; dangerous: boolean) : boolean;
(* returns true, if the type contains (or may reach via pointers) *)
(*   shadow, reference or pool                                    *)
(* or                                                             *)
(*   a pointer, which it not protected                            *)

var
no_of_pointers : typeptr;  (* counts number of tested pointer-types *)
checked_pointers : packed array [ typeptr ] of nameptr;  (* contains typenames *)
param_error : boolean;

procedure test (xtypename: nameptr; dangerous: boolean);
var
field: nameptr;
ptr_index: typeptr;

begin
if not param_error then
with names [ xtypename ] do  (* namenode of the type to be tested *)
with types [ typ ] do      (* corresponding typenode *)
if [ t_shadow, t_reference, t_pool ] * contents (* of typenode *) <> [] then
param_error := true
else
if t_pointer in contents (* of typenode *) then
case typekind (* of typenode *) of
carray:
test (element, dangerous);
crecord:
begin
field := element;
while field <> nilname do
with names [ field ] do  (* namenode of field *)
begin
test (typename, dangerous);
field := nextname;
end;
end;
creadonly:
test (element, false);
cpointer:
if dangerous then
param_error := true
else
(* test that the outpointed type has'nt been tested already *)
(* i.e. 'recursive' pointer *)
for ptr_index := niltype to no_of_pointers do
if ptr_index = no_of_pointers then
begin
checked_pointers [ no_of_pointers ] := element;
no_of_pointers := no_of_pointers + 1;
test (element, true);  (* from now on: really dangerous ... *)
end
else
if checked_pointers [ ptr_index ] = element then
ptr_index := no_of_pointers;  (* force fast exit of loop *)

end (* case *)
otherwise
if compilertest then comp_error (unknown_typekind);

end; (* procedure test *)

begin (* body of testprocessparam *)
no_of_pointers := niltype;
param_error := false;
test (xtypename, dangerous);
testprocessparam := param_error;
end; (* procedure testprocessparam *)

(*QQQ*)

(* procedures for handling of label declarations/definitions/use *)

procedure pseudodefine (xscopeno: labelrange; xspix: spixrange);
(* the label-definition is inserted in the local label-list *)

var
curlabel: labelptr;
multdef : boolean;

begin

(* test for multiple definition *)

curlabel := lastlabel;
multdef  := false; (* suppose ok *)

while curlabel <> nillabel do
with labels [ curlabel ] do
begin
if labelspix = xspix then
begin
multdef  := true;
multflag := true; (* mark the labelnode as inconsistent *)
end;
curlabel := prevlabel;  (* follow chain backwards *)
end;

(* allocate and initialize a new labelnode *)

curlabel := alloc_label;
with labels [ curlabel ] do
begin
labelspix  := xspix;
labelscope := xscopeno;
labelstate := outside_scope;
multflag   := multdef; (* mark it as (in)consistent *)
used_from_inner_routines := false;

prevlabel  := lastlabel;  (* insert in front of other label defs *)
lastlabel  := curlabel;

(* test for declaration on current namelevel *)
searchspix (xspix, namelevel, namelevel);
labelname := retrieved_name;  (* = nilname, if undeclared *)
with names [ labelname ] do
if namekind <> clabel then
labelname := nilname;

(* test for use from inner routines *)
(*   if so, it better be defined at outermost labelscope *)
if labelname <> nilname then
with names [ labelname ] do
if usedlevel >= lowestlevel then
(* it has been used *)
used_from_inner_routines := true;

end; (* with curlabel *)

end; (* procedure pseudodefine *)



procedure initlabels;
(* called at enddeclaration, at 'begin' *)
(* initializes the labelnodes from 'pass3labels' *)

var lab: labelptr;

begin

(* set all labelnodes in free-list *)
freelabels := nillabel;
for lab := nillabel to max_labels do
with labels [ lab ] do
begin
prevlabel := freelabels;
freelabels := lab;
end;
lastlabel := nillabel; (* initialize list of defined labels *)

while convtable [ labelin (* = rectype in labelfile *) ] = clabeldef do
pseudodefine (labelin (* = scopeno *), labelin (* = spix *) );

end; (* procedure initlabels *)



procedure definelabel (xscopeno: labelrange; xspix: spixrange);
(* searches the local labels to retrieve the wanted label *)

var
curlabel: labelptr;

begin

(* check the list of defined labels to produce errors *)

curlabel := lastlabel;
while curlabel <> nillabel do
with labels [ curlabel ] do
if labelspix = xspix then
begin
if multflag then
error (multiple_defined_label);
if labelname = nilname then
error (label_not_declared);
if used_from_inner_routines then
error (label_used_from_inner);
(* nbnbnb hvem checker erkl paa yderste scope ? *)

(* output:  label(labelnix) *)
emitid (xlabel, labelname);

curlabel := nillabel;
end
else
curlabel := prevlabel;

end;



procedure set_label_state (setscope: boolean);
(* used by: begin/end-label-scope *)
(* the labelstates, belonging to current labelscope are set *)
(*   to inside/outside label scope *)
var
curlabel: labelptr;

begin

curlabel := lastlabel;
while curlabel <> nillabel do
with labels [ curlabel ] do
begin

(* first: reopen any labels, hidden by this labelscope *)
if labelstate = hidden_by_lock_or_channel then
if hiddenscope = cur_labelscope then
labelstate := inside_scope;  (* it must be: clear-scope *)

(* second: hide all open labels, if start of lock or channel statements *)
if start_of_protected then
(* first setscope in lock-channel *)
if labelstate = inside_scope then
begin
labelstate  := hidden_by_lock_or_channel;
hiddenscope := cur_labelscope;
end;

(* third: open or close label, when belonging to this labelscope *)
if labelscope = cur_labelscope then
if setscope then
labelstate := inside_scope
else
labelstate := outside_scope;
curlabel := prevlabel;
end; (* while/with curlabel *)

start_of_protected := false;

end; (* procedure set_label_state *)



function check_label (labname: nameptr) : pass3errors;
(* used when referring to a labelname *)

var
curlabel: labelptr;

begin

with names [ labname ] do
begin
if namekind <> clabel then
check_label := not_label_name
else
begin
(* check locally declared label *)

check_label := label_undefined; (* suppose not locally declared *)

curlabel := lastlabel;
while curlabel <> nillabel do
with labels [ curlabel ] do
begin
if labelspix = spix (* of labelname *) then
begin
(* it was locally declared, now check the labelscope *)
case labelstate of
inside_scope: check_label := noerror;
outside_scope:
check_label := label_used_outside_scope;
(* i.e. goto leading into control structure *)
hidden_by_lock_or_channel:
check_label := label_defined_outside_lock_or_channel;
(* i.e. goto out of lock or channel statement *)
end otherwise;
if multflag then
check_label := erroneous_label;
if labelname = nilname then
check_label := label_not_locally_declared;
end;

curlabel := prevlabel;
end; (* while/with curlabel *)

(* note: *)
(*   if label is global to current block, it will be *)
(*   checked later, that the label is defined in the *)
(*   outermost labelscope of that block              *)

end; (* if namekind = label *)

end; (* with labname *)

end; (* procedure checklabel *)



procedure testvalue (opand: opandptr);
(* tests that the opand posseses a value *)

label 98;

begin

with opands [ opand ] do
case opcode of
cid:
with names [ op_name ] do (* namenode of identifier *)

(* certain of the namekinds are illegal in expressions *)
case namekind of
clabel,
cprocess,
cprocedure,
ctype,
cprefix:
goto 98; (* error *)

cundeclared,  (* don't cause error-cascades *)
cvar,
cvarp,
cvaluep,
cfuncval,
cscalarelem,
cconstant: ; (* ok *)

cfunction:
make_call (opand); (* assume that the paramlist was empty *)

end otherwise; (* case *)

cindex,
cfield,
cendstruc,
cuparrow,
cliteral,
cfcall,      (* when fielding or indexing on function value *)
cendvariable,   (* after function call and any fielding or indexing *)
cfunctemp,  (* used when describing 'processcall' as actual param *)
cendsetlist,
cdoubleparam,
cerrorarg,
cplus, cminus, cstar, cslash, cdiv, cmod,
ceq, cne, clt, cle, cgt, cge, cin, cor, cxor, cand, cnot,
cuminus, cuplus: ; (* ok *)

end (* case opcode *)
otherwise
98:  (* error *)
begin
error (illegal_in_expr);
op_typename := error_typename;   (* try to prevent error-cascades *)
end;

end; (* procedure testvalue *)



procedure testvariable (opand: opandptr);
(* tests that the opand may be used as <variable> *)

label 99; (* opand ok *)

begin

with opands [ opand ] do

case opcode of

cindex,
cfield,
cuparrow:
begin
if opcode = cindex then
testvariable( left )
else testvariable( rigth );
goto 99; (* ok *)
end;

cid:
with names [ op_name ] do  (* namenode of variable *)
case namekind (* of namenode *) of
cfunction:
make_call (opand); (* terminate an erroneous parameter-less function *)
cundeclared,  (* don't cause error-cascades *)
cvar,
cvarp, cvaluep, cfuncval:
goto 99; (* ok *)
end otherwise (* continue *); (* case namekind *)

end otherwise; (* case opcode *)

error (not_variable);
99: ; (* ok *)

end; (* procedure testvariable *)



function testfrozen (opand: opandptr): boolean;
var all_tested : boolean;

begin
testfrozen := false;
all_tested := false;

repeat
with opands [ opand ] do
with names [ op_typename ] do  (* typename of operand *)
with types [ typ ] do        (* corresponding typenode *)
if typekind = creadonly then
begin
testfrozen := true;
all_tested := true;
end
else
(* follow typenodes backwards for records and arrays *)
(* to search for 'readonly' types *)
case opcode (* of opand *) of
cindex : opand := left;  (* follow the array-operand *)
cfield : opand := rigth; (* follow the record-operand *)
end (* case *)
otherwise
all_tested := true;

until all_tested;

end; (* procedure testfrozen *)



procedure testassignable (opand: opandptr);
(* generates an error, if operand cannot be assigned *)
begin
testvariable (opand);  (* it has to be a variable *)
with opands [ opand ] do
with names [ op_typename ] do  (* namenode of operand-type *)
with types [ typ ] do        (* corresponding typenode *)
if (contents (* of typenode *)
* [ t_semaphore, t_pool, t_reference, t_shadow, t_readonly ] <> [])
or testfrozen (opand) then
error (not_assignable);
end; (* procedure testassignable *)



procedure testexchangable (opand: opandptr);
(* generates an error, if operand cannot be exchanged *)
begin
testvariable (opand);  (* it has to be a variable *)
with opands [ opand ] do
with names [ op_typename ] do  (* namenode of operand-type *)
with types [ typ ] do        (* corresponding typenode *)
if contents (* of typenode *)
* [ t_semaphore, t_pool, t_readonly ] <> [] then
error (not_exchangeable);
end; (* procedure testexchangable *)



procedure testunpacked (opand: opandptr);
(* generates an error, if the operand is packed *)
begin
with opands [ opand ] do
if op_packflag then
error (variable_may_not_be_packed);
end; (* procedure testunpacked *)



procedure resolve_named_type (
xxtypename: nameptr;
var xtypename: nameptr;
var xelement: nameptr;
var xtypekind: codes);  (* ************** *)  forward;  (* ********* *)



procedure resolvetype (
opand: opandptr;
var xtypename: nameptr;
var xelement: nameptr;
var xtypekind: codes);
label 10;

var local_typename: nameptr;
local_element: nameptr;
local_kind: codes;

begin

testvalue (opand);  (* the operand must posses a value *)

local_typename := opands [ opand ] . op_typename;

10: (* after stripping of superflous type inf *)

with names [ local_typename ] do
with types [ typ ] do

begin

if typekind = csubrange then
begin
local_typename := index;
goto 10;
end;
if typekind = creadonly then
begin
local_typename := element;
goto 10;
end;

xtypename := local_typename;
xtypekind := typekind;
xelement  := element;

if xtypekind = cset then
(* resolve element-type *)
resolve_named_type (element, xelement, local_element, local_kind);
if xtypekind = calias then
resolve_named_type (element, local_typename, xelement, xtypekind);

end; (* with names,types *)

end; (* procedure resolvetype *)



procedure resolve_named_type (
xxtypename: nameptr; (* typename to be resolved *)
var xtypename: nameptr;
var xelement: nameptr;
var xtypekind: codes);
begin
(* seperates the type into its components, stripping subrange etc. *)
opands [ nilopand ] . op_typename := xxtypename; (* use as working cell *)
resolvetype (nilopand, xtypename, xelement, xtypekind);
end;



function unstripfrozen (xtypename: nameptr): nameptr;
label 10;

begin

10: with names [ xtypename ] do   (* namenode of type *)
with types [ typ ] do       (* corresponding typenode *)
if typekind = creadonly then
begin
xtypename := element;  (* follow typename-chain *)
goto 10;  (* repeat search for several 'frozen' in row *)
end;

unstripfrozen := xtypename;   (* deliver unstrip'ed typename *)

end;



procedure equaltypes (leftop, rigthop: opandptr; wanted_typename: nameptr; errorcode: pass3errors);

begin
resolvetype (leftop, lefttypename, leftelement, leftbasekind);
resolvetype (rigthop, rigthtypename, rigthelement, rigthbasekind);

if (lefttypename <> rigthtypename) or
(lefttypename <> wanted_typename) then
error (errorcode);

end;



procedure assure (opand: opandptr; wanted_typename: nameptr; errorcode: pass3errors);

(* modifies 'optypename' if any error is produced *)

begin
resolvetype (opand, exprtypename, exprelement, exprbasekind);
if exprtypename <> wanted_typename then
if exprbasekind <> cerrorkind then  (* suppress error-cascades *)
begin
error (errorcode);
with opands [ opand ] do
op_typename := error_typename;
end;
end;



procedure testindex (var xtypename: nameptr);
(* tests that the typekind is enumeration kind *)

begin
resolve_named_type (xtypename, exprtypename, exprelement, exprbasekind);
case exprbasekind of

cerrorkind,  (* don't cause error-cascades *)
cinteger,
cscalar: ; (* ok *)

end

otherwise
begin
error (not_index_type);
xtypename := error_typename;
end;

end; (* procedure testindex *)



function testenumeration (opand: opandptr): nameptr;
(* works as 'testindex', but works on an opand *)
var local_typename: nameptr;

begin
testvalue (opand);
with opands [ opand ] do
begin
local_typename := op_typename;
testindex (local_typename);
op_typename := local_typename;
testenumeration := op_typename;
end;

end; (* procedure testenumeration *)



procedure set_procedure;
(* handles set-values *)

begin

case rectype of

csetlist:
(* prepare s-element or m-element to be binary operators *)
newopand (csetlist, nil_typename);

cs_element,
cm_element:
begin
rigthtypename := testenumeration (stacktop);
if rectype = cm_element then
begin
lefttypename := testenumeration (nexttop);
if lefttypename <> rigthtypename then
error (mixed_types_in_setlist);
oper (binary, rectype, rigthtypename);
rectype := cnocode;  (* let rectype describe simple binding *)
end;

(* compare element-type with previous type in setlist *)
with opands [ nexttop ] do  (* previous setlist-element or dummy-element *)
if op_typename (* of nexttop *) <> rigthtypename then
if op_typename <> nil_typename then  (* suppress error for very first element *)
error (mixed_types_in_setlist);

oper (binary, rectype, rigthtypename);

end;


cendsetlist:
begin
(* construct a typenode: set of <typename> *)
exprtypename := alloc_namenode;
with names [ exprtypename ] do
begin
namekind := ctype;
typename := nil_typename;
typ      := alloc_typenode;
with types [ typ ] do
begin

typekind := cset;
element  := opands [ stacktop ] . op_typename;
(* contents := [ t_simple ];  *)
end;
end; (* with exprtypename *)

oper (unary, cendsetlist, exprtypename);

end;

end (* case rectype *)
otherwise
if compilertest then
comp_error (unknown_rectype);

end; (* procedure set-procedure *)



procedure export_procedure;

var
errortxt: pass3errors;
opand:    opandptr;
expkind:  (undefkind, constkind, varkind);

begin

case rectype of

cexport:
begin
(* save the external name, to be emitted later *)
opand_procname := take_stacktop;
if expecting_block then
begin_block;
cur_dyn_var_level := maxint;
end;

cexportvalue,
cexportsize,
cexportdisp,
cexportoffset,
cexportaddr:
begin
(* opand_procname holds external name *)
(* stacktop holds variable to be exported *

(* exportvalue: only const *)
(* exportsize : const or var *)
(* exportdisp : field (of const or var) *)
(* exportoffset: only var *)
(* exportaddr : const or var *)

resolvetype (stacktop, exprtypename, exprelement, exprbasekind);

if exprbasekind <> cerrorkind then
begin
(* find the identifier, in front of any possible fields *)
opand := stacktop;
while opands [ opand ] . opcode = cfield do
opand := opands [ opand ] . rigth;

errortxt := noerror; (* suppose no errors... *)

(* decide kind of identifier *)
with opands [ opand ] do  (* opcode = 'cid' ... *)
with names [ op_name ] do (* namenode of identifier *)
case namekind (* of identifier *) of
cvar,
cvarp,
cvaluep,
cfuncval: expkind := varkind;
cconstant: expkind := constkind;
end otherwise expkind := undefkind;

case rectype of
cexportvalue:
if expkind <> constkind then
errortxt := const_export_only;
cexportsize,
cexportdisp,
cexportaddr:
if (expkind <> constkind) and (expkind <> varkind) then
errortxt := const_or_var_export_only
else
if rectype = cexportdisp then
if opand = stacktop (* i.e. no field-operators *) then
errortxt := disp_export_must_have_fields;
cexportoffset:
if expkind <> varkind then
errortxt := var_export_only;
end; (* case *)

if errortxt <> noerror then
error (errortxt);

end; (* if no error-operand *)

case rectype of
cexportvalue : emit (xexportvalue);
cexportsize  : emit (xexportsize);
cexportdisp  : emit (xexportdispl);
cexportoffset: emit (xexportoffset);
cexportaddr  : emit (xexportaddr);
end;

emit_extname (opand_procname);
emit_and_release (stacktop);
emit (xendexport);

cur_dyn_var_level := namelevel;

end;
end (* case rectype... *)
otherwise if compilertest then comp_error(unknown_rectype);

end; (* procedure export-procedure *)



function allow_text_literals: boolean;
(* implicit parameters: left... and rigth... *)
(* returns true, if one is textliteral and the other is ( text or array of char *)
begin

allow_text_literals := (
((leftbasekind  = ctext) and (rigthbasekind = ctext)) or
((leftbasekind  = ctext) and (rigthbasekind = carray) and (rigthelement = char_typename)) or
((rigthbasekind = ctext) and (leftbasekind  = carray) and (leftelement  = char_typename))
);

end;



procedure relation (rel: codes);
(* creates a binary operation node, working on top and nexttop *)
(* in case of errors, the resulting type etc will be set to error-values *)

var rel_error: pass3errors;
begin

resolvetype (nexttop, lefttypename, leftelement, leftbasekind);
resolvetype (stacktop, rigthtypename, rigthelement, rigthbasekind);

rel_error := noerror;

if rigthbasekind = cerrorkind then
leftbasekind := rigthbasekind;  (* suppress error-cascades *)

case leftbasekind of

cscalar:
(* both must be scalars, belonging to the same scalar-type *)
if (leftbasekind <> rigthbasekind) or
(leftelement <> rigthelement) then
rel_error := relation_error;

carray,
ctext,
crecord:
(* both must belong to the same type, and only '=' or '<>' *)
if ((lefttypename <> rigthtypename) and (not allow_text_literals)) or 
((rel <> ceq) and (rel <> cne)) then
rel_error := relation_error
else
(* test that the record does'nt contains system types *)
with names [ lefttypename ] do
with types [ typ ] do  (* typenode of one of the operands *)
if contents * [ t_semaphore, t_pool, t_reference, t_shadow ] <> [] then
rel_error := relation_error;

cpointer:
(* both must be pointers to same type, and only '=' or '<>' *)
(* note: especially used by the monitor-process... *)
if (leftbasekind <> rigthbasekind) or
(leftelement <> rigthelement) or
((rel <> ceq) and (rel <> cne)) then
rel_error := relation_error;

cset:
(* both must be sets, with same elementtypes (or empty) *)
if (leftbasekind <> rigthbasekind) (* both 'set' *) or
((leftelement <> rigthelement) (* with same element-type *) and
(leftelement <> nil_typename) (* allow empty sets *) and
(rigthelement <> nil_typename)) or
(rel = clt) or (rel = cgt) then
rel_error := relation_error;

cerrorkind: ; (* anything allowed *)

cinteger,
creal:
(* simple version: demand same type *)
if leftbasekind <> rigthbasekind then
rel_error := relation_error;

end
otherwise
rel_error := relation_error;

(* perform the binary operation *)
if rel_error <> noerror then
begin
oper (binary, rel, error_typename);
error (rel_error);
end
else
oper (binary, rel, boolean_typename);

end; (* procedure relation *)



procedure arithop (operator: codes);
(* creates a binary operation node, working on stacktop and nexttop *)
(* in case of errors, the resulting type etc will be set to error-values *)

var arith_error: pass3errors;

begin

resolvetype (nexttop, lefttypename, leftelement, leftbasekind);
resolvetype (stacktop, rigthtypename, rigthelement, rigthbasekind);

arith_error := noerror;

if rigthbasekind = cerrorkind then
begin
lefttypename := error_typename;
leftbasekind := rigthbasekind;
end;

if leftbasekind <> rigthbasekind then
arith_error := arithmetic_error
else
case leftbasekind of
cinteger: begin
if not lambda_version and
((operator = cor) or (operator = cand)) then 
arith_error := not_implemented_on_z80;

if operator = cslash then
arith_error := real_division_not_implemented;
lefttypename := integer_typename;  (* let result be base type *)
end;
creal:
arith_error := real_not_implemented;
cset:
if ((leftelement <> rigthelement) (* sets with same elementtypes *) and
(leftelement <> nil_typename) (* allow empty sets *) and
(rigthelement <> nil_typename)) or
(operator = cand) or (operator = cor) or (operator = cxor) or
(operator = cslash) then
arith_error := arithmetic_error
else
if leftelement = nil_typename then   (* if left-operand is empty set then *)
lefttypename := rigthtypename;     (*   use typename from rigth-operand *)

cerrorkind: ; (* allow anything *)

cscalar: if (lefttypename <> rigthtypename) or (lefttypename <> boolean_typename)
or ( (operator <> cand) and (operator <> cor) and (operator <> cxor) ) then
arith_error := arithmetic_error;


end
otherwise
arith_error := arithmetic_error;

if arith_error <> noerror then
begin
oper (binary, operator, error_typename);
error (arith_error);
end
else
oper (binary, operator, lefttypename);

end; (* procedure arithop *)



procedure test_value_compatible (errorcode: pass3errors);
(* implicit params: left... and rigth... *)

begin

if rigthbasekind = cerrorkind then
leftbasekind := rigthbasekind;

case leftbasekind of

carray:
(* allow rigth to be text literal, when left is array of char *)
if not allow_text_literals then
error (errorcode);

cpointer:
(* both must be pointers to same type ( or to 'niltype' ) *)
if (leftbasekind <> rigthbasekind) or
((leftelement <> rigthelement) (* to same type *) and
(leftelement <> nil_typename) (* allow pointers to niltype *) and
(rigthelement <> nil_typename)) then
error (errorcode);

cset:
(* both must be sets, with same componenttypes (or empty) *)
if (leftbasekind <> rigthbasekind)  (* both sets *) or
((leftelement <> rigthelement) (* with same element-type *) and
(rigthelement <> nil_typename)) (* allow rigth to be empty set *) then
error (errorcode);

cerrorkind: ; (* anything allowed *)

end
otherwise
error (errorcode);

end; (* procedure test_value_compatible *)



procedure test_var_compatible (errorcode: pass3errors);
(* implicit params: left... and rigth... *)

begin

if rigthbasekind = cerrorkind then
leftbasekind := rigthbasekind;

case leftbasekind of

cpointer:
(* both must be pointers, to same type (or to 'niltype') *)
if (leftbasekind <> rigthbasekind) or
((leftelement <> rigthelement) (* pointer to same type *) and
(leftelement <> nil_typename) (* allow pointers to niltype *) and
(rigthelement <> nil_typename)) then
(* suppress error message for the 'nil'-function, with actual shadow ... *)
if (leftelement <> nil_typename) or (rigthtypename <> shadow_typename) then
error (errorcode);

cpool:
(* both must be pool, but don't care about 'of-type' *)
if leftbasekind <> rigthbasekind then
error (errorcode);

cerrorkind: ; (* anything allowed *)

end
otherwise
error (errorcode);

end; (* procedure test_var_compatible *)



procedure testinitialize (xtypename: nameptr; errorcode: pass3errors);
(* tests that stacktop may be 'assigned' to a *)
(*  variable of the given type *)

begin

(* special test for 'skipparam' *)
with opands [ stacktop ] do
if opcode <> cskipparam then
begin
(*the type-comparison is omitted in case of 'skipparam' *)

resolve_named_type (xtypename, lefttypename, leftelement, leftbasekind);
resolvetype (stacktop, rigthtypename, rigthelement, rigthbasekind);

if lefttypename <> rigthtypename then
test_value_compatible (errorcode);

end;

end;



procedure testrange (xtypename: nameptr);
(* inserts a 'range-test', in case the type is a subrange *)

var local_typename: nameptr;

begin

local_typename := unstripfrozen (xtypename);
with names [ local_typename ] do  (* namenode of simplified type *)
with types [ typ ] do           (* corresponding typenode *)
if typekind = csubrange then
oper (unary, crange, xtypename);

end;



procedure beginactual;
(* called on the left paranthesis in a parameter-list *)

label 1, 2;

begin

1: (* repeat after parameter-less function-call *)
pushcall; (* because of nested calls *)

(* decide the kind of argument-list: index, parameter, strucconst *)
(* stacktop describes the 'owner' *)

with opands [ stacktop ] do
case opcode of
cid:
with names [ op_name ] do  (* namenode of 'owner' *)
case namekind (* of owner *) of
cprocess,
cprocedure,
cfunction:
begin
prepare_call (stacktop);
if namekind = cfunction then
if empty_paramlist (params) then
begin
(* the function did'nt have any formal parameters *)
(* therefor the arglist must be an index-list     *)
terminate_call (stacktop);  (* make the function-call *)
goto 1;                    (* start all over again *)
(* note: this time the arglist will be taken as an index-list *)
end;
end;
ctype:
begin
(* the list is a struc-constant *)
arglist_owner := op_name;  (* remember the namenode of the type *)
with types [ typ ] do        (* typenode of the type *)
case typekind of
carray:
begin
argkind    := cstrucarray;
curformal  := element;     (* typename of array elements *)
end;
crecord:
begin
argkind    := cstrucrecord;
curformal  := element;     (* first of field-name list *)
end;
end
otherwise
begin
(* only array-types and record-types may be struc-consts *)
argkind := cerrorarg;
error (type_must_be_record_or_array);
end; (* case typekind *)
end; (* case namekind = ctype *)

end (* case namekind *)

otherwise

begin
2: ;   (* the argumentlist must be an index-list *)
argkind    := cindex;
curformal  := op_typename;  (* typename ougth to be name of array-type *)
end;

end (* case opcode *)
otherwise goto 2; (* assume index in all other cases *)

end; (* procedure beginactual *)



procedure act_param;
(* called on: cdoubleparam-cactualparam *)

label 5, 10;

var sonvar_name: nameptr;
loc_tempvar: nameptr;

begin

(* first bind any possible doubleparam to a single one *)
if rectype = cdoubleparam then
begin
(* nexttop must be integer expression *)
assure (nexttop, integer_typename, repetition_type);
testvalue (stacktop);
with opands [ stacktop ] do
oper (binary, cdoubleparam, op_typename (* propagate typename... *) );
end;


(* argkind now describes how to interprete the actualarg *)

case argkind of
cerrorarg,
cindex:
begin
(* curformal is typename of (supposed) array *)
(* test that its typekind is 'carray'        *)

(* doubleparam is not allowed *)
if rectype <> cactualparam then
error (double_param_only_in_struc_const);

curformal := unstripfrozen (curformal);  (* in case the array itself was frozen *)

with names [ curformal ] do     (* namenode of type *)
with types [ typ ] do         (* corresponding typenode *)
begin
if argkind <> cerrorarg then   (* suppress error-cascade *)
case typekind (* of typenode *) of

cerrorkind: ; (* nothing, but suppress error-cascade *)

carray:
begin
with opands [ stacktop ] do
if opcode = cskipparam then
error (skipparam_only_in_struc_const)
else
begin
(* get basekind of indextype of the array *)
resolve_named_type (index, lefttypename, leftelement, leftbasekind);
(* get basekind of actual index expression *)
resolvetype (stacktop, rigthtypename, rigthelement, rigthbasekind);

if lefttypename <> rigthtypename then
error (incompatible_index);
end;
end;
end
otherwise
begin
error (subscript_after_nonarray);
argkind := cerrorarg;
end;

(* bind the index-expression to the owner *)
oper (binary, argkind, element (* typename of array-element *));
with opands [ stacktop ] do
begin
op_name := curformal;  (* type-name of array *)
op_packflag := packflag (* from typenode of the array *);
end;

(* let curformal be typename of array-element *)
curformal := element;

end; (* with curformal *)

end; (* argkind = index *)

cargument:
begin
(* curformal is name in formal param list *)
while curformal <> nilname do
(* skip implicit types and function-return-value *)
with names [ curformal ] do
begin
case namekind (* of formal name *) of
cscalarelem,
ctype,
cundeclared,  (* undeclared typename *)
cfuncval: ; (* skip *)

cvarp:
begin
(* check var parameter *)

(* first test if the actual is a process-name *)
with opands [ stacktop ] do
if opcode = cskipparam then
begin
error (skipparam_only_in_struc_const);
goto 5;
end
else
if opcode = cid then
with names [ op_name ] do  (* namenode of identifier *)
if namekind = cprocess then
begin
(* it was really a process-name *)
(* exchange the identifier with the corresponding son-variable *)
op_name := nextname (* of processname *);
with names [ op_name ] do  (* namenode of son-var *)
op_typename := typename (* of sonvar *)
end;

(* actual must be unpacked variable *)
testvariable (stacktop);
testunpacked (stacktop);

(* the formaltype and the actualtype must match exactly, except for 'frozen' *)
with opands [ stacktop ] do
(* now: typename    = formal typename *)
(*      op-typename = actual typename *)
if typename <> op_typename then
begin
(* try to remove readonly-marks from formal and actual types *)
if unstripfrozen (typename) <> unstripfrozen (op_typename) then
begin
resolve_named_type (typename (* of curformal *), lefttypename, leftelement, leftbasekind);
resolvetype (stacktop, rigthtypename, rigthelement, rigthbasekind);
test_var_compatible (illegal_var_param_substitution);
end
else
(* it helped... but in this case formal must be readonly *)
if typename = unstripfrozen (typename) then
(* formal was'nt frozen, but actual was *)
error ( actual_may_not_be_frozen);
end; (* if not direct match *)
goto 5;
end;

cvaluep:
begin
(* check value parameter *)

(* first test if the actual is a process-call *)
make_call (stacktop);  (* if parameter-less *)
with opands [ stacktop ] do
if opcode = cskipparam then
begin
error (skipparam_only_in_struc_const);
goto 5;
end
else
if opcode = ccallprocess then
begin

count_processparam := count_processparam + 1;

(* insert extra opands to describe the process-'call' as normal argument *)
sonvar_name := op_name;  (* get the name from the 'callprocess'-node *)

(* get a temporary namenode for describing the 'processrec' *)
loc_tempvar := functempvar;  (* save the global variable *)
functempvar := alloc_namenode;
insert_functype (processrec_typename);

(* bind the processcall to the normal paramlist *)
oper (binary, cprocessargument, functempvar (* = temp variable *) );
with opands [ stacktop ] do
op_name := sonvar_name;

(* create an operand, with 'processrec' type *)
newopand (cfunctemp, nilname (* irrell typename *));
with opands [ stacktop ] do
op_name := functempvar;

(* now: stacktop is a functemp-var, of processrec type *)
functempvar := loc_tempvar;  (* restore the global variable *)
end;

with names [ curformal ] do
begin

if typename (* of curformal *) = nil_typename then
begin
(* test certain special std-functions *)

if (arglist_owner = succ_procname)
or (arglist_owner = pred_procname) then
begin
(* actual expression has to be enumeration *)
rigthtypename := testenumeration (stacktop);
insert_functype (rigthtypename);
goto 5;
end;

if arglist_owner = ord_procname then
begin
(* actual expression has to be enumeration *)
rigthtypename := testenumeration (stacktop);
goto 5;
end;

if arglist_owner = abs_procname then
begin
(* actual expression must be integer (or subrange hereof) *)
(* note: in future also real... *)
assure (stacktop, integer_typename, illegal_value_param_substitution);
insert_functype (integer_typename);
goto 5;
end;

end; (* special functions *)

resolve_named_type (typename, lefttypename, leftelement, leftbasekind);
resolvetype (stacktop, rigthtypename, rigthelement, rigthbasekind);

if lefttypename <> rigthtypename then
test_value_compatible (illegal_value_param_substitution);
(* note: implicit params: left... and rigth... *)

testrange (typename (* of curformal *) );
end; (* with curformal *)

goto 5;
end;

end

otherwise

if compilertest then comp_error (unknown_namekind);

curformal := nextname;  (* follow chain *)

end; (* with curformal *)

(* the formal paramlist was exhausted *)
error (too_many_actual_params);

5: ;

(* note: see also 'prepare_call' *)

(* bind the actual parameter to the owner *)
oper (binary, argkind, nilname (* irrell typename *) );
with opands [ stacktop ] do
op_name := curformal;   (* let opname point at current formal name *)

(* let curformal be the next in formal-list *)
with names [ curformal ] do
curformal := nextname;

end;  (* argkind = cargument *)


cstrucarray:
begin
(* curformal is element typename *)
(* check transfer to array-element *)
testinitialize (curformal, struc_arr_incompatible);
oper (binary, argkind, curformal (* = element typename *) );
end;

cstrucrecord:
begin
(* curformal is name in field list *)

while curformal <> nilname do
(* skip implicit types in field list *)
with names [ curformal ] do
begin
if namekind = crecfield then
begin
(* check transfer to field *)
testinitialize (typename, struc_rec_incompatible);
goto 10;
end;
curformal := nextname;
end; (* with curformal *)

error (too_many_values_in_record_struc);

10: ;

(* bind the value to the owner *)
with names [ curformal ] do   (* namenode of field name *)
begin
oper (binary, argkind, curformal (* i.e. fieldname itself *) );
curformal := nextname;
end;

end;


end (* case argkind *)
otherwise
if compilertest then
comp_error (unknown_argkind);

end; (* procedure actparam *)



(*QQQ*)

procedure initialize;

var strptr: stringptr;
err: pass3errors;

begin

(* initialize files *)
reset (pass1file);
reset (pass1labels);
rewrite (pass3file);

(* initialize conversion table *)
for rectype := cnocode to clastcode do
convtable [ ord(rectype) ] := rectype;

(* clear error-bitmap *)
for err := noerror to lastpass3error do
error_bitmap [ err ] := false;

(* initialize stdspix table *)
(* all entries are set to: 'max-spix'... this will ensure *)
(*   that pass1 actually sends out initialization for these *)
for rectype := canonymous to cord do
stdspix (max_spix, rectype);

(* initialize test facilities *)
testinput     := false;
testoutput    := false;
testing       := testinput or testoutput;
testlinecnt   := 0;

call_pass4    := true;   (* default = continue with pass4 *)
lambda_version := true;  (* default = rc3502 *)

date (cur_date);
time (cur_time);
if testing then changeline;

cur_lineno    := 0;
no_of_errors := 0;
cur_opandno := 0;
emit_lineno   := 0;
cur_blockno := process_block;

(* announce own version to next pass *)
emit (xoption);
emitin (0);
emitin (own_passno);
emitin (versionpass3);

(* initialize list structures *)

(* initialize 'names' *)
insert_in_front := false;  (* default is: insert all names in rear *)
freenames := nilname;
for curname := nilname to max_names do
with names [ curname ] do
begin
namekind := cundeclared;
nextname := freenames;
freenames := curname;
end;

(* initialize 'types' *)
freetypename := nilname;
dummy_typenode := max_types;  (* notice: must have the correct value from the very beginning *)
for cur_typenode := niltype to max_types do
begin
with types [ cur_typenode ] do
begin
typekind := cnocode;
subexpr := nilopand;
end;
release_typenode (cur_typenode);
end;

(* initialize 'dummy_typenode' as the very first one *)
cur_typenode := alloc_typenode;
if compilertest then
if dummy_typenode <> cur_typenode then
comp_error (inconsist_alloc);

(* initialize 'strings' *)
freestrings := nilstring;
for strptr := nilstring to max_strings do
with strings [ strptr ] do
begin
nextstr := freestrings;
freestrings := strptr;
end;

(* initialize 'opands' *)
freeopands := nilopand;
for stacktop := nilopand to max_opands do
with opands [ stacktop ] do
begin
next := freeopands;
opcode := cerrorarg;  (* nbnbnbnbnbnb: skal laves bedre ved lejlighed *)
(* initialized because it is used as 'working location' by 'testindex' *)
freeopands := stacktop;
end;

(* initialize variables concerning opands *)
stacktop := nilopand;
nexttop  := nilopand;

(* initialize namestack etc *)
namelevel := lowestlevel - 1;
levelkind := processlevel;
lastgloballevel := namelevel;
newnamelevel;
expecting_block := false;
after_funccall := false;
expr_release := false;
const_decl := false;
retain_top := false;
expr_level := 0;  (* i.e. first 'emitexpr' is beginning of expression *)
pushnameindex := 0;
start_of_protected := false;

(* initialize stack for holding arguments *)
callindex    := 0;
argkind      := cnocode;
curformal    := nilname;
arglist_owner:= nilname;
functempvar  := nilname;
count_processparam := 0;


(* initialize important spix'es *)

(* the following construction matches the main-loop *)
endpass1 := false;

repeat

if testing then changeline;

rectype := password;

case rectype of

cnewline:
cur_lineno := passin;

coption:
readoption;

cstandardname:
standardname;

cendstandards:
endpass1 := true;

end otherwise comp_error (unknown_rectype);

until endpass1;
envir_spix := anonymous_spix; (* used as flag to omit 'typedef' for integer etc *)

(* initialze important name/type nodes *)

error_typename := alloc_namenode;
error_typenode := alloc_typenode;

with names [ error_typename ] do
begin
spix          := anonymous_spix;
namekind      := cerrorkind;
typename      := error_typename;
typ           := error_typenode;
end;

with types [ error_typenode ] do
typekind    := cerrorkind;

with types [ dummy_typenode ] do
typekind    := cnocode;

text_typename := alloc_namenode;
with names [ text_typename ] do
begin
spix     := anonymous_spix;
namekind := ctype;
typ      := alloc_typenode;
with types [ typ ] do
typekind := ctext;
end;

names [ nilname ] := names [ error_typename ];
types [ niltype ] := types [ dummy_typenode ];

set_error_type;
emittype (error_typename);
emittype (text_typename);
prepare_expression;

end; (* procedure initialize *)

\f



(* start of main program *)

begin

initialize;

endpass1 := false;

(* the mainloop reads the next record-keyword *)

repeat (* until endpass1, i.e. eom-record from pass1 *)

if testing then changeline;

rectype := password;

(* switch out, depending on rectype *)

case rectype of

cnewline:
begin
lineno := passin;
if lineno <> cur_lineno then
begin
cur_lineno := lineno;
cur_opandno := 0;  (* let opands be counted from zero *)
emit (xnewline);
emitin (cur_lineno);
emit_lineno := cur_lineno;
end;
end;

coption:
readoption;

(*QQQ*)

cstandardname:
standardname; (* merely to skip the parameters... *)

cendprefix: ; (* blind *)

cinclude,
ccontext:
begin
cur_opandno := cur_opandno + 1;
envir_spix := passin;  (* skip the spix *)
end;

cerrcontext: ; (* blind *)

cendcontext: ; (* blind *)

cendinclude:
begin
lastgloballevel := namelevel;
emit (xendprelude);
end;

cbeginlevel:
begin
(* increase the namelevel, and initialize a new namelist *)
cur_dyn_var_level := namelevel;
newnamelevel;
end;

cendlevel:
begin
(* if any global names have been used at this level, *)
(*   then pretend that they were used at the next, outer, level *)
endnamelevel;
cur_dyn_var_level := namelevel;
end;

(*QQQ*)

cdeclaration:
begin
if expecting_block then
begin_block;
cur_namekind := password;

(* test that 'process' only occurs at process-level *)
(* ... else bhange it to: 'procedure'               *)

if cur_namekind = cprocess then
if cur_blockno <> process_block then
begin
error (process_only_allowed_at_processlevel);
cur_namekind := cprocedure;
end;

if compilertest then
(* test legality of namekind *)
case cur_namekind of
cprocess, cprocedure, cfunction, ctype,
cscalarelem, crecfield, cconstant, cvar,
cvarp, cvaluep,
clabel,
cprefix: ; (* ok *)
end
otherwise
comp_error (unknown_namekind);

(* certain declarations are followed by a literal with the external name *)
(* adjust the opand-number in advance... *)
case cur_namekind of
cprocess,
cprocedure,
cfunction,
cprefix:
cur_opandno := cur_opandno - 1;
end otherwise (* don't care *);

(* prepare endlist-actions, by initializing list-pointers *)
listfirst := nilname;
listlast  := nilname;

end;

cdeclare,
cdeclarelist:
begin
cur_opandno := cur_opandno + 1;
curname := newname ( passin (* = spix *) );

(* insert the name in the list *)
if listfirst = nilname then
listfirst := curname;
listlast := curname;

case cur_namekind of
cprocess,
cprocedure,
cfunction:
begin
(* the name will be followed by a formal list *)
(* remember the routine name *)
cur_routine := curname;
(* the name of the routine was preceded by a literal name *)
(* remember the  opand holding this name *)
opand_procname := take_stacktop;

(* prepare for 'beginlevel': process-level is special *)
if cur_namekind = cprocess then
begin
cur_namekind := cvar;  (* set up for variable *)
curname := newname (anonymous_spix); (* used for 'son-variable *)
with names [ curname ] do
typename := son_typename;
levelkind := processlevel;
end
else
levelkind := irrell_level;

end;

cprefix:
begin
emit (xprefix);
emit_extname (take_stacktop);
end;

clabel:
emitid (xlabelid, curname);

end otherwise; (* nothing *)

end;

(*QQQ*)

cexternal,
cforward:
begin


with names [ cur_routine ] do
begin
if forward_decl then
error (double_declaration) (* i.e. it was already forward declared *)
else

(* set the proper flag and emit the corresponding record *)
case rectype of

cexternal:
begin
external_decl := true;
emit (xexternal);
end;

cforward:
begin
forward_decl := true;
emit (xforward);
end;

end otherwise; (* case rectype *)

end; (* with names *)

expecting_block := false; (* flag to omit 'blockno' *)

end;

(*QQQ*)

cendformallist:

begin

(* 'cur_routine'  is the name of the process/procedure/function *)
(* 'cur_typename' is the name of the common type *)
(* 'cur_typenode' is the corresponding typenode *)

(* 'listfirst' and 'listlast' outpoint the list of formal params *)

paramkind := names [ listfirst ] . namekind;
(* may be value/var param *)

(* check paramkind versus routinekind to test legality of parameter-type *)
with types [ cur_typenode ] do
with names [ cur_routine ] do
begin

not_allowed := [];

case namekind (* of routine *) of

ctype: ; (* not implemented *)

cfunction,
cprocedure:
(* semaphore, reference, shadow, pool not allowed as value param *)
if paramkind = cvaluep then
not_allowed := [ t_semaphore, t_reference, t_shadow, t_pool ];

cprocess:
(* reference, shadow, pool not allowed at all *)
(* semaphore not allowed as value param *)
(* pointers may not be modified by child *)
if testprocessparam (cur_typename, paramkind = cvarp) then
not_allowed := contents (* i.e. provoke error below *)
else
if paramkind = cvaluep then
not_allowed := [ t_semaphore ];

end otherwise; (* case namekind *)

if not_allowed * contents (* of param type *) <> [] then
begin
(* the parameter type was illegal, replace it *)
error (illegal_formal_type);
set_error_type;
end;

end; (* with cur_routine *) (* with cur_typenode *)

(* pass the type to all formals in the list *)

with names [ listlast ] do listtop := nextname;

while listfirst <> listtop do
with names [ listfirst ] do
begin
typename := cur_typename;
listfirst := nextname;
end;

poptype;

end;

(*QQQ*)

cendformal: ; (* blind, the action is delayed until end-routine-decl *)


(*QQQ*)

cendprocdecl,
cendfuncdecl:

begin

(* in case of function, add the type *)

(* emit the routinehead *)

with names [ cur_routine ] do
begin

if rectype = cendfuncdecl then
begin
(* function type may not contain ... *)
with types [ cur_typenode ] do  (* typenode of function type *)
if contents * [ t_semaphore, t_reference, t_shadow, t_pool ] <> [] then
begin
error (illegal_function_type);
set_error_type;
end;

typename (* of function name *) := cur_typename;

(* allocate a namenode for holding the function value *)
cur_namekind := cfuncval;
insert_in_front := true;  (* note: it must be the very first in formal list *)
curname := newname (anonymous_spix);
insert_in_front := false; (* all other names etc are inserted in rear *)
with names [ curname ] do
typename := cur_typename;

poptype;

end; (* if function declaration *)

(* the whole paramlist (including implicit types) are in the *)
(*   current namelevel in namestack *)

(* transfer the paramlist pointer to this routine *)

(* but first compare the new paramlist against the old paramlist *)
(* (in case of earlier forward declaration) *)
if forward_decl then
compare_paramlists; (* the new list must be empty or match exactly *)

with namestack [ namelevel ] do
params (* of cur_routine *) := firstname; (* of namelevel *)

emitroutinehead (cur_routine);

(* the routine is of special interrest, if it is: 'ord', 'succ', etc *)
if spix <= maxstdspix then
specialspix (cur_routine);

end; (* with cur_routine *)

end;

(*QQQ*)

cendconstantdecl:

begin

testvalue (stacktop);

with names [ listfirst ] do (* nb just one name in the list *)
begin
usedlevel := lowestlevel;  (* let the identifier be defined now... *)
with opands [ stacktop ] do
typename (* of constant *) := op_typename;  (*i.e. copy from expression type *)
end;

const_decl := true;
emit_and_release (stacktop);
const_decl := false;

emitid (xconstid, listfirst);

end;



cendtypedecl:

begin

emittype (cur_typename);
emit (xtypedef);

(* the type is of special interrest in case of 'reference' etc *)
with names [ cur_typename ] do
if spix <= maxstdspix then
specialspix (cur_typename);

poptype;

end;



cnoinit,
cinit:
var_initialization := rectype = cinit;



cendvardecl:

begin

(* semaphore and pool may only *)
(*   be declared on process-level *)

if cur_blockno <> process_block then
begin
with types [ cur_typenode ] do
if [ t_semaphore, t_pool ] * contents (* of type *) <> [] then
begin
error (type_may_only_be_used_at_process_level);
set_error_type;
end;
end;

(* transfer the type to all variables in the namelist *)

with names [ listlast ] do listtop := nextname;

while listfirst <> listtop do
begin
emitid (xvarid, listfirst);
with names [ listfirst ] do
begin
typename := cur_typename;
listfirst := nextname;
end;
end;

emittype (cur_typename);

if var_initialization then
begin
testinitialize (cur_typename, var_init_incompatible);
emit_and_release (stacktop);
emit (xinitconst);
end;

emit (xvarlist);

poptype;

end;

(*QQQ*)


cexport,
cexportvalue,
cexportsize,
cexportdisp,
cexportoffset,
cexportaddr:
export_procedure;

cenddeclarations:

begin

(* this record comes at end of each context *)
(*   and at the first 'begin' in a routine *)

(* the separation of these two will be done by 'startlabelscope' *)

afterdecl := true;

(* test that all forward-declared routines have been solved *)
with namestack [ namelevel ] do
curname := firstname;   (* first name in name list *)
while curname <> nilname do
with names [ curname ] do
begin
if forward_decl then
error (forward_not_solved);
curname := nextname;
end;

cur_dyn_var_level := maxint; (* all varianble levels are legal *)

end;



cstartlabelscope,
cendlabelscope:

begin

cur_labelscope := passin;

if afterdecl then
begin
(* this is the 'begin' of a routine *)
afterdecl := false;

if expecting_block then
begin_block; (* because there were no declarations at all *)

if cur_blockno = process_block then
begin
(* process level *)
(* find the relevant exception procedure *)

curname := findname (exception_spix);
with names [ curname ] do (* namenode of exception procedure *)
if namekind <> cprocedure then
curname := exception_procname;
(* nb nb nb nb: test korrekt paramliste *)

emitid (xexception, curname);
end;

emit (xinitblock);

initlabels;

end;

set_label_state (rectype = cstartlabelscope);

end;



cbegincode:
begin
begin_block;
emit_and_release (stacktop);  (* literal == stack appetite *)
emit (xbegincode);
end;



ccodeline:
begin
emit (xcodeline);
lineno := passin;  (* number of chars in line *)
emitin (lineno);
for lineno := lineno downto 1 do emitin (passin);
end;

(*QQQ*)

cendcode,
cendblock:

begin

(* release all local namenodes and their typenodes *)

(* notice: the namenodes of the parameters to a block *)
(*         are released by the surrounding block *)

if rectype = cendcode then
emit (xendcode)
else
emit (xendblock);
if cur_blockno <> process_block then
cur_blockno := cur_blockno - 1;
(* release local declared variables, nb ! only precent if "endblock" *)
if rectype = cendblock then

releasenames (namestack [ namelevel ] . firstname);

(* test that functions have had a chance for assigning a value *)
if rectype = cendblock then
with namestack [ namelevel - 1 ] do  (* parameter-level *)
with names [ firstname ] do        (* first formal name *)
if namekind = cfuncval then      (* it must have been a function *)
if not func_assigned then
error (funcval_not_used);

end;

(*QQQ*)

ctypeid:

begin

(* an existing typenode will be reused *)

pushtype; (* because types may be nested *)

cur_typename := usename (passin (* = spix *) );

with names [ cur_typename ] do

begin

cur_typenode := typ;

(* test that the namenode really was a typename node *)
(* notice: all namenodes are initialized with 'dummy_typenode' *)
(*         while type/error nodes have specific typenodes *)
if cur_typenode = dummy_typenode then
begin
error (not_typename);
set_error_type;
end;

end; (* with cur_typename *)

if cur_namekind = ctype then
begin
(* this is:   <name> = <typename>;  *)
(* setup an 'alias' node *)

(* it is not nescessary to test for recursive use, because it *)
(* has been tested that rigthhand name was the name of a defined type *)

cur_typenode := alloc_typenode;
with types [ cur_typenode ] do
begin
typekind := calias;
element := cur_typename;
index    := types [ names [ element ] . typ ] . index;
contents := types [ names [ element ] . typ ] . contents;
packflag := types [ names [ element ] . typ ] . packflag;
end;

cur_typename := curname;

with names [ cur_typename ] do
typ := cur_typenode;

end; (* if namekind = ctype *)

end;




cendtype: ; (* blind *)

(*QQQ*)

cwithout_data:

begin

(* this special type is used by 'pool definition' to simulate notype *)

pushtype; (* because this type is nested in 'pool-def' *)

cur_typename := nil_typename;
cur_typenode := names [ cur_typename ] . typ;

end;

(*QQQ*)

cnewtype:

begin

(* a new type will be constructed *)

pushtype; (* because types may be nested *)

if cur_namekind <> ctype then
begin
(* implicit type, insert pseudo typename *)

cur_typename := newname (anonymous_spix);

with names [ cur_typename ] do
namekind := ctype;

end

else

begin
(* explicit type, use 'curname' as 'cur_typename' *)

cur_typename := curname;
cur_namekind := cnocode; (* only the outermost newtype is explicit *)

end;

(* prepare the succeding typedefinitions *)
cur_typenode := alloc_typenode;

with names [ cur_typename ] do
typ := cur_typenode;

end;

(*QQQ*)

cscalar,
csubrange,
carray,
crecord,
cset,
cpool,
cpointer,
creadonly,
cinteger,
creal,
cniltype:

begin

(* insert the rectype in the 'cur_typenode' *)
with types [ cur_typenode ] do
typekind := rectype;

(* maybe push the current namelist, in case of inner namelists *)
if rectype = crecord then
begin
pushlist;
newnamelevel;
end;

if rectype = cscalar then
begin
pushlist;
with names [ cur_typename ] do
if spix <> anonymous_spix then   (* keep scalarelems on the correct namelevel *)
endnamelevel;
end;

if rectype = cinteger then
integer_typename := cur_typename;

if rectype = cniltype then
nil_typename := cur_typename;

end;

(*QQQ*)

cendscalar:

begin

(* add the scalar-list to the scalar type node *)

with types [ cur_typenode ] do (* typenode of scalar type *)
begin
element := listfirst;
(* contents := [ t_simple ];  *)
end;

(* insert typename in the whole scalar-list *)
with names [ listlast ] do listtop := nextname;

while listfirst <> listtop do
with names [ listfirst ] do
begin
typename := cur_typename;
listfirst := nextname;
end;

with names [ cur_typename ] do
if spix <> anonymous_spix then  (* keep scalarelems on correct namelevel *)
newnamelevel;

poplist; (* return to outer namelist *)

end;

(*QQQ*)

cendsubrange:

begin

(* the two expressions are in 'stacktop' and 'nexttop' *)

(* the two expressions must be <same> enumeration type *)
lefttypename (* dummy assignment *) := testenumeration (nexttop); (* nb: exprtypename = simplified typename *)
resolvetype (stacktop, rigthtypename, rigthelement, rigthbasekind);
if exprtypename <> rigthtypename then
begin
error (subrange_elems_must_be_enumeration);
exprtypename := error_typename;
end;

(* bind the two expressions to a single one *)
oper (binary, crangefirst, exprtypename);

with types [ cur_typenode ] do
begin
subexpr := take_stacktop; (* remember the two expressions *)
index   := exprtypename;
(* contents:= [ t_simple ];  *)
end;

end;

(*QQQ*)

cendarray:

begin

test_recursive_type;

elemname := cur_typename; poptype;
indexname:= cur_typename; poptype;

testindex (indexname);

with types [ cur_typenode ] do (* typenode of array *)
begin
element := elemname;
index   := indexname;
contents:= types [ names [ elemname ] . typ ] . contents;
(* transfer contents from element type *)
end;

end;

(*QQQ*)

cendfield:

begin

(* types may not be recursive *)
test_recursive_type;

with names [ listlast ] do listtop := nextname;
while listfirst <> listtop do
with names [ listfirst ] do
begin
typename := cur_typename;
listfirst := nextname;
end;

elemname := cur_typename;
poptype; (* return to record typename/typenode *)

(* transfer field-contents to record-contents *)
with types [ cur_typenode ] do (* typenode of record *)
contents := contents + types [ names [ elemname ] . typ ] . contents;

end;

(*QQQ*)

cendrecord:

begin

endnamelevel;

(* transfer the whole current namelist, i.e. the list of field-names *)
(*   and their implicit types, to the record-typenode *)

with types [ cur_typenode ] do (* typenode of record *)
with namestack [ namelevel + 1 ] do (* just terminated field list *)
element := firstname;

poplist; (* return to outer namelist *)

end;

(*QQQ*)

cendset:

begin

(* indexname must be enumeration type *)

indexname := cur_typename;
poptype; (* return to set-type *)

testindex (indexname);

with types [ cur_typenode ] do (* typenode of set *)
begin
element := indexname;
(* contents:= [ t_simple ];  *)
end;

end;

(*QQQ*)

cendpool:

begin

(* test that <cardinality> is <integer> *)

resolvetype (stacktop, exprtypename, exprelement, exprbasekind);
if exprbasekind <> cinteger then
begin
error (pool_cardinality_must_be_integer);
end;

(* test legality of type *)

with types [ cur_typenode ] do (* typenode of pool-sizetype *)
(* it may not contain: semaphore, shadow, reference, pool, pointer *)
if contents * [ t_semaphore, t_reference, t_shadow, t_pool, t_pointer ] <> [] then
begin
error (illegal_pool_type);
set_error_type;
end;

elemname := cur_typename;
poptype; (* return to pool *)

with types [ cur_typenode ] do (* typenode of pool *)
begin
subexpr := take_stacktop;
element := elemname;
contents:= [ t_pool ];
end;

end;

(*QQQ*)

cendreadonly,
cendpointer:

begin

if rectype = cendreadonly then
test_recursive_type;
(* notice: pointer-to-type is not considered recursion *)

elemname := cur_typename;
poptype; (* return to readonly/pointer type *)

with types [ cur_typenode ] do
begin
element := elemname;
if rectype = cendpointer then
contents := [ t_pointer ]
else
contents := types [ names [ element ] . typ ] . contents + [ t_readonly ];
end;

end;

(*QQQ*)

cpacked:

begin
(* 'cur_typename/node' are nodes of record/array (or set) *)

with types [ cur_typenode ] do
packflag := true;

end;

(*QQQ*)

cbegin:
prepare_statement;

cend: ; (* blind *)

clabeldef:

begin

(* label def statement *)
definelabel ( passin (* = scopeno *) , passin (* = spix *) );

end;

(*QQQ*)

ccasestat:
prepare_expression;

ccaseexpr:
begin
pushname (case_typename); (* because case may be nested *)
case_typename := testenumeration (stacktop);
case_typename := exprtypename;  (* nbnbnbnb: get simplified typename *)

emit (xcasestat);

emit_and_release (stacktop);

emit (xcase);
end;


ccaselabel:
begin
(* the caselabel must have same type as the case-expression *)
resolvetype (stacktop, exprtypename, exprelement, exprbasekind);
if case_typename <> exprtypename then
  error (case_incompatible);

emit_and_release (stacktop);

emit (xcaselabel);

end;



ccaselabelrange:
begin
(* both caselabels must match the case-expression *)
equaltypes (nexttop, stacktop, case_typename, case_incompatible);

(* bind the two expressions together *)
oper (binary, crangefirst, nilname (* irrell typename *) );
emit_and_release (stacktop);

emit (xcaserange);

end;



cotherwise:
emit (xotherwise);



ccaselist:
begin
prepare_statement;
emit (xendcaselist);
end;



ccaseelement:
begin
prepare_expression;
emit (xendcasestat);
end;



cendcase:
begin

emit (xendcase);

(* unstack case type name *)
case_typename := popname;

prepare_statement;

end;

(*QQQ*)

cforstat:
prepare_expression;

cforvar:
begin
(* the controlling variable must be: assignable, unpacked variable *)
testassignable (stacktop);
testunpacked (stacktop);
for_typename := testenumeration (stacktop);
for_basetypename := exprtypename;  (* nbnbnbnb: get simplified typename *)

emit (xforstat);
emit_and_release (stacktop);
emit (xfor);

end;



cup,
cdown:
begin
equaltypes (nexttop, stacktop, for_basetypename, for_incompatible);

(* insert a possible range-test after each of the two expressions *)
testrange (for_typename);
for_expr := take_stacktop;
testrange (for_typename);

emit_and_release (stacktop); (* start-value *)
if rectype = cup then
emit (xup)
else
emit (xdown);
emit_and_release (for_expr); (* end-value *)

emit (xdo);

prepare_statement;

end;



cendfor:
emit (xendfor);

(*QQQ*)

cifstat:
prepare_expression;



cifexpr:
begin

assure (stacktop, boolean_typename, if_type);

emit (xifstat);

emit_and_release (stacktop);

emit (xifexpr);

prepare_statement;

end;



celse:
emit (xelse);



cendif:
emit (xif);

(*QQQ*)

crepeatstat:
emit (xrepeat);



cuntil:
prepare_expression;



cendrepeat:
begin

assure (stacktop, boolean_typename, repeat_type);

emit (xuntil);

emit_and_release (stacktop);

emit (xendrepeat);

prepare_statement;

end;

(*QQQ*)

cwhilestat:
begin
emit (xwhile);
prepare_expression;
end;



cwhileexpr:
begin

assure (stacktop, boolean_typename, while_type);

emit_and_release (stacktop);

emit (xwhileexpr);

prepare_statement;

end;



cendwhile:
emit (xendwhile);

(*QQQ*)

clockstat,
cwithcomma,
cwithstat:

begin

case rectype of
clockstat:
begin
start_of_protected := true;  (* prepare: goto not out of lock-statement *)
emit (xlockstat);
levelkind := firstlock;
end;
cwithcomma,
cwithstat:
begin
emit (xwith);
if rectype = cwithstat then
levelkind := firstwith;
end;
end otherwise; (* case *)

prepare_expression;

end;



cwithvar:

begin
(* most of this action is common for lock- and with-statements *)

resolvetype (stacktop, exprtypename, exprelement, exprbasekind);
with opands [ stacktop ] do
with_typename := op_typename;

(* it must be a variable *)
testvariable (stacktop);

newnamelevel;

cur_dyn_var_level := namelevel;

with namestack [ namelevel ] do

begin

(* test the lockvar/withvar *)

case namelistkind of

firstlock,
nextlock:
begin
levelkind := nextlock;
(* variable must be of type reference *)
if exprtypename <> reference_typename then
if exprbasekind <> cerrorkind then  (* suppress if already in error *)
error (lock_type);
end;

firstwith,
nextwith:
begin
levelkind := nextwith;
(* variable must be a record *)
if exprbasekind = crecord then
insert_namelist (exprelement (* = field name list of record *) )
else
if exprbasekind <> cerrorkind then  (* suppress if already in error *)
error(with_type);
end;

end otherwise; (* case namelistkind *)

emit_and_release (stacktop); (* variable *)

if levelkind = nextwith then
begin

(* initialize namelevel *)
withname := alloc_namenode;
with names [ withname ] do
begin
namekind := cvar;  (* pretend variable-kind *)
typename := with_typename;  (* insert typename *)
end;
emitid (xwithvar, withname);

end; (* if with-statement *)

end; (* with namelevel *)

end;



cnolocaldecl: 
cur_dyn_var_level := maxint;



cendlocaldeclare:
begin

with names [ listfirst ] do (* namenode of local var *)
begin

(* test legality of local type *)
with types [ cur_typenode ] do
begin
if contents * [ t_semaphore, t_reference, t_shadow, t_pool ] <> [] then
begin
set_error_type;
error (type_has_systemtypes);
end;

if t_pointer in contents then
warning (type_has_pointers);
end;

typename := cur_typename;

end;

with namestack [ namelevel ] do
begin
withname := listfirst;  (* save the list-start, for later releasenames *)
emitid (xlockvar, withname);  (* notice: first name in namelevel *)
end;

emittype (cur_typename);
emit (xlock);

poptype;

cur_dyn_var_level := maxint;

end;



cdo:
prepare_statement;



cendwith:

begin

repeat

with namestack [ namelevel ] do
begin

case namelistkind of

firstlock,
nextlock:
emit (xendlock);
firstwith,
nextwith:
emit (xendwith);

end otherwise; (* case *)

levelkind := namelistkind;

releasenames (withname);

end; (* with namelevel *)

endnamelevel;

until (levelkind = firstlock) or (levelkind = firstwith);

end;

(*QQQ*)

cgotostat:
prepare_expression;



cendgoto:

begin
with opands [ stacktop ] do
begin

errorcode := check_label (op_name);

if errorcode <> noerror then
  error (errorcode)
else
emitid (xgoto, op_name);

end; (* with stacktop *)
release_expr (take_stacktop);
prepare_statement;
end;

(*QQQ*)

cchannelstat:
begin
start_of_protected := true;  (* prepare: goto not out of channel-statement *)
prepare_expression;
end;



cchanvar:

begin

testvariable (stacktop);
assure (stacktop, reference_typename, channel_type);

emit (xchannel);
emit_and_release (stacktop);
emit (xchanvar);

prepare_statement;

end;



cendchannel:
emit (xendchannel);



cendassign:
begin
(* lefthandside must be assignable variable *)
testassignable (nexttop);
(* the rigthhandside must be a value *)
(* implicitly tested by resolvetype *)
resolvetype (nexttop, lefttypename, leftelement, leftbasekind);
resolvetype (stacktop, rigthtypename, rigthelement, rigthbasekind);
if lefttypename <> rigthtypename then
test_value_compatible (assign_incompatible);
if opands [ nexttop ] . op_typename <> opands [ stacktop ] . op_typename then
with opands [ nexttop ] do
testrange (op_typename);  (* make rangetest, when assigning to subrange *)
oper (binary, rectype, nilname);
emit_and_release (stacktop);
prepare_statement;
end;



cendexchange:
begin
(* both left- and rigthhandside must be assignable variables *)
(* of exactly the same type *)
testexchangable (nexttop);
testexchangable (stacktop);
with opands [ nexttop ] do
(* type must be reference or shadow *)
if (op_typename <> reference_typename) and
(op_typename <> shadow_typename) then
error (exchange_type)
else
if op_typename <> opands [ stacktop ] . op_typename then
error (exchange_incompatible);
oper (binary, rectype, nilname);
emit_and_release (stacktop);
prepare_statement;
end;



cid:
begin

curname := usename (passin (* = spix *) );

if before_assign then
begin
(* be prepared for doing special when assigning to function-name *)

prepare_expression;

with names [ curname ] do   (* namenode of identifier *)
if namekind = cfunction then
(* a function name was used in front of an assignment *)
if retrieved_level < namelevel-1 then
(* test if we are inside the body of the function *)
with namestack [ retrieved_level + 1 ] do  (* level of possible paramlist *)
if firstname (* of namestack *) = params (* of function *) then
(* exchange the function name by the returnvalue name *)
begin
curname := params;  (* first of paramlist *)
with names [ curname ] do
func_assigned := true;
end;

end; (* before assign *)

with names [ curname ] do
begin
if retrieved_level >= cur_dyn_var_level then
begin
if (namekind <> cconstant) and (namekind <> cscalarelem) 
and (namekind <> ctype) then
error( illegal_scope_of_type_component );
end
else
if namekind = crecfield then
begin
(* there has been a previous 'with' *)
(* simulate: <withname>.<fieldname> *)
with namestack [ retrieved_level ] do  (* ... defined by: 'usename' *)
begin
with names [ withname ] do
newopand (cid, typename (* of withname *) );
with opands [ stacktop ] do
op_name := withname;
oper (unary, cfield, typename (* of curname ( = fieldname ) *) );

(* insert the packflag from the record-type of the withvar *)
with names [ withname ] do
with names [ typename ] do   (* namenode of record-type for withvar *)
with types [ typ ] do      (* corresponding typenode *)
with opands [ stacktop ] do
op_packflag := packflag; (* from typenode of withvar *)

end;

end; (* if recfield *)
if namekind <> crecfield then
newopand (cid, typename (* of curname *) );

end; (* with names[ curname ] *)

with opands [ stacktop ] do
op_name := curname;

end; (* cid *)



cliteral:
begin

cur_opandno := cur_opandno + 1;

rectype := password;  (* get type of literal *)

newopand (cliteral, nilname (* typename not defined yet *) );

with opands [ stacktop ] do
begin
if rectype = cinteger then op_typename := integer_typename
else
if rectype = creal    then op_typename := nil_typename
else
if rectype = ctext    then op_typename := text_typename
else
if compilertest then
comp_error (unknown_rectype);

op_value := readstring;
(* midlertidig simplifikation, for at kunne opfatte literal-chars *)
if rectype = ctext then
with strings [ op_value ] do
if stringlgt = 1 then
op_typename := char_typename;
end;

end; (* literal *)



cskipparam:
begin
cur_opandno := cur_opandno + 1;
newopand (cskipparam, nilname (* irrell typename *) );
end;



(* pickup of sets *)
csetlist,
cs_element,
cm_element,
cendsetlist:
set_procedure;



cfield:
begin
(* stacktop contains previous 'variable' *)

cur_opandno := cur_opandno + 1;

newspix := passin;

(* if the previous 'variable' is parameterless functioncall, then... *)
with opands [ stacktop ] do
if opcode = cid then
with names [ op_name ] do  (* namenode of (possible) function name *)
if namekind = cfunction then
begin
pushcall;
prepare_call (stacktop);
terminate_call (stacktop);
end;

resolvetype (stacktop, exprtypename, exprelement, exprbasekind);

with names [ exprtypename ] do   (* namenode of <variable> type *)
with types [ typ ] do          (* corresponding typenode *)
(* note: in case of alias-type, exprelement and exprbasekind are the best ones *)
if exprbasekind <> crecord then
suppress_error (exprbasekind, field_must_follow_recordtype)
else
begin
(* find the spix among the fieldnames of the record *)
curname := exprelement;  (* first of field name list *)
while (newspix <> anonymous_spix (* i.e. not found yet *) )
and (curname <> nilname (* i.e. more names to compare to *) ) do
with names [ curname ] do
if newspix = spix (* of curname *) then
begin
oper (unary, cfield, typename (* of fieldname *) );
with opands [ stacktop ] do
begin
op_name := curname;
op_packflag := packflag; (* from record typenode *)
end;
newspix := anonymous_spix; (* indicate succes *)
end 
else
curname := nextname; (* follow chain, and test next name in list *)

if curname = nilname then
error (name_not_fieldname);

end; (* with names, types etc of stacktop *)

end; (* cfield *)



cuparrow:
begin
(* stacktop is <variable>, which must be pointertype *)

resolvetype (stacktop, exprtypename, exprelement, exprbasekind);
if exprbasekind <> cpointer then
suppress_error (exprbasekind, must_be_pointertype_before_uparrow)
else
oper (unary, cuparrow, exprelement (* follow pointer-type *) );

end; (* cpointer *)



ceq,
cne,
clt,
cle,
cgt,
cge:
relation (rectype);



cand,
cor,
cxor,
cplus,
cminus,
cstar,
cslash:
arithop (rectype);



cendexpression: ; (* blind *)



cbeginactual:
beginactual;



cactualparam,
cdoubleparam:
act_param;



cendactual:
terminate_call (stacktop);



cendvariable:
terminate_funccall (stacktop);



ccallprocedure:
begin
terminate_funccall (stacktop);  (* terminate any erroneous function-call *)
make_call (stacktop); (* make sure even a parameter-less procedure is called *)
(* test that the stacktop identifies a procedure call *)
with opands [ stacktop ] do
if opcode <> cendproc then
error (not_procedure_call);
emit_and_release (stacktop);
prepare_statement;
end;



cuplus,
cuminus:
begin
resolvetype (stacktop, exprtypename, exprelement, exprbasekind);
(* expression must be of integer or real types *)
if (exprbasekind = cinteger) or (exprbasekind = creal) then
oper (unary, rectype, exprtypename)
else
suppress_error (exprbasekind, monadic_error);
end;



cdiv,
cmod:
begin
(* both operands must be integers *)
equaltypes (nexttop, stacktop, integer_typename, arithmetic_error);
oper (binary, rectype, integer_typename);
end;



cnot:
begin
(* stacktop must be of boolean or integer type *)
resolvetype (stacktop, exprtypename, exprelement, exprbasekind);

if not lambda_version and (exprtypename <> boolean_typename) then
error( monadic_error );

if (exprtypename = integer_typename) or ( exprtypename = boolean_typename ) then
oper( unary, rectype, exprtypename )
else
suppress_error( exprbasekind, monadic_error);
end;



cin:
begin
(* rigth opand must be of set-type *)
lefttypename (* dummy assignment *) := testenumeration (nexttop);
(* note: exprtypename is simplified typename of nexttop *)
resolvetype (stacktop, rigthtypename, rigthelement, rigthbasekind);
if (exprbasekind <> cerrorkind) and (rigthbasekind <> cerrorkind) then
if rigthbasekind <> cset then
error (relation_error)
else
if rigthelement <> nil_typename then   (* omit error, if empty set *)
if exprtypename <> rigthelement then
error (relation_error);
oper (binary, cin, boolean_typename);
end;




ceom:
endpass1 := true;



end (* case rectype *)

otherwise
comp_error (unknown_rectype);

until endpass1;






(* end of program *)
999:
terminate;

if call_pass4 then
if lambda_version then
replace ('platonpass4')
else
replace ('pass4z80')
else
writeln ('*** compilation terminated after pass', own_passno:1);
end.
«eof»