[m-dev.] for review: Added a new format "newpretty" to the browser.

Mark Anthony BROWN dougl at cs.mu.OZ.AU
Fri Jan 19 14:24:38 AEDT 2001


Hi,

This conludes my comments for this round of reviewing.

After you have addressed these comments, and the ones in the earlier post,
please send another diff with your changes.  It should be a relative diff
showing just the differences from this diff you have already posted.  (Ask
around the office if you need help making a relative diff.)  However, since
a lot of the changes I have requested revolve around formatting and layout,
I would appreciate it if you also posted a full diff showing all changes,
in addition to posting the relative diff.  After that, I'll continue the
reviewing.

Cheers,
Mark.

Sarvamanan THURAIRATNAM writes:
> +
> +:- module sizepretty.
> +
> +:- interface.
> +
> +:- import_module std_util, list, pprint, bool, int, string.
> +
> +:- type measure_params
> +	--->	measure_params
> +	;	measure_params(int).
> +
> +:- type maybe_deconstructed(T)
> +	--->	not_deconstructed
> +	;	deconstructed(
> +			string,
> +			int,
> +			list(maybe(pair(T, size_annotated_term(T))))  	
> +		).
> +
> +:- type size_annotated_term(T)
> +	--->	exact(
> +			univ,
> +			T,
> +			string,
> +			int,
> +			list(maybe(pair(T, size_annotated_term(T))))
> +		)
> +	;	at_least(
> +			univ,
> +			T,
> +			maybe_deconstructed(T)
> +		).
> +
> +:- type functor_count
> +	--->	functor_count(int).
> +
> +:- type char_count
> +	--->	char_count(int).
> +
> +:- type size_count
> +	--->	size_count(int, int).	

The way this type is defined, there is always exactly one line count and
one character count for each value.  However, there seem to be two distinct
ways that these values are used: either the term will be placed on multiple
lines (in which case we are interested in just the line count), or the term
will be fitted onto a single line (in which case we are interested in just
the character count).  And there are two distinct ways in which your code
constructs these values: either the line count is zero or the character
count is zero.

When there are two distinct cases, a good way to model it is with a type
that has two functors.  Each functor should have only the information
required for that case.  For example:

	:- type size_count
		--->	line_count(int)
		;	character_count(int).

where line_count(Lines) plays the same role as your size_count(Lines, 0),
and character_count(Chars) plays the same role as size_count(0, Chars).

I think it would be worth experimenting with making a measure/1 instance of
a type like the one above.  You don't have to do this as part of this change,
though.  I'll be happy if you consider the idea sometime after committing
this change.

> +
> +:- typeclass measure(T) where [
> +	func add_measures(T, T, measure_params) = T is det,
> +	func subtract_measures(T, T, measure_params) = T is det,
> +	func compare_measures(T, T) = comparison_result is det,
> +	func max_measure(T, T) = T is det,
> +	func zero_measure = T is det,
> +	pred measured_split(univ::in, measure_params::in, T::in, int::in,
> +	     bool::in, T::out, maybe(T)::out, T::out,measure_params::out) is det
> +		
> +].
> +
> +:- instance measure(functor_count).
> +:- instance measure(char_count).
> +:- instance measure(size_count).
> +	
> +	% This may throw an exception or cause a runtime abort if the term
> +	% in question has user-defined equality.
> +:- pred annotate_with_size(univ::in, measure_params::in, T::in,
> +	size_annotated_term(T)::out) is det <= measure(T).
> +
> +	% A function to convert a size annotated term to a 'doc' type,
> +	% a type defined in pprint.m.
> +:- func my_to_doc(size_annotated_term(T)) = doc <= measure(T).
> +
> +:- implementation.
> +
> +:- import_module require, assoc_list.
> +
> +%------------------------------------------------------------------------------%
> +	% first_pass gives an idea of how much space each term takes
> +	% (In this pass the space is unevenly distributed. First come first
> +	% served. In The Second pass the space is evenly distriduted between
> +	% the terms.

You could give more detail here.  The first pass doesn't do _every_ term,
for example.  Once it is determined that there won't be enough room, the
term is not fully deconstructed.

> +annotate_with_size(Univ, Params, Limit, Size2) :-
> +	first_pass(Univ, Params, Limit, Size1),
> +	second_pass(Size1, Params, Limit, Size2).
> +
> +%------------------------------------------------------------------------------%
> +	
> +:- pred first_pass(univ::in, measure_params::in, T::in,
> +	size_annotated_term(T)::out) is det <= measure(T).
> +
> +first_pass(Univ, Params, Limit, Size) :-
> +	deconstruct(univ_value(Univ), Functor, Arity, Args),	
> +	measured_split(Univ, Params, Limit, Arity, yes, FunctorSize, 
> +					Flag, NewLimit, NewParams),
> +	flag_with(Args, Flag, FlaggedUnivArgs),
> +	( (Arity \= 0, Flag = no) ->
> +		Exact0 = no
> +	;
> +		Exact0 = yes
> +	),
> +        annotate_args_with_size(FlaggedUnivArgs, NewParams, NewLimit, 
> +		FunctorSize, SoFar, Exact0, Exact, MaybeArgSizes),
> +	(
> +		Exact = no,
> +	        Size = at_least(Univ, SoFar,
> +	                        deconstructed(Functor, Arity, MaybeArgSizes))
> +	;
> +	        Exact = yes,
> +	        Size = exact(Univ, SoFar, Functor, Arity, MaybeArgSizes)
> +	).
> +
> +%------------------------------------------------------------------------------%
> +	% annotating the arguments.
> +:- pred annotate_args_with_size(assoc_list(maybe(T), univ)::in,
> +	measure_params::in, T::in, T::in, T::out, bool::in, bool::out,
> +	list(maybe(pair(T, size_annotated_term(T))))::out)
> +	is det <= measure(T).
> +
> +annotate_args_with_size([], _, _, SoFar, SoFar, Exact, Exact, []).
> +annotate_args_with_size([Flag - Arg | FlaggedArgs], Params, Limit,
> +		SoFar0, SoFar, Exact0, Exact,
> +		[MaybeFlaggedSize | MaybeFlaggedSizes]) :-
> +	(
> +		Flag = yes(ArgLimit),
> +		AppliedArgLimit = max_measure(ArgLimit,
> +			subtract_measures(Limit, SoFar0, Params)),
> +		first_pass(Arg, Params, AppliedArgLimit, Size),
> +		MaybeFlaggedSize = yes(ArgLimit - Size),
> +		extract_size_from_annotation(Size) = ArgSize,
> +		SoFar1 = add_measures(SoFar0, ArgSize, Params),
> +		(
> +			Size = exact(_, _, _, _, _),
> +			Exact1 = Exact0
> +		;
> +			Size = at_least(_, _, _),
> +			Exact1 = no
> +		)
> +	;
> +		Flag = no,
> +		MaybeFlaggedSize = no,
> +		SoFar1 = SoFar0,
> +		Exact1 = Exact0
> +	),
> +	( compare_measures(SoFar1, Limit) = (>) ->
> +		SoFar = SoFar1,
> +		Exact = no,
> +		annotate_args_with_zero_size(FlaggedArgs, zero_measure,
> +			MaybeFlaggedSizes)
> +	;
> +		annotate_args_with_size(FlaggedArgs, Params, Limit,
> +			SoFar1, SoFar, Exact1, Exact, MaybeFlaggedSizes)
> +	).
> +
> +%------------------------------------------------------------------------------%
> +
> +:- pred annotate_args_with_zero_size(assoc_list(maybe(T), univ)::in, T::in,
> +	list(maybe(pair(T, size_annotated_term(T))))::out) is det <= measure(T).
> +
> +annotate_args_with_zero_size([], _, []).
> +annotate_args_with_zero_size([Flag - Univ | FlaggedArgs], ZeroMeasure,
> +		[FlaggedSize | FlaggedSizes]) :-
> +	(
> +		Flag = yes(ArgLimit),
> +		FlaggedSize = yes(ArgLimit -
> +			at_least(Univ, ZeroMeasure, not_deconstructed))
> +	;
> +		Flag = no,
> +		FlaggedSize = no
> +	),
> +	annotate_args_with_zero_size(FlaggedArgs, ZeroMeasure, FlaggedSizes).
> +
> +%------------------------------------------------------------------------------%
> +
> +:- func extract_size_from_annotation(size_annotated_term(T)) = T.
> +
> +extract_size_from_annotation(exact(_, Size, _, _, _)) = Size.
> +extract_size_from_annotation(at_least(_, Size, _)) = Size.
> +
> +%------------------------------------------------------------------------------%
> +
> +:- func extract_univ_from_annotation(size_annotated_term(T)) = univ.
> +
> +extract_univ_from_annotation(exact(Univ, _, _, _, _)) = Univ.
> +extract_univ_from_annotation(at_least(Univ, _, _)) = Univ.
> +
> +%------------------------------------------------------------------------------%
> +	% This predicate basically ensures that the arguments that
> +	% take up smaller "Space" than their fair share is fully
> +	% printed and the rest the Space is shared equally between
> +	% the other terms which could take up more than their share.
> +	% If a term can be fully printed within the given space,
> +	% ("exact" type) then the Term is not altered.
> +:- pred second_pass(size_annotated_term(T)::in, measure_params::in, T::in,
> +	size_annotated_term(T)::out) is det <= measure(T).
> +
> +second_pass(OldSizeTerm, Params, Limit, NewSizeTerm) :-
> +    if OldSizeTerm = exact(_Univ, _Size, _, _Arity, _MaybeArgs) then
> +	NewSizeTerm = OldSizeTerm
> +    else if OldSizeTerm = at_least(_Univ, _Size, not_deconstructed) then
> +	NewSizeTerm = OldSizeTerm
> +    else if OldSizeTerm = at_least(Univ, _Size, deconstructed(Functor, Arity,
> +	MaybeArgs)) then

This chain of if-then-elses could be made into a switch, viz:

	(
		OldSizeTerm = exact(_, _, _, _, _),
		...
	;
		OldSizeTerm = at_least(_, _, not_deconstructed),
		...
	;
		OldSizeTerm = at_least(_, _, deconstructed( ... )),
		...
	)

As a rule of thumb, it is better to use a switch whenever possible, and
only use if-then-elses when a switch won't work or is too awkward.  Aside
from the fact that it generally makes the code more readable, this also
gives you better compile-time checking -- if one of the types in question
changes and you forget to update this bit of code, then often a switch will
result in a determinism error whereas an if-then-else will silently accept
the incorrect code.

> +	measured_split(Univ, Params, Limit, Arity, yes, FSize, Flag, NewLimit, 
> +		NewParams),
> +	( if Flag = yes(X) then
> +	    ArgLimit = X,
> +	    check_args(NewParams, MaybeArgs, ArgLimit, 0, Passed, 
> +	   	zero_measure, Used),
> +	    measured_split(Univ, Params, subtract_measures(NewLimit, Used, 
> +	    	Params), Arity-Passed, no, _, Flag2, _, _),
> +	    ( if Flag2 = yes(Y) then
> +	        SplitLimit = Y,
> +	        process_args(NewParams, MaybeArgs, ArgLimit, SplitLimit, 
> +			NewArgs, NewSize0),
> +		NewSize = add_measures(FSize, NewSize0, NewParams),
> +		Result0 = list__map(check_if_exact, NewArgs),
> +    		list__remove_adjacent_dups(Result0, Result),
> +		( Result = [yes] ->
> +			NewSizeTerm = exact(Univ, NewSize, Functor, 
> +				Arity, NewArgs) 	
> +	        ;
> +			NewSizeTerm = at_least(Univ, NewSize, 
> +				deconstructed(Functor, Arity, NewArgs))
> +		)
> +	    else
> +	        NewSizeTerm = at_least(Univ, FSize, not_deconstructed)
> +	    )
> +	else
> +	    NewSizeTerm = at_least(Univ, FSize, not_deconstructed)
> +	)
> +    else
> +    	error("Incorrect type of Size Annotated Term").

Some of the indentation here needs to be fixed.  As the coding standard
says, there should be one tab per level of indentation (except for highly
indented code).  This applies to other places in the diff, too.

> +	
> +%------------------------------------------------------------------------------%
> +	% Given a list of size annotated terms(ie arguments) and a
> +	% Limit, this predicate returns the values "Passed" and 
> +	% "Used". Where "Passed" represents the number of terms that
> +	% obey the Limit and are fully represented("exact") and "Used"
> +	% represents the space that these terms take up.
> +:- pred check_args(measure_params::in, list(maybe(pair(T, size_annotated_term(T)
> +	)))::in, T::in, int::in, int::out, T::in, T::out) is det <= measure(T).
> +
> +check_args(_, [], _, Passed0, Passed0, Used0, Used0).
> +check_args(Params, [HeadArg | Rest], ArgLimit, Passed0, Passed, Used0, Used) :-
> +    if HeadArg = yes(X) then
> +	X = _-STerm,
> +	Size = extract_size_from_annotation(STerm), 
> +	( if STerm = exact(_, _, _, _, _) then
> +	    ( if compare_measures(ArgLimit, Size) = (<) then
> +	    	check_args(Params, Rest, ArgLimit, Passed0, Passed, Used0, Used)
> +	    else
> +	    	check_args(Params, Rest, ArgLimit, Passed0+1, Passed, 
> +				add_measures(Used0, Size, Params), Used)
> +	    )
> +	else
> +	    check_args(Params, Rest, ArgLimit, Passed0, Passed, Used0, Used)
> +	)
> +    else
> +	check_args(Params, Rest, ArgLimit, Passed0, Passed, Used0, Used).
> +
> +%------------------------------------------------------------------------------%
> +	% This predicate accepts a list of size annotated terms(paired
> +	% with a flag) and returns a list of the same type. This new
> +	% list would consist of the same number of terms as the other
> +	% but the terms which do not obey the limit or not fully 
> +	% represented would be annoted again with a new limit
> +	% (SplitLimit). The rest of the terms are left alone.
> +:- pred process_args(measure_params::in, 
> +	list(maybe(pair(T, size_annotated_term(T))))::in, T::in, T::in, 
> +	list(maybe(pair(T, size_annotated_term(T))))::out, T::out)
> +	is det <= measure(T).
> +
> +process_args(_, [], _, _, [], zero_measure).
> +process_args(Params, [HeadArg | Rest], ArgLimit, SplitLimit, 
> +		[NewHeadArg | NewRest], SizeOut) :-
> +    ( if HeadArg = yes(X) then
> +	X = _-STerm,
> +	Size = extract_size_from_annotation(STerm), 
> +        Univ = extract_univ_from_annotation(STerm), 


> +	( if STerm = exact(_, _, _, _, _) then
> +	    ( if compare_measures(ArgLimit, Size) = (<) then
> +		NewHeadArg = yes(pair(SplitLimit, NewSTerm)),
> +		annotate_with_size(Univ, Params, SplitLimit, NewSTerm)
> +	    else
> +		NewHeadArg = HeadArg
> +	    )
> +	else
> +	    NewHeadArg = yes(pair(SplitLimit, NewSTerm)),
> +	    annotate_with_size(Univ, Params, SplitLimit, NewSTerm)
> +	)

This piece of code could be simpler if the two conditions were merged
into one.  That way you could avoid putting the call two annotate_with_size
in two separate branches.

> +    else
> +	NewHeadArg = no
> +    ),
> +    ( NewHeadArg = yes(_-Term) ->
> +	NewSize = extract_size_from_annotation(Term),
> +	SizeOut = add_measures(NewSize, RestSize, Params)
> +    ;
> +	SizeOut = RestSize
> +    ),
> +    process_args(Params, Rest, ArgLimit, SplitLimit, NewRest, RestSize).
> +
> +%------------------------------------------------------------------------------%
> +	% checking if an size-annotated arg is an exact type (fully represented)
> +:- func check_if_exact(maybe(pair(T, size_annotated_term(T)))) = bool.
> +
> +check_if_exact(no) = no.
> +check_if_exact(yes(_-Term)) = Result:-
> +	( Term = exact(_, _, _, _, _) ->
> +		Result = yes
> +	;
> +		Result = no
> +	).	

This would be better as a switch than an if-then-else.

> +
> +%------------------------------------------------------------------------------%
> +	% Converting size-annotated terms to 'doc' type
> +my_to_doc(at_least(Univ, _, not_deconstructed)) = Doc :-
> +	deconstruct(univ_value(Univ), Functor, Arity, _Args),
> +	Doc = text(Functor) `<>` text("/") `<>` poly(i(Arity)).
> +
> +my_to_doc(at_least(_, _, deconstructed(Functor, Arity, MaybeArgs))) = Doc :-
> +	Doc = my_to_doc2(Functor, Arity, MaybeArgs).
> +
> +my_to_doc(exact(_, _, Functor, Arity, MaybeArgs)) = Doc :-
> +	Doc = my_to_doc2(Functor, Arity, MaybeArgs).
> +
> +%------------------------------------------------------------------------------%
> +	% Assumes that every argument must be on a different line
> +	% or all of them should be on the same line.
> +:- func my_to_doc2(string, int, list(maybe(pair(T, size_annotated_term(T))))) 
> +	= doc <= measure(T).
> +
> +my_to_doc2(Functor, _Arity, []) = text(Functor).
> +
> +my_to_doc2(Functor, Arity, [HeadArg|Tail]) = Doc :-
> +    Args = list__map(handleArg, [HeadArg|Tail]),
> +    list__remove_adjacent_dups(Args, NewArgs),
> +    ( NewArgs \= [text("*")] -> 

I'm not sure its a good idea using an ordinary value to encode a special
case.  Do you know for certain that the ordinary value will never crop
up except when specially put there?  (If so, you should explain why in
comments.)  What will happen when the top-level functor of the argument
in question is in fact "*"?

> +        (Doc = text(Functor) `<>`
> +	      parentheses(
> +	                 group(
> +	                      nest(2,
> +	                          line `<>` separated(id,comma_space_line, Args)
> +	                          )
> +	                      )
> +	                 )
> +	)
> +    ;
> +        Doc = text(Functor) `<>` text("/") `<>` poly(i(Arity))
> +    ).
> +	
> +%------------------------------------------------------------------------------%
> +
> +:- func handleArg(maybe(pair(T,size_annotated_term(T)))) = doc <= measure(T).
> +
> +handleArg(yes(_ - Arg_Term)) = my_to_doc(Arg_Term). 
> +handleArg(no) = text("*").

For consistency, this should be called "handle_arg".

> +
> +%------------------------------------------------------------------------------%
> +	% A predicate that creates an associated list of Univ and their
> +	% individual Limit
> +:- pred flag_with(list(univ)::in, maybe(T)::in,
> +	assoc_list(maybe(T), univ)::out) is det.
> +flag_with([], _, []).
> +flag_with([Arg | Args], Flag, [Flag - Arg | FlaggedArgs]) :-
> +	flag_with(Args, Flag, FlaggedArgs).
> +
> +%------------------------------------------------------------------------------%
> +	% functor_count is a representation where the size of a term
> +	% is measured by the number of function symbols.
> +
> +:- func add_functor_count(functor_count, functor_count, 
> +	measure_params) = functor_count.
> +
> +add_functor_count(functor_count(A), functor_count(B), _) = functor_count(A + B).
> +
> +:- func subtract_functor_count(functor_count, functor_count, 
> +	measure_params) = functor_count.
> +
> +subtract_functor_count(functor_count(A), functor_count(B), _) =
> +	functor_count(A - B).
> +
> +:- func compare_functor_count(functor_count, functor_count) = comparison_result.
> +
> +compare_functor_count(functor_count(A), functor_count(B)) = R :-
> +	compare(R, A, B).
> +
> +:- func max_functor_count(functor_count, functor_count) = functor_count.
> +
> +max_functor_count(functor_count(A), functor_count(B)) = functor_count(Max) :-
> +	int__max(A, B, Max).
> +
> +:- func zero_functor_count = functor_count.
> +
> +zero_functor_count = functor_count(0).
> +	
> +	% Refer to size_count_split for comments.
> +:- pred functor_count_split(univ::in, measure_params::in, functor_count::in,
> +	int::in, bool::in, functor_count::out, maybe(functor_count)::out,
> +	functor_count::out, measure_params::out) is det.
> +
> +functor_count_split(_, X, functor_count(Limit), Arity, _, functor_count(1),
> +		Flag, functor_count(Limit), X) :-
> +	( Arity = 0 ->
> +		Flag = no
> +	;
> +		( Limit =< (Arity + 1) ->			
> +			Flag = no
> +		;
> +			RoundUp = (Limit + Arity - 1) // Arity,
> +			Flag = yes(functor_count(RoundUp))
> +		)
> +	).
> +
> +:- instance measure(functor_count) where [
> +	func(add_measures/3) is add_functor_count,
> +	func(subtract_measures/3) is subtract_functor_count,
> +	func(compare_measures/2) is compare_functor_count,
> +	func(max_measure/2) is max_functor_count,
> +	func(zero_measure/0) is zero_functor_count,
> +	pred(measured_split/9) is functor_count_split
> +].
> +
> +%------------------------------------------------------------------------------%
> +	% char_count is a representation where the size of a term is
> +	% measured by the number of characters.
> +
> +:- func add_char_count(char_count, char_count, measure_params) = char_count.
> +
> +add_char_count(char_count(A), char_count(B), _) = char_count(A + B).
> +
> +:- func subtract_char_count(char_count, char_count, 
> +	measure_params) = char_count.
> +
> +subtract_char_count(char_count(A), char_count(B), _) =
> +	char_count(A - B).
> +
> +:- func compare_char_count(char_count, char_count) = comparison_result.
> +
> +compare_char_count(char_count(A), char_count(B)) = R :-
> +	compare(R, A, B).
> +
> +:- func max_char_count(char_count, char_count) = char_count.
> +
> +max_char_count(char_count(A), char_count(B)) = char_count(Max) :-
> +	int__max(A, B, Max).
> +
> +:- func zero_char_count = char_count.
> +
> +zero_char_count = char_count(0).
> +
> +	% Refer to size_count_split for comments.
> +:- pred char_count_split(univ::in, measure_params::in, char_count::in,
> +	int::in, bool::in, char_count::out, maybe(char_count)::out,
> +	char_count::out, measure_params::out) is det.
> +
> +char_count_split(Univ, X, char_count(Limit), Arity, Check, 
> +		char_count(FunctorSize), Flag, char_count(Limit), X) :-
> +	deconstruct(univ_value(Univ), Functor, _, Args),
> +	( Check = yes ->
> +		get_arg_length(Args, TotalLength, _)
> +	;
> +		TotalLength = 0
> +	),
> +	FunctorSize = string__length(Functor) + 2*(Arity),
> +	( Arity = 0 ->
> +		Flag = no
> +	;
> +		( Limit =< (FunctorSize + TotalLength) ->
> +			Flag = no
> +		;
> +			RoundUp = (Limit + Arity - FunctorSize) // Arity,
> +			Flag = yes(char_count(RoundUp))
> +		)
> +	).
> +
> +:- instance measure(char_count) where [
> +        func(add_measures/3) is add_char_count,
> +        func(subtract_measures/3) is subtract_char_count,
> +        func(compare_measures/2) is compare_char_count,
> +        func(max_measure/2) is max_char_count,
> +        func(zero_measure/0) is zero_char_count,
> +        pred(measured_split/9) is char_count_split
> +].
> +
> +%------------------------------------------------------------------------------%
> +	% size_count is representation where the size of a term is
> +	% measured by number of lines and number of characters.
> +
> +:- func add_size_count(size_count, size_count, measure_params) = size_count.
> +
> +add_size_count(size_count(L1, C1), size_count(L2, C2), 
> +		Params) = size_count(Line, Char) :-
> +	( Params = measure_params(W) ->
> +		LineWidth = W
> +	;
> +		LineWidth = 80
> +	),
> +	( (L1 > 0 ; L2 > 0) ->
> +		( C1 > 0 ->
> +			R1 = L1 + 1
> +		;
> +			R1 = L1
> +		),
> +		( C2 > 0 ->
> +			R2 = L2 +1
> +		;	
> +			R2 = L2
> +		),
> +		Line = R1 + R2,
> +		Char = 0
> +	;
> +		( (C1 + C2) > LineWidth ->
> +			Line = 1,
> +			Char = 0
> +		;
> +			Line = 0,
> +			Char = C1 + C2
> +		)
> +	).
> +		
> +	% Rounding up the Lines and subtracting works because we assume
> +	% that each argument is a differnet line or they are all on 

s/differnet/different/

> +	% the same line. But this requires you to determine which case
> +	% likely to happen before hand. For example if a term is to be
> +	% on one line, you should do subtract_size_count(size_count(0, 
> +	% LineLength), size_count(0, arglength)) rather than
> +	% subtract_size_count(size_count(1, 0), size_count(0, arglength)).

Why not detect this situation in this code?  You have access to the line
width (in the measure_params), so you should be able to change an argument
of the form size_count(1, 0) into the required form.  Is there any reason
why this wouldn't work?

> +:- func subtract_size_count(size_count, size_count,measure_params) = size_count.
> +
> +subtract_size_count(size_count(L1, C1), size_count(L2, C2), _Params) 
> +		= size_count(Line, Char) :-
> +	( (L1 > 0 ; L2 > 0) ->
> +		( C1 > 0 ->
> +			R1 = L1 + 1
> +		;
> +			R1 = L1
> +		),
> +		( C2 > 0 ->
> +			R2 = L2 +1
> +		;	R2 = L2
> +		),
> +		Line0 = R1 - R2,
> +		Char0 = 0
> +	;
> +		Line0 = 0,
> +		Char0 = C1 - C2
> +	),
> +	( (Line0 < 0 ; Char0 < 0) ->
> +		Line = 0,
> +		Char = 0
> +	;
> +		Line = Line0,
> +		Char = Char0
> +	).
> +
> +:- func compare_size_count(size_count, size_count) = comparison_result.
> +
> +compare_size_count(size_count(L1, C1), size_count(L2, C2)) = R :-
> +	L1 = L2 ->
> +		compare(R, C1, C2)
> +	;
> +		compare(R, L1, L2).
> +
> +:- func max_size_count(size_count, size_count) = size_count.
> +
> +max_size_count(A, B) = Max :-
> +	( compare_size_count(A, B) = (>) ->
> +		Max = A
> +	;
> +		Max = B
> +	).
> +
> +:- func zero_size_count = size_count.
> +
> +zero_size_count = size_count(0, 0).
> +
> +	% This code divides up the Limit into smaller Limits for
> +	% the terms's arguments. We assume that all arguments have
> +	% to be on separte lines, or the whole term should be printed

s/separte/separate/

> +	% on a single line.
> +	% I have modified this code so that a term is not deconstructed
> +	% unless it has enough space to print functor and the functors
> +	% of it's arguments. But in some cases this check is not needed
> +	% that's why the booleen is included.
> +:- pred size_count_split(univ::in, measure_params::in, size_count::in,
> +	int::in, bool::in, size_count::out, maybe(size_count)::out,
> +	size_count::out, measure_params::out) is det.
> +
> +size_count_split(Univ, Params, Limit, Arity, Check, FunctorSize, 
> +		Flag, NewLimit, NewParams) :-
> +    ( Params = measure_params(X) ->
> +        LineWidth = X	
> +    ;
> +	LineWidth = 80
> +    ),
> +    deconstruct(univ_value(Univ), Functor, ActualArity, Args),
> +    FSize = string__length(Functor) + 2*(ActualArity),
> +    ( Check = yes ->
> +    	get_arg_length(Args, TotalLength, MaxLength)
> +    ;
> +    	TotalLength = 0,
> +	MaxLength = 0
> +    ), 
> +    ( Arity = 0 ->
> +	Flag = no,
> +    	FunctorSize = size_count(0, FSize),
> +	NewLimit = Limit,
> +	NewParams = Params
> +    ;
> +	Limit = size_count(LineLimit, CharLimit),
> +	( if (LineLimit >= (Arity + 1), (LineWidth - 2) >= MaxLength) then

Please put the two goals on separate lines, otherwise it is difficult
to read.  The coding standard shows how if-then-elses are best laid out
when the condition is complicated.

This applies in other places as well.

> +	    Line = (LineLimit - 1) // Arity,
> +	    Char = 0,
> +	    Flag = yes(size_count(Line, Char)),
> +	    FunctorSize = size_count(1, 0),
> +	    NewLimit = Limit,
> +	    NewParams = measure_params(LineWidth - 2)
> +	else if (LineLimit > 0, LineWidth >= (FSize + TotalLength)) then
> +	    Line = 0,
> +	    Char = (LineWidth - FSize + Arity - 1) // Arity ,
> +	    Flag = yes(size_count(Line, Char)),
> +	    FunctorSize = size_count(0, FSize),
> +	    NewLimit = size_count(0, LineWidth),
> +	    NewParams = Params
> +   	else if (CharLimit >= (FSize + TotalLength)) then
> +	   Line = 0,	
> +	   Char = (CharLimit - FSize + Arity - 1)// Arity,
> +	   Flag = yes(size_count(Line, Char)),
> +	   FunctorSize = size_count(0, FSize),
> +	   NewLimit = Limit,
> +	   NewParams = Params
> +	else
> +	   Flag = no, 
> +	   FunctorSize = size_count(0, string__length(Functor)+2),
> +	   NewLimit = Limit, 
> +	   NewParams = Params
> +	) 	
> +    ).
> +
> +:- instance measure(size_count) where [
> +	func(add_measures/3) is add_size_count,
> +	func(subtract_measures/3) is subtract_size_count,
> +	func(compare_measures/2) is compare_size_count,
> +	func(max_measure/2) is max_size_count,
> +	func(zero_measure/0) is zero_size_count,
> +	pred(measured_split/9) is size_count_split
> +].
> +
> +%------------------------------------------------------------------------------%
> +	% This predicate determines how many characters it will take
> +	% to print the functors of the arguments. Also determines the
> +	% length of biggest functor.
> +:- pred get_arg_length(list(univ)::in, int::out, int::out) is det.
> +
> +get_arg_length([], 0, 0).
> +get_arg_length([HeadUniv | Rest], TotalLength, MaxLength) :-
> +	deconstruct(univ_value(HeadUniv), Functor, Arity, _),
> +	( Arity = 0 -> 
> +		Length = string__length(Functor)
> +	;
> +		% 2 is added because if a term has arguments then the
> +		% shortest way to print it is "functor/Arity"
> +		% Assuming Arity is a single digit
> +		Length = string__length(Functor) + 2
> +	),
> +	TotalLength = Length + RestTotalLength,
> +	int__max(Length, RestMaxLength, MaxLength),
> +	get_arg_length(Rest, RestTotalLength, RestMaxLength).
> +
> +%------------------------------------------------------------------------------%
--------------------------------------------------------------------------
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