1/* Part of SWI-Prolog 2 3 Author: Jan Wielemaker 4 E-mail: J.Wielemaker@vu.nl 5 WWW: http://www.swi-prolog.org 6 Copyright (c) 2012-2016, VU University Amsterdam 7 All rights reserved. 8 9 Redistribution and use in source and binary forms, with or without 10 modification, are permitted provided that the following conditions 11 are met: 12 13 1. Redistributions of source code must retain the above copyright 14 notice, this list of conditions and the following disclaimer. 15 16 2. Redistributions in binary form must reproduce the above copyright 17 notice, this list of conditions and the following disclaimer in 18 the documentation and/or other materials provided with the 19 distribution. 20 21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 31 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 POSSIBILITY OF SUCH DAMAGE. 33*/ 34 35:- module(prolog_codewalk, 36 [ prolog_walk_code/1, % +Options 37 prolog_program_clause/2 % -ClauseRef, +Options 38 ]). 39:- use_module(library(option)). 40:- use_module(library(record)). 41:- use_module(library(debug)). 42:- use_module(library(apply)). 43:- use_module(library(lists)). 44:- use_module(library(prolog_metainference)).
78:- meta_predicate 79 prolog_walk_code( ). 80 81:- multifile 82 prolog:called_by/4, 83 prolog:called_by/2. 84 85:- predicate_options(prolog_walk_code/1, 1, 86 [ undefined(oneof([ignore,error,trace])), 87 autoload(boolean), 88 clauses(list), 89 module(atom), 90 module_class(list(oneof([user,system,library, 91 test,development]))), 92 source(boolean), 93 trace_reference(any), 94 on_trace(callable), 95 infer_meta_predicates(oneof([false,true,all])), 96 evaluate(boolean), 97 verbose(boolean) 98 ]). 99 100:- record 101 walk_option(undefined:oneof([ignore,error,trace])=ignore, 102 autoload:boolean=true, 103 source:boolean=true, 104 module:atom, % Only analyse given module 105 module_class:list(oneof([user,system,library, 106 test,development]))=[user,library], 107 infer_meta_predicates:oneof([false,true,all])=true, 108 clauses:list, % Walk only these clauses 109 trace_reference:any=(-), 110 on_trace:callable, % Call-back on trace hits 111 % private stuff 112 clause, % Processed clause 113 caller, % Head of the caller 114 initialization, % Initialization source 115 undecided, % Error to throw error 116 evaluate:boolean, % Do partial evaluation 117 verbose:boolean=false). % Report progress 118 119:- thread_local 120 multifile_predicate/3. % Name, Arity, Module
Options processed:
ignore
or
error
(default is ignore
).source(false)
and then process only interesting
clauses with source information.user
and library
.true
(default), analysis is
only restarted if the inferred meta-predicate contains a
callable argument. If all
, it will be restarted until no
more new meta-predicates can be found.trace_reference
is found, call
call(OnTrace, Callee, Caller, Location)
, where Location is one
of these:
clause_term_position(+ClauseRef, +TermPos)
clause(+ClauseRef)
file_term_position(+Path, +TermPos)
file(+File, +Line, -1, _)
Caller is the qualified head of the calling clause or the atom '<initialization>'.
false
(default true
), to not try to obtain detailed
source information for printed messages.true
(default false
), report derived meta-predicates
and iterations.
@compat OnTrace was called using Caller-Location in older versions.
196prolog_walk_code(Options) :- 197 meta_options(is_meta, Options, QOptions), 198 prolog_walk_code(1, QOptions). 199 200prolog_walk_code(Iteration, Options) :- 201 statistics(cputime, CPU0), 202 make_walk_option(Options, OTerm, _), 203 ( walk_option_clauses(OTerm, Clauses), 204 nonvar(Clauses) 205 -> walk_clauses(Clauses, OTerm) 206 ; forall(( walk_option_module(OTerm, M), 207 current_module(M), 208 scan_module(M, OTerm) 209 ), 210 find_walk_from_module(M, OTerm)), 211 walk_from_multifile(OTerm), 212 walk_from_initialization(OTerm) 213 ), 214 infer_new_meta_predicates(New, OTerm), 215 statistics(cputime, CPU1), 216 ( New \== [] 217 -> CPU is CPU1-CPU0, 218 ( walk_option_verbose(OTerm, true) 219 -> Level = informational 220 ; Level = silent 221 ), 222 print_message(Level, 223 codewalk(reiterate(New, Iteration, CPU))), 224 succ(Iteration, Iteration2), 225 prolog_walk_code(Iteration2, Options) 226 ; true 227 ). 228 229is_meta(on_trace).
236walk_clauses(Clauses, OTerm) :-
237 must_be(list, Clauses),
238 forall(member(ClauseRef, Clauses),
239 ( user:clause(CHead, Body, ClauseRef),
240 ( CHead = Module:Head
241 -> true
242 ; Module = user,
243 Head = CHead
244 ),
245 walk_option_clause(OTerm, ClauseRef),
246 walk_option_caller(OTerm, Module:Head),
247 walk_called_by_body(Body, Module, OTerm)
248 )).
254scan_module(M, OTerm) :-
255 walk_option_module_class(OTerm, Classes),
256 module_property(M, class(Class)),
257 memberchk(Class, Classes).
266walk_from_initialization(OTerm) :- 267 walk_option_caller(OTerm, '<initialization>'), 268 forall('$init_goal'(_File, Goal, SourceLocation), 269 ( walk_option_initialization(OTerm, SourceLocation), 270 walk_from_initialization(Goal, OTerm))). 271 272walk_from_initialization(M:Goal, OTerm) :- 273 scan_module(M, OTerm), 274 !, 275 walk_called_by_body(Goal, M, OTerm). 276walk_from_initialization(_, _).
284find_walk_from_module(M, OTerm) :- 285 debug(autoload, 'Analysing module ~q', [M]), 286 forall(predicate_in_module(M, PI), 287 walk_called_by_pred(M:PI, OTerm)). 288 289walk_called_by_pred(Module:Name/Arity, _) :- 290 multifile_predicate(Name, Arity, Module), 291 !. 292walk_called_by_pred(Module:Name/Arity, _) :- 293 functor(Head, Name, Arity), 294 predicate_property(Module:Head, multifile), 295 !, 296 assertz(multifile_predicate(Name, Arity, Module)). 297walk_called_by_pred(Module:Name/Arity, OTerm) :- 298 functor(Head, Name, Arity), 299 ( no_walk_property(Property), 300 predicate_property(Module:Head, Property) 301 -> true 302 ; walk_option_caller(OTerm, Module:Head), 303 walk_option_clause(OTerm, ClauseRef), 304 forall(catch(clause(Module:, Body, ClauseRef), _, fail), 305 walk_called_by_body(Body, Module, OTerm)) 306 ). 307 308no_walk_property(number_of_rules(0)). % no point walking only facts 309no_walk_property(foreign). % cannot walk foreign code
315walk_from_multifile(OTerm) :- 316 forall(retract(multifile_predicate(Name, Arity, Module)), 317 walk_called_by_multifile(Module:Name/Arity, OTerm)). 318 319walk_called_by_multifile(Module:Name/Arity, OTerm) :- 320 functor(Head, Name, Arity), 321 forall(catch(clause_not_from_development( 322 Module:Head, Body, ClauseRef, OTerm), 323 _, fail), 324 ( walk_option_clause(OTerm, ClauseRef), 325 walk_option_caller(OTerm, Module:Head), 326 walk_called_by_body(Body, Module, OTerm) 327 )).
335clause_not_from_development(Module:Head, Body, Ref, OTerm) :-
336 clause(Module:, Body, Ref),
337 \+ ( clause_property(Ref, file(File)),
338 module_property(LoadModule, file(File)),
339 \+ scan_module(LoadModule, OTerm)
340 ).
ignore
, error
350walk_called_by_body(True, _, _) :- 351 True == true, 352 !. % quickly deal with facts 353walk_called_by_body(Body, Module, OTerm) :- 354 set_undecided_of_walk_option(error, OTerm, OTerm1), 355 set_evaluate_of_walk_option(false, OTerm1, OTerm2), 356 catch(walk_called(Body, Module, _TermPos, OTerm2), 357 missing(Missing), 358 walk_called_by_body(Missing, Body, Module, OTerm)), 359 !. 360walk_called_by_body(Body, Module, OTerm) :- 361 format(user_error, 'Failed to analyse:~n', []), 362 portray_clause(('<head>' :- Body)), 363 debug_walk(Body, Module, OTerm). 364 365% recompile this library after `debug(codewalk(trace))` and re-try 366% for debugging failures. 367:- if(debugging(codewalk(trace))). 368debug_walk(Body, Module, OTerm) :- 369 gtrace, 370 walk_called_by_body(Body, Module, OTerm). 371:- else. 372debug_walk(_,_,_). 373:- endif.
380walk_called_by_body(Missing, Body, _, OTerm) :- 381 debugging(codewalk), 382 format(user_error, 'Retrying due to ~w (~p)~n', [Missing, OTerm]), 383 portray_clause(('<head>' :- Body)), fail. 384walk_called_by_body(undecided_call, Body, Module, OTerm) :- 385 catch(forall(walk_called(Body, Module, _TermPos, OTerm), 386 true), 387 missing(Missing), 388 walk_called_by_body(Missing, Body, Module, OTerm)). 389walk_called_by_body(subterm_positions, Body, Module, OTerm) :- 390 ( ( walk_option_clause(OTerm, ClauseRef), nonvar(ClauseRef), 391 clause_info(ClauseRef, _, TermPos, _NameOffset), 392 TermPos = term_position(_,_,_,_,[_,BodyPos]) 393 -> WBody = Body 394 ; walk_option_initialization(OTerm, SrcLoc), 395 ground(SrcLoc), SrcLoc = _File:_Line, 396 initialization_layout(SrcLoc, Module:Body, WBody, BodyPos) 397 ) 398 -> catch(forall(walk_called(WBody, Module, BodyPos, OTerm), 399 true), 400 missing(subterm_positions), 401 walk_called_by_body(no_positions, Body, Module, OTerm)) 402 ; set_source_of_walk_option(false, OTerm, OTerm2), 403 forall(walk_called(Body, Module, _BodyPos, OTerm2), 404 true) 405 ). 406walk_called_by_body(no_positions, Body, Module, OTerm) :- 407 set_source_of_walk_option(false, OTerm, OTerm2), 408 forall(walk_called(Body, Module, _NoPos, OTerm2), 409 true).
If Goal is disjunctive, walk_called succeeds with a
choice-point. Backtracking analyses the alternative control
path(s)
.
Options:
undecided_call
true
(default), evaluate some goals. Notably =/2.439walk_called(Term, Module, parentheses_term_position(_,_,Pos), OTerm) :- 440 nonvar(Pos), 441 !, 442 walk_called(Term, Module, Pos, OTerm). 443walk_called(Var, _, TermPos, OTerm) :- 444 var(Var), % Incomplete analysis 445 !, 446 undecided(Var, TermPos, OTerm). 447walk_called(M:G, _, term_position(_,_,_,_,[MPos,Pos]), OTerm) :- 448 !, 449 ( nonvar(M) 450 -> walk_called(G, M, Pos, OTerm) 451 ; undecided(M, MPos, OTerm) 452 ). 453walk_called((A,B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :- 454 !, 455 walk_called(A, M, PA, OTerm), 456 walk_called(B, M, PB, OTerm). 457walk_called((A->B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :- 458 !, 459 walk_called(A, M, PA, OTerm), 460 walk_called(B, M, PB, OTerm). 461walk_called((A*->B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :- 462 !, 463 walk_called(A, M, PA, OTerm), 464 walk_called(B, M, PB, OTerm). 465walk_called(\+(A), M, term_position(_,_,_,_,[PA]), OTerm) :- 466 !, 467 \+ \+ walk_called(A, M, PA, OTerm). 468walk_called((A;B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :- 469 !, 470 ( walk_option_evaluate(OTerm, Eval), Eval == true 471 -> Goal = (A;B), 472 setof(Goal, 473 ( walk_called(A, M, PA, OTerm) 474 ; walk_called(B, M, PB, OTerm) 475 ), 476 Alts0), 477 variants(Alts0, Alts), 478 member(Goal, Alts) 479 ; \+ \+ walk_called(A, M, PA, OTerm), % do not propagate bindings 480 \+ \+ walk_called(B, M, PB, OTerm) 481 ). 482walk_called(Goal, Module, TermPos, OTerm) :- 483 walk_option_trace_reference(OTerm, To), To \== (-), 484 ( subsumes_term(To, Module:Goal) 485 -> M2 = Module 486 ; predicate_property(Module:Goal, imported_from(M2)), 487 subsumes_term(To, M2:Goal) 488 ), 489 print_reference(M2:Goal, TermPos, trace, OTerm), 490 fail. % Continue search 491walk_called(Goal, Module, _, OTerm) :- 492 evaluate(Goal, Module, OTerm), 493 !. 494walk_called(Goal, M, TermPos, OTerm) :- 495 ( ( predicate_property(M:Goal, imported_from(IM)) 496 -> true 497 ; IM = M 498 ), 499 prolog:called_by(Goal, IM, M, Called) 500 ; prolog:called_by(Goal, Called) 501 ), 502 Called \== [], 503 !, 504 walk_called_by(Called, M, Goal, TermPos, OTerm). 505walk_called(Meta, M, term_position(_,E,_,_,ArgPosList), OTerm) :- 506 ( walk_option_autoload(OTerm, false) 507 -> nonvar(M), 508 '$get_predicate_attribute'(M:Meta, defined, 1) 509 ; true 510 ), 511 ( predicate_property(M:Meta, meta_predicate(Head)) 512 ; inferred_meta_predicate(M:Meta, Head) 513 ), 514 !, 515 walk_option_clause(OTerm, ClauseRef), 516 register_possible_meta_clause(ClauseRef), 517 walk_meta_call(1, Head, Meta, M, ArgPosList, E-E, OTerm). 518walk_called(Goal, Module, _, _) :- 519 nonvar(Module), 520 '$get_predicate_attribute'(Module:Goal, defined, 1), 521 !. 522walk_called(Goal, Module, TermPos, OTerm) :- 523 callable(Goal), 524 !, 525 undefined(Module:Goal, TermPos, OTerm). 526walk_called(Goal, _Module, TermPos, OTerm) :- 527 not_callable(Goal, TermPos, OTerm).
531undecided(Var, TermPos, OTerm) :- 532 walk_option_undecided(OTerm, Undecided), 533 ( var(Undecided) 534 -> Action = ignore 535 ; Action = Undecided 536 ), 537 undecided(Action, Var, TermPos, OTerm). 538 539undecided(ignore, _, _, _) :- !. 540undecided(error, _, _, _) :- 541 throw(missing(undecided_call)).
545evaluate(Goal, Module, OTerm) :- 546 walk_option_evaluate(OTerm, Evaluate), 547 Evaluate \== false, 548 evaluate(Goal, Module). 549 550evaluate(A=B, _) :- 551 unify_with_occurs_check(A, B).
557undefined(_, _, OTerm) :- 558 walk_option_undefined(OTerm, ignore), 559 !. 560undefined(Goal, _, _) :- 561 predicate_property(Goal, autoload(_)), 562 !. 563undefined(Goal, TermPos, OTerm) :- 564 ( walk_option_undefined(OTerm, trace) 565 -> Why = trace 566 ; Why = undefined 567 ), 568 print_reference(Goal, TermPos, Why, OTerm).
574not_callable(Goal, TermPos, OTerm) :-
575 print_reference(Goal, TermPos, not_callable, OTerm).
584print_reference(Goal, TermPos, Why, OTerm) :- 585 walk_option_clause(OTerm, Clause), nonvar(Clause), 586 !, 587 ( compound(TermPos), 588 arg(1, TermPos, CharCount), 589 integer(CharCount) % test it is valid 590 -> From = clause_term_position(Clause, TermPos) 591 ; walk_option_source(OTerm, false) 592 -> From = clause(Clause) 593 ; From = _, 594 throw(missing(subterm_positions)) 595 ), 596 print_reference2(Goal, From, Why, OTerm). 597print_reference(Goal, TermPos, Why, OTerm) :- 598 walk_option_initialization(OTerm, Init), nonvar(Init), 599 Init = File:Line, 600 !, 601 ( compound(TermPos), 602 arg(1, TermPos, CharCount), 603 integer(CharCount) % test it is valid 604 -> From = file_term_position(File, TermPos) 605 ; walk_option_source(OTerm, false) 606 -> From = file(File, Line, -1, _) 607 ; From = _, 608 throw(missing(subterm_positions)) 609 ), 610 print_reference2(Goal, From, Why, OTerm). 611print_reference(Goal, _, Why, OTerm) :- 612 print_reference2(Goal, _, Why, OTerm). 613 614print_reference2(Goal, From, trace, OTerm) :- 615 walk_option_on_trace(OTerm, Closure), 616 walk_option_caller(OTerm, Caller), 617 nonvar(Closure), 618 call(Closure, Goal, Caller, From), 619 !. 620print_reference2(Goal, From, Why, _OTerm) :- 621 make_message(Why, Goal, From, Message, Level), 622 print_message(Level, Message). 623 624 625make_message(undefined, Goal, Context, 626 error(existence_error(procedure, PI), Context), error) :- 627 goal_pi(Goal, PI). 628make_message(not_callable, Goal, Context, 629 error(type_error(callable, Goal), Context), error). 630make_message(trace, Goal, Context, 631 trace_call_to(PI, Context), informational) :- 632 goal_pi(Goal, PI). 633 634 635goal_pi(Goal, M:Name/Arity) :- 636 strip_module(Goal, M, Head), 637 callable(Head), 638 !, 639 functor(Head, Name, Arity). 640goal_pi(Goal, Goal). 641 642:- dynamic 643 possible_meta_predicate/2.
652register_possible_meta_clause(ClausesRef) :- 653 nonvar(ClausesRef), 654 clause_property(ClausesRef, predicate(PI)), 655 pi_head(PI, Head, Module), 656 module_property(Module, class(user)), 657 \+ predicate_property(Module:Head, meta_predicate(_)), 658 \+ inferred_meta_predicate(Module:Head, _), 659 \+ possible_meta_predicate(Head, Module), 660 !, 661 assertz(possible_meta_predicate(Head, Module)). 662register_possible_meta_clause(_). 663 664pi_head(Module:Name/Arity, Head, Module) :- 665 !, 666 functor(Head, Name, Arity). 667pi_head(_, _, _) :- 668 assertion(fail).
672infer_new_meta_predicates([], OTerm) :- 673 walk_option_infer_meta_predicates(OTerm, false), 674 !. 675infer_new_meta_predicates(MetaSpecs, OTerm) :- 676 findall(Module:MetaSpec, 677 ( retract(possible_meta_predicate(Head, Module)), 678 infer_meta_predicate(Module:Head, MetaSpec), 679 ( walk_option_infer_meta_predicates(OTerm, all) 680 -> true 681 ; calling_metaspec(MetaSpec) 682 ) 683 ), 684 MetaSpecs).
691calling_metaspec(Head) :- 692 arg(_, Head, Arg), 693 calling_metaarg(Arg), 694 !. 695 696calling_metaarg(I) :- integer(I), !. 697calling_metaarg(^). 698calling_metaarg(//).
711walk_meta_call(I, Head, Meta, M, ArgPosList, EPos, OTerm) :- 712 arg(I, Head, AS), 713 !, 714 ( ArgPosList = [ArgPos|ArgPosTail] 715 -> true 716 ; ArgPos = EPos, 717 ArgPosTail = [] 718 ), 719 ( integer(AS) 720 -> arg(I, Meta, MA), 721 extend(MA, AS, Goal, ArgPos, ArgPosEx, OTerm), 722 walk_called(Goal, M, ArgPosEx, OTerm) 723 ; AS == (^) 724 -> arg(I, Meta, MA), 725 remove_quantifier(MA, Goal, ArgPos, ArgPosEx, M, MG, OTerm), 726 walk_called(Goal, MG, ArgPosEx, OTerm) 727 ; AS == (//) 728 -> arg(I, Meta, DCG), 729 walk_dcg_body(DCG, M, ArgPos, OTerm) 730 ; true 731 ), 732 succ(I, I2), 733 walk_meta_call(I2, Head, Meta, M, ArgPosTail, EPos, OTerm). 734walk_meta_call(_, _, _, _, _, _, _). 735 736remove_quantifier(Goal, _, TermPos, TermPos, M, M, OTerm) :- 737 var(Goal), 738 !, 739 undecided(Goal, TermPos, OTerm). 740remove_quantifier(_^Goal0, Goal, 741 term_position(_,_,_,_,[_,GPos]), 742 TermPos, M0, M, OTerm) :- 743 !, 744 remove_quantifier(Goal0, Goal, GPos, TermPos, M0, M, OTerm). 745remove_quantifier(M1:Goal0, Goal, 746 term_position(_,_,_,_,[_,GPos]), 747 TermPos, _, M, OTerm) :- 748 !, 749 remove_quantifier(Goal0, Goal, GPos, TermPos, M1, M, OTerm). 750remove_quantifier(Goal, Goal, TermPos, TermPos, M, M, _).
758walk_called_by([], _, _, _, _). 759walk_called_by([H|T], M, Goal, TermPos, OTerm) :- 760 ( H = G0+N 761 -> subterm_pos(G0, M, Goal, TermPos, G, GPos), 762 ( extend(G, N, G2, GPos, GPosEx, OTerm) 763 -> walk_called(G2, M, GPosEx, OTerm) 764 ; true 765 ) 766 ; subterm_pos(H, M, Goal, TermPos, G, GPos), 767 walk_called(G, M, GPos, OTerm) 768 ), 769 walk_called_by(T, M, Goal, TermPos, OTerm). 770 771subterm_pos(Sub, _, Term, TermPos, Sub, SubTermPos) :- 772 subterm_pos(Sub, Term, TermPos, SubTermPos), 773 !. 774subterm_pos(Sub, M, Term, TermPos, G, SubTermPos) :- 775 nonvar(Sub), 776 Sub = M:H, 777 !, 778 subterm_pos(H, M, Term, TermPos, G, SubTermPos). 779subterm_pos(Sub, _, _, _, Sub, _). 780 781subterm_pos(Sub, Term, TermPos, SubTermPos) :- 782 subterm_pos(Sub, Term, same_term, TermPos, SubTermPos), 783 !. 784subterm_pos(Sub, Term, TermPos, SubTermPos) :- 785 subterm_pos(Sub, Term, ==, TermPos, SubTermPos), 786 !. 787subterm_pos(Sub, Term, TermPos, SubTermPos) :- 788 subterm_pos(Sub, Term, =@=, TermPos, SubTermPos), 789 !. 790subterm_pos(Sub, Term, TermPos, SubTermPos) :- 791 subterm_pos(Sub, Term, subsumes_term, TermPos, SubTermPos), 792 !.
798walk_dcg_body(Var, _Module, TermPos, OTerm) :- 799 var(Var), 800 !, 801 undecided(Var, TermPos, OTerm). 802walk_dcg_body([], _Module, _, _) :- !. 803walk_dcg_body([_|_], _Module, _, _) :- !. 804walk_dcg_body(String, _Module, _, _) :- 805 string(String), 806 !. 807walk_dcg_body(!, _Module, _, _) :- !. 808walk_dcg_body(M:G, _, term_position(_,_,_,_,[MPos,Pos]), OTerm) :- 809 !, 810 ( nonvar(M) 811 -> walk_dcg_body(G, M, Pos, OTerm) 812 ; undecided(M, MPos, OTerm) 813 ). 814walk_dcg_body((A,B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :- 815 !, 816 walk_dcg_body(A, M, PA, OTerm), 817 walk_dcg_body(B, M, PB, OTerm). 818walk_dcg_body((A->B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :- 819 !, 820 walk_dcg_body(A, M, PA, OTerm), 821 walk_dcg_body(B, M, PB, OTerm). 822walk_dcg_body((A*->B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :- 823 !, 824 walk_dcg_body(A, M, PA, OTerm), 825 walk_dcg_body(B, M, PB, OTerm). 826walk_dcg_body((A;B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :- 827 !, 828 ( walk_dcg_body(A, M, PA, OTerm) 829 ; walk_dcg_body(B, M, PB, OTerm) 830 ). 831walk_dcg_body((A|B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :- 832 !, 833 ( walk_dcg_body(A, M, PA, OTerm) 834 ; walk_dcg_body(B, M, PB, OTerm) 835 ). 836walk_dcg_body({G}, M, brace_term_position(_,_,PG), OTerm) :- 837 !, 838 walk_called(G, M, PG, OTerm). 839walk_dcg_body(G, M, TermPos, OTerm) :- 840 extend(G, 2, G2, TermPos, TermPosEx, OTerm), 841 walk_called(G2, M, TermPosEx, OTerm).
same_term
, ==
, =@=
or subsumes_term
852:- meta_predicate 853 subterm_pos( , , , , ), 854 sublist_pos( , , , , , ). 855 856subterm_pos(_, _, _, Pos, _) :- 857 var(Pos), !, fail. 858subterm_pos(Sub, Term, Cmp, Pos, Pos) :- 859 call(Cmp, Sub, Term), 860 !. 861subterm_pos(Sub, Term, Cmp, term_position(_,_,_,_,ArgPosList), Pos) :- 862 is_list(ArgPosList), 863 compound(Term), 864 nth1(I, ArgPosList, ArgPos), 865 arg(I, Term, Arg), 866 subterm_pos(Sub, Arg, Cmp, ArgPos, Pos). 867subterm_pos(Sub, Term, Cmp, list_position(_,_,ElemPosList,TailPos), Pos) :- 868 sublist_pos(ElemPosList, TailPos, Sub, Term, Cmp, Pos). 869subterm_pos(Sub, {Arg}, Cmp, brace_term_position(_,_,ArgPos), Pos) :- 870 subterm_pos(Sub, Arg, Cmp, ArgPos, Pos). 871 872sublist_pos([EP|TP], TailPos, Sub, [H|T], Cmp, Pos) :- 873 ( subterm_pos(Sub, H, Cmp, EP, Pos) 874 ; sublist_pos(TP, TailPos, Sub, T, Cmp, Pos) 875 ). 876sublist_pos([], TailPos, Sub, Tail, Cmp, Pos) :- 877 TailPos \== none, 878 subterm_pos(Sub, Tail, Cmp, TailPos, Pos).
884extend(Goal, 0, Goal, TermPos, TermPos, _) :- !. 885extend(Goal, _, _, TermPos, TermPos, OTerm) :- 886 var(Goal), 887 !, 888 undecided(Goal, TermPos, OTerm). 889extend(M:Goal, N, M:GoalEx, 890 term_position(F,T,FT,TT,[MPos,GPosIn]), 891 term_position(F,T,FT,TT,[MPos,GPosOut]), OTerm) :- 892 !, 893 ( var(M) 894 -> undecided(N, MPos, OTerm) 895 ; true 896 ), 897 extend(Goal, N, GoalEx, GPosIn, GPosOut, OTerm). 898extend(Goal, N, GoalEx, TermPosIn, TermPosOut, _) :- 899 callable(Goal), 900 !, 901 Goal =.. List, 902 length(Extra, N), 903 extend_term_pos(TermPosIn, N, TermPosOut), 904 append(List, Extra, ListEx), 905 GoalEx =.. ListEx. 906extend(Goal, _, _, TermPos, _, OTerm) :- 907 print_reference(Goal, TermPos, not_callable, OTerm). 908 909extend_term_pos(Var, _, _) :- 910 var(Var), 911 !. 912extend_term_pos(term_position(F,T,FT,TT,ArgPosIn), 913 N, 914 term_position(F,T,FT,TT,ArgPosOut)) :- 915 !, 916 length(Extra, N), 917 maplist(=(0-0), Extra), 918 append(ArgPosIn, Extra, ArgPosOut). 919extend_term_pos(F-T, N, term_position(F,T,F,T,Extra)) :- 920 length(Extra, N), 921 maplist(=(0-0), Extra).
926variants([], []). 927variants([H|T], List) :- 928 variants(T, H, List). 929 930variants([], H, [H]). 931variants([H|T], V, List) :- 932 ( H =@= V 933 -> variants(T, V, List) 934 ; List = [V|List2], 935 variants(T, H, List2) 936 ).
942predicate_in_module(Module, PI) :- 943 current_predicate(Module:PI), 944 PI = Name/Arity, 945 functor(Head, Name, Arity), 946 \+ predicate_property(Module:Head, imported_from(_)). 947 948 949 /******************************* 950 * ENUMERATE CLAUSES * 951 *******************************/
module_class(+list(Classes))
963prolog_program_clause(ClauseRef, Options) :- 964 make_walk_option(Options, OTerm, _), 965 setup_call_cleanup( 966 true, 967 ( current_module(Module), 968 scan_module(Module, OTerm), 969 module_clause(Module, ClauseRef, OTerm) 970 ; retract(multifile_predicate(Name, Arity, MM)), 971 multifile_clause(ClauseRef, MM:Name/Arity, OTerm) 972 ; initialization_clause(ClauseRef, OTerm) 973 ), 974 retractall(multifile_predicate(_,_,_))). 975 976 977module_clause(Module, ClauseRef, _OTerm) :- 978 predicate_in_module(Module, Name/Arity), 979 \+ multifile_predicate(Name, Arity, Module), 980 functor(Head, Name, Arity), 981 ( predicate_property(Module:Head, multifile) 982 -> assertz(multifile_predicate(Name, Arity, Module)), 983 fail 984 ; predicate_property(Module:Head, Property), 985 no_enum_property(Property) 986 -> fail 987 ; catch(nth_clause(Module:Head, _, ClauseRef), _, fail) 988 ). 989 990no_enum_property(foreign). 991 992multifile_clause(ClauseRef, M:Name/Arity, OTerm) :- 993 functor(Head, Name, Arity), 994 catch(clauseref_not_from_development(M:Head, ClauseRef, OTerm), 995 _, fail). 996 997clauseref_not_from_development(Module:Head, Ref, OTerm) :- 998 nth_clause(Module:Head, _N, Ref), 999 \+ ( clause_property(Ref, file(File)), 1000 module_property(LoadModule, file(File)), 1001 \+ scan_module(LoadModule, OTerm) 1002 ). 1003 1004initialization_clause(ClauseRef, OTerm) :- 1005 catch(clause(system:'$init_goal'(_File, M:_Goal, SourceLocation), 1006 true, ClauseRef), 1007 _, fail), 1008 walk_option_initialization(OTerm, SourceLocation), 1009 scan_module(M, OTerm). 1010 1011 1012 /******************************* 1013 * MESSAGES * 1014 *******************************/ 1015 1016:- multifile 1017 prolog:message//1, 1018 prolog:message_location//1. 1019 1020prologmessage(trace_call_to(PI, Context)) --> 1021 [ 'Call to ~q at '-[PI] ], 1022 prolog:message_location(Context). 1023 1024prologmessage_location(clause_term_position(ClauseRef, TermPos)) --> 1025 { clause_property(ClauseRef, file(File)) }, 1026 message_location_file_term_position(File, TermPos). 1027prologmessage_location(clause(ClauseRef)) --> 1028 { clause_property(ClauseRef, file(File)), 1029 clause_property(ClauseRef, line_count(Line)) 1030 }, 1031 !, 1032 [ '~w:~d: '-[File, Line] ]. 1033prologmessage_location(clause(ClauseRef)) --> 1034 { clause_name(ClauseRef, Name) }, 1035 [ '~w: '-[Name] ]. 1036prologmessage_location(file_term_position(Path, TermPos)) --> 1037 message_location_file_term_position(Path, TermPos). 1038prologmessage(codewalk(reiterate(New, Iteration, CPU))) --> 1039 [ 'Found new meta-predicates in iteration ~w (~3f sec)'- 1040 [Iteration, CPU], nl ], 1041 meta_decls(New), 1042 [ 'Restarting analysis ...'-[], nl ]. 1043 1044meta_decls([]) --> []. 1045meta_decls([H|T]) --> 1046 [ ':- meta_predicate ~q.'-[H], nl ], 1047 meta_decls(T). 1048 1049message_location_file_term_position(File, TermPos) --> 1050 { arg(1, TermPos, CharCount), 1051 filepos_line(File, CharCount, Line, LinePos) 1052 }, 1053 [ '~w:~d:~d: '-[File, Line, LinePos] ].
1060filepos_line(File, CharPos, Line, LinePos) :-
1061 setup_call_cleanup(
1062 ( open(File, read, In),
1063 open_null_stream(Out)
1064 ),
1065 ( copy_stream_data(In, Out, CharPos),
1066 stream_property(In, position(Pos)),
1067 stream_position_data(line_count, Pos, Line),
1068 stream_position_data(line_position, Pos, LinePos)
1069 ),
1070 ( close(Out),
1071 close(In)
1072 ))
Prolog code walker
This module walks over the loaded program, searching for callable predicates. It started as part of
library(prolog_autoload)
and has been turned into a seperate module to facilitate operations that require the same reachability analysis, such as finding references to a predicate, finding unreachable code, etc.For example, the following determins the call graph of the loaded program. By using
source(true)
, The exact location of the call in the source file is passed into _Where.*/