I would suggest to keep your prolog pure as far as possible, without relying on DB, because this make easier to correct your logic.
Practically, you can use DCGs for both parse and state handling. About state handling, see for instance this particular solution to zebra puzzle:
solve :- solve(Sol, From), writeln(From), maplist(writeln, Sol).
solve(Sol, From) :-
phrase( (from(1, norway)
,color(red) = from(england)
,color(GREEN, green), color(WHITE, white), {GREEN is WHITE-1}
,from(denmark) = drink(tea)
,smoke(Light, light), animal(Cat, cat), next_to(Light, Cat)
,color(Yellow, yellow), smoke(Yellow, cigar)
,from(germany) = smoke(waterpipe)
,drink(3, milk)
,drink(Water, water), next_to(Light, Water)
,animal(bird) = smoke(nofilter)
,from(sweden) = animal(dog)
,from(NORWAY, norway), color(BLUE, blue), next_to(NORWAY, BLUE)
,animal(HORSE, horse), next_to(HORSE, GREEN) % next_to(HORSE, Yellow)
,drink(beer) = smoke(menthol)
,color(green) = drink(coffee)
,animal(Fish, fish), from(Fish, From)
), [[1,_,_,_,_,_],
[2,_,_,_,_,_],
[3,_,_,_,_,_],
[4,_,_,_,_,_],
[5,_,_,_,_,_]
], Sol).
state(S), [A,B,C,D,E] --> [A,B,C,D,E], {member(S, [A,B,C,D,E])}.
color(A, B) --> state([A,B,_,_,_,_]).
from(A, B) --> state([A,_,B,_,_,_]).
animal(A, B) --> state([A,_,_,B,_,_]).
drink(A, B) --> state([A,_,_,_,B,_]).
smoke(A, B) --> state([A,_,_,_,_,B]).
X = Y --> {
X=..[Fx|Ax], Y=..[Fy|Ay],
Xs=..[Fx,S|Ax], Ys=..[Fy,S|Ay]
}, call(Xs), call(Ys).
next_to(X, Y) --> {1 is abs(X-Y)}.
Beware the correction
..., next_to(HORSE, GREEN) % next_to(HORSE, Yellow)
The code come from here, when debugging because of a missing solution, it’s ‘easy’ to locate the step just commenting out the rule in the phrase… of course, this would be the same using the more traditional member/2 based solution of the puzzle, where the state is explicilty inlined in every goal.
I followed this Markus Triska’ suggestion from ‘The Power of Prolog’, but when your state becomes too much complex, there is pack(edcg) you could check… it has a bit steep learning phase…
HTH