[m-rev.] EDCG new file (edcg.m)

Peter Nicholas MALKIN pnmalk at students.cs.mu.oz.au
Thu Mar 15 19:26:08 AEDT 2001


%-----------------------------------------------------------------------------%
% Copyright (C) 1999-2000 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: edcg.m.
% Main author: pnmalk.

% This file provides predicates to transform code with EDCG notation into code
% without. The transformation takes place after purity.
%
% There are three steps to EDCG transformation:
% (i) EDCG inference: The form of EDCG references for a predicate are
% inferred from EDCG operators and also predicate calls which use EDCG
% variables, used within a clause for the predicate. If the global option
% infer_edcgs is true then fixed point iteration is used to infer the forms of
% EDCG references without using any EDCG form declarations. Otherwise a single
% pass is done using the declared forms of EDCG references as well as the EDCG
% operators to infer the forms of EDCG references. This single pass is only done
% for those predicates that require it. A predicate requires it if it has
% declared EDCG references, or contains an EDCG operator, EDCG goal or a
% predicate call which has EDCG arguments (unless the predicate is called in
% expanded form).
%
% (ii) EDCG checking: The inferred form of an EDCG reference for a predicate is
% checked against the declared form. Error messages are given if the inferred
% and declared forms are incompatible and warning messages are given if the
% inferred and declared forms are compatible but not the same (refer to
% edcg_form_compare/3).
%
% (iii) EDCG expansion: EDCG operators, EDCG goals and predicate calls with EDCG
% references are expanded. After the expansion there will be no EDCG operators
% or EDCG goals in the HLDS, and all predicate calls will be in standard form.
% A predicate requires expansion if it has declared or inferred EDCG references,
% or contains an EDCG operator, EDCG goal or a predicate call which has EDCG
% arguments (unless the predicate is called in expanded form).
%
% Expansion is based upon atomic goals (an atomic goal is a unification or a
% predicate call). The value of the current reference ($) and the next reference
% ($=) are the same throughout the atomic goal, this includes any EDCG reference
% implicit or explicit in a predicate call. The order in which they appear
% within the atomic goals is irrelevant (however the order of the atomic goals
% is relevant). The purpose of the atomic_goal_id for goals is to indicate
% when two hlds goals were originally (in the source code) in the same atomic
% goal.
%
% For example the following atomic goals are equivalent:
%	(i) $EDCG_ref = $=EDCG_ref
%	(ii) $=EDCG_ref = $EDCG_ref
%
% However these following goals are not equivalent. In the first instance the
% operators refer to the same variable. In the second they refer to different
% variables:
%	(i) $=EDCG_ref = Var, Var = a_functor(A, B, C, $EDCG_ref)
%	(ii) a_functor(A, B, C, $EDCG_ref) = Var, Var = $=EDCG_ref
%
% Also, given `:- pred pred_call +edcg(changed(EDCG_ref)).',
% the following predicate calls are equivalent:
%	(i) pred_call		% implicit
%	(ii) pred_call($EDCG_ref, $=EDCG_ref) % expanded
%	(iii) pred_call +edcg(EDCG_ref($EDCG_ref, $=EDCG_ref)) % explicit
% 
% XXX: Using EDCGs with higher order terms has not been implemented. In fact
% the reason that EDCGs has been implmemented after typecheck was to enable
% EDCGs to be used with higher order terms.

%---------------------------------------------------------------------------%

:- module edcg.

:- interface.  

:- import_module list, io.
:- import_module hlds_module.
:- import_module prog_data.

	% Predicate to expand EDCG clauses.

:- pred expand_edcgs(module_info, module_info, io__state, io__state).
:- mode expand_edcgs(in, out, di, uo) is det.

	% Returns a list types that are to be appended onto the end of the 
	% head type list of a particular predicate. Used by typecheck.
:- pred get_edcg_pred_types(module_info, edcg_forms, list(type)).
:- mode get_edcg_pred_types(in, in, out) is det.

%---------------------------------------------------------------------------%

:- implementation.

:- import_module prog_util, prog_out, prog_io, prog_io_util, prog_io_goal.
:- import_module bool, io, term, map, string, set, int, assoc_list.
:- import_module require, varset.
:- import_module hlds_data, hlds_goal, hlds_module, hlds_pred.
:- import_module make_hlds, error_util, std_util, mercury_to_mercury.
:- import_module code_util, quantification, options, globals.

%---------------------------------------------------------------------------%

	% Predicate to inferr and check edcg declarations.
:- pred infer_edcgs(module_info, module_info, io__state, io__state).
:- mode infer_edcgs(in, out, di, uo) is det.

infer_edcgs(Module0, Module) -->
	{ module_info_predids(Module0, PredIds) },
	globals__io_lookup_int_option(edcg_inference_iteration_limit,
		MaxIterations),
	infer_to_fixpoint(MaxIterations, PredIds, Module0, Module).

:- pred infer_to_fixpoint(int, list(pred_id), module_info, module_info,
		io__state, io__state).
:- mode infer_to_fixpoint(in, in, in, out, di, uo) is det.

infer_to_fixpoint(NumIterations, PredIds, Module0, Module) -->
	globals__io_lookup_bool_option(infer_edcgs, Inferring),
	infer_preds_edcgs(PredIds, Inferring, Module0, Module1, no, Changed),

	% Do not iterate if nothing has changed since last time or we
	% are not inferring EDCGs and so we are just performing a check.
	( ({ Changed = no }  ; { Inferring = no}) ->
		{ Module = Module1 }
	;
		{ NumIterations1 = NumIterations - 1 },
		( { NumIterations1 > 0 } ->
			infer_to_fixpoint(NumIterations1, PredIds, Module1,
				Module)
		;
			report_max_iterations_exceeded,
			{ Module = Module1 }
		)
	).

:- pred report_max_iterations_exceeded(io__state, io__state).
:- mode report_max_iterations_exceeded(di, uo) is det.

report_max_iterations_exceeded -->
	io__set_exit_status(1),
	io__write_strings([
	   "EDCG inference iteration limit exceeded.\n",
	   "This probably indicates that your program has an EDCG error.\n",
	   "You should declare the edcg arguments explicitly.\n"
	]),
	globals__io_lookup_int_option(edcg_inference_iteration_limit,
		MaxIterations),
	io__format("(The current limit is %d iterations.  You can use the\n",
		[i(MaxIterations)]),
	io__write_string("`--edcg-inference-iteration-limit' option to increase the limit).\n").


:- pred infer_preds_edcgs(list(pred_id), bool, module_info, module_info,
	bool, bool, io__state, io__state).
:- mode infer_preds_edcgs(in, in, in, out, in, out, di, uo) is det.

infer_preds_edcgs([], _, ModuleInfo, ModuleInfo, Changed, Changed) --> [].
infer_preds_edcgs([PredId | PredIds], Inferring, ModuleInfo0, ModuleInfo,
		Changed0, Changed) -->
	{ module_info_preds(ModuleInfo0, Preds0) },
	{ map__lookup(Preds0, PredId, PredInfo0) },
	(
		{ pred_info_is_imported(PredInfo0) }
	->
		{ ModuleInfo2 = ModuleInfo0 },
		{ Changed2 = Changed0 }
	;
		{ infer_predicate_edcgs(PredInfo0, ModuleInfo0, Inferring,
			PredInfo1, Changed1) },
		{ map__det_update(Preds0, PredId, PredInfo1, Preds) },
		{ module_info_set_preds(ModuleInfo0, Preds,
			ModuleInfo2) },
		{ bool__or(Changed0, Changed1, Changed2) }
	),
	infer_preds_edcgs(PredIds, Inferring, ModuleInfo2, ModuleInfo,
		Changed2, Changed).

:- pred infer_predicate_edcgs(pred_info, module_info, bool,
		pred_info, bool).
:- mode infer_predicate_edcgs(in, in, in, out, out) is det.

infer_predicate_edcgs(PredInfo0, ModuleInfo, Inferring, PredInfo, Changed) :-
	(
		% Compiler-generated predicates do not use edcgs.
		% Same for builtins.
		( code_util__compiler_generated(PredInfo0)
		; code_util__predinfo_is_builtin(PredInfo0))
	->
		Changed = no,
		PredInfo = PredInfo0
	;
	 	pred_info_clauses_info(PredInfo0, ClausesInfo0),
		pred_info_edcg_args(PredInfo0, 
			pred_edcg_info(Declared, Inferred0, 
			NeedsEDCGTransform0)),
		(
			% Infer edcgs for this predicate if we are
			% inferring or if we are checking edcg forms
			% and this predicate needs an EDCG
			% transformation.
			(Inferring = yes ; NeedsEDCGTransform0 = yes)
		->
			clauses_info_clauses(ClausesInfo0, Clauses0),

			edcg_infer_init(ModuleInfo, Inferring, EDCGInfer0),
			edcg_infer_clause_list(Clauses0, Clauses,
					EDCGInfer0, EDCGInfer),
			edcg_infer_final(EDCGInfer, Inferred, Changed0,
				NeedsEDCGTransform0, NeedsEDCGTransform),

			clauses_info_set_clauses(ClausesInfo0, Clauses,
				ClausesInfo),
			pred_info_set_clauses_info(PredInfo0, ClausesInfo,
				PredInfo1),
			pred_info_set_edcg_args(PredInfo1, 
				pred_edcg_info(Declared, Inferred, 
				NeedsEDCGTransform), PredInfo),

			% Check if anything changed
			(
				Changed0 = no,
				edcg_forms_identical(Inferred0, Inferred)
			->
				Changed = no
			;
				Changed = yes
			)
		;
			% We are not inferring edcg arguments and we do not
			% need to check this predicate for edcg argument
			% usage.
			Changed = no,
			PredInfo = PredInfo0
		)
	).

:- pred edcg_infer_clause_list(list(clause), list(clause),
		edcg_infer, edcg_infer).
:- mode edcg_infer_clause_list(in, out, in, out) is det.

edcg_infer_clause_list(Clauses0, Clauses, EDCGInfer0, EDCGInfer) :-
	edcg_infer_clause_list_2(Clauses0, Clauses, EDCGInfer0, 
		EDCGInferList),
	edcg_infer_join(EDCGInferList, EDCGInfer0, EDCGInfer).

:- pred edcg_infer_clause_list_2(list(clause), list(clause),
		edcg_infer, list(edcg_infer)).
:- mode edcg_infer_clause_list_2(in, out, in, out) is det.

edcg_infer_clause_list_2([], [], _, []).
edcg_infer_clause_list_2([Clause0|Clauses0], [Clause|Clauses],
		EDCGInferIn, [EDCGInfer|EDCGInferList]) :-
	edcg_infer_clause(Clause0, Clause, EDCGInferIn, EDCGInfer),
	edcg_infer_clause_list_2(Clauses0, Clauses, EDCGInferIn,
		EDCGInferList).

:- pred edcg_infer_clause(clause, clause, edcg_infer,
		edcg_infer).
:- mode edcg_infer_clause(in, out, in, out) is det.

edcg_infer_clause(Clause0, Clause) -->
	{ Clause0 = clause(Modes, Body0, MaybeEDCG, Context) },
	{ Clause = clause(Modes, Body, MaybeEDCG, Context) },
	edcg_infer_goal(Body0, Body).

:- pred edcg_infer_goal(hlds_goal, hlds_goal, edcg_infer,
		edcg_infer).
:- mode edcg_infer_goal(in, out, in, out) is det.

edcg_infer_goal(conj(List0) - Info, conj(List) - Info) -->
	list__map_foldl(edcg_infer_goal, List0, List).
edcg_infer_goal(par_conj(List0, SM) - Info, par_conj(List, SM) - Info) -->
	list__map_foldl(edcg_infer_goal, List0, List).
edcg_infer_goal(disj(List0, SM) - Info, disj(List, SM) - Info) -->
	edcg_infer_disj(List0, List).
edcg_infer_goal(if_then_else(Vs, A, B, C, SM) - Info,
		if_then_else(Vs, A0, B0, C0, SM) - Info) -->
	edcg_infer_if_then_else(A, B, C, A0, B0, C0).
edcg_infer_goal(not(A0) - Info, not(A) - Info) -->
	edcg_infer_goal(A0, A).
edcg_infer_goal(some(Vs, C, G0) - Info, some(Vs, C, G) - Info) -->
	edcg_infer_goal(G0, G).
edcg_infer_goal(call(PredId, Mode, Vars, EDCGArgs, Builtin, Context, Name) -
		Info, 
		call(PredId, Mode, Vars, EDCGArgs, Builtin, Context, Name) -
		Info, EDCGInfer0, EDCGInfer) :-
	goal_info_get_atomic_goal_id(Info, GoalId),
	edcg_infer_pred_call(PredId, Vars, EDCGArgs, GoalId,
		EDCGInfer0, EDCGInfer).
edcg_infer_goal(generic_call(GenericCall, Args, C, D) - Info,
		generic_call(GenericCall, Args, C, D) - Info, 
		EDCGInfer, EDCGInfer).
edcg_infer_goal(edcg_goal(Declared, Inferred0, Goal0) - Info,
		edcg_goal(Declared, Inferred, Goal) - Info, 
		EDCGInfer0, EDCGInfer) :-
	edcg_infer_begin_goal(Declared, EDCGInfer0, EDCGInfer1),
	edcg_infer_goal(Goal0, Goal, EDCGInfer1, EDCGInfer2),
	edcg_infer_end_goal(Inferred0, Inferred, EDCGInfer2,
		EDCGInfer).
edcg_infer_goal(unify(A, B, Mode, Category, UnifyContext) - Info,
		unify(A, B, Mode, Category, UnifyContext) - Info) -->
	{ goal_info_get_atomic_goal_id(Info, GoalId) },
	edcg_infer_unif(B, GoalId).
edcg_infer_goal(switch(_, _, _, _) - _Info, _) -->
	{ error("edcg_infer_goal: unexpected switch") }.
edcg_infer_goal(pragma_foreign_code(A, B, C, D, E, F, G) - Info,
		pragma_foreign_code(A, B, C, D, E, F, G) - Info) --> [].
edcg_infer_goal(bi_implication(LHS0, RHS0) - Info, 
		bi_implication(LHS, RHS) - Info) -->
	edcg_infer_goal(LHS0, LHS),
	edcg_infer_goal(RHS0, RHS).

:- pred edcg_infer_disj(list(hlds_goal), list(hlds_goal), edcg_infer,
		edcg_infer).
:- mode edcg_infer_disj(in, out, in, out) is det.

edcg_infer_disj(Goals0, Goals, EDCGInfer0, EDCGInfer) :-
	edcg_infer_disj_2(Goals0, Goals, EDCGInfer0, EDCGInferList),
	edcg_infer_join(EDCGInferList, EDCGInfer0, EDCGInfer).

:- pred edcg_infer_disj_2(list(hlds_goal), list(hlds_goal), edcg_infer,
		list(edcg_infer)).
:- mode edcg_infer_disj_2(in, out, in, out) is det.

edcg_infer_disj_2([], [], _, []).
edcg_infer_disj_2([Goal0|Goals0], [Goal|Goals], EDCGInferIn, 
		[EDCGInfer | EDCGInferList]) :-
	edcg_infer_goal(Goal0, Goal, EDCGInferIn, EDCGInfer),
	edcg_infer_disj_2(Goals0, Goals, EDCGInferIn, EDCGInferList).

:- pred edcg_infer_if_then_else(hlds_goal, hlds_goal, hlds_goal,
		hlds_goal, hlds_goal, hlds_goal, edcg_infer, edcg_infer).
:- mode edcg_infer_if_then_else(in, in, in, out, out, out, in, out) is det.

edcg_infer_if_then_else(A0, B0, C0, A, B, C, EDCGInfer0,
		EDCGInfer) :-
	edcg_infer_goal(A0, A, EDCGInfer0, EDCGInfer1),
	edcg_infer_goal(B0, B, EDCGInfer1, EDCGInfer2),
	edcg_infer_goal(C0, C, EDCGInfer0, EDCGInfer3),
	edcg_infer_join([EDCGInfer2, EDCGInfer3], EDCGInfer0,
		EDCGInfer).

:- pred edcg_infer_pred_call(pred_id, list(prog_var), edcgs, atomic_goal_id,
		edcg_infer, edcg_infer).
:- mode edcg_infer_pred_call(in, in, in, in, in, out) is det.

edcg_infer_pred_call(PredId, Vars, EDCGs, Id, EDCGInfer0, EDCGInfer) :-
	EDCGInfer0^module_info = ModuleInfo,
	module_info_pred_info(ModuleInfo, PredId, PredInfo),
	list__length(Vars, CallArity),
	pred_info_visual_arity(PredInfo, VisualArity),
	(
		% Check to see if it was an expanded edcg pred call.
		CallArity = VisualArity
	->
		pred_info_edcg_args(PredInfo, 
			pred_edcg_info(Declared, Inferred, _)),
		(
			EDCGInfer0^is_inferring = yes
		->
			EDCGForms = Inferred
		;
			EDCGForms = Declared
		),
		assoc_list__keys(EDCGs, EDCGArgs),
		% The set of edcgs listed explicitly at the call site.
		set__list_to_set(EDCGArgs, EDCGSet),
		edcg_infer_pred_call_2(EDCGForms, EDCGSet, Id, 
			EDCGInfer0, EDCGInfer)
	;
		EDCGInfer = EDCGInfer0
	).

:- pred edcg_infer_pred_call_2(edcg_forms, set(edcg_arg), atomic_goal_id,
		edcg_infer, edcg_infer).
:- mode edcg_infer_pred_call_2(in, in, in, in, out) is det.

edcg_infer_pred_call_2([], _, _, EDCGInfer, EDCGInfer).
edcg_infer_pred_call_2([EDCGArg - Form|EDCGForms], EDCGSet, Id,
		EDCGInfer0, EDCGInfer) :-
	(
		% Is the edcg argument listed explicitly at call site
		set__member(EDCGArg, EDCGSet)
	->
		EDCGInfer1 = EDCGInfer0
	;
		edcg_form_update(EDCGArg, Form, Id, 
			EDCGInfer0, EDCGInfer1)
	),
	edcg_infer_pred_call_2(EDCGForms, EDCGSet, Id, 
		EDCGInfer1, EDCGInfer).

:- pred edcg_infer_unif(unify_rhs, atomic_goal_id, 
		edcg_infer, edcg_infer).
:- mode edcg_infer_unif(in, in, in, out) is det.

edcg_infer_unif(RHS, Id, EDCGInfer0, EDCGInfer) :-
	(
		RHS = edcg_op(EDCGArg, Operator)
	->
		operator_to_form(Operator, Form),
		edcg_form_update(EDCGArg, Form, Id, 
			EDCGInfer0, EDCGInfer)
	;
		EDCGInfer = EDCGInfer0
	).

	% edcg_infer_join(EDCGInferList, EDCGInferIn, EDCGInferOut)
	% 	EDCGInferOut is all of the edcg_infer data in EDCGInferList
	% 	combined. If EDCGList is empty then EDCGInferOut is EDCGInferIn.
	%	This predicate is used to combine edcg_infer data from
	%	disjuncts, if-then-elses and clauses.
:- pred edcg_infer_join(list(edcg_infer), edcg_infer, edcg_infer).
:- mode edcg_infer_join(in, in, out) is det.

edcg_infer_join([], EDCGInfer, EDCGInfer).
edcg_infer_join([EDCGInfer0|EDCGInfers], _, EDCGInfer) :-
	edcg_infer_join_2(EDCGInfer0, EDCGInfers, EDCGInfer).

:- pred edcg_infer_join_2(edcg_infer, list(edcg_infer), edcg_infer).
:- mode edcg_infer_join_2(in, in, out) is det.

edcg_infer_join_2(EDCGInfer, [], EDCGInfer).
edcg_infer_join_2(EDCGInfer0, [EDCGInferIn|EDCGInfers], EDCGInfer) :-
	EDCGInfer0^inferred = EDCGFormMap0,
	EDCGInferIn^inferred = EDCGFormMap1,
	map__union(edcg_form_join, EDCGFormMap0, EDCGFormMap1, 
		EDCGFormMap),
	EDCGInfer0^goal_inferred = EDCGGoalFormMaps0,
	EDCGInferIn^goal_inferred = EDCGGoalFormMaps1,
	edcg_infer_join_3(EDCGGoalFormMaps0, EDCGGoalFormMaps1,
		EDCGGoalFormMaps),
	EDCGInfer0^is_changed = Changed0,
	EDCGInferIn^is_changed = Changed1,
	bool__or(Changed0, Changed1, Changed),
	EDCGInfer1 = EDCGInfer0^inferred := EDCGFormMap,
	EDCGInfer2 = EDCGInfer1^goal_inferred := EDCGGoalFormMaps,
	EDCGInfer3 = EDCGInfer2^is_changed := Changed,
	edcg_infer_join_2(EDCGInfer3, EDCGInfers, EDCGInfer).

	% edcg_infer_join_3(EDCGFormMapList1, EDCGFormMapList2,
	% 		EDCGFormMapListOut)
	%	EDCGFormMapList1 and EDCGFormList2 are infer data for edcg
	%	goals. EDCGFormMapListOut is edcg goal infer data from
	%	EDCGFormMapList1 and EDCGFormMapList2 combined.
:- pred edcg_infer_join_3(list(edcg_form_map), list(edcg_form_map),
		list(edcg_form_map)).
:- mode edcg_infer_join_3(in, in, out) is det.

edcg_infer_join_3([], [], []).
edcg_infer_join_3([_|_], [], _) :-
	error("edcg_infer_join_3: list length mismatch.").
edcg_infer_join_3([], [_|_], _) :-
	error("edcg_infer_join_3: list length mismatch.").
edcg_infer_join_3([EDCGFormMap1|Rest1], [EDCGFormMap2|Rest2],
		[EDCGFormMap|Rest]) :-
	map__union(edcg_form_join, EDCGFormMap1, EDCGFormMap2, 
		EDCGFormMap),
	edcg_infer_join_3(Rest1, Rest2, Rest).

	% edcg_infer_begin_goal(EDCGGoalInfo, EDCGInfer0, EDCGInfer)
	%	This predicate is called before inferring the edcg forms for an
	%	edcg goal. EDCGInfer0 is updated to EDCGInfer according the to
	%	forms of hidden arguments EDCGGoalInfo.
:- pred edcg_infer_begin_goal(edcg_goal_info, edcg_infer, edcg_infer).
:- mode edcg_infer_begin_goal(in, in, out) is det.

edcg_infer_begin_goal(EDCGs, EDCGInfer0, EDCGInfer) :-
	assoc_list__keys(EDCGs, EDCGArgs),
	map__init(EDCGFormMap0),
	new_edcg_form_map(EDCGArgs, EDCGFormMap0, EDCGFormMap),
	EDCGInfer0^goal_inferred = GoalInferred0,
	GoalInferred = EDCGFormMap.GoalInferred0,
	EDCGInfer = EDCGInfer0^goal_inferred := GoalInferred.

	% edcg_infer_end_goal(Inferred0, Inferred, EDCGInfer0, EDCGInfer)
	%	This predicate is called after inferring the edcg forms for an
	%	edcg goal. EDCGInfer is EDCGInfer0 with the edcg goal form data
	%	removed.
:- pred edcg_infer_end_goal(edcg_forms, edcg_forms, 
		edcg_infer, edcg_infer).
:- mode edcg_infer_end_goal(in, out, in, out) is det.

edcg_infer_end_goal(Inferred0, Inferred, EDCGInfer0, EDCGInfer) :-
	EDCGInfer0^goal_inferred = GoalInferred0,
	(
		GoalInferred0 = [GoalInfer|GoalInferred]
	->
		EDCGInfer1 = EDCGInfer0^goal_inferred := GoalInferred,
		edcg_form_map_to_edcg_forms(GoalInfer, Inferred)
	;
		error("edcg_infer_end_goal: no goal to end.")
	),
	(
		% Check whether the inferred forms for the edcg goal has changed
		% since last iteration.
		EDCGInfer1^is_changed = no,
		edcg_forms_identical(Inferred0, Inferred)
	->
		EDCGInfer = EDCGInfer1^is_changed := yes
	;
		EDCGInfer = EDCGInfer1
	).

%---------------------------------------------------------------------------%

:- type edcg_form_map == map(edcg_arg, pair(form, atomic_goal_id)).

:- pred new_edcg_form_map(list(edcg_arg),
		edcg_form_map, edcg_form_map).
:- mode new_edcg_form_map(in, in, out) is det.

new_edcg_form_map([], EDCGFormMap, EDCGFormMap).
new_edcg_form_map([EDCGArg | EDCGArgs], EDCGFormMap0,
		EDCGFormMap) :-
	init_atomic_goal_id(Id),
	map__det_insert(EDCGFormMap0, EDCGArg, nothing - Id, 
		EDCGFormMap1),
	new_edcg_form_map(EDCGArgs, EDCGFormMap1, EDCGFormMap).

	% edcg_form_update(EDCGArg, Form, Id, EDCGInfer0, EDCGInfer)
	%	EDCGInfer is EDCGInfer0 with the form of EDCGArg updated.
:- pred edcg_form_update(edcg_arg, form, atomic_goal_id, 
		edcg_infer, edcg_infer).
:- mode edcg_form_update(in, in, in, in, out) is det.

edcg_form_update(EDCGArg, Form, Id, EDCGInfer0, EDCGInfer) :-
	(
		% Update the edcg goal inferred forms for EDCGArg
		EDCGInfer0^goal_inferred = GoalInferred0,
		edcg_form_update_goal_inferred(EDCGArg, Form, Id,
			GoalInferred0, GoalInferred)
	->
		EDCGInfer = EDCGInfer0^goal_inferred := GoalInferred
	;
		% Update the predicate inferred forms for EDCGArg
		EDCGInfer0^inferred = Inferred0,
		edcg_form_update_inferred(EDCGArg, Form, Id, Inferred0,
			Inferred),
		EDCGInfer = EDCGInfer0^inferred := Inferred
	).

	% Update predicate inferred forms
:- pred edcg_form_update_inferred(edcg_arg, form, atomic_goal_id, 
		edcg_form_map, edcg_form_map).
:- mode edcg_form_update_inferred(in, in, in, in, out) is det.

edcg_form_update_inferred(EDCGArg, EDCGForm, EDCGId,
		EDCGFormMap0, EDCGFormMap) :-
	(
		map__search(EDCGFormMap0, EDCGArg, Form0 - Id0)
	->
		update_edcg_form(Form0, Id0, EDCGForm, EDCGId, Form),
		map__det_update(EDCGFormMap0, EDCGArg, Form - EDCGId, 
			EDCGFormMap)
	;
		map__det_insert(EDCGFormMap0, EDCGArg, EDCGForm-EDCGId,
			EDCGFormMap)
	).

	% Update edcg goal inferred forms.
:- pred edcg_form_update_goal_inferred(edcg_arg, form, atomic_goal_id, 
		list(edcg_form_map), list(edcg_form_map)).
:- mode edcg_form_update_goal_inferred(in, in, in, in, out) is semidet.

edcg_form_update_goal_inferred(EDCGArg, EDCGForm, EDCGId,
		[EDCGFormMap0|Rest0], [EDCGFormMap|Rest]) :-
	(
		map__search(EDCGFormMap0, EDCGArg, Form0 - Id0)
	->
		update_edcg_form(Form0, Id0, EDCGForm, EDCGId, Form),
		map__det_update(EDCGFormMap0, EDCGArg, Form - EDCGId, 
			EDCGFormMap),
		Rest = Rest0
	;
		EDCGFormMap = EDCGFormMap0,
		edcg_form_update_goal_inferred(EDCGArg, EDCGForm,
			EDCGId, Rest0, Rest)
	).

	% Assumes that the edcg forms are sorted.
:- pred edcg_forms_identical(edcg_forms, edcg_forms).
:- mode edcg_forms_identical(in, in) is semidet.

edcg_forms_identical(EDCGForms, EDCGForms).

	% Update the inferred form of an edcg argument.
:- pred update_edcg_form(form, atomic_goal_id, form, atomic_goal_id, form).
:- mode update_edcg_form(in, in, in, in, out) is det.

update_edcg_form(nothing, _, Form, _, Form).
update_edcg_form(changed, _, _, _, changed).
update_edcg_form(passed, _, nothing, _, passed).
update_edcg_form(passed, _, passed, _, passed).
update_edcg_form(passed, _, changed, _, changed).
update_edcg_form(passed, _, produced, _, changed).
update_edcg_form(produced, _, nothing, _, produced).
update_edcg_form(produced, _, produced, _, produced).
update_edcg_form(produced, IdOld, changed, IdNew, Form) :-
	(
		IdOld = IdNew
	->
		Form = changed
	;
		Form = produced
	).
update_edcg_form(produced, IdOld, passed, IdNew, Form) :-
	(
		IdOld = IdNew
	->
		Form = changed
	;
		Form = produced
	).

	% Join the inferred forms of an edcg argument from disjuncts,
	% if-the-elses, and clauses.
:- pred edcg_form_join(pair(form, atomic_goal_id), pair(form, atomic_goal_id),
		pair(form, atomic_goal_id)).
:- mode edcg_form_join(in, in, out) is det.

edcg_form_join(nothing - _, Form - Id, Form - Id).
edcg_form_join(changed - _, _ - Id, changed - Id).
edcg_form_join(produced - _, changed - Id, changed - Id).
edcg_form_join(produced - _, produced - Id, produced - Id).
edcg_form_join(produced - _, passed - Id, changed - Id).
edcg_form_join(produced - _, nothing - Id, produced - Id).
edcg_form_join(passed - _, changed - Id, changed - Id).
edcg_form_join(passed - _, produced - Id, changed - Id).
edcg_form_join(passed - _, passed - Id, passed - Id).
edcg_form_join(passed - _, nothing - Id, passed - Id).

:- pred operator_to_form(edcg_operator, form).
:- mode operator_to_form(in, out) is det.

operator_to_form(current, passed).
operator_to_form(next, produced).

%---------------------------------------------------------------------------%

:- type edcg_infer 
	---> edcg_infer(
		inferred 	:: edcg_form_map,
			% inferred edcg forms for a predicate.
		goal_inferred	:: list(edcg_form_map),
			% inferred edcg forms for edcg goals.
		module_info :: module_info,
		is_changed	:: bool, 
			% is_changed indicates whether EDCG goal inferrences
			% have changed
		is_inferring	:: bool
			% Are we inferrring edcg arguments
	).

:- pred edcg_infer_init(module_info, bool, edcg_infer).
:- mode edcg_infer_init(in, in, out) is det.

edcg_infer_init(Module, Inferring, 
		edcg_infer(Inferred, [], Module, no, Inferring)) :-
	map__init(Inferred).

	% edcg_infer_final(EDCGInfer, EDCGForms, 
	%	Changed, NeedsEDCGTransform0, NeedsEDCGTransform) :-
	% Changed is true if during the edcgs inferred changed since last
	% time. NeedsEdcgTransform is true if the predicate needs an Edcg
	% transform performed upon it.
:- pred edcg_infer_final(edcg_infer, edcg_forms, bool, bool, bool).
:- mode edcg_infer_final(in, out, out, in, out) is det.

edcg_infer_final(EDCGInfer, EDCGForms, Changed,
		NeedsEDCGTransform0, NeedsEDCGTransform) :-
	EDCGInfer^is_changed = Changed,
	EDCGInfer^inferred = Inferred,
	edcg_form_map_to_edcg_forms(Inferred, EDCGForms),
	(
		% If edcg arguments have been declared, inferred or the
		% predicate contains a clause in which some EDCG construct is
		% used (NeedsEDCGTransform0) then we must perform an EDCG
		% transformation on the predicate.
		(
			EDCGForms = [_|_]
		;
			NeedsEDCGTransform0 = yes
		)
	->
		NeedsEDCGTransform = yes
	;
		NeedsEDCGTransform = no
	).

:- pred edcg_form_map_to_edcg_forms(edcg_form_map, edcg_forms).
:- mode edcg_form_map_to_edcg_forms(in, out) is det.

edcg_form_map_to_edcg_forms(EDCGFormMap, EDCGForms) :-
	map__to_sorted_assoc_list(EDCGFormMap, EDCGForms0),
	list__map(edcg_form_convert, EDCGForms0, EDCGForms).

:- pred edcg_form_convert(pair(edcg_arg, pair(form, atomic_goal_id)),
		pair(edcg_arg, form)).
:- mode edcg_form_convert(in, out) is det.

edcg_form_convert(EDCGArg - (Form - _), EDCGArg - Form).

%---------------------------------------------------------------------------%

:- type var_set_types == pair(prog_varset, vartypes).

expand_edcgs(ModuleInfo0, ModuleInfo) -->
	infer_edcgs(ModuleInfo0, ModuleInfo1),
	{ module_info_predids(ModuleInfo1, PredIds) },
	{ module_info_edcgs(ModuleInfo1, Edcgs) },
	globals__io_lookup_bool_option(infer_edcgs, Inferring),
	{ edcg_info_init(Edcgs, Inferring, EDCGInfo) },
	expand_edgs_2(PredIds, EDCGInfo, ModuleInfo1, ModuleInfo).

:- pred expand_edgs_2(list(pred_id), edcg_info, module_info, module_info,
		io__state, io__state).
:- mode expand_edgs_2(in, in, in, out, di, uo) is det.

expand_edgs_2([], _, ModuleInfo, ModuleInfo) --> [].
expand_edgs_2([PredId | PredIds], EDCGInfo, ModuleInfo0, ModuleInfo) -->
	{ module_info_preds(ModuleInfo0, Preds0) },
	{ map__lookup(Preds0, PredId, PredInfo0) },
	(
		{ pred_info_is_imported(PredInfo0)
		; pred_info_is_pseudo_imported(PredInfo0) }
	->
		{ ModuleInfo2 = ModuleInfo0 }
	;
		expand_pred(PredId, PredInfo0, PredInfo, ModuleInfo0, 
			EDCGInfo, Errors),
		maybe_edcg_error(Errors, PredId, ModuleInfo0,
			ModuleInfo1),
		{ map__det_update(Preds0, PredId, PredInfo, Preds) },
		{ module_info_get_predicate_table(ModuleInfo1, PredTable0) },
		{ predicate_table_set_preds(PredTable0, Preds, PredTable) },
		{ module_info_set_predicate_table(ModuleInfo1, PredTable,
			ModuleInfo2) }
	),
	expand_edgs_2(PredIds, EDCGInfo, ModuleInfo2, ModuleInfo).

:- pred expand_pred(pred_id, pred_info, pred_info, module_info, 
		edcg_info, list(edcg_error), io__state, io__state).
:- mode expand_pred(in, in, out, in, in, out, di, uo) is det.

expand_pred(PredId, PredInfo0, PredInfo, ModuleInfo, EDCGInfo0, Errors) -->
	{ pred_info_edcg_args(PredInfo0, 
		pred_edcg_info(_, _, NeedsEdcgTransform)) },
	(
		% We do not need to perform an edcg transformation on this
		% predicate.
		{ NeedsEdcgTransform = no }
	->
		{ PredInfo0 = PredInfo },
		{ Errors = [] }
	;
		{ expand_pred_2(PredInfo0, PredInfo, ModuleInfo, 
			EDCGInfo0, Errors, Inferred) },
		write_inference_message(PredId, PredInfo, ModuleInfo, Inferred)
	).

:- pred expand_pred_2(pred_info, pred_info, module_info, 
		edcg_info, list(edcg_error), bool).
:- mode expand_pred_2(in, out, in, in, out, out) is det.

expand_pred_2(PredInfo0, PredInfo, ModuleInfo, EDCGInfo0, Errors, 
		Inferred) :-
	pred_info_edcg_args(PredInfo0, 
		pred_edcg_info(DeclaredForms, InferredForms, _)),
	pred_info_visual_arity(PredInfo0, VisualArity),
	pred_info_context(PredInfo0, Context),
	pred_info_clauses_info(PredInfo0, ClausesInfo0),
	pred_info_procedures(PredInfo0, Procs0),
	pred_info_arg_types(PredInfo0, TVarSet, Eqtvars, VisualTypes),
	clauses_info_clauses(ClausesInfo0, Clauses0),
	clauses_info_varset(ClausesInfo0, VarSet0),
	clauses_info_headvars(ClausesInfo0, VisualHeadVars),
	clauses_info_vartypes(ClausesInfo0, VarTypes0),

	% Correct the total arity and the clause head visual and edcg
	% arguments if the edcg arguments were inferred.
	(
		EDCGInfo0^inferring = yes,
		DeclaredForms = []
	->
		EDCGForms = InferredForms,
		EDCGInfo1 = EDCGInfo0,
		(
			InferredForms = [_|_],
			Inferred = yes
		;
			InferredForms = [],
			Inferred = no
		)
	;
		EDCGForms = DeclaredForms,
			% Check the declared forms against the inferred forms.
		edcg_info_check_forms(DeclaredForms, InferredForms, Context, 
			EDCGInfo0, EDCGInfo1),
		Inferred = no
	),
	list__append(VisualHeadVars, EDCGHeadVars, HeadVars),

		% Update the predicate's type information
	get_edcg_pred_types(ModuleInfo, EDCGForms, EDCGTypes),
	list__length(EDCGTypes, EDCGArity),
	TotalArity = VisualArity + EDCGArity,
	make_n_fresh_vars("EDCGHeadVar__", VisualArity,
		TotalArity, VarSet0, EDCGHeadVars, VarSet1),
	map__det_insert_from_corresponding_lists(VarTypes0, EDCGHeadVars,
		EDCGTypes, VarTypes1),
	list__append(VisualTypes, EDCGTypes, Types),
		% Expand edcgs
	EDCGInfo2 = EDCGInfo1^varsettypes := (VarSet1 - VarTypes1),
	edcg_info_initial_state(EDCGForms, EDCGInfo2, EDCGInfo3),
	expand_clauses(Clauses0, Clauses, HeadVars, EDCGForms, InferredForms,
		EDCGHeadVars, VisualArity, ModuleInfo, EDCGInfo3, EDCGInfo4),

	update_procedures(EDCGForms, Context, EDCGTypes,
		VisualArity, ModuleInfo, Procs0, Procs, EDCGInfo4,
		EDCGInfo),

	EDCGInfo^varsettypes = (VarSet - VarTypes),
	EDCGInfo^errors = Errors,

	clauses_info_set_vartypes(ClausesInfo0, VarTypes, ClausesInfo1),
	clauses_info_set_headvars(ClausesInfo1, HeadVars, ClausesInfo2),
	clauses_info_set_varset(ClausesInfo2, VarSet, ClausesInfo3),
	clauses_info_set_clauses(ClausesInfo3, Clauses, ClausesInfo),
	pred_info_set_clauses_info(PredInfo0, ClausesInfo, PredInfo1),
	pred_info_set_arg_types(PredInfo1, TVarSet, Eqtvars, Types, 
		PredInfo2),
	pred_info_set_procedures(PredInfo2, Procs, PredInfo3),
	pred_info_set_arity(PredInfo3, TotalArity, PredInfo).

:- pred expand_clauses(list(clause), list(clause), list(prog_var), edcg_forms,
		edcg_forms, list(prog_var), arity, module_info,
		edcg_info, edcg_info).
:- mode expand_clauses(in, out, in, in, in, in, in, in, in, out) is det.

expand_clauses([], [], _, _, _, _, _, _, EDCGInfo, EDCGInfo).
expand_clauses([Clause0 | Clauses0], [Clause | Clauses], HeadVars,
		EDCGForms, InferredForms, EDCGHeadVars, VisualArity, ModuleInfo,
		EDCGInfoIn0, EDCGInfo) :-
	Clause0 = clause(Ids, Body0, MaybeEdcg, Context),
	Clause = clause(Ids, Body, MaybeEdcg, Context),

	(	
		MaybeEdcg = edcg_no,	% functor -->> is not used
		( EDCGForms = [_|_] ; InferredForms = [_|_] )
	->
		% -->> should have been used
		edcg_info_error(functor_error(Context),
			EDCGInfoIn0, EDCGInfoIn1)
	;
		MaybeEdcg = edcg_yes,	% functor -->> is used
		EDCGForms = [],
		InferredForms = []
	->
		% Unnecessary use of functor -->>
		edcg_info_error(functor_warning(Context),
			EDCGInfoIn0, EDCGInfoIn1)
	;
		EDCGInfoIn1 = EDCGInfoIn0
	),

	expand_body(Body0, Body1, ModuleInfo, EDCGInfoIn1, EDCGInfoOut0),
	edcg_head_vars(EDCGForms, EDCGInfoIn1, EDCGInfoOut0, 
		EDCGVars),
	insert_unifications(EDCGHeadVars, EDCGVars, Context, VisualArity,
		Body1, Body2),
	map__init(Empty),
	EDCGInfoOut0^varsettypes = (VarSet0 - VarTypes),
	implicitly_quantify_clause_body(HeadVars, Body2, VarSet0, Empty,
		Body, VarSet, _, _Warnings),
	EDCGInfoOut = EDCGInfoOut0^varsettypes := (VarSet - VarTypes),
	edcg_info_merge(EDCGInfoIn1, EDCGInfoOut, EDCGInfoIn),

	expand_clauses(Clauses0, Clauses, HeadVars, EDCGForms, InferredForms,
		EDCGHeadVars, VisualArity, ModuleInfo, EDCGInfoIn, EDCGInfo).

:- pred expand_body(hlds_goal, hlds_goal, module_info,
		edcg_info, edcg_info).
:- mode expand_body(in, out, in, in, out) is det.

expand_body(Body0 - Info, Body - Info, ModuleInfo, EDCGInfo0, EDCGInfo) :-
	Body0 = conj(Goals0), 
	Body  = conj(Goals), 
	expand_conj(Goals0, Goals, ModuleInfo, EDCGInfo0, EDCGInfo).

expand_body(Body0 - Info, Body - Info, ModuleInfo, EDCGInfo0, EDCGInfo) :-
	Body0 = par_conj(Goals0, SM),
	Body  =  par_conj(Goals, SM),
	expand_par_conj(Goals0, Goals, ModuleInfo, EDCGInfo0, EDCGInfo).

expand_body(Body0 - Info, Body - Info, ModuleInfo, EDCGInfo0, EDCGInfo) :-
	Body0 = disj(Goals0,Store), 
	Body  =  disj(Goals,Store), 
	expand_disj(Goals0, ModuleInfo, EDCGInfo0, GoalsAndEDCGInfo,
		EDCGInfo1),
	update_disj(GoalsAndEDCGInfo, EDCGInfo1, Goals, EDCGInfo).

expand_body(Body0 - Info, Body - Info, ModuleInfo, EDCGInfo0, EDCGInfo) :-
	Body0 = call(PredId,ProcId,Vars0,EDCGVars,BIState,UContext,Name),
	Body  = call(PredId,ProcId,Vars,[],BIState,UContext,Name),
	module_info_preds(ModuleInfo, Preds),
	map__lookup(Preds, PredId, CalleePredInfo),
	pred_info_visual_arity(CalleePredInfo, VisualArity),
	goal_info_get_context(Info, Context),
	goal_info_get_atomic_goal_id(Info, GoalId),
	list__length(Vars0, CallArity),
	(
		% Check whether the predicate is called in expanded form
		CallArity = VisualArity
	->
		pred_info_edcg_args(CalleePredInfo, 
			pred_edcg_info(Declared, Inferred, _)),
		( 
			EDCGInfo0^inferring = yes,
			Declared = []
		->
			EDCGForms = Inferred
		;
			EDCGForms = Declared
		),
		pred_call_edcg_args(GoalId, EDCGForms, 
			EDCGVars, Context, PredId, Vars1, EDCGInfo0, EDCGInfo),
		list__append(Vars0, Vars1, Vars)
	;
		% Predicate was called in expanded form.
		Vars = Vars0,
		(
			EDCGVars = [],
			edcg_info_error(pred_call_warning(PredId, Context), 
				EDCGInfo0, EDCGInfo)
		;
			% Predicate was called in expanded form with explicit
			% edcg arguments listed.
			EDCGVars = [_|_],
			edcg_info_error(
				pred_call_expanded_error(PredId, Context),
				EDCGInfo0, EDCGInfo)
		)
	).

expand_body(Body0 - Info, Body - Info, _ModuleInfo,
		EDCGInfo0, EDCGInfo) :-
	Body0 = unify(Var,RHS0,Mode,Unification,UnifyContext),
	Body  = unify(Var,RHS,Mode,Unification,UnifyContext),
	goal_info_get_context(Info, Context),
	goal_info_get_atomic_goal_id(Info, GoalId),
	expand_unify_rhs(RHS0, RHS, Context, GoalId, 
		EDCGInfo0, EDCGInfo).

expand_body(Body0 - Info, Body - Info, ModuleInfo,
		EDCGInfo0, EDCGInfo) :-
	Body0 = if_then_else(Vars,Goali0,Goalt0,Goale0,Store),
	Body  = if_then_else(Vars,Goali,Goalt,Goale,Store),
	expand_body(Goali0, Goali, ModuleInfo, EDCGInfo0, EDCGInfo1),
	expand_body(Goalt0, Goalt1, ModuleInfo, EDCGInfo1, EDCGInfoThen),
	edcg_info_merge(EDCGInfo0, EDCGInfoThen, EDCGInfo2),
	expand_body(Goale0, Goale1, ModuleInfo, EDCGInfo2, EDCGInfoElse),
	edcg_info_combine(EDCGInfoThen, EDCGInfoElse, EDCGInfo3),
	edcg_info_new(EDCGInfo3, EDCGInfo),
	update_goal(Goalt1, EDCGInfoThen, EDCGInfo, Goalt),
	update_goal(Goale1, EDCGInfoElse, EDCGInfo, Goale).

expand_body(Body0 - Info, Body - Info, ModuleInfo,
		EDCGInfo0, EDCGInfo) :-
	Body0 = not(Goal0),
	Body  = not(Goal),
	expand_body(Goal0, Goal, ModuleInfo, EDCGInfo0, EDCGInfo).

expand_body(Body0 - Info, Body - Info, ModuleInfo,
		EDCGInfo0, EDCGInfo) :-
	Body0 = some(Vars,CanRemove,Goal0),
	Body = some(Vars,CanRemove,Goal),
	expand_body(Goal0, Goal, ModuleInfo, EDCGInfo0, EDCGInfo).

expand_body(Body0 - Info, Body - Info, _ModuleInfo,
		EDCGInfo, EDCGInfo) :-
	Body0 = generic_call(GenericCall,Args,Modes,Det),
	Body = generic_call(GenericCall,Args,Modes,Det).

expand_body(Body0 - Info, Body - Info, ModuleInfo,
		EDCGInfo0, EDCGInfo) :-
	Body0 = edcg_goal(EdcgGoalInfo, Inferred, Goal0),
	expand_edcg_goal(EdcgGoalInfo, Goal0, ModuleInfo,
		EDCGInfo0, EDCGInfo1, Declared, Body - _Info),
	goal_info_get_context(Info, Context),
	% Check the declared forms against the inferred forms.
	edcg_info_check_forms(Declared, Inferred, Context,
		EDCGInfo1, EDCGInfo).

expand_body(Body0 - Info, Body - Info, _ModuleInfo,
		EDCGInfo, EDCGInfo) :-
	Body0 = pragma_foreign_code(A,B,C,D,E,F,G),
	Body  = pragma_foreign_code(A,B,C,D,E,F,G).

expand_body(Body0 - Info, Body - Info, _ModuleInfo,
		EDCGInfo, EDCGInfo) :-
	Body0 = switch(Var,Canfail,Cases,StoreMap),
	Body  = switch(Var,Canfail,Cases,StoreMap),
	% Switches are yet to be created.
	error("edcg:expand_body: unexpected switch").

expand_body(Body0 - Info, Body - Info, _ModuleInfo,
		EDCGInfo, EDCGInfo) :-
	Body0 = bi_implication(Goal1,Goal2),
	Body  = bi_implication(Goal1,Goal2),
	error("edcg:expand_body: unexpected bi-implication").

:- pred expand_conj(list(hlds_goal), list(hlds_goal), module_info,
		edcg_info, edcg_info).
:- mode expand_conj(in, out, in, in, out) is det.

expand_conj([], [], _, EDCGInfo, EDCGInfo).
expand_conj([Goal0 | Goals0], Goals, ModuleInfo,
		EDCGInfo0, EDCGInfo) :-
	expand_body(Goal0, Goal, ModuleInfo,
		EDCGInfo0, EDCGInfo1),
	 % edcg expansion may produce conjunctions that are not completely
	 % flattened.
	goal_to_conj_list(Goal, ConjList),
	expand_conj(Goals0, Goals1, ModuleInfo,
		EDCGInfo1, EDCGInfo),
	list__append(ConjList, Goals1, Goals).

:- pred expand_par_conj(list(hlds_goal), list(hlds_goal), module_info, 
		edcg_info, edcg_info).
:- mode expand_par_conj(in, out, in, in, out) is det.

expand_par_conj([], [], _, EDCGInfo, EDCGInfo).
expand_par_conj([Goal0 | Goals0], [Goal | Goals], ModuleInfo,
		EDCGInfo0, EDCGInfo) :-
	expand_body(Goal0, Goal, ModuleInfo, EDCGInfo0, EDCGInfo1),
	expand_par_conj(Goals0, Goals, ModuleInfo, EDCGInfo1, EDCGInfo).

:- pred expand_disj(list(hlds_goal), module_info, edcg_info,
		assoc_list(hlds_goal, edcg_info), edcg_info).
:- mode expand_disj(in, in, in, out, out) is det.

expand_disj([], _, EDCGInfo, [], EDCGInfo).
expand_disj([Goal0 | Goals0],  ModuleInfo, EDCGInfoIn0, 
		[Goal - EDCGInfoOut | GoalsAndEDCGInfo], EDCGInfo) :-
	expand_body(Goal0, Goal, ModuleInfo, EDCGInfoIn0, EDCGInfoOut),
	edcg_info_merge(EDCGInfoIn0, EDCGInfoOut, EDCGInfoIn),
	expand_disj_2(Goals0, ModuleInfo, EDCGInfoIn, GoalsAndEDCGInfo,
		EDCGInfoOut, EDCGInfo).

:- pred expand_disj_2(list(hlds_goal), module_info, edcg_info,
		assoc_list(hlds_goal, edcg_info), edcg_info, edcg_info).
:- mode expand_disj_2(in, in, in, out, in, out) is det.

expand_disj_2([], _, _, [], EDCGInfo, EDCGInfo).
expand_disj_2([Goal0 | Goals0],  ModuleInfo, EDCGInfoIn0,
		[Goal - EDCGInfoOut | GoalsAndEDCGInfo], 
		EDCGInfo0, EDCGInfo) :-
	expand_body(Goal0, Goal, ModuleInfo, EDCGInfoIn0, EDCGInfoOut),
	edcg_info_merge(EDCGInfoIn0, EDCGInfoOut, EDCGInfoIn),
	edcg_info_combine(EDCGInfo0, EDCGInfoOut, EDCGInfo1),
	expand_disj_2(Goals0, ModuleInfo, EDCGInfoIn, GoalsAndEDCGInfo,
		EDCGInfo1, EDCGInfo).

:- pred expand_unify_rhs(unify_rhs, unify_rhs, prog_context, atomic_goal_id,
		edcg_info, edcg_info).
:- mode expand_unify_rhs(in, out, in, in, in, out) is det.

expand_unify_rhs(var(Var), var(Var), _Context, _Id, 
		EDCGInfo, EDCGInfo).
expand_unify_rhs(functor(Id,Vars), functor(Id,Vars), _Context, _Id,
		EDCGInfo, EDCGInfo).
expand_unify_rhs(lambda_goal(A,B,C,D,E,F,G,H), lambda_goal(A,B,C,D,E,F,G,H), 
		_Context, _Id, EDCGInfo, EDCGInfo).
expand_unify_rhs(edcg_op(EDCGArg, current), var(Var), Context, Id,
		EDCGInfo0, EDCGInfo) :-
	(
		edcg_info_pass(Id, EDCGArg, Var0,
			EDCGInfo0, EDCGInfo1)
	->
		EDCGInfo = EDCGInfo1,
		Var = Var0
	;
		edcg_info_error(scope_error(current, Context, EDCGArg), 
			EDCGInfo0, EDCGInfo1),
			% Make it alive to avoid future errors
		edcg_info_birth(EDCGArg, EDCGInfo1, EDCGInfo),
		edcg_info_get_var(EDCGArg, EDCGInfo, Var)
	).
expand_unify_rhs(edcg_op(EDCGArg, next), var(Var), Context, Id,
		EDCGInfo0, EDCGInfo) :-
	(
		edcg_info_produce(Id, EDCGArg, Var0, 
			EDCGInfo0, EDCGInfo1)
	->
		EDCGInfo = EDCGInfo1,
		Var0 = Var
	;
		edcg_info_error(scope_error(next, Context, EDCGArg), 
			EDCGInfo0, EDCGInfo1),
			% Make it alive to avoid future errors
		edcg_info_birth(EDCGArg, EDCGInfo1, EDCGInfo),
		edcg_info_get_var(EDCGArg, EDCGInfo, Var)
	).

:- pred expand_edcg_goal(edcg_goal_info, hlds_goal, 
		module_info, edcg_info, edcg_info, edcg_forms, hlds_goal).
:- mode expand_edcg_goal(in, in, in, in, out, out, out) is det.

expand_edcg_goal(EdcgGoalInfo, Goal0, ModuleInfo,
		EDCGInfo0, EDCGInfo, EDCGForms, Goal) :-
	assoc_list__keys(EdcgGoalInfo, EDCGArgs),
	edcg_info_convert(EDCGArgs, EDCGInfo0, EDCGInfo1),
	expand_body(Goal0, Goal1, ModuleInfo, EDCGInfo1, EDCGInfo2),
	insert_edcg_unifs(EdcgGoalInfo, Goal1, Goal, 
		EDCGInfo1, EDCGInfo2, EDCGForms),
	edcg_info_revert(EDCGArgs, EDCGInfo0, EDCGInfo2, EDCGInfo).

%---------------------------------------------------------------------------%

get_edcg_pred_types(_, [], []).
get_edcg_pred_types(ModuleInfo, [EDCGArg - Form|Rest], 
	    TypeList) :-
	module_info_edcgs(ModuleInfo, EDCGTable),
	edcg_table_fetch_edcg_type(EDCGArg, EDCGTable, Type),
	( 
		Form = changed 
	->
		TypeList = [Type,Type|TypeList0]
	;
		Form = nothing
	->
		TypeList = TypeList0
	;
		TypeList = [Type|TypeList0]
	),
	get_edcg_pred_types(ModuleInfo, Rest, TypeList0).

	% Returns a list of modes that are to be appended onto the end of the
	% head mode list of a particular predicate.
:- pred get_edcg_pred_modes(edcg_forms, list(mode), 
		term__context, module_info, edcg_info, edcg_info, bool).
:- mode get_edcg_pred_modes(in, out, in, in, in, out, out) is det.

get_edcg_pred_modes([], [], _, _, EDCGInfo, EDCGInfo, no).
get_edcg_pred_modes([EDCGArg - Form|Rest], ModeList, Context,
		ModuleInfo, EDCGInfo0, EDCGInfo, FoundError) :-
	module_info_edcgs(ModuleInfo, EDCGTable),
	( 
		edcg_table_fetch_edcg_mode(EDCGArg, Form, EDCGTable, Modes)
	->
		get_edcg_pred_modes(Rest, ModeList0, Context,
			ModuleInfo, EDCGInfo0, EDCGInfo, FoundError),
		list__append(Modes, ModeList0, ModeList)
	;
		edcg_info_error(undef_mode_error(EDCGArg, Form, Context),	
			EDCGInfo0, EDCGInfo1),
		get_edcg_pred_modes(Rest, ModeList, Context,
			ModuleInfo, EDCGInfo1, EDCGInfo, _),
		FoundError = yes
	).

	% Adds edcg argument modes to the procedure calls.
:- pred update_procedures(edcg_forms, term__context, list(type), arity,
		module_info, proc_table, proc_table, edcg_info, edcg_info).
:- mode update_procedures(in, in, in, in, in, in, out, in, out) is det.

update_procedures(EDCGForms, Context, Types, VisualArity, ModuleInfo, 
		ProcTable0, ProcTable, EDCGInfo0, EDCGInfo) :-
	get_edcg_pred_modes(EDCGForms, Modes, Context,
		ModuleInfo, EDCGInfo0, EDCGInfo, FoundError),
	( FoundError = no ->
		map__keys(ProcTable0, ProcIds),
		update_procedures_2(ProcIds, Modes, Types, VisualArity,
			ProcTable0, ProcTable)
	;
		ProcTable = ProcTable0
	).

:- pred update_procedures_2(list(proc_id), list(mode), list(type), arity,
		proc_table, proc_table).
:- mode update_procedures_2(in, in, in, in, in, out) is det.

update_procedures_2([], _, _, _, ProcTable, ProcTable).
update_procedures_2([ProcId | Rest], EDCGModes, Types, VisualArity,
		ProcTable0, ProcTable) :-
	map__lookup(ProcTable0, ProcId, ProcInfo0),
	proc_info_argmodes(ProcInfo0, Modes0),
	proc_info_varset(ProcInfo0, VarSet0),
	proc_info_headvars(ProcInfo0, VisualVars),
	proc_info_vartypes(ProcInfo0, VarTypes0),
	list__append(Modes0, EDCGModes, Modes),
	list__length(Modes, TotalArity),
	make_n_fresh_vars("EDCGHeadVar__", VisualArity, TotalArity, VarSet0,
		EDCGVars, VarSet),
	map__det_insert_from_corresponding_lists(VarTypes0, EDCGVars,
		Types, VarTypes),
	list__append(VisualVars, EDCGVars, Vars),
	proc_info_set_vartypes(ProcInfo0, VarTypes, ProcInfo1),
	proc_info_set_argmodes(ProcInfo1, Modes, ProcInfo2),
	proc_info_set_varset(ProcInfo2, VarSet, ProcInfo3),
	proc_info_set_headvars(ProcInfo3, Vars, ProcInfo),
	map__det_update(ProcTable0, ProcId, ProcInfo, ProcTable1),
	update_procedures_2(Rest, EDCGModes, Types, VisualArity, 
		ProcTable1, ProcTable).

	% edcg_head_vars(FormsAndNamesList, EDCGInfoIn,
	% 	EDCGInfoOut, VisualArgs)
	% 
	% Given a list of FormsAndNames declared for a particular predicate,
	% the pre state (EDCGInfoIn) and post state of a clause for that
	% predicate, VisualAndEDCGArgs is a list edcg arguments for that
	% clause head.
:- pred edcg_head_vars(edcg_forms, edcg_info, edcg_info, list(prog_var)).
:- mode edcg_head_vars(in, in, in, out) is det.

edcg_head_vars([], _, _, []).
edcg_head_vars([EDCGArg - changed | Rest], EDCGInfo0, EDCGInfo1,
	    Vars) :-
	edcg_info_get_state_info(EDCGArg, EDCGInfo0,
		StateInfo1),
	state_info_get_var(StateInfo1, Var1),
	edcg_info_get_state_info(EDCGArg, EDCGInfo1,
		StateInfo2),
	state_info_get_var(StateInfo2, Var2),
	Vars = [Var1, Var2 | Vars0],
	edcg_head_vars(Rest, EDCGInfo0, EDCGInfo1, Vars0).
edcg_head_vars([EDCGArg - passed | Rest], EDCGInfo0, EDCGInfo1,
	    Vars) :-
	edcg_info_get_state_info(EDCGArg, EDCGInfo0,
		StateInfo),
	state_info_get_var(StateInfo, Var),
	Vars = [Var | Vars0],
	edcg_head_vars(Rest, EDCGInfo0, EDCGInfo1, Vars0).
edcg_head_vars([EDCGArg - produced|Rest], EDCGInfo0, EDCGInfo1,
	    Vars) :-
	edcg_info_get_state_info(EDCGArg, EDCGInfo1,
		StateInfo),
	state_info_get_var(StateInfo, Var),
	Vars = [Var | Vars0],
	edcg_head_vars(Rest, EDCGInfo0, EDCGInfo1, Vars0).
edcg_head_vars([_ - nothing|_], _, _, _) :-
	error("edcg_head_vars: nothing cannot be a user defined form.").

	% pred_call_edcg_args(Id, EDCGForms, EDCGExplicitArgs,
	% 	Context, PredId, Vars, EDCGInfoIn, EDCGInfoOut)
	% 
	% Vars is the list of edcg arguments to be appended onto the predicate
	% call.
:- pred pred_call_edcg_args(atomic_goal_id, edcg_forms, edcgs, 
		prog_context, pred_id, list(prog_var), edcg_info,
		edcg_info).
:- mode pred_call_edcg_args(in, in, in, in, in, out, in, out) is det.

pred_call_edcg_args(_, [], [], _, _, [], EDCGInfo, EDCGInfo).
pred_call_edcg_args(Id, [], [EDCGArg - _| Rest], Context, PredId, Vars, 
		EDCGInfo0, EDCGInfo) :-
	edcg_info_error(pred_call_explicit_error(PredId, Context, EDCGArg),
		EDCGInfo0, EDCGInfo1),
	pred_call_edcg_args(Id, [], Rest, Context, PredId, Vars, 
		EDCGInfo1, EDCGInfo).
pred_call_edcg_args(Id, [EDCGArg - Form | Rest], EDCGs0, Context, PredId, 
		Vars, EDCGInfo0, EDCGInfo) :-
	(
			% Was the edcg argument explicitly listed in the
			% predicate call
		get_explicit_edcg_args(EDCGArg, Form, Context, PredId, 
			EDCGs0, EDCGs1, ExplicitVars0, 
			EDCGInfo0, EDCGInfo1)
	->
		pred_call_edcg_args(Id, Rest, EDCGs1, Context, PredId, 
			Vars1, EDCGInfo1, EDCGInfo),
		list__append(ExplicitVars0, Vars1, Vars)
	;
		Form = passed,
		edcg_info_pass(Id, EDCGArg, Var, 
			EDCGInfo0, EDCGInfo1)
	->
		Vars = [Var | Vars0],
		pred_call_edcg_args(Id, Rest, EDCGs0, Context, PredId, 
			Vars0, EDCGInfo1, EDCGInfo)
	;
		Form = changed,
		edcg_info_change(Id, EDCGArg, Var1, Var2, 
			EDCGInfo0, EDCGInfo1)
	->
		Vars = [Var1, Var2 | Vars0],
		pred_call_edcg_args(Id, Rest, EDCGs0, Context, PredId, 
			Vars0, EDCGInfo1, EDCGInfo)
	;
		Form = produced,
		edcg_info_produce(Id, EDCGArg, Var, 
			EDCGInfo0, EDCGInfo1)
	->
		Vars = [Var | Vars0],
		pred_call_edcg_args(Id, Rest, EDCGs0, Context, PredId, 
			Vars0, EDCGInfo1, EDCGInfo)
	;
		edcg_info_error(pred_call_error(PredId, Context, EDCGArg),
			EDCGInfo0, EDCGInfo1),
		edcg_info_birth(EDCGArg, EDCGInfo1, EDCGInfo2),
		pred_call_edcg_args(Id, [EDCGArg - Form|Rest], EDCGs0, 
			Context, PredId, Vars, EDCGInfo2, EDCGInfo)
	).

	% Get the edcg arguments listed with the predicate call for an edcg
	% argument.
:- pred get_explicit_edcg_args(edcg_arg, form, prog_context, pred_id,
		edcgs, edcgs, list(prog_var), edcg_info, edcg_info).
:- mode get_explicit_edcg_args(in, in, in, in, in, out, out, in, out) 
		is semidet.

get_explicit_edcg_args(EDCGArg, Form, Context, PredId, 
		[EDCGArg0 - Vars | EDCGs0], EDCGs, 
		VarList, EDCGInfo0, EDCGInfo) :-
	(
		EDCGArg = EDCGArg0
	->
		EDCGs = EDCGs0,
		(
			(
				Form = changed,
				Vars = [_Var1, _Var2]
			;
				Form = passed,
				Vars = [_Var]
			;
				Form = produced,
				Vars = [_Var]
			)
		->
			VarList = Vars,
			EDCGInfo = EDCGInfo0
		;
			VarList = [],
			edcg_info_error(pred_call_form_error(Context,
				EDCGArg, Form, PredId),
				EDCGInfo0, EDCGInfo)
		)
	;
		get_explicit_edcg_args(EDCGArg, Form, Context, PredId,
			EDCGs0, EDCGs1, VarList, EDCGInfo0, EDCGInfo),
		EDCGs = [EDCGArg0 - Vars | EDCGs1]
	).

	% update_disj(DisjunctAndEDCGInfoList, EDCGInfoIn,
	% 	GoalInfo, HLDSGoalList, EDCGInfoOut)
	% 
	% Given a list of disjuncts it calls update_disjunct/8 on every
	% disjunct. HLDSGoalList is the final list of HLDSGoals with 
	% the appropriate unifications appended such that all edcg
	% variables in scope at the end of the disjunction represent
	% consistent variables accross disjunctions. EDCGInfoOut is the
	% consistent post state of the disjunctions.
:- pred update_disj(assoc_list(hlds_goal, edcg_info), edcg_info,
		list(hlds_goal), edcg_info).
:- mode update_disj(in, in, out, out) is det.

update_disj([], EDCGInfo, [], EDCGInfo).
update_disj([HldsGoal0 - EDCGInfo0 | Rest], EDCGInfo1, 
		[HldsGoal | GoalList], EDCGInfo) :-
	update_goal(HldsGoal0, EDCGInfo0, EDCGInfo1, HldsGoal),
	update_disj(Rest, EDCGInfo1, GoalList, EDCGInfo).


	% update_goal(HLDSGoalIn, Context, EDCGInfoIn,
	% 	EDCGInfoFinal, HLDSGoalOut)
	%
	% HLDSGoalOut is HLDSIn with a list of unifications appended onto it.
	% The unifications are to match variables across disjunctions or
	% the THEN and ELSE parts of if-then-elses. All edcg argument
	% variables in EDCGInfoIn, that do not match their corresponding
	% edcg argument variable in EDCGInfoFinal are unified with it. 
:- pred update_goal(hlds_goal, edcg_info, edcg_info, hlds_goal).
:- mode update_goal(in, in, in, out) is det.

update_goal(HldsGoal0, EDCGInfo0, EDCGInfo1, HldsGoal) :-
	EDCGInfo0^state_info_table = StateTable0,
	EDCGInfo1^state_info_table = StateTable1,
	map__keys(StateTable0, EDCGArgs),
	HldsGoal0 = _Goal - HldsGoalInfo,
	goal_info_get_context(HldsGoalInfo, Context),
	update_goal_2(EDCGArgs, Context, StateTable0, StateTable1, 
		HldsGoals),
	goal_to_conj_list(HldsGoal0, ConjList),
	list__append(ConjList, HldsGoals, HldsGoalList),
	conj_list_to_goal(HldsGoalList, HldsGoalInfo, HldsGoal).

:- pred update_goal_2(list(edcg_arg), term__context,
	    state_info_table, state_info_table, list(hlds_goal)).
:- mode update_goal_2(in, in, in, in, out) is det.

update_goal_2([], _, _, _, []).
update_goal_2([EDCGArg|EDCGArgList], Context,
	    StateTable0, StateTable1, Goals) :-
	map__lookup(StateTable0, EDCGArg, StateInfo0),
	map__lookup(StateTable1, EDCGArg, StateInfo1),
	( 
		state_info_is_alive(StateInfo0),
		state_info_is_alive(StateInfo1)
	->
		state_info_get_var(StateInfo0, Var0),
		state_info_get_var(StateInfo1, Var1),
		create_atomic_unification(Var0, var(Var1), Context, 
			explicit, [], Goal),
		Goals = [Goal | Goals0],
		update_goal_2(EDCGArgList, Context,
			StateTable0, StateTable1, Goals0)
	; 
		state_info_is_alive(StateInfo0)
	->
		error("EDCG variable mismatch across goals.")
	;
		state_info_is_alive(StateInfo1)
	->
		error("EDCG variable mismatch across goals.")
	;
		update_goal_2(EDCGArgList, Context,
			StateTable0, StateTable1, Goals)
	).

	% Insert unifications at the start of an edcg goal, to unify edcg
	% arguments with the edcg goal head vars.
:- pred insert_edcg_unifs(edcg_goal_info, hlds_goal, hlds_goal,
		edcg_info, edcg_info, edcg_forms).
:- mode insert_edcg_unifs(in, in, out, in, in, out) is det.

insert_edcg_unifs(EdcgGoalInfo, Goal0, Goal, EDCGInfo0, EDCGInfo,
		EDCGForms) :-
	Goal0 = _GoalExpr - GoalInfo,
	goal_info_get_context(GoalInfo, Context),
	insert_edcg_unifs_2(EdcgGoalInfo, Context, EDCGInfo0, EDCGInfo,
		ConjList0, EDCGForms),
	goal_to_conj_list(Goal0, ConjList1),
	list__append(ConjList0, ConjList1, ConjList),
	conj_list_to_goal(ConjList, GoalInfo, Goal).

:- pred insert_edcg_unifs_2(edcg_goal_info, context, edcg_info, edcg_info,
		list(hlds_goal), edcg_forms).
:- mode insert_edcg_unifs_2(in, in, in, in, out, out) is det.

insert_edcg_unifs_2([], _, _, _, [], []).
insert_edcg_unifs_2([EDCGArg - changed(Var1, Var2) | Rest],
		Context, EDCGInfo0, EDCGInfo1, Goals, EDCGForms) :-
	edcg_info_get_state_info(EDCGArg, EDCGInfo0,
		StateInfo1),
	state_info_get_var(StateInfo1, EDCGVar1),
	edcg_info_get_state_info(EDCGArg, EDCGInfo1,
		StateInfo2),
	state_info_get_var(StateInfo2, EDCGVar2),
	create_atomic_unification(Var1, var(EDCGVar1), Context,
		explicit, [], Goal1),
	create_atomic_unification(Var2, var(EDCGVar2), Context,
		explicit, [], Goal2),
	Goals = [Goal1, Goal2 | Goals0],
	EDCGForms = [EDCGArg - changed | EDCGForms0],
	insert_edcg_unifs_2(Rest, Context, EDCGInfo0, EDCGInfo1, Goals0,
		EDCGForms0).
insert_edcg_unifs_2([EDCGArg - passed(Var) | Rest], Context,
		EDCGInfo0, EDCGInfo1, Goals, EDCGForms) :-
	edcg_info_get_state_info(EDCGArg, EDCGInfo0,
		StateInfo),
	state_info_get_var(StateInfo, EDCGVar),
	create_atomic_unification(Var, var(EDCGVar), Context,
		explicit, [], Goal),
	Goals = [Goal | Goals0],
	EDCGForms = [EDCGArg - passed | EDCGForms0],
	insert_edcg_unifs_2(Rest, Context, EDCGInfo0, EDCGInfo1, Goals0,
		EDCGForms0).
insert_edcg_unifs_2([EDCGArg - produced(Var) | Rest], Context,
		EDCGInfo0, EDCGInfo1, Goals, EDCGForms) :-
	edcg_info_get_state_info(EDCGArg, EDCGInfo1,
		StateInfo),
	state_info_get_var(StateInfo, EDCGVar),
	create_atomic_unification(Var, var(EDCGVar), Context,
		explicit, [], Goal),
	Goals = [Goal | Goals0],
	EDCGForms = [EDCGArg - produced | EDCGForms0],
	insert_edcg_unifs_2(Rest, Context, EDCGInfo0, EDCGInfo1, Goals0,
		EDCGForms0).

	% This predicate is used to set the initial edcg_info for an EDCG
	% goal. EDCGArgs is a list of edcg variables with a scope local to
	% the EDCG goal. Every edcg variable in EDCGArgs has its state
	% changed within EDCGInfoIn. The new state is `alive' with a fresh
	% variable as listed in Vars. The form of the new state is the
	% corresponding form from Forms. EDCGInfoOut is EDCGInfoIn with the
	% new states of the EDCGArgs.
:- pred edcg_info_convert(list(edcg_arg), edcg_info, edcg_info).
:- mode edcg_info_convert(in, in, out) is det. 

edcg_info_convert([], EDCGInfo, EDCGInfo).
edcg_info_convert([EDCGArg | EDCGArgs], EDCGInfo0, EDCGInfo) :-
	edcg_info_get_state_info(EDCGArg, EDCGInfo0,
		StateInfo0),
	EDCGInfo0^varsettypes = VarSetTypes0,
	state_info_new(VarSetTypes0, VarSetTypes, StateInfo0, StateInfo),
	EDCGInfo1 = EDCGInfo0^varsettypes := VarSetTypes,
	edcg_info_set_state_info(EDCGArg, EDCGInfo1,
		StateInfo, EDCGInfo2),
	edcg_info_convert(EDCGArgs, EDCGInfo2, EDCGInfo).

	% edcg_info_revert(EDCGArgs, EDCGInfoInitial, EDCGInfoFinal,
	%	       EDCGInfoOut)
	%
	% EDCGInfoOut is EDCGInfoFinal except that the edcg variables in
	% EDCGArgs have their state set to what it is in EDCGInfoInitial.
	% This predicate is used after processing EDCG goals. EDCGInfoIntial
	% is the edcg_info before the goal and EDCGInfoFinal is the state at
	% the end of the goal. EDCGArgs is a list of edcg variables with a
	% scope local to the EDCG goal. This predicate undoes
	% edcg_info_convert/6.
:- pred edcg_info_revert(list(edcg_arg), edcg_info, 
	edcg_info, edcg_info).
:- mode edcg_info_revert(in, in, in, out) is det.

edcg_info_revert([], _, EDCGInfo, EDCGInfo).
edcg_info_revert([EDCGArg | EDCGArgs], EDCGInfo0, EDCGInfo1,
		EDCGInfo) :-
	edcg_info_get_state_info(EDCGArg, EDCGInfo0, StateInfo),
	edcg_info_set_state_info(EDCGArg, EDCGInfo1, StateInfo,
		EDCGInfo2),
	edcg_info_revert(EDCGArgs, EDCGInfo0, EDCGInfo2,
		EDCGInfo).

:- pred insert_unifications(list(prog_var), list(prog_var), prog_context,
		arity, hlds_goal, hlds_goal).
:- mode insert_unifications(in, in, in, in, in, out) is det.

insert_unifications(HeadVars, Vars, Context, VisualArity, Goal0, Goal) :-
	Goal0 = _ - GoalInfo,
	goal_to_conj_list(Goal0, Goals0),
	insert_unifications_2(HeadVars, Vars, Context, VisualArity, 
		Goals0, Goals),
	conj_list_to_goal(Goals, GoalInfo, Goal).

:- pred insert_unifications_2(list(prog_var), list(prog_var), prog_context,
		arity, list(hlds_goal), list(hlds_goal)).
:- mode insert_unifications_2(in, in, in, in, in, out) is det.

insert_unifications_2([], [], _, _, Goals, Goals).
insert_unifications_2([_|_], [], _, _, _, _) :-
	error("insert_unifications_2: list length mistmatch.").
insert_unifications_2([], [_|_], _, _, _, _) :-
	error("insert_unifications_2: list length mistmatch.").
insert_unifications_2([Var1 | Vars1], [Var2 | Vars2], Context, ArgNum0,
		Goals0, [Goal | Goals]) :-
	ArgNum is ArgNum0 + 1,
	UnifyMainContext = head(ArgNum),
	UnifySubContexts = [],
	create_atomic_unification(Var1, var(Var2), Context, UnifyMainContext,
		UnifySubContexts, Goal),
	insert_unifications_2(Vars1, Vars2, Context, ArgNum, Goals0, Goals).

:- pred write_inference_message(pred_id, pred_info, module_info, bool,
		io__state, io__state).
:- mode write_inference_message(in, in, in, in, di, uo) is det.

write_inference_message(_, _, _, no) --> [].
write_inference_message(PredId, PredInfo, ModuleInfo, yes) -->
	{ pred_info_context(PredInfo, Context) },
	{ pred_info_edcg_args(PredInfo,
		pred_edcg_info(_Declared, Inferred, _)) },
	prog_out__write_context(Context),
	io__write_string("EDCG Inferred "),
	mercury_output_pred_type(ModuleInfo, PredId, Inferred, Context).

%---------------------------------------------------------------------------%
	% EDCGInfo Utility predicates.

	% edcg_info is a data structure containing all the states of the
	% edcg arguments defined in the module. 
:- type edcg_info
	--->    edcg_info(
				state_info_table 	:: state_info_table, 
				errors 			:: list(edcg_error),
				varsettypes 		:: var_set_types,
				inferring 		:: bool
							).  

:- type state_info_table     ==    map(edcg_arg, state_info).

	% name_table is a map from all unqualified edcg argument names
	% to qualified names.
:- type name_table     ==    map(string, list(edcg_arg)). 

:- pred edcg_info_init(edcg_table, bool, edcg_info).
:- mode edcg_info_init(in, in, out) is det.

edcg_info_init(EdcgTable, Inferring, EDCGInfo) :-
	map__init(StateTable),
	varset__init(VarSet),
	map__init(VarTypes),
	EDCGInfo0 = edcg_info(StateTable, [], VarSet-VarTypes, Inferring),
	edcg_table_get_etype_table(EdcgTable, Htypes),
	map__to_assoc_list(Htypes, EDCGArgAndTypeInfo),
	list__foldl(edcg_info_add_edcg_arg, EDCGArgAndTypeInfo, 
		EDCGInfo0, EDCGInfo).

	% Adds a edcg argument to the current edcg information.
:- pred edcg_info_add_edcg_arg(pair(edcg_arg, etype_info),
		edcg_info, edcg_info).
:- mode edcg_info_add_edcg_arg(in, in, out) is det.

edcg_info_add_edcg_arg(EDCGArg - (etype_defn(Type) - _Context), 
		EDCGInfo0, EDCGInfo) :-
	state_info_init(EDCGArg, Type, StateInfo),
	EDCGInfo0^state_info_table = StateTable0,
	map__det_insert(StateTable0, EDCGArg, StateInfo, StateTable),
	EDCGInfo = EDCGInfo0^state_info_table := StateTable.

	% edcg_info_initial_state(FormsAndNames, EDCGInfoIn, EDCGInfoOut):
	%
	% EDCGInfoOut is EDCGInfoIn with all the edcg arguments
	% in FormsAndNames having appropriate information added, such 
	% that EDCGInfoOut is the pre state of a predicate with 
	% the FormsAndNames declaration. 
	% IsFact is yes if the Clause with FormsAndNames is a fact.
	% It is needed to flag whether an underscore needs to be
	% prefixed to the NameBase, to prevent unused variable warnings.
:- pred edcg_info_initial_state(edcg_forms, edcg_info, edcg_info).
:- mode edcg_info_initial_state(in, in, out) is det.

edcg_info_initial_state([]) --> [].
edcg_info_initial_state([EDCGArg - _|Rest]) -->
	edcg_info_birth(EDCGArg),
	edcg_info_initial_state(Rest).

	% edcg_info_combine(EDCGInfoIn, EDCGInfoOld, EDCGInfoOut):
	% 
	% This predicate is called inbetween processing disjuncts or THEN and
	% ELSE parts. EDCGInfoOld is the edcg pre state of all the
	% disjuncts or THEN and ELSE parts to be processed. EDCGInfoIn is
	% the post state of the last processed disjuct or THEN part.
	% EDCGInfoOut is EDCGInfoOld with the variable suffixes updated
	% to correspond to the ones in EDCGInfoIn.
	% the unique naming of all edcg variables created.
:- pred edcg_info_combine(edcg_info, edcg_info, edcg_info).
:- mode edcg_info_combine(in, in, out) is det.

edcg_info_combine(EDCGInfo1, EDCGInfo2, EDCGInfo) :-
	EDCGInfo2^state_info_table = StateTable,
	map__keys(StateTable, EDCGArgs),
	edcg_info_combine_2(EDCGArgs, EDCGInfo1, EDCGInfo2, EDCGInfo).

:- pred edcg_info_combine_2(list(edcg_arg), edcg_info, edcg_info,
	edcg_info).
:- mode edcg_info_combine_2(in, in, in, out) is det.

edcg_info_combine_2([], _, EDCGInfo, EDCGInfo).
edcg_info_combine_2(EDCGArg.EDCGArgs, EDCGInfo0, EDCGInfo1,
		EDCGInfo) :-
	edcg_info_get_state_info(EDCGArg, EDCGInfo0, StateInfo0),
	edcg_info_get_state_info(EDCGArg, EDCGInfo1, StateInfo1),
	Suffix0 = StateInfo0 ^ var_suffix,
	Suffix1 = StateInfo1 ^ var_suffix,
	(
		Suffix0 > Suffix1
	->
		StateInfo = StateInfo0
	;
		StateInfo = StateInfo1
	),
	edcg_info_set_state_info(EDCGArg, EDCGInfo1, StateInfo,
		EDCGInfo2),
	edcg_info_combine_2(EDCGArgs, EDCGInfo0, EDCGInfo2, EDCGInfo).

	% edcg_info_new(EDCGInfoIn, EDCGInfoOut).
	% 
	% This predicate is called after edcg_info_combine/3, and before
	% update_goal/4 for if-then-elses.  EDCGInfoOut is EDCGInfoIn with
	% all alive edcg arguments assigned to new variables. EDCGInfoOut
	% becomes the final edcg_info for the then and else parts of the goal.
	% This ensures that edcg argument's scopes are never restricted to the
	% `if' part of an if-then-else.
:- pred edcg_info_new(edcg_info, edcg_info).
:- mode edcg_info_new(in, out) is det.

edcg_info_new(EDCGInfo0, EDCGInfo) :-
	EDCGInfo0^state_info_table = StateTable0,
	map__keys(StateTable0, EDCGArgs),
	edcg_info_new_2(EDCGArgs, EDCGInfo0, EDCGInfo).

:- pred edcg_info_new_2(list(edcg_arg), edcg_info, edcg_info).
:- mode edcg_info_new_2(in, in, out) is det.

edcg_info_new_2([], EDCGInfo, EDCGInfo).
edcg_info_new_2([EDCGArg | Rest], EDCGInfo0, EDCGInfo) :-
	edcg_info_get_state_info(EDCGArg, EDCGInfo0, StateInfo0),
	EDCGInfo0^varsettypes = VarSetTypes0,
	state_info_new_var(VarSetTypes0, VarSetTypes, StateInfo0, StateInfo),
	EDCGInfo1 = EDCGInfo0^varsettypes := VarSetTypes,
	edcg_info_set_state_info(EDCGArg, EDCGInfo1, StateInfo, 
		EDCGInfo2),
	edcg_info_new_2(Rest, EDCGInfo2, EDCGInfo).

	% Start the scope of a edcg variable.
	% Will produce a software error if the edcg variable is already in
	% scope.
:- pred edcg_info_birth(edcg_arg, edcg_info, edcg_info).
:- mode edcg_info_birth(in, in, out) is det.

edcg_info_birth(EDCGArg, EDCGInfo0, EDCGInfo) :-
	edcg_info_get_state_info(EDCGArg, EDCGInfo0, StateInfo0),
	EDCGInfo0^varsettypes = VarSetTypes0,
	state_info_birth(VarSetTypes0, VarSetTypes,
		StateInfo0, StateInfo),
	EDCGInfo1 = EDCGInfo0^varsettypes := VarSetTypes,
	edcg_info_set_state_info(EDCGArg, EDCGInfo1, 
		StateInfo, EDCGInfo).

	% Returns the variable that the edcg variable currently represents.
	% Used for predicate calls where the form of the edcg variable for the
	% predicate is passed.
	% Fails if the edcg argument is not in scope.
:- pred edcg_info_pass(atomic_goal_id, edcg_arg, prog_var,
		edcg_info, edcg_info).
:- mode edcg_info_pass(in, in, out, in, out) is semidet.

edcg_info_pass(Id, EDCGArg, Var, EDCGInfo0, EDCGInfo) :-
	edcg_info_get_state_info(EDCGArg, EDCGInfo0, StateInfo0),
	state_info_pass(Id, StateInfo0, StateInfo, Var),
	edcg_info_set_state_info(EDCGArg, EDCGInfo0, StateInfo,
		EDCGInfo).

	% Returns the variable that the edcg variable currently represents.
	% Used for predicate calls where the form of the edcg variable for the
	% predicate is produced.
	% Fails if the edcg argument is not in scope.
:- pred edcg_info_produce(atomic_goal_id, edcg_arg, prog_var, 				edcg_info, edcg_info).
:- mode edcg_info_produce(in, in, out, in, out) is semidet.

edcg_info_produce(Id, EDCGArg, Var, EDCGInfo0, EDCGInfo) :-
	edcg_info_get_state_info(EDCGArg, EDCGInfo0, StateInfo0),
	EDCGInfo0^varsettypes = VarSetTypes0,
	state_info_produce(Id, VarSetTypes0, VarSetTypes, 
		StateInfo0, StateInfo, Var),
	EDCGInfo1 = EDCGInfo0^varsettypes := VarSetTypes,
	edcg_info_set_state_info(EDCGArg, EDCGInfo1, 
		StateInfo, EDCGInfo).

	% Returns the variable that the edcg variable currently represents.
	% Used for predicate calls where the form of the edcg variable for the
	% predicate is changed.
	% Fails if the edcg argument is dead.
:- pred edcg_info_change(atomic_goal_id, edcg_arg, prog_var, prog_var, 
		edcg_info, edcg_info).
:- mode edcg_info_change(in, in, out, out, in, out) is semidet.

edcg_info_change(Id, EDCGArg, Var1, Var2, EDCGInfo0, EDCGInfo) :-
	edcg_info_get_state_info(EDCGArg, EDCGInfo0, StateInfo0),
	EDCGInfo0^varsettypes = VarSetTypes0,
	state_info_change(Id, VarSetTypes0, VarSetTypes, StateInfo0, StateInfo, 
		Var1, Var2),
	EDCGInfo1 = EDCGInfo0^varsettypes := VarSetTypes,
	edcg_info_set_state_info(EDCGArg, EDCGInfo1, 
		StateInfo, EDCGInfo).

	% edcg_info_merge(EDCGInfo0, EDCGInfo1, EDCGInfo)
	%	EDCGInfo is EDCGInfo0 with the errors and the varsettypes
	%	of EDCGInfo1.
:- pred edcg_info_merge(edcg_info, edcg_info, edcg_info).
:- mode edcg_info_merge(in, in, out) is det.

edcg_info_merge(EDCGInfo0, EDCGInfoOut, EDCGInfo) :-
	EDCGInfo1 = EDCGInfo0^errors := EDCGInfoOut^errors,
	EDCGInfo = EDCGInfo1^varsettypes := EDCGInfoOut^varsettypes.

:- pred edcg_info_check_forms(edcg_forms, edcg_forms, term__context,
		edcg_info, edcg_info).
:- mode edcg_info_check_forms(in, in, in, in, out) is det.

edcg_info_check_forms(Declared0, Inferred, Context, 
		EDCGInfo0, EDCGInfo) :-
	list__sort(Declared0, Declared),
	% Inferred should already be sorted.
	edcg_info_check_forms_2(Declared, Inferred, Strict, Loose),
	( not(Strict = []) ->
		edcg_info_error(infer_error(Strict, Context), EDCGInfo0,
			EDCGInfo1)
	;
		EDCGInfo1 = EDCGInfo0
	),
	( not(Loose = []) ->
		edcg_info_error(infer_warning(Loose, Context), EDCGInfo1,
			EDCGInfo)
	;
		EDCGInfo = EDCGInfo1
	).

:- pred edcg_info_check_forms_2(edcg_forms, edcg_forms,	
		assoc_list(edcg_arg, pair(form)), 
		assoc_list(edcg_arg, pair(form))).
:- mode edcg_info_check_forms_2(in, in, out, out) is det.

edcg_info_check_forms_2(Declared, Inferred, Strict, Loose) :-
	(
		Declared = [],
		Inferred = [],
		Strict = [],
		Loose = []
	;
		Declared = [EDCGArg - Form| Rest],
		Inferred = [],
		Loose = [EDCGArg - (Form - nothing) | Loose0],
		edcg_info_check_forms_2(Rest, Inferred, Strict, Loose0)
	;
		Declared = [],
		Inferred = [EDCGArg - Form| Rest],
		Strict = [EDCGArg - (nothing - Form) | Strict0],
		edcg_info_check_forms_2(Declared, Rest, Strict0, Loose)
	;
		Declared = [EDCGArg1 - Form1 | DeclaredTail],
		Inferred = [EDCGArg2 - Form2 | InferredTail],
		compare(R, EDCGArg1, EDCGArg2),
		(
			R = (=),
			edcg_form_compare(FormR, Form1, Form2),
			(
				FormR = equal,
				Strict = Strict0,
				Loose = Loose0
			;
				FormR = strict,
				Strict = [EDCGArg1 - (Form1 - Form2)|Strict0],
				Loose = Loose0
			;
				FormR = loose,
				Loose = [EDCGArg1 - (Form1 - Form2)|Loose0],
				Strict = Strict0
			),
			edcg_info_check_forms_2(DeclaredTail, InferredTail,
				Strict0, Loose0)
		;
			R = (<),
			Loose = [EDCGArg1 - (Form1 - nothing) | Loose0],
			edcg_info_check_forms_2(DeclaredTail, Inferred,
				Strict, Loose0)
		;
			R = (>),
			Strict = [EDCGArg2 - (nothing - Form2) | Strict0],
			edcg_info_check_forms_2(Declared, InferredTail,
				Strict0, Loose)
		)
	).

	% form_compare: 
	%	strict means that the forms are incompatible.
	%	loose means that the forms are compatible but not equal.
:- type form_compare
	--->	equal
	;	strict
	;	loose.

:- pred edcg_form_compare(form_compare, form, form).
:- mode edcg_form_compare(out, in, in) is det.

edcg_form_compare(equal, nothing, nothing).
edcg_form_compare(strict, nothing, passed).
edcg_form_compare(strict, nothing, produced).
edcg_form_compare(strict, nothing, changed).
edcg_form_compare(equal, passed, passed).
edcg_form_compare(loose, passed, nothing).
edcg_form_compare(strict, passed, produced).
edcg_form_compare(strict, passed, changed).
edcg_form_compare(equal, produced, produced).
edcg_form_compare(loose, produced, nothing).
edcg_form_compare(strict, produced, passed).
edcg_form_compare(strict, produced, changed).
edcg_form_compare(equal, changed, changed).
edcg_form_compare(loose, changed, nothing).
edcg_form_compare(loose, changed, passed).
edcg_form_compare(loose, changed, produced).

%---------------------------------------------------------------------------%
	% EDCGInfo Error predicates.

	% The edcg_error structure stores all information relating to any
	% errors caused by edcg arguments.
:- type edcg_error 
	--->	pred_call_error(pred_id, term__context, edcg_arg)
		% edcg argument for predicate call out of scope 
	;	pred_call_explicit_error(pred_id, term__context, edcg_arg)
		% explicit edcg argument not used in called predicate 
	;	pred_call_expanded_error(pred_id, term__context)
		% predicate called in expanded form also has explicit arguments
	;	pred_call_form_error(term__context, edcg_arg, form, pred_id) 
		% an explicit edcg argument has the wrong form for the declared
		% predicate
	;	pred_call_warning(pred_id, term__context)
		% warning that a predicate was called in explicit form
	;	scope_error(edcg_operator, term__context, edcg_arg)
		% an edcg argument was referenced out of scope
	;	infer_error(assoc_list(edcg_arg, pair(form)), term__context)
		% mismatched declared and inferred edcg arguments
	;	infer_warning(assoc_list(edcg_arg, pair(form)), term__context)
		% mismatched declared and inferred edcg arguments
	;	undef_mode_error(edcg_arg, form, term__context)
		% no mode defined for a form
	;	functor_error(term__context)
		% `-->>' not used when it should be
	;	functor_warning(term__context).
		% `-->>' used when is does not have to be

	% edcg_info_error(EDCGError, EDCGInfoIn, EDCGInfoOut)
	%
	% EDCGInfoOut is EDCGInfoIn with EDCGError appended onto
	% its list of errors.
:- pred edcg_info_error(edcg_error, edcg_info, edcg_info).
:- mode edcg_info_error(in, in, out) is det.

edcg_info_error(Error, EDCGInfo0, EDCGInfo) :-
	EDCGInfo0^errors = Errors,
	EDCGInfo = EDCGInfo0^errors := Error.Errors.

	% maybe_edcg_error/8 :
	% prints out error messages corresponding to error states returned
	% from processing the Body of a clause.
:- pred maybe_edcg_error(list(edcg_error), pred_id,
		module_info, module_info, io__state, io__state).
:- mode maybe_edcg_error(in, in, in, out, di, uo) is det.

maybe_edcg_error(Errors0, PredId, ModuleInfo0, ModuleInfo) -->
	{ list__reverse(Errors0, Errors) },
	{ error_util__describe_one_pred_name(ModuleInfo0, PredId,
		PredString) },
	{ PredFormat = [words("In clause for"), fixed(PredString), nl] },
	maybe_edcg_error_2(Errors, PredFormat, ModuleInfo0, ModuleInfo).

:- pred maybe_edcg_error_2(list(edcg_error), list(format_component),
		module_info, module_info, io__state, io__state).
:- mode maybe_edcg_error_2(in, in, in, out, di, uo) is det.

maybe_edcg_error_2([], _, ModuleInfo, ModuleInfo) --> [].
maybe_edcg_error_2([Error|Rest], PredFormat, ModuleInfo0, ModuleInfo) -->
	maybe_edcg_error_3(Error, PredFormat, ModuleInfo0, ModuleInfo1),
	maybe_edcg_error_2(Rest, PredFormat, ModuleInfo1, ModuleInfo).

:- pred maybe_edcg_error_3(edcg_error, list(format_component), 
		module_info, module_info,
		io__state, io__state).
:- mode maybe_edcg_error_3(in, in, in, out, di, uo) is det.

maybe_edcg_error_3(pred_call_form_error(Context, EDCGArg, Form, PredId),
		PredFormat, ModuleInfo0, ModuleInfo) -->
	{ form_to_string(Form, FormString) },
	{ sym_name_to_string(EDCGArg, EDCGArgString) },
	{ error_util__describe_one_pred_name(ModuleInfo0, PredId,
		PredString) },
	{ Message = [words("EDCG variable"), words(EDCGArgString), 
		words("has the wrong arity for the declared form:"), 
		words(FormString), words(", for the call to"), 
		fixed(PredString)] },
	{ list__append(PredFormat, Message, FormatComps) },
	error_util__write_error_pieces(Context, 0, FormatComps),
	{ module_info_incr_errors(ModuleInfo0, ModuleInfo) }.

maybe_edcg_error_3(infer_error(Strict, Context),
		PredFormat, ModuleInfo0, ModuleInfo) -->
	{ list__append(PredFormat,
		[words("EDCG edcg form declaration error:")],
		FormComps0) },
	{ form_error(Strict, FormComps1) },
	{ list__append(FormComps0, FormComps1, FormComps) },
	error_util__write_error_pieces(Context, 0, FormComps),
	{ module_info_incr_errors(ModuleInfo0, ModuleInfo) }.

maybe_edcg_error_3(infer_warning(Loose, Context),
		PredFormat, ModuleInfo, ModuleInfo) -->
	{ list__append(PredFormat,
		[words("EDCG edcg form declaration warning:")],
		FormComps0) },
	{ form_error(Loose, FormComps1) },
	{ list__append(FormComps0, FormComps1, FormComps) },
	error_util__report_warning(Context, 0, FormComps).

maybe_edcg_error_3(pred_call_error(PredId, Context, 
	    EDCGArg), PredFormat, ModuleInfo0, ModuleInfo) -->
	{ error_util__describe_one_pred_name(ModuleInfo0, PredId, PredString) },
	{ list__append(PredFormat, 
		[words("EDCG scope error: At call to"), 
		words(PredString), nl], FormatComps0) },
	{ scope_error(EDCGArg, MessageString) },
	{ list__append(FormatComps0, [words(MessageString)], FormatComps) },
	error_util__write_error_pieces(Context, 0, FormatComps),
	{ module_info_incr_errors(ModuleInfo0, ModuleInfo) }.

maybe_edcg_error_3(pred_call_explicit_error(PredId, Context, 
	    EDCGArg), PredFormat, ModuleInfo0, ModuleInfo) -->
	{ error_util__describe_one_pred_name(ModuleInfo0, PredId, PredString) },
	{ list__append(PredFormat, 
		[words("EDCG pred call error: At call to"), 
		words(PredString), nl], FormatComps0) },
	{ sym_name_to_string(EDCGArg, EDCGArgString) },
	{ string__append(EDCGArgString, 
		" is not used in the called predicate.", MessageString) },
	{ list__append(FormatComps0, [words(MessageString)], FormatComps) },
	error_util__write_error_pieces(Context, 0, FormatComps),
	{ module_info_incr_errors(ModuleInfo0, ModuleInfo) }.

maybe_edcg_error_3(pred_call_expanded_error(PredId, Context),
		PredFormat, ModuleInfo0, ModuleInfo) -->
	{ error_util__describe_one_pred_name(ModuleInfo0, PredId, PredString) },
	{ list__append(PredFormat, 
		[words("EDCG pred call error: At call to"), 
		words(PredString), nl], FormatComps0) },
	{ string__append(" predicate is called in expanded form ",
		"and has explicit edcg arguments.", MessageString) },
	{ list__append(FormatComps0, [words(MessageString)], FormatComps) },
	error_util__write_error_pieces(Context, 0, FormatComps),
	{ module_info_incr_errors(ModuleInfo0, ModuleInfo) }.

maybe_edcg_error_3(pred_call_warning(PredId, Context),
		PredFormat, ModuleInfo, ModuleInfo) -->
	{ error_util__describe_one_pred_name(ModuleInfo, PredId,
		PredString) },
	{ list__append(PredFormat,
		[words("Call to predicate"), fixed(PredString),
		words("with edcg arguments in expanded form.")], FormComps) },
	error_util__report_warning(Context, 0, FormComps).

maybe_edcg_error_3(scope_error(Op, Context, EDCGArg),
		PredFormat, ModuleInfo0, ModuleInfo) -->
	{ edcg_operator_to_string(Op, OpString) },
	{ list__append(PredFormat, [words("EDCG scope error: At operator"), 
		words(OpString), nl], FormatComps0) },
	{ scope_error(EDCGArg, MessageString) },
	{ list__append(FormatComps0, [words(MessageString)], FormatComps) },
	error_util__write_error_pieces(Context, 0, FormatComps),
	{ module_info_incr_errors(ModuleInfo0, ModuleInfo) }.

maybe_edcg_error_3(undef_mode_error(EDCGArg, Form, Context),
		PredFormat, ModuleInfo0, ModuleInfo) -->
	{ sym_name_to_string(EDCGArg, EDCGString) },
	{ form_to_string(Form, FormString) },
	{ list__append(PredFormat,
		[words("No mode(s) declared for edcg argument"),
		words(EDCGString), words("for the form"), words(FormString),
		nl], FormatComps) },
	error_util__write_error_pieces(Context, 0, FormatComps),
	{ module_info_incr_errors(ModuleInfo0, ModuleInfo) }.

maybe_edcg_error_3(functor_error(Context),
		PredFormat, ModuleInfo0, ModuleInfo) -->
	{ list__append(PredFormat,
		[words("Clause uses edcg variables and hence"),
		words("should have functor `-->>'.")],
		FormatComps) },
	error_util__write_error_pieces(Context, 0, FormatComps),
	{ module_info_incr_errors(ModuleInfo0, ModuleInfo) }.

maybe_edcg_error_3(functor_warning(Context),
		PredFormat, ModuleInfo, ModuleInfo) -->
	{ list__append(PredFormat,
		[words("Clause does not use edcg variables and hence"),
		words("does not need functor `-->>'.")],
		FormatComps) },
	error_util__report_warning(Context, 0, FormatComps).

:- pred scope_error(edcg_arg, string).
:- mode scope_error(in, out) is det.

scope_error(EDCGArg, String) :-
	sym_name_to_string(EDCGArg, EDCGArgString),
	string__append(EDCGArgString, " is not in scope", String).

:- pred form_error(assoc_list(edcg_arg, pair(form)), list(format_component)).
:- mode form_error(in, out) is det.

form_error(EDCGForms, FormComp) :-
	list__map(form_error_2, EDCGForms, Strings),
	error_util__list_to_pieces(Strings, FormComp).

:- pred form_error_2(pair(edcg_arg, pair(form)), string).
:- mode form_error_2(in, out) is det.

form_error_2(EDCGArg - (Form1 - Form2), String) :-
	sym_name_to_string(EDCGArg, EDCGString),
	form_to_string(Form1, Form1String),
	form_to_string(Form2, Form2String),
	string__append_list([EDCGString, " declared ", Form1String,
		" but inferred ", Form2String], String).

%---------------------------------------------------------------------------%
	% EDCGInfo Get and Set predicates

:- implementation.

:- pred edcg_info_get_state_info(edcg_arg, edcg_info, state_info).
:- mode edcg_info_get_state_info(in, in, out) is det.

edcg_info_get_state_info(EDCGArg, EDCGInfo, StateInfo) :-
	EDCGInfo^state_info_table = StateTable,
	map__lookup(StateTable, EDCGArg, StateInfo).

	% Overwrite any existing state information for the edcg id.
:- pred edcg_info_set_state_info(edcg_arg, edcg_info, state_info,
	edcg_info).
:- mode edcg_info_set_state_info(in, in, in, out) is det.

edcg_info_set_state_info(EDCGArg, EDCGInfo0, StateInfo, EDCGInfo) :-
	EDCGInfo0^state_info_table = StateTable0,
	map__set(StateTable0, EDCGArg, StateInfo, StateTable),
	EDCGInfo = EDCGInfo0^state_info_table := StateTable.

	% edcg_info_get_vars(EDCGArg, EDCGInfo, Var)
	%
	% Var is the variable that EDCGArg currently represents in EDCGInfo.
:- pred edcg_info_get_var(edcg_arg, edcg_info, prog_var).
:- mode edcg_info_get_var(in, in, out) is det.

edcg_info_get_var(EDCGArg, EDCGInfo, Var) :-
	edcg_info_get_state_info(EDCGArg, EDCGInfo, StateInfo),
	state_info_get_var(StateInfo, Var).

%---------------------------------------------------------------------------%

	% StateInfo Utility predicates.

:- implementation.

	% Note that var_suffix is the next available suffix and not the
	% actual suffix of var.
:- type state_info
	--->	state_info(
			name_base :: name_base,
			var_suffix :: var_suffix,
			(type) :: (type),
			state_sub_info :: state_sub_info).

:- type state_sub_info
	--->	curr(prog_var)
		% The current variable the edcg reference refers to.
	;	curr_next(atomic_goal_id, prog_var, prog_var)
		% The current and next variables the edcg reference refers to.
	;	dead.
		% The edcg reference is not in scope.

:- type name_base    ==    string.
:- type var_suffix    ==    int.
	

:- pred state_info_init(edcg_arg, type, state_info).
:- mode state_info_init(in, in, out) is det.

state_info_init(EDCGArg, Type, StateInfo) :-
	create_var_name(EDCGArg, NameBase),
	init_var_suffix(Suffix),
	StateInfo = state_info(NameBase, Suffix, Type, dead).

	% change the state_info from dead to current.
:- pred state_info_birth(var_set_types, var_set_types,
	state_info, state_info).
:- mode state_info_birth(in, out, in, out) is det.

state_info_birth(VarSetTypes0, VarSetTypes, 
		state_info(NameBase, Suffix0, Type, dead), 
		state_info(NameBase, Suffix, Type, curr(Var))) :-
	new_var(VarSetTypes0, NameBase, Suffix0, Type, VarSetTypes, 
		Suffix, Var).
state_info_birth(_,_,state_info(_,_,_,curr(_)), _) :-
	error("EDCG argument assumed dead when alive.\n").
state_info_birth(_,_,state_info(_,_,_,curr_next(_,_,_)), _) :-
	error("EDCG argument assumed dead when alive.\n").

	% Used for edcg goals,
	% Creates a new variable and sets the state to current 
:- pred state_info_new(var_set_types, var_set_types, 
		state_info, state_info).
:- mode state_info_new(in, out, in, out) is det.

state_info_new(VarSetTypes0, VarSetTypes, StateInfo0, StateInfo) :-
	StateInfo0 = state_info(NameBase, Suffix0, Type, _),
	new_var(VarSetTypes0, NameBase, Suffix0, Type, VarSetTypes, 
		Suffix, Var),
	StateInfo = state_info(NameBase, Suffix, Type, curr(Var)).

:- pred state_info_new_var(var_set_types, var_set_types, 
		state_info, state_info).
:- mode state_info_new_var(in, out, in, out) is det.

state_info_new_var(VarSetTypes0, VarSetTypes, 
		state_info(NameBase, Suffix0, Type, StateSubInfo0),
		state_info(NameBase, Suffix, Type, StateSubInfo)) :-
	(
		StateSubInfo0 = dead
	->
		StateSubInfo = dead,
		Suffix = Suffix0,
		VarSetTypes = VarSetTypes0
	;
		StateSubInfo = curr(Var),
		new_var(VarSetTypes0, NameBase, Suffix0, Type, VarSetTypes, 
			Suffix, Var)
	).

	% Used with the operator '$=', and for a predicate call with a form of
	% produced for the edcg variable.
:- pred state_info_produce(atomic_goal_id, var_set_types, var_set_types, 
		state_info, state_info, prog_var).
:- mode state_info_produce(in, in, out, in, out, out) is semidet.

state_info_produce(Id, VarSetTypes0, VarSetTypes, StateInfo0, StateInfo, Var) :-
	state_info_change(Id, VarSetTypes0, VarSetTypes, StateInfo0, StateInfo,
		_, Var).

	% Used for a predicate call with a form of changed for the edcg
	% variable.
:- pred state_info_change(atomic_goal_id, var_set_types, var_set_types, 
		state_info, state_info, prog_var, prog_var).
:- mode state_info_change(in, in, out, in, out, out, out) is semidet.

state_info_change(IdNew, VarSetTypes0, VarSetTypes, 
		state_info(NameBase,Suffix0,Type,StateSubInfo),
		state_info(NameBase,Suffix,Type,curr_next(IdNew,Var1,Var2)), 
		Var1, Var2) :-
	(
		StateSubInfo = curr(Var1),
		new_var(VarSetTypes0, NameBase, Suffix0, Type, VarSetTypes, 
			Suffix, Var2)
	;
		StateSubInfo = curr_next(IdOld,VarOld1,VarOld2),
		(
			IdNew = IdOld
		->
			Var1 = VarOld1,
			Var2 = VarOld2,
			VarSetTypes = VarSetTypes0,
			Suffix = Suffix0
		;
			new_var(VarSetTypes0, NameBase, Suffix0, Type, 
				VarSetTypes, Suffix, Var2),
			Var1 = VarOld2
		)
	).

	% Used with the '$' operator and used for a predicate call with a form
	% of passed for the edcg variable.
:- pred state_info_pass(atomic_goal_id, state_info, state_info, prog_var).
:- mode state_info_pass(in, in, out, out) is semidet.

state_info_pass(IdNew,
		state_info(NameBase,Suffix,Type,StateSubInfo0),
		state_info(NameBase,Suffix,Type,StateSubInfo), Var) :-
	(
		StateSubInfo0 = curr(Var),
		StateSubInfo = StateSubInfo0
	;
		StateSubInfo0 = curr_next(IdOld,Var1,Var2),
		(
			IdNew = IdOld
		->
			StateSubInfo = curr_next(IdNew,Var1,Var2),
			Var = Var1
		;
			StateSubInfo = curr(Var2),
			Var = Var2
		)
	).
	
:- pred state_info_is_alive(state_info::in) is semidet.

state_info_is_alive(state_info(_,_,_,curr(_))).
state_info_is_alive(state_info(_,_,_,curr_next(_,_,_))).

:- pred state_info_get_var(state_info, prog_var).
:- mode state_info_get_var(in, out) is det.

state_info_get_var(state_info(_,_,_,curr(Var)), Var).
state_info_get_var(state_info(_,_,_,curr_next(_,_,Var)), Var).
state_info_get_var(state_info(_,_,_,dead), _Var) :-
	error("EDCG argument incorrectly assumed dead.\n").

	% new_var(VarSetIn, NameBase, SuffixOld, VarSetOut, SuffixNew,
	%	Var): 
	% VarSetOut is VarSetIn with the Var added with NameBase and SuffixOld,
	% SuffixNew is the next available suffix to use.
:- pred new_var(var_set_types, name_base, var_suffix, type, 
		var_set_types, var_suffix, prog_var).
:- mode new_var(in,  in, in, in, out, out, out) is det.

new_var(VarSet0 - VarTypes0, NameBase, Suffix0, Type, VarSet - VarTypes, 
		Suffix, Var) :-
	string__int_to_string(Suffix0, SuffixString),
	string__append(NameBase, SuffixString, VarName),
	varset__new_named_var(VarSet0, VarName, Var, VarSet),
	map__det_insert(VarTypes0, Var, Type, VarTypes),
	next_var_suffix(Suffix0, Suffix).

	% Creates a string to be used as the base for the variable names.
:- pred create_var_name(edcg_arg::in, name_base::out) is det.

create_var_name(EDCGArg, VarName) :-
	unqualify_name(EDCGArg, Name),
	sym_name_get_module_name(EDCGArg, unqualified(""), ModuleName0),
	unqualify_name(ModuleName0, ModuleName),
	string__append_list(["EDCG_", ModuleName, "_", Name], VarName).

:- pred init_var_suffix(var_suffix::out) is det.

init_var_suffix(0).

:- pred next_var_suffix(var_suffix::in, var_suffix::out) is det.

next_var_suffix(Suffix, NewSuffix) :-
	NewSuffix is Suffix + 1.

%---------------------------------------------------------------------------%

--------------------------------------------------------------------------
mercury-reviews mailing list
post:  mercury-reviews at cs.mu.oz.au
administrative address: owner-mercury-reviews at cs.mu.oz.au
unsubscribe: Address: mercury-reviews-request at cs.mu.oz.au Message: unsubscribe
subscribe:   Address: mercury-reviews-request at cs.mu.oz.au Message: subscribe
--------------------------------------------------------------------------



More information about the reviews mailing list