mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-18 10:53:40 +00:00
library/varset.m:
We have long had two versions of the lookup_name function, and of
the equivalent lookup_name predicate, with the difference being
that one version had an extra argument that specified to prefix
for the names to be constructed for not-explicitly-named variables.
Obsolete the function and the predicate with this extra argument
in favor of a new function/predicate pair, whose shared name is
lookup_name_default_prefix.
Act on an old sortof-XXX by changing the order of arguments of the
set_bindings predicate to make it state-variable-friendly.
Improve some comments.
NEWS.md:
Document the above changes.
library/term.m:
Document the var and var_supply types at their definitions.
Don't intersperse obsolete pragmas together with predicate/function
declarations.
Update some old comments.
compiler/lp_rational.m:
compiler/mlds_to_target_util.m:
Replace calls to the newly-obsoleted function and predicate.
963 lines
35 KiB
Mathematica
963 lines
35 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 1993-2000,2002-2007, 2009-2011 The University of Melbourne.
|
|
% Copyright (C) 2014-2022 The Mercury team.
|
|
% This file is distributed under the terms specified in COPYING.LIB.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: varset.m.
|
|
% Main author: fjh.
|
|
% Stability: low.
|
|
%
|
|
% This file provides facilities for manipulating collections of variables,
|
|
% through the 'varset' abstract data type. These variables are object-level
|
|
% variables, and are represented as ground terms, so the right way to think
|
|
% of them is as "variable ids" rather than Prolog-style variables.
|
|
%
|
|
% A varset may record a name and/or a value (binding) with each variable.
|
|
%
|
|
% Many situations require dealing with several distinct sets of variables
|
|
% that should never be mixed together. For example, a compiler may handle
|
|
% both program variables and type variables, and it does not make sense
|
|
% to a have a single varset containing both program variables and type
|
|
% variables. The varsets provided by this module are thus parameterized;
|
|
% the compiler can use e.g. varset(prog_var_type) to hold program variables
|
|
% and varset(tvar_type) to hold type variables. Since all operations on
|
|
% two or more varsets require agreement on the argument of the varset/1
|
|
% type constructor, any accidental mixup of different instances of varset/1
|
|
% is guaranteed to be caught by the compiler.
|
|
%
|
|
% In situations in which this is not a concern, programmers may use
|
|
% the standard generic varset instance.
|
|
%
|
|
% Note that varset.m and term.m are strongly coupled together, meaning that
|
|
% they each need the other. The reason why they have not been merged into
|
|
% one larger module is that many user modules call predicates and functions
|
|
% from just one of these two modules, even though, through that one module,
|
|
% they implicitly depend on the other as well.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module varset.
|
|
:- interface.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module set.
|
|
:- import_module term.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type varset(T).
|
|
:- type varset == varset(generic).
|
|
|
|
%---------------------%
|
|
|
|
% Construct an empty varset.
|
|
%
|
|
:- func init = varset(T).
|
|
:- pred init(varset(T)::out) is det.
|
|
|
|
% Check whether a varset is empty.
|
|
%
|
|
:- pred is_empty(varset(T)::in) is semidet.
|
|
|
|
%---------------------%
|
|
|
|
% Create a new variable.
|
|
%
|
|
:- pred new_var(var(T)::out, varset(T)::in, varset(T)::out) is det.
|
|
|
|
% Create a new named variable.
|
|
%
|
|
:- pred new_named_var(string::in, var(T)::out,
|
|
varset(T)::in, varset(T)::out) is det.
|
|
|
|
% Create a new named variable with a unique (w.r.t. the varset) number
|
|
% appended to the name.
|
|
%
|
|
:- pred new_uniquely_named_var(string::in, var(T)::out,
|
|
varset(T)::in, varset(T)::out) is det.
|
|
|
|
% Create a new variable, and maybe give it a name.
|
|
%
|
|
:- pred new_maybe_named_var(maybe(string)::in, var(T)::out,
|
|
varset(T)::in, varset(T)::out) is det.
|
|
|
|
% Create multiple new variables.
|
|
% Throws an exception if a negative number of new variables
|
|
% is requested.
|
|
%
|
|
:- pred new_vars(int::in, list(var(T))::out,
|
|
varset(T)::in, varset(T)::out) is det.
|
|
|
|
%---------------------%
|
|
|
|
% Delete the name and value for a variable.
|
|
%
|
|
:- func delete_var(varset(T), var(T)) = varset(T).
|
|
:- pred delete_var(var(T)::in, varset(T)::in, varset(T)::out) is det.
|
|
|
|
% Delete the names and values for a list of variables.
|
|
%
|
|
:- func delete_vars(varset(T), list(var(T))) = varset(T).
|
|
:- pred delete_vars(list(var(T))::in, varset(T)::in, varset(T)::out) is det.
|
|
|
|
% Delete the names and values for a sorted list of variables.
|
|
% (If the list is not sorted, the predicate or function will
|
|
% either throw an exception or return incorrect output.)
|
|
%
|
|
:- func delete_sorted_vars(varset(T), list(var(T))) = varset(T).
|
|
:- pred delete_sorted_vars(list(var(T))::in,
|
|
varset(T)::in, varset(T)::out) is det.
|
|
|
|
%---------------------%
|
|
|
|
% Return a list of all the variables in a varset.
|
|
%
|
|
:- func vars(varset(T)) = list(var(T)).
|
|
:- pred vars(varset(T)::in, list(var(T))::out) is det.
|
|
|
|
%---------------------%
|
|
|
|
% Set the name of a variable.
|
|
%
|
|
:- func name_var(varset(T), var(T), string) = varset(T).
|
|
:- pred name_var(var(T)::in, string::in,
|
|
varset(T)::in, varset(T)::out) is det.
|
|
|
|
% Unset the name of a variable.
|
|
%
|
|
:- func unname_var(varset(T), var(T)) = varset(T).
|
|
:- pred unname_var(var(T)::in, varset(T)::in, varset(T)::out) is det.
|
|
|
|
% Look up the name of a variable;
|
|
% If it doesn't have one, return a default name consisting of two parts:
|
|
% "V_" as a prefix, followed by a unique number. This is meant to evoke
|
|
% "variable number N".
|
|
%
|
|
:- func lookup_name(varset(T), var(T)) = string.
|
|
:- pred lookup_name(varset(T)::in, var(T)::in, string::out) is det.
|
|
|
|
% Look up the name of a variable;
|
|
% if it doesn't have one, create one using the specified prefix.
|
|
%
|
|
:- func lookup_name(varset(T), var(T), string) = string.
|
|
:- pred lookup_name(varset(T)::in, var(T)::in, string::in, string::out)
|
|
is det.
|
|
:- pragma obsolete(func(lookup_name/3), [varset.lookup_name_default_prefix/3]).
|
|
:- pragma obsolete(pred(lookup_name/4), [varset.lookup_name_default_prefix/4]).
|
|
|
|
:- func lookup_name_default_prefix(varset(T), var(T), string) = string.
|
|
:- pred lookup_name_default_prefix(varset(T)::in, var(T)::in, string::in,
|
|
string::out) is det.
|
|
|
|
% Look up the name of a variable;
|
|
% fail if it doesn't have one.
|
|
%
|
|
:- pred search_name(varset(T)::in, var(T)::in, string::out) is semidet.
|
|
|
|
%---------------------%
|
|
|
|
% Bind a value to a variable.
|
|
% This will overwrite any existing binding.
|
|
%
|
|
:- func bind_var(varset(T), var(T), term(T)) = varset(T).
|
|
:- pred bind_var(var(T)::in, term(T)::in,
|
|
varset(T)::in, varset(T)::out) is det.
|
|
|
|
% Bind a set of terms to a set of variables.
|
|
%
|
|
:- func bind_vars(varset(T), substitution(T)) = varset(T).
|
|
:- pred bind_vars(substitution(T)::in,
|
|
varset(T)::in, varset(T)::out) is det.
|
|
|
|
% Look up the value of a variable.
|
|
%
|
|
:- pred search_var(varset(T)::in, var(T)::in, term(T)::out) is semidet.
|
|
|
|
%---------------------%
|
|
|
|
% Get the bindings for all the bound variables.
|
|
%
|
|
:- func lookup_vars(varset(T)) = substitution(T).
|
|
:- pred lookup_vars(varset(T)::in, substitution(T)::out) is det.
|
|
:- pragma obsolete(func(lookup_vars/1), [varset.get_bindings/1]).
|
|
:- pragma obsolete(pred(lookup_vars/2), [varset.get_bindings/2]).
|
|
|
|
% Get the bindings for all the bound variables.
|
|
%
|
|
:- func get_bindings(varset(T)) = substitution(T).
|
|
:- pred get_bindings(varset(T)::in, substitution(T)::out) is det.
|
|
|
|
% Set the bindings for all the bound variables.
|
|
%
|
|
:- func set_bindings(varset(T), substitution(T)) = varset(T).
|
|
:- pred set_bindings(substitution(T)::in,
|
|
varset(T)::in, varset(T)::out) is det.
|
|
|
|
%---------------------%
|
|
|
|
% Combine two different varsets, renaming apart:
|
|
% merge_renaming(VarSet0, NewVarSet, VarSet, Renaming) is true
|
|
% iff VarSet is the varset that results from joining a suitably renamed
|
|
% version of NewVarSet to VarSet0. (Any bindings in NewVarSet are ignored.)
|
|
% Renaming will map each variable in NewVarSet to the corresponding
|
|
% fresh variable in VarSet.
|
|
%
|
|
:- pred merge_renaming(varset(T)::in, varset(T)::in,
|
|
varset(T)::out, renaming(T)::out) is det.
|
|
|
|
% Same as merge_renaming, except that the names of variables
|
|
% in NewVarSet are not included in the final varset.
|
|
% This is useful if create_name_var_map needs to be used
|
|
% on the resulting varset.
|
|
%
|
|
:- pred merge_renaming_without_names(varset(T)::in, varset(T)::in,
|
|
varset(T)::out, renaming(T)::out) is det.
|
|
|
|
% merge(VarSet0, NewVarSet, Terms0, VarSet, Terms):
|
|
%
|
|
% As merge_renaming, except instead of returning the renaming,
|
|
% this predicate applies it to the given list of terms.
|
|
%
|
|
:- pred merge(varset(T)::in, varset(T)::in, list(term(T))::in,
|
|
varset(T)::out, list(term(T))::out) is det.
|
|
|
|
% Same as merge, except that the names of variables
|
|
% in NewVarSet are not included in the final varset.
|
|
% This is useful if create_name_var_map needs to be used
|
|
% on the resulting varset.
|
|
%
|
|
:- pred merge_without_names(varset(T)::in, varset(T)::in, list(term(T))::in,
|
|
varset(T)::out, list(term(T))::out) is det.
|
|
|
|
%---------------------%
|
|
|
|
% Create a map from names to variables.
|
|
% Each name is mapped to only one variable, even if a name is
|
|
% shared by more than one variable. Therefore this predicate
|
|
% is only really useful if it is already known that no two
|
|
% variables share the same name.
|
|
%
|
|
:- func create_name_var_map(varset(T)) = map(string, var(T)).
|
|
:- pred create_name_var_map(varset(T)::in, map(string, var(T))::out)
|
|
is det.
|
|
|
|
% Return an association list giving the name of each variable.
|
|
% Every variable has an entry in the returned association list,
|
|
% even if it shares its name with another variable.
|
|
%
|
|
:- func var_name_list(varset(T)) = assoc_list(var(T), string).
|
|
:- pred var_name_list(varset(T)::in, assoc_list(var(T), string)::out)
|
|
is det.
|
|
|
|
% Given a list of variable and varset in which some variables have
|
|
% no name but some other variables may have the same name,
|
|
% return another varset in which every variable has a unique name.
|
|
% If necessary, names will have suffixes added on the end;
|
|
% the second argument gives the suffix to use.
|
|
%
|
|
:- func ensure_unique_names(list(var(T)), string, varset(T)) = varset(T).
|
|
:- pred ensure_unique_names(list(var(T))::in, string::in,
|
|
varset(T)::in, varset(T)::out) is det.
|
|
|
|
% Unname all variables whose explicitly given names have the form
|
|
% of the default names used by lookup_name, i.e. "V_" followed by
|
|
% an integer.
|
|
%
|
|
% This predicate is intended to be used in situations where
|
|
% a term has been read in after being written out. The process of
|
|
% writing out the term forces every variable to given a name
|
|
% that can be written out, even variables that until then did not
|
|
% have names. If these variables are given names of the default form, then,
|
|
% after the written-out term is read back in, this predicate will recreate
|
|
% the original varset, including the variables without names.
|
|
%
|
|
:- pred undo_default_names(varset(T)::in, varset(T)::out) is det.
|
|
|
|
%---------------------%
|
|
|
|
% Given a varset and a set of variables, remove the names
|
|
% and values of any other variables stored in the varset.
|
|
%
|
|
:- func select(varset(T), set(var(T))) = varset(T).
|
|
:- pred select(set(var(T))::in, varset(T)::in, varset(T)::out) is det.
|
|
|
|
% Given a varset and a list of variables, construct a new varset
|
|
% containing one variable for each one in the list, and no others.
|
|
% Also return a substitution mapping the selected variables in the
|
|
% original varset into variables in the new varset. The relative
|
|
% ordering of variables in the original varset is maintained.
|
|
%
|
|
:- pred squash(varset(T)::in, list(var(T))::in,
|
|
varset(T)::out, renaming(T)::out) is det.
|
|
|
|
% Coerce the types of the variables in a varset.
|
|
%
|
|
:- func coerce(varset(T)) = varset(U).
|
|
:- pred coerce(varset(T)::in, varset(U)::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
% Everything below here is not intended to be part of the public interface,
|
|
% and will not be included in the Mercury library reference manual.
|
|
|
|
:- interface.
|
|
|
|
% Returns the highest numbered variable returned from this varset's
|
|
% var_supply.
|
|
%
|
|
:- func max_var(varset(T)) = var(T).
|
|
|
|
:- func num_allocated(varset(T)) = int.
|
|
|
|
:- pred get_var_names(varset(T)::in, map(var(T), string)::out) is det.
|
|
|
|
:- pred construct_varset(int::in, map(var(T), string)::in,
|
|
varset(T)::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module int.
|
|
:- import_module pair.
|
|
:- import_module require.
|
|
:- import_module string.
|
|
:- import_module term_subst.
|
|
|
|
:- type varset(T)
|
|
---> varset(
|
|
var_supply :: var_supply(T),
|
|
var_names :: map(var(T), string),
|
|
var_values :: map(var(T), term(T))
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
init = VarSet :-
|
|
varset.init(VarSet).
|
|
|
|
init(VarSet) :-
|
|
term.init_var_supply(VarSupply),
|
|
map.init(Names),
|
|
map.init(Values),
|
|
VarSet = varset(VarSupply, Names, Values).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
is_empty(varset(VarSupply, _, _)) :-
|
|
term.init_var_supply(VarSupply).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
new_var(Var, !VarSet) :-
|
|
MaxId0 = !.VarSet ^ var_supply,
|
|
term.create_var(Var, MaxId0, MaxId),
|
|
!VarSet ^ var_supply := MaxId.
|
|
|
|
new_named_var(Name, Var, !VarSet) :-
|
|
!.VarSet = varset(MaxId0, Names0, Values),
|
|
term.create_var(Var, MaxId0, MaxId),
|
|
map.set(Var, Name, Names0, Names),
|
|
!:VarSet = varset(MaxId, Names, Values).
|
|
|
|
new_uniquely_named_var(Name, Var, !VarSet) :-
|
|
!.VarSet = varset(MaxId0, Names0, Values),
|
|
term.create_var(Var, MaxId0, MaxId),
|
|
N = term.var_to_int(Var),
|
|
map.set(Var, string.format("%s_%d", [s(Name), i(N)]), Names0, Names),
|
|
!:VarSet = varset(MaxId, Names, Values).
|
|
|
|
new_maybe_named_var(MaybeName, Var, !VarSet) :-
|
|
!.VarSet = varset(MaxId0, Names0, Values),
|
|
term.create_var(Var, MaxId0, MaxId),
|
|
(
|
|
MaybeName = no,
|
|
Names = Names0
|
|
;
|
|
MaybeName = yes(Name),
|
|
map.set(Var, Name, Names0, Names)
|
|
),
|
|
!:VarSet = varset(MaxId, Names, Values).
|
|
|
|
new_vars(NumVars, NewVars, !VarSet) :-
|
|
new_vars_loop(NumVars, [], RevNewVars, !VarSet),
|
|
% Return the new variables in order.
|
|
list.reverse(RevNewVars, NewVars).
|
|
|
|
:- pred new_vars_loop(int::in, list(var(T))::in, list(var(T))::out,
|
|
varset(T)::in, varset(T)::out) is det.
|
|
|
|
new_vars_loop(NumVars, !RevNewVars, !VarSet) :-
|
|
( if NumVars > 0 then
|
|
varset.new_var(Var, !VarSet),
|
|
!:RevNewVars = [Var | !.RevNewVars],
|
|
varset.new_vars_loop(NumVars - 1, !RevNewVars, !VarSet)
|
|
else if NumVars = 0 then
|
|
true
|
|
else
|
|
unexpected($pred, "invalid call")
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
delete_var(!.VarSet, DeleteVar) = !:VarSet :-
|
|
varset.delete_var(DeleteVar, !VarSet).
|
|
|
|
delete_var(DeleteVar, !VarSet) :-
|
|
!.VarSet = varset(MaxId, Names0, Values0),
|
|
map.delete(DeleteVar, Names0, Names),
|
|
map.delete(DeleteVar, Values0, Values),
|
|
!:VarSet = varset(MaxId, Names, Values).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
delete_vars(!.VarSet, DeleteVars) = !:VarSet :-
|
|
varset.delete_vars(DeleteVars, !VarSet).
|
|
|
|
delete_vars(DeleteVars, !VarSet) :-
|
|
!.VarSet = varset(MaxId, Names0, Values0),
|
|
map.delete_list(DeleteVars, Names0, Names),
|
|
map.delete_list(DeleteVars, Values0, Values),
|
|
!:VarSet = varset(MaxId, Names, Values).
|
|
|
|
delete_sorted_vars(!.VarSet, DeleteVars) = !:VarSet :-
|
|
varset.delete_sorted_vars(DeleteVars, !VarSet).
|
|
|
|
delete_sorted_vars(DeleteVars, !VarSet) :-
|
|
!.VarSet = varset(MaxId, Names0, Values0),
|
|
map.delete_sorted_list(DeleteVars, Names0, Names),
|
|
map.delete_sorted_list(DeleteVars, Values0, Values),
|
|
!:VarSet = varset(MaxId, Names, Values).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
vars(VarSet) = Vars :-
|
|
varset.vars(VarSet, Vars).
|
|
|
|
vars(VarSet, Vars) :-
|
|
MaxId = VarSet ^ var_supply,
|
|
term.init_var_supply(N0),
|
|
vars_loop(N0, MaxId, [], RevVars),
|
|
list.reverse(RevVars, Vars).
|
|
|
|
:- pred vars_loop(var_supply(T)::in, var_supply(T)::in,
|
|
list(var(T))::in, list(var(T))::out) is det.
|
|
|
|
vars_loop(Cur, Max, !RevVars) :-
|
|
( if Cur = Max then
|
|
true
|
|
else
|
|
term.create_var(Var, Cur, Next),
|
|
!:RevVars = [Var | !.RevVars],
|
|
vars_loop(Next, Max, !RevVars)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
name_var(!.VarSet, Var, Name) = !:VarSet :-
|
|
varset.name_var(Var, Name, !VarSet).
|
|
|
|
name_var(Var, Name, !VarSet) :-
|
|
Names0 = !.VarSet ^ var_names,
|
|
map.set(Var, Name, Names0, Names),
|
|
!VarSet ^ var_names := Names.
|
|
|
|
unname_var(!.VarSet, Var) = !:VarSet :-
|
|
varset.unname_var(Var, !VarSet).
|
|
|
|
unname_var(Var, !VarSet) :-
|
|
Names0 = !.VarSet ^ var_names,
|
|
map.delete(Var, Names0, Names),
|
|
!VarSet ^ var_names := Names.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
lookup_name(VarSet, Var) = Name :-
|
|
varset.lookup_name(VarSet, Var, Name).
|
|
|
|
lookup_name(VarSet, Var, Name) :-
|
|
( if varset.search_name(VarSet, Var, NamePrime) then
|
|
Name = NamePrime
|
|
else
|
|
term.var_to_int(Var, VarNum),
|
|
Name = "V_" ++ string.int_to_string(VarNum)
|
|
).
|
|
|
|
lookup_name(VarSet, Id, Prefix) = Name :-
|
|
lookup_name_default_prefix(VarSet, Id, Prefix, Name).
|
|
|
|
lookup_name(VarSet, Id, Prefix, Name) :-
|
|
lookup_name_default_prefix(VarSet, Id, Prefix, Name).
|
|
|
|
lookup_name_default_prefix(VarSet, Id, Prefix) = Name :-
|
|
varset.lookup_name_default_prefix(VarSet, Id, Prefix, Name).
|
|
|
|
lookup_name_default_prefix(VarSet, Id, Prefix, Name) :-
|
|
( if varset.search_name(VarSet, Id, NamePrime) then
|
|
Name = NamePrime
|
|
else
|
|
term.var_to_int(Id, VarNum),
|
|
Name = Prefix ++ string.int_to_string(VarNum)
|
|
).
|
|
|
|
search_name(VarSet, Var, Name) :-
|
|
VarSet = varset(_, Names, _),
|
|
map.search(Names, Var, Name).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
bind_var(!.VarSet, Var, Value) = !:VarSet :-
|
|
varset.bind_var(Var, Value, !VarSet).
|
|
|
|
bind_var(Var, Value, !VarSet) :-
|
|
Values0 = !.VarSet ^ var_values,
|
|
map.set(Var, Value, Values0, Values),
|
|
!VarSet ^ var_values := Values.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
bind_vars(!.VarSet, Subst) = !:VarSet :-
|
|
varset.bind_vars(Subst, !VarSet).
|
|
|
|
bind_vars(Subst, !VarSet) :-
|
|
map.to_assoc_list(Subst, VarsValues),
|
|
bind_vars_loop(VarsValues, !VarSet).
|
|
|
|
:- pred bind_vars_loop(assoc_list(var(T), term(T))::in, varset(T)::in,
|
|
varset(T)::out) is det.
|
|
|
|
bind_vars_loop([], !VarSet).
|
|
bind_vars_loop([Var - Value | VarsValues], !VarSet) :-
|
|
bind_var(Var, Value, !VarSet),
|
|
bind_vars_loop(VarsValues, !VarSet).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
search_var(VarSet, Var, Value) :-
|
|
Values = VarSet ^ var_values,
|
|
map.search(Values, Var, Value).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
lookup_vars(VarSet) = Values :-
|
|
varset.lookup_vars(VarSet, Values).
|
|
|
|
lookup_vars(VarSet, Values) :-
|
|
Values = VarSet ^ var_values.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
get_bindings(VarSet) = Values :-
|
|
varset.get_bindings(VarSet, Values).
|
|
|
|
get_bindings(VarSet, Values) :-
|
|
Values = VarSet ^ var_values.
|
|
|
|
set_bindings(!.VarSet, Values) = !:VarSet :-
|
|
varset.set_bindings(Values, !VarSet).
|
|
|
|
set_bindings(Values, !VarSet) :-
|
|
!VarSet ^ var_values := Values.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% We have two near-identical copies of code implementing two related
|
|
% but nevertheless distinct exported predicates:
|
|
%
|
|
% - merge_renaming
|
|
% - merge_renaming_without_names
|
|
%
|
|
% The code of the two must be distinct from each other if we don't want
|
|
% to pay the cost of passing around name maps unnecessarily.
|
|
%
|
|
% The approach we follow in the simple implementation of each of the above
|
|
% exported predicates is that we scan through the second varset, introducing
|
|
% a fresh variable into the first varset for each var in the second,
|
|
% and building up a renaming which maps the variables in the second varset
|
|
% into the corresponding fresh variable in the first varset.
|
|
%
|
|
% The fast implementation each of the above exported predicates does the
|
|
% same conceptual job, but with differences in detail.
|
|
%
|
|
% 1 Instead of inserting oldvar-newvar pairs into the rename map,
|
|
% it constructs a sorted flat assoc list to be turned into a map.
|
|
% If the two varsets have M and N vars respectively, then the simple
|
|
% algorithm is O(N log N), while the fast one is O(N).
|
|
%
|
|
% 2 Instead of looking up the name of each variable in the second varset
|
|
% in a map, it flattens the second varset's name map and iterates
|
|
% over the resulting list of var-name pairs. Again, the simple algorithm
|
|
% is O(N log N), while the fast one is O(N).
|
|
%
|
|
% 3 Instead of inserting newvar-name pairs into the updated name map,
|
|
% it constructs a sorted flat assoc list for the new entries.
|
|
% Constructing the updated name map requires flattening the old name map,
|
|
% appending the new entries to it, and turning the result into a map.
|
|
% The simple algorithm is O(N log (M+N)), since it does N insertions
|
|
% into a map whose final size is O(M+N), while the fast algorithm is
|
|
% O(M + N). This *may* yield a slowdown if M is much bigger than N.
|
|
%
|
|
% As its name says, merge_renaming_without_names does not update the name map,
|
|
% so the slowdown mentioned in point 3 cannot occur for them. This is why
|
|
% it has a separate heuristic.
|
|
%
|
|
|
|
:- pred use_simple_loop(int::in, int::in) is semidet.
|
|
:- pragma inline(pred(use_simple_loop/2)).
|
|
|
|
use_simple_loop(NumAllocatedA, NumAllocatedB) :-
|
|
% XXX The numbers in this heuristic should to be determined by benchmarks.
|
|
(
|
|
% If NumAllocatedB (the N in the complexity analysis above)
|
|
% is small, then the asymptotic complexity does not matter as much
|
|
% as the constant factors.
|
|
NumAllocatedB =< 5
|
|
;
|
|
% Avoid flattening NamesA if it does not buy us enough speed on NamesB.
|
|
NumAllocatedA >= NumAllocatedB * 16
|
|
).
|
|
|
|
:- pred use_simple_loop_without_names(int::in, int::in) is semidet.
|
|
:- pragma inline(pred(use_simple_loop_without_names/2)).
|
|
|
|
use_simple_loop_without_names(_NumAllocatedA, NumAllocatedB) :-
|
|
% XXX The numbers in this heuristic should to be determined by benchmarks.
|
|
% If NumAllocatedB (the N in the complexity analysis above)
|
|
% is small, then the asymptotic complexity does not matter as much
|
|
% as the constant factors.
|
|
NumAllocatedB =< 5.
|
|
|
|
%---------------------%
|
|
|
|
merge_renaming(VarSetA, VarSetB, VarSet, Renaming) :-
|
|
VarSetA = varset(SupplyA, NamesA, ValuesA),
|
|
VarSetB = varset(SupplyB, NamesB, _ValuesB),
|
|
NumAllocatedA = var_supply_num_allocated(SupplyA),
|
|
NumAllocatedB = var_supply_num_allocated(SupplyB),
|
|
( if use_simple_loop(NumAllocatedA, NumAllocatedB) then
|
|
term.init_var_supply(SupplyB0),
|
|
map.init(Renaming0),
|
|
simple_merge_renaming_loop(SupplyB0, SupplyB, NamesB,
|
|
SupplyA, Supply, NamesA, Names, Renaming0, Renaming)
|
|
else
|
|
map.to_sorted_assoc_list(NamesA, NamesListA),
|
|
map.to_sorted_assoc_list(NamesB, NamesListB),
|
|
fast_merge_renaming_loop(1, NumAllocatedB, NumAllocatedA, NumAllocated,
|
|
NamesListB, [], RevNamesListSuffix, [], RevRenamingList),
|
|
Supply = force_construct_var_supply(NumAllocated),
|
|
list.reverse(RevNamesListSuffix, NamesListSuffix),
|
|
NamesList = NamesListA ++ NamesListSuffix,
|
|
map.from_sorted_assoc_list(NamesList, Names),
|
|
map.from_rev_sorted_assoc_list(RevRenamingList, Renaming)
|
|
),
|
|
VarSet = varset(Supply, Names, ValuesA).
|
|
|
|
:- pred simple_merge_renaming_loop(var_supply(T)::in, var_supply(T)::in,
|
|
map(var(T), string)::in,
|
|
var_supply(T)::in, var_supply(T)::out,
|
|
map(var(T), string)::in, map(var(T), string)::out,
|
|
renaming(T)::in, renaming(T)::out) is det.
|
|
|
|
simple_merge_renaming_loop(!.SupplyB, MaxSupplyB, NamesB,
|
|
!Supply, !Names, !Renaming) :-
|
|
( if !.SupplyB = MaxSupplyB then
|
|
true
|
|
else
|
|
term.create_var(Var, !Supply),
|
|
term.create_var(VarB, !SupplyB),
|
|
( if map.search(NamesB, VarB, NameB) then
|
|
map.det_insert(Var, NameB, !Names)
|
|
else
|
|
true
|
|
),
|
|
map.det_insert(VarB, Var, !Renaming),
|
|
disable_warning [suspicious_recursion] (
|
|
simple_merge_renaming_loop(!.SupplyB, MaxSupplyB, NamesB,
|
|
!Supply, !Names, !Renaming)
|
|
)
|
|
).
|
|
|
|
:- pred fast_merge_renaming_loop(int::in, int::in, int::in, int::out,
|
|
assoc_list(var(T), string)::in,
|
|
assoc_list(var(T), string)::in, assoc_list(var(T), string)::out,
|
|
assoc_list(var(T), var(T))::in, assoc_list(var(T), var(T))::out) is det.
|
|
|
|
fast_merge_renaming_loop(CurVarNumB, NumAllocatedB, !NumAllocated,
|
|
VarNamesB, !RevNamesListSuffix, !RevRenaming) :-
|
|
(
|
|
VarNamesB = [],
|
|
fast_merge_renaming_loop_leftover(CurVarNumB, NumAllocatedB,
|
|
!NumAllocated, !RevRenaming)
|
|
;
|
|
VarNamesB = [HeadVarB - HeadNameB | TailVarNamesB],
|
|
!:NumAllocated = !.NumAllocated + 1,
|
|
Var = force_construct_var(!.NumAllocated),
|
|
VarB = force_construct_var(CurVarNumB),
|
|
!:RevRenaming = [VarB - Var | !.RevRenaming],
|
|
( if CurVarNumB = var_to_int(HeadVarB) then
|
|
!:RevNamesListSuffix = [Var - HeadNameB | !.RevNamesListSuffix],
|
|
NextVarNamesB = TailVarNamesB
|
|
else
|
|
NextVarNamesB = VarNamesB
|
|
),
|
|
fast_merge_renaming_loop(CurVarNumB + 1, NumAllocatedB, !NumAllocated,
|
|
NextVarNamesB, !RevNamesListSuffix, !RevRenaming)
|
|
).
|
|
|
|
% A version of fast_merge_renaming_loop specialized for the case
|
|
% where there are no names left from VarSetB.
|
|
%
|
|
:- pred fast_merge_renaming_loop_leftover(int::in, int::in, int::in, int::out,
|
|
assoc_list(var(T), var(T))::in, assoc_list(var(T), var(T))::out) is det.
|
|
|
|
fast_merge_renaming_loop_leftover(CurVarNumB, NumAllocatedB,
|
|
!NumAllocated, !RevRenaming) :-
|
|
( if CurVarNumB > NumAllocatedB then
|
|
true
|
|
else
|
|
!:NumAllocated = !.NumAllocated + 1,
|
|
Var = force_construct_var(!.NumAllocated),
|
|
VarB = force_construct_var(CurVarNumB),
|
|
!:RevRenaming = [VarB - Var | !.RevRenaming],
|
|
fast_merge_renaming_loop_leftover(CurVarNumB + 1, NumAllocatedB,
|
|
!NumAllocated, !RevRenaming)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
merge_renaming_without_names(VarSetA, VarSetB, VarSet, Renaming) :-
|
|
VarSetA = varset(SupplyA, NamesA, ValuesA),
|
|
VarSetB = varset(SupplyB, _NamesB, _ValuesB),
|
|
NumAllocatedA = var_supply_num_allocated(SupplyA),
|
|
NumAllocatedB = var_supply_num_allocated(SupplyB),
|
|
( if use_simple_loop_without_names(NumAllocatedA, NumAllocatedB) then
|
|
term.init_var_supply(SupplyB0),
|
|
map.init(Renaming0),
|
|
simple_merge_renaming_without_names_loop(SupplyB0, SupplyB,
|
|
SupplyA, Supply, Renaming0, Renaming)
|
|
else
|
|
fast_merge_renaming_without_names_loop(1, NumAllocatedB,
|
|
NumAllocatedA, NumAllocated, [], RevRenamingList),
|
|
Supply = force_construct_var_supply(NumAllocated),
|
|
map.from_rev_sorted_assoc_list(RevRenamingList, Renaming)
|
|
),
|
|
VarSet = varset(Supply, NamesA, ValuesA).
|
|
|
|
:- pred simple_merge_renaming_without_names_loop(var_supply(T)::in,
|
|
var_supply(T)::in, var_supply(T)::in, var_supply(T)::out,
|
|
renaming(T)::in, renaming(T)::out) is det.
|
|
|
|
simple_merge_renaming_without_names_loop(!.SupplyB, MaxSupplyB,
|
|
!Supply, !Renaming) :-
|
|
( if !.SupplyB = MaxSupplyB then
|
|
true
|
|
else
|
|
term.create_var(Var, !Supply),
|
|
term.create_var(VarB, !SupplyB),
|
|
map.det_insert(VarB, Var, !Renaming),
|
|
disable_warning [suspicious_recursion] (
|
|
simple_merge_renaming_without_names_loop(!.SupplyB, MaxSupplyB,
|
|
!Supply, !Renaming)
|
|
)
|
|
).
|
|
|
|
:- pred fast_merge_renaming_without_names_loop(int::in, int::in,
|
|
int::in, int::out,
|
|
assoc_list(var(T), var(T))::in, assoc_list(var(T), var(T))::out) is det.
|
|
|
|
fast_merge_renaming_without_names_loop(CurVarNumB, NumAllocatedB,
|
|
!NumAllocated, !RevRenaming) :-
|
|
( if CurVarNumB > NumAllocatedB then
|
|
true
|
|
else
|
|
!:NumAllocated = !.NumAllocated + 1,
|
|
Var = force_construct_var(!.NumAllocated),
|
|
VarB = force_construct_var(CurVarNumB),
|
|
!:RevRenaming = [VarB - Var | !.RevRenaming],
|
|
fast_merge_renaming_without_names_loop(CurVarNumB + 1, NumAllocatedB,
|
|
!NumAllocated, !RevRenaming)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
merge(VarSetA, VarSetB, TermList0, VarSet, TermList) :-
|
|
varset.merge_renaming(VarSetA, VarSetB, VarSet, Renaming),
|
|
term_subst.apply_renaming_in_terms(Renaming, TermList0, TermList).
|
|
|
|
merge_without_names(VarSetA, VarSetB, TermList0, VarSet, TermList) :-
|
|
varset.merge_renaming_without_names(VarSetA, VarSetB, VarSet, Renaming),
|
|
term_subst.apply_renaming_in_terms(Renaming, TermList0, TermList).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
create_name_var_map(VarSet) = NameVars :-
|
|
varset.create_name_var_map(VarSet, NameVars).
|
|
|
|
create_name_var_map(VarSet, NameVars) :-
|
|
VarNames = VarSet ^ var_names,
|
|
map.keys(VarNames, Vars),
|
|
map.values(VarNames, Names),
|
|
map.from_corresponding_lists(Names, Vars, NameVars).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
var_name_list(VarSet) = VarNameList :-
|
|
varset.var_name_list(VarSet, VarNameList).
|
|
|
|
var_name_list(VarSet, VarNameList) :-
|
|
VarNames = VarSet ^ var_names,
|
|
map.to_assoc_list(VarNames, VarNameList).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
ensure_unique_names(AllVars, Suffix, !.VarSet) = !:VarSet :-
|
|
varset.ensure_unique_names(AllVars, Suffix, !VarSet).
|
|
|
|
ensure_unique_names(AllVars, Suffix, !VarSet) :-
|
|
VarNames0 = !.VarSet ^ var_names,
|
|
varset.ensure_unique_names_loop(AllVars, Suffix, set.init, VarNames0,
|
|
map.init, VarNames),
|
|
!VarSet ^ var_names := VarNames.
|
|
|
|
:- pred ensure_unique_names_loop(list(var(T))::in, string::in,
|
|
set(string)::in, map(var(T), string)::in, map(var(T), string)::in,
|
|
map(var(T), string)::out) is det.
|
|
|
|
ensure_unique_names_loop([], _, _, _, !VarNames).
|
|
ensure_unique_names_loop([Var | Vars], Suffix, !.UsedNames,
|
|
OldVarNames, !VarNames) :-
|
|
( if map.search(OldVarNames, Var, OldName) then
|
|
( if set.member(OldName, !.UsedNames) then
|
|
term.var_to_int(Var, VarNum),
|
|
TrialName = OldName ++ "_" ++ string.int_to_string(VarNum)
|
|
else
|
|
TrialName = OldName
|
|
)
|
|
else
|
|
term.var_to_int(Var, VarNum),
|
|
TrialName = "Var_" ++ string.int_to_string(VarNum)
|
|
),
|
|
append_suffix_until_unique(TrialName, Suffix, !.UsedNames, FinalName),
|
|
set.insert(FinalName, !UsedNames),
|
|
map.det_insert(Var, FinalName, !VarNames),
|
|
ensure_unique_names_loop(Vars, Suffix, !.UsedNames,
|
|
OldVarNames, !VarNames).
|
|
|
|
:- pred append_suffix_until_unique(string::in, string::in, set(string)::in,
|
|
string::out) is det.
|
|
|
|
append_suffix_until_unique(Trial0, Suffix, UsedNames, Final) :-
|
|
( if set.member(Trial0, UsedNames) then
|
|
string.append(Trial0, Suffix, Trial1),
|
|
append_suffix_until_unique(Trial1, Suffix, UsedNames, Final)
|
|
else
|
|
Final = Trial0
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
undo_default_names(VarSet0, VarSet) :-
|
|
VarSet0 = varset(Supply0, Names0, Values0),
|
|
map.to_sorted_assoc_list(Names0, NamesList0),
|
|
undo_default_names_loop(NamesList0, [], RevNamesList),
|
|
map.from_rev_sorted_assoc_list(RevNamesList, Names),
|
|
VarSet = varset(Supply0, Names, Values0).
|
|
|
|
:- pred undo_default_names_loop(assoc_list(var(T), string)::in,
|
|
assoc_list(var(T), string)::in, assoc_list(var(T), string)::out) is det.
|
|
|
|
undo_default_names_loop([], !RevNamesList).
|
|
undo_default_names_loop([Var - Name | VarNames], !RevNamesList) :-
|
|
( if
|
|
% Does the variable name have the default form used by lookup_name?
|
|
remove_prefix("V_", Name, NameAfterV),
|
|
string.to_int(NameAfterV, _IntAfterV)
|
|
then
|
|
% Yes, so delete the name by simply not adding it to the list.
|
|
true
|
|
else
|
|
!:RevNamesList = [Var - Name | !.RevNamesList]
|
|
),
|
|
undo_default_names_loop(VarNames, !RevNamesList).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
select(!.VarSet, Vars) = !:VarSet :-
|
|
varset.select(Vars, !VarSet).
|
|
|
|
select(Vars, !VarSet) :-
|
|
!.VarSet = varset(Supply, VarNameMap0, Values0),
|
|
map.select(VarNameMap0, Vars, VarNameMap),
|
|
map.select(Values0, Vars, Values),
|
|
!:VarSet = varset(Supply, VarNameMap, Values).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
squash(OldVarSet, KeptVars, NewVarSet, Subst) :-
|
|
% Create a new varset with the same number of variables.
|
|
list.length(KeptVars, NumVars),
|
|
varset.init(NewVarSet0),
|
|
varset.new_vars(NumVars, NewVars0, NewVarSet0, NewVarSet1),
|
|
|
|
% We need to sort the fresh variables, to ensure that the substitution
|
|
% that we create below does not alter the relative ordering of the
|
|
% variables.
|
|
list.sort(NewVars0, NewVars),
|
|
|
|
% Copy the variable names across from the old varset to the new varset.
|
|
varset.var_name_list(OldVarSet, VarNames),
|
|
map.from_corresponding_lists(KeptVars, NewVars, Subst),
|
|
copy_var_names(VarNames, Subst, NewVarSet1, NewVarSet).
|
|
|
|
:- pred copy_var_names(assoc_list(var(T), string)::in, renaming(T)::in,
|
|
varset(T)::in, varset(T)::out) is det.
|
|
|
|
copy_var_names([], _Subst, !NewVarSet).
|
|
copy_var_names([OldVar - Name | Rest], Subst, !NewVarSet) :-
|
|
( if map.search(Subst, OldVar, NewVar) then
|
|
varset.name_var(NewVar, Name, !NewVarSet)
|
|
else
|
|
true
|
|
),
|
|
copy_var_names(Rest, Subst, !NewVarSet).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
coerce(!.VarSet) = !:VarSet :-
|
|
varset.coerce(!VarSet).
|
|
|
|
coerce(!VarSet) :-
|
|
% Normally calls to this predicate should only be generated by the
|
|
% compiler, but type coercion by copying was taking about 3% of the
|
|
% compiler's runtime.
|
|
private_builtin.unsafe_type_cast(!VarSet).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
max_var(varset(VarSupply, _, _)) = term.var_supply_max_var(VarSupply).
|
|
|
|
num_allocated(varset(VarSupply, _, _)) =
|
|
term.var_supply_num_allocated(VarSupply).
|
|
|
|
get_var_names(VarSet, VarNameMap) :-
|
|
VarSet = varset(_, VarNameMap, _).
|
|
|
|
construct_varset(N, VarNameMap, VarSet) :-
|
|
VarSupply = force_construct_var_supply(N),
|
|
map.init(VarValueMap),
|
|
VarSet = varset(VarSupply, VarNameMap, VarValueMap).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module varset.
|
|
%---------------------------------------------------------------------------%
|