[m-dev.] Contribution: Pretty Printing Library

Fergus Henderson fjh at cs.mu.OZ.AU
Wed Apr 12 21:54:03 AEST 2000


On 12-Apr-2000, Ralph Becket <rbeck at microsoft.com> wrote:
> The question is what semantics do we want for nested string `nest's?
> Should we have
> 
> nest("my ", nest("label: ", Doc)) = nest("my label: ", Doc)
> 
> or
> 
> nest("my ", nest("label: ", Doc)) = nest("label: ", Doc)
> 
> ?

Probably the former, I think.

> What should we do for `nest(2, nest("my label: ", Doc))' ?

That should definitely indent by two spaces followed by
"my label:".

For example, if you're formatting C code, then you might
want to be able to format something like this:

	if (...) {
		/*
		** blah blah
		** blah blah
		*/
	}

Here that would be represented by
`nest(8, ... nest("** ", ...) ...)',
and you want the indentation for the comment
lines to come out as 8 spaces followed by "** ".

Similarly for formatting Mercury error messages, you want
something like

	nest("file:123: ", nest(2, ...))

to be formatted with the second and subsequent lines
indented by "file:123: " plus two spaces.

BTW, I suggest spelling the one that indents by an
int as `indent' rather than `nest'.

> We could add a second kind of nest taking a string as its first
> argument rather than an indentation amount, e.g.
> 
> pprint__write(N,
>   nest(
>     "my label: ",
>     line `<>` group(text("this is ") `</>` text("some text"))
>   )
> )
> 
> would produce the following, depending upon N:
> 
> my label: this is some text		{ N > 24 }
> 
> or
> 
> my label: this is
> my label: some text			{ N < 24 }

I notice that you have defined the string version of
nest to always insert the label at the start,
whereas the int version only inserts the spaces
after newlines.

I think that is probably a mistake.  For example,
if I want to format C comments in the style

		/* blah blah
		** blah blah
		** blah blah
		*/

then I want the string version of nest to not insert the
"** " at the start -- I want "/* " at the start, not "** ".

Note that given the version which does not insert at
the start, you can easily define the version which does,
e.g.

	:- func prefix(string, doc) = doc.
	prefix(Prefix, Doc) = nest(Prefix, Prefix `<>` Doc).

but you can't do it the other way around.

Something else that would be nice would be providing a way
to insert something at the end of every line.  This would
be useful for formatting code in languages such as `sh',
`sed', or `make' that use line continuation characters
(e.g. trailing backslash).

Another nice feature would be a function which handles word-wrap.
This can be programmed easily enough using the functions in
your pretty-print module, but I think it would be useful
enough to include in that module.

		% Splits the string at spaces, and joins the resulting
		% pieces using `separated(text, space_line, Pieces)'.
	:- func word_wrap(string) = doc.
	word_wrap(String) = separated(text, space_line, Words) :-
		Words = split_string(String, ' ').

See the attached files for a definition of split_string.

-- 
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.
-------------- next part --------------
:- interface.
:- import_module string, list, char.

:- func split_string(string, char) = list(string).

:- implementation.
:- import_module int.

split_string(String, Separator) = Pieces :-
	string__length(String, Len),
	split_string_2(String, Separator, Len - 1, Len - 1, [], Pieces).

:- mode split_string_2(in, in, in, in, in, out) is det.
split_string_2(String, Separator, Posn, EndPosn, Pieces0, Pieces) :-
	(
		Posn >= 0,
		string__unsafe_index(String, Posn, Char),
		Char \= ' '
	->
		split_string_2(String, Separator,
			Posn - 1, EndPosn, Pieces0, Pieces)
	;
		( EndPosn > Posn ->
			string__unsafe_substring(String, Posn + 1,
				EndPosn - Posn, Word),
			Pieces1 = [Word | Pieces0]
		;
			Pieces1 = Pieces0
		),
		( Posn < 0 ->
			Pieces = Pieces1
		;
			split_string_2(String, Separator,
				Posn - 1, Posn - 1, Pieces1, Pieces)
		)
	).

-------------- next part --------------
:- module ww_test.
:- interface.
:- import_module io.

:- pred main(state::di, state::uo) is det.

:- implementation.
:- import_module ww.

main -->
	test("foo bar baz"),
	test(" foo bar baz"),
	test("  foo bar baz"),
	test("foo bar baz "),
	test("foo bar baz  "),
	test("foo  baz"),
	test("foo   baz"),
	test("foo    baz"),
	test(""),
	test(" "),
	test("  "),
	test("   ").

test(String) -->
	{ Words = split_string(String, ' ') },
	print("split_string(\""),
	print(String),
	print("\") = "),
	print(Words),
	nl.


More information about the developers mailing list