[net.sources] Prolog library: pp.pl

pereira@sri-unix.UUCP (08/15/83)

%   File: Mec:PP.Pl      Author: R.A.O'Keefe       Updated: 28 July 82
 
%               Prolog Pretty-Printer
%   For documentation, see the on-line helper file Mec:PP.Hlp
 
:-      op(950, xfx, on).
:-      op(900,  fx, ca).
:-	op(900,  fx, cf).
:-	op(900,  fx, co).
:-	op(900,  fx, cp).
:-	op(900,  fx, pp).
 
:- public
        (ca)/0, (ca)/1, (ca)/2,
        (cf)/0, (cf)/1, (cf)/2,
        (co)/0, (co)/1, (co)/2, (co)/3,
        (cp)/0, (cp)/1, (cp)/2,
        isCurrent/2, isCurrent/3, isCurrent/4,  %  just for setof
        (on)/2,
        (pp)/0, (pp)/1.
 
:- mode
        answer_List(+, +),
        ca, ca(+), ca(+, -),
        cf, cf(+), cf(+, -),
        co, co(+), co(+, -), co(+, +, +),
        cp, cp(+), cp(+, -),
        final_Check(+, +, +, +),
        isCurrent(+, ?), isCurrent(+, +, ?), isCurrent(+, +, +, ?),
        in_Range(+, +),
        match_wild(+, +),
        pp, pp(+),
        pp_conjunction(+, +, +, +),
        pp_disjunction(+, +, +, +),
        pp_explicit(+), pp_explicit(+, +),
        pp_head(+, +, +),
        pp_tail(+, +),
        puts(+).
 
 
/*----------------------------------------------------------------------+
|                                                                       |
|                               Utilities                               |
|                                                                       |
|       Call on File            -- obey Call with output to File        |
|       puts(String)            -- print escaped string                 |
|       answer_List(L,Sep)      -- write non-empty list neatly          |
|       match_wild(Pat,Str)     -- Tops-10-like "wild card" matcher     |
|                                                                       |
+----------------------------------------------------------------------*/
 
Output on File :-
        telling(OldFile),
        nofileerrors,
        (   tell(File),
                fileerrors, !,
                (   call(Output), tell(OldFile)
                |   tell(OldFile), fail
                )
        |   /* couldn't open the file */
                fileerrors,
                display('** can''t tell '), display(File), ttynl,
                fail
        ).
 
 
puts([]) :- !.                  %  puts(Var) forcibly terminates
puts([94,C|Rest]) :-            %   94 is "^"
        (   C = 94, put(C)      %   ^^ -> ^
        |   C < 32              %   ignore control characters
        |   D is C/\31,put(D)   %   control shift
        ),  !,
        puts(Rest).
puts([C|Rest]) :-
        put(C),
        puts(Rest).
 
 
answer_List([Head], Separator) :- !,
        writeq(Head), put(46), put(Separator).          %  46 is ","
answer_List([Head|Tail], Separator) :-
        writeq(Head), put(44), put(Separator), !,       %  44 is "."
        answer_List(Tail, Separator).
 
 
%   patterns are like TOPS-10 files with wild cards.
%       Ascii 42 = '*', Ascii 63 = '?' are the wild card codes.
 
match_wild([Ch|Patt], [Ch|Name]) :- !,
        match_wild(Patt, Name).
match_wild([63|Patt], [Ch|Name]) :- !,
        match_wild(Patt, Name).
match_wild([42|Patt], [Ch|Name]) :-
        match_wild(Patt, [Ch|Name]), !.
match_wild([42|Patt], [Ch|Name]) :- !,
        match_wild([42|Patt], Name).
match_wild([42], []).
match_wild([], []).
 
 
/*----------------------------------------------------------------------+
|                                                                       |
|                           Current Atoms                               |
|                                                                       |
|    The predicates provided to the user are                            |
|       ca(AtomPattern, AnswerList)     -- return current atoms         |
|       ca(AtomPattern)                 -- display current atoms        |
|       ca                              -- display all current atoms    |
|                                                                       |
|   AtomPattern ::=                                                     |
|       Variable                        -- matches any atom             |
|       Atom                            -- matches only itself          |
|       StringPattern                   -- matches atoms by name        |
|       [ AtomPattern | AtomPattern ]   -- matches what either matches  |
|                                                                       |
|   ca is defined as an operator for convenience.  The atoms will be    |
|   returned in ascending alphabetic order.  NB ca/1 will FAIL if no    |
|   atoms match its pattern.  ca/2 can return the empty list.           |
|                                                                       |
+----------------------------------------------------------------------*/
 
ca :-
        ca(Variable).                   %  a Variable matches anything
 
ca(Pattern) :-
        ca(Pattern, Atoms),
        answer_List(Atoms, 32).
 
ca(Pattern, Atoms) :-
        setof(Atom, isCurrent(Pattern, Atom), Atoms).
 
 
isCurrent(Variable, Atom) :-            %  <Variable>
        var(Variable), !,       
        current_atom(Atom).
isCurrent(Atom, Atom) :-                %  <Atom>
        atom(Atom), !.
isCurrent([Head|Tail], Atom) :-         %  <String Pattern>
        integer(Head), !,
        current_atom(Atom),
        name(Atom, String),
        match_wild([Head|Tail], String).
isCurrent([Head|_], Atom) :-            %  [ <Atom Pattern> | _ ]
        isCurrent(Head, Atom).
isCurrent([_|Tail], Atom) :- !,         %  [ _ | <Atom Pattern> ]
        isCurrent(Tail, Atom).
 
 
/*----------------------------------------------------------------------+
|                                                                       |
|                          Current Operators                            |
|                                                                       |
|   The predicates provided to the user are                             |
|       co(OperatorPattern, AnswerList) -- return matching operaotrs    |
|       co(OperatorPattern)             -- display matching operators   |
|       co                              -- display all operators        |
|       co(P, T, A)                     -- same as co op(P,T,A)         |
|                                                                       |
|   OperatorPattern ::=                                                 |
|       op(Range, AtomPattern, AtomPattern)                             |
|       AtomPattern                     -- same as op(_,_,P)            |
|                                                                       |
|   Range ::=                                                           |
|       Lower - Upper                   -- Lower <= Actual <= Upper ?   |
|       RelOp(Limit)                    -- Actual Relop Limit ?         |
|       Number                          -- Actual = Number ?            |
|       [ Range | Range ]               -- true if in either Range      |
|       Variable                        -- matches anything             |
|                                                                       |
|   co is defined as an operator for convenience.  The result is a list |
|   of op(Prec,Type,Name) terms in increasing order.  The order is that |
|   imposed by compare/3 : first in numeric order of precedence, then   |
|   by type, where fx < fy < xf < xfx < xfy < yf < yfx < yfy, then in   |
|   alphabetic order of operator name.  NB co/1 will FAIL if there are  |
|   no matching operator definitions.  If you want to save operators on |
|   a file, do (write(':- '), co).                                      |
|                                                                       |
+----------------------------------------------------------------------*/
 
co :-
        co("*").
 
co(Pattern) :-
        co(Pattern, Operators),
        answer_List(Operators, 31).
 
co(op(Precedence, Type, Name), Operators) :-
        setof(Operator, isCurrent(Precedence, Type, Name, Operator), Operators).
co(Pattern, Operators) :-
        co(op(_, _, Pattern), Operators).
 
co(Precedence, Type, Name) :-
        co(op(Precedence, Type, Name)).
 
 
        isCurrent(Range, TypePattern, NamePattern, op(Prec, Type, Name)) :-
                current_op(Prec, Type, Name),
                in_Range(Range, Prec),
                isCurrent(TypePattern, Type),
                isCurrent(NamePattern, Name).
 
 
                in_Range(Variable, X) :-
                        var(Variable), !.
                in_Range(Lower-Upper, X) :-
                        Lower =< X, X =< Upper.
                in_Range(<(Limit), X) :-
                        X < Limit.
                in_Range(>=(Limit), X) :-
                        X >= Limit.
                in_Range(>(Limit), X) :-
                        X > Limit.
                in_Range(=<(Limit), X) :-
                        X =< Limit.
                in_Range(=\=(Limit), X) :-
                        X =\= Limit.
                in_Range(=:=(Limit), Limit).
                in_Range(=(Limit), Limit).
                in_Range([Head|_], X) :-
                        in_Range(Head, X).
                in_Range([_|Tail], X) :- !,
                        in_Range(Tail, X).
                in_Range(X, X) :-
                        integer(X).
 
 
/*----------------------------------------------------------------------+
|                                                                       |
|                           Current Functor                             |
|                           Current Predicate                           |
|                                                                       |
|   The predicates available to the user are                            |
|       cf(TermPattern, AnswerList)             -- return functors      |
|       cf(TermPattern)                         -- display functors     |
|       cf                                      -- display all functors |
|       cp(TermPattern, AnswerList)             -- return predicates    |
|       cp(TermPattern)                         -- display predicates   |
|       cp                                      -- display all "        |
|                                                                       |
|   TermPattern ::=                                                     |
|       AtomPattern/Range                                               |
|       AtomPattern                             -- same as P/_          |
|       [ TermPattern | TermPattern ]           -- matches either       |
|       Term                                    -- functor(T,F,N)=>F/N  |
|                                                                       |
|   cf is defined as an operator for convenience.  The result is a list |
|   of functor specifications in the form Functor/Arity.  It would be   |
|   useful at times to have functors represented by their most general  |
|   term, but unfortunately setof/3 calls compare/3 to do the ordering  |
|   and compare/3 orders on arity first.  This method will at least     |
|   return things in the natural alphabetic order.  NB cf/1 will FAIL   |
|   if no functors match, while cf/2 will return the empty list.  Since |
|   Atom Patterns can be disjunctions too, there would appear to be an  |
|   ambiguity here.  However, [A|A] qua F = [A qua F|A qua F], so all   |
|   is well.                                                            |
|   cp is similar, but matches only current predicates.  A predicate    |
|   is current if and only if the interpreter has a clause for it; it   |
|   might be worth while telling cp about system predicates but I have  |
|   not done so yet.                                                    |
|                                                                       |
+----------------------------------------------------------------------*/
 
cf :-
        cf(Functor/Arity).
 
cf(Pattern) :-
        cf(Pattern, Functors),
        answer_List(Functors, 32).
 
cf(Pattern, Functors) :-
        setof(Functor, isCurrent(Pattern, cf, Functor), Functors).
 
 
cp :-
        cp(_/_).
 
cp(Pattern) :-
        cp(Pattern, Predicates),
        answer_List(Predicates, 32).
 
cp(Pattern, Predicates) :-
        setof(Predicate, isCurrent(Pattern, cp, Predicate), Predicates).
 
 
isCurrent(Functor/Arity, WhoWantsToKnow, Functor/Arity) :-
        atom(Functor),
        integer(Arity), !,
        current_functor(Functor, MostGeneralTerm),
        functor(MostGeneralTerm, Functor, Arity),
        final_Check(WhoWantsToKnow, atom, Functor, MostGeneralTerm).
isCurrent(AtomPattern/Range, WhoWantsToKnow, Functor/Arity) :- !,
        isCurrent(AtomPattern, Functor),
        current_functor(Functor, MostGeneralTerm),
        functor(MostGeneralTerm, Functor, Arity),
        in_Range(Range, Arity),
        final_Check(WhoWantsToKnow, var, Functor, MostGeneralTerm).
isCurrent([Head|Tail], WhoWantsToKnow, Answer) :-
        integer(Head), !,
        isCurrent([Head|Tail]/_, WhoWantsToKnow, Answer).
isCurrent([Head|_], WhoWantsToKnow, Answer) :-
        isCurrent(Head, WhoWantsToKnow, Answer).
isCurrent([_|Tail], WhoWantsToKnow, Answer) :- !,
        isCurrent(Tail, WhoWantsToKnow, Answer).
isCurrent(Functor, WhoWantsToKnow, Answer) :-
        atom(Functor), !,
        isCurrent(Functor/Arity, WhoWantsToKnow, Answer).
isCurrent(Pattern, WhoWantsToKnow, Functor/Arity) :-
        ixref_Pattern(Pattern), !,
        ixref_Current(Pattern, Functor, Arity).
%%%     functor(MostGeneralTerm, Functor, Arity),
%%%     final_Check(WhoWantsToKnow, atom, Functor, MostGeneralTerm).
isCurrent(Term, WhoWantsToKnow, Answer) :-
        functor(Term, Functor, Arity), !,
        isCurrent(Functor/Arity, WhoWantsToKnow, Answer).
 
 
final_Check(cf, _,    Functor, Term).
final_Check(cp, _,    Functor, Term) :-
        current_predicate(Functor, Term).
final_Check(pp, var,  Functor, Term) :-
        current_predicate(Functor, Term),
        \+ clause(Term, incore(Term)).
final_Check(pp, atom, Functor, Term).
final_Check(sp, _,    Functor, Term) :-
        functor(Term, Functor, Arity),
        call('$seen'( Functor, Arity)).
 
 
/*----------------------------------------------------------------------+
|                                                                       |
|                       Pretty-Print Predicates                         |
|                                                                       |
|   The predicates provided to the user are                             |
|       pp(TermPattern)                 -- display selected predicates  |
|       pp(help)                        -- display help summary         |
|       pp                              -- display entire program       |
|                                                                       |
|   'pp' will print out the entire program, and will precede it with    |
|   a declaration of all the current operators.  pp(foo/2) will look    |
|   in the file which defines foo/2 if the predicate is compiled or     |
|   otherwise invisible; it adds a comment saying which file it read    |
|   so that you can tell.  This needs the '$defn'(Fn,Ar, File) facts    |
|   that IXREF puts in the database.  If you have these facts, you can  |
|   also ask pp from(File) to see everything defined in a particular    |
|   file; if you have no such facts no harm is done.  pp(help) needs    |
|   HELPER loaded before it will work.  pp is an operator.              |
|                                                                       |
|   The layout produced by PP suits my taste.  I have asked for other   |
|   people to supply me with rules to produce something that suits them,|
|   with the idea of parameterising PP.  Since no-one has done this,    |
|   PP is still as inflexible as listing/1.                             |
|                                                                       |
+----------------------------------------------------------------------*/
 
pp :-
        puts(":-^_"), co, nl,
        pp(_/_).
 
pp(help) :- !,
        give_help('pp.hlp').
pp(Pattern) :-
        setof(Predicate, isCurrent(Pattern, pp, Predicate), Predicates),
        pp_explicit(Predicates).
 
 
        pp_explicit([Head|Tail]) :-
                pp_explicit(Head), !,
                pp_explicit(Tail).
        pp_explicit([]).
        pp_explicit(Functor/Arity) :-
                functor(Head, Functor, Arity),
                final_Check(pp, var, Functor, Head), !,
                (   clause(Head, Body),
                        pp_explicit((Head:-Body), Head)
                |   nl
                ).
        pp_explicit(Functor/Arity) :-
                call('$defn'(Functor, Arity, File)), !,
                functor(Head, Functor, Arity),
                seeing(OldFile),
                nofileerrors,
                (   see(File),
                        puts("% From "), writeq(File), puts("^_^_"),
                        repeat,
                            read(Term),
                            expand_term(Term, Form),
                            pp_explicit(Form, Head), !,
                        nl,
                        seen,
                        see(OldFile)
                |   /* unable to open the file */
                        display('** can''t see '), display(File), ttynl
                ), !.
        pp_explicit(Functor/Arity) :-
                display('** '), display(Functor), display(/), display(Arity),
                display(' is undefined'), ttynl.
        
 
                pp_explicit(end_of_file, _) :- !.
                pp_explicit(Head, Head) :- !,
                        pp_explicit((Head:-true), Head).
                pp_explicit((Head :-Body), Head) :-
                        numbervars((Head:-Body), 0, _),
                        writeq(Head),
                        pp_conjunction(Body, 0, 2, 8),
                        fail.
        
        
        pp_conjunction((A,B), L, R, D) :- !,
                pp_conjunction(A, L, 1, D), !,
                pp_conjunction(B, 1, R, D).
        pp_conjunction(true, L, 2, D) :- !,
                puts(".^_").
        pp_conjunction((A;B), L, R, D) :- !,
                pp_head(fail, L, D),
                pp_disjunction((A;B), 0, 2, D),
                pp_tail(R, ".^_").
        pp_conjunction((A->B), L, R, D) :- !,
                pp_conjunction(A, L, 5, D), !,
                pp_conjunction(B, 5, R, D).
        pp_conjunction(Goal, L, R, D) :-
                pp_head(Goal, L, D),
                writeq(Goal),
                pp_tail(R, ".^_").
 
                pp_head(!,    0, D) :- !, puts(" :- ").
                pp_head(!,    1, D) :- !, puts(",  ").
                pp_head(Goal, 0, D) :- !, puts(" :-^_"), tab(D).
                pp_head(Goal, 1, D) :- !, puts(",^_"), tab(D).
                pp_head(Goal, 3, D) :- !, puts("(   ").
                pp_head(Goal, 4, D) :- !, puts("|   ").
                pp_head(Goal, 5, D) :- !, puts(" ->^_"), tab(D).
 
                pp_tail(2, C) :- !, puts(C).
                pp_tail(_, _).
 
                pp_disjunction((A;B), L, R, D) :- !,
                        pp_disjunction(A, L, 1, D), !,
                        pp_disjunction(B, 1, R, D).
                pp_disjunction(Conj,  L, R, D) :-
                        E is D+8, M is L+3,
                        pp_conjunction(Conj, M, 1, E),
                        nl, tab(D),
                        pp_tail(R, ")").
 %%EOF%%