Files
mercury/compiler/prog_io_dcg.m
Simon Taylor 46a8da81cb Implement builtin tuple types, similar to those in Haskell.
Estimated hours taken: 30

Implement builtin tuple types, similar to those in Haskell.

Tuples are constructed and deconstructed using
the syntax X = {Arg1, Arg2, ...}.
Tuples have type `{Arg1, Arg2, ...}'.

Unary tuples (X = {Arg}) do work, unlike in Haskell. The rationale
for this is that it is useful to be able to construct unary tuples
to be passed to a polymorphic predicate which uses std_util__deconstruct
to deal with a tuple of any arity. Since this is probably the only
use for unary tuples, it's not really worth the effort of treating
them as no_tag types, so we don't.

The type-infos for tuples have the same structure as for higher-order
types. There is a single type_ctor_info for tuples, and the arity
is placed before the argument type_infos.

library/parser.m:
	Change the way '{}/N' terms are parsed, so that the parsed
	representation is consistent with the way other functors
	are represented (previously the arguments were left as
	unparsed ','/2 terms). This avoids special case code
	in prog_io__parse_qualified_term, term__term_to_type
	and term__type_to_term.

compiler/prog_io_dcg.m:
compiler/prog_io_util.m:
	Handle the new structure of '{}/N' terms when parsing DCG escapes
	by converting the argument list back into a single ','/2 term.

compiler/module_qual.m:
	Treat tuples as a builtin type.

compiler/typecheck.m:
	Typecheck tuple constructors.

compiler/mode_util.m:
	Propagate types into tuple bound insts.

compiler/type_util.m:
	Add type_is_tuple/2 and type_id_is_tuple/1 to identify tuple types.
	Add tuples to the list of types which are not atomic types.

	Handle tuple types in `type_constructors' and
	`get_cons_id_arg_types' and `switch_type_num_functors'.

compiler/tabling.m:
	Handle tabling of tuples.

compiler/term_util.m:
	Handle tuples in the code to compute functor norms.

compiler/magic_util.m:
compiler/rl.m:
compiler/rl_key.m:
	Handle tuple types in the Aditi back end.

compiler/mercury_to_mercury.m:
library/io.m:
library/term_io.m:
	Handle output of '{}/N' terms.

compiler/higher_order.m:
compiler/simplify.m:
	Don't specialize complicated unifications of tuple
	types into calls to a specific unification procedure --
	even if the procedure were implemented, it probably
	wouldn't be that much more efficient.

compiler/unify_proc.m:
	Generate unification procedures for complicated unifications
	of tuples (other than in-in unifications). These are generated
	lazily as required.

compiler/make_hlds.m:
	Export add_special_pred for use by unify_proc.m.

compiler/polymorphism.m:
	Export polymorphism__process_pred for use by unify_proc.m.

compiler/bytecode_gen.m:
compiler/code_util.m:
compiler/ml_code_util.m:
	Handle unify procedure names and tags for tuple types.

compiler/mlds_to_c.m:
	Output tuple types as MR_Tuple.

compiler/ml_unify_gen.m:
	Compute the field types for tuples.

compiler/polymorphism.m:
compiler/pseudo_type_info.m:
	Treat tuple type_infos in a similar way to higher-order type_infos.

compiler/hlds_data.m:
	Document how cons_ids for tuple types are represented.

compiler/switch_gen.m:
compiler/table_gen.m:
	Add tuple types to switches on type_util__builtin_type.

compiler/llds_out.m:
util/mdemangle.c:
profiler/demangle.m:
	Transform items named "{}" to "f_tuple" when mangling symbols.

library/builtin.m:
	Define the type_ctor_info used for tuples.

library/private_builtin.m:
	Add `builtin_unify_tuple/2' and `builtin_compare_tuple/3',
	both of which abort. All comparisons and in-in unifications
	of tuples are performed by the generic unification functions
	in runtime/mercury_ho_call.c and runtime/mercury.c.

library/std_util.m:
	Implement the various RTTI functions for tuples.

	Encode tuple `TypeCtorDesc's in a similar way to that
	used for higher-order types. This has the consequence that the limit
	on the arity of higher-order types is now MAX_VIRTUAL_REG,
	rather than 2*MAX_VIRTUAL_REG.

	Avoid calling MR_GC_free for the type-info vector returned
	from ML_expand() for tuples because unlike the vectors
	for du types, it is not copied.

runtime/mercury_type_info.h:
	Add macros for extracting fields from tuple type-infos.
	These just call the macros for extracting fields from higher-order
	type-infos.

	Add a macro MR_type_ctor_rep_is_variable_arity(), which
	returns TRUE for tuples and higher-order types.

	The distinction between higher-order and first-order types
	is now misnamed -- the distinction is really between fixed arity
	types and builtin variable arity types. I'm not sure whether
	it's worth renaming everything.

runtime/mercury.h:
runtime/mercury.c:
	Define unification and comparison of tuples in
	high-level code grades.

runtime/mercury_deep_copy_body.h:
runtime/mercury_make_type_info_body.h:
runtime/mercury_tabling.c:
runtime/mercury_unify_compare_body.h:
	Handle tuple types in code which traverses data using RTTI.

tests/hard_coded/construct.{m,exp}:
tests/hard_coded/expand.{m,exp}:
	Test RTTI functions from std_util.m applied to tuples.

tests/hard_coded/tuple_test.{m,exp}:
	Test unification, comparison, term_to_type etc. applied to tuples.

tests/hard_coded/deep_copy.{m,exp}:
	Test deep copy of tuples.

tests/hard_coded/typeclasses/tuple_instance.{m,exp}:
	Test instance declarations for tuples.

tests/tabling/expand_tuple.{m,exp}:
	Test tabling of tuples.

tests/hard_coded/write.m:
	Add some module qualifications for code which uses
	`{}/1' constructors which are not tuples.

tests/invalid/errors2.{m,err_exp,err_exp2}:
	Test handling of tuples in type errors messages.

NEWS:
doc/reference_manual.texi:
w3/news/newsdb.inc:
	Document tuples.

doc/transition_guide.texi:
	Document the change to the parsing of '{}/N' terms.
2000-09-18 11:53:19 +00:00

468 lines
16 KiB
Mathematica

%-----------------------------------------------------------------------------%
% Copyright (C) 1996-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: 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(pred_clause(VarSet, 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)).