mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 09:23:44 +00:00
783 lines
29 KiB
Mathematica
783 lines
29 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2011-2012 The University of Melbourne.
|
|
% Copyright (C) 2014-2015, 2017-2018, 2022-2025 The Mercury team.
|
|
% This file is distributed under the terms specified in COPYING.LIB.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: goal_path.m
|
|
% Authors: zs, pbone
|
|
%
|
|
% We can think of the Mercury goal that defines a procedure as a tree, where
|
|
%
|
|
% - the leaves are primitive goals, and
|
|
% - the interior nodes are compound goals.
|
|
%
|
|
% The goal_id, forward_goal_path and reverse_goal_path types that this module
|
|
% defines all describe the position of a goal in this tree. Therefore values
|
|
% of these three types can uniquely identify a goal within its defining
|
|
% procedure.
|
|
%
|
|
% Goal ids are allocated in a depth-first manner that guarantees the following
|
|
% invariants:
|
|
%
|
|
% - the goal id of a goal representing the procedure body will be 0, and
|
|
% - the goal id of a goal will be greater than the goal ids of all the goals
|
|
% that contain it.
|
|
%
|
|
% A value of the goal_path_step type says which branch to take at an
|
|
% interior node, such as which conjunct of a conjunction we want to select.
|
|
%
|
|
% A forward goal path lists the steps from the root of the tree to the goal
|
|
% being identified. (These are the kinds of goal paths you can see
|
|
% on mdb events, for example.)
|
|
%
|
|
% A reverse goal path lists the steps from to the goal being identified
|
|
% back to the root of the tree.
|
|
%
|
|
% HISTORY
|
|
%
|
|
% Of these three types, the first one that the compiler used is forward goal
|
|
% paths. It used these for purposes such as identifying which subgoal
|
|
% of a procedure body an mdb event occurred at. However, the code constructing
|
|
% these forward goal paths to put into the static data structures used by mdb
|
|
% operated on *reversed* lists of goal_path_steps in order to avoid the O(N^2)
|
|
% complexity of repeatedly appending new steps to the end of a list.
|
|
% Replacing the list(goal_path_step) type with separate forward_goal_path and
|
|
% reverse_goal_path types made understanding the code doing that significantly
|
|
% easier.
|
|
%
|
|
% Later on, other compiler passes also needed ways to uniquely identify
|
|
% each subgoal in a procedure body. Using goal paths for this works,
|
|
% but it is overkill, and therefore adds unnecessary overhead. This is
|
|
% why we added goal_ids. A map whose keys are goal_ids can be expected to be
|
|
% *much* faster than a map whose keys are (forward or reverse) goal paths,
|
|
% because comparing two integers is much less work than comparing two nested
|
|
% (possibly deeply nested) structured terms.
|
|
%
|
|
% Having two separate slots in a hlds_goal_info for a goal_id and a goal path
|
|
% would also be wasteful. This is why hlds_goal_infos have only one slot,
|
|
% containing a goal_id. Nevertheless, code that wants to do so can convert
|
|
% this goal_id into a goal path (of either kind), because the code in the
|
|
% compiler that allocates goal ids also returns a map, the containing goal
|
|
% map, to make this possible. This data structure maps each goal id to the id
|
|
% of its innermost containing goal (if there is one), and the step from
|
|
% that containing goal to this one.
|
|
%
|
|
% THE FUTURE
|
|
%
|
|
% There are some compiler modules that use goal_paths to identify subgoals,
|
|
% because goal_ids did not yet exist when they were written. Later modules
|
|
% use goal_ids for that purpose, and any new code should follow their example.
|
|
% Ideally, the use of goal paths in the future should be restricted to
|
|
% use cases where we want to communicate the identity of a goal to a human,
|
|
% who may be using e.g. mdb or mdprof.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module mdbcomp.goal_path.
|
|
:- interface.
|
|
|
|
:- import_module array.
|
|
:- import_module bimap.
|
|
:- import_module char.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type forward_goal_path
|
|
---> fgp_nil
|
|
; fgp_cons(goal_path_step, forward_goal_path).
|
|
|
|
:- type reverse_goal_path
|
|
---> rgp_nil
|
|
; rgp_cons(reverse_goal_path, goal_path_step).
|
|
|
|
:- type goal_path_string == string.
|
|
|
|
% The integers in these steps start counting at one.
|
|
:- type goal_path_step
|
|
---> step_conj(int) % which conjunct of a conjunction
|
|
; step_disj(int) % which disjunct of a disjunction
|
|
; step_switch(int, maybe_switch_num_functors)
|
|
; step_ite_cond
|
|
; step_ite_then
|
|
; step_ite_else
|
|
; step_neg
|
|
; step_scope(maybe_cut)
|
|
; step_lambda
|
|
; step_try
|
|
; step_atomic_main
|
|
; step_atomic_orelse(int).
|
|
|
|
% For switches on values of discriminated union types, this will be
|
|
% known_num_functors_in_type(N), where N is the number of function symbols
|
|
% in the type of the variable being switched.
|
|
%
|
|
% For switches on builtin types such as integer and string, for which
|
|
% the number of function symbols in the type is effectively infinite,
|
|
% this will be unknown_num_functors_in_type.
|
|
%
|
|
:- type maybe_switch_num_functors
|
|
---> unknown_num_functors_in_type
|
|
; known_num_functors_in_type(int).
|
|
|
|
% Does the scope goal have a different determinism inside than outside?
|
|
:- type maybe_cut
|
|
---> scope_is_cut
|
|
; scope_is_no_cut.
|
|
|
|
%---------------------%
|
|
|
|
% Append a goal path step onto the end of a goal path.
|
|
%
|
|
:- func goal_path_add_at_end(forward_goal_path, goal_path_step) =
|
|
forward_goal_path.
|
|
|
|
% Append a goal path step onto the end of a reverse goal path.
|
|
%
|
|
:- func rev_goal_path_add_at_end(reverse_goal_path, goal_path_step) =
|
|
reverse_goal_path.
|
|
|
|
% Remove the last item from the goal path, returning it and the new
|
|
% goal path.
|
|
%
|
|
:- pred goal_path_remove_last(forward_goal_path::in, forward_goal_path::out,
|
|
goal_path_step::out) is semidet.
|
|
|
|
% Get the last item from the goal path. This fails if the goal path is
|
|
% empty.
|
|
%
|
|
:- pred goal_path_get_last(forward_goal_path::in, goal_path_step::out)
|
|
is semidet.
|
|
|
|
% Remove the first item from the goal path, returning it and the new
|
|
% goal path.
|
|
%
|
|
:- pred goal_path_remove_first(forward_goal_path::in, forward_goal_path::out,
|
|
goal_path_step::out) is semidet.
|
|
|
|
% Get the first item from the goal path. This fails if the goal path is
|
|
% empty.
|
|
%
|
|
:- pred goal_path_get_first(forward_goal_path::in, goal_path_step::out)
|
|
is semidet.
|
|
|
|
% Remove the last item from the goal path, returning it and the new
|
|
% goal path.
|
|
%
|
|
:- pred rev_goal_path_remove_last(reverse_goal_path::in,
|
|
reverse_goal_path::out, goal_path_step::out) is semidet.
|
|
|
|
% Get the last item from the goal path. This fails if the goal path is
|
|
% empty.
|
|
%
|
|
:- pred rev_goal_path_get_last(reverse_goal_path::in, goal_path_step::out)
|
|
is semidet.
|
|
|
|
%---------------------%
|
|
|
|
% goal_path_inside(PathA, PathB):
|
|
%
|
|
% Succeed if PathB denotes a goal *inside* the goal denoted by PathA.
|
|
% (It considers a goal to be inside itself.)
|
|
%
|
|
:- pred goal_path_inside(forward_goal_path::in, forward_goal_path::in)
|
|
is semidet.
|
|
:- pred rev_goal_path_inside(reverse_goal_path::in, reverse_goal_path::in)
|
|
is semidet.
|
|
|
|
% goal_path_inside_relative(PathA, PathB, RelativePath):
|
|
%
|
|
% As goal_path_inside, except that it also returns RelativePath, which
|
|
% denotes the same goal that PathB denotes, only from GoalA's perspective.
|
|
%
|
|
:- pred goal_path_inside_relative(forward_goal_path::in,
|
|
forward_goal_path::in, forward_goal_path::out) is semidet.
|
|
:- pred rev_goal_path_inside_relative(reverse_goal_path::in,
|
|
reverse_goal_path::in, reverse_goal_path::out) is semidet.
|
|
|
|
%---------------------%
|
|
|
|
% Convert one kind of goal path into the other.
|
|
%
|
|
:- pred rgp_to_fgp(reverse_goal_path::in, forward_goal_path::out) is det.
|
|
:- pred fgp_to_rgp(forward_goal_path::in, reverse_goal_path::out) is det.
|
|
|
|
%---------------------%
|
|
|
|
% Remove information from the goal path that depends on type information.
|
|
%
|
|
% This is necessary when using goal paths to lookup a map within the deep
|
|
% profiler. The goal paths used to perform the query cannot construct the
|
|
% parts of the goal paths that depend on type information.
|
|
%
|
|
:- pred rev_goal_path_remove_type_info(reverse_goal_path::in,
|
|
reverse_goal_path::out) is det.
|
|
|
|
%---------------------%
|
|
|
|
% Converts a string to a forward goal path, failing if the string
|
|
% is not a valid goal path.
|
|
%
|
|
:- pred goal_path_from_string(string::in, forward_goal_path::out) is semidet.
|
|
|
|
% Converts a string to a forward goal path, aborting if the string
|
|
% is not a valid goal path.
|
|
%
|
|
:- pred goal_path_from_string_det(string::in, forward_goal_path::out) is det.
|
|
|
|
% Converts a string to a reverse goal path, failing if the string
|
|
% is not a valid goal path.
|
|
%
|
|
:- pred rev_goal_path_from_string(string::in, reverse_goal_path::out)
|
|
is semidet.
|
|
|
|
% Converts a string to a reverse goal path, aborting if the string
|
|
% is not a valid goal path.
|
|
%
|
|
:- pred rev_goal_path_from_string_det(string::in, reverse_goal_path::out)
|
|
is det.
|
|
|
|
% Converts a string to a goal path step, failing if the string is not
|
|
% a valid goal path step.
|
|
%
|
|
:- pred goal_path_step_from_string(string::in, goal_path_step::out) is semidet.
|
|
|
|
% Is this character the one that ends each goal path step?
|
|
%
|
|
:- pred is_goal_path_separator(char::in) is semidet.
|
|
|
|
%---------------------%
|
|
|
|
% Convert the goal path to its string representation. The resulting string
|
|
% is guaranteed to be acceptable to path_from_string_det.
|
|
%
|
|
:- func goal_path_to_string(forward_goal_path) = string.
|
|
|
|
% Convert the goal path to its string representation. The resulting string
|
|
% is guaranteed to be acceptable to rev_path_from_string_det.
|
|
%
|
|
:- func rev_goal_path_to_string(reverse_goal_path) = string.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% We identify individual (sub)goals in clause bodies and procedure bodies
|
|
% with a goal id. The whole goal is goal_id(1u), and its subgoals are
|
|
% goal_id(N) for N > 1u. The goal id goal_id(0u) is reserved for use by
|
|
% goal_id_for_head_constraints.
|
|
%
|
|
:- type goal_id
|
|
---> goal_id(uint).
|
|
|
|
% Return the goal_id that identifies the whole of a procedure's body.
|
|
%
|
|
:- func whole_body_goal_id = goal_id.
|
|
|
|
% Return a recognizably invalid goal id.
|
|
%
|
|
:- func invalid_goal_id = goal_id.
|
|
|
|
% Succeed iff the given goal id is valid.
|
|
%
|
|
:- pred is_valid_goal_id(goal_id::in) is semidet.
|
|
|
|
% Return a goal id on which the typeclass checking code can hang
|
|
% the constraints that need to be proven for clause heads.
|
|
%
|
|
% This goal id will be *distinct* from all goal_ids that can be hung
|
|
% on goals in the bodies of clauses, i.e. it will NOT be a valid goal id.
|
|
%
|
|
:- func goal_id_for_head_constraints = goal_id.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type containing_goal
|
|
---> whole_body_goal
|
|
% This goal is the entire body of its procedure;
|
|
% there is no larger goal containing it.
|
|
; containing_goal(goal_id, goal_path_step).
|
|
% This goal is contained immediately inside the larger goal
|
|
% identified by the goal_id, from which you need to take the
|
|
% given goal_path_step to get to this goal.
|
|
%
|
|
% The goal_id of the containing goal is guaranteed to be
|
|
% less than the goal_id of this goal.
|
|
|
|
:- type containing_goal_map == map(goal_id, containing_goal).
|
|
:- type goal_forward_path_map == map(goal_id, forward_goal_path).
|
|
:- type goal_reverse_path_map == map(goal_id, reverse_goal_path).
|
|
:- type goal_reverse_path_bimap == bimap(goal_id, reverse_goal_path).
|
|
|
|
% Convert a goal_id to a forward goal path.
|
|
%
|
|
:- func goal_id_to_forward_path(containing_goal_map, goal_id) =
|
|
forward_goal_path.
|
|
|
|
% Convert a goal_id to a reverse goal path.
|
|
%
|
|
:- func goal_id_to_reverse_path(containing_goal_map, goal_id) =
|
|
reverse_goal_path.
|
|
|
|
% Given a containing_goal_map, create a map that maps each goal_id in it
|
|
% to a forward goal path.
|
|
%
|
|
:- func create_forward_goal_path_map(containing_goal_map) =
|
|
goal_forward_path_map.
|
|
|
|
% Given a containing_goal_map, create a map that maps each goal_id in it
|
|
% to a reverse goal path.
|
|
%
|
|
:- func create_reverse_goal_path_map(containing_goal_map) =
|
|
goal_reverse_path_map.
|
|
|
|
% Given a containing_goal_map, create a map that maps each goal_id in it
|
|
% to a reverse goal path, and back.
|
|
%
|
|
:- func create_reverse_goal_path_bimap(containing_goal_map) =
|
|
goal_reverse_path_bimap.
|
|
|
|
% goal_id_inside(ContainingGoalMap, GoalIdA, GoalIdB):
|
|
%
|
|
% Succeeds if GoalIdB denotes a goal *inside* the goal denoted by GoalIdA.
|
|
% (It considers a goal to be inside itself.)
|
|
%
|
|
:- pred goal_id_inside(containing_goal_map::in,
|
|
goal_id::in, goal_id::in) is semidet.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type goal_attr_array(T)
|
|
---> goal_attr_array(array(maybe(T))).
|
|
|
|
% This isn't really unique. See the comments on the `uniq_array' type
|
|
% in library/array.m.
|
|
%
|
|
:- inst uniq_goal_attr_array for goal_attr_array/1
|
|
---> goal_attr_array(uniq_array).
|
|
|
|
:- mode gaa_di == di(uniq_goal_attr_array).
|
|
:- mode gaa_uo == out(uniq_goal_attr_array).
|
|
|
|
% create_goal_id_array(LastGoalId) = Array.
|
|
%
|
|
% Create an array of the correct size to label all the goals
|
|
% up to and including LastGoalId.
|
|
%
|
|
:- func create_goal_id_array(goal_id) = goal_attr_array(T).
|
|
:- mode create_goal_id_array(in) = gaa_uo is det.
|
|
|
|
% create_goal_id_array(LastGoalId, Default) = Array.
|
|
%
|
|
% As above, except a default value is provided for array elements.
|
|
%
|
|
:- func create_goal_id_array(goal_id, T) = goal_attr_array(T).
|
|
:- mode create_goal_id_array(in, in) = gaa_uo is det.
|
|
|
|
% update_goal_attribute(GoalId, Attribute, !Array),
|
|
%
|
|
% Make Attribute the new attribute for GoalId in !:Array.
|
|
%
|
|
:- pred update_goal_attribute(goal_id::in, T::in,
|
|
goal_attr_array(T)::gaa_di, goal_attr_array(T)::gaa_uo) is det.
|
|
|
|
% get_goal_attribute(Array, GoalId) = Attribute.
|
|
%
|
|
% Get a goal attribute.
|
|
%
|
|
:- func get_goal_attribute_det(goal_attr_array(T), goal_id) = T.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module list.
|
|
:- import_module pair.
|
|
:- import_module require.
|
|
:- import_module string.
|
|
:- import_module uint.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
goal_path_add_at_end(fgp_nil, NewStep) = fgp_cons(NewStep, fgp_nil).
|
|
goal_path_add_at_end(fgp_cons(OldStep, GoalPath0), NewStep) =
|
|
fgp_cons(OldStep, GoalPath) :-
|
|
GoalPath = goal_path_add_at_end(GoalPath0, NewStep).
|
|
|
|
rev_goal_path_add_at_end(GoalPath0, NewStep) = GoalPath :-
|
|
GoalPath = rgp_cons(GoalPath0, NewStep).
|
|
|
|
goal_path_remove_last(fgp_cons(HeadStep, TailSteps),
|
|
AllButLastGoalPath, LastStep) :-
|
|
goal_path_remove_last_loop(HeadStep, TailSteps,
|
|
AllButLastGoalPath, LastStep).
|
|
|
|
:- pred goal_path_remove_last_loop(goal_path_step::in, forward_goal_path::in,
|
|
forward_goal_path::out, goal_path_step::out) is det.
|
|
|
|
goal_path_remove_last_loop(Head, fgp_nil, fgp_nil, Head).
|
|
goal_path_remove_last_loop(Head, fgp_cons(TailHead, TailTail),
|
|
AllButLastGoalPath, LastStep) :-
|
|
goal_path_remove_last_loop(TailHead, TailTail,
|
|
AllButLastGoalPath0, LastStep),
|
|
AllButLastGoalPath = fgp_cons(Head, AllButLastGoalPath0).
|
|
|
|
goal_path_get_last(fgp_cons(HeadStep, TailSteps), LastStep) :-
|
|
goal_path_last_loop(HeadStep, TailSteps, LastStep).
|
|
|
|
:- pred goal_path_last_loop(goal_path_step::in, forward_goal_path::in,
|
|
goal_path_step::out) is det.
|
|
|
|
goal_path_last_loop(Head, fgp_nil, Head).
|
|
goal_path_last_loop(_Head, fgp_cons(TailHead, TailTail), LastStep) :-
|
|
goal_path_last_loop(TailHead, TailTail, LastStep).
|
|
|
|
goal_path_remove_first(fgp_cons(FirstStep, OtherSteps), OtherSteps,
|
|
FirstStep).
|
|
|
|
goal_path_get_first(GoalPath, FirstStep) :-
|
|
goal_path_remove_first(GoalPath, _, FirstStep).
|
|
|
|
rev_goal_path_remove_last(rgp_cons(GoalPath, LastStep), GoalPath, LastStep).
|
|
|
|
rev_goal_path_get_last(rgp_cons(_, LastStep), LastStep).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
goal_path_inside(PathA, PathB) :-
|
|
goal_path_inside_relative(PathA, PathB, _).
|
|
|
|
rev_goal_path_inside(RevPathA, RevPathB) :-
|
|
rev_goal_path_inside_relative(RevPathA, RevPathB, _).
|
|
|
|
goal_path_inside_relative(fgp_nil, PathB, PathB).
|
|
goal_path_inside_relative(fgp_cons(StepA, PathA), fgp_cons(StepB, PathB),
|
|
RelativePath) :-
|
|
StepA = StepB,
|
|
goal_path_inside_relative(PathA, PathB, RelativePath).
|
|
|
|
rev_goal_path_inside_relative(RevPathA, RevPathB, RevRelativePath) :-
|
|
% XXX It would be more efficient if we could do this test
|
|
% without having to translate twice the part of RevPathB that
|
|
% goal_path_inside_relative returns but does not test.
|
|
rgp_to_fgp(RevPathA, PathA),
|
|
rgp_to_fgp(RevPathB, PathB),
|
|
goal_path_inside_relative(PathA, PathB, RelativePath),
|
|
fgp_to_rgp(RelativePath, RevRelativePath).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
rgp_to_fgp(ReverseGoalPath, ForwardGoalPath) :-
|
|
rgp_to_fgp_acc(ReverseGoalPath, fgp_nil, ForwardGoalPath).
|
|
|
|
:- pred rgp_to_fgp_acc(reverse_goal_path::in,
|
|
forward_goal_path::in, forward_goal_path::out) is det.
|
|
|
|
rgp_to_fgp_acc(rgp_nil, !ForwardGoalPath).
|
|
rgp_to_fgp_acc(rgp_cons(EarlierSteps, LastStep), !ForwardGoalPath) :-
|
|
!:ForwardGoalPath = fgp_cons(LastStep, !.ForwardGoalPath),
|
|
rgp_to_fgp_acc(EarlierSteps, !ForwardGoalPath).
|
|
|
|
fgp_to_rgp(ForwardGoalPath, ReverseGoalPath) :-
|
|
fgp_to_rgp_acc(ForwardGoalPath, rgp_nil, ReverseGoalPath).
|
|
|
|
:- pred fgp_to_rgp_acc(forward_goal_path::in,
|
|
reverse_goal_path::in, reverse_goal_path::out) is det.
|
|
|
|
fgp_to_rgp_acc(fgp_nil, !ReverseGoalPath).
|
|
fgp_to_rgp_acc(fgp_cons(FirstStep, LaterSteps), !ReverseGoalPath) :-
|
|
!:ReverseGoalPath = rgp_cons(!.ReverseGoalPath, FirstStep),
|
|
fgp_to_rgp_acc(LaterSteps, !ReverseGoalPath).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
rev_goal_path_remove_type_info(rgp_nil, rgp_nil).
|
|
rev_goal_path_remove_type_info(rgp_cons(Steps0, Step0),
|
|
rgp_cons(Steps, Step)) :-
|
|
goal_path_step_remove_type_info(Step0, Step),
|
|
rev_goal_path_remove_type_info(Steps0, Steps).
|
|
|
|
:- pred goal_path_step_remove_type_info(goal_path_step::in,
|
|
goal_path_step::out) is det.
|
|
|
|
goal_path_step_remove_type_info(!Step) :-
|
|
(
|
|
( !.Step = step_conj(_)
|
|
; !.Step = step_disj(_)
|
|
; !.Step = step_ite_cond
|
|
; !.Step = step_ite_then
|
|
; !.Step = step_ite_else
|
|
; !.Step = step_neg
|
|
; !.Step = step_scope(_)
|
|
; !.Step = step_lambda
|
|
; !.Step = step_try
|
|
; !.Step = step_atomic_main
|
|
; !.Step = step_atomic_orelse(_)
|
|
)
|
|
;
|
|
!.Step = step_switch(N, _),
|
|
!:Step = step_switch(N, unknown_num_functors_in_type)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
goal_path_from_string(GoalPathStr, GoalPath) :-
|
|
StepStrs = string.words_separator(is_goal_path_separator, GoalPathStr),
|
|
goal_path_from_strings(StepStrs, GoalPath).
|
|
|
|
:- pred goal_path_from_strings(list(string)::in, forward_goal_path::out)
|
|
is semidet.
|
|
|
|
goal_path_from_strings([], fgp_nil).
|
|
goal_path_from_strings([Str | Strs], fgp_cons(HeadStep, LaterSteps)) :-
|
|
goal_path_step_from_string(Str, HeadStep),
|
|
goal_path_from_strings(Strs, LaterSteps).
|
|
|
|
goal_path_from_string_det(GoalPathStr, GoalPath) :-
|
|
( if goal_path_from_string(GoalPathStr, GoalPathPrime) then
|
|
GoalPath = GoalPathPrime
|
|
else
|
|
unexpected($pred, "goal_path_from_string failed")
|
|
).
|
|
|
|
rev_goal_path_from_string(GoalPathStr, GoalPath) :-
|
|
StepStrs = string.words_separator(is_goal_path_separator, GoalPathStr),
|
|
list.reverse(StepStrs, RevStepStrs),
|
|
rev_goal_path_from_rev_strings(RevStepStrs, GoalPath).
|
|
|
|
:- pred rev_goal_path_from_rev_strings(list(string)::in,
|
|
reverse_goal_path::out) is semidet.
|
|
|
|
rev_goal_path_from_rev_strings([], rgp_nil).
|
|
rev_goal_path_from_rev_strings([Str | Strs], rgp_cons(HeadSteps, TailStep)) :-
|
|
rev_goal_path_from_rev_strings(Strs, HeadSteps),
|
|
goal_path_step_from_string(Str, TailStep).
|
|
|
|
rev_goal_path_from_string_det(GoalPathStr, GoalPath) :-
|
|
( if rev_goal_path_from_string(GoalPathStr, GoalPathPrime) then
|
|
GoalPath = GoalPathPrime
|
|
else
|
|
unexpected($pred, "rev_goal_path_from_string failed")
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
goal_path_step_from_string(String, Step) :-
|
|
string.first_char(String, FirstChar, RestStr),
|
|
(
|
|
( FirstChar = '?', Step = step_ite_cond
|
|
; FirstChar = 't', Step = step_ite_then
|
|
; FirstChar = 'e', Step = step_ite_else
|
|
; FirstChar = ('~'), Step = step_neg
|
|
; FirstChar = 'r', Step = step_try
|
|
; FirstChar = ('='), Step = step_lambda
|
|
; FirstChar = 'a', Step = step_atomic_main
|
|
),
|
|
RestStr = ""
|
|
;
|
|
FirstChar = 'c',
|
|
string.to_int(RestStr, N),
|
|
Step = step_conj(N)
|
|
;
|
|
FirstChar = 'd',
|
|
string.to_int(RestStr, N),
|
|
Step = step_disj(N)
|
|
;
|
|
FirstChar = 'o',
|
|
string.to_int(RestStr, N),
|
|
Step = step_atomic_orelse(N)
|
|
;
|
|
FirstChar = 's',
|
|
string.words_separator(unify('-'), RestStr) = [NStr, MStr],
|
|
string.to_int(NStr, N),
|
|
( if MStr = "na" then % "na" is short for "not applicable"
|
|
MaybeM = unknown_num_functors_in_type
|
|
else
|
|
string.to_int(MStr, M),
|
|
MaybeM = known_num_functors_in_type(M)
|
|
),
|
|
Step = step_switch(N, MaybeM)
|
|
;
|
|
FirstChar = 'q',
|
|
(
|
|
RestStr = "",
|
|
Step = step_scope(scope_is_no_cut)
|
|
;
|
|
RestStr = "!",
|
|
Step = step_scope(scope_is_cut)
|
|
)
|
|
).
|
|
|
|
is_goal_path_separator(';').
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
goal_path_to_string(GoalPath) = GoalPathStr :-
|
|
StepStrs = goal_path_to_strings(GoalPath),
|
|
string.append_list(StepStrs, GoalPathStr).
|
|
|
|
:- func goal_path_to_strings(forward_goal_path) = list(string).
|
|
|
|
goal_path_to_strings(fgp_nil) = [].
|
|
goal_path_to_strings(fgp_cons(Step, Steps)) = [Str | Strs] :-
|
|
Str = goal_path_step_to_string(Step),
|
|
Strs = goal_path_to_strings(Steps).
|
|
|
|
rev_goal_path_to_string(GoalPath) = GoalPathStr :-
|
|
RevStepStrs = rev_goal_path_to_strings(GoalPath),
|
|
list.reverse(RevStepStrs, StepStrs),
|
|
string.append_list(StepStrs, GoalPathStr).
|
|
|
|
:- func rev_goal_path_to_strings(reverse_goal_path) = list(string).
|
|
|
|
rev_goal_path_to_strings(rgp_nil) = [].
|
|
rev_goal_path_to_strings(rgp_cons(Steps, Step)) = [Str | Strs] :-
|
|
Str = goal_path_step_to_string(Step),
|
|
Strs = rev_goal_path_to_strings(Steps).
|
|
|
|
:- func goal_path_step_to_string(goal_path_step) = string.
|
|
|
|
goal_path_step_to_string(step_conj(N)) = "c" ++ int_to_string(N) ++ ";".
|
|
goal_path_step_to_string(step_disj(N)) = "d" ++ int_to_string(N) ++ ";".
|
|
goal_path_step_to_string(step_switch(N, known_num_functors_in_type(M))) =
|
|
"s" ++ int_to_string(N) ++ "-" ++ int_to_string(M) ++ ";".
|
|
goal_path_step_to_string(step_switch(N, unknown_num_functors_in_type)) =
|
|
"s" ++ int_to_string(N) ++ "-na;". % short for "not applicable"
|
|
goal_path_step_to_string(step_ite_cond) = "?;".
|
|
goal_path_step_to_string(step_ite_then) = "t;".
|
|
goal_path_step_to_string(step_ite_else) = "e;".
|
|
goal_path_step_to_string(step_neg) = "~;".
|
|
goal_path_step_to_string(step_scope(scope_is_cut)) = "q!;".
|
|
goal_path_step_to_string(step_scope(scope_is_no_cut)) = "q;".
|
|
goal_path_step_to_string(step_try) = "r;".
|
|
goal_path_step_to_string(step_lambda) = "=;".
|
|
goal_path_step_to_string(step_atomic_main) = "a;".
|
|
goal_path_step_to_string(step_atomic_orelse(N)) =
|
|
"o" ++ int_to_string(N) ++ ";".
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
whole_body_goal_id = goal_id(1u).
|
|
|
|
invalid_goal_id = goal_id(0u).
|
|
|
|
is_valid_goal_id(goal_id(GoalIdNum)) :-
|
|
GoalIdNum > 0u.
|
|
|
|
goal_id_for_head_constraints = goal_id(0u).
|
|
% Note that this is NOT a valid goal_id for a goal. Not being able
|
|
% to confuse the goal_id on which head constraints are hung with the
|
|
% goal_id of an actual goal is the POINT of this function.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
goal_id_to_forward_path(ContainingGoalMap, GoalId) = GoalPath :-
|
|
RevGoalPath = goal_id_to_reverse_path(ContainingGoalMap, GoalId),
|
|
rgp_to_fgp(RevGoalPath, GoalPath).
|
|
|
|
goal_id_to_reverse_path(ContainingGoalMap, GoalId) = GoalPath :-
|
|
map.lookup(ContainingGoalMap, GoalId, ContainingGoal),
|
|
(
|
|
ContainingGoal = whole_body_goal,
|
|
GoalPath = rgp_nil
|
|
;
|
|
ContainingGoal = containing_goal(ParentGoalId, LastStep),
|
|
EarlierPath = goal_id_to_reverse_path(ContainingGoalMap, ParentGoalId),
|
|
GoalPath = rgp_cons(EarlierPath, LastStep)
|
|
).
|
|
|
|
create_forward_goal_path_map(ContainingGoalMap) = ForwardGoalPathMap :-
|
|
ReverseGoalPathMap = create_reverse_goal_path_map(ContainingGoalMap),
|
|
map.map_values_only(rgp_to_fgp, ReverseGoalPathMap, ForwardGoalPathMap).
|
|
|
|
create_reverse_goal_path_map(ContainingGoalMap) = ReverseGoalPathMap :-
|
|
map.to_assoc_list(ContainingGoalMap, ContainingGoalList),
|
|
create_reverse_goal_path_map_acc(ContainingGoalList,
|
|
map.init, ReverseGoalPathMap).
|
|
|
|
:- pred create_reverse_goal_path_map_acc(
|
|
assoc_list(goal_id, containing_goal)::in,
|
|
map(goal_id, reverse_goal_path)::in, map(goal_id, reverse_goal_path)::out)
|
|
is det.
|
|
|
|
create_reverse_goal_path_map_acc([], !ReverseGoalPathMap).
|
|
create_reverse_goal_path_map_acc([Head | Tail], !ReverseGoalPathMap) :-
|
|
Head = GoalId - ContainingGoal,
|
|
(
|
|
ContainingGoal = whole_body_goal,
|
|
GoalReversePath = rgp_nil
|
|
;
|
|
ContainingGoal = containing_goal(ContainingGoalId, Step),
|
|
map.lookup(!.ReverseGoalPathMap, ContainingGoalId,
|
|
ContainingGoalReversePath),
|
|
GoalReversePath = rgp_cons(ContainingGoalReversePath, Step)
|
|
),
|
|
map.det_insert(GoalId, GoalReversePath, !ReverseGoalPathMap),
|
|
create_reverse_goal_path_map_acc(Tail, !ReverseGoalPathMap).
|
|
|
|
create_reverse_goal_path_bimap(ContainingGoalMap) = ReverseGoalPathBiMap :-
|
|
map.to_assoc_list(ContainingGoalMap, ContainingGoalList),
|
|
create_reverse_goal_path_bimap_acc(ContainingGoalList,
|
|
bimap.init, ReverseGoalPathBiMap).
|
|
|
|
:- pred create_reverse_goal_path_bimap_acc(
|
|
assoc_list(goal_id, containing_goal)::in,
|
|
bimap(goal_id, reverse_goal_path)::in,
|
|
bimap(goal_id, reverse_goal_path)::out) is det.
|
|
|
|
create_reverse_goal_path_bimap_acc([], !ReverseGoalPathBiMap).
|
|
create_reverse_goal_path_bimap_acc([Head | Tail], !ReverseGoalPathBiMap) :-
|
|
Head = GoalId - ContainingGoal,
|
|
(
|
|
ContainingGoal = whole_body_goal,
|
|
GoalReversePath = rgp_nil
|
|
;
|
|
ContainingGoal = containing_goal(ContainingGoalId, Step),
|
|
bimap.lookup(!.ReverseGoalPathBiMap, ContainingGoalId,
|
|
ContainingGoalReversePath),
|
|
GoalReversePath = rgp_cons(ContainingGoalReversePath, Step)
|
|
),
|
|
bimap.det_insert(GoalId, GoalReversePath, !ReverseGoalPathBiMap),
|
|
create_reverse_goal_path_bimap_acc(Tail, !ReverseGoalPathBiMap).
|
|
|
|
goal_id_inside(ContainingGoalId, GoalIdA, GoalIdB) :-
|
|
(
|
|
GoalIdB = GoalIdA
|
|
;
|
|
map.lookup(ContainingGoalId, GoalIdB, GoalContainingB),
|
|
GoalContainingB = containing_goal(ParentGoalIdB, _),
|
|
goal_id_inside(ContainingGoalId, GoalIdA, ParentGoalIdB)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
create_goal_id_array(goal_id(LastGoalIdNum)) =
|
|
goal_attr_array(array.init(uint.cast_to_int(LastGoalIdNum + 1u), no)).
|
|
|
|
create_goal_id_array(goal_id(LastGoalIdNum), Default) =
|
|
goal_attr_array(array.init(uint.cast_to_int(LastGoalIdNum + 1u),
|
|
yes(Default))).
|
|
|
|
update_goal_attribute(goal_id(Index), Value,
|
|
goal_attr_array(!.Array), goal_attr_array(!:Array)) :-
|
|
array.set(uint.cast_to_int(Index), yes(Value), !Array).
|
|
|
|
get_goal_attribute_det(goal_attr_array(Array), goal_id(Index)) = Attr :-
|
|
MaybeAttr = array.lookup(Array, uint.cast_to_int(Index)),
|
|
(
|
|
MaybeAttr = yes(Attr)
|
|
;
|
|
MaybeAttr = no,
|
|
unexpected($pred, "Goal attribute array slot empty")
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module mdbcomp.goal_path.
|
|
%---------------------------------------------------------------------------%
|