mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-14 13:23:53 +00:00
Estimated hours taken: 0.5 Branches: main browser/sized_pretty.m: Avoid the term "Flag" as a variable name, since in this context it is inaccurate and uninformative. Clarify the comment for the measured_split typeclass method.
937 lines
29 KiB
Mathematica
937 lines
29 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2001 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.
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% sized_pretty- When printing a term during debugging this module allows
|
|
% the user to put a limit on the size of the term displayed.
|
|
% This limit is specified by setting the number of lines you
|
|
% want and the width of these lines.
|
|
%
|
|
%
|
|
% author: sthur
|
|
%
|
|
% How to use sized_pretty.m :
|
|
% ---------------------------
|
|
%
|
|
% Call univ_to_string_line with the follwing variables:
|
|
% univ_to_string_line(Univ, LineWidth, Lines, String)
|
|
% where Univ : is the Term (in univ type) you want to convert
|
|
% LineWidth : is the length of the lines
|
|
% Lines : is the number of lines you want the term to be
|
|
% printed on
|
|
% String : output string
|
|
%
|
|
% EXAMPLES
|
|
% --------
|
|
%
|
|
% Term Used in these examples:
|
|
%
|
|
% Term = big(
|
|
% big(
|
|
% big(
|
|
% small,
|
|
% "Level 3",
|
|
% small
|
|
% ),
|
|
% "Level 2",
|
|
% small
|
|
% ),
|
|
% "Level 1",
|
|
% big(
|
|
% big(
|
|
% small,
|
|
% "Level 3",
|
|
% small
|
|
% ),
|
|
% "Level 2",
|
|
% small
|
|
% )).
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
% Width = 18, Line(s) = 16
|
|
%
|
|
% big(
|
|
% big(
|
|
% big(
|
|
% small,
|
|
% "Level 3",
|
|
% small),
|
|
% "Level 2",
|
|
% small),
|
|
% "Level 1",
|
|
% big(
|
|
% big(
|
|
% small,
|
|
% "Level 3",
|
|
% small),
|
|
% "Level 2",
|
|
% small))
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
% Width = 16, Line(s) = 16
|
|
%
|
|
% big(
|
|
% big(
|
|
% big/3,
|
|
% "Level 2",
|
|
% small),
|
|
% "Level 1",
|
|
% big(
|
|
% big/3,
|
|
% "Level 2",
|
|
% small))
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
% Width = 50, Line(s) = 4
|
|
%
|
|
% big(
|
|
% big(big/3, "Level 2", small),
|
|
% "Level 1",
|
|
% big(big/3, "Level 2", small))
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
% Width = 56, Line(s) = 4
|
|
%
|
|
% big(
|
|
% big(big(small, "Level 3", small), "Level 2", small),
|
|
% "Level 1",
|
|
% big(big(small, "Level 3", small), "Level 2", small))
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
% Width = 30, Line(s) = 5
|
|
%
|
|
% big(big/3, "Level 1", big/3)
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
% Width = 33, Line(s) = 5
|
|
%
|
|
% big(
|
|
% big(big/3, "Level 2", small),
|
|
% "Level 1",
|
|
% big(big/3, "Level 2", small))
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
% Width = 75, Line(s) = 1
|
|
%
|
|
% big(big(big/3, "Level 2", small), "Level 1", big(big/3, "Level 2", small))
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
% Width = 20, Line(s) = 10
|
|
%
|
|
% big(
|
|
% big(
|
|
% big/3,
|
|
% "Level 2",
|
|
% small),
|
|
% "Level 1",
|
|
% big(
|
|
% big/3,
|
|
% "Level 2",
|
|
% small))
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
% Width = 40, Line(s) = 10
|
|
%
|
|
% big(
|
|
% big(
|
|
% big(small, "Level 3", small),
|
|
% "Level 2",
|
|
% small),
|
|
% "Level 1",
|
|
% big(
|
|
% big(small, "Level 3", small),
|
|
% "Level 2",
|
|
% small))
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
% Width = 29, Line(s) = 1
|
|
%
|
|
% big(big/3, "Level 1", big/3)
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
% Width = 20, Line(s) = 1
|
|
%
|
|
% big/3
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module mdb__sized_pretty.
|
|
|
|
:- interface.
|
|
|
|
:- import_module std_util, int, string.
|
|
|
|
% This may throw an exception or cause a runtime abort if the term
|
|
% in question has user-defined equality.
|
|
% The Limit is number of lines.
|
|
:- pred univ_to_string_line(univ::in, int::in, int::in, string::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module list, require, assoc_list, pprint, bool.
|
|
|
|
:- type no_measure_params ---> no_measure_params.
|
|
:- type measure_params
|
|
---> measure_params(int). % This parameter specifies Linewidth
|
|
|
|
|
|
:- type maybe_deconstructed(T)
|
|
---> not_deconstructed
|
|
; deconstructed(
|
|
string, % Functor
|
|
int, % Arity
|
|
size_annotated_args(T) % arguments
|
|
).
|
|
|
|
% The exact type indicates the term has been fully deconstructed.
|
|
% The at_least type indicates that the term has been deconstructed
|
|
% upto the point where the specified limit was reached.
|
|
:- type size_annotated_term(T)
|
|
---> exact(
|
|
univ, % univ(Term)
|
|
T, % size of the term
|
|
string, % Functor
|
|
int, % Arity
|
|
size_annotated_args(T) % arguments
|
|
)
|
|
; at_least(
|
|
univ, % univ(Term)
|
|
T, % size of the term upto the point
|
|
% where it's deconstructed
|
|
maybe_deconstructed(T)
|
|
).
|
|
|
|
:- type size_annotated_args(T) ==
|
|
list(maybe(pair(T, size_annotated_term(T)))).
|
|
|
|
:- typeclass measure(T) where [
|
|
func max_measure(T, T) = T is det,
|
|
func zero_measure = T is det,
|
|
func compare_measures(T, T) = comparison_result is det
|
|
].
|
|
|
|
:- typeclass measure_with_params(T, MeasureParams) <= measure(T) where [
|
|
func add_measures(T, T, MeasureParams) = T is det,
|
|
func subtract_measures(T, T, MeasureParams) = T is det,
|
|
|
|
% Given a measurement and the measure parameters,
|
|
% calculate an upper bound on the number of functors
|
|
% that could fit in that space.
|
|
func maximum_functors(T, MeasureParams) = int,
|
|
|
|
% Given a term, its arity, and a limit, this method decides
|
|
% the partial limit that each of the arguments should be
|
|
% given. Its arguments in order are:
|
|
% - the term to print,
|
|
% - measure parameter(s),
|
|
% - measurement of the available space,
|
|
% - arity of the principal functor of the term,
|
|
% - a boolean which hints that the size of the principal
|
|
% functors of arguments should be checked before
|
|
% analyzing the term,
|
|
% - size of the principal functor of the term,
|
|
% - maybe an initial estimate of the measurement for
|
|
% each argument,
|
|
% - adjusted measurement of available space,
|
|
% - adjusted measure parameter(s).
|
|
pred measured_split(univ::in, MeasureParams::in, T::in, int::in,
|
|
bool::in, T::out, maybe(T)::out, T::out, MeasureParams::out) is det
|
|
|
|
].
|
|
|
|
%---------------------------------------------------------------------------%
|
|
% When the space (to print the term) is given in number of lines there
|
|
% is an inital check that has to be done. This is due to the fact that
|
|
% size_count_split assumes that every term is part of a bigger term and
|
|
% therefore assumes that a term will have a comma and a space after it.
|
|
% This is not true for the biggest term (Head Term) therefore if the
|
|
% Head Term is going to be printed on a single line then it should be
|
|
% given a limit of character_count(LineWidth - 1) instead of
|
|
% character_count(LineWidth - 3).
|
|
univ_to_string_line(Univ, LineWidth, Lines, String) :-
|
|
Params = measure_params(LineWidth),
|
|
functor(univ_value(Univ), _, Arity),
|
|
( Arity \= 0,
|
|
Lines \= 0,
|
|
(Lines - 1) // Arity = 0
|
|
->
|
|
% "- 1" is to account for the newline character
|
|
Limit = character_count(LineWidth - 1)
|
|
;
|
|
Limit = line_count(Lines)
|
|
),
|
|
annotate_with_size(Univ, Params, Limit, AnnotTerm),
|
|
Doc = to_doc_sized(AnnotTerm),
|
|
String = pprint__to_string(LineWidth, Doc).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
% first_pass gives an idea of how much space each term takes
|
|
% In this pass the space is unevenly distributed. First come first
|
|
% served. And once the space runs out, the term is not deconstructed
|
|
% further.
|
|
% In The Second pass the space is evenly distributed between
|
|
% the terms and therefore the subterms are deconstructed evenly.
|
|
:- pred annotate_with_size(univ::in, MeasureParams::in, T::in,
|
|
size_annotated_term(T)::out) is det
|
|
<= measure_with_params(T, MeasureParams).
|
|
|
|
annotate_with_size(Univ, Params, Limit, Size2) :-
|
|
first_pass(Univ, Params, Limit, Size1),
|
|
second_pass(Size1, Params, Limit, Size2).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred first_pass(univ::in, MeasureParams::in, T::in,
|
|
size_annotated_term(T)::out) is det
|
|
<= measure_with_params(T, MeasureParams).
|
|
|
|
first_pass(Univ, Params, Limit, Size) :-
|
|
MaxFunctors = maximum_functors(Limit, Params),
|
|
(
|
|
limited_deconstruct(univ_value(Univ), MaxFunctors,
|
|
Functor, Arity, UnivArgs)
|
|
->
|
|
measured_split(Univ, Params, Limit, Arity, yes, FunctorSize,
|
|
MaybeInitArgLimit, NewLimit, NewParams),
|
|
( (Arity \= 0, MaybeInitArgLimit = no) ->
|
|
Exact0 = no
|
|
;
|
|
Exact0 = yes
|
|
),
|
|
annotate_args_with_size(UnivArgs, MaybeInitArgLimit, 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)
|
|
)
|
|
;
|
|
Size = at_least(Univ, zero_measure, not_deconstructed)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
% annotating the arguments.
|
|
:- pred annotate_args_with_size(list(univ)::in, maybe(T)::in,
|
|
MeasureParams::in, T::in, T::in, T::out, bool::in, bool::out,
|
|
size_annotated_args(T)::out) is det <= measure_with_params(T,
|
|
MeasureParams).
|
|
|
|
annotate_args_with_size([], _, _, _, SoFar, SoFar, Exact, Exact, []).
|
|
annotate_args_with_size([Arg | Args], MaybeInitArgLimit, Params, Limit,
|
|
SoFar0, SoFar, Exact0, Exact,
|
|
[MaybeArgSize | MaybeArgSizes]) :-
|
|
(
|
|
MaybeInitArgLimit = yes(InitArgLimit),
|
|
( compare_measures(SoFar0, Limit) = (>) ->
|
|
AppliedArgLimit = InitArgLimit
|
|
;
|
|
AppliedArgLimit = max_measure(InitArgLimit,
|
|
subtract_measures(Limit, SoFar0, Params))
|
|
),
|
|
first_pass(Arg, Params, AppliedArgLimit, Size),
|
|
MaybeArgSize = yes(InitArgLimit - Size),
|
|
extract_size_from_annotation(Size) = ArgSize,
|
|
SoFar1 = add_measures(SoFar0, ArgSize, Params),
|
|
(
|
|
Size = exact(_, _, _, _, _),
|
|
Exact1 = Exact0
|
|
;
|
|
Size = at_least(_, _, _),
|
|
Exact1 = no
|
|
)
|
|
;
|
|
MaybeInitArgLimit = no,
|
|
MaybeArgSize = no,
|
|
SoFar1 = SoFar0,
|
|
Exact1 = Exact0
|
|
),
|
|
( compare_measures(SoFar1, Limit) = (>) ->
|
|
Exact2 = no
|
|
;
|
|
Exact2 = Exact1
|
|
),
|
|
annotate_args_with_size(Args, MaybeInitArgLimit, Params, Limit,
|
|
SoFar1, SoFar, Exact2, Exact, MaybeArgSizes).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- 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, MeasureParams::in, T::in,
|
|
size_annotated_term(T)::out) is det
|
|
<= measure_with_params(T, MeasureParams).
|
|
|
|
second_pass(OldSizeTerm, Params, Limit, NewSizeTerm) :-
|
|
(
|
|
OldSizeTerm = exact(_Univ, _Size, _, _Arity, _MaybeArgs),
|
|
NewSizeTerm = OldSizeTerm
|
|
;
|
|
OldSizeTerm = at_least(_Univ, _Size, not_deconstructed),
|
|
NewSizeTerm = OldSizeTerm
|
|
;
|
|
OldSizeTerm = at_least(Univ, _Size, deconstructed(Functor,
|
|
Arity,MaybeArgs)),
|
|
measured_split(Univ, Params, Limit, Arity, yes, FSize,
|
|
MaybeInitLimit, NewLimit, NewParams),
|
|
( if MaybeInitLimit = yes(InitLimit) then
|
|
check_args(NewParams, MaybeArgs, InitLimit, Passed,
|
|
FSize, Used),
|
|
LeftOver = add_measures(subtract_measures(NewLimit,
|
|
Used, Params), FSize, Params),
|
|
measured_split(Univ, Params, LeftOver, Arity - Passed,
|
|
no, _, MaybeSplitLimit, _, _),
|
|
( if MaybeSplitLimit = yes(SplitLimit) then
|
|
process_args(NewParams, MaybeArgs, InitLimit,
|
|
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)
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
% 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(MeasureParams::in, size_annotated_args(T)::in, T::in,
|
|
int::out, T::in, T::out) is det <= measure_with_params(T,
|
|
MeasureParams).
|
|
|
|
check_args(_, [], _, 0, Used0, Used0).
|
|
check_args(Params, [HeadArg | Rest], ArgLimit, 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, Passed,
|
|
Used0, Used)
|
|
else
|
|
Passed = 1 + PassedRest,
|
|
UsedSofar = add_measures(Used0, Size, Params),
|
|
check_args(Params, Rest, ArgLimit, PassedRest,
|
|
UsedSofar, Used)
|
|
)
|
|
else
|
|
check_args(Params, Rest, ArgLimit, Passed, Used0, Used)
|
|
)
|
|
else
|
|
check_args(Params, Rest, ArgLimit, 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(MeasureParams::in, size_annotated_args(T)::in, T::in,
|
|
T::in, size_annotated_args(T)::out, T::out) is det <=
|
|
measure_with_params(T, MeasureParams).
|
|
|
|
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),
|
|
(
|
|
STerm = exact(_, _, _, _, _),
|
|
(
|
|
compare_measures(ArgLimit, Size) = (>)
|
|
;
|
|
compare_measures(ArgLimit, Size) = (=)
|
|
)
|
|
->
|
|
NewHeadArg = HeadArg
|
|
;
|
|
NewHeadArg = yes(pair(SplitLimit, NewSTerm)),
|
|
annotate_with_size(Univ, Params, SplitLimit, NewSTerm)
|
|
)
|
|
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
|
|
;
|
|
Term = at_least(_, _, _),
|
|
Result = no
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
% A function to convert a size annotated term to a 'doc' type,
|
|
% a type defined in pprint.m.
|
|
:- func to_doc_sized(size_annotated_term(T)) = doc.
|
|
|
|
to_doc_sized(at_least(Univ, _, not_deconstructed)) = Doc :-
|
|
functor(univ_value(Univ), Functor, Arity),
|
|
Doc = text(Functor) `<>` text("/") `<>` poly(i(Arity)).
|
|
|
|
to_doc_sized(at_least(_, _, deconstructed(Functor, Arity, MaybeArgs))) = Doc :-
|
|
Doc = to_doc_sized_2(Functor, Arity, MaybeArgs).
|
|
|
|
to_doc_sized(exact(_, _, Functor, Arity, MaybeArgs)) = Doc :-
|
|
Doc = to_doc_sized_2(Functor, Arity, MaybeArgs).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
% Assumes that every argument must be on a different line
|
|
% or all of them should be on the same line.
|
|
:- func to_doc_sized_2(string, int, size_annotated_args(T)) = doc.
|
|
|
|
to_doc_sized_2(Functor, _Arity, []) = text(Functor).
|
|
|
|
to_doc_sized_2(Functor, Arity, [HeadArg|Tail]) = Doc :-
|
|
Args = list__map(handle_arg, [HeadArg|Tail]),
|
|
list__remove_adjacent_dups(Args, NewArgs),
|
|
( NewArgs \= [nil] ->
|
|
Doc = text(Functor) `<>` parentheses(group(nest(2, line `<>`
|
|
separated(id,comma_space_line, Args))))
|
|
;
|
|
Doc = text(Functor) `<>` text("/") `<>` poly(i(Arity))
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- func handle_arg(maybe(pair(T,size_annotated_term(T)))) = doc.
|
|
|
|
handle_arg(yes(_ - Arg_Term)) = to_doc_sized(Arg_Term).
|
|
handle_arg(no) = nil.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
% functor_count is a representation where the size of a term
|
|
% is measured by the number of function symbols.
|
|
|
|
:- type functor_count
|
|
---> functor_count(int). % No of function symbols
|
|
|
|
:- func add_functor_count(functor_count, functor_count,
|
|
no_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,
|
|
no_measure_params) = functor_count.
|
|
|
|
subtract_functor_count(functor_count(A), functor_count(B), _) =
|
|
functor_count(A - B).
|
|
|
|
:- func maximum_functor_count(functor_count, no_measure_params) = int.
|
|
|
|
maximum_functor_count(functor_count(N), _) = N.
|
|
|
|
:- 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).
|
|
|
|
:- pred functor_count_split(univ::in, no_measure_params::in, functor_count::in,
|
|
int::in, bool::in, functor_count::out, maybe(functor_count)::out,
|
|
functor_count::out, no_measure_params::out) is det.
|
|
|
|
functor_count_split(_, Params, functor_count(Limit), Arity, _, functor_count(1),
|
|
MaybeArgLimit, functor_count(Limit), Params) :-
|
|
( Arity = 0 ->
|
|
MaybeArgLimit = no
|
|
;
|
|
( Limit =< (Arity + 1) ->
|
|
MaybeArgLimit = no
|
|
;
|
|
RoundUp = (Limit + Arity - 1) // Arity,
|
|
MaybeArgLimit = yes(functor_count(RoundUp))
|
|
)
|
|
).
|
|
|
|
:- instance measure(functor_count) where [
|
|
func(compare_measures/2) is compare_functor_count,
|
|
func(max_measure/2) is max_functor_count,
|
|
func(zero_measure/0) is zero_functor_count
|
|
].
|
|
|
|
:- instance measure_with_params(functor_count, no_measure_params) where [
|
|
func(add_measures/3) is add_functor_count,
|
|
func(subtract_measures/3) is subtract_functor_count,
|
|
func(maximum_functors/2) is maximum_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.
|
|
|
|
:- type char_count
|
|
---> char_count(int). % No of characters
|
|
|
|
:- func add_char_count(char_count, char_count, no_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,
|
|
no_measure_params) = char_count.
|
|
|
|
subtract_char_count(char_count(A), char_count(B), _) =
|
|
char_count(A - B).
|
|
|
|
:- func maximum_char_count(char_count, no_measure_params) = int.
|
|
|
|
maximum_char_count(char_count(N), _) = Max :-
|
|
% Each functor except the first takes a minimum of three chars.
|
|
Max = (N + 2) div 3.
|
|
|
|
:- 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).
|
|
|
|
:- pred char_count_split(univ::in, no_measure_params::in, char_count::in,
|
|
int::in, bool::in, char_count::out, maybe(char_count)::out,
|
|
char_count::out, no_measure_params::out) is det.
|
|
|
|
char_count_split(Univ, Params, char_count(Limit), Arity, Check,
|
|
char_count(FunctorSize), MaybeArgLimit, char_count(Limit),
|
|
Params) :-
|
|
|
|
deconstruct(univ_value(Univ), Functor, _, Args),
|
|
( Check = yes ->
|
|
get_arg_length(Args, TotalLength, _)
|
|
;
|
|
TotalLength = 0
|
|
),
|
|
FunctorSize = string__length(Functor) + 2*(Arity),
|
|
( Arity = 0 ->
|
|
MaybeArgLimit = no
|
|
;
|
|
( Limit =< (FunctorSize + TotalLength) ->
|
|
MaybeArgLimit = no
|
|
;
|
|
RoundUp = (Limit + Arity - FunctorSize) // Arity,
|
|
MaybeArgLimit = yes(char_count(RoundUp))
|
|
)
|
|
).
|
|
|
|
:- instance measure(char_count) where [
|
|
func(compare_measures/2) is compare_char_count,
|
|
func(max_measure/2) is max_char_count,
|
|
func(zero_measure/0) is zero_char_count
|
|
].
|
|
|
|
:- instance measure_with_params(char_count, no_measure_params) where [
|
|
func(add_measures/3) is add_char_count,
|
|
func(subtract_measures/3) is subtract_char_count,
|
|
func(maximum_functors/2) is maximum_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 or number of characters.
|
|
|
|
:- type size_count
|
|
---> line_count(int) % no of lines
|
|
; character_count(int). % no of characters
|
|
|
|
:- func add_size_count(size_count, size_count, measure_params) = size_count.
|
|
|
|
add_size_count(character_count(A), character_count(B), Params) = Result :-
|
|
Params = measure_params(LineWidth),
|
|
CharSum = A + B,
|
|
( CharSum > LineWidth ->
|
|
Result = line_count(1)
|
|
;
|
|
Result = character_count(CharSum)
|
|
).
|
|
|
|
add_size_count(character_count(A), line_count(B), _) = Result :-
|
|
( A > 0 ->
|
|
Result = line_count(B + 1)
|
|
;
|
|
Result = line_count(B)
|
|
).
|
|
|
|
add_size_count(line_count(A), character_count(B), _) = Result :-
|
|
( B > 0 ->
|
|
Result = line_count(A + 1)
|
|
;
|
|
Result = line_count(A)
|
|
).
|
|
|
|
add_size_count(line_count(A), line_count(B), _) = line_count(A + B).
|
|
|
|
% Rounding up the Lines and subtracting works because we assume
|
|
% that each argument is a different line or they are all on
|
|
% 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(character_count(
|
|
% LineLength), charater_count(arglength)) rather than
|
|
% subtract_size_count(line_count(1), character_count(arglength)).
|
|
% The reason that this situation cannot be detected in this code is:
|
|
% A term can be printed on a single line only if, all of it's
|
|
% arguments can be printed on the same line. And you cannot determine
|
|
% which case is likely to happen in this code using the information
|
|
% it has. Therefore size_count_split determines which case is true
|
|
% (and changes the limit accordingly).
|
|
:- func subtract_size_count(size_count, size_count,measure_params) = size_count.
|
|
|
|
subtract_size_count(character_count(A), character_count(B), _) = Result :-
|
|
CharDiff = A - B,
|
|
( CharDiff < 0 ->
|
|
Result = character_count(0)
|
|
;
|
|
Result = character_count(CharDiff)
|
|
).
|
|
|
|
subtract_size_count(character_count(A), line_count(B), _) = Result :-
|
|
( B = 0 ->
|
|
Result = character_count(A)
|
|
;
|
|
Result = character_count(0)
|
|
).
|
|
|
|
subtract_size_count(line_count(A), character_count(B), _) = Result :-
|
|
( B = 0 ->
|
|
Result = line_count(A)
|
|
;
|
|
( A - 1 >= 0 ->
|
|
Result = line_count(A - 1)
|
|
;
|
|
Result = line_count(0)
|
|
)
|
|
).
|
|
|
|
subtract_size_count(line_count(A), line_count(B), _) = Result :-
|
|
( A - B >= 0 ->
|
|
Result = line_count(A - B)
|
|
;
|
|
Result = line_count(0)
|
|
).
|
|
|
|
:- func maximum_size_count(size_count, measure_params) = int.
|
|
|
|
maximum_size_count(character_count(N), _) = Max :-
|
|
% Each functor except the first takes a minimum of three chars.
|
|
Max = (N + 2) div 3.
|
|
|
|
maximum_size_count(line_count(N), measure_params(LineWidth)) = Max :-
|
|
% We can't assume any particular layout, so we go by how many
|
|
% functors will fit in the area covered by N lines. Each functor
|
|
% except the first takes a minimum of three chars.
|
|
Area = N * LineWidth,
|
|
Max = (Area + 2) div 3.
|
|
|
|
:- func compare_size_count(size_count, size_count) = comparison_result.
|
|
|
|
compare_size_count(character_count(C1), character_count(C2)) = R :-
|
|
compare(R, C1, C2).
|
|
|
|
compare_size_count(character_count(_), line_count(_)) = (<).
|
|
|
|
compare_size_count(line_count(_), character_count(_)) = (>).
|
|
|
|
compare_size_count(line_count(L1), line_count(L2)) = R :-
|
|
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 = character_count(0).
|
|
|
|
% We assume that all arguments have to be on separate lines, or
|
|
% the whole term should be printed on a single line.
|
|
:- 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,
|
|
MaybeArgLimit, NewLimit, NewParams) :-
|
|
% LineWidth is length of the line in which the functor is printed.
|
|
Params = measure_params(LineWidth),
|
|
deconstruct(univ_value(Univ), Functor, ActualArity, Args),
|
|
FSize = string__length(Functor) + 2 * (ActualArity),
|
|
( Check = yes ->
|
|
get_arg_length(Args, TotalLength, MaxArgLength),
|
|
int__max(MaxArgLength, (string__length(Functor) + 1), MaxLength)
|
|
;
|
|
TotalLength = 0,
|
|
MaxLength = 0
|
|
),
|
|
(
|
|
Arity = 0
|
|
->
|
|
MaybeArgLimit = no,
|
|
FunctorSize = character_count(FSize),
|
|
NewLimit = Limit,
|
|
NewParams = Params
|
|
;
|
|
(
|
|
Limit = line_count(LineLimit),
|
|
% we need one line for the functor and atleast
|
|
% one line for each argument
|
|
LineLimit >= (Arity + 1),
|
|
% linewidth is decreased by two characters to account
|
|
% for indentation
|
|
(LineWidth - 2) >= MaxLength
|
|
->
|
|
Line = (LineLimit - 1) // Arity,
|
|
MaybeArgLimit = yes(line_count(Line)),
|
|
FunctorSize = line_count(1),
|
|
NewLimit = Limit,
|
|
NewParams = measure_params(LineWidth - 2)
|
|
;
|
|
Limit = line_count(LineLimit),
|
|
LineLimit > 0,
|
|
% Since every term is part of a bigger term (in this
|
|
% context anyway) it will have a comma, space and a
|
|
% newline at the end of it (Hence the "- 3").
|
|
LineWidth - 3 >= (FSize + TotalLength)
|
|
->
|
|
% "Arity - 1" is for rounding up.
|
|
Char = (LineWidth - 3 - FSize + Arity - 1) // Arity ,
|
|
MaybeArgLimit = yes(character_count(Char)),
|
|
FunctorSize = character_count(FSize),
|
|
NewLimit = character_count(LineWidth - 3),
|
|
NewParams = Params
|
|
;
|
|
Limit = character_count(CharLimit),
|
|
CharLimit >= (FSize + TotalLength)
|
|
->
|
|
Char = (CharLimit - FSize + Arity - 1) // Arity,
|
|
MaybeArgLimit = yes(character_count(Char)),
|
|
FunctorSize = character_count(FSize),
|
|
NewLimit = Limit,
|
|
NewParams = Params
|
|
;
|
|
MaybeArgLimit = no,
|
|
% If a term is not deconstructed, it is printed as
|
|
% "functor/Arity". The "+ 2" accounts for that.
|
|
FunctorSize =
|
|
character_count(string__length(Functor) + 2),
|
|
NewLimit = Limit,
|
|
NewParams = Params
|
|
)
|
|
).
|
|
|
|
:- instance measure(size_count) where [
|
|
func(compare_measures/2) is compare_size_count,
|
|
func(max_measure/2) is max_size_count,
|
|
func(zero_measure/0) is zero_size_count
|
|
].
|
|
|
|
:- instance measure_with_params(size_count, measure_params) where [
|
|
func(add_measures/3) is add_size_count,
|
|
func(subtract_measures/3) is subtract_size_count,
|
|
func(maximum_functors/2) is maximum_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) :-
|
|
functor(univ_value(HeadUniv), Functor, Arity),
|
|
( Rest = [] ->
|
|
Correction = 2
|
|
;
|
|
Correction = 3
|
|
),
|
|
( 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 + Correction), RestMaxLength, MaxLength),
|
|
get_arg_length(Rest, RestTotalLength, RestMaxLength).
|
|
|
|
%---------------------------------------------------------------------------%
|