Is there a better way to convert a compound term to a string than this?

One of the most common uses of DCG is to parse unstructured text into a compound term/structure, e.g.

the number 1 into number(1).

However in the real world some of those generated structures get rather large and would look nicer if put through a pretty-printer when viewing e.g.

structure(id(23,0),dictionary([key_value(name(Length),number(1992)),key_value(name(Filter),name(FlateDecode))]))

would look nicer as

structure(
    id(23,0),
    dictionary([
        key_value(name(Length),number(1992)),
        key_value(name(Filter),name(FlateDecode))
    ])
)

Here is some basic code that works as the start of a pretty-printer, but it is begging to be turned into a DCG and have the use of append/3 removed.

format(number(Number),S0,S) :-
    string_codes("Number: ",Prompt_codes),
    append(S0,Prompt_codes,S1),
    atom_codes(Number,Number_codes),
    append(S1,Number_codes,S).

Yes I know format/3 is not format//1 as is customary but would like it to be.

And some test cases to understand how it should work.

:- begin_tests(format).

test(001) :-
    Structure = number(1),
    format(Structure,[],Codes),
    string_codes(String,Codes),
    assertion( String ==  "Number: 1" ).

test(002) :-
    Structure = number(1),
    DCG = format(Structure),
    phrase(DCG,[],Codes),
    string_codes(String,Codes),
    assertion( String ==  "Number: 1" ).

:- end_tests(format).

For those not familiar with SWI-Prolog test cases, running is simple.

?- run_tests(format).
% PL-Unit: format .. done
% All 2 tests passed
true.

As I have never done DCGs in this manner (compound terms/structure to string) and this seems like it would have been done before without using append/3. Any comments, suggestions, feedback, example code, a rewrite of my code, etc., are sought.

$ swipl
Welcome to SWI-Prolog (threaded, 64 bits, version 8.1.6)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
Please run ?- license. for legal details.

For online help and background, visit http://www.swi-prolog.org
For built-in help, use ?- help(Topic). or ?- apropos(Word).

1 ?- print_term(structure(id(23,0),dictionary([key_value(name(Length),number(1992)),key_value(name(Filter),name(FlateDecode))])), [tab_width(0)]).
structure(id(23,0),
          dictionary([ key_value(name(_),number(1992)),
                       key_value(name(_),name(_))
                     ]))
true.

1 Like

@peter.ludemann Didn’t think it could be so easy and and it never occurred to me to look for a built in predicate such as print_term/2.

Will try this on the harder terms that run a few pages and see how it holds up.

EDIT

Worked first time out of the gate on a term several pages long. :slightly_smiling_face:
Live and learn.

Thanks.

A long time ago, I wrote a DCG for converting C declarations to human-readable form and vice-versa (similar to cdecl). It could run in either direction — it could convert declare foo as pointer to function (void) returning pointer to array 3 of int to int (*(*foo)(void ))[3] and also could convert int (*(*foo)(void ))[3] to declare foo as pointer to function (void) returning pointer to array 3 of int .

Also, there’s no reason that the accumulator in a DCG should be a list. See edcg.

(I once wrote a pretty-printer that used DCG-like formalism, but I don’t know where it is now. Besides building up the result in a list, it also passed around information such as indent level and other context.)

I have been aware of the extended DCG library for some time, and came close to using it once or twice but never made it over the hump.

@jamesnvc added a post to the new Prolog Hub with a section on Extended DCG. Just might use it tomorrow as I do have a need for multiple accumulators for parsing PDFs. :slightly_smiling_face:

1 Like