mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-23 21:33:49 +00:00
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.
499 lines
19 KiB
Mathematica
499 lines
19 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1994-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: delay_info.m.
|
|
% Main author: fjh.
|
|
|
|
% This module implements part of the mode analysis algorithm.
|
|
% In the mode analysis, reordering of conjunctions is done
|
|
% by simulating coroutining at compile time. This module
|
|
% defines an abstract data type `delay_info' which records the
|
|
% information necessary for suspending and waking up goals.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module delay_info.
|
|
|
|
:- interface.
|
|
|
|
:- import_module hlds_goal, mode_errors.
|
|
:- import_module list, term.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type delay_info.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Sanity-check the delay_info structure.
|
|
%
|
|
:- pred delay_info__check_invariant(delay_info).
|
|
:- mode delay_info__check_invariant(in) is det.
|
|
|
|
% Initialize the delay_info structure.
|
|
%
|
|
:- pred delay_info__init(delay_info).
|
|
:- mode delay_info__init(out) is det.
|
|
|
|
% Tell the delay_info structure that we've entered a new conjunction.
|
|
%
|
|
:- pred delay_info__enter_conj(delay_info, delay_info).
|
|
:- mode delay_info__enter_conj(in, out) is det.
|
|
|
|
% Tell the delay_info structure that we've left a conjunction.
|
|
% This predicate returns a list of the delayed goals from that
|
|
% conjunction, i.e. goals which could not be scheduled.
|
|
%
|
|
:- pred delay_info__leave_conj(delay_info, list(delayed_goal), delay_info).
|
|
:- mode delay_info__leave_conj(in, out, out) is det.
|
|
|
|
% Insert a new delayed goal into the delay_info structure.
|
|
%
|
|
:- pred delay_info__delay_goal(delay_info, mode_error_info,
|
|
hlds_goal, delay_info).
|
|
:- mode delay_info__delay_goal(in, in, in, out) is det.
|
|
|
|
% Mark a list of variables as having been bound.
|
|
% This may allow a previously delayed goal to change status
|
|
% from "delayed" to "pending".
|
|
% (This predicate just calls delay_info__bind_var in a loop.)
|
|
%
|
|
:- pred delay_info__bind_var_list(list(var), delay_info, delay_info).
|
|
:- mode delay_info__bind_var_list(in, in, out) is det.
|
|
|
|
% Mark a variable as having been bound.
|
|
% This may allow a previously delayed goal to change status
|
|
% from "delayed" to "pending".
|
|
%
|
|
:- pred delay_info__bind_var(delay_info, var, delay_info).
|
|
:- mode delay_info__bind_var(in, in, out) is det.
|
|
|
|
% Mark all variables as having been bound.
|
|
% This will allow all previously delayed goals to change status
|
|
% from "delayed" to "pending".
|
|
%
|
|
:- pred delay_info__bind_all_vars(delay_info, delay_info).
|
|
:- mode delay_info__bind_all_vars(in, out) is det.
|
|
|
|
% Check if there are any "pending" goals, and if so,
|
|
% remove them from the delay_info and return them.
|
|
%
|
|
:- pred delay_info__wakeup_goals(delay_info, list(hlds_goal), delay_info).
|
|
:- mode delay_info__wakeup_goals(in, out, out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
:- import_module int, stack, set, map, require, std_util.
|
|
:- import_module mode_errors. % for the mode_error_info and delay_info
|
|
% types.
|
|
|
|
% The delay_info structure is a tangled web of substructures
|
|
% all of which are pointing at each other - debugging it
|
|
% reminds me of debugging C code - it's a nightmare!
|
|
% (Of course, it could just be that my brain has decided to
|
|
% switch off in protest - after all, it is 5:25am. ;-)
|
|
|
|
:- type delay_info
|
|
---> delay_info(
|
|
depth_num, % CurrentDepth:
|
|
% the current conjunction depth,
|
|
% i.e. the number of nested conjunctions
|
|
% which are currently active
|
|
stack(map(seq_num, delayed_goal)),
|
|
% DelayedGoalStack:
|
|
% for each nested conjunction,
|
|
% we store a collection of delayed goals
|
|
% associated with that conjunction,
|
|
% indexed by sequence number
|
|
waiting_goals_table,
|
|
% WaitingGoalsTable:
|
|
% for each variable, we keep track of
|
|
% all the goals which are waiting on
|
|
% that variable
|
|
pending_goals_table,
|
|
% PendingGoalsTable:
|
|
% when a variable gets bound, we
|
|
% mark all the goals which are waiting
|
|
% on that variable as ready to be
|
|
% reawakened at the next opportunity
|
|
stack(seq_num)
|
|
% SeqNumsStack:
|
|
% For each nested conjunction, the
|
|
% next available sequence number.
|
|
).
|
|
|
|
:- type waiting_goals_table == map(var, waiting_goals).
|
|
% Used to store the collection of goals waiting on a variable.
|
|
:- type waiting_goals == map(goal_num, list(var)).
|
|
% For each goal, we store all the variables that it is waiting on.
|
|
|
|
:- type pending_goals_table == map(depth_num, list(seq_num)).
|
|
|
|
:- type goal_num == pair(depth_num, seq_num).
|
|
:- type depth_num == int. /* Eeek! Pointers! */
|
|
:- type seq_num == int.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Check that the invariants for the delay_info structure
|
|
% hold, and if not, call error/1.
|
|
|
|
delay_info__check_invariant(_).
|
|
/***
|
|
% for debugging purposes
|
|
delay_info__check_invariant(DelayInfo) :-
|
|
delay_info__check_invariant_x(DelayInfo).
|
|
***/
|
|
|
|
:- pred delay_info__check_invariant_x(delay_info).
|
|
:- mode delay_info__check_invariant_x(in) is det.
|
|
|
|
delay_info__check_invariant_x(DelayInfo) :-
|
|
DelayInfo = delay_info(CurrentDepth, DelayedGoalStack,
|
|
WaitingGoalsTable, _PendingGoals, NextSeqNums),
|
|
(
|
|
stack__depth(DelayedGoalStack, CurrentDepth),
|
|
stack__depth(NextSeqNums, CurrentDepth),
|
|
map__keys(WaitingGoalsTable, WaitingVars),
|
|
waiting_goals_check_invariant(WaitingVars, WaitingGoalsTable)
|
|
->
|
|
true
|
|
;
|
|
error("delay_info: invariant violated")
|
|
).
|
|
|
|
|
|
% For every variable which goals are waiting on, check the
|
|
% consistency of all the goals waiting on that var.
|
|
|
|
:- pred waiting_goals_check_invariant(list(var), waiting_goals_table).
|
|
:- mode waiting_goals_check_invariant(in, in) is semidet.
|
|
|
|
waiting_goals_check_invariant([], _).
|
|
waiting_goals_check_invariant([V|Vs], WaitingGoalsTable) :-
|
|
map__lookup(WaitingGoalsTable, V, WaitingGoals),
|
|
map__keys(WaitingGoals, GoalNums),
|
|
waiting_goal_check_invariant(GoalNums, WaitingGoals, WaitingGoalsTable),
|
|
waiting_goals_check_invariant(Vs, WaitingGoalsTable).
|
|
|
|
% Check the consistency of a list of goal_nums in the
|
|
% waiting_goals_table.
|
|
|
|
:- pred waiting_goal_check_invariant(list(goal_num), waiting_goals,
|
|
waiting_goals_table).
|
|
:- mode waiting_goal_check_invariant(in, in, in) is semidet.
|
|
|
|
waiting_goal_check_invariant([], _, _).
|
|
waiting_goal_check_invariant([GoalNum | GoalNums], WaitingGoals,
|
|
WaitingGoalsTable) :-
|
|
map__lookup(WaitingGoals, GoalNum, Vars),
|
|
set__list_to_set(Vars, VarsSet),
|
|
waiting_goal_vars_check_invariant(Vars, GoalNum, VarsSet,
|
|
WaitingGoalsTable),
|
|
waiting_goal_check_invariant(GoalNums, WaitingGoals, WaitingGoalsTable).
|
|
|
|
% For every variable which a goal is waiting on, there should
|
|
% be an entry in the waiting_goals_table for that goal,
|
|
% and the set of vars which it is waiting on in that entry
|
|
% should be the same as in all its other entries.
|
|
|
|
:- pred waiting_goal_vars_check_invariant(list(var), goal_num, set(var),
|
|
waiting_goals_table).
|
|
:- mode waiting_goal_vars_check_invariant(in, in, in, in) is semidet.
|
|
|
|
waiting_goal_vars_check_invariant([], _, _, _).
|
|
waiting_goal_vars_check_invariant([V|Vs], GoalNum, Vars, WaitingGoalsTable) :-
|
|
map__search(WaitingGoalsTable, V, WaitingGoals),
|
|
map__search(WaitingGoals, GoalNum, VarsList),
|
|
set__list_to_set(VarsList, VarsSet),
|
|
set__equal(Vars, VarsSet),
|
|
waiting_goal_vars_check_invariant(Vs, GoalNum, Vars, WaitingGoalsTable).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Initialize the delay info structure in preparation for
|
|
% mode analysis of a goal.
|
|
|
|
delay_info__init(DelayInfo) :-
|
|
CurrentDepth = 0,
|
|
stack__init(DelayedGoalStack),
|
|
map__init(WaitingGoalsTable),
|
|
map__init(PendingGoals),
|
|
stack__init(NextSeqNums),
|
|
DelayInfo = delay_info(CurrentDepth, DelayedGoalStack,
|
|
WaitingGoalsTable, PendingGoals, NextSeqNums),
|
|
delay_info__check_invariant(DelayInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
delay_info__enter_conj(DelayInfo0, DelayInfo) :-
|
|
delay_info__check_invariant(DelayInfo0),
|
|
DelayInfo0 = delay_info(CurrentDepth0, DelayedGoalStack0,
|
|
WaitingGoalsTable, PendingGoals, NextSeqNums0),
|
|
map__init(DelayedGoals),
|
|
stack__push(DelayedGoalStack0, DelayedGoals, DelayedGoalStack),
|
|
stack__push(NextSeqNums0, 0, NextSeqNums),
|
|
CurrentDepth is CurrentDepth0 + 1,
|
|
DelayInfo = delay_info(CurrentDepth, DelayedGoalStack,
|
|
WaitingGoalsTable, PendingGoals, NextSeqNums),
|
|
delay_info__check_invariant(DelayInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
delay_info__leave_conj(DelayInfo0, DelayedGoalsList, DelayInfo) :-
|
|
delay_info__check_invariant(DelayInfo0),
|
|
DelayInfo0 = delay_info(CurrentDepth0, DelayedGoalStack0,
|
|
WaitingGoalsTable0, PendingGoals, NextSeqNums0),
|
|
stack__pop_det(DelayedGoalStack0, DelayedGoals, DelayedGoalStack),
|
|
map__keys(DelayedGoals, SeqNums),
|
|
remove_delayed_goals(SeqNums, DelayedGoals, CurrentDepth0,
|
|
WaitingGoalsTable0, WaitingGoalsTable),
|
|
stack__pop_det(NextSeqNums0, _, NextSeqNums),
|
|
CurrentDepth is CurrentDepth0 - 1,
|
|
map__values(DelayedGoals, DelayedGoalsList),
|
|
DelayInfo = delay_info(CurrentDepth, DelayedGoalStack,
|
|
WaitingGoalsTable, PendingGoals, NextSeqNums),
|
|
delay_info__check_invariant(DelayInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% When a conjunction flounders, we need to remove the delayed sub-goals
|
|
% from the waiting goals table before we delay the conjunction as a
|
|
% whole.
|
|
|
|
:- pred remove_delayed_goals(list(seq_num), map(seq_num, delayed_goal),
|
|
depth_num, waiting_goals_table, waiting_goals_table).
|
|
:- mode remove_delayed_goals(in, in, in, in, out) is det.
|
|
|
|
remove_delayed_goals([], _, _, WaitingGoalsTable, WaitingGoalsTable).
|
|
remove_delayed_goals([SeqNum | SeqNums], DelayedGoalsTable, Depth,
|
|
WaitingGoalsTable0, WaitingGoalsTable) :-
|
|
map__lookup(DelayedGoalsTable, SeqNum, DelayedGoal),
|
|
DelayedGoal = delayed_goal(Vars, _Error, _Goal),
|
|
GoalNum = Depth - SeqNum,
|
|
set__to_sorted_list(Vars, VarList),
|
|
delete_waiting_vars(VarList, GoalNum,
|
|
WaitingGoalsTable0, WaitingGoalsTable1),
|
|
remove_delayed_goals(SeqNums, DelayedGoalsTable, Depth,
|
|
WaitingGoalsTable1, WaitingGoalsTable).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% We are going to delay a goal.
|
|
% Update the delay info structure to record the delayed goal.
|
|
|
|
delay_info__delay_goal(DelayInfo0, Error, Goal, DelayInfo) :-
|
|
delay_info__check_invariant(DelayInfo0),
|
|
Error = mode_error_info(Vars, _, _, _),
|
|
DelayInfo0 = delay_info(CurrentDepth, DelayedGoalStack0,
|
|
WaitingGoalsTable0, PendingGoals, NextSeqNums0),
|
|
|
|
% Get the next sequence number
|
|
stack__pop_det(NextSeqNums0, SeqNum, NextSeqNums1),
|
|
NextSeq is SeqNum + 1,
|
|
stack__push(NextSeqNums1, NextSeq, NextSeqNums),
|
|
|
|
% Store the goal in the delayed goal stack
|
|
stack__pop_det(DelayedGoalStack0, DelayedGoals0, DelayedGoalStack1),
|
|
map__set(DelayedGoals0, SeqNum, delayed_goal(Vars, Error, Goal),
|
|
DelayedGoals),
|
|
stack__push(DelayedGoalStack1, DelayedGoals, DelayedGoalStack),
|
|
|
|
% Store indexes to the goal in the waiting goals table
|
|
GoalNum = CurrentDepth - SeqNum,
|
|
set__to_sorted_list(Vars, VarList),
|
|
add_waiting_vars(VarList, GoalNum, VarList, WaitingGoalsTable0,
|
|
WaitingGoalsTable),
|
|
|
|
DelayInfo = delay_info(CurrentDepth, DelayedGoalStack,
|
|
WaitingGoalsTable, PendingGoals, NextSeqNums),
|
|
delay_info__check_invariant(DelayInfo).
|
|
|
|
|
|
% add_waiting_vars(Vars, Goal, AllVars, WGT0, WGT):
|
|
% update the waiting goals table by adding indexes
|
|
% from each of the variables in Vars to Goal.
|
|
% AllVars must be the list of all the variables which the goal is
|
|
% waiting on.
|
|
|
|
:- pred add_waiting_vars(list(var), goal_num, list(var), waiting_goals_table,
|
|
waiting_goals_table).
|
|
:- mode add_waiting_vars(in, in, in, in, out) is det.
|
|
|
|
add_waiting_vars([], _, _, WaitingGoalsTable, WaitingGoalsTable).
|
|
add_waiting_vars([Var | Vars], Goal, AllVars, WaitingGoalsTable0,
|
|
WaitingGoalsTable) :-
|
|
(
|
|
map__search(WaitingGoalsTable0, Var, WaitingGoals0)
|
|
->
|
|
WaitingGoals1 = WaitingGoals0
|
|
;
|
|
map__init(WaitingGoals1)
|
|
),
|
|
map__set(WaitingGoals1, Goal, AllVars, WaitingGoals),
|
|
map__set(WaitingGoalsTable0, Var, WaitingGoals, WaitingGoalsTable1),
|
|
add_waiting_vars(Vars, Goal, AllVars, WaitingGoalsTable1,
|
|
WaitingGoalsTable).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Whenever we hit a goal which cannot succeed, we need to wake
|
|
% up all the delayed goals, so that we don't get mode errors in
|
|
% unreachable code. We remove all the goals from the waiting
|
|
% goals table and add them to the pending goals table. They
|
|
% will be woken up next time we get back to their conjunction.
|
|
|
|
delay_info__bind_all_vars(DelayInfo0, DelayInfo) :-
|
|
DelayInfo0 = delay_info(_, _, WaitingGoalsTable0, _, _),
|
|
map__keys(WaitingGoalsTable0, WaitingVars),
|
|
delay_info__bind_var_list(WaitingVars, DelayInfo0, DelayInfo).
|
|
|
|
delay_info__bind_var_list([], DelayInfo, DelayInfo).
|
|
delay_info__bind_var_list([Var|Vars], DelayInfo0, DelayInfo) :-
|
|
delay_info__bind_var(DelayInfo0, Var, DelayInfo1),
|
|
delay_info__bind_var_list(Vars, DelayInfo1, DelayInfo).
|
|
|
|
% Whenever we bind a variable, we also check to see whether
|
|
% we need to wake up some goals. If so, we remove those
|
|
% goals from the waiting goals table and add them to the pending
|
|
% goals table. They will be woken up next time we get back
|
|
% to their conjunction.
|
|
|
|
delay_info__bind_var(DelayInfo0, Var, DelayInfo) :-
|
|
delay_info__check_invariant(DelayInfo0),
|
|
DelayInfo0 = delay_info(CurrentDepth, DelayedGoalStack,
|
|
WaitingGoalsTable0, PendingGoals0, NextSeqNums),
|
|
(
|
|
map__search(WaitingGoalsTable0, Var, GoalsWaitingOnVar)
|
|
->
|
|
map__keys(GoalsWaitingOnVar, NewlyPendingGoals),
|
|
add_pending_goals(NewlyPendingGoals, GoalsWaitingOnVar,
|
|
PendingGoals0, PendingGoals,
|
|
WaitingGoalsTable0, WaitingGoalsTable),
|
|
DelayInfo = delay_info(CurrentDepth, DelayedGoalStack,
|
|
WaitingGoalsTable, PendingGoals, NextSeqNums),
|
|
delay_info__check_invariant(DelayInfo)
|
|
;
|
|
DelayInfo = DelayInfo0
|
|
).
|
|
|
|
% Add a collection of goals, identified by depth_num and seq_num
|
|
% (depth of nested conjunction and sequence number within conjunction),
|
|
% to the collection of pending goals.
|
|
|
|
:- pred add_pending_goals(list(goal_num), map(goal_num, list(var)),
|
|
pending_goals_table, pending_goals_table,
|
|
waiting_goals_table, waiting_goals_table).
|
|
:- mode add_pending_goals(in, in, in, out, in, out) is det.
|
|
|
|
add_pending_goals([], _WaitingVarsTable,
|
|
PendingGoals, PendingGoals,
|
|
WaitingGoals, WaitingGoals).
|
|
add_pending_goals([Depth - SeqNum | Rest], WaitingVarsTable,
|
|
PendingGoals0, PendingGoals,
|
|
WaitingGoals0, WaitingGoals) :-
|
|
|
|
% remove any other indexes to the goal from the waiting
|
|
% goals table
|
|
GoalNum = Depth - SeqNum,
|
|
map__lookup(WaitingVarsTable, GoalNum, WaitingVars),
|
|
delete_waiting_vars(WaitingVars, GoalNum, WaitingGoals0, WaitingGoals1),
|
|
|
|
% add the goal to the pending goals table
|
|
( map__search(PendingGoals0, Depth, PendingSeqNums0) ->
|
|
% XXX should use a queue
|
|
list__append(PendingSeqNums0, [SeqNum], PendingSeqNums)
|
|
;
|
|
PendingSeqNums = [SeqNum]
|
|
),
|
|
map__set(PendingGoals0, Depth, PendingSeqNums, PendingGoals1),
|
|
|
|
% do the same for the rest of the pending goals
|
|
add_pending_goals(Rest, WaitingVarsTable,
|
|
PendingGoals1, PendingGoals,
|
|
WaitingGoals1, WaitingGoals).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Remove all references to a goal from the waiting goals table.
|
|
|
|
:- pred delete_waiting_vars(list(var), goal_num,
|
|
waiting_goals_table, waiting_goals_table).
|
|
:- mode delete_waiting_vars(in, in, in, out) is det.
|
|
|
|
delete_waiting_vars([], _, WaitingGoalTables, WaitingGoalTables).
|
|
delete_waiting_vars([Var | Vars], GoalNum, WaitingGoalsTable0,
|
|
WaitingGoalsTable) :-
|
|
map__lookup(WaitingGoalsTable0, Var, WaitingGoals0),
|
|
map__delete(WaitingGoals0, GoalNum, WaitingGoals),
|
|
( map__is_empty(WaitingGoals) ->
|
|
map__delete(WaitingGoalsTable0, Var, WaitingGoalsTable1)
|
|
;
|
|
map__set(WaitingGoalsTable0, Var, WaitingGoals,
|
|
WaitingGoalsTable1)
|
|
),
|
|
delete_waiting_vars(Vars, GoalNum, WaitingGoalsTable1,
|
|
WaitingGoalsTable).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% delay_info__wakeup_goals(DelayInfo0, Goals, DelayInfo):
|
|
% Goals is the list of pending goal in the order that they should
|
|
% be woken up, and DelayInfo is the new delay_info, updated to
|
|
% reflect the fact that the Goals have been woken up and is
|
|
% hence are longer pending.
|
|
|
|
delay_info__wakeup_goals(DelayInfo0, Goals, DelayInfo) :-
|
|
( delay_info__wakeup_goal(DelayInfo0, Goal, DelayInfo1) ->
|
|
Goals = [Goal | Goals1],
|
|
delay_info__wakeup_goals(DelayInfo1, Goals1, DelayInfo)
|
|
;
|
|
Goals = [],
|
|
DelayInfo = DelayInfo0
|
|
).
|
|
|
|
% Check if there are any "pending" goals, and if so,
|
|
% select one to wake up, remove it from the delay_info,
|
|
% and return it. If there are no pending goals, this
|
|
% predicate will fail.
|
|
%
|
|
:- pred delay_info__wakeup_goal(delay_info, hlds_goal, delay_info).
|
|
:- mode delay_info__wakeup_goal(in, out, out) is semidet.
|
|
|
|
% delay_info__wakeup_goal(DelayInfo0, Goal, DelayInfo) is true iff
|
|
% DelayInfo0 specifies that there is at least one goal which is
|
|
% pending, Goal is the pending goal which should be reawakened first,
|
|
% and DelayInfo is the new delay_info, updated to reflect the fact
|
|
% that Goal has been woken up and is hence no longer pending.
|
|
|
|
delay_info__wakeup_goal(DelayInfo0, Goal, DelayInfo) :-
|
|
delay_info__check_invariant(DelayInfo0),
|
|
DelayInfo0 = delay_info(CurrentDepth, DelayedGoalStack0, WaitingGoals,
|
|
PendingGoalsTable0, NextSeqNums),
|
|
|
|
% is there a goal in the current conjunction which is pending?
|
|
map__search(PendingGoalsTable0, CurrentDepth, PendingGoals0),
|
|
|
|
% if so, remove it from the pending goals table,
|
|
% remove it from the delayed goals stack, and return it
|
|
PendingGoals0 = [SeqNum | PendingGoals],
|
|
map__set(PendingGoalsTable0, CurrentDepth, PendingGoals,
|
|
PendingGoalsTable),
|
|
stack__pop_det(DelayedGoalStack0, DelayedGoals0, DelayedGoalStack1),
|
|
map__lookup(DelayedGoals0, SeqNum, DelayedGoal),
|
|
DelayedGoal = delayed_goal(_Vars, _ErrorReason, Goal),
|
|
map__delete(DelayedGoals0, SeqNum, DelayedGoals),
|
|
stack__push(DelayedGoalStack1, DelayedGoals, DelayedGoalStack),
|
|
DelayInfo = delay_info(CurrentDepth, DelayedGoalStack, WaitingGoals,
|
|
PendingGoalsTable, NextSeqNums),
|
|
delay_info__check_invariant(DelayInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|