List predicate(s) calling predicate(s)

In file prolog_walk_code examples.

:- module(prolog_walk_code_examples,[
        assert_call_graph/0,
        list_called_by/2
        ]).

% -----------------------------------------------------------------------------

:- working_directory(_,'C:/Users/Groot').
% ['prolog_walk_code examples'].

% -----------------------------------------------------------------------------

:- dynamic
        calls/2.

assert_call_graph :-
        retractall(calls(_, _)),
        prolog_walk_code([ trace_reference(_),
                           on_trace(assert_edge),
                           source(false)
                         ]),
        predicate_property(calls(_,_), number_of_clauses(N)),
        format('Got ~D edges~n', [N]).

assert_edge(Callee, Caller, _Where) :-
        calls(Caller, Callee), !.
assert_edge(Callee, Caller, _Where) :-
        assertz(calls(Caller, Callee)).

% NB This has to come after assert_call_graph/0
%
% If there are no calls/2 facts, the goals will return nothing,
% so this will assert the facts into the Prolog database.
:- if(\+ prolog_walk_code_examples:calls(_,_)).
:- assert_call_graph.
:- endif.

% queries

list_called_by((M1:Name1/Arity1),M:Name/Arity) :-
    ground(M),
    ground(Name),
    ground(Arity), !,
    compound_name_arity(Head,Name,Arity),
    prolog_walk_code_examples:calls(M1:Head1,_:Head),
    functor(Head1,Name1,Arity1).

list_called_by(M:Name/Arity,Called_by) :-
    var(Called_by), !,
    setof(pi(M:Name/Arity),local_predicate_generator(Name,M:Name/Arity),PI_set),
    member(pi(M:Name/Arity),PI_set),
    setof(Call,list_called_by(Call,M:Name/Arity),Called_by).

local_predicate_generator(Name,M:Name/Arity) :-
    current_predicate(M:Name/Arity),
    local_predicate(M:Name/Arity).

local_predicate(M:Name/Arity) :-
    compound_name_arity(Head,Name,Arity),
    \+ predicate_property(M:Head,imported_from(_)).

Example run:

Welcome to SWI-Prolog (threaded, 64 bits, version 8.3.5-8-ge14460a94)

?- working_directory(_,'C:/Users/Groot').
true.

?- ['prolog_walk_code examples'].
Got 6,462 edges
true.

?- list_called_by(M:append/Arity,Called_by).
M = lists,
Arity = 2,
Called_by = [ansi_term:ansi_get_color_/2] ;
M = lists,
Arity = 3,
Called_by = [ansi_term:read_pattern/3, arithmetic:do_expand_function/3, pce_goal_expansion:expand/2, predicate_options:extend/3, predopts_analysis:attr_unify_hook/2, prolog_codewalk:extend/6, prolog_codewalk:extend_term_pos/3, prolog_debug:debug_message_context/1, prolog_debug:update_debug/6, prolog_edit:substitute/4, prolog_metainference:annotate_meta_arg/3, prolog_stack:join_stacks/3] ;
false.

I made this a Discourse Wiki topic so that anyone with a proper trust level can edit this. If you want to improve the code, add some documentation, or other, feel free.

There will probably be a few more topics in this category related to Prolog code analysis that I might pull together into a Wiki, but for now having the code in a more prominent place is better than being buried in a Wiki discussion topic.


Also see: List the properties for predicate(s)