Elegant prolog codes on displaying answers for queries having multiple answers

I wrote a simple emacs handler in SWI-Prolog around 2010, which have been working stable based on asynchronous communication start-process. However recently enter key is found not working as was expected. Fortunately I could fix it, though spending much time and efforts. Here is one of bi-products of the fix. Now it can mimic one of Ediprolog mode features. See sample log for samle queries below.

The following codes is added for the new feature. I tried not to use cut (!) in ugly way, but there I had to use destructive non-backtrackable nb_setarg/3. Without that the codes got dirty for my feeling, though perhaps due to my limitation. I appreciate for elegant way of writing prolog codes for such typical “case” sentence.

user:solve_query(H) :-
    term_codes(Q, H, [variable_names(Vs)]),
	solve_and_answer(ans_count(0), Q, Vs).
%
solve_and_answer(_, Q, []):-!,
	(   Q -> A = true
    ;   A = false
    ),
    send_text(["\n", A, ".\n"]).
solve_and_answer(F, Q, Vs):-
	(   Q,
        maplist(display_bind, Vs),
		nb_setarg(1, F, 1),				%  destructive non-backtracable setarg 
		once(ask_next_key(P)),
        (   P == next -> send_text(";"), fail
        ;   P == stop -> send_text(".\nstop.\n"), !
        ;   send_text("\nstop.\n"), !
        )
    ;   arg(1, F, 0) -> send_text("\nfalse.\n")
	;	send_text("\nno more.\n")
    ).
%
display_bind(X=_) :- sub_atom(X, 0, 1, _, '_'), !.
display_bind(Pair) :-
    format(string(S), "\n~w", [Pair]),
    send_text([S]).
%
ask_next_key(P) :-
    call_lisp('read-prolog-interaction-event'(), term(P)).
?- member(b, [a,b,c]).
true.
?- member(d, [a,b,c]).
false.
?- member(X, [a,b,c]).
X=a;
X=b;
X=c;
no more.
?- member(X, [a,b,c]).
X=a.
stop.
?- append(X, Y, [a,b,c]).
X=[]
Y=[a,b,c];
X=[a]
Y=[b,c];
X=[a,b]
Y=[c];
X=[a,b,c]
Y=[];
no more.
?- append(X, X, [a,b,c]).
false.

term_codes ?
term_codes/3 seems not to exist in the swi-prolog.org manual

It is a private predicate defined as follows.

% ?- term_codes(f(a,X), C, [variable_names(['X'=X])]), basic:smash(C).
% ?- term_codes(f(a,X), C, [variable_names(['X'=X])]).
% ?- string_codes("f(\"a\")", R),  term_codes(T, R).
% ?- term_string(T, "f(X, \"a\", Y)", [variable_names(V)]).
% ?- term_codes(T, `f(X, \"a\", Y)`, [variable_names(V)]).

term_codes(X, Y, Opts):- nonvar(Y), !,
	string_codes(S, Y),
	term_string(X, S, Opts).
term_codes(X, Y, Opts):-
	term_string(X, S, Opts),
	string_codes(S, Y).

% ?- term_codes(X, `f(\"a\")`).
term_codes(X, Y):- term_codes(X, Y, [variable_names([])]).

Actually, term_string/3 accepts anything that is text is input to read from, i.e., a string, atom, list of codes or list of chars. That is the case for most SWI-Prolog predicates that need to consume “text” from some argument. The type name merely defines the output type in case the argument is uninstantiated.

Thanks for comment, @Jan. Of course I think I know the generic power of conversion between terms and strings, but I could not use such power directly for asynchronous communications between Emacs (plus@30) and Swi-prolog, because of unexpected segmentaton of data stream. To build up complete one from segmentations I had to add ad hoc byte xff (255) for each primitive data (term or S-expression). I thought there should be some setting for my purpose of commnucatioon to use directly term_string, but I couldn’t find it, and I preferred to give the minimum patch to the old working version.

I have found pair of open_string/2 and read_term/3 has much more power
than term_string/2,3, which is also powerful already.
It makes no need of parsing comments at end of lines,
which must be very hard and tedious to write for the user.

user:solve_query(H) :-
	string_codes(S, H),
	open_string(S, Stream),
	read_term(Stream, Q, [variable_names(Vs)]),
	Q = ?-(Goal),
	solve_and_answer(ans_count(0), Goal, Vs).

How come? Note that `hello(World)` is a code-list.

1 ?- term_string(Q, `hello(World)`, [variable_names(Bindings)]).
Q = hello(_A),
Bindings = ['World'=_A].

For curiosity, I happened to want to accept following query such as bellow in emacs buffer (like ediprolog mode, but not the same). It is easy to drop leading fillers at lines, but not easy to drop commnens at end of lines. Each query after dropping prefix fillers is sent with \xff byte as end-of-date byte, which is reason why prolog side has to get query by only using get_code/2. But once once after getting codes for query, open_string and read-term parse the query as if with no comments at end of each line. So I thought the pair is powerful, otherwise, I have to parse format sentence or string terms to know if the % is the comment symbol or not.
Notice that codes on eliminating reading fillers on emacs side is simple.

Is there easy way to drop comments at end of lines ? I guessed not.

%@ ?-  append(X, Y, [a,b,c]).

X=[]
Y=[a,b,c];
X=[a]
Y=[b,c];
X=[a,b]
Y=[c];
X=[a,b,c]
Y=[].



% ?-  append(  
%%%			 
%%	  X
%	  ,			% .    
Y, [
	a,		   % "Hello.\n "
	b
	,c
]).

X=[]
Y=[a,b,c];
X=[a]
Y=[b,c];
X=[a,b]
Y=[c];
X=[a,b,cwh]
Y=[];
no more.

Al I want to say is that to get from a list of codes to a term, term_string/3 is doing exactly the same as string_codes/2, open_string/2 and read_term/3 (where you must close the stream).

In Prolog? Generally it is not so easy as you have to identify quoted material to see whether the % is between quotes. That is difficult as we also have 0'<char>, where <char> is anything that can appear inside a quoted string, so 0''' reads as 39 ('). SWI-Prolog provides for its toplevel '$raw_read'(+Input, -Atom) (should be String) that reads a query as an atom, merely detecting the full stop and changing all comments into white space (to ensure position logic still works).

Oh ! It is real. Thank you.

?- term_string(X, "a % + b.\\n").
X = a.

I thought I had to write in regex in elisp to cut off the commens.
Now I know that the term_string/2,3 extends life of human.