Files
mercury/compiler/prog_io_util.m
Fergus Henderson 11d8161692 Add support for nested modules.
Estimated hours taken: 50

Add support for nested modules.

- module names may themselves be module-qualified
- modules may contain `:- include_module' declarations
  which name sub-modules
- a sub-module has access to all the declarations in the
  parent module (including its implementation section).

This support is not yet complete; see the BUGS and LIMITATIONS below.

LIMITATIONS
- source file names must match module names
	(just as they did previously)
- mmc doesn't allow path names on the command line any more
	(e.g. `mmc --make-int ../library/foo.m').
- import_module declarations must use the fully-qualified module name
- module qualifiers must use the fully-qualified module name
- no support for root-qualified module names
	(e.g. `:parent:child' instead of `parent:child').
- modules may not be physically nested (only logical nesting, via
  `include_module').

BUGS
- doesn't check that the parent module is imported/used before allowing
	import/use of its sub-modules.
- doesn't check that there is an include_module declaration in the
	parent for each module claiming to be a child of that parent
- privacy of private modules is not enforced

-------------------

NEWS:
	Mention that we support nested modules.

library/ops.m:
library/nc_builtin.nl:
library/sp_builtin.nl:
compiler/mercury_to_mercury.m:
	Add `include_module' as a new prefix operator.
	Change the associativity of `:' from xfy to yfx
	(since this made parsing module qualifiers slightly easier).

compiler/prog_data.m:
	Add new `include_module' declaration.
	Change the `module_name' and `module_specifier' types
	from strings to sym_names, so that module names can
	themselves be module qualified.

compiler/modules.m:
	Add predicates module_name_to_file_name/2 and
	file_name_to_module_name/2.
	Lots of changes to handle parent module dependencies,
	to create parent interface (`.int0') files, to read them in,
	to output correct dependencies information for them to the
	`.d' and `.dep' files, etc.
	Rewrite a lot of the code to improve the readability
	(add comments, use subroutines, better variable names).
	Also fix a couple of bugs:
	- generate_dependencies was using the transitive implementation
	  dependencies rather than the transitive interface dependencies
	  to compute the `.int3' dependencies when writing `.d' files
	  (this bug was introduced during crs's changes to support
	  `.trans_opt' files)
	- when creating the `.int' file, it was reading in the
	  interfaces for modules imported in the implementation section,
	  not just those in the interface section.
	  This meant that the compiler missed a lot of errors.

library/graph.m:
library/lexer.m:
library/term.m:
library/term_io.m:
library/varset.m:
compiler/*.m:
	Add `:- import_module' declarations to the interface needed
	by declarations in the interface.  (The previous version
	of the compiler did not detect these missing interface imports,
	due to the above-mentioned bug in modules.m.)

compiler/mercury_compile.m:
compiler/intermod.m:
	Change mercury_compile__maybe_grab_optfiles and
	intermod__grab_optfiles so that they grab the opt files for
	parent modules as well as the ones for imported modules.

compiler/mercury_compile.m:
	Minor changes to handle parent module dependencies.
	(Also improve the wording of the warning about trans-opt
	dependencies.)

compiler/make_hlds.m:
compiler/module_qual.m:
	Ignore `:- include_module' declarations.

compiler/module_qual.m:
	A couple of small changes to handle nested module names.

compiler/prog_out.m:
compiler/prog_util.m:
	Add new predicates string_to_sym_name/3 (prog_util.m) and
	sym_name_to_string/{2,3} (prog_out.m).

compiler/*.m:
	Replace many occurrences of `string' with `module_name'.
	Change code that prints out module names or converts
	them to strings or filenames to handle the fact that
	module names are now sym_names intead of strings.
	Also change a few places (e.g. in intermod.m, hlds_module.m)
	where the code assumed that any qualified symbol was
	fully-qualified.

compiler/prog_io.m:
compiler/prog_io_goal.m:
	Move sym_name_and_args/3, parse_qualified_term/4 and
	parse_qualified_term/5 preds from prog_io_goal.m to prog_io.m,
	since they are very similar to the parse_symbol_name/2 predicate
	already in prog_io.m.  Rewrite these predicates, both
	to improve maintainability, and to handle the newly
	allowed syntax (module-qualified module names).
	Rename parse_qualified_term/5 as `parse_implicit_qualified_term'.

compiler/prog_io.m:
	Rewrite the handling of `:- module' and `:- end_module'
	declarations, so that it can handle nested modules.
	Add code to parse `include_module' declarations.

compiler/prog_util.m:
compiler/*.m:
	Add new predicates mercury_public_builtin_module/1 and
	mercury_private_builtin_module/1 in prog_util.m.
	Change most of the hard-coded occurrences of "mercury_builtin"
	to call mercury_private_builtin_module/1 or
	mercury_public_builtin_module/1 or both.

compiler/llds_out.m:
	Add llds_out__sym_name_mangle/2, for mangling module names.

compiler/special_pred.m:
compiler/mode_util.m:
compiler/clause_to_proc.m:
compiler/prog_io_goal.m:
compiler/lambda.m:
compiler/polymorphism.m:
	Move the predicates in_mode/1, out_mode/1, and uo_mode/1
	from special_pred.m to mode_util.m, and change various
	hard-coded definitions to instead call these predicates.

compiler/polymorphism.m:
	Ensure that the type names `type_info' and `typeclass_info' are
	module-qualified in the generated code.  This avoids a problem
	where the code generated by polymorphism.m was not considered
	type-correct, due to the type `type_info' not matching
	`mercury_builtin:type_info'.

compiler/check_typeclass.m:
	Simplify the code for check_instance_pred and
	get_matching_instance_pred_ids.

compiler/mercury_compile.m:
compiler/modules.m:
	Disallow directory names in command-line arguments.

compiler/options.m:
compiler/handle_options.m:
compiler/mercury_compile.m:
compiler/modules.m:
	Add a `--make-private-interface' option.
	The private interface file `<module>.int0' contains
	all the declarations in the module; it is used for
	compiling sub-modules.

scripts/Mmake.rules:
scripts/Mmake.vars.in:
	Add support for creating `.int0' and `.date0' files
	by invoking mmc with `--make-private-interface'.

doc/user_guide.texi:
	Document `--make-private-interface' and the `.int0'
	and `.date0' file extensions.

doc/reference_manual.texi:
	Document nested modules.

util/mdemangle.c:
profiler/demangle.m:
	Demangle names with multiple module qualifiers.

tests/general/Mmakefile:
tests/general/string_format_test.m:
tests/general/string_format_test.exp:
tests/general/string__format_test.m:
tests/general/string__format_test.exp:
tests/general/.cvsignore:
	Change the `:- module string__format_test' declaration in
	`string__format_test.m' to `:- module string_format_test',
	because with the original declaration the `__' was taken
	as a module qualifier, which lead to an error message.
	Hence rename the file accordingly, to avoid the warning
	about file name not matching module name.

tests/invalid/Mmakefile:
tests/invalid/missing_interface_import.m:
tests/invalid/missing_interface_import.err_exp:
	Regression test to check that the compiler reports
	errors for missing `import_module' in the interface section.

tests/invalid/*.err_exp:
tests/warnings/unused_args_test.exp:
tests/warnings/unused_import.exp:
	Update the expected diagnostics output for the test cases to
	reflect a few minor changes to the warning messages.

tests/hard_coded/Mmakefile:
tests/hard_coded/parent.m:
tests/hard_coded/parent.child.m:
tests/hard_coded/parent.exp:
tests/hard_coded/parent2.m:
tests/hard_coded/parent2.child.m:
tests/hard_coded/parent2.exp:
	Two simple tests case for the use of nested modules with
	separate compilation.
1998-03-03 17:48:14 +00:00

365 lines
12 KiB
Mathematica

%-----------------------------------------------------------------------------%
% Copyright (C) 1996-1998 The University of Melbourne.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%-----------------------------------------------------------------------------%
%
% File: prog_io_util.m.
% Main author: fjh.
%
% This module defines the types used by prog_io and its subcontractors
% to return the results of parsing, and some utility predicates needed
% by several of prog_io's submodules.
%
% Most parsing predicates must check for errors. They return either the
% item(s) they were looking for, or an error indication.
%
% Most of the parsing predicates return a `maybe1(T)'
% or a `maybe2(T1, T2)', which will either be the
% `ok(ParseTree)' (or `ok(ParseTree1, ParseTree2)'),
% if the parse is successful, or `error(Message, Term)'
% if it is not. The `Term' there should be the term which
% is syntactically incorrect.
:- module prog_io_util.
:- interface.
:- import_module prog_data, hlds_data, (inst).
:- import_module list, term, io.
:- type maybe2(T1, T2) ---> error(string, term)
; ok(T1, T2).
:- type maybe1(T) ---> error(string, term)
; ok(T).
:- type maybe_functor == maybe2(sym_name, list(term)).
:- type maybe_item_and_context
== maybe2(item, term__context).
:- pred add_context(maybe1(item), term__context, maybe_item_and_context).
:- mode add_context(in, in, out) is det.
:- pred convert_mode_list(list(term), list(mode)).
:- mode convert_mode_list(in, out) is semidet.
:- pred convert_mode(term, mode).
:- mode convert_mode(in, out) is semidet.
:- pred convert_inst_list(list(term), list(inst)).
:- mode convert_inst_list(in, out) is semidet.
% Parse an inst.
% Fails on syntax errors.
%
:- pred convert_inst(term, inst).
:- mode convert_inst(in, out) is semidet.
:- pred standard_det(string, determinism).
:- mode standard_det(in, out) is semidet.
% convert a "disjunction" (bunch of terms separated by ';'s) to a list
:- pred disjunction_to_list(term, list(term)).
:- mode disjunction_to_list(in, out) is det.
% convert a "conjunction" (bunch of terms separated by ','s) to a list
:- pred conjunction_to_list(term, list(term)).
:- mode conjunction_to_list(in, out) is det.
% convert a "sum" (bunch of terms separated by '+' operators) to a list
:- pred sum_to_list(term, list(term)).
:- mode sum_to_list(in, out) is det.
% The following /3, /4 and /5 predicates are to be used for reporting
% warnings to stderr. This is preferable to using io__write_string, as
% this checks the halt-at-warn option.
%
% This predicate is best used by predicates that do not have access to
% module_info for a particular module. It sets the exit status to error
% when a warning is encountered in a module, and the --halt-at-warn
% option is set.
:- pred report_warning(string::in, io__state::di, io__state::uo) is det.
:- pred report_warning(io__output_stream::in, string::in, io__state::di,
io__state::uo) is det.
:- pred report_warning(string::in, int::in, string::in, io__state::di,
io__state::uo) is det.
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module prog_io, prog_io_goal, hlds_pred, options, globals.
:- import_module bool, string, std_util.
add_context(error(M, T), _, error(M, T)).
add_context(ok(Item), Context, ok(Item, Context)).
convert_mode_list([], []).
convert_mode_list([H0|T0], [H|T]) :-
convert_mode(H0, H),
convert_mode_list(T0, T).
convert_mode(Term, Mode) :-
(
Term = term__functor(term__atom("->"), [InstA, InstB], _Context)
->
convert_inst(InstA, ConvertedInstA),
convert_inst(InstB, ConvertedInstB),
Mode = (ConvertedInstA -> ConvertedInstB)
;
% Handle higher-order predicate modes:
% a mode of the form
% pred(<Mode1>, <Mode2>, ...) is <Det>
% is an abbreviation for the inst mapping
% ( pred(<Mode1>, <Mode2>, ...) is <Det>
% -> pred(<Mode1>, <Mode2>, ...) is <Det>
% )
Term = term__functor(term__atom("is"), [PredTerm, DetTerm], _),
PredTerm = term__functor(term__atom("pred"), ArgModesTerms, _)
->
DetTerm = term__functor(term__atom(DetString), [], _),
standard_det(DetString, Detism),
convert_mode_list(ArgModesTerms, ArgModes),
PredInstInfo = pred_inst_info(predicate, ArgModes, Detism),
Inst = ground(shared, yes(PredInstInfo)),
Mode = (Inst -> Inst)
;
% Handle higher-order function modes:
% a mode of the form
% func(<Mode1>, <Mode2>, ...) = <RetMode> is <Det>
% is an abbreviation for the inst mapping
% ( func(<Mode1>, <Mode2>, ...) = <RetMode> is <Det>
% -> func(<Mode1>, <Mode2>, ...) = <RetMode> is <Det>
% )
Term = term__functor(term__atom("is"), [EqTerm, DetTerm], _),
EqTerm = term__functor(term__atom("="),
[FuncTerm, RetModeTerm], _),
FuncTerm = term__functor(term__atom("func"), ArgModesTerms, _)
->
DetTerm = term__functor(term__atom(DetString), [], _),
standard_det(DetString, Detism),
convert_mode_list(ArgModesTerms, ArgModes0),
convert_mode(RetModeTerm, RetMode),
list__append(ArgModes0, [RetMode], ArgModes),
FuncInstInfo = pred_inst_info(function, ArgModes, Detism),
Inst = ground(shared, yes(FuncInstInfo)),
Mode = (Inst -> Inst)
;
parse_qualified_term(Term, Term, "mode definition", R),
R = ok(Name, Args), % should improve error reporting
convert_inst_list(Args, ConvertedArgs),
Mode = user_defined_mode(Name, ConvertedArgs)
).
convert_inst_list([], []).
convert_inst_list([H0|T0], [H|T]) :-
convert_inst(H0, H),
convert_inst_list(T0, T).
convert_inst(term__variable(V), inst_var(V)).
convert_inst(Term, Result) :-
Term = term__functor(Name, Args0, _Context),
% `free' insts
( Name = term__atom("free"), Args0 = [] ->
Result = free
% `any' insts
; Name = term__atom("any"), Args0 = [] ->
Result = any(shared)
; Name = term__atom("unique_any"), Args0 = [] ->
Result = any(unique)
; Name = term__atom("mostly_unique_any"), Args0 = [] ->
Result = any(mostly_unique)
; Name = term__atom("clobbered_any"), Args0 = [] ->
Result = any(clobbered)
; Name = term__atom("mostly_clobbered_any"), Args0 = [] ->
Result = any(mostly_clobbered)
% `ground' insts
; Name = term__atom("ground"), Args0 = [] ->
Result = ground(shared, no)
; Name = term__atom("unique"), Args0 = [] ->
Result = ground(unique, no)
; Name = term__atom("mostly_unique"), Args0 = [] ->
Result = ground(mostly_unique, no)
; Name = term__atom("clobbered"), Args0 = [] ->
Result = ground(clobbered, no)
; Name = term__atom("mostly_clobbered"), Args0 = [] ->
Result = ground(mostly_clobbered, no)
;
% The syntax for a higher-order pred inst is
%
% pred(<Mode1>, <Mode2>, ...) is <Detism>
%
% where <Mode1>, <Mode2>, ... are a list of modes,
% and <Detism> is a determinism.
Name = term__atom("is"), Args0 = [PredTerm, DetTerm],
PredTerm = term__functor(term__atom("pred"), ArgModesTerm, _)
->
DetTerm = term__functor(term__atom(DetString), [], _),
standard_det(DetString, Detism),
convert_mode_list(ArgModesTerm, ArgModes),
PredInst = pred_inst_info(predicate, ArgModes, Detism),
Result = ground(shared, yes(PredInst))
;
% The syntax for a higher-order func inst is
%
% func(<Mode1>, <Mode2>, ...) = <RetMode> is <Detism>
%
% where <Mode1>, <Mode2>, ... are a list of modes,
% <RetMode> is a mode, and <Detism> is a determinism.
Name = term__atom("is"), Args0 = [EqTerm, DetTerm],
EqTerm = term__functor(term__atom("="),
[FuncTerm, RetModeTerm], _),
FuncTerm = term__functor(term__atom("func"), ArgModesTerm, _)
->
DetTerm = term__functor(term__atom(DetString), [], _),
standard_det(DetString, Detism),
convert_mode_list(ArgModesTerm, ArgModes0),
convert_mode(RetModeTerm, RetMode),
list__append(ArgModes0, [RetMode], ArgModes),
FuncInst = pred_inst_info(function, ArgModes, Detism),
Result = ground(shared, yes(FuncInst))
% `not_reached' inst
; Name = term__atom("not_reached"), Args0 = [] ->
Result = not_reached
% `bound' insts
; Name = term__atom("bound"), Args0 = [Disj] ->
parse_bound_inst_list(Disj, shared, Result)
/* `bound_unique' is for backwards compatibility - use `unique' instead */
; Name = term__atom("bound_unique"), Args0 = [Disj] ->
parse_bound_inst_list(Disj, unique, Result)
; Name = term__atom("unique"), Args0 = [Disj] ->
parse_bound_inst_list(Disj, unique, Result)
; Name = term__atom("mostly_unique"), Args0 = [Disj] ->
parse_bound_inst_list(Disj, mostly_unique, Result)
% anything else must be a user-defined inst
;
parse_qualified_term(Term, Term, "inst",
ok(QualifiedName, Args1)),
convert_inst_list(Args1, Args),
Result = defined_inst(user_inst(QualifiedName, Args))
).
standard_det("det", det).
standard_det("cc_nondet", cc_nondet).
standard_det("cc_multi", cc_multidet).
standard_det("nondet", nondet).
standard_det("multi", multidet).
standard_det("multidet", multidet).
standard_det("semidet", semidet).
standard_det("erroneous", erroneous).
standard_det("failure", failure).
:- pred parse_bound_inst_list(term::in, uniqueness::in, (inst)::out) is semidet.
parse_bound_inst_list(Disj, Uniqueness, bound(Uniqueness, Functors)) :-
disjunction_to_list(Disj, List),
convert_bound_inst_list(List, Functors0),
list__sort_and_remove_dups(Functors0, Functors).
:- pred convert_bound_inst_list(list(term), list(bound_inst)).
:- mode convert_bound_inst_list(in, out) is semidet.
convert_bound_inst_list([], []).
convert_bound_inst_list([H0|T0], [H|T]) :-
convert_bound_inst(H0, H),
convert_bound_inst_list(T0, T).
:- pred convert_bound_inst(term, bound_inst).
:- mode convert_bound_inst(in, out) is semidet.
convert_bound_inst(InstTerm, functor(ConsId, Args)) :-
InstTerm = term__functor(Functor, Args0, _),
( Functor = term__atom(_) ->
parse_qualified_term(InstTerm, InstTerm, "inst",
ok(SymName, Args1)),
list__length(Args1, Arity),
ConsId = cons(SymName, Arity)
;
Args1 = Args0,
list__length(Args1, Arity),
make_functor_cons_id(Functor, Arity, ConsId)
),
convert_inst_list(Args1, Args).
disjunction_to_list(Term, List) :-
binop_term_to_list(";", Term, List).
conjunction_to_list(Term, List) :-
binop_term_to_list(",", Term, List).
sum_to_list(Term, List) :-
binop_term_to_list("+", Term, List).
% general predicate to convert terms separated by any specified
% operator into a list
:- pred binop_term_to_list(string, term, list(term)).
:- mode binop_term_to_list(in, in, out) is det.
binop_term_to_list(Op, Term, List) :-
binop_term_to_list_2(Op, Term, [], List).
:- pred binop_term_to_list_2(string, term, list(term), list(term)).
:- mode binop_term_to_list_2(in, in, in, out) is det.
binop_term_to_list_2(Op, Term, List0, List) :-
(
Term = term__functor(term__atom(Op), [L, R], _Context)
->
binop_term_to_list_2(Op, R, List0, List1),
binop_term_to_list_2(Op, L, List1, List)
;
List = [Term|List0]
).
report_warning(Message) -->
io__stderr_stream(StdErr),
globals__io_lookup_bool_option(halt_at_warn, HaltAtWarn),
( { HaltAtWarn = yes } ->
io__set_exit_status(1)
;
[]
),
io__write_string(StdErr, Message).
report_warning(Stream, Message) -->
globals__io_lookup_bool_option(halt_at_warn, HaltAtWarn),
( { HaltAtWarn = yes } ->
io__set_exit_status(1)
;
[]
),
io__write_string(Stream, Message).
report_warning(ModuleName, LineNum, Message) -->
{ string__format("%s.m:%3d: Warning: %s\n",
[s(ModuleName), i(LineNum), s(Message)], FullMessage) },
io__stderr_stream(StdErr),
io__write_string(StdErr, FullMessage),
globals__io_lookup_bool_option(halt_at_warn, HaltAtWarn),
( { HaltAtWarn = yes } ->
io__set_exit_status(1)
;
[]
).
%-----------------------------------------------------------------------------%