[m-dev.] for review: term browser (version one)

Fergus Henderson fjh at cs.mu.OZ.AU
Thu Oct 15 20:25:52 AEST 1998


On 14-Oct-1998, Bert THOMPSON <aet at cs.mu.OZ.AU> wrote:
> Add a simple term browser for use by the trace-based debugger.
...
> N.B. This still needs to be hooked into the debugger.
...
> +++ browser_library.m	1998/10/14 15:16:26
> @@ -14,6 +14,10 @@
>  
>  :- import_module help.
>  :- import_module debugger_interface.
> +:- import_module browse.
> +:- import_module frame.
> +:- import_module parse.
> +:- import_module util.

This is a bit of a side issue, but I should point out that
these module names will invade the user's module name space;
the user won't be able to use a module named `util', for example.
To solve this, these modules ought to be submodules of the
`browser_library' module (which should perhaps be renamed as just
`browser').

But nested modules still have a few problems at the moment,
so leave it as is.

> :- module browse.
> 
> :- interface.
> 
> :- import_module io, std_util.
> 
> :- pred browse__browser(univ, io__state, io__state).
> :- mode browse__browser(in, di, uo) is det.
> 
> %---------------------------------------------------------------------------%
> :- implementation.

Probably it would be better to name the routine `browse' (a verb)
rather than `browser' (a noun).

I think we really need two different interfaces to the browser:
an interactive one, and a non-interactive one which just displays
terms with some kind of fixed depth limit.

> 	browser2(State).
> 
> :- pred browser2(browser_state, io__state, io__state).
> :- mode browser2(in, di, uo) is det.
> browser2(State) -->

s/browser2/browser_2/g

> 	( { Command = quit } ->
> 		io__write_string("quitting...\n")
...
> :- pred startup_message(io__state::di, io__state::uo) is det.
> startup_message -->
> 	io__write_string("-- Simple Mercury Term Browser.\n"),
> 	io__write_string("-- Type \"help\" for help.\n\n").

The startup and quit messages will be very annoying if they
are printed every time you want to browse a term from within
the debugger.

> :- pred prompt(io__state::di, io__state::uo) is det.
> prompt -->
> 	io__write_string("browser> ").
> 
> 
> :- pred run_command(command, browser_state, browser_state,
> 	io__state, io__state).
> :- mode run_command(in, in, out, di, uo) is det.
> run_command(Command, State, NewState) -->
> 	( { Command = unknown } ->
> 		io__write_string("error: unknown command. try \"help\"\n"),

Sentences in error messages should start with capital letters
and end with full stops.

> :- pred portray(browser_state, io__state, io__state).
> :- mode portray(in, di, uo) is det.
> portray(State) -->
> 	{ get_fmt(State, Fmt) },
> 	( { Fmt = flat } ->
> 		portray_flat(State)
> 	; { Fmt = pretty } ->
> 		portray_pretty(State)
> 	; { Fmt = verbose } ->
> 		portray_verbose(State)
> 	;
> 		{ error("portray: domain error") }
> 	).

You should use a switch (i.e. a disjunction) rather than using an
if-then-else chain with `error' in the else case.

> :- pred portray_fmt(browser_state, portray_format, io__state, io__state).
> :- mode portray_fmt(in, in, di, uo) is det.
> portray_fmt(State, Format) -->
> 	( { Format = flat } ->
> 		portray_flat(State)
> 	; { Format = pretty } ->
> 		portray_pretty(State)
> 	; { Format = verbose } ->
> 		portray_verbose(State)
> 	;
> 		{ error("portray_fmt: domain error") }
> 	).

Likewise.

> 	% Single-line representation of a term.
> 
> :- pred term_to_string(univ, int, int, string).
> :- mode term_to_string(in, in, in, out) is det.
> term_to_string(Univ, MaxSize, MaxDepth, Str) :-
> 	CurSize = 0,
> 	CurDepth = 0,
> 	term_to_string2(Univ, MaxSize, CurSize, _NewSize,
> 		MaxDepth, CurDepth, Str).
> 
> 	% Note: When the size limit is reached, we simply display
> 	% further subterms compressed. We don't just stopping printing.
> 	% XXX: Is this reasonable?
> :- pred term_to_string2(univ, int, int, int, int, int, string).
> :- mode term_to_string2(in, in, in, out, in, in, out) is det.
> term_to_string2(Univ, MaxSize, CurSize, NewSize, MaxDepth, CurDepth, Str) :-

s/string/string_2/g

Likewise elsewhere.  This is just for consistency with other
source code in the Mercury implementation.

> :- pred comma_args(list(string), string).
> :- mode comma_args(in, out) is det.
> comma_args(Args, Str) :-
> 	( Args = [] ->
> 		Str = ""
> 	; Args = [S] ->
> 		Str = S
> 	; Args = [S1,S2|Ss] ->
> 		comma_args([S2|Ss], Rest),

Please put spaces after commas and around `|' operators.

> write_path([Dir,Dir2|Dirs]) -->
> 	write_path2([Dir,Dir2|Dirs]).

Ditto.

> :- pred default_state(univ, browser_state).
> :- mode default_state(in, out) is det.
> default_state(Univ, State) :-
> 	State = browser_state(Univ,3,10,[],verbose, 79, 25).

Ditto.

> :- pred set_term(univ, browser_state, browser_state).
> :- mode set_term(in, in, out) is det.
> set_term(NewUniv, browser_state(_OldUniv,Dep,Siz,Path,Fmt,X,Y),
> 	browser_state(NewUniv,Dep,Siz,Path,Fmt,X,Y)).

Ditto.

> :- pred show_settings(browser_state, io__state, io__state).
> :- mode show_settings(in, di, uo) is det.
> show_settings(State) -->
...
> 	io__write_string("Print format is "),
> 		io__write_string(FmtStr), io__nl,
> 	( { Fmt = flat } ->
> 		{ FmtStr = "flat" }
> 	; { Fmt = pretty } ->
> 		{ FmtStr = "pretty" }
> 	; { Fmt = verbose } ->
> 		{ FmtStr = "verbose" }
> 	;
> 		{ error("show_settings: domain error") }
> 	).

Use a switch rather than an if-then-else.
Or more simply, just change that to

	io__write_string("Print format is "),
 		io__print(FmtStr), io__nl,

> :- pred simplify(list(dir), list(dir)).
> :- mode simplify(in, out) is det.
> simplify([], []).
> simplify([parent|Dirs], Dirs).
> simplify([child(Dir)], [child(Dir)]).
> simplify([child(_Dir), parent |Dirs], Dirs).
> simplify([child(Dir1), child(Dir2) |Dirs], [child(Dir1) | Rest]) :-
> 	simplify([child(Dir2)|Dirs], Rest).

Please put spaces around `|' operators.

I didn't understand what this predicate is supposed to be doing --
a comment would help.

parse.m:
> %	path:
> %		["/"] dir ["/" path]

Hmm, that grammar allows "1//2" as a valid path, probably it shouldn't.

> 	% XXX: could nuke zero-arity "ls" and "cd".
> :- type command
> 	--->	ls(path)
> 	;	ls
> 	;	cd(path)
> 	;	cd

"XXX" is supposed to indicate something that should be done,
rather than merely something that could be done.

> 	% XXX: default depth also in browse.m
> :- pred default_depth(int).
> :- mode default_depth(out) is det.
> default_depth(10).

I think you should fix that one.

> :- pred parse_path(list(token), path).
> :- mode parse_path(in, out) is semidet.
> 	% SICStus is forgiving in the syntax of paths, hence so are we.
> 	% XXX: Be less forgiving?

Yes, I think it would better to be less forgiving.
If the user types something syntactically incorrect,
then it's probably more helpful to print an error message
than to ignore the error.

> :- pred parse_setting(list(token), setting).
> :- mode parse_setting(in, out) is semidet.
> parse_setting([Tok|Toks], Setting) :-
> 	( Tok = name("depth") ->
> 		Toks = [num(Depth)],
> 		Setting = depth(Depth)
> 	; Tok = name("size") ->
> 		Toks = [num(Size)],
> 		Setting = size(Size)
> 	; Tok = name("clipx") ->
> 		Toks = [num(X)],
> 		Setting = clipx(X)
> 	; Tok = name("clipy") ->
> 		Toks = [num(Y)],
> 		Setting = clipy(Y)
> 	; Tok = name("format") ->
> 		Toks = [Fmt],
> 		( Fmt = name("flat") ->
> 			Setting = format(flat)
> 		; Fmt = name("pretty") ->
> 			Setting = format(pretty)
> 		;
> 			Fmt = name("verbose"),
> 			Setting = format(verbose)
> 		)
> 	;
> 		fail
> 	).

It would be more efficient to use switches rather than if-then-elses.

util.m:
> :- module util.
> 
> :- interface.
> 
> :- import_module std_util, string, list.
> 
> :- pred util__typename(univ, string).
> :- mode util__typename(in, out) is det.
> 
> :- pred util__functor(univ, string).
> :- mode util__functor(in, out) is det.
> 
> :- pred util__args(univ, list(univ)).
> :- mode util__args(in, out) is det.
> 
> :- pred util__arity(univ, int).
> :- mode util__arity(in, out) is det.
> 
> :- pred util__copy(int, T, list(T)).
> :- mode util__copy(in, in, out) is det.
> 
> :- pred util__zip_with(pred(T1, T2, T3), list(T1), list(T2), list(T3)).
> :- mode util__zip_with(pred(in, in, out) is det, in, in, out) is det.
> 
> :- pred util__limit(pred(list(T), list(T)), list(T), list(T)).
> :- mode util__limit(pred(in,out) is det, in, out) is det.
> 
> :- implementation.
> 
> :- import_module std_util, list, string, int, require.
> 
> 
> util__functor(Univ, Functor) :-
> 	deconstruct(Univ, Functor, _Arity, _Args).
> 	
> util__args(Univ, Args) :-
> 	deconstruct(Univ, _Functor, _Arity, Args).
> 
> util__arity(Univ, Arity) :-
> 	deconstruct(Univ, _Functor, Arity, _Args).

It might be more efficient to use std_util__functor
to implement util__functor and util__arity.

But this layer of abstraction adds a small amount of
additional complexity -- what does it buy you in return?
Why not just use std_util__deconstruct or std_util__functor directly? 

> util__typename(Univ, TypeName) :-
> 	TypeInfo = univ_type(Univ),
> 	TypeName = type_name(TypeInfo).

This too seems hardly worth a subroutine -- you could just write

	TypeName = type_name(univ_type(Univ))

instead of

	util__typename(Univ, TypeName).

> util__copy(N, X, Xs) :-
> 	( N =< 0 ->
> 		Xs = []
> 	;
> 		util__copy(N1, X, Xs2),
> 		Xs = [X|Xs2],
> 		N1 is N - 1
> 	).

I think this does the same thing as list__duplicate, 
so you should use list__duplicate instead.

> New file: frame.m
> 	% XXX: Make frame type abstract instead?
> 	% XXX: I'd rather not fully module-qualify the frame type.
> % :- type frame.
> :- type frame == list(string).

I don't understand the second XXX comment.  Could you elaborate, please?

> :- pred frame__hsize(frame, int).
> :- mode frame__hsize(in, out) is det.
> 
> :- pred frame__vsize(frame, int).
> :- mode frame__vsize(in, out) is det.
> 
> :- pred frame__from_string(string, frame).
> :- mode frame__from_string(in, out) is det.
> 
> :- pred frame__vglue(frame, frame, frame).
> :- mode frame__vglue(in, in, out) is det.
> 
> :- pred frame__hglue(frame, frame, frame).
> :- mode frame__hglue(in, in, out) is det.
> 
> :- pred frame__clip(frame__clip_rect, frame, frame).
> :- mode frame__clip(in, in, out) is det.

These predicates should be documented in the interface section.

> 	% Clip a frame to the rectangle ((0,0),(X,Y)) where
> 	% origin is on the top-left. Coordinate axes go down and right.
> 	% XXX: Any reasons to implement a general clip?
> frame__clip(X-Y, Frame, ClippedFrame) :-
> 	list__take_upto(Y, Frame, YClippedFrame),
> 	list__map(left(X), YClippedFrame, ClippedFrame).

The "XXX" comment here isn't clear to me.  Could you please elaborate?

--------------------

Apart from that, it looks OK to me.  But could you please post another diff
and/or a relative diff after you've addressed those changes?

-- 
Fergus Henderson <fjh at cs.mu.oz.au>  |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>  |  of excellence is a lethal habit"
PGP: finger fjh at 128.250.37.3        |     -- the last words of T. S. Garp.



More information about the developers mailing list