1/***************************************************************************** 2 * This file is part of the Prolog Development Tool (PDT) 3 * 4 * Author: Andreas Becker 5 * WWW: http://sewiki.iai.uni-bonn.de/research/pdt/start 6 * Mail: pdt@lists.iai.uni-bonn.de 7 * Copyright (C): 2012, CS Dept. III, University of Bonn 8 * 9 * All rights reserved. This program is made available under the terms 10 * of the Eclipse Public License v1.0 which accompanies this distribution, 11 * and is available at http://www.eclipse.org/legal/epl-v10.html 12 * 13 ****************************************************************************/ 14 15/* NOTE: This file contains third-party code! 16 17 Most of this file was borrowed from the swi-prolog library 18 prolog_codewalk. Many thanks to the original authors for making their 19 work available to the public. 20 21 The copyright header of the original file 22 follows. 23*/ 24 25/* Part of SWI-Prolog 26 27 Author: Jan Wielemaker 28 E-mail: J.Wielemaker@cs.vu.nl 29 WWW: http://www.swi-prolog.org 30 Copyright (C): 2012, VU University Amsterdam 31 32 This program is free software; you can redistribute it and/or 33 modify it under the terms of the GNU General Public License 34 as published by the Free Software Foundation; either version 2 35 of the License, or (at your option) any later version. 36 37 This program is distributed in the hope that it will be useful, 38 but WITHOUT ANY WARRANTY; without even the implied warranty of 39 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 40 GNU General Public License for more details. 41 42 You should have received a copy of the GNU General Public 43 License along with this library; if not, write to the Free Software 44 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 45 46 As a special exception, if you link this library with other files, 47 compiled with a Free Software compiler, to produce an executable, this 48 library does not by itself cause the resulting executable to be covered 49 by the GNU General Public License. This exception does not however 50 invalidate any other reasons why the executable file might be covered by 51 the GNU General Public License. 52*/ 53 54:- module(pdt_prolog_codewalk, 55 [ pdt_prolog_walk_code/1 % +Options 56 ]). 57:- use_module(library(option)). 58:- use_module(library(record)). 59:- use_module(library(debug)). 60:- use_module(library(apply)). 61:- use_module(library(error)). 62:- use_module(library(lists)). 63:- use_module(library(prolog_clause)). 64:- use_module(pdt_common_pl('metainference/pdt_prolog_metainference')). 65:- use_module(pdt_common_pl('metainference/pdt_meta_specification')).
99:- meta_predicate 100 prolog_walk_code( ). 101 102:- predicate_options(pdt_prolog_walk_code/1, 1, 103 [ undefined(oneof([ignore,error,trace])), 104 autoload(boolean), 105 clauses(list), 106 module(atom), 107 module_class(oneof([default,user,system, 108 library,test,development])), 109 source(boolean), 110 trace_reference(any), 111 on_trace(callable), 112 infer_meta_predicates(oneof([false,true,all])), 113 reiterate(boolean), 114 predicates(list) 115 ]). 116 117:- record 118 walk_option(undefined:oneof([ignore,error,trace])=ignore, 119 autoload:boolean=true, 120 source:boolean=true, 121 module:atom, % Only analyse given module 122 module_class:oneof([default,user,system, 123 library,test,development])=default, 124 infer_meta_predicates:oneof([false,true,all])=true, 125 clauses:list, % Walk only these clauses 126 trace_reference:any=(-), 127 on_trace:callable, % Call-back on trace hits 128 new_meta_specs:callable, 129 reiterate:boolean=true, 130 predicates:list, 131 % private stuff 132 clause, % Processed clause 133 caller, % Head of the caller 134 initialization, % Initialization source 135 undecided, % Error to throw error 136 evaluate:boolean, % Do partial evaluation 137 call_kind, 138 is_transparent_meta_call:boolean=false). 139 140:- thread_local 141 multifile_predicate/3. % Name, Arity, Module
Options processed:
ignore
or
error
.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.
@compat OnTrace was called using Caller-Location in older versions.
212pdt_prolog_walk_code(Options) :- 213 meta_options(is_meta, Options, QOptions), 214 pdt_prolog_walk_code(1, QOptions). 215 216pdt_prolog_walk_code(Iteration, Options) :- 217 statistics(cputime, CPU0), 218 make_walk_option(Options, OTerm, _), 219 walk_option_call_kind(OTerm, call), 220 ( walk_option_clauses(OTerm, Clauses), 221 nonvar(Clauses) 222 -> walk_clauses(Clauses, OTerm) 223 ; walk_option_predicates(OTerm, Predicates), 224 nonvar(Predicates) 225 -> forall(( 226 member(Module:Name/Arity, Predicates), 227 functor(Head, Name, Arity), 228 \+ predicate_property(Module:Head, imported_from(_)) 229 ), ( 230 walk_called_by_pred(Module:Name/Arity, OTerm) 231 )) 232 ; forall(( walk_option_module(OTerm, M), 233 current_module(M), 234 scan_module(M, OTerm) 235 ), 236 find_walk_from_module(M, OTerm)) 237 ), 238 walk_from_multifile(OTerm), 239 walk_from_initialization(OTerm), 240 infer_new_meta_predicates(New, OTerm), 241 statistics(cputime, CPU1), 242 ( New \== [] 243 -> walk_option_new_meta_specs(OTerm, Closure), 244 ( callable(Closure) 245 -> call(Closure, New) 246 ; true 247 ), 248 CPU is CPU1-CPU0, 249 print_message(informational, 250 codewalk(reiterate(New, Iteration, CPU))), 251 walk_option_reiterate(OTerm, Reiterate), 252 ( Reiterate == true 253 -> succ(Iteration, Iteration2), 254 pdt_prolog_walk_code(Iteration2, Options) 255 ; true 256 ) 257 ; true 258 ). 259 260is_meta(on_trace).
266walk_clauses(Clauses, OTerm) :- 267 must_be(list, Clauses), 268 forall(member(ClauseRef, Clauses), 269 ( user:clause(CHead, Body, ClauseRef), 270 ( CHead = Module:Head 271 -> true 272 ; Module = user, 273 Head = CHead 274 ), 275 walk_option_clause(OTerm, ClauseRef), 276 walk_option_caller(OTerm, Module:Head), 277 walk_called_by_body(Body, Module, OTerm) 278 )). 279 280scan_module(M, OTerm) :- 281 walk_option_module_class(OTerm, Class), 282 Class \== default, !, 283 module_property(M, class(Class)). 284scan_module(M, _) :- 285 module_property(M, class(Class)), 286 scan_module_class(Class). 287 288scan_module_class(user). 289scan_module_class(library).
299:- if(current_prolog_flag(dialect, swi)). 300walk_from_initialization(OTerm) :- 301 walk_option_predicates(OTerm, Predicates), 302 var(Predicates), 303 walk_option_clauses(OTerm, Clauses), 304 var(Clauses), 305 !, 306 walk_option_caller(OTerm, '<initialization>'), 307 forall('$init_goal'(_File, Goal, SourceLocation), 308 ( walk_option_initialization(OTerm, SourceLocation), 309 walk_from_initialization(Goal, OTerm))). 310 311walk_from_initialization(_OTerm). 312 313walk_from_initialization(M:Goal, OTerm) :- 314 scan_module(M, OTerm), !, 315 walk_called_by_body(Goal, M, OTerm). 316walk_from_initialization(_, _). 317 318:- else. 319walk_from_initialization(_OTerm). 320:- endif.
327find_walk_from_module(M, OTerm) :- 328 debug(autoload, 'Analysing module ~q', [M]), 329 forall(predicate_in_module(M, PI), 330 walk_called_by_pred(M:PI, OTerm)). 331 332walk_called_by_pred(Module:Name/Arity, _) :- 333 multifile_predicate(Name, Arity, Module), !. 334walk_called_by_pred(Module:Name/Arity, _) :- 335 functor(Head, Name, Arity), 336 predicate_property(Module:Head, multifile), !, 337 assertz(multifile_predicate(Name, Arity, Module)). 338walk_called_by_pred(Module:Name/Arity, OTerm) :- 339 functor(Head, Name, Arity), 340 ( no_walk_property(Property), 341 predicate_property(Module:Head, Property) 342 -> true 343 ; walk_option_caller(OTerm, Module:Head), 344 walk_option_clause(OTerm, ClauseRef), 345 forall(catch(clause(Module:, Body, ClauseRef), _, fail), 346 walk_called_by_body(Body, Module, OTerm)) 347 ). 348 349no_walk_property(number_of_rules(0)). % no point walking only facts 350no_walk_property(foreign). % cannot walk foreign code
356walk_from_multifile(OTerm) :- 357 forall(retract(multifile_predicate(Name, Arity, Module)), 358 walk_called_by_multifile(Module:Name/Arity, OTerm)). 359 360walk_called_by_multifile(Module:Name/Arity, OTerm) :- 361 functor(Head, Name, Arity), 362 forall(catch(clause_not_from_development( 363 Module:Head, Body, ClauseRef, OTerm), 364 _, fail), 365 ( walk_option_clause(OTerm, ClauseRef), 366 walk_option_caller(OTerm, Module:Head), 367 walk_called_by_body(Body, Module, OTerm) 368 )).
376clause_not_from_development(Module:Head, Body, Ref, OTerm) :-
377 clause(Module:, Body, Ref),
378 \+ ( clause_property(Ref, file(File)),
379 module_property(LoadModule, file(File)),
380 \+ scan_module(LoadModule, OTerm)
381 ).
ignore
, error
391walk_called_by_body(True, _, _) :- 392 True == true, !. % quickly deal with facts 393walk_called_by_body(Body, Module, OTerm) :- 394% set_undecided_of_walk_option(error, OTerm, OTerm1), 395 set_evaluate_of_walk_option(false, OTerm, OTerm2), 396 catch(walk_called(Body, Module, _TermPos, OTerm2), 397 missing(Missing), 398 walk_called_by_body(Missing, Body, Module, OTerm)), !. 399walk_called_by_body(Body, Module, OTerm) :- 400 format(user_error, 'Failed to analyse:~n', []), 401 portray_clause(('<head>' :- Body)), 402 ( debugging(autoload(trace)) 403 -> trace, %gtrace, 404 walk_called_by_body(Body, Module, OTerm) 405 ; true 406 ).
413walk_called_by_body(Missing, Body, _, OTerm) :- 414 debugging(autoload), 415 format(user_error, 'Retrying due to ~w (~p)~n', [Missing, OTerm]), 416 portray_clause(('<head>' :- Body)), fail. 417walk_called_by_body(undecided_call, Body, Module, OTerm) :- 418 catch(forall(walk_called(Body, Module, _TermPos, OTerm), 419 true), 420 missing(Missing), 421 walk_called_by_body(Missing, Body, Module, OTerm)). 422walk_called_by_body(subterm_positions, Body, Module, OTerm) :- 423 ( ( walk_option_clause(OTerm, ClauseRef), nonvar(ClauseRef), 424 clause_info(ClauseRef, _, TermPos, _NameOffset), 425 TermPos = term_position(_,_,_,_,[_,BodyPos]) 426 -> WBody = Body 427 ; walk_option_initialization(OTerm, SrcLoc), 428 ground(SrcLoc), SrcLoc = _File:_Line, 429 initialization_layout(SrcLoc, Module:Body, WBody, BodyPos) 430 ) 431 -> catch(forall(walk_called(WBody, Module, BodyPos, OTerm), 432 true), 433 missing(subterm_positions), 434 walk_called_by_body(no_positions, Body, Module, OTerm)) 435 ; set_source_of_walk_option(false, OTerm, OTerm2), 436 forall(walk_called(Body, Module, _BodyPos, OTerm2), 437 true) 438 ). 439walk_called_by_body(no_positions, Body, Module, OTerm) :- 440 set_source_of_walk_option(false, OTerm, OTerm2), 441 forall(walk_called(Body, Module, _NoPos, OTerm2), 442 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.472walk_called(Var, _, TermPos, OTerm) :- 473 var(Var), !, % Incomplete analysis 474 undecided(Var, TermPos, OTerm). 475walk_called(M:G, M0, term_position(_,_,_,_,[MPos,Pos]), OTerm) :- !, 476 ( nonvar(M) 477 -> walk_called(G, M, Pos, OTerm) 478 ; ( nonvar(M0), 479 get_attr(M, codewalk, V), 480 V == is_context_module 481 -> walk_called(G, M0, Pos, OTerm) 482 ; undecided(M, MPos, OTerm) 483 ) 484 ). 485walk_called(_G, M, TermPos, OTerm) :- 486 var(M), 487 !, 488 undecided(M, TermPos, OTerm). 489walk_called((A,B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :- !, 490 walk_called(A, M, PA, OTerm), 491 walk_called(B, M, PB, OTerm). 492walk_called((A;B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :- !, 493 ( walk_option_evaluate(OTerm, Eval), Eval == true 494 -> Goal = (A;B), 495 setof(Goal, 496 ( walk_called(A, M, PA, OTerm) 497 ; walk_called(B, M, PB, OTerm) 498 ), 499 Alts0), 500 variants(Alts0, Alts), 501 member(Goal, Alts) 502 ; walk_called(A, M, PA, OTerm), 503 walk_called(B, M, PB, OTerm) 504 ). 505walk_called(Goal, Module, TermPos, OTerm) :- 506 walk_option_trace_reference(OTerm, To), To \== (-), 507 ( subsumes_term(To, Module:Goal) 508 -> M2 = Module 509 ; predicate_property(Module:Goal, imported_from(M2)), 510 subsumes_term(To, M2:Goal) 511 ), 512 print_reference(M2:Goal, TermPos, trace, OTerm), 513 fail. % Continue search 514walk_called(Goal, Module, _, OTerm) :- 515 evaluate(Goal, Module, OTerm), !. 516:- if(current_prolog_flag(dialect, swi)). 517walk_called(Goal, Module, TermPos, OTerm) :- 518 prolog:called_by(Goal, Called), 519 Called \== [], !, 520 walk_called_by(Called, Module, Goal, TermPos, OTerm). 521:- endif. 522walk_called(Meta, Module, term_position(_,_,_,_,ArgPosList), OTerm) :- 523 ( walk_option_autoload(OTerm, false) 524 -> nonvar(Module), 525 is_defined(Module:Meta) 526 ; true 527 ), 528 % PDT Extension: 529 ( extended_meta_predicate(Module:Meta, Head) 530 ; inferred_meta(Module:Meta, Head) 531 ; predicate_property(Module:Meta, meta_predicate(Head)) 532 ), !, 533 walk_option_clause(OTerm, ClauseRef), 534 register_possible_meta_clause(ClauseRef), 535 ( % PDT Extension: 536 walk_option_caller(OTerm, CallerModule:CallerGoal), 537 predicate_property(CallerModule:CallerGoal, transparent), 538 \+ predicate_property(CallerModule:CallerGoal, meta_predicate(_)), 539 \+ walk_option_is_transparent_meta_call(OTerm, true) 540 -> % PDT: Meta-call in module transparent predicates 541 set_is_transparent_meta_call_of_walk_option(true, OTerm, NewOTerm), 542 ( predicate_property(ImportingModule:CallerGoal, imported_from(CallerModule)), 543 walk_meta_call(1, Head, Meta, ImportingModule, ArgPosList, NewOTerm), 544 fail 545 ; walk_meta_call(1, Head, Meta, Module, ArgPosList, NewOTerm) 546 ) 547 ; % Meta-call in non-transparent predicate or 548 % nested meta-call in transparent predicate: 549 walk_meta_call(1, Head, Meta, Module, ArgPosList, OTerm) 550 ). 551walk_called(context_module(M), _, _, OTerm) :- 552 walk_option_caller(OTerm, Caller), 553 predicate_property(Caller, transparent), 554 \+ predicate_property(Caller, meta_predicate(_)), 555 !, 556 put_attr(M, codewalk, is_context_module). 557walk_called(Goal, Module, _, _) :- 558 nonvar(Module), 559 is_defined(Module:Goal), !. 560walk_called(Goal, Module, TermPos, OTerm) :- 561 callable(Goal), !, 562 undefined(Module:Goal, TermPos, OTerm). 563walk_called(Goal, _Module, TermPos, OTerm) :- 564 not_callable(Goal, TermPos, OTerm). 565 566:- if(current_prolog_flag(dialect, swi)). 567is_defined(Module:Goal) :- 568 '$get_predicate_attribute'(Module:Goal, defined, 1). 569:- else. 570is_defined(Module:Goal) :- 571 functor(Goal,N,A), 572 current_predicate(Module:N/A), 573 !. 574:- endif.
578undecided(Var, TermPos, OTerm) :- 579 walk_option_undecided(OTerm, Undecided), 580 ( var(Undecided) 581 -> Action = ignore 582 ; Action = Undecided 583 ), 584 undecided(Action, Var, TermPos, OTerm). 585 586undecided(ignore, _, _, _) :- !. 587undecided(error, _, _, _) :- 588 throw(missing(undecided_call)).
592evaluate(Goal, Module, OTerm) :- 593 walk_option_evaluate(OTerm, Evaluate), 594 Evaluate \== false, 595 evaluate(Goal, Module). 596 597evaluate(A=B, _) :- 598 unify_with_occurs_check(A, B).
604undefined(_, _, OTerm) :- 605 walk_option_undefined(OTerm, ignore), 606 !. 607%undefined(_, _, OTerm) :- 608% walk_option_is_transparent_meta_call(OTerm, true), 609% !. 610undefined(Goal, _, _) :- 611 predicate_property(Goal, autoload(_)), !. 612undefined(Goal, TermPos, OTerm) :- 613 ( walk_option_undefined(OTerm, trace) 614 -> Why = trace 615 ; Why = undefined 616 ), 617 walk_option_call_kind(OTerm, CallKind), 618 set_call_kind_of_walk_option(undefined(CallKind), OTerm, NewOTerm), 619 print_reference(Goal, TermPos, Why, NewOTerm).
625not_callable(Goal, TermPos, OTerm) :-
626 print_reference(Goal, TermPos, not_callable, OTerm).
635print_reference(Goal, TermPos, Why, OTerm) :- 636 walk_option_clause(OTerm, Clause), nonvar(Clause), !, 637 ( compound(TermPos), 638 arg(1, TermPos, CharCount), 639 integer(CharCount) % test it is valid 640 -> From = clause_term_position(Clause, TermPos) 641 ; walk_option_source(OTerm, false) 642 -> From = clause(Clause) 643 ; throw(missing(subterm_positions)) 644 ), 645 print_reference2(Goal, From, Why, OTerm). 646print_reference(Goal, TermPos, Why, OTerm) :- 647 walk_option_initialization(OTerm, Init), nonvar(Init), 648 Init = File:Line, !, 649 ( compound(TermPos), 650 arg(1, TermPos, CharCount), 651 integer(CharCount) % test it is valid 652 -> From = file_term_position(File, TermPos) 653 ; walk_option_source(OTerm, false) 654 -> From = file(File, Line, -1, _) 655 ; throw(missing(subterm_positions)) 656 ), 657 print_reference2(Goal, From, Why, OTerm). 658print_reference(Goal, _, Why, OTerm) :- 659 print_reference2(Goal, _, Why, OTerm). 660 661print_reference2(Goal, From, trace, OTerm) :- 662 walk_option_on_trace(OTerm, Closure), 663 walk_option_caller(OTerm, Caller), 664 walk_option_call_kind(OTerm, CallKind), 665 nonvar(Closure), 666 call(Closure, Goal, Caller, From, CallKind), !. 667print_reference2(Goal, From, Why, _OTerm) :- 668 make_message(Why, Goal, From, Message, Level), 669 print_message(Level, Message). 670 671 672make_message(undefined, Goal, Context, 673 error(existence_error(procedure, PI), Context), error) :- 674 goal_pi(Goal, PI). 675make_message(not_callable, Goal, Context, 676 error(type_error(callable, Goal), Context), error). 677make_message(trace, Goal, Context, 678 trace_call_to(PI, Context), informational) :- 679 goal_pi(Goal, PI). 680 681 682goal_pi(Goal, M:Name/Arity) :- 683 strip_module(Goal, M, Head), 684 callable(Head), !, 685 functor(Head, Name, Arity). 686goal_pi(Goal, Goal). 687 688:- dynamic 689 possible_meta_predicate/2.
698register_possible_meta_clause(ClausesRef) :- 699 nonvar(ClausesRef), 700 clause_property(ClausesRef, predicate(PI)), 701 pi_head(PI, Head, Module), 702 module_property(Module, class(user)), 703 \+ predicate_property(Module:Head, meta_predicate(_)), 704% \+ inferred_meta(Module:Head, _), 705 \+ possible_meta_predicate(Head, Module), !, 706 assertz(possible_meta_predicate(Head, Module)). 707register_possible_meta_clause(_). 708 709pi_head(Module:Name/Arity, Head, Module) :- !, 710 functor(Head, Name, Arity). 711pi_head(_, _, _) :- 712 assertion(fail).
716infer_new_meta_predicates([], OTerm) :- 717 walk_option_infer_meta_predicates(OTerm, false), !. 718infer_new_meta_predicates(MetaSpecs, _OTerm) :- 719 findall(Module:MetaSpec, 720 ( retract(possible_meta_predicate(Head, Module)), 721 infer_meta(Module:Head, MetaSpec, NewOrUpdated), 722 NewOrUpdated == true 723 ), 724 MetaSpecs).
731calling_metaspec(Head) :- 732 arg(_, Head, Arg), 733 calling_metaarg(Arg), !. 734 735calling_metaarg(I) :- integer(I), !. 736calling_metaarg(^). 737calling_metaarg(//). 738calling_metaarg(database).
747walk_meta_call(I, assert(_), Meta, M, [ArgPos|_], OTerm) :- 748 !, 749 walk_database_arg(I, Meta, M, ArgPos, OTerm). 750walk_meta_call(I, asserta(_), Meta, M, [ArgPos|_], OTerm) :- 751 !, 752 walk_database_arg(I, Meta, M, ArgPos, OTerm). 753walk_meta_call(I, assertz(_), Meta, M, [ArgPos|_], OTerm) :- 754 !, 755 walk_database_arg(I, Meta, M, ArgPos, OTerm). 756walk_meta_call(I, retract(_), Meta, M, [ArgPos|_], OTerm) :- 757 !, 758 walk_database_arg(I, Meta, M, ArgPos, OTerm). 759walk_meta_call(I, retractall(_), Meta, M, [ArgPos|_], OTerm) :- 760 !, 761 walk_database_arg(I, Meta, M, ArgPos, OTerm). 762walk_meta_call(I, Head, Meta, M, [ArgPos|ArgPosList], OTerm) :- 763 arg(I, Head, AS), !, 764 walk_meta_call_arg(AS, I, Meta, M, ArgPos, OTerm), 765 succ(I, I2), 766 walk_meta_call(I2, Head, Meta, M, ArgPosList, OTerm). 767walk_meta_call(_, _, _, _, _, _). 768 769walk_database_arg(I, Meta, _M, _ArgPos, _OTerm) :- 770 arg(I, Meta, Arg), 771 nonvar(Arg), 772 Arg = (_ :- _), 773 !. 774walk_database_arg(I, Meta, M, ArgPos, OTerm) :- 775 walk_meta_call_arg(database, I, Meta, M, ArgPos, OTerm). 776walk_meta_call_arg([], _I, _Meta, _M, _ArgPos, _OTerm) :- 777 !. 778walk_meta_call_arg([ArgSpec|ArgSpecs], I, Meta, M, ArgPos, OTerm) :- 779 !, 780 walk_meta_call_arg(ArgSpec, I, Meta, M, ArgPos, OTerm), 781 walk_meta_call_arg(ArgSpecs, I, Meta, M, ArgPos, OTerm). 782walk_meta_call_arg(AS, I, Meta, M, ArgPos, OTerm) :- 783 ( integer(AS) 784 -> arg(I, Meta, MA), 785 extend(MA, AS, Goal, ArgPos, ArgPosEx, OTerm), 786 set_call_kind_of_walk_option(metacall(Meta, I), OTerm, NewOTerm), 787 walk_called(Goal, M, ArgPosEx, NewOTerm) 788 ; AS == (^) 789 -> arg(I, Meta, MA), 790 remove_quantifier(MA, Goal, ArgPos, ArgPosEx, M, MG, OTerm), 791 set_call_kind_of_walk_option(metacall(Meta, I), OTerm, NewOTerm), 792 walk_called(Goal, MG, ArgPosEx, NewOTerm) 793 ; AS == (//) 794 -> arg(I, Meta, DCG), 795 set_call_kind_of_walk_option(metacall(Meta, I), OTerm, NewOTerm), 796 walk_dcg_body(DCG, M, ArgPos, NewOTerm) 797 ; AS == database 798 -> arg(I, Meta, MA), 799 set_call_kind_of_walk_option(database(Meta, I), OTerm, NewOTerm), 800 walk_called(MA, M, ArgPos, NewOTerm) 801 ; arg(I, Meta, Arg), 802 atomic(Arg), 803 ( AS = has_arity(_,_) 804 ; AS = add_prefix(_,_) 805 ; AS = add_suffix(_,_) 806 ) 807 -> ( functor_arity_for(AS, Arg, Functor, Arity) 808 -> extend(Functor, Arity, Goal, ArgPos, ArgPosEx, OTerm), 809 set_call_kind_of_walk_option(metacall(Meta, I, AS), OTerm, NewOTerm), 810 walk_called(Goal, M, ArgPosEx, NewOTerm) 811 ; true 812 ) 813 ; true 814 ). 815 816functor_arity_for(I, _, _, I) :- 817 integer(I), 818 !. 819functor_arity_for(^, _, _, 0). 820functor_arity_for(database, _, _, 0). 821functor_arity_for(has_arity(N, Spec), Arg, Arg, Arity) :- 822 functor_arity_for(Spec, _, _, Arity0), 823 Arity is N + Arity0. 824functor_arity_for(add_prefix(Prefix, Spec), Arg, Functor, Arity) :- 825 functor_arity_for(Spec, _, _, Arity), 826 atom_concat(Prefix, Arg, Functor). 827functor_arity_for(add_suffix(Suffix, Spec), Arg, Functor, Arity) :- 828 functor_arity_for(Spec, _, _, Arity), 829 atom_concat(Arg, Suffix, Functor). 830 831remove_quantifier(Goal, _, TermPos, TermPos, M, M, OTerm) :- 832 var(Goal), !, 833 undecided(Goal, TermPos, OTerm). 834remove_quantifier(_^Goal0, Goal, 835 term_position(_,_,_,_,[_,GPos]), 836 TermPos, M0, M, OTerm) :- !, 837 remove_quantifier(Goal0, Goal, GPos, TermPos, M0, M, OTerm). 838remove_quantifier(M1:Goal0, Goal, 839 term_position(_,_,_,_,[_,GPos]), 840 TermPos, _, M, OTerm) :- !, 841 remove_quantifier(Goal0, Goal, GPos, TermPos, M1, M, OTerm). 842remove_quantifier(Goal, Goal, TermPos, TermPos, M, M, _). 843 844:- if(current_prolog_flag(dialect, swi)).
850walk_called_by([], _, _, _, _). 851walk_called_by([H|T], M, Goal, TermPos, OTerm) :- 852 ( H = G+N 853 -> subterm_pos(G, Goal, TermPos, GPos), 854 ( extend(G, N, G2, GPos, GPosEx, OTerm) 855 -> walk_called(G2, M, GPosEx, OTerm) 856 ; true 857 ) 858 ; subterm_pos(H, Goal, TermPos, GPos), 859 walk_called(H, M, GPos, OTerm) 860 ), 861 walk_called_by(T, M, Goal, TermPos, OTerm). 862:- endif. 863 864:- if(current_prolog_flag(dialect, swi)). 865subterm_pos(Sub, Term, TermPos, SubTermPos) :- 866 subterm_pos(Sub, Term, same_term, TermPos, SubTermPos), !. 867:- endif. 868subterm_pos(Sub, Term, TermPos, SubTermPos) :- 869 subterm_pos(Sub, Term, ==, TermPos, SubTermPos), !. 870subterm_pos(Sub, Term, TermPos, SubTermPos) :- 871 subterm_pos(Sub, Term, =@=, TermPos, SubTermPos), !. 872subterm_pos(Sub, Term, TermPos, SubTermPos) :- 873 subterm_pos(Sub, Term, =, TermPos, SubTermPos), !. 874subterm_pos(_, _, _, _).
881walk_dcg_body(Var, _Module, TermPos, OTerm) :- 882 var(Var), !, 883 undecided(Var, TermPos, OTerm). 884walk_dcg_body([], _Module, _, _) :- !. 885walk_dcg_body([_|_], _Module, _, _) :- !. 886walk_dcg_body(!, _Module, _, _) :- !. 887walk_dcg_body(M:G, _, term_position(_,_,_,_,[MPos,Pos]), OTerm) :- !, 888 ( nonvar(M) 889 -> walk_dcg_body(G, M, Pos, OTerm) 890 ; undecided(M, MPos, OTerm) 891 ). 892walk_dcg_body((A,B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :- !, 893 walk_dcg_body(A, M, PA, OTerm), 894 walk_dcg_body(B, M, PB, OTerm). 895walk_dcg_body((A->B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :- !, 896 walk_dcg_body(A, M, PA, OTerm), 897 walk_dcg_body(B, M, PB, OTerm). 898walk_dcg_body((A*->B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :- !, 899 walk_dcg_body(A, M, PA, OTerm), 900 walk_dcg_body(B, M, PB, OTerm). 901walk_dcg_body((A;B), M, term_position(_,_,_,_,[PA,PB]), OTerm) :- !, 902 ( walk_dcg_body(A, M, PA, OTerm) 903 ; walk_dcg_body(B, M, PB, OTerm) 904 ). 905walk_dcg_body(G, M, TermPos, OTerm) :- 906 extend(G, 2, G2, TermPos, TermPosEx, OTerm), 907 walk_called(G2, M, TermPosEx, OTerm).
same_term
, ==
, =@=
or =
918:- meta_predicate 919 subterm_pos( , , , , ), 920 sublist_pos( , , , , , ). 921 922subterm_pos(_, _, _, Pos, _) :- 923 var(Pos), !, fail. 924subterm_pos(Sub, Term, Cmp, Pos, Pos) :- 925 call(Cmp, Sub, Term), !. 926subterm_pos(Sub, Term, Cmp, term_position(_,_,_,_,ArgPosList), Pos) :- 927 nth1(I, ArgPosList, ArgPos), 928 arg(I, Term, Arg), 929 subterm_pos(Sub, Arg, Cmp, ArgPos, Pos). 930subterm_pos(Sub, Term, Cmp, list_position(_,_,ElemPosList,TailPos), Pos) :- 931 sublist_pos(ElemPosList, TailPos, Sub, Term, Cmp, Pos). 932subterm_pos(Sub, {Arg}, Cmp, brace_term_position(_,_,ArgPos), Pos) :- 933 subterm_pos(Sub, Arg, Cmp, ArgPos, Pos). 934 935sublist_pos([EP|TP], TailPos, Sub, [H|T], Cmp, Pos) :- 936 ( subterm_pos(Sub, H, Cmp, EP, Pos) 937 ; sublist_pos(TP, TailPos, Sub, T, Cmp, Pos) 938 ). 939sublist_pos([], TailPos, Sub, Tail, Cmp, Pos) :- 940 TailPos \== none, 941 subterm_pos(Sub, Tail, Cmp, TailPos, Pos).
947extend(Goal, 0, Goal, TermPos, TermPos, _) :- !. 948extend(Goal, _, _, TermPos, TermPos, OTerm) :- 949 var(Goal), !, 950 undecided(Goal, TermPos, OTerm). 951extend(M:Goal, N, M:GoalEx, 952 term_position(F,T,FT,TT,[MPos,GPosIn]), 953 term_position(F,T,FT,TT,[MPos,GPosOut]), OTerm) :- !, 954 ( var(M) 955 -> undecided(N, MPos, OTerm) 956 ; true 957 ), 958 extend(Goal, N, GoalEx, GPosIn, GPosOut, OTerm). 959extend(Goal, N, GoalEx, TermPosIn, TermPosOut, _) :- 960 callable(Goal), 961 Goal =.. List, 962 length(Extra, N), 963 extend_term_pos(TermPosIn, N, TermPosOut), 964 append(List, Extra, ListEx), 965 GoalEx =.. ListEx. 966 967extend_term_pos(Var, _, _) :- 968 var(Var), !. 969extend_term_pos(term_position(F,T,FT,TT,ArgPosIn), 970 N, 971 term_position(F,T,FT,TT,ArgPosOut)) :- !, 972 length(Extra, N), 973 maplist(=(0-0), Extra), 974 append(ArgPosIn, Extra, ArgPosOut). 975extend_term_pos(F-T, N, term_position(F,T,F,T,Extra)) :- 976 length(Extra, N), 977 maplist(=(0-0), Extra).
982variants([], []). 983variants([H|T], List) :- 984 variants(T, H, List). 985 986variants([], H, [H]). 987variants([H|T], V, List) :- 988 ( H =@= V 989 -> variants(T, V, List) 990 ; List = [V|List2], 991 variants(T, H, List2) 992 ).
998predicate_in_module(Module, PI) :- 999 current_predicate(Module:PI), 1000 PI = Name/Arity, 1001 functor(Head, Name, Arity), 1002 \+ predicate_property(Module:Head, imported_from(_)). 1003 1004 1005 /******************************* 1006 * MESSAGES * 1007 *******************************/ 1008 1009:- multifile 1010 prolog:message//1, 1011 prolog:message_location//1. 1012 1013prologmessage(trace_call_to(PI, Context)) --> 1014 [ 'Call to ~q at '-[PI] ], 1015 prolog:message_location(Context). 1016 1017prologmessage_location(clause_term_position(ClauseRef, TermPos)) --> 1018 { clause_property(ClauseRef, file(File)) }, 1019 message_location_file_term_position(File, TermPos). 1020prologmessage_location(clause(ClauseRef)) --> 1021 { clause_property(ClauseRef, file(File)), 1022 clause_property(ClauseRef, line_count(Line)) 1023 }, !, 1024 [ '~w:~d: '-[File, Line] ]. 1025prologmessage_location(clause(ClauseRef)) --> 1026 { clause_name(ClauseRef, Name) }, 1027 [ '~w: '-[Name] ]. 1028prologmessage_location(file_term_position(Path, TermPos)) --> 1029 message_location_file_term_position(Path, TermPos). 1030prologmessage_location(file(Path, Line, _, _)) --> 1031 [ '~w:~d: '-[Path, Line] ]. 1032prologmessage(codewalk(reiterate(New, Iteration, CPU))) --> 1033 [ 'Found new meta-predicates in iteration ~w (~3f sec)'- 1034 [Iteration, CPU], nl ], 1035 meta_decls(New), 1036 [ 'Restarting analysis ...'-[], nl ]. 1037 1038meta_decls([]) --> []. 1039meta_decls([H|T]) --> 1040 [ ':- meta_predicate ~q.'-[H], nl ], 1041 meta_decls(T). 1042 1043message_location_file_term_position(File, TermPos) --> 1044 { arg(1, TermPos, CharCount), 1045 filepos_line(File, CharCount, Line, LinePos) 1046 }, 1047 [ '~w:~d:~d: '-[File, Line, LinePos] ].
1054filepos_line(File, CharPos, Line, LinePos) :-
1055 setup_call_cleanup(
1056 ( open(File, read, In),
1057 open_null_stream(Out)
1058 ),
1059 ( copy_stream_data(In, Out, CharPos),
1060 stream_property(In, position(Pos)),
1061 stream_position_data(line_count, Pos, Line),
1062 stream_position_data(line_position, Pos, LinePos)
1063 ),
1064 ( close(Out),
1065 close(In)
1066 ))
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.*/