View source with raw comments or as raw
    1:- module(diff,
    2          [ serialize/1,                % +Term
    3            serialize/3,                % +Out, +Term, +Options
    4            serialize_into/2,           % +File, +Term
    5            serialize_into/3,           % +File, +Term, +Options
    6            diff_terms/2,               % +Term1, +Term2
    7            diff_terms/3                % +Term1, +Term2, +Options
    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