% Reifying clauses
% Transforming clauses from Prolog representation to
% explicit terms and propositions
% Author: Frank Pfenning

% reify(Pred/Arity, D) if D is the explicit representation of Pred/Arity
reify(Pred/Arity, D) :-
	fresh_vars(Arity, Xs),
	P =.. [Pred|Xs],
	findall((P :- B), clause(P, B), Clauses),
	reify_clauses(Clauses, D).

% reify_goal(A, G, N) if G is explicit represenation of query A
% containing N-1 free variables var(1),...,var(n-1)
reify_goal(A, GRho, N) :-
	reify_prop(A, G, 1, N),
	bound_to_free(1, N, Rho),
	apply_prop(G, Rho, GRho).

% reify_clauses(Clauses, D)
reify_clauses([], top).
reify_clauses([(P :- B)|Clauses], and(D1, D2)) :-
	reify_clause((P :- B), D1),
	reify_clauses(Clauses, D2).

% reify_clause(Clause, D)
reify_clause((P :- B), D) :-
	reify_prop(P, Q, 1, N2),
	reify_prop(B, G, N2, N3),
	quantify_upto(1, N3, implies(G, Q), D).

% reify_prop(A, G, N1, N2) if G is the reified form of A
% using variables bd(N1), ..., bd(N2-1)
reify_prop(true, top, N, N) :- !.
reify_prop((A1 , A2), and(G1, G2), N1, N3) :- !,
	reify_prop(A1, G1, N1, N2),
	reify_prop(A2, G2, N2, N3).
reify_prop(fail, bot, N, N) :- !.
reify_prop((A1 ; A2), or(G1, G2), N1, N3) :- !,
	reify_prop(A1, G1, N1, N2),
	reify_prop(A2, G2, N2, N3).
reify_prop((T1 = T2), equal(S1, S2), N1, N3) :- !,
	reify_term(T1, S1, N1, N2),
	reify_term(T2, S2, N2, N3).
reify_prop(P, Q, N1, N2) :-
	reify_term(P, Q, N1, N2).

% reify_term(T, S, N1, N2) if S is the reified form of T
% using variables bd(N1), ..., bd(N2-1)
reify_term(T, S, N1, N3) :-
	var(T), !,
	T = '$VAR'(N1),				% inst to next variable
	N2 is N1+1,
	reify_term(T, S, N2, N3).
reify_term('$VAR'(M), var(bd(M)), N, N) :-	% recognize earlier variable
	!.					
reify_term(T, app(T, []), N, N) :-
	integer(T), !.
reify_term(T, app(F, Ss), N1, N2) :-
	T =.. [F|Ts],
	reify_terms(Ts, Ss, N1, N2).

reify_terms([], [], N, N).
reify_terms([T|Ts], [S|Ss], N1, N3) :-
	reify_term(T, S, N1, N2),
	reify_terms(Ts, Ss, N2, N3).

% quantify_upto(N, M, D0, D) if D = forall(bd(N),...forall(bd(M-1), D0))
quantify_upto(N, N, D0, D0).
quantify_upto(N, M, D0, forall(bd(N), D)) :-
	N < M,
	N1 is N+1,
	quantify_upto(N1, M, D0, D).

% fresh_vars(N) = [_X1,...,_Xn], _Xi fresh
fresh_vars(0, []).
fresh_vars(N, [_X|Xs]) :-
	N > 0, N1 is N-1,
	fresh_vars(N1, Xs).

% bound_to_free(N, M, Theta) if Theta = [var(N)/bd(N),...var(M-1)/bd(M-1)]
bound_to_free(N, N, []).
bound_to_free(N, M, [for(var(N),bd(N)) | Theta]) :-
	N < M,
	N1 is N+1,
	bound_to_free(N1, M, Theta).
