Files
mercury/compiler/prog_io_dcg.m
Simon Taylor 9dd11b2fc6 Smart recompilation. Record version numbers for each item
Estimated hours taken: 400

Smart recompilation. Record version numbers for each item
in interface files. Record which items are used in each compilation.
Only recompile a module if the output file does not exist or
nothing has changed.

There is still some work to do on this:
- it doesn't work with inter-module optimization.
- it doesn't work when the module name doesn't match the file name.
  (this problem will go away when mmake functionality is moved into
  the compiler.

I'll hold off documenting this change in the NEWS file and
on the web page for a month or so, until I've had a bit more
experience using it.

compiler/options.m:
compiler/handle_options.m:
doc/user_guide.texi:
	Add an option `--smart-recompilation', currently off by default.

	Add an internal option `--generate-version-numbers' to control
	whether version numbers are written to the interface files. If
	`--smart-recompilation' is disabled because the module
	is being compiled with `--intermodule-optimization' (e.g. in the
	standard library), we still want to write the version numbers
	to the interface files.

	Add an option `--verbose-recompilation' (default off)
	to write messages describing why recompilation is needed.

	Add an option `--warn-smart-recompilation' (default on)
	to control warnings relating to the smart recompilation
	system. Warn if smart recompilation will not work with
	the output and inter-module optimization options given.

compiler/recompilation.m:
	Type declarations for smart recompilation.
	Predicates to record program items used by compilation.

compiler/recompilation_version.m:
	Compute version numbers for program items in interface files.

compiler/recompilation_usage.m:
	Find all items used by a compilation.

compiler/recompilation_check.m:
	Check whether recompilation is necessary.

compiler/timestamp.m:
	Timestamp ADT for smart recompilation.

compiler/mercury_compile.m:
	Invoke the smart recompilation passes.

compiler/modules.m:
compiler/prog_io.m:
	Return timestamps for modules read.

	When reading a module make sure the current input stream
	is reset to its old value, not stdin.

	Handle version number items in interface files.

compiler/module_qual.m:
compiler/unify_proc.m:
compiler/make_hlds.m:
	Record all items used by local items.

compiler/make_hlds.m:
	Process `:- pragma type_spec' declarations in
	add_item_list_clauses. The qual_info is needed
	when processing `:- pragma type_spec' declarations
	so that any equivalence types used by the declaration
	can be recorded as used by the predicate or function to
	which the `:- pragma type_spec' applies.

compiler/equiv_type.m:
	For each imported item, record which equivalence types
	are used by that item.

compiler/hlds_module.m:
	Add a field to the module_info to store information about
	items used during compilation of a module.

compiler/check_typeclass.m:
	Make sure any items used in clauses for typeclass method
	implementations are recorded in the `.used' file.

compiler/prog_data.m:
compiler/*.m:
	Factor out some duplicated code by combining the
	pred and func, and pred_mode and func_mode items.

	Make it easier to extract the name of a type, inst or mode
	from its declaration.

	Add an item type to hold the version numbers for an interface file.

	Allow warnings to be reported for `nothing' items (used for
	reporting when version numbers are written using an
	obsolete format).

compiler/prog_io.m:
compiler/prog_io_util.m:
compiler/typecheck.m:
compiler/type_util.m:
compiler/*.m:
	Strip contexts from all types, not just those in class constraints.
	This makes it possible to use ordinary unification to check
	whether items have changed (with the exception of clauses).

	Remove code to create types with contexts in typechecking.

	Remove code scattered through the compiler to remove contexts
	from types in class constraints.

compiler/hlds_pred.m:
compiler/prog_util.m:
	Move hlds_pred__adjust_func_arity to prog_util, so that it
	can be used by the pre-hlds passes.

compiler/typecheck.m:
compiler/hlds_module.m:
	Move typecheck__visible_modules to hlds_module.m, so it can
	be used by recompilation_usage.m.

compiler/typecheck.m:
	Add a comment telling where updates may be required if the
	code to typecheck a var-functor unification changes.

compiler/error_util.m:
	Allow writing messages without contexts (used for the verbose
	recompilation messages).

	Add functions to format sym_name and sym_name_and_arity,
	and to add punctuation to the end of an error message
	without unwanted line breaks before the punctuation.

scripts/Mmake.rules:
compiler/modules.m:
	Don't remove the output file before running the compiler. We need
	to leave the old output file intact if smart recompilation detects
	that recompilation is not needed.

compiler/notes/compiler_design.html:
	Document the new modules.

library/io.m:
NEWS:
	Add predicates to find the modification time of files
	and input_streams.

library/set.m:
NEWS:
	Add a predicate version of set__fold

	Don't sort the output of set__filter, it's already sorted.

library/std_util.m:
NEWS:
	Add a predicate `std_util__map_maybe/3' and a function
  	`std_util__map_maybe/2' to apply a predicate or a function to
    	a value stored in a term of type `std_util__maybe'.

configure.in:
runtime/mercury_conf.h.in:
runtime/RESERVED_MACRO_NAMES:
	When checking whether the compiler is recent enough, check for
	the --warn-smart-recompilation option.

	Check for stat().

library/Mmakefile:
	Disable warnings about smart recompilation not working with
	`--intermodule-optimization'.

browser/Mmakefile:
	Disable warnings about smart recompilation not working when
	the module name doesn't match the file name.

runtime/mercury_string.h:
	Add a macro MR_make_string_const() which automates computation
	of the length of string argument to MR_string_const().

tests/recompilation/Mmakefile:
tests/recompilation/runtests:
tests/recompilation/test_functions:
tests/recompilation/TESTS:
tests/recompilation/README:
	A framework for testing smart recompilation.
	The option currently only works for the recompilation directory.

tests/recompilation/TEST.m.{1,2}:
tests/recompilation/TEST_2.m.{1,2}:
tests/recompilation/TEST.exp.{1,2}:
tests/recompilation/TEST.err_exp.2:
	Test cases, where TEST is one of add_constructor_r, add_instance_r,
	add_instance_2_r, add_type_nr, change_class_r, change_instance_r,
	change_mode_r, field_r, func_overloading_nr, func_overloading_r,
	lambda_mode_r, nested_module_r, no_version_numbers_r,
	pragma_type_spec_r, pred_ctor_ambiguity_r, pred_overloading_r,
	add_type_re, remove_type_re, type_qual_re.

tests/handle_options:
	Add an option `-e' to generate any missing expected output files.
2001-06-27 05:05:21 +00:00

468 lines
16 KiB
Mathematica

%-----------------------------------------------------------------------------%
% Copyright (C) 1996-2001 The University of Melbourne.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%-----------------------------------------------------------------------------%
%
% File: prog_io_dcg.m.
% Main authors: fjh, zs.
%
% This module handles the parsing of clauses in Definite Clause Grammar
% notation.
:- module prog_io_dcg.
:- interface.
:- import_module prog_data, prog_io_util.
:- import_module varset, term.
:- pred parse_dcg_clause(module_name, varset, term, term,
prog_context, maybe_item_and_context).
:- mode parse_dcg_clause(in, in, in, in, in, out) is det.
% parse_dcg_pred_goal(GoalTerm, VarSet0, Goal,
% DCGVarInitial, DCGVarFinal, Varset)
% parses `GoalTerm' and expands it as a DCG goal,
% `VarSet0' is the initial varset, and `VarSet' is
% the final varset. `DCGVarInitial' is the first DCG variable,
% and `DCGVarFinal' is the final DCG variable.
:- pred parse_dcg_pred_goal(term, prog_varset, goal, prog_var,
prog_var, prog_varset).
:- mode parse_dcg_pred_goal(in, in, out, out, out, out) is det.
:- implementation.
:- import_module prog_io, prog_io_goal, prog_util, purity.
:- import_module int, map, string, std_util, list.
%-----------------------------------------------------------------------------%
parse_dcg_clause(ModuleName, VarSet0, DCG_Head, DCG_Body, DCG_Context,
Result) :-
varset__coerce(VarSet0, ProgVarSet0),
new_dcg_var(ProgVarSet0, 0, ProgVarSet1, N0, DCG_0_Var),
parse_dcg_goal(DCG_Body, ProgVarSet1, N0, DCG_0_Var,
Body, ProgVarSet, _N, DCG_Var),
parse_implicitly_qualified_term(ModuleName,
DCG_Head, DCG_Body, "DCG clause head", HeadResult),
process_dcg_clause(HeadResult, ProgVarSet, DCG_0_Var, DCG_Var,
Body, R),
add_context(R, DCG_Context, Result).
%-----------------------------------------------------------------------------%
parse_dcg_pred_goal(GoalTerm, VarSet0, Goal, DCGVar0, DCGVar, VarSet) :-
new_dcg_var(VarSet0, 0, VarSet1, N0, DCGVar0),
parse_dcg_goal(GoalTerm, VarSet1, N0, DCGVar0,
Goal, VarSet, _N, DCGVar).
%-----------------------------------------------------------------------------%
% Used to allocate fresh variables needed for the DCG expansion.
:- pred new_dcg_var(prog_varset, int, prog_varset, int, prog_var).
:- mode new_dcg_var(in, in, out, out, out) is det.
new_dcg_var(VarSet0, N0, VarSet, N, DCG_0_Var) :-
string__int_to_string(N0, StringN),
string__append("DCG_", StringN, VarName),
varset__new_var(VarSet0, DCG_0_Var, VarSet1),
varset__name_var(VarSet1, DCG_0_Var, VarName, VarSet),
N is N0 + 1.
%-----------------------------------------------------------------------------%
% Expand a DCG goal.
:- pred parse_dcg_goal(term, prog_varset, int, prog_var, goal,
prog_varset, int, prog_var).
:- mode parse_dcg_goal(in, in, in, in, out, out, out, out) is det.
parse_dcg_goal(Term, VarSet0, N0, Var0, Goal, VarSet, N, Var) :-
% first, figure out the context for the goal
(
Term = term__functor(_, _, Context)
;
Term = term__variable(_),
term__context_init(Context)
),
% next, parse it
(
term__coerce(Term, ProgTerm),
sym_name_and_args(ProgTerm, SymName, Args0)
->
% First check for the special cases:
(
SymName = unqualified(Functor),
list__map(term__coerce, Args0, Args1),
parse_dcg_goal_2(Functor, Args1, Context,
VarSet0, N0, Var0, Goal1, VarSet1, N1, Var1)
->
Goal = Goal1,
VarSet = VarSet1,
N = N1,
Var = Var1
;
% It's the ordinary case of non-terminal.
% Create a fresh var as the DCG output var from this
% goal, and append the DCG argument pair to the
% non-terminal's argument list.
new_dcg_var(VarSet0, N0, VarSet, N, Var),
list__append(Args0,
[term__variable(Var0),
term__variable(Var)], Args),
Goal = call(SymName, Args, pure) - Context
)
;
% A call to a free variable, or to a number or string.
% Just translate it into a call to call/3 - the typechecker
% will catch calls to numbers and strings.
new_dcg_var(VarSet0, N0, VarSet, N, Var),
term__coerce(Term, ProgTerm),
Goal = call(unqualified("call"), [ProgTerm,
term__variable(Var0), term__variable(Var)],
pure) - Context
).
% parse_dcg_goal_2(Functor, Args, Context, VarSet0, N0, Var0,
% Goal, VarSet, N, Var):
% VarSet0/VarSet are an accumulator pair which we use to
% allocate fresh DCG variables; N0 and N are an accumulator pair
% we use to keep track of the number to give to the next DCG
% variable (so that we can give it a semi-meaningful name "DCG_<N>"
% for use in error messages, debugging, etc.).
% Var0 and Var are an accumulator pair we use to keep track of
% the current DCG variable.
:- pred parse_dcg_goal_2(string, list(term), prog_context, prog_varset,
int, prog_var, goal, prog_varset, int, prog_var).
:- mode parse_dcg_goal_2(in, in, in, in, in, in, out, out, out, out)
is semidet.
% Ordinary goal inside { curly braces }.
parse_dcg_goal_2("{}", [G0 | Gs], Context, VarSet0, N, Var,
Goal, VarSet, N, Var) :-
% The parser treats '{}/N' terms as tuples, so we need
% to undo the parsing of the argument conjunction here.
list_to_conjunction(Context, G0, Gs, G),
parse_goal(G, VarSet0, Goal, VarSet).
parse_dcg_goal_2("impure", [G], _, VarSet0, N0, Var0, Goal, VarSet, N, Var) :-
parse_dcg_goal_with_purity(G, VarSet0, N0, Var0, (impure),
Goal, VarSet, N, Var).
parse_dcg_goal_2("semipure", [G], _, VarSet0, N0, Var0, Goal, VarSet, N,
Var) :-
parse_dcg_goal_with_purity(G, VarSet0, N0, Var0, (semipure),
Goal, VarSet, N, Var).
% Empty list - just unify the input and output DCG args.
parse_dcg_goal_2("[]", [], Context, VarSet0, N0, Var0,
Goal, VarSet, N, Var) :-
new_dcg_var(VarSet0, N0, VarSet, N, Var),
Goal = unify(term__variable(Var0), term__variable(Var), pure) - Context.
% Non-empty list of terminals. Append the DCG output arg
% as the new tail of the list, and unify the result with
% the DCG input arg.
parse_dcg_goal_2(".", [X, Xs], Context, VarSet0, N0, Var0,
Goal, VarSet, N, Var) :-
new_dcg_var(VarSet0, N0, VarSet, N, Var),
ConsTerm0 = term__functor(term__atom("."), [X, Xs], Context),
term__coerce(ConsTerm0, ConsTerm),
term_list_append_term(ConsTerm, term__variable(Var), Term),
Goal = unify(term__variable(Var0), Term, pure) - Context.
% Call to '='/1 - unify argument with DCG input arg.
parse_dcg_goal_2("=", [A0], Context, VarSet, N, Var, Goal, VarSet, N, Var) :-
term__coerce(A0, A),
Goal = unify(A, term__variable(Var), pure) - Context.
% Call to ':='/1 - unify argument with DCG output arg.
parse_dcg_goal_2(":=", [A0], Context, VarSet0, N0, _Var0,
Goal, VarSet, N, Var) :-
new_dcg_var(VarSet0, N0, VarSet, N, Var),
term__coerce(A0, A),
Goal = unify(A, term__variable(Var), pure) - Context.
% If-then (Prolog syntax).
% We need to add an else part to unify the DCG args.
/******
Since (A -> B) has different semantics in standard Prolog
(A -> B ; fail) than it does in NU-Prolog or Mercury (A -> B ; true),
for the moment we'll just disallow it.
parse_dcg_goal_2("->", [Cond0, Then0], Context, VarSet0, N0, Var0,
Goal, VarSet, N, Var) :-
parse_dcg_if_then(Cond0, Then0, Context, VarSet0, N0, Var0,
SomeVars, Cond, Then, VarSet, N, Var),
( Var = Var0 ->
Goal = if_then(SomeVars, Cond, Then) - Context
;
Unify = unify(term__variable(Var), term__variable(Var0)),
Goal = if_then_else(SomeVars, Cond, Then, Unify - Context)
- Context
).
******/
% If-then (NU-Prolog syntax).
parse_dcg_goal_2("if", [
term__functor(term__atom("then"), [Cond0, Then0], _)
], Context, VarSet0, N0, Var0, Goal, VarSet, N, Var) :-
parse_dcg_if_then(Cond0, Then0, Context, VarSet0, N0, Var0,
SomeVars, Cond, Then, VarSet, N, Var),
( Var = Var0 ->
Goal = if_then(SomeVars, Cond, Then) - Context
;
Unify = unify(term__variable(Var), term__variable(Var0), pure),
Goal = if_then_else(SomeVars, Cond, Then, Unify - Context)
- Context
).
% Conjunction.
parse_dcg_goal_2(",", [A0, B0], Context, VarSet0, N0, Var0,
(A, B) - Context, VarSet, N, Var) :-
parse_dcg_goal(A0, VarSet0, N0, Var0, A, VarSet1, N1, Var1),
parse_dcg_goal(B0, VarSet1, N1, Var1, B, VarSet, N, Var).
parse_dcg_goal_2("&", [A0, B0], Context, VarSet0, N0, Var0,
(A & B) - Context, VarSet, N, Var) :-
parse_dcg_goal(A0, VarSet0, N0, Var0, A, VarSet1, N1, Var1),
parse_dcg_goal(B0, VarSet1, N1, Var1, B, VarSet, N, Var).
% Disjunction or if-then-else (Prolog syntax).
parse_dcg_goal_2(";", [A0, B0], Context, VarSet0, N0, Var0,
Goal, VarSet, N, Var) :-
(
A0 = term__functor(term__atom("->"), [Cond0, Then0], _Context)
->
parse_dcg_if_then_else(Cond0, Then0, B0, Context,
VarSet0, N0, Var0, Goal, VarSet, N, Var)
;
parse_dcg_goal(A0, VarSet0, N0, Var0,
A1, VarSet1, N1, VarA),
parse_dcg_goal(B0, VarSet1, N1, Var0,
B1, VarSet, N, VarB),
( VarA = Var0, VarB = Var0 ->
Var = Var0,
Goal = (A1 ; B1) - Context
; VarA = Var0 ->
Var = VarB,
Unify = unify(term__variable(Var),
term__variable(VarA), pure),
append_to_disjunct(A1, Unify, Context, A2),
Goal = (A2 ; B1) - Context
; VarB = Var0 ->
Var = VarA,
Unify = unify(term__variable(Var),
term__variable(VarB), pure),
append_to_disjunct(B1, Unify, Context, B2),
Goal = (A1 ; B2) - Context
;
Var = VarB,
prog_util__rename_in_goal(A1, VarA, VarB, A2),
Goal = (A2 ; B1) - Context
)
).
% If-then-else (NU-Prolog syntax).
parse_dcg_goal_2( "else", [
term__functor(term__atom("if"), [
term__functor(term__atom("then"), [Cond0, Then0], _)
], Context),
Else0
], _, VarSet0, N0, Var0, Goal, VarSet, N, Var) :-
parse_dcg_if_then_else(Cond0, Then0, Else0, Context,
VarSet0, N0, Var0, Goal, VarSet, N, Var).
% Negation (NU-Prolog syntax).
parse_dcg_goal_2( "not", [A0], Context, VarSet0, N0, Var0,
not(A) - Context, VarSet, N, Var ) :-
parse_dcg_goal(A0, VarSet0, N0, Var0, A, VarSet, N, _),
Var = Var0.
% Negation (Prolog syntax).
parse_dcg_goal_2( "\\+", [A0], Context, VarSet0, N0, Var0,
not(A) - Context, VarSet, N, Var ) :-
parse_dcg_goal(A0, VarSet0, N0, Var0, A, VarSet, N, _),
Var = Var0.
% Universal quantification.
parse_dcg_goal_2("all", [Vars0, A0], Context,
VarSet0, N0, Var0, all(Vars, A) - Context,
VarSet, N, Var) :-
term__coerce(Vars0, Vars1),
term__vars(Vars1, Vars),
parse_dcg_goal(A0, VarSet0, N0, Var0, A, VarSet, N, Var).
% Existential quantification.
parse_dcg_goal_2("some", [Vars0, A0], Context,
VarSet0, N0, Var0, some(Vars, A) - Context,
VarSet, N, Var) :-
term__coerce(Vars0, Vars1),
term__vars(Vars1, Vars),
parse_dcg_goal(A0, VarSet0, N0, Var0, A, VarSet, N, Var).
:- pred parse_dcg_goal_with_purity(term, prog_varset, int, prog_var,
purity, goal, prog_varset, int, prog_var).
:- mode parse_dcg_goal_with_purity(in, in, in, in, in, out, out, out, out)
is det.
parse_dcg_goal_with_purity(G, VarSet0, N0, Var0, Purity, Goal, VarSet,
N, Var) :-
parse_dcg_goal(G, VarSet0, N0, Var0, Goal1, VarSet, N, Var),
( Goal1 = call(Pred, Args, pure) - Context ->
Goal = call(Pred, Args, Purity) - Context
; Goal1 = unify(ProgTerm1, ProgTerm2, pure) - Context ->
Goal = unify(ProgTerm1, ProgTerm2, Purity) - Context
;
% Inappropriate placement of an impurity marker, so we treat
% it like a predicate call. typecheck.m prints out something
% descriptive for these errors.
Goal1 = _ - Context,
purity_name(Purity, PurityString),
term__coerce(G, G1),
Goal = call(unqualified(PurityString), [G1], pure) - Context
).
:- pred append_to_disjunct(goal, goal_expr, prog_context, goal).
:- mode append_to_disjunct(in, in, in, out) is det.
append_to_disjunct(Disjunct0, Goal, Context, Disjunct) :-
( Disjunct0 = (A0 ; B0) - Context2 ->
append_to_disjunct(A0, Goal, Context, A),
append_to_disjunct(B0, Goal, Context, B),
Disjunct = (A ; B) - Context2
;
Disjunct = (Disjunct0, Goal - Context) - Context
).
:- pred parse_some_vars_dcg_goal(term, list(prog_var), prog_varset,
int, prog_var, goal, prog_varset, int, prog_var).
:- mode parse_some_vars_dcg_goal(in, out, in, in, in, out, out, out, out)
is det.
parse_some_vars_dcg_goal(A0, SomeVars, VarSet0, N0, Var0,
A, VarSet, N, Var) :-
( A0 = term__functor(term__atom("some"), [SomeVars0, A1], _Context) ->
term__coerce(SomeVars0, SomeVars1),
term__vars(SomeVars1, SomeVars),
A2 = A1
;
SomeVars = [],
A2 = A0
),
parse_dcg_goal(A2, VarSet0, N0, Var0, A, VarSet, N, Var).
% Parse the "if" and the "then" part of an if-then or an
% if-then-else.
% If the condition is a DCG goal, but then "then" part
% is not, then we need to translate
% ( a -> { b } ; c )
% as
% ( a(DCG_1, DCG_2) ->
% b,
% DCG_3 = DCG_2
% ;
% c(DCG_1, DCG_3)
% )
% rather than
% ( a(DCG_1, DCG_2) ->
% b
% ;
% c(DCG_1, DCG_2)
% )
% so that the implicit quantification of DCG_2 is correct.
:- pred parse_dcg_if_then(term, term, prog_context, prog_varset, int,
prog_var, list(prog_var), goal, goal, prog_varset, int,
prog_var).
:- mode parse_dcg_if_then(in, in, in, in, in, in, out, out, out, out, out,
out) is det.
parse_dcg_if_then(Cond0, Then0, Context, VarSet0, N0, Var0,
SomeVars, Cond, Then, VarSet, N, Var) :-
parse_some_vars_dcg_goal(Cond0, SomeVars, VarSet0, N0, Var0,
Cond, VarSet1, N1, Var1),
parse_dcg_goal(Then0, VarSet1, N1, Var1, Then1, VarSet2, N2,
Var2),
( Var0 \= Var1, Var1 = Var2 ->
new_dcg_var(VarSet2, N2, VarSet, N, Var),
Unify = unify(term__variable(Var), term__variable(Var2), pure),
Then = (Then1, Unify - Context) - Context
;
Then = Then1,
N = N2,
Var = Var2,
VarSet = VarSet2
).
:- pred parse_dcg_if_then_else(term, term, term, prog_context,
prog_varset, int, prog_var, goal, prog_varset, int, prog_var).
:- mode parse_dcg_if_then_else(in, in, in, in, in, in, in,
out, out, out, out) is det.
parse_dcg_if_then_else(Cond0, Then0, Else0, Context, VarSet0, N0, Var0,
Goal, VarSet, N, Var) :-
parse_dcg_if_then(Cond0, Then0, Context, VarSet0, N0, Var0,
SomeVars, Cond, Then1, VarSet1, N1, VarThen),
parse_dcg_goal(Else0, VarSet1, N1, Var0, Else1, VarSet, N,
VarElse),
( VarThen = Var0, VarElse = Var0 ->
Var = Var0,
Then = Then1,
Else = Else1
; VarThen = Var0 ->
Var = VarElse,
Unify = unify(term__variable(Var), term__variable(VarThen),
pure),
Then = (Then1, Unify - Context) - Context,
Else = Else1
; VarElse = Var0 ->
Var = VarThen,
Then = Then1,
Unify = unify(term__variable(Var), term__variable(VarElse),
pure),
Else = (Else1, Unify - Context) - Context
;
% We prefer to substitute the then part since it is likely
% to be smaller than the else part, since the else part may
% have a deeply nested chain of if-then-elses.
% parse_dcg_if_then guarantees that if VarThen \= Var0,
% then the then part introduces a new DCG variable (i.e.
% VarThen does not appear in the condition). We therefore
% don't need to do the substitution in the condition.
Var = VarElse,
prog_util__rename_in_goal(Then1, VarThen, VarElse, Then),
Else = Else1
),
Goal = if_then_else(SomeVars, Cond, Then, Else) - Context.
% term_list_append_term(ListTerm, Term, Result):
% if ListTerm is a term representing a proper list,
% this predicate will append the term Term
% onto the end of the list
:- pred term_list_append_term(term(T), term(T), term(T)).
:- mode term_list_append_term(in, in, out) is semidet.
term_list_append_term(List0, Term, List) :-
( List0 = term__functor(term__atom("[]"), [], _Context) ->
List = Term
;
List0 = term__functor(term__atom("."), [Head, Tail0], Context2),
List = term__functor(term__atom("."), [Head, Tail], Context2),
term_list_append_term(Tail0, Term, Tail)
).
:- pred process_dcg_clause(maybe_functor, prog_varset, prog_var,
prog_var, goal, maybe1(item)).
:- mode process_dcg_clause(in, in, in, in, in, out) is det.
process_dcg_clause(ok(Name, Args0), VarSet, Var0, Var, Body,
ok(clause(VarSet, predicate, Name, Args, Body))) :-
list__map(term__coerce, Args0, Args1),
list__append(Args1, [term__variable(Var0),
term__variable(Var)], Args).
process_dcg_clause(error(Message, Term), _, _, _, _, error(Message, Term)).