For the same exam in which I had to do the LISP project I also had to write a Clausify program in Prolog.
Long story short: the goal was to translate a well formed form (WFF or FBF in italian) of a first order logic language in a conjunctive normal form (CNF).

congiunzione(and).
disgiunzione(or).
negazione(not).
implicazione(implies).
esiste(exist).
per_ogni(every).

fbf2cnf(FBF, CNFFBF):- atomic(FBF),
					   CNFFBF = FBF,
					   !.
fbf2cnf(FBF, CNFFBF):- FBF =.. [X|ListaArgs],
					   semplifica([X|ListaArgs], [], CNFFBF_3_p),
					   semplifica_2(CNFFBF_3_p, [], CNFFBF_4_p),
					   controlla_lista(CNFFBF_4_p, [], CNFFBF_2_p),
					   CNFFBF_2_p =.. [M|Ms],
					   controlla_lista([M|Ms], [], CNFFBF_2),
					   CNFFBF_2 =.. [Y|Ys],
					   semplifica([Y|Ys], [], CNFFBF_3),
					   semplifica_2(CNFFBF_3, [], CNFFBF_4),
					   CNFFBF_R_P =.. CNFFBF_4,
					   not(confronta(CNFFBF_2_p, CNFFBF_2)),
					   fbf2cnf(CNFFBF_R_P, CNFFBF),
					   !.
fbf2cnf(FBF, CNFFBF):- FBF =.. [X|ListaArgs],
					   semplifica([X|ListaArgs], [], CNFFBF_3_p),
					   semplifica_2(CNFFBF_3_p, [], CNFFBF_4_p),
					   controlla_lista(CNFFBF_4_p, [], CNFFBF_2_p),
					   CNFFBF_2_p =.. [M|Ms],
					   controlla_lista([M|Ms], [], CNFFBF_2),
					   CNFFBF_2 =.. [Y|Ys],
					   semplifica([Y|Ys], [], CNFFBF_3),
					   semplifica_2(CNFFBF_3, [], CNFFBF_4),
					   CNFFBF =.. CNFFBF_4,
					   !.

confronta(CNFFBF, CNFFBF).	

semplifica([], CNFFBF_R, CNFFBF_R).
semplifica([Y|Ys], CNFFBF, CNFFBF_R):- var(Y),
									   append(CNFFBF, [Y], CNFFBF_1),
									   semplifica(Ys, CNFFBF_1, CNFFBF_R),
									   !.
semplifica([Y|Ys], CNFFBF, CNFFBF_R):- congiunzione(Y),
									   togli_and(Ys, CNFFBF, CNFFBF_1),
									   append([Y], CNFFBF_1, CNFFBF_R).
semplifica([Y|Ys], CNFFBF, CNFFBF_R):- compound(Y),
									   Y =.. [Z|Zs],
									   semplifica([Z|Zs], [], CNFFBF_1),
									   semplifica(Ys, [], CNFFBF_2),
									   R1 =.. CNFFBF_1,
									   append(CNFFBF, [R1], CNFFBF_3),
									   append(CNFFBF_3, CNFFBF_2, CNFFBF_R),
								       !.
semplifica([Y|Ys], CNFFBF, CNFFBF_R):- append(CNFFBF, [Y], CNFFBF_1),
									   semplifica(Ys, [], CNFFBF_2),
									   append(CNFFBF_1, CNFFBF_2, CNFFBF_R),
									   !.

semplifica_2([], CNFFBF_R, CNFFBF_R).
semplifica_2([Y|Ys], CNFFBF, CNFFBF_R):- var(Y),
										 append(CNFFBF, [Y], CNFFBF_1),	
									     semplifica_2(Ys, CNFFBF_1, CNFFBF_R),
									     !.
semplifica_2([Y|Ys], CNFFBF, CNFFBF_R):- disgiunzione(Y),
									   togli_or(Ys, CNFFBF, CNFFBF_1),
									   append([Y], CNFFBF_1, CNFFBF_R).
semplifica_2([Y|Ys], CNFFBF, CNFFBF_R):- compound(Y),
									   Y =.. [Z|Zs],
									   semplifica_2([Z|Zs], [], CNFFBF_1),
									   semplifica_2(Ys, [], CNFFBF_2),
									   R1 =.. CNFFBF_1,
									   append(CNFFBF, [R1], CNFFBF_3),
									   append(CNFFBF_3, CNFFBF_2, CNFFBF_R),
								       !.
semplifica_2([Y|Ys], CNFFBF, CNFFBF_R):- append(CNFFBF, [Y], CNFFBF_1),
									   semplifica_2(Ys, [], CNFFBF_2),
									   append(CNFFBF_1, CNFFBF_2, CNFFBF_R),
									   !.

togli_and([], CNFFBF_R, CNFFBF_R).
togli_and([Y|Ys], CNFFBF, CNFFBF_R):- Y =.. [Z|Zs],
									  congiunzione(Z),
									  togli_and(Zs, CNFFBF, CNFFBF_1),
									  togli_and(Ys, CNFFBF_1, CNFFBF_R),
									  !.
togli_and([Y|Ys], CNFFBF, CNFFBF_R):- append(CNFFBF, [Y], CNFFBF_1),
									  togli_and(Ys, CNFFBF_1, CNFFBF_R),
									  !.

togli_or([], CNFFBF_R, CNFFBF_R).
togli_or([Y|Ys], CNFFBF, CNFFBF_R):- Y =.. [Z|Zs],
									  disgiunzione(Z),
									  togli_or(Zs, CNFFBF, CNFFBF_1),
									  togli_or(Ys, CNFFBF_1, CNFFBF_R),
									  !.
togli_or([Y|Ys], CNFFBF, CNFFBF_R):- append(CNFFBF, [Y], CNFFBF_1),
									  togli_or(Ys, CNFFBF_1, CNFFBF_R),
									  !.

%% lista vuota
controlla_lista([], CNFFBF_R, CNFFBF_R).
%% trova and
controlla_lista([X|ListaArgs], CNFFBF, CNFFBF_R):- congiunzione(X),
							count_elements(ListaArgs, N),
							N >= 2,
							costruisci_and(ListaArgs, CNFFBF, CNFFBF_1),
							CNFFBF_R =.. [X|CNFFBF_1],
							!.
%% controllare se or (... and)
controlla_lista([X|ListaArgs], CNFFBF, CNFFBF_R):- disgiunzione(X),
								count_elements(ListaArgs, N),
								N >= 2,
								cerca_and(ListaArgs),
								estrai_parametri(ListaArgs, [], Lista_AND),
								costruisci_or_and(Lista_AND, [], CNFFBF_R1),
								append(CNFFBF, CNFFBF_R1, CNFFBF_R2),
								CNFFBF_R =.. ['and'|CNFFBF_R2],
								!.

%% trova or
controlla_lista([X|ListaArgs], CNFFBF, CNFFBF_R):- disgiunzione(X),
								count_elements(ListaArgs, N),
								N >= 2,
								costruisci_or(ListaArgs, [], CNFFBF_1),
								append(CNFFBF, CNFFBF_1, CNFFBF_2),
								CNFFBF_R =.. [X|CNFFBF_2],
								!.
%% trova not di not
controlla_lista([X,X1|ListaArgs], CNFFBF, CNFFBF_R):- negazione(X),
								X1 =.. [Y,Y1|Ys],
								count_elements(Ys, M),
								M = 0,
								count_elements(ListaArgs, N),
								N = 0,
								negazione(Y),
								costruisci_not(Y1, CNFFBF, CNFFBF_R),
								!.
%% trova not di atomo
controlla_lista([X,X1|ListaArgs], CNFFBF, CNFFBF_R):- negazione(X),
								count_elements(ListaArgs, N),	
								N = 0,
								atomic(X1),
								CNFFBF_1 =.. [X|[X1]],
								append(CNFFBF, CNFFBF_1, CNFFBF_R),
								!. 
%% CASO #1 : not (and (p, q)) = or (not (p), not (q))
controlla_lista([X,X1|ListaArgs], CNFFBF, CNFFBF_R):- negazione(X),	
								count_elements(ListaArgs, N),
								N = 0,
								compound(X1),	
								X1 =.. [Y|Ys],
								congiunzione(Y),	
								costruisci_not_and(Ys, CNFFBF, CNFFBF_1),
								CNFFBF_R =.. ['or'|CNFFBF_1],	
								!.
%% CASO #2 : not (or (p, q)) = and (not (p), not (q))
controlla_lista([X,X1|ListaArgs], CNFFBF, CNFFBF_R):- negazione(X),	
								count_elements(ListaArgs, N),	
								N = 0,
								compound(X1),
								X1 =.. [Y|Ys],
								disgiunzione(Y),
								costruisci_not_or(Ys, CNFFBF, CNFFBF_1),
								CNFFBF_R =.. ['and'|CNFFBF_1],
								!.
%% not(every) # not(exist(X, p(X))) = every(X, not(P(X)))
controlla_lista([X,X1|ListaArgs], CNFFBF, CNFFBF_R):- negazione(X),
								count_elements(ListaArgs, N),	
								N = 0,
								X1 =.. [Y,V,F|Ys],
								count_elements(Ys, M),
								M = 0,
								esiste(Y),
								CNFFBF_F =.. ['not'|[F]],	
								append([V], [CNFFBF_F], CNFFBF_1),
								CNFFBF_2 =.. ['every'|CNFFBF_1],
								CNFFBF_2 =.. [Z|Zs],
								controlla_lista([Z|Zs], [], CNFFBF_R),
								!.
%% not(every) # not(every(X, p(X))) = exist(X, not(P(X)))
controlla_lista([X,X1|ListaArgs], CNFFBF, CNFFBF_R):- negazione(X),
								count_elements(ListaArgs, N),
								N = 0,
								X1 =.. [Y,V,F|Ys],
								count_elements(Ys, M),
								M = 0,
								per_ogni(Y),
								CNFFBF_F =.. ['not'|[F]],
								append([V], [CNFFBF_F], CNFFBF_1),
								CNFFBF_2 =.. ['exist'|CNFFBF_1],
								CNFFBF_2 =.. [Z|Zs],
								controlla_lista([Z|Zs], [], CNFFBF_R),
								!.
%% trova un implies # implies(P, Q) = or(not(P), Q)
controlla_lista([X,P,Q|ListaArgs], CNFFBF, CNFFBF_R):- implicazione(X),
								count_elements(ListaArgs, N),
								N = 0,
								costruisci_implicazione(P, Q, CNFFBF_R),
													   !.
%% trova un exist # exist(Y, every(Y,p(X,Y))) = every(Y, p(sf666(Y), Y))
controlla_lista([X,V,F|ListaArgs], CNFFBF, CNFFBF_R):- esiste(X),
								count_elements(ListaArgs, N),
								N = 0,
								var(V),
								F =.. [Y,V1,F1|Ys],
								count_elements(Ys, M),
								M = 0,
								per_ogni(Y),
								skolem_function([V],SF).
%% trova un exist # exist(X, p(X)) = p(skv+numero)
controlla_lista([X,V,F|ListaArgs], CNFFBF, CNFFBF_R):- esiste(X),
								count_elements(ListaArgs, N),
								N = 0,
								var(V),
								skolem_function(SK, V),
								F =.. [Y|Ys],
								controlla_lista([Y|Ys], [], CNFFBF_R),
								!.
%% trova un every # every(X, p(X)) = p(skv+numero)
controlla_lista([X,V,F|ListaArgs], CNFFBF, CNFFBF_R):- per_ogni(X),
								count_elements(ListaArgs, N),
								N = 0,
								var(V),
								skolem_function(SK, V),
								F =.. [Y|Ys],
								controlla_lista([Y|Ys], [], CNFFBF_R),
								!.
%% trova un predicato
controlla_lista([X|Xs], CNFFBF, CNFFBF_R):- count_elements(Xs, N),
								N >= 1,
								controlla_predicato([X|Xs], CNFFBF, [Y|Ys]),
								CNFFBF_R =.. [Y|Ys],
								!.

%% trovato un predicato, controlla i suoi elementi
controlla_predicato([], CNFFBF_R, CNFFBF_R).
controlla_predicato([X|Xs], CNFFBF, CNFFBF_R):- atomic(X),
								append(CNFFBF, [X], CNFFBF_1),
								controlla_predicato(Xs, CNFFBF_1, CNFFBF_R),
								!.
controlla_predicato([X|Xs], CNFFBF, CNFFBF_R):- compound(X),
								X =.. [Y|Ys],
								controlla_lista([Y|Ys], [], CNFFBF_1),
								append(CNFFBF, [CNFFBF_1], CNFFBF_2),
								controlla_predicato(Xs, CNFFBF_2, CNFFBF_R),
								!.

%% trova un and, nel caso gli elementi siano atomi fa l'append a CNFFBF_R
%% , altrimenti richiama controlla_lista
%% elementi, lista_a_cui_aggiungere, lista_risultato
costruisci_and([], CNFFBF_R, CNFFBF_R).
costruisci_and([X|Xs], CNFFBF, CNFFBF_R):- atomic(X),
								append(CNFFBF, [X], CNFFBF_1),
								costruisci_and(Xs, CNFFBF_1, CNFFBF_R),
								!.
costruisci_and([X|Xs], CNFFBF, CNFFBF_R):- compound(X),
								X =.. [Y|Ys],
								controlla_lista([Y|Ys], [], CNFFBF_R1),
								append(CNFFBF, [CNFFBF_R1], CNFFBF_R2),
								costruisci_and(Xs, CNFFBF_R2, CNFFBF_R),
										   !.

%% trova un or
costruisci_or([], CNFFBF_R, CNFFBF_R).
costruisci_or([X|Xs], CNFFBF, CNFFBF_R):- atomic(X),
								append(CNFFBF, [X], CNFFBF_1),
								costruisci_or(Xs, CNFFBF_1, CNFFBF_R),
								!.
costruisci_or([X|Xs], CNFFBF, CNFFBF_R):- compound(X),
								X =.. [Y|Ys],
								controlla_lista([Y|Ys], [], CNFFBF_R1),
								costruisci_or(Xs, [], CNFFBF_R2),
								append(CNFFBF, [CNFFBF_R1], CNFFBF_2),
								append(CNFFBF_2, CNFFBF_R2, CNFFBF_R3),
								CNFFBF_R3 =.. [Z|Zs],
								controlla_lista([Z|Zs], [], CNFFBF_R),
								!.

%% trovo un or con dentro almeno un and, allora costruisco le coppie
%% Lista_parametri_da_accoppiare, CNFFBF_parziale, CNFFBF_finale
costruisci_or_and([], CNFFBF_R, CNFFBF_R).
costruisci_or_and([X|Xs], CNFFBF, CNFFBF_R):- 
								accoppia(X, Xs, CNFFBF, CNFFBF_R1),
								costruisci_or_and(Xs, CNFFBF_R1, CNFFBF_R),
								!.

accoppia(Elem, [], CNFFBF_R, CNFFBF_R).
accoppia([], _, CNFFBF_R, CNFFBF_R).
accoppia([E|Es], [[Y|Ys]|Xs], CNFFBF, CNFFBF_R):- 
								accoppia_and(E, [Y|Ys], CNFFBF, CNFFBF_1),
								accoppia_and(E, Xs, CNFFBF_1, CNFFBF_2),
								accoppia(Es, [[Y|Ys]|Xs], CNFFBF_2, CNFFBF_R),
								!.
accoppia([E|Es], [Y|Ys], CNFFBF, CNFFBF_R):- 
								accoppia_and(E, [Y|Ys], CNFFBF, CNFFBF_1),
								elementi_lista(Es, [Y|Ys], CNFFBF_1, CNFFBF_R).
accoppia(Elem, [[Y|Ys]|Xs], CNFFBF, CNFFBF_R):- 
								append([Elem], [Y], CNFFBF_1),
								CNFFBF_2 =.. ['or'|CNFFBF_1],
								append(CNFFBF, [CNFFBF_2], CNFFBF_3),
								accoppia(Elem, [Ys|Xs], CNFFBF_3, CNFFBF_R),
								!.
accoppia(Elem, [X|Xs], CNFFBF, CNFFBF_R):- 
								accoppia(Elem, Xs, CNFFBF, CNFFBF_R),
								!.

accoppia_and(_, [], CNFFBF_R, CNFFBF_R).
accoppia_and(Elem, [[Y|Ys]|Xs], CNFFBF, CNFFBF_R):- 
								accoppia_and(Elem, [Y|Ys], CNFFBF, CNFFBF_1),
								accoppia_and(Elem, Xs, CNFFBF_1, CNFFBF_R).
accoppia_and(Elem, [Y|Ys], CNFFBF, CNFFBF_R):- append([Elem], [Y], CNFFBF_1),
								CNFFBF_2 =.. ['or'|CNFFBF_1],
								append(CNFFBF, [CNFFBF_2], CNFFBF_3),
								accoppia_and(Elem, Ys, CNFFBF_3, CNFFBF_R),
								!.

elementi_lista([], _, CNFFBF_R, CNFFBF_R).
elementi_lista([E|Es], [Y|Ys], CNFFBF, CNFFBF_R):- 
								accoppia_and(E, [Y|Ys], CNFFBF, CNFFBF_1),
								elementi_lista(Es, [Y|Ys], CNFFBF_1, CNFFBF_R).

%% estrae i parametri dell'or
estrai_parametri([], Lista_R, Lista_R).
estrai_parametri([X|Xs], Lista_1, Lista_R):- atomic(X),
								append(Lista_1, [X], Lista_2),
								estrai_parametri(Xs, Lista_2, Lista_R),
								!.
estrai_parametri([X|Xs], Lista_1, Lista_R):- compound(X),
								X =.. [Y|Ys],
								congiunzione(Y),
								controlla_lista([Y|Ys], [], Lista_X),
								Lista_X =.. [Z|Zs],
								estrai_parametri(Xs, [], Lista_Xs),
								append(Lista_1, [Zs], Lista_R1),
								append(Lista_R1, Lista_Xs, Lista_R),
								!.

estrai_parametri([X|Xs], Lista_1, Lista_R):- compound(X),
								 X =.. [Y|Ys],
								controlla_lista([Y|Ys], [], Lista_X),
								estrai_parametri(Xs, [], Lista_Xs),
								append(Lista_1, [Lista_X], Lista_R1),
								append(Lista_R1, Lista_Xs, Lista_R),
								!.

%% cerca se c'è un and
cerca_and([X|Xs]):- atomic(X),
					cerca_and(Xs),
					!.		
cerca_and([X|Xs]):- compound(X),
					X =.. [Y|Ys],
					count_elements(Ys, N),
					N >= 2,
					congiunzione(Y),
					!.										
cerca_and([X|Xs]):- compound(X),
					X =.. [Y|Ys],
					cerca_and(Xs),
					!.

%% not è stato trovato, guarda cosa ha come parametro
costruisci_not(Y1, CNFFBF, CNFFBF_R):- atomic(Y1),
									   append(CNFFBF, Y1, CNFFBF_R),
									   !.
costruisci_not(Y1, CNFFBF, CNFFBF_R):- compound(Y1),
								Y1 =.. [Z|Zs],
								controlla_lista([Z|Zs], CNFFBF, CNFFBF_R),
								!.
costruisci_not(Y1, CNFFBF, CNFFBF_R):- Y1 =.. [X|Xs],
								negazione(X),
								controlla_lista([X|Xs], CNFFBF, CNFFBF_R),
								!.

%% trova un not che ha come parametro un and
costruisci_not_and([], CNFFBF_R, CNFFBF_R).
costruisci_not_and([X|Xs], CNFFBF, CNFFBF_R):- atomic(X),
								CNFFBF_1 =.. ['not'|[X]],
								append(CNFFBF, [CNFFBF_1], CNFFBF_2),
								costruisci_not_and(Xs, CNFFBF_2, CNFFBF_R),
								!.
costruisci_not_and([X|Xs], CNFFBF, CNFFBF_R):- compound(X),
								X =.. [Y|Ys],
								controlla_lista([Y|Ys], CNFFBF_1, CNFFBF_2),
								CNFFBF_3 =.. ['not'|[CNFFBF_2]],
								append(CNFFBF, [CNFFBF_3], CNFFBF_4),
								costruisci_not_and(Xs, CNFFBF_4, CNFFBF_R),
								!.

%% trova un not che ha come parametro un or
costruisci_not_or([], CNFFBF_R, CNFFBF_R).
costruisci_not_or([X|Xs], CNFFBF, CNFFBF_R):- atomic(X),
								CNFFBF_1 =.. ['not'|[X]],
								append(CNFFBF, [CNFFBF_1], CNFFBF_2),
								costruisci_not_or(Xs, CNFFBF_2, CNFFBF_R),
								!.	   
costruisci_not_or([X|Xs], CNFFBF, CNFFBF_R):- compound(X),
								 X =.. [Y|Ys],
								controlla_lista([Y|Ys], CNFFBF_1, CNFFBF_2),
								CNFFBF_3 =.. ['not'|[CNFFBF_2]],
								append(CNFFBF, [CNFFBF_3], CNFFBF_4),
								costruisci_not_or(Xs, CNFFBF_4, CNFFBF_R),
									!.

%% trova un implies # implies(P, Q) = or(not(P), Q)
costruisci_implicazione(P, Q, CNFFBF_R):- atomic(P),
										  atomic(Q),
										  CNFFBF_P =.. ['not'|[P]],	
										  append([CNFFBF_P], [Q], CNFFBF_1),
										  CNFFBF_R =.. ['or'|CNFFBF_1].
costruisci_implicazione(P, Q, CNFFBF_R):- atomic(P),
								compound(Q),
								CNFFBF_P =.. ['not'|[P]],
								Q =.. [Y|Ys],
								controlla_lista([Y|Ys], [], CNFFBF_Q_1),
								append([CNFFBF_P], [CNFFBF_Q_1], CNFFBF_1),
								CNFFBF_R =.. ['or'|CNFFBF_1].
costruisci_implicazione(P, Q, CNFFBF_R):- compound(P),
								atomic(Q),
								CNFFBF_P =.. ['not'|[P]],
								CNFFBF_P =.. [X|Xs],
								controlla_lista([X|Xs], [], CNFFBF_P_1),
								append([CNFFBF_P_1], [Q], CNFFBF_1),
								CNFFBF_R =.. ['or'|CNFFBF_1].
costruisci_implicazione(P, Q, CNFFBF_R):- CNFFBF_P =.. ['not'|[P]],
								CNFFBF_P =.. [X|Xs],
								controlla_lista([X|Xs], [], CNFFBF_P_1),
								Q =.. [Y|Ys],
								controlla_lista([Y|Ys], [], CNFFBF_Q_1),
								append([CNFFBF_P_1], [CNFFBF_Q_1], CNFFBF_1),
								CNFFBF_R =.. ['or'|CNFFBF_1].

skolem_variable(V, SK):- var(V), gensym(skv, SK).
skolem_function([], SF):- skolem_variable(_, SF).
skolem_function([A | ARGS], SF) :- gensym(skf, SF_op),
								   SF =.. [SF_op, A | ARGS].

%% conta gli elementi della lista
count_elements([],0).
count_elements([X|Xs],N):- count_elements(Xs,M), N is M + 1.

%%%% controlla se la FBF è clausola di Horn
horn(FBF):- atomic(FBF).
horn(FBF):- fbf2cnf(FBF, CNF),
			CNF =.. [X|Xs],
			negazione(X).
horn(FBF):- fbf2cnf(FBF, CNF),
			CNF =.. [X|Xs],
			conta(Xs, 0, N),
			N < 2.

%%%% conta i letterali non negativi			
conta([], N, N).
conta([X|Xs], N, N):- negazione(X).
conta([X|Xs], N1, N):- compound(X),
					   X =.. [Y|Ys],
				       negazione(Y),
				       conta(Xs, 0, M),
				       N is N1 + M,
				       !.
conta([X|Xs], N1, N):- compound(X),	
  				       X =.. [Y|Ys],
				       conta(Ys, 0, M0),
				       conta(Xs, 0, M1),
				       M2 is M0 + N1,
					   N is M2 + M1,
				       !.
conta([X|Xs], N1, N):- atomic(X),
				       M1 is N1 + 1,
				       conta(Xs, 0, M),
				       N is M1 + M,
				       !.

confronta_liste([],[]).					   
confronta_liste([X|Xs],[X|Ys]):- confronta_liste(Xs, Ys),
								 !.

I think that’s all.
Cheers.