Files
mercury/browser/declarative_oracle.m
Mark Brown 134e188154 Represent bugs directly rather than with edt nodes.
Estimated hours taken: 10

Represent bugs directly rather than with edt nodes.  This makes the
interface to the analyser more consistent: bugs are now handled
in much the same way as questions.

browser/declarative_analyser.m:
	- In the mercury_edt/2 typeclass, change edt_root/3 to
	  edt_root_question/3 and add the method edt_root_e_bug/3.
	- Add the type prime_suspect/1 which keeps track of the
	  evidence (oracle answers) which implicates a particular
	  edt node.
	- Use the new bug representation.

browser/declarative_debugger.m:
browser/declarative_oracle.m:
	- Move oracle_confirmation to declarative_debugger.m, and
	  rename it decl_confirmation.

browser/declarative_debugger.m:
	- Change decl_bug/1 to decl_bug/0.  Instead of a bug being
	  represented by an edt node, decl_bug now represents
	  directly the different sorts of bugs possible.
	- Add an implementation for the new mercury_edt method.

browser/declarative_oracle.m:
browser/declarative_user.m:
	- Update the confirmation to use the new types.

tests/debugger/declarative/*.{exp,exp2}:
	- Update test results.
2000-03-01 04:17:27 +00:00

277 lines
8.5 KiB
Mathematica

%-----------------------------------------------------------------------------%
% Copyright (C) 1999-2000 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 list, io.
% A response that the oracle gives to a query about the
% truth of an EDT node.
%
:- type oracle_response
---> oracle_answers(list(decl_answer))
; no_oracle_answers
; 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, io__output_stream, oracle_state).
:- mode oracle_state_init(in, in, 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), oracle_response, oracle_state,
oracle_state, io__state, io__state).
:- mode query_oracle(in, out, in, out, di, uo) is det.
% Confirm that the node found is indeed an e_bug or an i_bug.
%
:- pred oracle_confirm_bug(decl_bug, decl_confirmation, oracle_state,
oracle_state, io__state, io__state).
:- mode oracle_confirm_bug(in, out, in, out, di, uo) is det.
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module mdb__declarative_user, mdb__util.
:- import_module bool, std_util, map, set, require.
query_oracle(Queries, Response, Oracle0, Oracle) -->
{ get_oracle_kb(Oracle0, KB0) },
{ list__filter_map(query_oracle_kb(KB0), Queries, Answers) },
(
{ Answers = [] }
->
{ get_oracle_user(Oracle0, User0) },
query_user(Queries, UserResponse, User0, User),
{
UserResponse = user_answer(Answer),
assert_oracle_kb(Answer, KB0, KB),
Response = oracle_answers([Answer])
;
UserResponse = no_user_answer,
Response = no_oracle_answers,
KB = KB0
;
UserResponse = abort_diagnosis,
Response = abort_diagnosis,
KB = KB0
},
{ set_oracle_kb(Oracle0, KB, Oracle1) },
{ set_oracle_user(Oracle1, User, Oracle) }
;
{ Response = oracle_answers(Answers) },
{ Oracle = Oracle0 }
).
oracle_confirm_bug(Bug, Confirmation, Oracle0, Oracle) -->
{ get_oracle_user(Oracle0, User0) },
user_confirm_bug(Bug, Confirmation, User0, User),
{ set_oracle_user(Oracle0, User, Oracle) }.
%-----------------------------------------------------------------------------%
:- type oracle_state
---> oracle(
oracle_kb, % Knowledge base.
user_state % User interface.
).
oracle_state_init(InStr, OutStr, Oracle) :-
user_state_init(InStr, OutStr, User),
oracle_kb_init(KB),
Oracle = oracle(KB, User).
:- pred get_oracle_kb(oracle_state, oracle_kb).
:- mode get_oracle_kb(in, out) is det.
get_oracle_kb(oracle(KB, _), KB).
:- pred set_oracle_kb(oracle_state, oracle_kb, oracle_state).
:- mode set_oracle_kb(in, in, out) is det.
set_oracle_kb(oracle(_, UI), KB, oracle(KB, UI)).
:- pred get_oracle_user(oracle_state, user_state).
:- mode get_oracle_user(in, out) is det.
get_oracle_user(oracle(_, UI), UI).
:- pred set_oracle_user(oracle_state, user_state, oracle_state).
:- mode set_oracle_user(in, in, out) is det.
set_oracle_user(oracle(KB, _), UI, oracle(KB, UI)).
%-----------------------------------------------------------------------------%
%
% 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.
%
map(decl_atom, decl_truth),
% Mapping from call atoms to their solution sets.
% The sets in this map are all complete---but they may
% contain wrong answers.
%
map(decl_atom, set(decl_atom)),
% Mapping from call atoms to their solution sets.
% The sets in this map are all incomplete---there
% exists a correct solution which is not in the set.
%
map(decl_atom, set(decl_atom))
).
:- pred oracle_kb_init(oracle_kb).
:- mode oracle_kb_init(out) is det.
oracle_kb_init(oracle_kb(G, Y, N)) :-
map__init(G),
map__init(Y),
map__init(N).
:- pred get_kb_ground_map(oracle_kb, map(decl_atom, decl_truth)).
:- mode get_kb_ground_map(in, out) is det.
get_kb_ground_map(oracle_kb(Map, _, _), Map).
:- pred set_kb_ground_map(oracle_kb, map(decl_atom, decl_truth), oracle_kb).
:- mode set_kb_ground_map(in, in, out) is det.
set_kb_ground_map(oracle_kb(_, Y, N), G, oracle_kb(G, Y, N)).
:- pred get_kb_complete_map(oracle_kb, map(decl_atom, set(decl_atom))).
:- mode get_kb_complete_map(in, out) is det.
get_kb_complete_map(oracle_kb(_, Map, _), Map).
:- pred set_kb_complete_map(oracle_kb, map(decl_atom, set(decl_atom)),
oracle_kb).
:- mode set_kb_complete_map(in, in, out) is det.
set_kb_complete_map(oracle_kb(G, _, N), Y, oracle_kb(G, Y, N)).
:- pred get_kb_incomplete_map(oracle_kb, map(decl_atom, set(decl_atom))).
:- mode get_kb_incomplete_map(in, out) is det.
get_kb_incomplete_map(oracle_kb(_, _, Map), Map).
:- pred set_kb_incomplete_map(oracle_kb, map(decl_atom, set(decl_atom)),
oracle_kb).
:- mode set_kb_incomplete_map(in, in, out) is det.
set_kb_incomplete_map(oracle_kb(G, Y, _), N, oracle_kb(G, Y, N)).
%-----------------------------------------------------------------------------%
:- pred query_oracle_kb(oracle_kb, decl_question, decl_answer).
:- mode query_oracle_kb(in, in, out) is semidet.
query_oracle_kb(KB, Node, Node - Truth) :-
Node = wrong_answer(Atom),
get_kb_ground_map(KB, Map),
map__search(Map, Atom, Truth).
query_oracle_kb(KB, Node, Node - Truth) :-
Node = missing_answer(Call, Solns),
set__list_to_set(Solns, Ss),
get_kb_complete_map(KB, CMap),
(
map__search(CMap, Call, CSs),
set__subset(CSs, Ss)
->
Truth = yes
;
get_kb_incomplete_map(KB, IMap),
map__search(IMap, Call, ISs),
set__subset(Ss, ISs),
Truth = 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_answer, oracle_kb, oracle_kb).
:- mode assert_oracle_kb(in, in, out) is det.
assert_oracle_kb(wrong_answer(Atom) - Truth, KB0, KB) :-
get_kb_ground_map(KB0, Map0),
map__det_insert(Map0, Atom, Truth, Map),
set_kb_ground_map(KB0, Map, KB).
assert_oracle_kb(missing_answer(Call, Solns) - yes, KB0, KB) :-
get_kb_complete_map(KB0, Map0),
set__list_to_set(Solns, Ss0),
(
map__search(Map0, Call, OldSs)
->
% The sets are both complete, so their
% intersection must be also.
%
set__intersect(OldSs, Ss0, Ss),
map__set(Map0, Call, Ss, Map)
;
map__det_insert(Map0, Call, Ss0, Map)
),
set_kb_complete_map(KB0, Map, KB).
assert_oracle_kb(missing_answer(Call, Solns) - no, KB0, KB) :-
get_kb_incomplete_map(KB0, Map0),
set__list_to_set(Solns, Ss),
%
% XXX should also keep the old incomplete set around, too.
% It can still give us information that the new one can't.
%
map__set(Map0, Call, Ss, Map),
set_kb_incomplete_map(KB0, Map, KB).