sldnf.pl
sldnf.pl
—
text/x-perl,
13 kB (14147 bytes)
Contenuto del file
% SLDNF Draw
% Produces a LaTeX drawing of an SLDNF tree
% Author: Marco Gavanelli
% http://www.ing.unife.it/docenti/MarcoGavanelli/sldnfDraw/
% Version 1.4
% Does not crash when printing infinite terms
% Uses lib(var_names) for pretty printing the names of variables.
% Version 1.5
% If the resolvent is too long (over max_resolvent_length/1 characters), prints it in two or more rows
sldnf_version(1.5).
% TODO:
% stop when 1st solution found
% stop negation when 1st solution found
% CLP? Minimize?
:- lib(var_name).
% Note that it does not handle correctly the cut in the resolvent, but only
% in a clause. Please, do not write ?- p,!. but define e new predicate
% callp:- p,!.
% Given a clause a:-b,!,c, we have that:
% 1.the scope of the cut is a:-b, in the sense that it will cut the
% alternatives to a and b. This is represented with the list
% of OpenCuts: it contains all the cuts that are in a clause that
% has been selected.
% 2.The cut has an effect when it is reached, and its effect lasts
% until the backtracking goes back to a. Thus, the information about
% reaching a cut is saved with assert(reached(Cut)).
% Each cut has a unique name, given by a counter.
:- dynamic begin_resolvent/1, end_resolvent/1, begin_binding/1, end_binding/1, counter/1, reached/1, maxdepth/1.
:- dynamic my_clause/2.
% Maximum length of the resolvent, in chars (very approximate)
% If the length exceeds this length, the resolvent will be printed on two (or more) lines
:- dynamic max_resolvent_length/1.
max_resolvent_length(25).
set_depth(D):-
retract(maxdepth(_)),
assert(maxdepth(D)).
maxdepth(20). % Default max depth: 20
d(G):- draw_goal(G,"tree.tex").
draw_goal(G,FileName):-
init_file(FileName,write,File),
term_length_chopped(G,Length),
conv_sq_list(G,GSq),
init_cuts,
(draw(GSq,File,Length) ; true),
close(File).
draw(R,F,Longest):-
maxdepth(Depth),
draw(R,F,Longest,Depth,[]).
% draw(+Resolvent,+Stream,MaxLenghtOfResolvent,MaxDepth,OpenCuts)
draw([],F,Longest,_,_):-
print_string_spaces(F,Longest,"true"),
fail.
draw(_,F,Longest,0,_):- !,
print_string_spaces(F,Longest,"..."),
fail.
draw([not G|R],F,LongestIn,Depth,OpenCuts):-
Depth1 is Depth-1,
write(F,"\\begin{bundle}{"),
print_resolvent(F,[not G|R]),
writeln(F,"}\n\\chunk{"),
% NOTA: forse in questo caso non conviene metterlo, cosi` il box diventa giusto.
% pero` non si assicura che sia giusto il figlio del box...
% Compute the maximum length
term_length_chopped([not G|R],ResLen), Length is max(LongestIn,ResLen),
write(F,"\\begin{bundle}{\\framebox{"),
(draw([G],F,0,Depth1,[]) ; true),
write(F,"}}"),
(vanilla(G)
-> print_fail(F,ResLen)
; write(F,"\\chunk{"),
(draw(R,F,Length,Depth1,OpenCuts); true), write(F,"}")
),
writeln(F,"\\end{bundle}}\\end{bundle}"),
fail.
% Cut !
draw([!|R],F,LongestIn,Depth,[LastCut|OpenCuts]):- !,
Depth1 is Depth-1,
term_length_chopped([!|R],ResLen), Length is max(LongestIn,ResLen),
write(F,"\\begin{bundle}{"),
print_resolvent(F,[!|R]),
writeln(F,"}"),
assert(reached(LastCut)),
(print_builtin_children(true,R,F,Length,Depth1,OpenCuts); writeln(F,"\\end{bundle}")),
fail.
% Conjunction of goals: may occur inside another predicate, like not((G1,G2)).
draw([(G1,G2)|R],F,LongestIn,Depth,OpenCuts):-
draw([G1,G2|R],F,LongestIn,Depth,OpenCuts).
% Built-in Predicate
draw([G|R],F,LongestIn,Depth,OpenCuts):-
Depth1 is Depth-1,
not(G = not(_)),
not(G=(_,_)),
built_in(G),
term_length_chopped([G|R],ResLen), Length is max(LongestIn,ResLen),
write(F,"\\begin{bundle}{"),
print_resolvent(F,[G|R]),
writeln(F,"}"),
(print_builtin_children(G,R,F,Length,Depth1,OpenCuts); writeln(F,"\\end{bundle}")),
fail.
% User defined predicate
draw([G|R],F,LongestIn,Depth,OpenCuts):-
Depth1 is Depth-1,
not(G = not(_)),
not(G=(_,_)),
not(built_in(G)),
term_length_chopped([G|R],ResLen), Length is max(LongestIn,ResLen),
write(F,"\\begin{bundle}{"),
print_resolvent(F,[G|R]),
writeln(F,"}"),
(print_children(G,R,F,Length,Depth1,OpenCuts); writeln(F,"\\end{bundle}")),
fail.
print_children(G,R,F,Length,Depth,OpenCuts):-
Depth1 is Depth-1,
term_variables(G,Vars),
vars_names(Vars,VarNames),
count_children(G,NumChildren),
(NumChildren = 1 -> Len = Length ; Len=0),
increase_counter, get_counter(C),
% Unique name for the node: if a cut is reached, it will have this name
% First part: the cut may cut the alternatives for the clause
retract_cut_on_backtracking(C),
clausola(G,B),
(clause_is_not_cut(C)
-> true
; write(F,"\\chunk{(cut)}"), fail),
(check_body_contains_cut(B,OpenCuts,NewCuts,_AddedCut,C)
-> % retract_cut_on_backtracking(AddedCut)
true
; NewCuts=OpenCuts),
% Second part: takes care of the alternatives of the predicates
% in the body
( (member(Cut,OpenCuts),reached(Cut))
-> write(F,"\\chunk{(cut)}"), fail
; write(F,"\\chunk"),
print_binding(F,VarNames),
write(F,"{"),
append(B,R,Ris),
(draw(Ris,F,Len,Depth1,NewCuts) ; write(F,"}"), fail)
).
print_children(G,_,F,Length,_Depth,_):-
not(clausola(G,_)),
print_fail(F,Length),
fail.
print_builtin_children(G,R,F,Length,Depth,OpenCuts):-
Depth1 is Depth-1,
term_variables(G,Vars),
vars_names(Vars,VarNames),
findall(G,call(G),L),length(L,NumChildren),
(NumChildren = 1 -> Len = Length ; Len=0),
call(G),
write(F,"\\chunk"),
print_binding(F,VarNames),
write(F,"{"),
(draw(R,F,Len,Depth1,OpenCuts) ; write(F,"}"), fail).
print_builtin_children(G,_,F,Length,_Depth,_OpenCuts):-
not(call(G)),
print_fail(F,Length),
fail.
%%%%%%%%%%%%%%%% Predicates fot cut handling %%%%%%%%%%%%%%%%%
%check_body_contains_cut(+Body,++OpenCuts,-NewCuts,-AddedCut,++Counter)
check_body_contains_cut(B,OpenCuts,NewCuts,AddedCut,Counter):-
memberchk(!,B),push_cut(OpenCuts,NewCuts,AddedCut,Counter).
% A clause is not cut if the cut of the current
% node has not been reached.
clause_is_not_cut(C):-
not reached(cut(C)).
push_cut(L,[cut(C)|L],cut(C),C).
% On backtracking, remove the information about the reached cuts
% that are not open
retract_cut_on_backtracking(_).
retract_cut_on_backtracking(C):-
retract(reached(C)), fail.
follows(cut(N),cut(N1)):-
N>N1.
last_cut([C],C):-!.
last_cut([Cut|Cuts],C):-
last_cut(Cuts,LastSoFar),
(follows(LastSoFar,Cut)
-> C=LastSoFar
; C=Cut).
init_cuts:- retract_all(reached(_)),
reset_counter.
increase_counter:-
counter(C), retract(counter(C)), C1 is C+1,
assert(counter(C1)).
get_counter(C):- counter(C).
reset_counter:- retract_all(counter(_)),
assert(counter(0)).
%%%%%%%%%%%%%%% End predicates for cut handling %%%%%%%%%%%%%%
%%%%%%%%%%%%%%% Utilities %%%%%%%%%%%%%%
count_children(G,NumChildren):-
findall(B,clausola(G,B),L),
length(L,NumChildren).
vars_names([],[]).
vars_names([X|T],[b(X,N)|TN]):-
var_name(X,N),
vars_names(T,TN).
term_to_string(Term,String):-
open(string(""),write,Stream),
write(Stream,Term),
get_stream_info(Stream, name, String),
close(Stream).
var_name(X,N):-
get_var_name(X,N),!.
var_name(X,NewName):-
term_to_string(X,N),
(var(X)
-> % if the variable name starts with underscore, set X as its name
(string_list(N,[95|_]) -> set_var_name(X,'X')
; set_var_name(X,N)),
get_var_name(X,NewName)
; NewName=N).
print_binding(F,X):-
write(F,"["),
(begin_binding(S) -> write(F,S); true),
print_binding1(F,X),
(end_binding(Send) -> write(F,Send) ; true),
write(F,"]").
print_binding1(_F,[]).
print_binding1(F,[b(A,B)|T]):-
var_name(A,Name),
Name = B, !,
% Avoid writing "X=X" as a binding...
print_binding1(F,T).
print_binding1(F,[b(A,B)|T]):-
write_var(F,B), write(F,"/"),
(acyclic_term(A)
-> write_term_no_sqbrack(F,A,1000)
; maxdepth(D), write_term_no_sqbrack(F,A,D)
),
(T=[] -> true
; write(F,", "), print_binding1(F,T)).
write_var(F,V):-
var(V),
var_name(V,VarName),!,
split_string(VarName,"#","",[Name,Number]),
write(F,"$"),
write(F,Name),
write(F,"_{"),
write(F,Number),
write(F,"}$").
write_var(F,VarName):-
string(VarName),
split_string(VarName,"#","",[Name,Number]),!,
write(F,"$"),
write(F,Name),
write(F,"_{"),
write(F,Number),
write(F,"}$").
% Writes a term replacing the symbols "[" and "]"
% with \lbrack and \rbrack, because you cannot use "["
% inside a label of an arc.
% Stops at depth Depth
% write_term_no_sqbrack(File,Term,Depth):-
write_term_no_sqbrack(F,_,0):- !,
write(F,"\\dots").
write_term_no_sqbrack(F,A,_):-
% Named Variables
var(A), !,
write_var(F,A).
write_term_no_sqbrack(F,[],_):- !,
write(F,"\\lbrack\\rbrack ").
write_term_no_sqbrack(F,[H|T],N):- !,
write(F,"\\lbrack "),
print_list_no_sqbrack(F,[H|T],N),
write(F,"\\rbrack ").
write_term_no_sqbrack(F,T,_):-
T =.. [Fun], !,
write(F,Fun).
% infixed operators
write_term_no_sqbrack(F,T,N):-
T =.. [Fun,ArgX,ArgY],
N1 is N-1,
current_op(_,Associativity,Fun),
(Associativity = xfy ; Associativity = yfx ; Associativity = xfx),!,
write_term_no_sqbrack(F,ArgX,N1),
pretty_write_op(F,Fun),
write_term_no_sqbrack(F,ArgY,N1).
write_term_no_sqbrack(F,T,N):- !,
N1 is N-1,
T =.. [Fun|Arg],
write(F,Fun),
write(F,"("),
print_list_no_sqbrack(F,Arg,N1),
write(F,")").
% If the list is a difference list, we also have the
% case in which the rest is a variable.
%print_list_no_sqbrack(File,Term,Depth)
print_list_no_sqbrack(F,V,N):-
var(V), !,
write_term_no_sqbrack(F,V,N).
print_list_no_sqbrack(F,[H|T],N):-
var(T),!,
write_term_no_sqbrack(F,H,N),
write(F,"$|$"),
write_term_no_sqbrack(F,T,N).
print_list_no_sqbrack(F,[H],N):- !,
write_term_no_sqbrack(F,H,N).
print_list_no_sqbrack(F,[H1,H2|T],N):-
N1 is N-1,
write_term_no_sqbrack(F,H1,N1),write(F,","),
print_list_no_sqbrack(F,[H2|T],N1).
pretty_write_op(F,<):- !,
write(F,$<$).
pretty_write_op(F,=<):- !,
write(F,$=<$).
pretty_write_op(F,>=):- !,
write(F,$>=$).
pretty_write_op(F,>):- !,
write(F,$>$).
pretty_write_op(F,is):- !,
write(F," is ").
pretty_write_op(F,\=):- !,
write(F,"$\\backslash=$").
pretty_write_op(F,Op):- !,
write(F,Op).
print_resolvent(F,X):-
(begin_resolvent(S) -> write(F,S); true),
term_length(X,Len),
max_resolvent_length(MaxLength),
(X=[_,_|_], Len>MaxLength
-> write(F,"\\begin{tabular}{c}"),
print_list_tabular(F,X),
writeln(F,"\\end{tabular}")
; print_list(F,X)
),
(end_resolvent(Send) -> write(F,Send) ; true).
% Prints a list in a tabular environment, without exceeding max_resolvent_length in each row
print_list_tabular(F,[A]):-!, print_list(F,[A]).
print_list_tabular(F,X):-
append2(First,Rest,X),
(First = [_]
-> true
; term_length(First,Len1),
max_resolvent_length(MaxLength),
Len1<MaxLength,!
),
(Rest=[] -> true
; print_list(F,First),write(F,",\\\\"),
print_list_tabular(F,Rest)
).
% Like append, but provides the solutions in the opposite order
% i.e., if the third argument is ground and the others are not,
% first provides the longest lists as first aruments
append2([H|T],X,[H|S]):-
append2(T,X,S).
append2([],X,X).
print_list(F,[H]):- !,
maxdepth(D),
write_term_no_sqbrack(F,H,D).
print_list(F,[H1,H2|T]):-
maxdepth(D),
write_term_no_sqbrack(F,H1,D),write(F,","),
print_list(F,[H2|T]).
print_fail(F,Longest):-
write(F,"\\chunk{"),
max_resolvent_length(MaxLength),
(Longest < MaxLength
-> print_string_spaces(F,Longest,"false")
; print_string_spaces(F,MaxLength,"false")
),
write(F,"}").
% Prints a string adding "Longest" spaces.
print_string_spaces(F,Longest,String):-
NumSpace is Longest//2,
print_n_spaces(F,NumSpace),
write(F,String), writeln(F,"\n"),
print_n_spaces(F,NumSpace).
print_n_spaces(_,0):- !.
print_n_spaces(F,N):-
number(N), N>0,
write(F,"~"),
N1 is N-1,
print_n_spaces(F,N1).
vanilla([]).
vanilla([A|B]) :- !, vanilla(A), vanilla(B).
vanilla(not X) :- !,
(vanilla(X) -> fail ; true).
vanilla(X) :-
built_in(X), call(X).
vanilla(X) :-
clausola(X,Body),
vanilla(Body).
term_length(G,Length):-
term_to_string(G,S),
string_length(S,Length).
term_length_chopped(G,L):-
max_resolvent_length(MaxTermLength),
term_length(G,Length),
(Length=<MaxTermLength
-> L=Length
; L=MaxTermLength
).
remove_char(Sin,Char,Sout):-
split_string(Sin,Char,"",SubStrings),
concat_string(SubStrings,Sout).
clausola(H,BSq):-
functor(H,F,A), current_predicate(F/A),
clause(H,B),
conv_sq_list(B,BSq).
clausola(H,BSq):-
my_clause(H,BSq).
conv_sq_list((A,B),[A|Bsq]):- !,
conv_sq_list(B,Bsq).
conv_sq_list(true,[]):- !.
conv_sq_list(X,[X]).
built_in(G):-
functor(G,F,A),
current_built_in(F/A).
init_file(FileName,write,File):-
open(FileName,write,File),
write(File,"% File created by SLDNF Draw version "),
sldnf_version(Ver),
writeln(File,Ver),
writeln(File,"% http://www.ing.unife.it/docenti/MarcoGavanelli/sldnfDraw/"),
nl(File).