Strings when debugging DCGs

Tracing DCGs is difficult because input is shown as a list of codes: is there a way to see it as a string?

2 Likes

To start you out … I’ll often do something like this

:- multifile user:portray/1.

% Define the materialize_some_codes/3.
materialize_some_codes(Tail, [], Tail) :-   \+ compound(Tail),!.
materialize_some_codes([H|T], [H|Materialized], Rest) :-
    integer(H), H >= 9, H =< 127,!,
    materialize_some_codes(T, Materialized, Rest).


% Extend user:portray to handle partially instantiated lists of ASCII codes.
user:portray(CodeListWithUnboundTail) :- 
    % incase you really only want it while tracing
    % tracing, 
    % Apply materialize_some_codes to split the list.
    materialize_some_codes(CodeListWithUnboundTail, MaterializedPart, Rest),
    MaterializedPart \== [], 
    (Rest == [] 
         ->  format('`~s`', [MaterializedPart])
          % Print the partially materialized list as a string, followed by the tail.
          ; format('<~s|~p>', [MaterializedPart,  Rest ])).

oh and make sure debugger options to use portray(true) for printing terms.
:- set_prolog_flag(debugger_print_options, …)

Douglas

3 Likes

And, there is a library for that. See portray_text/1. The GUI tracer also has a setting that exploits this library. Notably if you also want to trace Unicode, character code lists and integer lists quickly becomes problematic. That would be a reason to switch to “chars”. Just, the transition is cumbersome and chars are a little more expensive, again especially when the full Unicode range is to be supported.

3 Likes

Can add to e.g. ~/.config/swi-prolog/init.pl to use set_portray_text/2

% Show lists of codes as text (if 3 chars or longer) 
:- set_portray_text(enabled, false). 
% Default is 3      
:- set_portray_text(min_length, 1).

Then:

?- L = [97,99,101].
L = `ace`.
3 Likes

It might be premature to label this as a definitive solution. This approach doesn’t seem to function within the command line debugger, which is significant since, in debugging, Definite Clause Grammars (DCGs) are commonly associated with partial and lazy lists. The predicates set_portray_text/2 and portray_text/1 are typically effective for fully completed lists, likely observed at the EXIT port during trace.

From what I’ve experienced, the consistent way to ensure representation during all phases of debugging involves overriding the portray/1 predicate. This adjustment allows for the depiction of partially instantiated lists for instance, <abc|_G666>. However, it’s important to note that this format doesn’t constitute a legally valid portrayal for an incomplete string, hence it may not be the most conventional method:

? - L = [97,99,101|_].
L = <abc|_G666>.

Should work fine:

87 ?- portray_text(true).
true.

88 ?- append(`hello world`, X, P).
P = `hello world|X`.

89 ?- L = [97,99,101|_].
L = `ace|_`.
1 Like

wow that is easy… thank you both!

You have probably noticed that this breaks the promise of being able to paste the input back to the top-level.

With SWI-Prolog 9.3.3:

?- X = `foo|Bar`, display(X).
'[|]'(102,'[|]'(111,'[|]'(111,'[|]'(124,'[|]'(66,'[|]'(97,'[|]'(114,[])))))))
X = `foo|Bar`.

?- X = [0'f,0'o,0'o|Bar], display(X).
'[|]'(102,'[|]'(111,'[|]'(111,_50336)))
X = `foo|Bar`.

Where should this be documented?

True. If you start using portray/1 that is what often happens as want to produce human readable output. If you prefer to have that documented, portray_text/1 and possibly portray/1, I’d say. There are a lot of misconceptions about when to use which print primitive …