mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 01:13:30 +00:00
library/term_subst.m:
Document when term_list_to_var_list/2 fails.
Minor formatting fix.
library/term_to_xml.m:
Fix a documentation error.
437 lines
15 KiB
Mathematica
437 lines
15 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ts=4 sw=4 et ft=mercury
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 1993-2000,2003-2009,2011-2012 The University of Melbourne.
|
|
% Copyright (C) 2014-2022, 2025-2026 The Mercury team.
|
|
% This file is distributed under the terms specified in COPYING.LIB.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: term_subst.m.
|
|
% Main author: fjh.
|
|
% Stability: high.
|
|
%
|
|
% This file provides operations that perform substitutions of various kinds
|
|
% on terms.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module term_subst.
|
|
:- interface.
|
|
|
|
:- import_module list.
|
|
:- import_module term.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Predicates that look for variables in terms, possibly after a substitution.
|
|
%
|
|
|
|
% var_occurs_in_subst_term(Var, Substitution, Term):
|
|
%
|
|
% True if-and-only-if Var occurs in the term resulting after
|
|
% applying Substitution to Term. Var must not be mapped by Substitution.
|
|
%
|
|
:- pred var_occurs_in_subst_term(var(T)::in, substitution(T)::in,
|
|
term(T)::in) is semidet.
|
|
|
|
% As above, except for a list of terms rather than a single term.
|
|
%
|
|
:- pred var_occurs_in_subst_terms(var(T)::in, substitution(T)::in,
|
|
list(term(T))::in) is semidet.
|
|
|
|
% term_is_ground(Term) is true if-and-only-if Term contains no variables.
|
|
%
|
|
:- pred term_is_ground(term(T)::in) is semidet.
|
|
|
|
% term_is_ground_in_bindings(Term, Bindings) is true if-and-only-if
|
|
% all variables contained in Term are mapped to ground terms by Bindings.
|
|
%
|
|
:- pred term_is_ground_in_bindings(term(T)::in, substitution(T)::in)
|
|
is semidet.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Rename predicates that specify the substitution by giving the
|
|
% variable/variable pair or pairs directly.
|
|
%
|
|
|
|
% rename_var_in_term(Var, ReplacementVar, Term0, Term):
|
|
%
|
|
% Replace all occurrences of Var in Term0 with ReplacementVar,
|
|
% and return the result in Term.
|
|
%
|
|
:- pred rename_var_in_term(var(T)::in, var(T)::in,
|
|
term(T)::in, term(T)::out) is det.
|
|
|
|
% rename_var_in_terms(Var, ReplacementVar, Terms0, Terms):
|
|
%
|
|
% Replace all occurrences of Var in Terms0 with ReplacementVar,
|
|
% and return the result in Terms.
|
|
%
|
|
:- pred rename_var_in_terms(var(T)::in, var(T)::in,
|
|
list(term(T))::in, list(term(T))::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Rename predicates that specify the renaming by giving an explicit
|
|
% variable-to-variable map.
|
|
%
|
|
|
|
% apply_renaming_in_var(Renaming, Var0, Var):
|
|
%
|
|
% Apply Renaming in Var0, and return the result as Var.
|
|
%
|
|
:- pred apply_renaming_in_var(renaming(T)::in,
|
|
var(T)::in, var(T)::out) is det.
|
|
|
|
% apply_renaming_in_vars(Renaming, Vars0, Vars):
|
|
%
|
|
% Apply Renaming in Vars0, and return the result as Vars.
|
|
%
|
|
:- pred apply_renaming_in_vars(renaming(T)::in,
|
|
list(var(T))::in, list(var(T))::out) is det.
|
|
|
|
% apply_renaming_in_term(Renaming, Term0, Term):
|
|
%
|
|
% Apply Renaming in Term0, and return the result as Term.
|
|
%
|
|
:- pred apply_renaming_in_term(renaming(T)::in,
|
|
term(T)::in, term(T)::out) is det.
|
|
|
|
% apply_renaming_in_terms(Renaming, Terms0, Terms):
|
|
%
|
|
% Apply Renaming in Terms0, and return the result as Terms.
|
|
%
|
|
:- pred apply_renaming_in_terms(renaming(T)::in,
|
|
list(term(T))::in, list(term(T))::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Substitution predicates that specify the substitution by giving the
|
|
% variable/term pair or pairs directly.
|
|
%
|
|
|
|
% substitute_var_in_term(Var, ReplacementTerm, Term0, Term):
|
|
%
|
|
% Replace all occurrences of Var in Term0 with ReplacementTerm,
|
|
% and return the result in Term.
|
|
%
|
|
:- pred substitute_var_in_term(var(T)::in, term(T)::in,
|
|
term(T)::in, term(T)::out) is det.
|
|
|
|
% substitute_var_in_terms(Var, ReplacementTerm, Terms0, Terms):
|
|
%
|
|
% Replace all occurrences of Var in Terms0 with ReplacementTerm,
|
|
% and return the result in Terms.
|
|
%
|
|
:- pred substitute_var_in_terms(var(T)::in, term(T)::in,
|
|
list(term(T))::in, list(term(T))::out) is det.
|
|
|
|
% substitute_corresponding_in_term(Vars, ReplacementTerms, Term0, Term):
|
|
%
|
|
% Replace all occurrences of variables in Vars in Term0 with
|
|
% the corresponding term in ReplacementTerms, and return the result
|
|
% as Term. If Vars contains duplicates, or if Vars and ReplacementTerms
|
|
% have different lengths, the behaviour is undefined and probably harmful.
|
|
%
|
|
:- pred substitute_corresponding_in_term(list(var(T))::in, list(term(T))::in,
|
|
term(T)::in, term(T)::out) is det.
|
|
|
|
% substitute_corresponding_in_terms(Vars, ReplacementTerms, Terms0, Terms):
|
|
%
|
|
% Replace all occurrences of variables in Vars in Terms0 with
|
|
% the corresponding term in ReplacementTerms, and return the result
|
|
% as Terms. If Vars contains duplicates, or if Vars and ReplacementTerms
|
|
% have different lengths, the behaviour is undefined and probably harmful.
|
|
%
|
|
:- pred substitute_corresponding_in_terms(list(var(T))::in, list(term(T))::in,
|
|
list(term(T))::in, list(term(T))::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Substitution predicates that specify the substitution by giving
|
|
% an explicit variable-to-term map.
|
|
%
|
|
|
|
% apply_substitution_in_term(Substitution, Term0, Term):
|
|
%
|
|
% Apply Substitution to Term0 and return the result as Term.
|
|
%
|
|
:- pred apply_substitution_in_term(substitution(T)::in,
|
|
term(T)::in, term(T)::out) is det.
|
|
|
|
% apply_substitution_in_terms(Substitution, Terms0, Terms):
|
|
%
|
|
% Apply Substitution to Terms0 and return the result as Terms.
|
|
%
|
|
:- pred apply_substitution_in_terms(substitution(T)::in,
|
|
list(term(T))::in, list(term(T))::out) is det.
|
|
|
|
% apply_rec_substitution_in_term(Substitution, Term0, Term):
|
|
%
|
|
% Recursively apply Substitution to Term0 until no more substitutions
|
|
% can be applied, and then return the result as Term.
|
|
%
|
|
:- pred apply_rec_substitution_in_term(substitution(T)::in,
|
|
term(T)::in, term(T)::out) is det.
|
|
|
|
% apply_rec_substitution_in_terms(Substitution, Terms0, Terms):
|
|
%
|
|
% Recursively apply Substitution to Terms0 until no more substitutions
|
|
% can be applied, and then return the result as Terms.
|
|
%
|
|
:- pred apply_rec_substitution_in_terms(substitution(T)::in,
|
|
list(term(T))::in, list(term(T))::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Conversions between variables and terms.
|
|
%
|
|
|
|
% Convert a list of terms which are all variables into a list of those
|
|
% variables. Throw an exception if the list contains any terms that are
|
|
% not variables.
|
|
%
|
|
:- func term_list_to_var_list(list(term(T))) = list(var(T)).
|
|
|
|
% Convert a list of terms which are all variables into a list of those
|
|
% variables. Fails if the list contains any terms that are not variables.
|
|
%
|
|
:- pred term_list_to_var_list(list(term(T))::in, list(var(T))::out) is semidet.
|
|
|
|
% Convert a list of variables into a list of terms, each containing
|
|
% one of those variables.
|
|
%
|
|
:- func var_list_to_term_list(list(var(T))) = list(term(T)).
|
|
:- pred var_list_to_term_list(list(var(T))::in, list(term(T))::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module map.
|
|
:- import_module require.
|
|
:- import_module term_context.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
var_occurs_in_subst_term(Var, Subst, Term) :-
|
|
(
|
|
Term = variable(X, _Context),
|
|
( if X = Var then
|
|
true
|
|
else
|
|
map.search(Subst, X, TermBoundToX),
|
|
var_occurs_in_subst_term(Var, Subst, TermBoundToX)
|
|
)
|
|
;
|
|
Term = functor(_Name, ArgTerms, _Context),
|
|
var_occurs_in_subst_terms(Var, Subst, ArgTerms)
|
|
).
|
|
|
|
var_occurs_in_subst_terms(Var, Subst, [Term | Terms]) :-
|
|
( if var_occurs_in_subst_term(Var, Subst, Term) then
|
|
true
|
|
else
|
|
var_occurs_in_subst_terms(Var, Subst, Terms)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
term_is_ground(functor(_, ArgTerms, _)) :-
|
|
terms_are_ground(ArgTerms).
|
|
|
|
:- pred terms_are_ground(list(term(T))::in) is semidet.
|
|
|
|
terms_are_ground([]).
|
|
terms_are_ground([Term | Terms]) :-
|
|
term_is_ground(Term),
|
|
terms_are_ground(Terms).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
term_is_ground_in_bindings(Term, Bindings) :-
|
|
(
|
|
Term = variable(Var, _),
|
|
map.search(Bindings, Var, BoundTerm),
|
|
term_is_ground_in_bindings(BoundTerm, Bindings)
|
|
;
|
|
Term = functor(_, ArgTerms, _),
|
|
terms_are_ground_in_bindings(ArgTerms, Bindings)
|
|
).
|
|
|
|
:- pred terms_are_ground_in_bindings(list(term(T))::in, substitution(T)::in)
|
|
is semidet.
|
|
|
|
terms_are_ground_in_bindings([], _Bindings).
|
|
terms_are_ground_in_bindings([Term | Terms], Bindings) :-
|
|
term_is_ground_in_bindings(Term, Bindings),
|
|
terms_are_ground_in_bindings(Terms, Bindings).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
rename_var_in_term(Var, ReplacementVar, Term0, Term) :-
|
|
(
|
|
Term0 = variable(Var0, Context),
|
|
( if Var0 = Var then
|
|
Term = variable(ReplacementVar, Context)
|
|
else
|
|
Term = Term0
|
|
)
|
|
;
|
|
Term0 = functor(Name, ArgTerms0, Context),
|
|
term_subst.rename_var_in_terms(Var, ReplacementVar,
|
|
ArgTerms0, ArgTerms),
|
|
Term = functor(Name, ArgTerms, Context)
|
|
).
|
|
|
|
rename_var_in_terms(_Var, _ReplacementVar, [], []).
|
|
rename_var_in_terms(Var, ReplacementVar, [Term0 | Terms0], [Term | Terms]) :-
|
|
term_subst.rename_var_in_term(Var, ReplacementVar, Term0, Term),
|
|
term_subst.rename_var_in_terms(Var, ReplacementVar, Terms0, Terms).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
apply_renaming_in_var(Renaming, Var0, Var) :-
|
|
( if map.search(Renaming, Var0, NewVar) then
|
|
Var = NewVar
|
|
else
|
|
Var = Var0
|
|
).
|
|
|
|
apply_renaming_in_vars(_Renaming, [], []).
|
|
apply_renaming_in_vars(Renaming, [Var0 | Vars0], [Var | Vars]) :-
|
|
term_subst.apply_renaming_in_var(Renaming, Var0, Var),
|
|
term_subst.apply_renaming_in_vars(Renaming, Vars0, Vars).
|
|
|
|
apply_renaming_in_term(Renaming, Term0, Term) :-
|
|
(
|
|
Term0 = variable(Var0, Context),
|
|
term_subst.apply_renaming_in_var(Renaming, Var0, Var),
|
|
Term = variable(Var, Context)
|
|
;
|
|
Term0 = functor(Name, ArgTerms0, Context),
|
|
term_subst.apply_renaming_in_terms(Renaming, ArgTerms0, ArgTerms),
|
|
Term = functor(Name, ArgTerms, Context)
|
|
).
|
|
|
|
apply_renaming_in_terms(_, [], []).
|
|
apply_renaming_in_terms(Renaming, [Term0 | Terms0], [Term | Terms]) :-
|
|
term_subst.apply_renaming_in_term(Renaming, Term0, Term),
|
|
term_subst.apply_renaming_in_terms(Renaming, Terms0, Terms).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
substitute_var_in_term(Var, ReplacementTerm, Term0, Term) :-
|
|
(
|
|
Term0 = variable(Var0, _Context),
|
|
( if Var0 = Var then
|
|
Term = ReplacementTerm
|
|
else
|
|
Term = Term0
|
|
)
|
|
;
|
|
Term0 = functor(Name, ArgTerms0, Context),
|
|
term_subst.substitute_var_in_terms(Var, ReplacementTerm,
|
|
ArgTerms0, ArgTerms),
|
|
Term = functor(Name, ArgTerms, Context)
|
|
).
|
|
|
|
substitute_var_in_terms(_Var, _ReplacementTerm, [], []).
|
|
substitute_var_in_terms(Var, ReplacementTerm,
|
|
[Term0 | Terms0], [Term | Terms]) :-
|
|
term_subst.substitute_var_in_term(Var, ReplacementTerm, Term0, Term),
|
|
term_subst.substitute_var_in_terms(Var, ReplacementTerm, Terms0, Terms).
|
|
|
|
substitute_corresponding_in_term(Vars, ReplacementTerms, Term0, Term) :-
|
|
map.init(Subst0),
|
|
build_subst(Vars, ReplacementTerms, Subst0, Subst),
|
|
term_subst.apply_substitution_in_term(Subst, Term0, Term).
|
|
|
|
substitute_corresponding_in_terms(Vars, ReplacementTerms, Terms0, Terms) :-
|
|
map.init(Subst0),
|
|
build_subst(Vars, ReplacementTerms, Subst0, Subst),
|
|
term_subst.apply_substitution_in_terms(Subst, Terms0, Terms).
|
|
|
|
%---------------------%
|
|
|
|
:- pred build_subst(list(var(T))::in, list(term(T))::in,
|
|
substitution(T)::in, substitution(T)::out) is det.
|
|
|
|
build_subst([], [], !Subst).
|
|
build_subst([], [_ | _], !Subst) :-
|
|
unexpected($pred, "length mismatch").
|
|
build_subst([_ | _], [], !Subst) :-
|
|
unexpected($pred, "length mismatch").
|
|
build_subst([Var | Vars], [Term | Terms], !Subst) :-
|
|
map.set(Var, Term, !Subst),
|
|
build_subst(Vars, Terms, !Subst).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
apply_substitution_in_term(Subst, Term0, Term) :-
|
|
(
|
|
Term0 = variable(Var, _),
|
|
( if map.search(Subst, Var, ReplacementTerm) then
|
|
Term = ReplacementTerm
|
|
else
|
|
Term = Term0
|
|
)
|
|
;
|
|
Term0 = functor(Name, ArgTerms0, Context),
|
|
term_subst.apply_substitution_in_terms(Subst, ArgTerms0, ArgTerms),
|
|
Term = functor(Name, ArgTerms, Context)
|
|
).
|
|
|
|
apply_substitution_in_terms(_Subst, [], []).
|
|
apply_substitution_in_terms(Subst, [Term0 | Terms0], [Term | Terms]) :-
|
|
term_subst.apply_substitution_in_term(Subst, Term0, Term),
|
|
term_subst.apply_substitution_in_terms(Subst, Terms0, Terms).
|
|
|
|
apply_rec_substitution_in_term(Subst, Term0, Term) :-
|
|
(
|
|
Term0 = variable(Var, _),
|
|
( if map.search(Subst, Var, ReplacementTerm) then
|
|
% Recursively apply the substitution to the replacement.
|
|
term_subst.apply_rec_substitution_in_term(Subst,
|
|
ReplacementTerm, Term)
|
|
else
|
|
Term = Term0
|
|
)
|
|
;
|
|
Term0 = functor(Name, ArgTerms0, Context),
|
|
term_subst.apply_rec_substitution_in_terms(Subst, ArgTerms0, ArgTerms),
|
|
Term = functor(Name, ArgTerms, Context)
|
|
).
|
|
|
|
apply_rec_substitution_in_terms(_Subst, [], []).
|
|
apply_rec_substitution_in_terms(Subst, [Term0 | Terms0], [Term | Terms]) :-
|
|
term_subst.apply_rec_substitution_in_term(Subst, Term0, Term),
|
|
term_subst.apply_rec_substitution_in_terms(Subst, Terms0, Terms).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
term_list_to_var_list(Terms) = Vars :-
|
|
( if term_subst.term_list_to_var_list(Terms, VarsPrime) then
|
|
Vars = VarsPrime
|
|
else
|
|
unexpected($pred, "not all vars")
|
|
).
|
|
|
|
term_list_to_var_list([], []).
|
|
term_list_to_var_list([variable(Var, _) | Terms], [Var | Vars]) :-
|
|
term_subst.term_list_to_var_list(Terms, Vars).
|
|
|
|
var_list_to_term_list(Vs) = Ts :-
|
|
term_subst.var_list_to_term_list(Vs, Ts).
|
|
|
|
var_list_to_term_list([], []).
|
|
var_list_to_term_list([Var | Vars], [variable(Var, dummy_context) | Terms]) :-
|
|
term_subst.var_list_to_term_list(Vars, Terms).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module term_subst.
|
|
%---------------------------------------------------------------------------%
|