/*************************************************************************
*									 *
*	 YAP Prolog 							 *
*									 *
*	Yap Prolog was developed at NCCUP - Universidade do Porto	 *
*									 *
* Copyright L.Damas, V.S.Costa and Universidade do Porto 1985-1997	 *
*									 *
**************************************************************************
*									 *
* File:		listing.pl						 *
* Last rev:								 *
* mods:									 *
* comments:	listing a prolog program				 *
*									 *
*************************************************************************/

:- system_module( '$_listing', [listing/0,
        listing/1,
        portray_clause/1,
        portray_clause/2], []).

:- use_system_module( '$_errors', ['$do_error'/2]).

:- use_system_module( '$_preds', ['$clause'/4,
        '$current_predicate_no_modules'/3]).

/* listing : Listing clauses in the database

*/


/** @pred  listing 


Lists in the current output stream all the clauses for which source code
is available (these include all clauses for dynamic predicates and
clauses for static predicates compiled when source mode was `on`).

 
*/
listing :-
	current_output(Stream),
	'$current_module'(Mod),
        '$current_predicate_no_modules'(Mod,_,Pred),
        '$list_clauses'(Stream,Mod,Pred).
listing.

/** @pred  listing(+ _P_)

Lists predicate  _P_ if its source code is available.

 
*/
listing(MV) :-
    current_output(Stream),
    listing(Stream, MV).

listing(Stream, MV) :-
    '$mlisting'(Stream, MV, _).
listing(Stream, []) :- !.
listing(Stream, [MV|MVs]) :- !,
    listing(Stream,  MV),
    listing(Stream, MVs).	    

'$mlisting'(Stream, MV, M) :-
    ( var(MV) -> MV = NA, '$do_listing'(Stream, M, NA)
	 ;
      atom(MV) -> MV/_ = NA, '$do_listing'(Stream, M, NA)
	 ;
      MV = N//Ar -> ( integer(Ar) -> Ar2 is Ar+2, NA is N/Ar2 ; '$do_listing'(Stream, Na/Ar2, M), Ar2 >= 2, Ar is Ar2-2 )
	 ;
      MV = N/Ar, ( atom(N) -> true ; var(N) ), ( integer(Ar) -> true ; var(A) )  -> '$do_listing'(Stream, M, MV)
	 ;
      MV = M1:PP ->  '$mlisting'(Stream, PP, M1)
	 ;
     '$do_error'(type_error(predicate_indicator,MV),listing(Stream, MV) )
    ).

'$do_listing'(Stream, M, NA) :-
    ( '$$current_predicate'(NA, M),
     '$listing'(NA,M,Stream),
     fail
   ;
     true
   ).

'$listing'(X, M, Stream) :-
        '$funcspec'(X,Name,Arity),
        functor(Pred,Name,Arity),
        '$list_clauses'(Stream,M,Pred).
'$listing'(_,_,_).

'$funcspec'(Name/Arity,Name,Arity) :- !, atom(Name).
'$funcspec'(Name,Name,_) :- atom(Name), !.
'$funcspec'(Name,_,_) :-
	'$do_error'(domain_error(predicate_spec,Name),listing(Name)).

'$list_clauses'(Stream, M, Pred) :-
	'$flags'(Pred,M,Flags,Flags),
	Flags /\ 0x48602000 =\= 0,
	nl(Stream),
	fail.
'$list_clauses'(Stream, M, Pred) :-
    ( '$is_dynamic'(Pred, M) -> true ; '$is_log_updatable'(Pred, M) ), 
    functor( Pred, N, Ar ),
    '$current_module'(Mod),
    ( 
	M == Mod
    ->
      format( Stream, ':- dynamic ~q/~d.~n', [N,Ar])
    ;
      format( Stream, ':- dynamic ~q:~q/~d.~n', [M,N,Ar])
     ),
     fail.
'$list_clauses'(Stream, M, Pred) :-
    '$is_thread_local'(Pred, M), 
    functor( Pred, N, Ar ),
    '$current_module'(Mod),
    ( 
	M == Mod
    ->
      format( Stream, ':- thread_local ~q/~d.~n', [N,Ar])
    ;
      format( Stream, ':- thread_local ~q:~q/~d.~n', [M,N,Ar])
     ),
     fail.
'$list_clauses'(Stream, M, Pred) :-
    '$is_multifile'(Pred, M), 
    functor( Pred, N, Ar ),
    '$current_module'(Mod),
    ( 
	M == Mod
    ->
      format( Stream, ':- multifile ~q/~d.~n', [N,Ar])
    ;
      format( Stream, ':- multifile ~q:~q/~d.~n', [M,N,Ar])
     ),
     fail.
'$list_clauses'(Stream, M, Pred) :-
   '$is_metapredicate'(Pred, M), 
    functor( Pred, Name, Arity ),
    prolog:'$meta_predicate'(Name,M,Arity,PredDef),
    '$current_module'(Mod),
    ( 
	M == Mod
    ->
      format( Stream, ':- ~q.~n', [PredDef])
    ;
      format( Stream, '~:- ~q:~q.~n', [M,PredDef])
     ),
     fail.
'$list_clauses'(Stream, M, Pred) :-
        nl( Stream ),
        fail.
'$list_clauses'(Stream, M, Pred) :-
    '$flags'(Pred,M,Flags,Flags),
	% has to be dynamic, source, or log update.
	Flags /\ 0x08402000 =\= 0,
	'$clause'(Pred, M, Body, _),
	'$current_module'(Mod),
	( M \= Mod -> H = M:Pred ; H = Pred ),
	'$portray_clause'(Stream,(H:-Body)),
        fail.

/** @pred  portray_clause(+ _S_,+ _C_)

Write clause  _C_ on stream  _S_ as if written by listing/0.

 
*/
portray_clause(Stream, Clause) :-
	copy_term_nat(Clause, CopiedClause),
	'$portray_clause'(Stream, CopiedClause),
	fail.
portray_clause(_, _).

/** @pred  portray_clause(+ _C_) 


Write clause  _C_ as if written by listing/0.

 
*/
portray_clause(Clause) :-
        current_output(Stream),
	portray_clause(Stream, Clause).

'$portray_clause'(Stream, (Pred :- true)) :- !,
	'$beautify_vars'(Pred),
	writeq(Stream, Pred),
	format(Stream, '.~n', []).
'$portray_clause'(Stream, (Pred:-Body)) :- !,
	'$beautify_vars'((Pred:-Body)),
	writeq(Stream, Pred),
	format(Stream, ' :-', []),
	'$write_body'(Body, 3, ',', Stream),
	format(Stream, '.~n', []).
'$portray_clause'(Stream, Pred) :- !,
	'$beautify_vars'(Pred),
	writeq(Stream, Pred),
	format(Stream, '.~n', []).

'$write_body'(X,I,T,Stream) :- var(X), !,
	'$beforelit'(T,I,Stream),
	writeq(Stream, '_').
'$write_body'((P,Q), I, T, Stream) :-
        !,
        '$write_body'(P,I,T, Stream),
        put(Stream, 0',),
        '$write_body'(Q,I,',',Stream).
'$write_body'((P->Q;S),I,_, Stream) :-
	!,
	format(Stream, '~n~*c(',[I,0' ]),
	I1 is I+2,
	'$write_body'(P,I1,'(',Stream),
	format(Stream, '~n~*c->',[I,0' ]),
	'$write_disj'((Q;S),I,I1,'->',Stream),
	format(Stream, '~n~*c)',[I,0' ]).
'$write_body'((P->Q|S),I,_,Stream) :-
	!,
	format(Stream, '~n~*c(',[I,0' ]),
	I1 is I+2,
	'$write_body'(P,I,'(',Stream),
	format(Stream, '~n~*c->',[I,0' ]),
	'$write_disj'((Q|S),I,I1,'->',Stream),
	format(Stream, '~n~*c)',[I,0' ]).
'$write_body'((P->Q),I,_,Stream) :-
	!,
	format(Stream, '~n~*c(',[I,0' ]),
	I1 is I+2,
        '$write_body'(P,I1,'(',Stream),
	format(Stream, '~n~*c->',[I,0' ]),
        '$write_body'(Q,I1,'->',Stream),
	format(Stream, '~n~*c)',[I,0' ]).
'$write_body'((P;Q),I,_,Stream) :-
        !,
	format(Stream, '~n~*c(',[I,0' ]),
	I1 is I+2,
	'$write_disj'((P;Q),I,I1,'->',Stream),
	format(Stream, '~n~*c)',[I,0' ]).
'$write_body'((P|Q),I,_,Stream) :-
        !,
	format(Stream, '~n~*c(',[I,0' ]),
	I1 is I+2,
	'$write_disj'((P|Q),I,I1,'->',Stream),
	format(Stream, '~n~*c)',[I,0' ]).
'$write_body'(X,I,T,Stream) :-
        '$beforelit'(T,I,Stream),
        writeq(Stream,X).


'$write_disj'((Q;S),I0,I,C,Stream) :- !,
	'$write_body'(Q,I,C,Stream),
	format(Stream, '~n~*c;',[I0,0' ]),
	'$write_disj'(S,I0,I,';',Stream).
'$write_disj'((Q|S),I0,I,C,Stream) :- !,
	'$write_body'(Q,I,C,Stream),
	format(Stream, '~n~*c|',[I0,0' ]),
	'$write_disj'(S,I0,I,'|',Stream).
'$write_disj'(S,_,I,C,Stream) :-
	'$write_body'(S,I,C,Stream).
	

'$beforelit'('(',_,Stream) :- !, format(Stream,' ',[]).
'$beforelit'(_,I,Stream) :- format(Stream,'~n~*c',[I,0' ]).

'$beautify_vars'(T) :-
	'$list_get_vars'(T,[],L),
	msort(L,SL),
	'$list_transform'(SL,0).


'$list_get_vars'(V,L,[V|L] ) :- var(V), !.
'$list_get_vars'(Atomic, M, M) :-
	primitive(Atomic), !.
'$list_get_vars'([Arg|Args], M, N) :-  !,
	'$list_get_vars'(Arg, M, K),
	'$list_get_vars'(Args, K, N).
'$list_get_vars'(Term, M, N) :-
	Term =.. [_|Args],
	'$list_get_vars'(Args, M, N).

'$list_transform'([],_) :- !.
'$list_transform'([X,Y|L],M) :-
	X == Y,
	X = '$VAR'(M),
	!,
	N is M+1,
	'$list_transform'(L,N).
'$list_transform'('$VAR'(-1).L,M) :- !,
	'$list_transform'(L,M).
'$list_transform'(_.L,M) :-
	'$list_transform'(L,M).