OK, this is not a simple example but I’m not sure how to make this simpler.
This is the output of two sessions, the first one where my query succeeds when it should fail and the second when it fails correctly. I also list the contents of the database which are the same in both sessions (and this is why I suspect a bug in SWI rather than a bug in my code).
First session, succeeds when it should fail:
119 ?- _I = 9, ground_csg(abc,[a,b,c],['S'],_I,Ps).
Ps = [a, a, a, b, b, b, c, c, c] .
120 ?- listing(abc).
grammars:abc(['S'|Ss], [a, 'S', 'B', 'C'|A], B) :-
abc(Ss, A, B).
grammars:abc(['S'|Ss], [a, 'B', 'C'|A], B) :-
abc(Ss, A, B).
grammars:abc(['C', 'B'|Ss], ['B', 'C'|A], B) :-
!,
C=A,
abc(Ss, C, B).
grammars:abc([a, 'B'|Ss], [a, b|A], B) :-
!,
C=A,
abc(Ss, C, B).
grammars:abc([b, 'B'|Ss], [b, b|A], B) :-
!,
C=A,
abc(Ss, C, B).
grammars:abc([b, 'C'|Ss], [b, c|A], B) :-
!,
C=A,
abc(Ss, C, B).
grammars:abc([c, 'C'|Ss], [c, c|A], B) :-
!,
C=A,
abc(Ss, C, B).
grammars:abc(['B'|Ss], ['B'|A], B) :-
!,
C=A,
abc(Ss, C, B).
grammars:abc(['C'|Ss], ['C'|A], B) :-
!,
C=A,
abc(Ss, C, B).
grammars:abc([b|Ss], [b|A], B) :-
!,
C=A,
abc(Ss, C, B).
grammars:abc([c|Ss], [c|A], B) :-
!,
C=A,
abc(Ss, C, B).
grammars:abc([], A, B) :-
A=B.
true.
Second session, fails when it should fail:
101 ?- _I = 9, ground_csg(abc,[a,b,c],['S'],_I,Ps).
false.
102 ?- listing(abc).
grammars:abc(['S'|Ss], [a, 'S', 'B', 'C'|A], B) :-
abc(Ss, A, B).
grammars:abc(['S'|Ss], [a, 'B', 'C'|A], B) :-
abc(Ss, A, B).
grammars:abc(['C', 'B'|Ss], ['B', 'C'|A], B) :-
!,
C=A,
abc(Ss, C, B).
grammars:abc([a, 'B'|Ss], [a, b|A], B) :-
!,
C=A,
abc(Ss, C, B).
grammars:abc([b, 'B'|Ss], [b, b|A], B) :-
!,
C=A,
abc(Ss, C, B).
grammars:abc([b, 'C'|Ss], [b, c|A], B) :-
!,
C=A,
abc(Ss, C, B).
grammars:abc([c, 'C'|Ss], [c, c|A], B) :-
!,
C=A,
abc(Ss, C, B).
grammars:abc(['B'|Ss], ['B'|A], B) :-
!,
C=A,
abc(Ss, C, B).
grammars:abc(['C'|Ss], ['C'|A], B) :-
!,
C=A,
abc(Ss, C, B).
grammars:abc([b|Ss], [b|A], B) :-
!,
C=A,
abc(Ss, C, B).
grammars:abc([c|Ss], [c|A], B) :-
!,
C=A,
abc(Ss, C, B).
grammars:abc([], A, B) :-
A=B.
true.
Note that the listing is the same in both sessions. Unless I’m missing something (possible) the behaviour should be the same in both sessions, if both sessions are working off the same database.
This is the code. First my DCG, with the rule for ‘a’ commented-out so that it should fail:
abc(['S'|Ss]) --> [a,'S','B','C'], abc(Ss).
abc(['S'|Ss]) --> [a,'B','C'], abc(Ss).
abc(['C','B'|Ss]) --> ['B','C'], !, abc(Ss).
abc([a,'B'|Ss]) --> [a,b], !, abc(Ss).
abc([b,'B'|Ss]) --> [b,b], !, abc(Ss).
abc([b,'C'|Ss]) --> [b,c], !, abc(Ss).
abc([c,'C'|Ss]) --> [c,c], !, abc(Ss).
abc(['B'|Ss]) --> ['B'], !, abc(Ss).
abc(['C'|Ss]) --> ['C'], !, abc(Ss).
%abc([a|Ss]) --> [a], !, abc(Ss).
abc([b|Ss]) --> [b], !, abc(Ss).
abc([c|Ss]) --> [c], !, abc(Ss).
abc([]) --> [].
And here’s the meta-interpreter in case it’s needed to test this:
%! ground_csg(+Symbol,+Terminals,+Input,+Length,?String)
%! is nondet.
%
% Meta-interpreter for Context Sensitive Grammars in DCG notation.
%
ground_csg(S,Ts,Is,K,Os):-
N = K
,parse(S,Is,Ss)
,ground_csg_(S,Ts,Ss,N,Os).
%! ground_csg_(+Symbol,+Terminals,+Input,+K,-Output) is nondet.
%
% Business end of ground_csg/5.
%
ground_csg_(_S,Ts,Is,N,Is):-
length(Is,M)
,M =:= N
,terminal_string(Ts,Is)
,!.
ground_csg_(S,Ts,Is,N,Os):-
length(Is,M)
,M =< N
,\+ terminal_string(Ts,Is)
,parse(S,Is,Ss)
,ground_csg_(S,Ts,Ss,N,Os).
%! parse(+Symbol,+Inputs,-Parse) is nondet.
%
% Thin wrapper around phrase/3 to setup a parse.
%
parse_(S,Is,Ss):-
S_ =.. [S,Ss]
,phrase(S_,Is).
parse(S,Is,Ss):-
S_ =.. [S,Is]
,phrase(S_,Ss).
%! terminal_string(+Terminals,+String) is det.
%
% True if String is made up of only Terminals.
%
terminal_string(Ts,Ss):-
maplist(sort,[Ts,Ss],[Ts_s,Ss_s])
,ord_subset(Ss_s,Ts_s).