home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
sa104os2.zip
/
SATHR104.ZIP
/
SATHER
/
COMPILER
/
PROG.SA
< prev
next >
Wrap
Text File
|
1995-02-13
|
34KB
|
859 lines
-- Copyright (C) International Computer Science Institute, 1994. COPYRIGHT --
-- NOTICE: This code is provided "AS IS" WITHOUT ANY WARRANTY and is subject --
-- to the terms of the SATHER LIBRARY GENERAL PUBLIC LICENSE contained in --
-- the file "Doc/License" of the Sather distribution. The license is also --
-- available from ICSI, 1947 Center St., Suite 600, Berkeley CA 94704, USA. --
--------> Please email comments to "sather-bugs@icsi.berkeley.edu". <----------
-- prog5.sa: Classes relating to entire Sather programs.
-------------------------------------------------------------------
-- PROG: The most common program object.
-- $PROG_ERR:Parent for classes which can identify error locations.
-- PROG_TR_TBL: Table mapping a classname idents to source trees.
-- PROG_PARSE: All files are parsed and tree forms built.
-- PROG_FIND_TYPES: Find all possible types in the program.
-- PROG_TYPE_GRAPH: Build the type graph of all types.
-- PROG_IFC_CONFORMANCE: Check the conformance of interfaces.
-- PROG_GET_MAIN: Determine the signature of main.
-- PROG_AM_GENERATE: Generate the AM form.
-- PROG_AM_CHECK: Check any remaining code.
-------------------------------------------------------------------
class PROG is
attr ident_tbl:IDENT_TBL;
attr tp_tbl:TP_TBL; -- The type table.
attr tp_builtin:TP_BUILTIN; -- Built-in types.
attr ident_builtin:IDENT_BUILTIN; -- Built-in identifiers.
attr tp_graph:TP_GRAPH; -- The type graph.
attr tp_graph_abs_des:TP_GRAPH_ABS_DES; -- Abstract descendants.
attr impl_tbl:IMPL_TBL; -- The implementation table for this
-- program. These entries may take a lot of space and so may be
-- deleted whenever space is needed. They can be regenerated
-- without adverse affects on the rest of the compiler.
attr ifc_tbl:IFC_TBL; -- The table of class interfaces.
attr global_tbl:GLOBAL_TBL; -- Table of globals for this program.
attr prog_parse:PROG_PARSE; -- The code trees.
attr prog_find_types:PROG_FIND_TYPES; -- Find the types.
attr prog_type_graph:PROG_TYPE_GRAPH; -- Build the type graph.
attr prog_ifc_conformance:PROG_IFC_CONFORMANCE; -- Check interfaces.
attr prog_get_main:PROG_GET_MAIN; -- Get main.
attr prog_am_generate:PROG_AM_GENERATE; -- Generate code.
attr prog_am_check:PROG_AM_CHECK; -- Check the remainder.
attr show_parse_file:BOOL; -- Show files parsed.
attr show_tr_insert:BOOL; -- Show trees inserted.
attr show_impl_create:BOOL; -- Show when an impl is created.
attr show_ifc_abs_create:BOOL; -- Show when abstract ifc's are created.
attr show_include:BOOL; -- Show when an include is processed.
attr show_types:BOOL; -- Show all types found.
attr show_graphs:BOOL; -- Show the type graphs.
attr show_ifc:BOOL; -- Show all interfaces.
attr show_main:BOOL; -- Show the main sig.
attr show_generated_sig:BOOL; -- Show the sigs with code generated.
attr show_am:BOOL; -- Show the am code generated.
attr show_checked_sig:BOOL; -- Show the sigs as they are checked.
attr show_am_check:BOOL; -- Show am for for checked sigs.
-- These are to use in the presence of errors:
attr generate_checked_code:BOOL; -- Generate all code if true.
attr back_end:BE; -- The back end
attr options:OPTIONS; -- Command line options
attr all_reached:BOOL; -- True when all reachable code emitted
create:SAME is
-- A new program object.
r::=new;
r.options:=#OPTIONS;
r.tp_tbl:=#(r);
r.tp_builtin:=#(r);
r.ident_builtin:=#(r);
r.tp_graph:=#(r);
r.tp_graph_abs_des:=#(r);
r.impl_tbl:=#(r);
r.ifc_tbl:=#(r);
r.global_tbl:=#(r);
r.prog_parse:=#(r);
r.prog_find_types:=#(r);
r.prog_type_graph:=#(r);
r.prog_ifc_conformance:=#(r);
r.prog_get_main:=#(r);
r.prog_am_generate:=#(r);
r.prog_am_check:=#(r);
return r end;
do_find_types_phase is
-- Find all types in the program, assuming the parse phase has
-- completed without error.
prog_find_types.find_types end;
do_type_graph_phase is
-- Build the type graphs, assuming the find types phase has
-- completed without error.
prog_type_graph.build_graphs end;
do_ifc_conformance_phase is
-- Check interfaces for conformance with parents and children.
prog_ifc_conformance.check_ifc_conformance end;
do_get_main_phase(nm:STR) is
-- Do the get main phase for class name `nm'.
prog_get_main.get_main_sig(nm) end;
do_am_generate_phase is
-- Generate the code.
prog_am_generate.am_generate end;
do_am_check_phase is
-- Check any remaining code for types etc.
all_reached:=true;
prog_am_check.check_code end;
am_ob_def_for_tp(tp:$TP):AM_OB_DEF is
-- The object layout for the type `tp'.
impl::=impl_tbl.impl_of(tp);
if void(impl) then return void end;
return impl.am_ob_def end;
ident_for(s:STR):IDENT is
-- The identifier corresonding to the string `s'.
i::=ident_tbl.get_query(s);
if void(i) then i:=#(s);
ident_tbl:=ident_tbl.insert(i) end;
return i end;
abs_subtype_test(t:$TP, at:TP_CLASS):BOOL
-- Return true if the type `t' is a subtype of the type `at'.
-- `at' must be abstract.
pre at.is_abstract is
return tp_graph.abs_subtype_test(t,at) end;
descendants_of_abs(t:TP_CLASS):FSET{$TP} is
-- A table of the concrete descendants of the abstract type `t'.
if ~void(tp_graph_abs_des) then
return tp_graph_abs_des.des_of(t)
-- else return void end end; -- NLP
end; return void; end; -- NLP
pnames_for(t:TP_CLASS):ARRAY{IDENT}
-- An array of the parameter names for the type `t'. Void if none.
pre ~void(t) is
tr::=tree_for(t.name,t.params.size);
if void(tr) then return void end;
if void(tr.params) then return void end;
r::=#ARRAY{IDENT}(tr.params.size);
pd:TR_PARAM_DEC:=tr.params; i:INT:=0;
loop until!(void(pd)); r.set!(pd.name); pd:=pd.next end;
return r end;
tp_context_for(t:TP_CLASS):TP_CONTEXT
-- The type context appropriate for the body of `t'. Void if
-- `t' is not a known type.
pre ~void(t) is
pn:ARRAY{IDENT}:=pnames_for(t);
ps:INT;
if ~void(pn) then ps:=pn.asize end;
if ps/=t.params.size then return void end;
r::=#TP_CONTEXT(t, pn, t.params, self);
if t.is_abstract then r.is_abs:=true end;
return r end;
tp_kind(t:$TP):INT is
-- One of `TP_KIND::missing_tp', `TP_KIND::val_tp',
-- `TP_KIND::ref_tp', `TP_KIND::abs_tp', or `TP_KIND::ext_tp',
-- `TP_KIND::rout_tp', `TP_KIND::iter_tp'.
-- `TP_KIND::missing_tp' if no such class.
typecase t
when TP_CLASS then
tr:TR_CLASS_DEF:=prog_parse.tree_for(t.name,t.params.size);
if void(tr) then return TP_KIND::missing_tp end;
return tr.kind
when TP_ROUT then return TP_KIND::rout_tp
-- when TP_ITER then return TP_KIND::iter_tp end end; -- NLP
when TP_ITER then return TP_KIND::iter_tp end; return 0; end; -- NLP
attr eloc:SFILE_ID; -- Current error location.
err_loc(t:$PROG_ERR) is
-- Make the node held by
-- `t' be the culprit for the next error, if any. If `t' is void,
-- then don't print a location with the next message.
if void(t) then eloc:=void; return end;
eloc:=t.source end;
set_eloc(l:SFILE_ID) is
-- Set `eloc' to `l'.
eloc:=l end;
attr err_seen:BOOL; -- True if an error has been seen.
attr err_list:FLIST{SFILE_ID};
err(s:STR) is
-- Report an error with `s' as the error
-- string and the last tree node given to `err_loc' as the
-- location. This string shouldn't have information like "Error"
-- and should be an unformatted line of text. It should be a
-- complete sentence beginning with a capital letter and ending
-- with a period. If this is called during a compile, source code
-- will not be generated, but the compile will proceed as far as
-- possible.
err_seen:=true;
if ~void(eloc) then
if err_loc_old(eloc) then return end end;
if ~void(eloc) then
#OUT + eloc.str + ": " end;
#OUT + s + "\n" end;
err_loc_old(l:SFILE_ID):BOOL is
-- Return true if `l' has been seen before, otherwise add
-- it to the list.
i::=0;
if void(err_list) then err_list:=err_list.push(l); return false end;
loop while!(i<err_list.size);
if l=err_list[i] then return true end;
i:=i+1 end;
err_list:=err_list.push(l);
return false end;
tree_head_for(nm:IDENT, num:INT):TR_CLASS_DEF is
-- Return the code tree for the class with name `nm' and the
-- number of type parameters `num'. Return void if no such class.
-- This differs from `tree_for' in that the class `body' may
-- optionally be missing. Operations which only need the header
-- info should call this since in some settings there may be
-- no need to parse the whole class.
return prog_parse.tree_for(nm,num) end;
tree_sigs_for(nm:IDENT, num:INT):TR_CLASS_DEF is
-- Return the code tree for the class with name `nm' and the
-- number of type parameters `num'. Return void if no such
-- class. This differs from `tree_for' in that the "stmts"
-- clause of any routines and iters may optionally be missing.
-- Operations which only access the signature information
-- should call this since in some settings there may be no
-- need to parse the whole class.
return prog_parse.tree_for(nm,num) end;
tree_for(nm:IDENT, num:INT):TR_CLASS_DEF is
-- Return the code tree for the class with name `nm' and the
-- number of type parameters `num'. Return void if no such class.
return prog_parse.tree_for(nm,num) end;
output_am_rout_def(a:AM_ROUT_DEF) is
-- Do whatever is necessary to output the routine definition `a'.
-- This is the main connection to the backend.
if ~all_reached and ~options.only_check then
back_end.output_am_rout_def(a)
end;
end;
create_back_end is
-- Start up back-end. Must occur after tp_tbl has been filled in.
back_end:=#BE(self);
end;
finalize_back_end is
-- Conclude code generation, invoking C compiler etc.
if ~options.only_check then back_end.finalize end;
end;
end; -- class PROG
-------------------------------------------------------------------
type $PROG_ERR is
-- Parent class for classes which can identify error locations.
source:SFILE_ID; -- The origin of a node in a Sather
-- source file.
end;
-------------------------------------------------------------------
class PROG_TR_TBL is
-- Table mapping a classname idents to source trees.
--
-- `get_query(TUP{IDENT,INT}):TR_CLASS_DEF' looks up a class.
-- `test_query(TUP{IDENT,INT}):BOOL' tests for a class.
-- `test(TR_CLASS_DEF):BOOL' tests for a tree.
-- `insert(TR_CLASS_DEF):SAME' inserts a tree.
-- `delete(TR_CLASS_DEF):SAME' deletes a tree.
include FQSET{TUP{IDENT,INT},TR_CLASS_DEF};
query_test(q:TUP{IDENT,INT}, t:TR_CLASS_DEF):BOOL is
-- True if `t' is the type described by `q'.
if void(t) then return false end;
if q.t1/=t.name then return false end;
if q.t2/=t.params.size then return false end;
return true end;
query_hash(q:TUP{IDENT,INT}):INT is
-- A hash value computed from the query types.
return q.t1.hash+1111*q.t2 end;
elt_hash(e:TR_CLASS_DEF):INT is
-- Hash on the types in `e'.
return e.name.hash+1111*e.params.size end;
end; -- class PROG_TR_TBL
-------------------------------------------------------------------
class PROG_PARSE is
-- The phase in which all files are parsed and tree forms built.
-- This phase catches both syntactic errors and multiply defined
-- classes.
attr prog:PROG; -- The program
attr tr_tbl:PROG_TR_TBL; -- The table of code trees.
attr parsed:FSET{STR}; -- Table of already parsed files.
create(p:PROG):SAME is
r::=new; r.prog:=p; return r end;
tree_for(nm:IDENT, num:INT):TR_CLASS_DEF is
-- Return the code tree for the class with name `nm' and the
-- number of type parameters `num'. Return void if no such class.
r::=tr_tbl.get_query(#(nm,num));
if void(r) then
-- If we haven't found it, try the -has files
fn::=prog.options.has.get(nm.str);
if ~void(fn) then
parse(fn);
r:=tr_tbl.get_query(#(nm,num));
end;
end;
if void(r) then
prog.err("There is no class with name " + nm.str + " and " +
num + " parameters.")
end;
return r;
end;
parse(f:STR) is
-- Tell the parser to parse the file `f', put the tree in
-- `tr_tbl'.
if parsed.test(f) then return; end;
parsed:=parsed.insert(f);
if prog.show_parse_file then
#OUT + "(Parse " + f + ") " end;
parser ::= #PARSER(prog, f, prog.options.psather);
if ~void(parser) then
tcd: TR_CLASS_DEF := parser.source_file;
loop until!(void(tcd));
if prog.show_tr_insert then
#OUT + "(Tree for " + tcd.name.str + ") " end;
ntcd:TR_CLASS_DEF:=tcd.next; tcd.next:=void;
hf::=prog.options.has.get(tcd.name.str);
if tr_tbl.test_query(#(tcd.name,tcd.params.size))
or (~void(hf) and hf/=f) then
dup_class_err(tcd)
else tr_tbl:=tr_tbl.insert(tcd) end;
tcd:=ntcd end end end;
dup_class_err(tcd:TR_CLASS_DEF) is
prog.err_loc(tcd);
prog.err("There are two classes with the name " + tcd.name.str +
" and " + tcd.params.size + " parameters.") end;
end; -- class PROG_PARSE
-------------------------------------------------------------------
class PROG_FIND_TYPES is
-- This is the phase which finds all possible types in the program.
-- It starts from the non-parameterized types parsed in the first
-- phase. It produces an IMPL for each such type and determines all
-- types mentioned within it. If any of these types is missing or if
-- any of the builtin types is missing an error is signalled
-- and compilation ends. It causes errors for overloaded name
-- conflicts.
attr prog:PROG; -- The program.
attr err_names:FSET{IDENT}; -- Erroneous names already reported.
attr tp_todo:FSET{$TP}; -- Table of types which must still be
-- examined to get other types.
attr tp_done:FSET{$TP}; -- Table of types which have already
-- been examined to get other types.
attr con:TP_CONTEXT; -- Context in which to interpret types.
create(p:PROG):SAME is
r::=new; r.prog:=p; return r end;
tree_for(nm:IDENT, num:INT):TR_CLASS_DEF is
-- Return the code tree for the class with name `nm' and the
-- number of type parameters `num'. Return void if no such class.
return prog.prog_parse.tree_for(nm,num) end;
find_types is
-- Walk through all the code trees and find all the types referred
-- to and put them in `tp_done'. Cause errors for any types
-- referred to but not existing.
loop
got_all:BOOL:=true;
loop
tcd:TR_CLASS_DEF:=prog.prog_parse.tr_tbl.elt!;
if tcd.params.size=0 then -- Non-parameterized.
tp:TP_CLASS:=prog.tp_tbl.tp_class_for(tcd.name,void);
if ~tp_done.test(tp) then
got_all:=false;
con:=prog.tp_context_for(tp);
tp_done:=tp_done.insert(tp); -- Mark this type as done.
do_tps(tcd); -- Do types in the header and the
-- include clauses.
if tp.is_abstract then do_abs(tcd)
else do_impl(prog.impl_tbl.impl_of(tp)) -- Do elts.
end;
end;
end;
end;
until!(got_all);
end;
-- At this point we have done all the non-parameterized
-- classes. Now we go through `tp_todo' to see which
-- parameterizations we need.
fe:$TP:=tp_todo.first_elt;
loop while!(~void(fe)); tp_todo:=tp_todo.delete(fe);
if ~tp_done.test(fe) then -- Must be parameterized and
-- must be a class type, and must have tree for it.
ctp:TP_CLASS;
typecase fe when TP_CLASS then ctp:=fe end;
tcd:TR_CLASS_DEF:=tree_for(ctp.name,ctp.params.size);
con:=prog.tp_context_for(ctp);
tp_done:=tp_done.insert(ctp); -- Mark this type as done.
if ~void(tcd) then
do_tps(tcd); -- Do types in the header and the
-- include clauses.
if ctp.is_abstract then do_abs(tcd)
else do_impl(prog.impl_tbl.impl_of(ctp)) end; -- Elements.
end;
end;
fe:=tp_todo.first_elt end;
if prog.show_types then tbl_out end
end;
process_tp(t:$TP)
-- If there isn't a class with the right name and number of
-- parameters for `t' and all of its subtypes, then print an
-- error if it isn't already in `err_names'. Otherwise, if
-- it is a parameterized class type and its not already in `tp_done'
-- then put it in `tp_todo'.
pre ~void(t) is
typecase t
when TP_CLASS then
nm:IDENT:=t.name; pnum:INT:=t.params.size;
if void(tree_for(nm,pnum)) then
if ~err_names.test(nm) then
prog.err("There is no source class for the type " + t.str);
err_names:=err_names.insert(nm) end;
elsif pnum>0 then -- Is parameterized.
if ~tp_done.test(t) then -- Not done yet.
tp_todo:=tp_todo.insert(t);
i:INT:=0;
loop while!(i<pnum); process_tp(t.params[i]);
i:=i+1 end end end;
when TP_ROUT then
if ~tp_done.test(t) then -- Not done yet.
tp_done:=tp_done.insert(t);
i:INT:=0;
if ~void(t.args) then
loop while!(i<t.args.size); process_tp(t.args[i]);
i:=i+1 end end;
if ~void(t.ret) then process_tp(t.ret); end;
end;
when TP_ITER then
if ~tp_done.test(t) then -- Not done yet.
tp_done:=tp_done.insert(t);
i:INT:=0;
if ~void(t.args) then
loop while!(i<t.args.size); process_tp(t.args[i]);
i:=i+1 end end;
if ~void(t.ret) then process_tp(t.ret); end;
end end end;
do_tps(tr:$TR_NODE) is
-- Find all the types mentioned in `tr' and interpret them via
-- `con'. If a class with the right name and number of parameters
-- doesn't exist, then print an error if the class name isn't
-- already in `err_names'. If it is a parameterized
-- class and it's not already in `tp_done', then put it in
-- `tp_todo'.
-- Have to do this on TR_CLASS_DEF and individual class elts
-- separately (so that includes are properly taken care of.
if void(tr) then return end;
prog.err_loc(tr); -- Set error location.
typecase tr
when TR_CLASS_DEF then do_tps(tr.params); do_tps(tr.under);
do_tps(tr.over); b:$TR_CLASS_ELT:=tr.body;
loop while!(~void(b)); -- Just do the "includes" here.
typecase b
when TR_INCLUDE_CLAUSE then do_tps(b)
else end;
b:=b.next end;
when TR_PARAM_DEC then do_tps(tr.type_constraint); do_tps(tr.next);
when TR_TYPE_SPEC then process_tp(con.tp_of(tr)); do_tps(tr.next);
when TR_CONST_DEF then do_tps(tr.tp); do_tps(tr.init);
when TR_SHARED_DEF then do_tps(tr.tp); do_tps(tr.init);
when TR_ATTR_DEF then do_tps(tr.tp);
when TR_ROUT_DEF then do_tps(tr.args_dec); do_tps(tr.ret_dec);
do_tps(tr.pre_e); do_tps(tr.post_e); do_tps(tr.stmts);
when TR_ARG_DEC then do_tps(tr.tp); do_tps(tr.next);
when TR_INCLUDE_CLAUSE then do_tps(tr.tp);
when TR_DEC_STMT then do_tps(tr.tp); do_tps(tr.next);
when TR_ASSIGN_STMT then do_tps(tr.lhs_expr); do_tps(tr.tp);
do_tps(tr.rhs); do_tps(tr.next);
when TR_IF_STMT then do_tps(tr.test); do_tps(tr.then_part);
do_tps(tr.else_part); do_tps(tr.next);
when TR_LOOP_STMT then do_tps(tr.body); do_tps(tr.next);
when TR_RETURN_STMT then do_tps(tr.val); do_tps(tr.next);
when TR_YIELD_STMT then do_tps(tr.val); do_tps(tr.next);
when TR_QUIT_STMT then do_tps(tr.next);
when TR_CASE_STMT then do_tps(tr.test); do_tps(tr.when_part);
do_tps(tr.else_part); do_tps(tr.next);
when TR_CASE_WHEN then do_tps(tr.val); do_tps(tr.then_part);
do_tps(tr.next);
when TR_TYPECASE_STMT then do_tps(tr.when_part);
do_tps(tr.else_part); do_tps(tr.next);
when TR_TYPECASE_WHEN then do_tps(tr.tp); do_tps(tr.then_part);
do_tps(tr.next);
when TR_ASSERT_STMT then do_tps(tr.test); do_tps(tr.next);
when TR_PROTECT_STMT then do_tps(tr.stmts); do_tps(tr.when_part);
do_tps(tr.else_part); do_tps(tr.next);
when TR_PROTECT_WHEN then do_tps(tr.tp); do_tps(tr.then_part);
do_tps(tr.next);
when TR_RAISE_STMT then do_tps(tr.val); do_tps(tr.next);
when TR_EXPR_STMT then do_tps(tr.e); do_tps(tr.next);
when TR_SELF_EXPR then do_tps(tr.next);
when TR_CALL_EXPR then do_tps(tr.ob); do_tps(tr.tp);
do_tps(tr.args); do_tps(tr.next);
when TR_VOID_EXPR then do_tps(tr.next);
when TR_IS_VOID_EXPR then do_tps(tr.arg);
when TR_ARRAY_EXPR then do_tps(tr.elts); do_tps(tr.next);
when TR_CREATE_EXPR then do_tps(tr.tp); do_tps(tr.elts);
do_tps(tr.next);
when TR_BOUND_CREATE_EXPR then do_tps(tr.call); do_tps(tr.ret);
do_tps(tr.next);
when TR_UNDERSCORE_ARG then do_tps(tr.tp); do_tps(tr.next);
when TR_AND_EXPR then do_tps(tr.e1); do_tps(tr.e2); do_tps(tr.next);
when TR_OR_EXPR then do_tps(tr.e1); do_tps(tr.e2); do_tps(tr.next);
when TR_EXCEPT_EXPR then do_tps(tr.next);
when TR_NEW_EXPR then do_tps(tr.arg); do_tps(tr.next);
when TR_INITIAL_EXPR then do_tps(tr.e); do_tps(tr.next);
when TR_BREAK_EXPR then do_tps(tr.next);
when TR_RESULT_EXPR then do_tps(tr.next);
when TR_BOOL_LIT_EXPR then do_tps(tr.next);
when TR_CHAR_LIT_EXPR then do_tps(tr.next);
when TR_STR_LIT_EXPR then do_tps(tr.next);
when TR_INT_LIT_EXPR then do_tps(tr.next);
when TR_FLT_LIT_EXPR then do_tps(tr.next);
end end;
do_impl(im:IMPL) is
-- Find all types in just the elements of the implementation
-- `im'. Need to do the header and include types separately.
if void(im) then return end;
loop e:ELT:=im.elts.elt!; con:=e.con; do_tps(e.tr) end end;
do_abs(tcd:TR_CLASS_DEF) is
-- Find all types in the body of an abstract class.
tb:$TR_CLASS_ELT:=tcd.body;
loop until!(void(tb)); do_tps(tb); tb:=tb.next end end;
tbl_out is
-- Output the tables for debugging.
#OUT + "\n\nPROG_FIND_TYPES=";
if ~void(tp_done) then
loop #OUT + " ".separate!(tp_done.elt!.str) end end;
#OUT + "\n\n" end;
end; -- class PROG_FIND_TYPES
-------------------------------------------------------------------
class PROG_TYPE_GRAPH is
-- This phase builds the type graph of the types found above.
-- It causes errors if there are loops.
attr prog:PROG; -- The program.
create(p:PROG):SAME is
r::=new; r.prog:=p; return r end;
build_graphs is
-- Build the type graphs for all types in the program.
loop tp:$TP:=prog.prog_find_types.tp_done.elt!;
typecase tp
when TP_CLASS then
ig1::=prog.tp_graph.anc.get_anc(tp);
ig2::=prog.tp_graph.des.get_des(tp)
else end end;
prog.tp_graph_abs_des.do_tbl;
if prog.show_graphs then
anc_out; des_out; abs_des_out end end;
anc_out is
-- The ancestors of all types in `tp_done' with some.
loop tp:$TP:=prog.prog_find_types.tp_done.elt!;
at:FSET{TP_CLASS}:=void;
typecase tp
when TP_CLASS then at:=prog.tp_graph.anc.get_anc(tp)
else end;
if ~void(at) then
#OUT + "Ancestors of " + tp.str + "=";
loop #OUT + " ".separate!(at.elt!.str) end;
#OUT + "\n" end end;
#OUT + "\n" end;
des_out is
-- The descendants of all types in `tp_done' with some.
loop tp:$TP:=prog.prog_find_types.tp_done.elt!;
at:FSET{$TP}:=void;
typecase tp
when TP_CLASS then at:=prog.tp_graph.des.get_des(tp)
else end;
if ~void(at) then
#OUT + "Descendants of " + tp.str + "=";
loop #OUT + " ".separate!(at.elt!.str) end;
#OUT + "\n" end end;
#OUT + "\n" end;
abs_des_out is
-- The concrete descendants of abstract types.
loop p::=prog.tp_graph_abs_des.tbl.pairs!;
#OUT + "The abstract type " + p.t1.str +
" has concrete descendants ";
loop #OUT + " ".separate!(p.t2.elt!.str) end;
#OUT + "\n" end;
#OUT + "\n" end;
end; -- class PROG_TYPE_GRAPH
-------------------------------------------------------------------
class PROG_IFC_CONFORMANCE is
-- This phase checks the conformance of interfaces for each
-- type against its parents and children.
attr prog:PROG; -- The program.
create(p:PROG):SAME is
r::=new; r.prog:=p; return r end;
check_ifc_conformance is
-- Check all type interfaces for conformance to their ancestors
-- and descendants.
prog.err_loc(void); -- Don't print a source location for these.
loop tp::=prog.prog_find_types.tp_done.elt!;
typecase tp
when TP_CLASS then
ti::=prog.ifc_tbl.ifc_of(tp);
if void(ti) then
prog.err("Can't compute interface of "+ tp.str + ".");
return end;
if prog.show_ifc then ti.show end;
loop par:TP_CLASS:=prog.tp_graph.anc.get_parents(tp).elt!;
ncs:SIG:=ti.nonconforming_sig(prog.ifc_tbl.ifc_of(par));
if ~void(ncs) then
prog.err("The interface of type " + tp.str +
" doesn't have a signature conforming to " +
ncs.str + " in its parent " + par.str + ".") end end;
loop chld:$TP:=prog.tp_graph.des.get_children(tp).elt!;
typecase chld
when TP_CLASS then
if ~prog.ifc_tbl.ifc_of(chld).conforms_to(ti) then
prog.err("The interface of type " + tp.str +
" isn't conformed to by the child " +
chld.str + ".") end;
else break! end; -- Bail out for non-class types.
-- Do right later.
end;
else end;
end;
end;
end; -- class PROG_IFC_CONFORMANCE
-------------------------------------------------------------------
class PROG_GET_MAIN is
-- This phase determines the signature of main.
attr prog:PROG; -- The program.
attr main_sig:SIG; -- The signature of `main'.
create(p:PROG):SAME is
r::=new; r.prog:=p; return r end;
get_main_sig(nm:STR) is
-- Get the signature of the main routine in the class named `nm'
-- and put it in `main_sig'.
if void(prog.prog_parse.tree_for(prog.ident_for(nm),0)) then
prog.err_loc(void);
prog.err("There is no type " + nm + " for main."); return end;
mt::=prog.tp_tbl.tp_class_for(prog.ident_for(nm),void);
im:IMPL; im:=prog.impl_tbl.impl_of(mt);
ifc:IFC:=prog.ifc_tbl.ifc_of(mt); if void(ifc) then return end;
msig,omsig:SIG;
loop msig:=ifc.sigs.get_query!(prog.ident_builtin.main_ident);
if ~void(omsig) then
prog.err(
"There may only be one `main' routine in the main class.");
return end;
omsig:=msig end;
if void(msig) then
prog.err("No routine named `main' in " + ifc.tp.str + ".");
return end;
if ~void(msig.args) then
if msig.args.size/=1 or msig.args[0]/=prog.tp_builtin.arr_of_str
then prog.err("The signature of main: " + msig.str +
" doesn't have legal arguments."); return end;
elsif ~void(msig.ret) and msig.ret/=prog.tp_builtin.int then
prog.err("The signature of main: " + msig.str +
" doesn't have a legal return type."); return end;
if prog.show_main then #OUT + "Main sig=" + msig.str + "\n" end;
main_sig:=msig end;
end; -- class PROG_GET_MAIN
-------------------------------------------------------------------
class PROG_AM_GENERATE is
-- This phase does a code walk from main and generates the AM form
-- It causes errors for failures of type checking, name clashes,
-- etc.
attr prog:PROG; -- The program.
attr sig_tbl:SIG_TBL; -- Table of signatures which have been
-- output.
attr sig_list:FLIST{SIG}; -- Signatures to still consider
-- outputting.
attr inline_tbl:INLINE_TBL; -- Table for inlining.
create(p:PROG):SAME is
r::=new; r.prog:=p; r.inline_tbl:=#(p); return r end;
am_generate is
-- Generate all the code.
mn:SIG:=prog.prog_get_main.main_sig;
if void(mn) then return end;
if prog.show_generated_sig then
#OUT + "Output sig " + mn.str + "\n" end;
output_sig(mn);
output_externals_with_bodies;
loop until!(sig_list.is_empty);
s:SIG:=sig_list.pop;
if prog.show_generated_sig then
#OUT + "Output sig " + s.str + "\n" end;
output_sig(s) end end;
output_sig(s:SIG)
-- Transform and output the signature `s' if it hasn't already
-- been done. Put the routines and iters that it calls on
-- `sig_list'. Now searches depth first on routines, so these
-- don't get put on the list.
-- Puts information in the inline table for inlining.
pre ~void(s) is
if ~void(sig_tbl.sig_eq_to(s)) then return end;
if s.tp.is_abstract then output_abs_sig(s); return end;
sig_tbl:=sig_tbl.insert(s);
elt:ELT:=prog.impl_tbl.impl_of(s.tp).elts.elt_with_sig(s);
if elt.is_external and elt.is_abstract then return end;
if void(elt) then return end;
am:AM_ROUT_DEF:=TRANS::transform_elt(elt);
if void(am) then return end;
inline_tbl:=inline_tbl.test_and_insert(am);
if prog.show_am then
#OUT + "\nAM for " + am.sig.str + "=";
AM_OUT::AM_ROUT_DEF_out(am); #OUT + "\n" end;
i:INT;
loop while!(i<am.calls.size);
c:$AM_EXPR:=am.calls[i];
typecase c
when AM_ROUT_CALL_EXPR then
if void(sig_tbl.sig_eq_to(c.fun)) then
sig_list:=sig_list.push(c.fun) end;
when AM_ITER_CALL_EXPR then
if void(sig_tbl.sig_eq_to(c.fun)) then
sig_list:=sig_list.push(c.fun) end;
when AM_BND_CREATE_EXPR then
if void(sig_tbl.sig_eq_to(c.fun)) then
sig_list:=sig_list.push(c.fun) end;
else end;
i:=i+1 end;
if ~prog.err_seen then
prog.output_am_rout_def(am) end end;
output_abs_sig(s:SIG)
-- Do the output for the abstract call `s'.
pre ~void(s) is
if ~void(sig_tbl.sig_eq_to(s)) then return end;
sig_tbl:=sig_tbl.insert(s);
am:AM_ROUT_DEF:=#AM_ROUT_DEF(1+s.args.size,void);
am.sig:=s; am.is_abstract:=true;
stp:TP_CLASS; ostp::=s.tp;
typecase ostp when TP_CLASS then stp:=ostp end;
loop tp::=prog.descendants_of_abs(stp).elt!;
ifc:IFC:=prog.ifc_tbl.ifc_of(tp);
cs:SIG:=ifc.sig_conforming_to(s); -- The call in the descendant.
sig_list:=sig_list.push(cs) end;
if prog.show_am then
#OUT + "\nAM for " + am.sig.str + "=";
AM_OUT::AM_ROUT_DEF_out(am); #OUT + "\n" end;
if ~prog.err_seen then
prog.output_am_rout_def(am) end end;
output_externals_with_bodies is
loop
tp::=prog.tp_tbl.class_tbl.elt!;
if tp.kind=TP_KIND::ext_tp then
et::=prog.impl_tbl.impl_of(tp).elts;
sig_tbl::=prog.ifc_tbl.ifc_of(tp).sigs;
loop
sig::=sig_tbl.elt!;
elt::=et.elt_with_sig(sig);
if ~elt.is_abstract then output_sig(sig) end;
end;
end;
end;
end;
end; -- class PROG_AM_GENERATE
-------------------------------------------------------------------
class PROG_AM_CHECK is
-- Check the code for routines which aren't called, but don't output
-- any am code for them.
attr prog:PROG; -- The program.
attr sig_tbl:SIG_TBL; -- Table of signatures which have been
-- output.
create(p:PROG):SAME is
r::=new; r.prog:=p; return r end;
check_code is
sig_tbl:=prog.prog_am_generate.sig_tbl; -- Start with those already
-- checked.
loop tp:$TP:=prog.prog_find_types.tp_done.elt!;
typecase tp
when TP_CLASS then
if ~tp.is_abstract then
ti::=prog.ifc_tbl.ifc_of(tp);
if ~void(ti) and ~void(ti.sigs) then
loop check_sig(ti.sigs.elt!) end;
end;
end;
else end;
end;
end;
check_sig(s:SIG)
-- Check `s' for errors, but only generate code for it if
-- `generate_checked_code' is true.
pre ~void(s) is
if ~void(sig_tbl.sig_eq_to(s)) then return end; -- Already did it.
sig_tbl:=sig_tbl.insert(s); -- Mark it done.
if prog.show_checked_sig then
#OUT + "Check sig " + s.str + "\n" end;
if s.tp.is_abstract then return end;
elt:ELT:=prog.impl_tbl.impl_of(s.tp).elts.elt_with_sig(s);
if elt.is_external and elt.is_abstract then return end;
if void(elt) then return end;
am:AM_ROUT_DEF:=TRANS::transform_elt(elt);
if void(am) then return end;
if prog.show_am_check then
#OUT + "\nCheck AM for " + am.sig.str + "=";
AM_OUT::AM_ROUT_DEF_out(am); #OUT + "\n" end;
if prog.generate_checked_code then
prog.output_am_rout_def(am) end end;
end; -- class PROG_AM_CHECK
-------------------------------------------------------------------