For review: Improve debugging printout under Prolog
Peter Schachte
pets at cs.mu.oz.au
Mon Nov 3 17:39:40 AEDT 1997
Improve debugging printout under Prolog
Fixed a few bugs since last time. Thanks to Fergus and Lee for
spotting them.
Estimated hours taken: 18
library/portray.nl:
You can now control the depth limit, and separately the limit
on the lengths of lists and the number of arguments shown for
high-arity terms. You can also "turn off" certain arguments
of certain terms, based on the functor of the term, or even
turn off the whole term. Finally, you can define your own
print methods for terms if you like, so lists of character
codes can be printed as double-quoted strings, and maps can be
printed out as associations. Both of these are provided, and
serve as examples.
doc/user_guide.texi:
Documented all the above.
Index: library/portray.nl
===================================================================
RCS file: /home/staff/zs/imp/mercury/library/portray.nl,v
retrieving revision 1.16
diff -u -r1.16 portray.nl
--- portray.nl 1997/07/27 15:07:02 1.16
+++ portray.nl 1997/11/03 06:22:26
@@ -1,74 +1,532 @@
-%-----------------------------------------------------------------------------%
+-----------------------------------------------------------------------------%
% Copyright (C) 1993-1997 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: portray.nl
-% Main author: fjh.
+% Main authors: fjh, pets.
%
-% This file contains a definition for portray/1 that only
-% displays the top-level functors of big terms.
-% This is useful for debugging, since otherwise the NU-Prolog
-% debugger prints out screenfuls of crap as you step through
-% a program. It also contains a definition for spyHook/2 that displays
-% the term in full, so that you can get the full details by
-% typing "|" in the debugger.
-% Note that you need to compile it to a .no file -
-% loading it as a .np file doesn't work.
+% This file defines portray/1 (and thereby defines print/1) to print terms in
+% a controlled way, so you don't wind up having to wade through several
+% screenfuls of stuff to find the bit you're interested in. Note that query
+% results and debugging state are printed out using print/1. You can control
+% the depth limit, and separately the limit on the lengths of lists and the
+% number of arguments shown for high-arity terms. You can also "turn off"
+% certain arguments of certain terms, based on the functor of the term, or
+% even turn off the whole term. Finally, you can define your own print
+% methods for terms if you like, so lists of character codes can be printed as
+% double-quoted strings, and maps can be printed out as associations. Both of
+% these are provided as examples.
+%
+% This file also contains a definition for spyHook/2 that displays the term in
+% full, so that you can get the full details by typing "|" in the debugger.
+% Note that you need to compile it to a .no file - loading it as a .nl file
+% doesn't work.
%
%-----------------------------------------------------------------------------%
-historyLength(500).
+:- dynamic print_depth/1.
+:- dynamic print_length/1.
+:- dynamic hidden/3.
+
+% Make the standard Prolog print/1 call my print_term/1.
portray(Term) :-
!,
- myportray(Term).
+ print_term(Term).
-myportray(Term) :-
- ( compound(Term), list_of_char_codes(Term) ->
- format("""~s""", [Term])
- ; compound(Term) ->
- Term =.. [F|Args],
- write(F),
- write('('),
- portray_args(Args),
- write(')')
+
+% print_depth(-Depth)
+% The current print depth limit is Depth. If the depth limit is less than 0,
+% the print depth is unlimited.
+
+print_depth(2).
+
+
+% set_print_depth(+Depth)
+% Set the print depth limit to Depth. If the depth limit is less than 0,
+% the print depth is unlimited.
+
+set_print_depth(Depth) :-
+ retractall(print_depth(_)),
+ assert(print_depth(Depth)).
+
+
+% print_length(-Length)
+% The current print length limit is Length. The length limit controls the
+% display of long lists and terms with many arguments. If the length limit
+% is less than 0, the print length is unlimited.
+
+print_length(10).
+
+
+% set_print_length(+Length)
+% Set the print length limit to Length. If the length limit is less than 0,
+% the print length is unlimited.
+
+set_print_length(New) :-
+ retractall(print_length(_)),
+ assert(print_length(New)).
+
+
+% hide(+Spec)
+% hide(+Spec, +Args)
+% This predicate allows you to elide certain arguments of certain terms, or
+% whole terms, based on the functor. Spec specifies a functor; it must be
+% either a Name/Arity, or just a functor name, in which case all arities are
+% specified. Args specifies which arguments of such terms should be hidden.
+% Hidden terms are printed as just a hash (#) character. Args, if given,
+% must be a list of integers specifying argument numbers to hide;
+% alternatively, it may be a single argument number to specify a single
+% argument to hide, or it may be the atom 'term', in which case only the
+% name and arity of the term are written within angle brackets, (e.g.,
+% <foo/3>). If Args is not specified, it defaults to 'term'.
+
+hide(Spec, Arg) :-
+ Predspec = "hide/2",
+ term_spec(Spec, Name, Arity, Predspec),
+ argument_spec(Arg, Args, Predspec),
+ hide_term_args(Name, Arity, Args).
+
+hide(Spec) :-
+ term_spec(Spec, Name, Arity, "hide/1"),
+ hide_term_args(Name, Arity, term).
+
+
+hide_term_args(Name, Arity, Args) :-
+ ( nonvar(Arity) ->
+ % handle case of specific arity
+ ( clause(hidden(Name,Arity0,OldHidden),_),
+ nonvar(Arity0),
+ Arity0 == Arity ->
+ % there was a specific entry for this arity
+ retract((hidden(Name, Arity0, _):-_))
+ ;
+ clause(hidden(Name,Arity0,OldHidden), _),
+ var(Arity0) ->
+ % there was a catch-all case
+ true
+ ;
+ % there was no case for this arity at all
+ OldHidden = []
+ ),
+ add_to_hidden_list(Args, OldHidden, NewHidden),
+ asserta((hidden(Name, Arity, NewHidden) :- !))
+ ; Args == term ->
+ % simple case: wants to hide all args for all arities
+ retractall(hidden(Name, _, _)),
+ assertz((hidden(Name, Arity, Args) :- !))
;
- write(Term)
+ % the hard case: add Args to set of args to hide for all
+ % arities. First we backtrack over all entries for any arity
+ % replacing them with entries for the same arities, but with
+ % the new arguments added....
+ retract((hidden(Name, Arity0, OldHidden):-_)),
+ add_to_hidden_list(Args, OldHidden, NewHidden),
+ assertz((hidden(Name, Arity0, NewHidden) :- !)),
+ fail
+ ;
+ % ... and then if there was a catch-all, we're done ...
+ clause(hidden(Name,Arity0,_), _),
+ var(Arity0) ->
+ true
+ ;
+ % ... if not, we add a new catch-all.
+ sort(Args, SortedArgs),
+ assertz((hidden(Name, _, SortedArgs) :- !))
+ ).
+
+
+add_to_hidden_list(term, _, term) :-
+ !.
+add_to_hidden_list(Hidden1, Hidden2, Hidden) :-
+ ( Hidden2 == term ->
+ Hidden = term
+ ; append(Hidden1, Hidden2, Hidden3),
+ sort(Hidden3, Hidden)
+ ).
+
+
+
+% show(+Spec, +Args)
+% show(+Spec)
+% show
+% Undo the effect of hiding argumments of some terms. If Spec is given, stop
+% hiding the indicated arguments of the specified terms. Spec is exactly as
+% for hide/2. Args specifies which arguments to stop hiding; it must be a
+% list of argument numbers, or a single argument number, or the atom 'term',
+% indicating that no arguments should be hidden. Args defaults to 'term'.
+% If Spec is not given, no arguments of any term will be hidden.
+%
+% Note that these predicates will not affect the depth limit of printing.
+% Even if you ask to show a term in full, if it appears at the depth limit of
+% the term being printed, its arguments will not be shown. If it appears
+% below the depth limit, you won't see it at all.
+
+show(Spec, Args0) :-
+ Predspec = "show/2",
+ term_spec(Spec, Name, Arity, Predspec),
+ argument_spec(Args0, Args, Predspec),
+ show_term_args(Name, Arity, Args).
+
+show(Spec) :-
+ term_spec(Spec, Name, Arity, "show/1"),
+ show_term_args(Name, Arity, term).
+
+show :-
+ retractall(hidden(_,_,_)).
+
+
+show_term_args(Name, Arity, Args) :-
+ ( var(Arity),
+ Args == term ->
+ % simple case: show all args of all arities of functor Name
+ retractall(hidden(Name,_,_))
+ ;
+ % just fix up all entries for functor Name
+ retract((hidden(Name,Arity1,OldHidden):-_)),
+ ( ( var(Arity) ; Arity == Arity1 ) ->
+ remove_from_hidden_list(Args, Arity1, OldHidden,
+ NewHidden)
+ ;
+ NewHidden = OldHidden
+ ),
+ assertz((hidden(Name, Arity1, NewHidden) :- !)),
+ fail
+ ;
+ % After that, there's one nasty case: we're saying to show
+ % arguments of a term with no specific entry, but with a
+ % catch-all clause. In this case we must create a new clause.
+ nonvar(Arity),
+ clause(hidden(Name,Arity1,_), _),
+ Arity == Arity1 ->
+ % There is a specific clause: we're ok
+ true
+ ;
+ nonvar(Arity),
+ clause(hidden(Name,Arity1,CatchallHidden), _),
+ var(Arity1) ->
+ % No specific, but there is a catch-all: add a new clause
+ remove_from_hidden_list(Args, Arity,
+ CatchallHidden, NewHidden),
+ asserta((hidden(Name, Arity, NewHidden) :- !))
+ ;
+ % Either var(Arity) or no catch-all: nothing more to do
+ true
).
-portray_args([]).
-portray_args([X|Xs]) :-
- portray2(X),
- portray_args_2(Xs).
-
-portray_args_2([]).
-portray_args_2([X|Xs]) :-
- write(', '),
- portray2(X),
- portray_args_2(Xs).
-
-portray2(Term) :-
- ( compound(Term), list_of_char_codes(Term) ->
- format("""~s""", [Term])
- ; compound(Term) ->
- functor(Term,F,N),
- format("<~a/~d>", [F,N])
+
+remove_from_hidden_list(term, _, _, []) :-
+ !.
+remove_from_hidden_list(Shown, Arity, Hidden0, Hidden) :-
+ ( Hidden0 \== term ->
+ Hidden1 = Hidden0
+ ; integer(Arity) ->
+ list_between(1, Arity, Hidden1)
+ ; list_between(1, 255, Hidden1)
+ ),
+ sort(Shown, Shown1),
+ sorted_list_difference(Hidden1, Shown1, Hidden).
+
+
+
+% hidden(-Spec, -Args)
+% Spec is a Name/Arity term, and Args is a list of the argument numbers of
+% terms with that Name and Arity which are hidden, or the atom 'term' if the
+% entire term is hidden. Arity may be the atom 'all', indicating that the
+% specified arguments are hidden for all terms with that Name and any arity.
+
+hidden(Spec, Args) :-
+ Spec = Name/Arity,
+ clause(hidden(Name,Arity,Args), _),
+ ( var(Arity) ->
+ Arity = (all)
;
+ true
+ ).
+
+
+term_spec(Spec, Name, Arity, Predspec) :-
+ ( nonvar(Spec),
+ Spec = Name/Arity0,
+ atom(Name) ->
+ ( integer(Arity0) ->
+ Arity = Arity0
+ ; Arity0 = (all) ->
+ true % leave Arity unbound
+ ;
+ pred_error(Predspec, "invalid predicate specifier")
+ )
+ ; atom(Spec) ->
+ Name = Spec % leave Arity unbound
+ ;
+ pred_error(Predspec, "invalid predicate specifier")
+ ).
+
+
+argument_spec(Args0, Args, Predspec) :-
+ ( integer(Args0) ->
+ Args = [Args0]
+ ; Args0 == term ->
+ Args = Args0
+ ; Args0 = [_|_] ->
+ ( member(Oops, Args0),
+ \+ integer(Oops) ->
+ pred_error(
+ error(Predspec,
+ "non-integer in argument specification list"))
+ ;
+ Args = Args0
+ )
+ ;
+ pred_error(Predspec,
+ "arg 2 must be an integer or list of integers")
+ ).
+
+
+pred_error(Predspec, Msg) :-
+ string__append(": ", Msg, Back1),
+ string__append(Predspec, Back1, Back2),
+ string__append("in call to ", Back2, Errmsg),
+ error(Errmsg).
+
+
+sorted_list_difference([], _, []).
+sorted_list_difference([A|As], Bs, Cs) :-
+ sorted_list_difference_1(Bs, A, As, Cs).
+
+sorted_list_difference_1([], A, As, [A|As]).
+sorted_list_difference_1([B|Bs], A, As, Cs) :-
+ compare(Comparison, A, B),
+ sorted_list_difference_2(Comparison, A, As, B, Bs, Cs).
+
+sorted_list_difference_2(<, A, As, B, Bs, [A|Cs]) :-
+ ( As == [] ->
+ Cs = []
+ ;
+ As = [A1|As1],
+ compare(Comparison, A1, B),
+ sorted_list_difference_2(Comparison, A1, As1, B, Bs, Cs)
+ ).
+sorted_list_difference_2(=, _, As, _, Bs, Cs) :-
+ sorted_list_difference(As, Bs, Cs).
+sorted_list_difference_2(>, A, As, _, Bs, Cs) :-
+ sorted_list_difference_1(Bs, A, As, Cs).
+
+
+list_between(Nlow, Nhigh, L) :-
+ ( Nlow =< Nhigh ->
+ L = [Nlow|L1],
+ Nlow1 is Nlow + 1,
+ list_between(Nlow1, Nhigh, L1)
+ ;
+ L = []
+ ).
+
+
+% print_term(+Term)
+% print_term(+Term, +DepthLimit, +Precedence)
+% Print out Term to the current output stream, respecting the current depth
+% and length limits, and the current set of hidden functors, and using the
+% user-supplied portray_term/3 hook predicate when it succeeds. If
+% DepthLimit is supplied and is non-negative, then it specifies the maximum
+% number of levels of Term that should be printed. Precedence, if supplied,
+% should be the precedence of the context in which the term will be printed.
+% DepthLimit defaults to the currently set depth limit; Precedence defaults
+% to 999.
+
+print_term(Term) :-
+ print_depth(Limit),
+ print_term(Term, Limit, 999).
+
+
+print_term(Term, Depth, Precedence) :-
+ ( var(Term) ->
write(Term)
+ ;
+ Depth1 is Depth - 1,
+ functor(Term, Name, Arity),
+ ( portray_term(Term, Depth1, Precedence) ->
+ true
+ ; Arity =:= 0 ->
+ writeq(Name)
+ ; Depth1 =:= -1 ->
+ format("<~q/~d>", [Name,Arity])
+ ;
+ standard_print_term(Term, Depth1, Precedence)
+ )
).
-list_of_char_codes([]).
-list_of_char_codes([X|Xs]) :-
- integer(X),
- ( X = 0'\t -> true
- ; X = 0'\n -> true
- ; X = 0'\r -> true
- ; X >= 32
+
+standard_print_term(Term, Depth, Precedence) :-
+ functor(Term, Name, Arity),
+ ( hidden(Name, Arity, Hidden) ->
+ true
+ ;
+ Hidden = []
),
- X < 256,
- list_of_char_codes(Xs).
+ ( Hidden == term,
+ print_depth(Depth1),
+ Depth =\= Depth1 - 1 ->
+ format("<~q/~d>", [Name,Arity])
+ ;
+ ( Hidden == term ->
+ % Special case: if the top level term we're
+ % printing is hidden, we print it anyway, but hide all
+ % of its arguments. This is because it seems
+ % undesirable to hide the entire term that was to be
+ % printed.
+ Depth2 = 0
+ ;
+ Depth2 = Depth
+ ),
+ ( Name == '.', Arity =:= 2, Hidden == [] ->
+ print_length(Length),
+ print_list(Term, Depth2, Length, 999,
+ [], '[]', '[', ']', ',')
+ ; Name == '{}', Arity =:= 1 ->
+ write('{'),
+ arg(1, Term, Arg),
+ maybe_print_term(Arg, Depth2, 999, 1, Hidden, _),
+ write('}')
+ ; Arity =:= 1,
+ prefix_op(Name, NeededPrecedence, ArgPrecedence) ->
+ open_paren_if_needed(Precedence, NeededPrecedence),
+ writeq(Name),
+ write(' '),
+ arg(1,Term, Arg),
+ maybe_print_term(Arg, Depth2, ArgPrecedence, 1,
+ Hidden, _),
+ close_paren_if_needed(Precedence, NeededPrecedence)
+ ; Arity =:= 1,
+ postfix_op(Name, NeededPrecedence, ArgPrecedence) ->
+ open_paren_if_needed(Precedence, NeededPrecedence),
+ arg(1,Term, Arg),
+ maybe_print_term(Arg, Depth2, ArgPrecedence, 1,
+ Hidden, _),
+ write(' '),
+ writeq(Name),
+ close_paren_if_needed(Precedence, NeededPrecedence)
+ ; Arity =:= 2,
+ infix_op(Name, NeededPrecedence, Arg1Precedence,
+ Arg2Precedence) ->
+ open_paren_if_needed(Precedence, NeededPrecedence),
+ arg(1,Term, Arg1),
+ maybe_print_term(Arg1, Depth2, Arg1Precedence, 1,
+ Hidden, H2),
+ write(' '),
+ ( Name == (',') ->
+ write(Name)
+ ;
+ writeq(Name)
+ ),
+ write(' '),
+ arg(2,Term, Arg2),
+ maybe_print_term(Arg2, Depth2, Arg2Precedence, 2,
+ H2, _),
+ close_paren_if_needed(Precedence, NeededPrecedence)
+ ;
+ Term =.. [Name|Args],
+ writeq(Name),
+ print_length(Length),
+ print_list(Args, Depth, Length, Precedence, Hidden,
+ '', '(', ')', ',')
+ )
+ ).
+
+prefix_op(Op, NeededPrecedence, ArgPrecedence) :-
+ ( current_op(NeededPrecedence, fx, Op) ->
+ ArgPrecedence is NeededPrecedence - 1
+ ; current_op(NeededPrecedence, fy, Op) ->
+ ArgPrecedence = NeededPrecedence
+ ;
+ fail % to keep NU Prolog happy
+ ).
+
+
+postfix_op(Op, NeededPrecedence, ArgPrecedence) :-
+ ( current_op(NeededPrecedence, xf, Op) ->
+ ArgPrecedence is NeededPrecedence - 1
+ ; current_op(NeededPrecedence, yf, Op) ->
+ ArgPrecedence = NeededPrecedence
+ ;
+ fail % to keep NU Prolog happy
+ ).
+
+
+infix_op(Op, NeededPrecedence, Arg1Precedence, Arg2Precedence) :-
+ ( current_op(NeededPrecedence, xfx, Op) ->
+ Arg1Precedence is NeededPrecedence - 1,
+ Arg2Precedence = Arg1Precedence
+ ; current_op(NeededPrecedence, xfy, Op) ->
+ Arg1Precedence is NeededPrecedence - 1,
+ Arg2Precedence = NeededPrecedence
+ ; current_op(NeededPrecedence, yfx, Op) ->
+ Arg1Precedence = NeededPrecedence,
+ Arg2Precedence is NeededPrecedence - 1
+ ;
+ fail % to keep NU Prolog happy
+ ).
+
+
+print_list([], _, _, _, _, Empty, _, _, _) :-
+ write(Empty).
+print_list([T|Ts], Depth, Limit, Precedence, Hidden, _, Start, End, Sep) :-
+ write(Start),
+ ( Limit =\= 0 ->
+ maybe_print_term(T, Depth, Precedence, 1, Hidden, Hidden1),
+ Limit1 is Limit + 1, % first arg NOT to show
+ print_list_tail(Ts, 2, Limit1, Hidden1, Depth, Precedence, Sep)
+ ;
+ write('...')
+ ),
+ write(End).
+
+print_list_tail(Term, _, _, _, _, _, _) :-
+ var(Term),
+ !,
+ write(' | '),
+ write(Term).
+print_list_tail([], _, _, _, _, _, _).
+print_list_tail([T|Ts], Num, Limit, Hidden, Depth, Precedence, Sep) :-
+ write(Sep),
+ ( Num =\= Limit ->
+ maybe_print_term(T, Depth, Precedence, Num, Hidden, Hidden1),
+ Num1 is Num + 1,
+ print_list_tail(Ts, Num1, Limit, Hidden1, Depth, Precedence,
+ Sep)
+ ;
+ write('...')
+ ).
+
+
+maybe_print_term(T, Depth, Precedence, Num, Hidden0, Hidden) :-
+ ( Hidden0 = [Num|Hidden] ->
+ write('#')
+ ;
+ Hidden = Hidden0,
+ print_term(T, Depth, Precedence)
+ ).
+
+
+open_paren_if_needed(Precedence, NeededPrecedence) :-
+ ( Precedence >= NeededPrecedence ->
+ true
+ ;
+ write('(')
+ ).
+
+
+close_paren_if_needed(Precedence, NeededPrecedence) :-
+ ( Precedence >= NeededPrecedence ->
+ true
+ ;
+ write(')')
+ ).
+
+
+historyLength(500).
spyHook(_, Term) :-
!,
@@ -124,5 +582,79 @@
;
true
).
+
+
+% portray_term(+Term, +DepthLimit, +Precedence)
+% User-supplied hook predicate. Like the standard Prolog portray/1 hook
+% predicate, this code should either fail without printing anything, or
+% succeed after printing out Term as the user would like to see it printed.
+% DepthLimit, if >= -1, is the number of levels of the subterms of Term that
+% should be printed without being elided. If it is -1, then Term should
+% probably not be printed; only a marker indicating what sort of term it is.
+% The standard print_term code will print the name and arity of the functor
+% enclosed in angle brackets (e.g., <foo/3>) if portray_term/3 fails at this
+% depth level. Be careful, though: if DepthLimit is negative, then there is
+% no depth limit. Precedence is the precedence of the context in which the
+% term will be printed; if you wish to write a term with operators, then you
+% should parenthesize the output if Precedence is smaller than the precedence
+% of that operator.
+%
+% In printing subterms, you should call print_term/3, described above. Note
+% that DepthLimit argument passed to portray_term/3 is the depth limit for
+% the subterms of Term, so you should usually pass this value to print_term/3
+% for printing subterms without decrementing it.
+%
+% The following code should serve as an example.
+
+:- dynamic portray_term/3. % NU Prolog doesn't support
+ % :- multifile decls.
+
+% Print "strings" (lists of character codes) as double-quoted strings.
+portray_term([N|Ns], _, _) :-
+ integer(N), % for efficiency
+ printable_char_list([N|Ns]),
+ !,
+ format("""~s""", [[N|Ns]]).
+
+% Print maps as MAP{ key->value , key->value , ... }
+portray_term(two(K,V,S1,S2), Depth, _) :-
+ !,
+ portray_map(two(K,V,S1,S2), Depth).
+portray_term(three(K1,V1,K2,V2,S1,S2,S3), Depth, _) :-
+ !,
+ portray_map(three(K1,V1,K2,V2,S1,S2,S3), Depth).
+portray_term(four(K1,V1,K2,V2,K3,V3,S1,S2,S3,S4), Depth, _) :-
+ !,
+ portray_map(four(K1,V1,K2,V2,K3,V3,S1,S2,S3,S4), Depth).
+
+
+printable_char_list(0) :- % hack to catch unbound tails
+ !,
+ fail.
+printable_char_list([]).
+printable_char_list([N|Ns]) :-
+ integer(N),
+ ( N >= 32, N =< 126 -> true
+ ; N =:= 0'\n -> true
+ ; N =:= 0'\r -> true
+ ; N =:= 0'\t
+ ),
+ printable_char_list(Ns).
+
+
+portray_map(Map, Depth) :-
+ ( Depth =:= -1 ->
+ write('MAP{...}')
+ ;
+ tree234__tree234_to_assoc_list(Map, Alist),
+ list__map(pair_to_arrow, Alist, Arrowlist),
+ print_length(Length),
+ Depth1 is Depth + 1,
+ print_list(Arrowlist, Depth1, Length, 1200, [],
+ 'MAP{}', 'MAP{', '}', ' , ')
+ ).
+
+pair_to_arrow(X-Y, (X->Y)).
+
%-----------------------------------------------------------------------------%
Index: doc/user_guide.texi
===================================================================
RCS file: /home/staff/zs/imp/mercury/doc/user_guide.texi,v
retrieving revision 1.97
diff -u -r1.97 user_guide.texi
--- user_guide.texi 1997/09/23 14:00:16 1.97
+++ user_guide.texi 1997/11/03 06:31:55
@@ -335,9 +335,10 @@
debugging problems that the compiler could have detected for you.
@menu
-* Using NU-Prolog:: Building and debugging Mercury programs with NU-Prolog
-* Using SICStus:: Building and debugging Mercury programs with SICStus Prolog
-* Prolog hazards:: The hazards of executing Mercury programs using Prolog
+* Using NU-Prolog:: Building and debugging Mercury programs with NU-Prolog
+* Using SICStus:: Building and debugging with SICStus Prolog
+* Controlling printout:: Controlling display of large terms during debugging
+* Prolog hazards:: The hazards of executing Mercury programs using Prolog
@end menu
@node Using NU-Prolog
@@ -427,6 +428,9 @@
By default the debugger only displays the top levels of terms;
you can use the @samp{|} command to enter an interactive term browser.
(Within the term browser, type @samp{h.} for help.)
+See @ref{Controlling printout} for more information on controlling the
+printout of terms.
+
Also note that in the debugger, we use a version of @code{error/1}
which fails rather than aborting after printing the ``Software Error:''
message. This makes debugging easier,
@@ -456,6 +460,192 @@
which instructs @code{msc} to use SICStus Prolog's @samp{compactcode}
mode, which compiles files to a bytecode format.
+ at node Controlling printout
+ at section Controlling printout of large terms
+
+You can control the printout of terms shown as the results of queries,
+and as shown by the debugger, to a great extent. At the coarsest level,
+you can limit the depth to which terms are shown, and the number of
+arguments of terms (and the number of elements of lists) to be shown.
+Further, you can ``hide'' some of the arguments of a term, based upon the
+functor of that term. And finally, you can write your own code to print
+out certain kinds of terms in a more convenient manner.
+
+ at menu
+* Limiting size:: Limiting Print Depth and Length
+* Hiding terms:: Hiding terms and subterms based upon term functor
+* Customization:: Customizing printout by writing your own printout code
+ at end menu
+
+ at node Limiting size
+ at subsection Limiting print depth and length
+
+You can limit the depth to which terms will be printed. When a term to
+be printed appears at the depth limit, only its name and arity will be
+shown, enclosed in angle brackets, for example @samp{<foo/3>}.
+
+You can also control the number of arguments of terms, and the lengths
+of lists, to be shown. When this length limit is reached, an elipsis
+mark (@samp{...}) will be printed.
+
+The following predicates control the print depth and length limits.
+
+ at example
+set_print_depth(@var{Depth})
+ at end example
+Sets the print depth limit to @var{Depth}. If @var{Depth} is less than 0, the
+print depth is unlimited. The default print depth limit is 2.
+
+ at example
+print_depth(@var{Depth})
+ at end example
+Unifies @var{Depth} with the current print depth limit.
+
+ at example
+set_print_length(@var{Length})
+ at end example
+Sets the print length limit to @var{Length}. If @var{Length} is less
+than 0, the print length is unlimited.
+
+ at example
+print_length(@var{Length})
+ at end example
+Unifies @var{Length} with the current print length limit.
+
+
+ at node Hiding terms
+ at subsection Hiding terms and subterms
+
+Sometimes you will have terms which appear frequently during debugging
+and which tend to fill up the screen with lots of uninteresting text,
+even with depth limits in force. In such cases, you may want to hide
+terms based upon their functor, regardless of their print depth. You
+may also occasionally want to hide just a few of the arguments of a
+term. These predicates allow you to do that.
+
+Note that if the @emph{top level} functor of the term to be printed is
+hidden, its first level arguments will be shown anyway. This is to
+avoid answer substitutions being wholly hidden, and to make it easier to
+use the subterm viewing facility of the SICStus Prolog debugger (using
+the @key{^} command) to view parts of a term.
+
+
+ at example
+hide(@var{Spec})
+hide(@var{Spec, Args})
+ at end example
+Allows you to elide certain arguments of certain terms, or whole terms,
+based on the functor. @var{Spec} specifies a functor; it must be either
+a @var{Name}@code{/}@var{Arity} term, or just a functor name (in which
+case all arities are specified). @var{Args} specifies which arguments
+of such terms should be hidden. If given, @var{Args} must be a single
+integer or a list of integers specifying which arguments to hide.
+Hidden arghuments will be printed as just a hash (@samp{#}) character.
+Alternatively, @var{Args} may be the atom @code{term}, in which case
+only the name and arity of the term are written within angle brackets,
+(e.g., @samp{<foo/3>}). If Args is not specified, it defaults to
+ at code{term}.
+
+Hidden arguments are cumulative. That is, new arguments to be hidden are
+added to the set of arguments already hidden. For example, if you hide
+argument 3 of all terms with functor name @samp{foo}, and then hide
+argument 1 of terms with functor @samp{foo/3}, then for @samp{foo/3}
+terms, arguments 1 and 3 will be hidden, while for all other terms with
+functor name @samp{foo}, only argument 3 will be hidden.
+
+ at example
+show
+Show(@var{Spec})
+show(@var{Spec, Args})
+ at end example
+Undos the effect of hiding some argumments of terms or whole terms.
+ at var{Spec} specifies a functor; it must be either a
+ at var{Name}@code{/}@var{Arity} term, or just a functor name (in which
+case all arities are specified). @var{Args} specifies which arguments
+to stop hiding; it must be a list of argument numbers or a single
+argument number. Alternatively, it may be the atom @code{term},
+indicating that no arguments should be hidden (the whole term should be
+shown). @var{Args} defaults to @code{term}. The predicate
+ at code{show/0} reenables showing all arguments of all terms.
+
+Note that these predicates will not affect the depth limit of printing.
+Even if you ask to show a term in full, if it appears at the depth limit of
+the term being printed, its arguments will not be shown. If it appears
+below the depth limit, you won't see it at all.
+
+You may use the @code{hide/1,2} and @code{show/2} predicates in concert
+to hide most of the arguments of a term by first hiding all arguments,
+and then explicitly showing the few of interest.
+
+ at example
+hidden(@var{Spec, Args})
+ at end example
+ at var{Spec} is a @var{Name}@code{/}@var{Arity} term, and @var{Args} is a
+list of the argument numbers of terms with that @var{Name} and
+ at var{Arity} which are hidden, or the atom @code{term} if the entire term
+is hidden. @var{Arity} may be the atom @code{all}, indicating that the
+specified arguments are hidden for all terms with that @var{Name} and
+any arity. This predicate may be used to backtrack through all the
+predicates which are hidden or have any arguments hidden.
+
+
+ at node Customization
+ at subsection Customization of printout
+
+Occasionally you will have a term that is important to understand during
+debugging but is shown in an inconvenient manner or may be difficult to
+understand as printed. Traditionally in Prolog, you can define clauses
+for the predicate @code{portray/1} to specify special code to print such
+a term. Unfortunately, this won't work very well with the other
+features of Mercury's printout system. For this you will need to define
+clauses for the predicate @code{portray_term/3}:
+
+ at example
+portray_term(@var{Term, DepthLimit, Precedence})
+ at end example
+Like the standard Prolog @code{portray/1} hook predicate, this code
+should either fail without printing anything, or succeed after printing
+out Term as the user would like to see it printed. @var{DepthLimit}, if
+greater than or equal to -1, is the number of levels of the subterms of
+ at var{Term} that should be printed without being elided. If it is -1,
+then @var{Term} should probably not be printed; only a marker indicating
+what sort of term it is. The standard @code{print_term/3} code will
+print the name and arity of the functor enclosed in angle brackets
+(e.g., <foo/3>) if @code{portray_term/3} fails at this depth level. Be
+careful, though: if @var{DepthLimit} is less than -1, then there should
+be no depth limit. @var{Precedence} is the precedence of the context in
+which the term will be printed; if you wish to write a term with
+operators, then you should parenthesize the printout if @var{Precedence}
+is smaller than the precedence of that operator.
+
+In printing subterms, you should call @code{print_term/3}, described
+below. Note that @var{DepthLimit} argument passed to portray_term/3 is
+the depth limit for the @emph{subterms} of @var{Term}, so you should
+usually pass this value to @code{print_term/3} for printing subterms
+without decrementing it. Note also that @var{Term} will always be bound
+when @code{portray_term/3} is called, so you needn't worry about
+checking for unbound variables.
+
+Mercury already supplies special-purpose printout code to print out
+lists of character codes as double-quoted strings, and to print out maps
+as @var{Key}@code{->}@var{Value} pairs, surrounded by @samp{MAP@{@}}.
+This code appears in the file @file{portray.nl} in the Mercury library,
+and may serve as a useful example of how to write customized printing
+code.
+
+ at example
+print_term(@var{Term, DepthLimit, Precedence})
+ at end example
+Print out @var{Term} to the current output stream, limiting print depth
+to @var{DepthLimit}, and parenthesizing the term if its principle
+functor is an operator with precedence greater than @var{Precedence}.
+ at code{print_term/3} also respects the current set of hidden functors,
+and uses the user-supplied @code{portray_term/3} hook predicate when it
+succeeds.
+
+
+
+
@node Prolog hazards
@section Hazards of using Prolog
@@ -1472,7 +1662,7 @@
library which has been compiled with the same setting.
Rather than setting them individually, you must
specify them all at once by selecting a particular
-compilation model ("grade").
+compilation model (``grade'').
@table @asis
@item @code{-s @var{grade}}
More information about the developers
mailing list