Files
mercury/browser/declarative_oracle.m
Ian MacLarty 7015c4a436 Made the declarative debugger respect the format settings of the procedural
Estimated hours taken: 3
Branches: main

Made the declarative debugger respect the format settings of the procedural
debugger.  The print format set in the procedural debugger will now be used
when the declarative debugger asks questions and the browse format will now be
used when browsing terms in the declarative debugger.  If the user changes the
browser format during a declarative debugging session the procedural debugger
will respect these changes.

This also means the declarative debugger will respect the settings in the
.mdbrc file.

browser/declarative_debugger.m
	Added term browser state arguments to main diagnosis predicate and
	initialisation predicate.

browser/declarative_oracle.m
	Added predicates to get and set the term browser state.
	Set the term browser state in the initialisation prediciate for the
	oracle state.

browser/declarative_user.m
	Added predicates to get and set the term browser state.
	Set the term browser state in the initialisation prediciate for the
	user state.

tests/debugger/declarative/dependency.exp
	This test sets the print format in the procedural debugger, so
	the declarative debugger now uses this format.

trace/mercury_trace_browse.c
trace/mercury_trace_browse.h
	Exported the global variable which stores the state of the procedural
	debugger term browser and the function which initialises this state, so
	they can be used from mercury_trace_declarative.c.

trace/mercury_trace_declarative.c
	Pass the procedural debugger term browser state to the declarative
	debugger and update the state when the declarative debugger is
	finished.
2004-10-25 05:30:19 +00:00

604 lines
18 KiB
Mathematica

%-----------------------------------------------------------------------------%
% Copyright (C) 1999-2004 The University of Melbourne.
% This file may only be copied under the terms of the GNU Library General
% Public License - see the file COPYING.LIB in the Mercury distribution.
%-----------------------------------------------------------------------------%
% File: declarative_oracle.m
% Author: Mark Brown
% Purpose:
% This module implements the oracle for a Mercury declarative debugger.
% It is called by the front end of the declarative debugger to provide
% information about the intended interpretation of the program being
% debugged.
%
% The module has a knowledge base as a sub-component. This is a cache
% for all the assumptions that the oracle is currently making. When
% the oracle is queried, it first checks the KB to see if an answer
% is available there.
%
% If no answer is available in the KB, then the oracle uses the UI
% (in browser/declarative_user.m) to get the required answer from the
% user. If any new knowledge is obtained, it is added to the KB so
% the user will not be asked the same question twice.
%
:- module mdb__declarative_oracle.
:- interface.
:- import_module mdb__declarative_debugger.
:- import_module mdb__declarative_execution.
:- import_module mdb.browser_info.
:- import_module list, io, bool, string.
% A response that the oracle gives to a query about the
% truth of an EDT node.
%
:- type oracle_response(T)
---> oracle_answers(list(decl_answer(T)))
; no_oracle_answers
; exit_diagnosis(T)
; abort_diagnosis.
% The oracle state. This is threaded around the declarative
% debugger.
%
:- type oracle_state.
% Produce a new oracle state.
%
:- pred oracle_state_init(io__input_stream::in, io__output_stream::in,
browser_info.browser_persistent_state::in, oracle_state::out) is det.
% Add a module to the set of modules trusted by the oracle
%
:- pred add_trusted_module(string::in, oracle_state::in, oracle_state::out)
is det.
% Add a predicate/function to the set of predicates/functions trusted
% by the oracle.
%
:- pred add_trusted_pred_or_func(proc_layout::in, oracle_state::in,
oracle_state::out) is det.
% remove_trusted(N, !Oracle).
% Removes the (N-1)th trusted object from the set of trusted objects.
% Fails if there are fewer than N-1 trusted modules (or N < 0).
% The trusted set is turned into a sorted list before finding the
% (N-1)th element.
%
:- pred remove_trusted(int::in, oracle_state::in, oracle_state::out)
is semidet.
% get_trusted_list(Oracle, MDBCommandFormat, String).
% Return a string listing the trusted objects.
% If MDBCommandFormat is true then returns the list so that it can be
% run as a series of mdb `trust' commands. Otherwise returns them
% in a format suitable for display only.
%
:- pred get_trusted_list(oracle_state::in, bool::in, string::out) is det.
% Query the oracle about the program being debugged. The first
% argument is a queue of nodes in the evaluation tree, the second
% argument is the oracle response to any of these. The oracle
% state is threaded through so its contents can be updated after
% user responses.
%
:- pred query_oracle(list(decl_question(T))::in, oracle_response(T)::out,
oracle_state::in, oracle_state::out, io__state::di, io__state::uo)
is cc_multi.
% Confirm that the node found is indeed an e_bug or an i_bug. If
% the bug is overruled, force the oracle to forget everything
% it knows about the evidence that led to that bug.
%
:- pred oracle_confirm_bug(decl_bug::in, decl_evidence(T)::in,
decl_confirmation::out, oracle_state::in, oracle_state::out,
io__state::di, io__state::uo) is cc_multi.
% Returns the state of the term browser.
%
:- func get_browser_state(oracle_state)
= browser_info.browser_persistent_state.
% Sets the state of the term browser.
%
:- pred set_browser_state(browser_info.browser_persistent_state::in,
oracle_state::in, oracle_state::out) is det.
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module mdb__declarative_user.
:- import_module mdb__tree234_cc.
:- import_module mdb__set_cc.
:- import_module mdb__util.
:- import_module bool, std_util, set, int.
query_oracle(Questions, Response, Oracle0, Oracle) -->
{ query_oracle_list(Oracle0, Questions, Answers) },
(
{ Answers = [] }
->
{ list__map(make_user_question(Oracle0 ^ kb_revised),
Questions, UserQuestions) },
query_oracle_user(UserQuestions, Response, Oracle0, Oracle)
;
{ Response = oracle_answers(Answers) },
{ Oracle = Oracle0 }
).
:- pred make_user_question(oracle_kb::in, decl_question(T)::in,
user_question(T)::out) is cc_multi.
make_user_question(Revised, DeclQuestion, UserQuestion) :-
query_oracle_kb(Revised, DeclQuestion, MaybeDeclAnswer),
(
MaybeDeclAnswer = yes(truth_value(_, DeclTruth))
->
UserQuestion = question_with_default(DeclQuestion, DeclTruth)
;
UserQuestion = plain_question(DeclQuestion)
).
:- pred query_oracle_user(list(user_question(T))::in, oracle_response(T)::out,
oracle_state::in, oracle_state::out, io__state::di, io__state::uo)
is cc_multi.
query_oracle_user(Questions, OracleResponse, Oracle0, Oracle) -->
{ User0 = Oracle0 ^ user_state },
query_user(Questions, UserResponse, User0, User),
{
UserResponse = user_answer(Question, Answer),
OracleResponse = oracle_answers([Answer]),
Current0 = Oracle0 ^ kb_current,
Revised0 = Oracle0 ^ kb_revised,
retract_oracle_kb(Question, Revised0, Revised),
assert_oracle_kb(Question, Answer, Current0, Current),
Oracle1 = (Oracle0
^ kb_current := Current)
^ kb_revised := Revised
;
UserResponse = no_user_answer,
OracleResponse = no_oracle_answers,
Oracle1 = Oracle0
;
UserResponse = exit_diagnosis(Node),
OracleResponse = exit_diagnosis(Node),
Oracle1 = Oracle0
;
UserResponse = abort_diagnosis,
OracleResponse = abort_diagnosis,
Oracle1 = Oracle0
},
{ Oracle = Oracle1 ^ user_state := User }.
oracle_confirm_bug(Bug, Evidence, Confirmation, Oracle0, Oracle) -->
{ User0 = Oracle0 ^ user_state },
user_confirm_bug(Bug, Confirmation, User0, User),
{ Oracle1 = Oracle0 ^ user_state := User },
{
Confirmation = overrule_bug
->
list__foldl(revise_oracle, Evidence, Oracle1, Oracle)
;
Oracle = Oracle1
}.
:- pred revise_oracle(decl_question(T)::in, oracle_state::in, oracle_state::out)
is cc_multi.
revise_oracle(Question, Oracle0, Oracle) :-
Current0 = Oracle0 ^ kb_current,
query_oracle_kb(Current0, Question, MaybeAnswer),
(
MaybeAnswer = yes(Answer),
retract_oracle_kb(Question, Current0, Current),
Revised0 = Oracle0 ^ kb_revised,
assert_oracle_kb(Question, Answer, Revised0, Revised),
Oracle = (Oracle0
^ kb_revised := Revised)
^ kb_current := Current
;
MaybeAnswer = no,
Oracle = Oracle0
).
%-----------------------------------------------------------------------------%
:- type oracle_state
---> oracle(
kb_current :: oracle_kb,
% Current information about the intended
% interpretation. These answers have been
% given, but have not since been revised.
kb_revised :: oracle_kb,
% Old information about the intended
% interpretation. These answers were given
% and subsequently revised, but new answers
% to the questions have not yet been given.
user_state :: user_state,
% User interface.
trusted :: set(trusted_module_or_predicate)
% Modules and predicates/functions trusted
% by the oracle.
).
oracle_state_init(InStr, OutStr, Browser, Oracle) :-
oracle_kb_init(Current),
oracle_kb_init(Old),
user_state_init(InStr, OutStr, Browser, User),
set.init(TrustedModules),
Oracle = oracle(Current, Old, User, TrustedModules).
%-----------------------------------------------------------------------------%
:- type trusted_module_or_predicate
---> all(string) % all predicates/functions in a module
; specific(
pred_or_func,
string, % module name
string, % pred or func name
int % arity
).
add_trusted_module(ModuleName, !Oracle) :-
insert(!.Oracle ^ trusted, all(ModuleName), Trusted),
!:Oracle = !.Oracle ^ trusted := Trusted.
add_trusted_pred_or_func(ProcLayout, !Oracle) :-
ProcId = get_proc_id_from_layout(ProcLayout),
(
ProcId = proc(ModuleName, PredOrFunc, _, Name, Arity, _)
;
ProcId = uci_proc(ModuleName, _, _, Name, Arity, _),
PredOrFunc = predicate
),
insert(!.Oracle ^ trusted, specific(PredOrFunc, ModuleName, Name,
Arity), Trusted),
!:Oracle = !.Oracle ^ trusted := Trusted.
remove_trusted(N, !Oracle) :-
TrustedList = to_sorted_list(!.Oracle ^ trusted),
index0(TrustedList, N, ObjectToDelete),
delete_all(TrustedList, ObjectToDelete, NewTrustedList),
!:Oracle = !.Oracle ^ trusted := sorted_list_to_set(NewTrustedList).
get_trusted_list(Oracle, CommandFormat, List) :-
Trusted = to_sorted_list(Oracle ^ trusted),
(
CommandFormat = yes,
foldl(format_trust_command, Trusted, "", List)
;
CommandFormat = no,
foldl(format_trust_display, Trusted, {0, "Trusted Objects:\n"},
{I, List0}),
(
I = 0
->
List = "There are no trusted modules, predicates "++
"or functions.\n"
;
List = List0
)
).
:- pred format_trust_command(trusted_module_or_predicate::in, string::in,
string::out) is det.
format_trust_command(all(ModuleName), S, S++"trust "++ModuleName++"\n").
format_trust_command(specific(PredOrFunc, ModuleName, Name, Arity), S,
S++Command) :-
(
PredOrFunc = predicate,
PredOrFuncStr = "pred*",
ArityStr = int_to_string(Arity)
;
PredOrFunc = function,
PredOrFuncStr = "func*",
ArityStr = int_to_string(Arity - 1)
),
Command = "trust "++PredOrFuncStr++ModuleName++"."++Name++"/"++
ArityStr++ "\n".
:- pred format_trust_display(trusted_module_or_predicate::in, {int,string}::in,
{int,string}::out) is det.
format_trust_display(all(ModuleName), {I, S},
{I + 1, S++int_to_string(I)++": module "++ModuleName++"\n"}).
format_trust_display(specific(PredOrFunc, ModuleName, Name, Arity), {I, S},
{I + 1, S++Display}) :-
(
PredOrFunc = predicate,
PredOrFuncStr = "pred",
ArityStr = int_to_string(Arity)
;
PredOrFunc = function,
PredOrFuncStr = "func",
ArityStr = int_to_string(Arity - 1)
),
Display = int_to_string(I)++": "++PredOrFuncStr++" "++ModuleName++"."++
Name++"/"++ArityStr++"\n".
%-----------------------------------------------------------------------------%
%
% This section implements the oracle knowledge base, which
% stores anything that the debugger knows about the intended
% interpretation. This can be used to check the correctness
% of an EDT node.
%
% The type of the knowledge base. Other fields may be added in
% the future, such as for assertions made on-the-fly by the user,
% or assertions in the program text.
%
:- type oracle_kb
---> oracle_kb(
% For ground atoms, the knowledge is represented directly
% with a map. This is used, for example, in the common
% case that the user supplies a truth value for a
% "wrong answer" node.
%
kb_ground_map :: map_cc(final_decl_atom, decl_truth),
% This map stores knowledge about the completeness of the
% set of solutions generated by calling the given initial
% atom. This is used, for example, in the common case that
% the user supplies a truth value for a "missing answer"
% node.
%
kb_complete_map :: map_cc(init_decl_atom, decl_truth),
% Mapping from call atoms to information about which
% exceptions are possible or impossible.
%
kb_exceptions_map :: map_cc(init_decl_atom, known_exceptions)
).
:- type map_cc(K, V) == tree234_cc(K, V).
:- type known_exceptions
---> known_excp(
set_cc(decl_exception), % Possible exceptions.
set_cc(decl_exception) % Impossible exceptions.
).
:- pred oracle_kb_init(oracle_kb).
:- mode oracle_kb_init(out) is det.
oracle_kb_init(oracle_kb(G, C, X)) :-
tree234_cc__init(G),
tree234_cc__init(C),
tree234_cc__init(X).
:- pred get_kb_ground_map(oracle_kb, map_cc(final_decl_atom, decl_truth)).
:- mode get_kb_ground_map(in, out) is det.
get_kb_ground_map(KB, KB ^ kb_ground_map).
:- pred set_kb_ground_map(oracle_kb, map_cc(final_decl_atom, decl_truth),
oracle_kb).
:- mode set_kb_ground_map(in, in, out) is det.
set_kb_ground_map(KB, M, KB ^ kb_ground_map := M).
:- pred get_kb_complete_map(oracle_kb,
map_cc(init_decl_atom, decl_truth)).
:- mode get_kb_complete_map(in, out) is det.
get_kb_complete_map(KB, KB ^ kb_complete_map).
:- pred set_kb_complete_map(oracle_kb,
map_cc(init_decl_atom, decl_truth), oracle_kb).
:- mode set_kb_complete_map(in, in, out) is det.
set_kb_complete_map(KB, M, KB ^ kb_complete_map := M).
:- pred get_kb_exceptions_map(oracle_kb,
map_cc(init_decl_atom, known_exceptions)).
:- mode get_kb_exceptions_map(in, out) is det.
get_kb_exceptions_map(KB, KB ^ kb_exceptions_map).
:- pred set_kb_exceptions_map(oracle_kb,
map_cc(init_decl_atom, known_exceptions), oracle_kb).
:- mode set_kb_exceptions_map(in, in, out) is det.
set_kb_exceptions_map(KB, M, KB ^ kb_exceptions_map := M).
%-----------------------------------------------------------------------------%
:- pred query_oracle_list(oracle_state::in, list(decl_question(T))::in,
list(decl_answer(T))::out) is cc_multi.
query_oracle_list(_, [], []).
query_oracle_list(OS, [Q | Qs0], As) :-
query_oracle_list(OS, Qs0, As0),
Atom = get_decl_question_atom(Q),
(
trusted(Atom ^ proc_layout, OS)
->
As = [truth_value(get_decl_question_node(Q), yes) | As0]
;
query_oracle_kb(OS ^ kb_current, Q, MaybeA),
(
MaybeA = yes(A),
As = [A | As0]
;
MaybeA = no,
As = As0
)
).
:- pred trusted(proc_layout::in, oracle_state::in) is semidet.
trusted(ProcLayout, Oracle) :-
ProcId = get_proc_id_from_layout(ProcLayout),
(
ProcId = proc(Module, PredOrFunc, _, Name, Arity, _),
(
set.member(all(Module), Oracle ^ trusted)
;
set.member(specific(PredOrFunc, Module, Name, Arity),
Oracle ^ trusted)
)
;
ProcId = uci_proc(_, _, _, _, _, _)
).
:- pred query_oracle_kb(oracle_kb::in, decl_question(T)::in,
maybe(decl_answer(T))::out) is cc_multi.
query_oracle_kb(KB, Question, Result) :-
Question = wrong_answer(Node, Atom),
get_kb_ground_map(KB, Map),
tree234_cc__search(Map, Atom, MaybeTruth),
(
MaybeTruth = yes(Truth),
Result = yes(truth_value(Node, Truth))
;
MaybeTruth = no,
Result = no
).
query_oracle_kb(KB, Question, Result) :-
Question = missing_answer(Node, Call, _Solns),
get_kb_complete_map(KB, CMap),
tree234_cc__search(CMap, Call, MaybeTruth),
(
MaybeTruth = yes(Truth),
Result = yes(truth_value(Node, Truth))
;
MaybeTruth = no,
Result = no
).
query_oracle_kb(KB, Question, Result) :-
Question = unexpected_exception(Node, Call, Exception),
get_kb_exceptions_map(KB, XMap),
tree234_cc__search(XMap, Call, MaybeX),
(
MaybeX = no,
Result = no
;
MaybeX = yes(known_excp(Possible, Impossible)),
set_cc__member(Exception, Possible, PossibleBool),
(
PossibleBool = yes,
Result = yes(truth_value(Node, yes))
;
PossibleBool = no,
set_cc__member(Exception, Impossible, ImpossibleBool),
(
ImpossibleBool = yes,
Result = yes(truth_value(Node, no))
;
ImpossibleBool = no,
Result = no
)
)
).
% assert_oracle_kb/3 assumes that the asserted fact is consistent
% with the current knowledge base. This will generally be the
% case, since the user will never be asked questions which
% the knowledge base knows anything about.
%
:- pred assert_oracle_kb(decl_question(T), decl_answer(T), oracle_kb,
oracle_kb).
:- mode assert_oracle_kb(in, in, in, out) is cc_multi.
assert_oracle_kb(_, suspicious_subterm(_, _, _), KB, KB).
assert_oracle_kb(wrong_answer(_, Atom), truth_value(_, Truth), KB0, KB) :-
get_kb_ground_map(KB0, Map0),
% insert all modes of the predicate/function
foldl(add_atom_to_ground_map(Truth, Atom),
get_all_modes_for_layout(Atom ^ final_atom ^ proc_layout),
Map0, Map),
set_kb_ground_map(KB0, Map, KB).
:- pred add_atom_to_ground_map(decl_truth::in, final_decl_atom::in,
proc_layout::in, map_cc(final_decl_atom, decl_truth)::in,
map_cc(final_decl_atom, decl_truth)::out) is cc_multi.
add_atom_to_ground_map(Truth, FinalAtom, ProcLayout, Map0, Map) :-
tree234_cc.set(Map0, final_decl_atom(
atom(ProcLayout, FinalAtom ^ final_atom ^ atom_args),
FinalAtom ^ final_io_actions), Truth, Map).
assert_oracle_kb(missing_answer(_, Call, _), truth_value(_, Truth), KB0, KB) :-
get_kb_complete_map(KB0, Map0),
tree234_cc__set(Map0, Call, Truth, Map),
set_kb_complete_map(KB0, Map, KB).
assert_oracle_kb(unexpected_exception(_, Call, Exception),
truth_value(_, Truth), KB0, KB) :-
get_kb_exceptions_map(KB0, Map0),
tree234_cc__search(Map0, Call, MaybeX),
(
MaybeX = yes(known_excp(Possible0, Impossible0))
;
MaybeX = no,
set_cc__init(Possible0),
set_cc__init(Impossible0)
),
(
Truth = yes,
set_cc__insert(Possible0, Exception, Possible),
Impossible = Impossible0
;
Truth = no,
Possible = Possible0,
set_cc__insert(Impossible0, Exception, Impossible)
),
tree234_cc__set(Map0, Call, known_excp(Possible, Impossible), Map),
set_kb_exceptions_map(KB0, Map, KB).
:- pred retract_oracle_kb(decl_question(T), oracle_kb, oracle_kb).
:- mode retract_oracle_kb(in, in, out) is cc_multi.
retract_oracle_kb(wrong_answer(_, FinalAtom), KB0, KB) :-
Map0 = KB0 ^ kb_ground_map,
tree234_cc__delete(Map0, FinalAtom, Map),
KB = KB0 ^ kb_ground_map := Map.
retract_oracle_kb(missing_answer(_, InitAtom, _), KB0, KB) :-
CompleteMap0 = KB0 ^ kb_complete_map,
tree234_cc__delete(CompleteMap0, InitAtom, CompleteMap),
KB = KB0 ^ kb_complete_map := CompleteMap.
retract_oracle_kb(unexpected_exception(_, InitAtom, Exception), KB0, KB) :-
ExceptionsMap0 = KB0 ^ kb_exceptions_map,
tree234_cc__search(ExceptionsMap0, InitAtom, MaybeKnownExceptions0),
(
MaybeKnownExceptions0 = yes(known_excp(Possible0, Impossible0))
->
set_cc__delete(Possible0, Exception, Possible),
set_cc__delete(Impossible0, Exception, Impossible),
KnownExceptions = known_excp(Possible, Impossible),
tree234_cc__set(ExceptionsMap0, InitAtom, KnownExceptions,
ExceptionsMap)
;
ExceptionsMap = ExceptionsMap0
),
KB = KB0 ^ kb_exceptions_map := ExceptionsMap.
%-----------------------------------------------------------------------------%
get_browser_state(Oracle) =
mdb.declarative_user.get_browser_state(Oracle ^ user_state).
set_browser_state(Browser, !Oracle) :-
mdb.declarative_user.set_browser_state(Browser, !.Oracle ^ user_state,
User),
!:Oracle = !.Oracle ^ user_state := User.