1:- module(diff,
2 [ serialize/1, 3 serialize/3, 4 serialize_into/2, 5 serialize_into/3, 6 diff_terms/2, 7 diff_terms/3 8 ]). 9
10diff_terms(T1, T2) :-
11 diff_terms(T1, T2, []).
12
13diff_terms(T1, T2, Options) :-
14 serialize_into(File1, T1, Options),
15 serialize_into(File2, T2, Options),
16 thread_create(show_diff(File1, File2, Options), _,
17 [ detached(true)
18 ]).
19
20show_diff(File1, File2, _) :-
21 setup_call_cleanup(
22 process_create(path(meld),
23 [ file(File1), file(File2),
24 '-L', "Compare Prolog terms"
25 ],
26 [ process(PID)]),
27 wait_or_kill(PID),
28 call_cleanup(
29 delete_file(File1),
30 delete_file(File2))).
31
32wait_or_kill(PID) :-
33 catch(process_wait(PID, _), '$aborted',
34 ( process_kill(PID),
35 process_wait(PID, _)
36 )).
37
38serialize_into(File, Term) :-
39 serialize_into(File, Term, []).
40
41serialize_into(File, Term, Options) :-
42 var(File),
43 !,
44 setup_call_cleanup(
45 tmp_file_stream(File, Out, [extension(txt)]),
46 serialize(Out, Term, Options),
47 close(Out)).
48serialize_into(File, Term, Options) :-
49 setup_call_cleanup(
50 open(File, write, Out),
51 serialize(Out, Term, Options),
52 close(Out)).
53
54serialize(Term) :-
55 serialize(current_output, Term, []).
56
57serialize(Out, Term, Options) :-
58 option(format(tokens), Options),
59 !,
60 serialize_as_tokens(Out, Term).
61serialize(Out, Term, _Options) :-
62 print_term(Term, [output(Out)]).
63
64serialize_as_tokens(Out, Term) :-
65 \+ \+ ( numbervars(Term, 0, _),
66 serialize_as_tokens(Out, Term, 0)
67 ).
68
69serialize_as_tokens(Out, Term, Depth) :-
70 Term == [],
71 !,
72 indent(Out, Depth),
73 format(Out, '[]~n', []).
74serialize_as_tokens(Out, Term, Depth) :-
75 is_list(Term),
76 !,
77 length(Term, Length),
78 indent(Out, Depth),
79 format(Out, '[~d]~n', [Length]),
80 Depth2 is Depth+1,
81 forall(member(Arg, Term),
82 serialize_as_tokens(Out, Arg, Depth2)).
83serialize_as_tokens(Out, Term, Depth) :-
84 improper_list(Term, List, Last),
85 List \== [],
86 !,
87 length(List, Length),
88 indent(Out, Depth),
89 format(Out, '[~d|+]~n', [Length]),
90 Depth2 is Depth+1,
91 forall(member(Arg, List),
92 serialize_as_tokens(Out, Arg, Depth2)),
93 indent(Out, Depth2, '+ '),
94 serialize_as_tokens(Out, Last, Depth2).
95serialize_as_tokens(Out, Term, Depth) :-
96 is_dict(Term, Tag),
97 !,
98 indent(Out, Depth),
99 format(Out, '{~q}~n', [Tag]),
100 Depth2 is Depth+1,
101 forall(get_dict(Key, Term, Value),
102 serialize_as_tokens_kv(Out, Key, Value, Depth2)).
103serialize_as_tokens(Out, Term, Depth) :-
104 compound(Term),
105 !,
106 compound_name_arity(Term, Name, Arity),
107 indent(Out, Depth),
108 format(Out, '~q/~d~n', [Name, Arity]),
109 Depth2 is Depth+1,
110 forall(arg(_I, Term, Arg),
111 serialize_as_tokens(Out, Arg, Depth2)).
112serialize_as_tokens(Out, Term, Depth) :-
113 indent(Out, Depth),
114 format(Out, '~p~n', [Term]).
115
116serialize_as_tokens_kv(Out, Key, Value, Depth) :-
117 indent(Out, Depth, ''),
118 format(Out, '~q:~n', [Key]),
119 Depth2 is Depth+1,
120 serialize_as_tokens(Out, Value, Depth2).
121
122indent(Out, Depth) :-
123 indent(Out, Depth, '- ').
124
125indent(Out, Depth, Prefix) :-
126 Depth > 0,
127 line_position(Out, 0),
128 !,
129 format(Out, '~t~*|~w', [Depth, Prefix]).
130indent(_, _, _).
131
132improper_list([H|T0], List, Last) =>
133 List = [H|T],
134 improper_list(T0, T, Last).
135improper_list(Term, List, Last) =>
136 List = [],
137 Last = Term