[m-dev.] for review: clean up declarative debugger

Mark Anthony BROWN dougl at cs.mu.OZ.AU
Mon Apr 26 03:29:33 AEST 1999


Lee Naish writes:
> Mark Anthony BROWN <dougl at cs.mu.OZ.AU> writes:
> 
> >+		** MR_edt_{min,max}_depth.  These events are either
> >+		** irrelevant, or else implicitly represented in the
> >+		** structure being built.
> 
> Additional comments somewhere would be nice (eg this implicit
> representation).  Maybe they existed already and are not in the diff?

Some comments existed already.  I have improved them, and added a
reference to them from here.

> 
> As above.  More comments would be nice...

Ok.

> 
> A possible answer to the objection above is that edt_first_child etc are
> just the implementation of a well defined ADT.  That would be a good
> answer if the ADT was properly documented and the interface and
> implementation were properly separated.  However, edt_children and
> edt_root (a nice abstract interface) are defined at the same level as
> edt_first_child and there are not even any comments on these procedures.

The ADT is now clearly defined by a typeclass.

> 
> > :- pred query_oracle(edt_node, bool, io__state, io__state).
> > :- mode query_oracle(in, out, di, uo) is det.
> 
> Its worth saying that eventually an oracle database will be passed
> around.  In fact I think its worth defining the right interfaces which
> allow for this even if its not currently used (you can pass around a
> dummy database which is never actually used - then future version will
> be just changing the implementation of predicates with well defined
> interfaces.  You will probably have to add an extra couple of arguments
> to a bunch of preds, but the basic structure should remain the same (the
> use of io__state has forced you to use the "right" structure anyway).
> 

Ok, I now thread a dummy around, but I don't (yet) save it from one call
to the next.

> I think the use of type bool here will have to be changed eventually
> also.  We may want to implement the three valued scheme and/or "don't
> know" answers (see below).  I think introducing a new type, eg
> edt_truth, is warranted.

Good idea.

> 
> > :- type debugger_command
> >	--->	yes
> >	;	no
> >	;	browse
> >	;	tree
> >	;	help
> >	;	unknown.
> 
> I suggest unknown be replaced by invalid/unrecognised/...
> Eventually it would be nice for users to be able to respond "don't know"
> to questions and then "unknown" will get confusing.

Good point.

The following diff addresses the above concerns.

Cheers,
Mark.

Estimated hours taken: 12

Clean up the declarative debugger.

browser/declarative_debugger.m:
browser/declarative_oracle.m:
	- Add a new type, oracle_data, to store the oracle database.
	- Thread an oracle database through a single call to the
	  front end.
	- Replace `unknown' with a less overloaded name in
	  some type definitions.

trace/mercury_trace_declarative.c:
trace/mercury_trace_declarative.h:
browser/declarative_debugger.m:
	- Improve documentation.

browser/declarative_debugger.m:
	- Export new type, edt_truth.
	- Improve the interface to the oracle.
	- Add a new typeclass, evaluation_tree, and update the
	  previous interface to reflect this.
	- Make declarative_bug a polymorphic type, and change the
	  constructor for buggy nodes from `wrong' to `e_bug'.

Index: browser/declarative_debugger.m
===================================================================
RCS file: /home/mercury1/repository/mercury/browser/declarative_debugger.m,v
retrieving revision 1.3
diff -u -r1.3 declarative_debugger.m
--- declarative_debugger.m	1999/04/16 03:07:39	1.3
+++ declarative_debugger.m	1999/04/25 17:04:00
@@ -5,23 +5,42 @@
 %-----------------------------------------------------------------------------%
 % File: declarative_debugger.m
 % Author: Mark Brown
-% Purpose:
-%	This module is the front end of a Mercury declarative debugger.
-% It is called by the back end, in trace/mercury_trace_declarative.c, and
-% is passed an evaluation dependency tree (EDT).  It then analyses this to
-% diagnose a bug.
 %
-% The implementation is in two sections:
-%	- Mercury interface to the main data structure.
-%	- Implementation of the analysis algorithm.
+% This module has two main purposes:
+% 	- to define the interface between the front and back ends of
+% 	  a Mercury declarative debugger, and
+% 	- to implement a front end.
+%
+% The interface between the front and back ends is partly defined
+% by the evaluation_tree typeclass.  An instance of this typeclass
+% implements evaluation dependency trees (EDTs), which are created
+% in the back end and passed to the front end for analysis.  The rest
+% of the interface is via analyse_edt/7, which is how the front end
+% is called from the back end.
+%
+% The front end implemented in this module analyses the EDT it is
+% passed to diagnose a bug.  It does this by a simple top-down search.
 %
 
 :- module declarative_debugger.
 :- interface.
-:- import_module io, list, string, std_util.
+:- import_module io, list, string, std_util, bool.
+:- import_module declarative_oracle.
 
-:- type evaluation_tree.
+	%
+	% This type represents the possible truth values for nodes
+	% in the EDT.
+	%
+:- type edt_truth == bool.
 
+	%
+	% Values of this type represent EDT nodes.  This representation
+	% is used by the front end (in this module), as well as the
+	% oracle (in browser/declarative_oracle.m).
+	%
+	% There will be nodes other than wrong_answer in future, such
+	% as for missing answer analysis.
+	%
 :- type edt_node
 			%
 			% The node is a possible wrong answer.  The first
@@ -30,57 +49,67 @@
 			%
 	--->	wrong_answer(string, list(univ)).
 
-
 	%
-	% This procedure is exported to C to be called from the back
-	% end of the declarative debugger.
+	% Display the node in user readable form on the current
+	% output stream.
 	%
-:- pred analyse_edt(evaluation_tree, io__input_stream, io__output_stream,
-		io__state, io__state).
-:- mode analyse_edt(in, in, in, di, uo) is det.
-
+:- pred write_node(edt_node, io__state, io__state).
+:- mode write_node(in, di, uo) is det.
 
 	%
-	% This prints the atom to the current output stream.
+	% See comments above.
 	%
-:- pred write_atom(string, list(univ), io__state, io__state).
-:- mode write_atom(in, in, di, uo) is det.
+:- typeclass evaluation_tree(Tree) where [
+	pred edt_root(Tree, edt_node),
+	mode edt_root(in, out) is det,
+
+	pred edt_children(Tree, list(Tree)),
+	mode edt_children(in, out) is det
+].
+
+:- pred analyse_edt(T, io__input_stream, io__output_stream, oracle_data,
+		oracle_data, io__state, io__state) <= evaluation_tree(T).
+:- mode analyse_edt(in, in, in, in, out, di, uo) is det.
 
 %-----------------------------------------------------------------------------%
 
 :- implementation.
-:- import_module bool, require, int, char.
-:- import_module declarative_oracle.
+:- import_module require, int, char.
 
 	%
-	% This section contains the Mercury interface to the EDTs that
-	% are built by the back end.
+	% This section defines the Mercury instance of the evaluation
+	% tree.
 	%
 
-:- type evaluation_tree == c_pointer.
+:- instance evaluation_tree(mercury_edt) where [
+	pred(edt_root/2) is mercury_edt_root,
+	pred(edt_children/2) is mercury_edt_children
+].
+
+:- type mercury_edt == c_pointer.
 
-:- pred edt_children(evaluation_tree, list(evaluation_tree)).
-:- mode edt_children(in, out) is det.
+:- pred mercury_edt_children(mercury_edt, list(mercury_edt)).
+:- mode mercury_edt_children(in, out) is det.
 
-edt_children(EDT, Children) :-
+mercury_edt_children(EDT, Children) :-
 	(
-		edt_first_child(EDT, FirstChild)
+		mercury_edt_first_child(EDT, FirstChild)
 	->
-		edt_children_2(FirstChild, Children0),
+		mercury_edt_children_2(FirstChild, Children0),
 		Children = [FirstChild | Children0]
 	;
 		Children = []
 	).
 
 
-:- pred edt_children_2(evaluation_tree, list(evaluation_tree)).
-:- mode edt_children_2(in, out) is det.
+:- pred mercury_edt_children_2(mercury_edt, list(mercury_edt)).
+:- mode mercury_edt_children_2(in, out) is det.
 
-edt_children_2(Child, Siblings) :-
+mercury_edt_children_2(Child, Siblings) :-
 	(
-		edt_sibling(Child, Sibling)
+		mercury_edt_sibling(Child, Sibling)
 	->
-		edt_children_2(Sibling, Siblings0),
+		mercury_edt_children_2(Sibling, Siblings0),
 		Siblings = [Sibling | Siblings0]
 	;
 		Siblings = []
@@ -92,10 +121,10 @@
 	#include ""mercury_wrapper.h""
 ").
 
-:- pred edt_first_child(evaluation_tree, evaluation_tree).
-:- mode edt_first_child(in, out) is semidet.
+:- pred mercury_edt_first_child(mercury_edt, mercury_edt).
+:- mode mercury_edt_first_child(in, out) is semidet.
 
-:- pragma c_code(edt_first_child(Parent::in, Child::out),
+:- pragma c_code(mercury_edt_first_child(Parent::in, Child::out),
 	[will_not_call_mercury],
 	"
 		MR_Edt_Node	*parent;
@@ -112,10 +141,10 @@
 	"
 ).
 
-:- pred edt_sibling(evaluation_tree, evaluation_tree).
-:- mode edt_sibling(in, out) is semidet.
+:- pred mercury_edt_sibling(mercury_edt, mercury_edt).
+:- mode mercury_edt_sibling(in, out) is semidet.
 
-:- pragma c_code(edt_sibling(Child::in, Sibling::out),
+:- pragma c_code(mercury_edt_sibling(Child::in, Sibling::out),
 	[will_not_call_mercury],
 	"
 		MR_Edt_Node	*child;
@@ -132,10 +161,10 @@
 	"
 ).
 
-:- pred edt_root(evaluation_tree, edt_node).
-:- mode edt_root(in, out) is det.
+:- pred mercury_edt_root(mercury_edt, edt_node).
+:- mode mercury_edt_root(in, out) is det.
 
-:- pragma c_code(edt_root(EDT::in, Root::out),
+:- pragma c_code(mercury_edt_root(EDT::in, Root::out),
 	[will_not_call_mercury],
 	"
 		#ifdef MR_USE_DECLARATIVE_DEBUGGER
@@ -169,25 +198,49 @@
 	% This is what the analysis can currently find.
 	%
 
-:- type declarative_bug
-	--->	unknown
-	;	wrong(evaluation_tree).
+:- type declarative_bug(T)		% <= evaluation_tree(T)
+	--->	not_found
+			%
+			% An EDT whose root node is erroneous but
+			% all children are correct.
+			%
+	;	e_bug(T).
 
 
-:- pragma export(declarative_debugger__analyse_edt(in, in, in, di, uo),
+	%
+	% To simplify calling this module from C code, we export
+	% a version of analyse_edt which is specifically for the instance
+	% used by the current back end.
+	%
+:- pred analyse_mercury_edt(mercury_edt, io__input_stream, io__output_stream,
+		io__state, io__state).
+:- mode analyse_mercury_edt(in, in, in, di, uo) is det.
+
+:- pragma export(declarative_debugger__analyse_mercury_edt(in, in, in, di, uo),
 		"ML_DD_analyse_edt").
 
-analyse_edt(EDT, MdbIn, MdbOut) -->
+analyse_mercury_edt(EDT, MdbIn, MdbOut) -->
+		%
+		% XXX this data structure needs to be more
+		% persistent.  It really should be saved between
+		% calls to this predicate.
+		%
+	{ oracle_data_init(Oracle0) },
+	analyse_edt(EDT, MdbIn, MdbOut, Oracle0, _).
+
+
+analyse_edt(EDT, MdbIn, MdbOut, Oracle0, Oracle) -->
 	io__set_input_stream(MdbIn, OldIn),
 	io__set_output_stream(MdbOut, OldOut),
 	{ edt_root(EDT, RootNode) },
-	query_oracle(RootNode, Valid),
+	query_oracle(RootNode, Valid, Oracle0, Oracle1),
 	(
 		{ Valid = yes },
-		{ Bug = unknown }
+		{ Bug = not_found },
+		{ Oracle = Oracle1 }
 	;
 		{ Valid = no },
-		analyse_edt_2(EDT, Bug)
+		analyse_edt_2(EDT, Bug, Oracle1, Oracle)
 	),
 	report_bug(Bug),
 	io__set_input_stream(OldIn, _),
@@ -197,40 +250,43 @@
 	%
 	% Assumes the root note is not valid.
 	%
-:- pred analyse_edt_2(evaluation_tree, declarative_bug, io__state, io__state).
-:- mode analyse_edt_2(in, out, di, uo) is det.
+:- pred analyse_edt_2(T, declarative_bug(T), oracle_data, oracle_data,
+		io__state, io__state) <= evaluation_tree(T).
+:- mode analyse_edt_2(in, out, in, out, di, uo) is det.
 
-analyse_edt_2(EDT, Bug) -->
+analyse_edt_2(EDT, Bug, Oracle0, Oracle) -->
 	{ edt_children(EDT, Children) },
-	analyse_children(Children, wrong(EDT), Bug).
+	analyse_children(Children, e_bug(EDT), Bug, Oracle0, Oracle).
 
 
-:- pred analyse_children(list(evaluation_tree), declarative_bug,
-		declarative_bug, io__state, io__state).
-:- mode analyse_children(in, in, out, di, uo) is det.
+:- pred analyse_children(list(T), declarative_bug(T), declarative_bug(T),
+		oracle_data, oracle_data, io__state, io__state)
+				<= evaluation_tree(T).
+:- mode analyse_children(in, in, out, in, out, di, uo) is det.
 
-analyse_children([], Bug, Bug) -->
+analyse_children([], Bug, Bug, Oracle, Oracle) -->
 	[].
-analyse_children([Child | Children], Bug0, Bug) -->
+analyse_children([Child | Children], Bug0, Bug, Oracle0, Oracle) -->
 	{ edt_root(Child, ChildNode) },
-	query_oracle(ChildNode, Valid),
+	query_oracle(ChildNode, Valid, Oracle0, Oracle1),
 	(
 		{ Valid = yes },
-		analyse_children(Children, Bug0, Bug)
+		analyse_children(Children, Bug0, Bug, Oracle1, Oracle)
 	;
 		{ Valid = no },
-		analyse_edt_2(Child, Bug)
+		analyse_edt_2(Child, Bug, Oracle1, Oracle)
 	).
 
 
-:- pred report_bug(declarative_bug, io__state, io__state).
+:- pred report_bug(declarative_bug(T), io__state, io__state)
+		<= evaluation_tree(T).
 :- mode report_bug(in, di, uo) is det.
 
-report_bug(unknown) -->
+report_bug(not_found) -->
 	io__write_string("Bug not found.\n").
-report_bug(wrong(EDT)) -->
+report_bug(e_bug(EDT)) -->
 	io__write_string("Incorrect instance found:\n\n"),
-	write_root_atom(EDT),
+	write_root_node(EDT),
 	{ edt_children(EDT, Children0) },
 	(
 		{ Children0 = [Child | Children1] }
@@ -239,39 +295,35 @@
 		{ list__reverse(Children1, Children) },
 		write_children(Children),
 		io__write_char('\t'),
-		write_root_atom(Child)
+		write_root_node(Child)
 	;
 		[]
 	),
 	io__write_string(".\n\n").
 
 
-:- pred write_children(list(evaluation_tree), io__state, io__state).
+:- pred write_children(list(T), io__state, io__state) <= evaluation_tree(T).
 :- mode write_children(in, di, uo) is det.
 
 write_children([]) -->
 	[].
 write_children([Child | Children]) -->
 	io__write_char('\t'),
-	write_root_atom(Child),
+	write_root_node(Child),
 	io__write_string(",\n"),
 	write_children(Children).
 
 
-:- pred write_root_atom(evaluation_tree, io__state, io__state).
-:- mode write_root_atom(in, di, uo) is det.
+:- pred write_root_node(T, io__state, io__state) <= evaluation_tree(T).
+:- mode write_root_node(in, di, uo) is det.
 
-write_root_atom(EDT) -->
+write_root_node(EDT) -->
 	{ edt_root(EDT, RootNode) },
-	{
-		RootNode = wrong_answer(Name0, Args0),
-		Name = Name0,
-		Args = Args0
-	},
-	write_atom(Name, Args).
+	write_node(RootNode).
 
 
-write_atom(Name, Args) -->
+write_node(Node) -->
+	{ Node = wrong_answer(Name, Args) },
 	io__write_string(Name),
 	(
 		{ Args = [Arg1 | Args0] }
Index: browser/declarative_oracle.m
===================================================================
RCS file: /home/mercury1/repository/mercury/browser/declarative_oracle.m,v
retrieving revision 1.1
diff -u -r1.1 declarative_oracle.m
--- declarative_oracle.m	1999/04/12 04:11:42	1.1
+++ declarative_oracle.m	1999/04/25 17:04:00
@@ -18,17 +18,32 @@
 :- import_module declarative_debugger.
 
 	%
+	% The oracle database.  This is threaded around the declarative
+	% debugger, but currently stores no information.
+	%
+:- type oracle_data.
+
+	%
 	% Query the oracle about the program being debugged.  The first
 	% argument is a node in the evaluation tree, the second argument
-	% is its validity in the intended interpreation.
+	% is its validity in the intended interpretation.
+	%
+:- pred query_oracle(edt_node, edt_truth, oracle_data, oracle_data,
+		io__state, io__state).
+:- mode query_oracle(in, out, in, out, di, uo) is det.
+
+	%
+	% Produce a new oracle state.
 	%
-:- pred query_oracle(edt_node, bool, io__state, io__state).
-:- mode query_oracle(in, out, di, uo) is det.
+:- pred oracle_data_init(oracle_data).
+:- mode oracle_data_init(out) is det.
 
 %-----------------------------------------------------------------------------%
 
 :- implementation.
-:- import_module list, char, require.
+:- import_module list, char, require, std_util.
+
+:- type oracle_data == unit.
 
 :- type debugger_command
 	--->	yes
@@ -36,27 +51,32 @@
 	;	browse
 	;	tree
 	;	help
-	;	unknown.
+	;	illegal_command.
 
 
-query_oracle(Node, Valid) -->
-	write_node(Node),
+oracle_data_init(unit).
+
+
+query_oracle(Node, Valid, Oracle0, Oracle) -->
+	query_user(Node),
 	io__flush_output,
 	get_command(Answer),
 	(
 		{ Answer = yes },
-		{ Valid = yes }
+		{ Valid = yes },
+		{ Oracle = Oracle0 }
 	;
 		{ Answer = no },
-		{ Valid = no }
+		{ Valid = no },
+		{ Oracle = Oracle0 }
 	;
 		{ Answer = browse },
 		io__write_string("Sorry, not implemented.\n"),
-		query_oracle(Node, Valid)
+		query_oracle(Node, Valid, Oracle0, Oracle)
 	;
 		{ Answer = tree },
 		io__write_string("Sorry, not implemented.\n"),
-		query_oracle(Node, Valid)
+		query_oracle(Node, Valid, Oracle0, Oracle)
 	;
 		{ Answer = help },
 		io__write_strings([
@@ -68,19 +88,19 @@
 %			"\tt\tprint the evaluation tree (not yet)\n",
 			"\th, ?\tthis help message\n"
 		]),
-		query_oracle(Node, Valid)
+		query_oracle(Node, Valid, Oracle0, Oracle)
 	;
-		{ Answer = unknown },
+		{ Answer = illegal_command },
 		io__write_string("Unknown command, 'h' for help.\n"),
-		query_oracle(Node, Valid)
+		query_oracle(Node, Valid, Oracle0, Oracle)
 	).
 
 
-:- pred write_node(edt_node, io__state, io__state).
-:- mode write_node(in, di, uo) is det.
+:- pred query_user(edt_node, io__state, io__state).
+:- mode query_user(in, di, uo) is det.
 
-write_node(wrong_answer(Name, Args)) -->
-	write_atom(Name, Args),
+query_user(Node) -->
+	write_node(Node),
 	io__nl,
 	io__write_string("Valid? ").
 
@@ -98,11 +118,11 @@
 		->
 			Command = Command0
 		;
-			Command = unknown
+			Command = illegal_command
 		)
 	;
 		% XXX this should definitely be handled better.
-		error("I/O error or EOF.\n")
+		Command = illegal_command
 	}.
 
 
Index: trace/mercury_trace_declarative.c
===================================================================
RCS file: /home/mercury1/repository/mercury/trace/mercury_trace_declarative.c,v
retrieving revision 1.6
diff -u -r1.6 mercury_trace_declarative.c
--- mercury_trace_declarative.c	1999/04/20 12:38:40	1.6
+++ mercury_trace_declarative.c	1999/04/25 17:04:40
@@ -13,6 +13,18 @@
 ** Tree (EDT).  Once built, the EDT is passed to the front end where it can
 ** be analysed to find bugs.  The front end is implemented in
 ** browse/declarative_debugger.m.
+**
+** The interface between the front and back ends is via the
+** evaluation_tree typeclass, which is documented in
+** browse/declarative_debugger.m.  It would be possible to replace
+** the front end or the back end with an alternative implementation
+** which also conforms to the typeclass constraints.  For example:
+** 	- An alternative back end could generate the same EDT
+** 	  structure in a different way, such as via program
+** 	  transformation.
+** 	- An alternative front end could graphically display the
+** 	  generated EDTs as part of a visualization tool rather
+** 	  than analyzing them for bugs.
 */
 
 #include "mercury_imp.h"
@@ -132,7 +144,8 @@
 		** out events with a depth outside the range given by
 		** MR_edt_{min,max}_depth.  These events are either
 		** irrelevant, or else implicitly represented in the
-		** structure being built.
+		** structure being built.  See comment in
+		** trace/mercury_trace_declarative.h.
 		*/
 		return NULL;
 	}
Index: trace/mercury_trace_declarative.h
===================================================================
RCS file: /home/mercury1/repository/mercury/trace/mercury_trace_declarative.h,v
retrieving revision 1.3
diff -u -r1.3 mercury_trace_declarative.h
--- mercury_trace_declarative.h	1999/04/12 04:11:47	1.3
+++ mercury_trace_declarative.h	1999/04/25 17:04:42
@@ -10,8 +10,9 @@
 /*
 ** This file defines the MR_Edt_Node data type, which stores nodes
 ** of an Evaluation Dependency Tree (EDT), used for declarative
-** debugging.  It also defines an interface to the back end of the
-** declarative debugger from the internal debugger.
+** debugging.  This is the underlying implementation of mercury_edt,
+** which is an instance of the evaluation_tree typeclass.  These
+** are defined in browser/declarative_debugger.m.
 */
 
 #include "mercury_imp.h"
@@ -24,8 +25,16 @@
 ** represented nodes.
 **
 ** Implicit nodes are similar to explicit nodes, but they do not
-** store their children.  The children can be created by re-executing
-** the events in the stored range and collecting a new EDT.
+** store their children.  They do, however, store enough information
+** to allow execution to be resumed at that point, so children can
+** be created by re-executing the events in the stored range and
+** collecting a new EDT.  XXX this is not yet implemented, though.
+**
+** In the future there will also be nodes to handle:
+** 	- missing answer analysis
+** 	- calls to solutions/2 and related predicates
+** 	- exceptions
+** and possibly other things as well.
 */
 
 typedef enum {

-- 
Mark Brown  (dougl at cs.mu.oz.au)       )O+   |  For Microsoft to win,
MEngSc student,                             |  the customer must lose
Dept of Computer Science, Melbourne Uni     |          -- Eric S. Raymond
--------------------------------------------------------------------------
mercury-developers mailing list
Post messages to:       mercury-developers at cs.mu.oz.au
Administrative Queries: owner-mercury-developers at cs.mu.oz.au
Subscriptions:          mercury-developers-request at cs.mu.oz.au
--------------------------------------------------------------------------



More information about the developers mailing list