View source with raw comments or as raw
    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)  2005-2023, University of Amsterdam
    7                              VU University Amsterdam
    8                              CWI, Amsterdam
    9                              SWI-Prolog Solutions b.v.
   10    All rights reserved.
   11
   12    Redistribution and use in source and binary forms, with or without
   13    modification, are permitted provided that the following conditions
   14    are met:
   15
   16    1. Redistributions of source code must retain the above copyright
   17       notice, this list of conditions and the following disclaimer.
   18
   19    2. Redistributions in binary form must reproduce the above copyright
   20       notice, this list of conditions and the following disclaimer in
   21       the documentation and/or other materials provided with the
   22       distribution.
   23
   24    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   25    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   26    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   27    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   28    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   29    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   30    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   31    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   32    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   33    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   34    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   35    POSSIBILITY OF SUCH DAMAGE.
   36*/
   37
   38:- module(prolog_clause,
   39          [ clause_info/4,              % +ClauseRef, -File, -TermPos, -VarNames
   40            clause_info/5,              % +ClauseRef, -File, -TermPos, -VarNames,
   41                                        % +Options
   42            initialization_layout/4,    % +SourceLoc, +Goal, -Term, -TermPos
   43            predicate_name/2,           % +Head, -Name
   44            clause_name/2               % +ClauseRef, -Name
   45          ]).   46:- use_module(library(debug),[debugging/1,debug/3]).   47:- autoload(library(listing),[portray_clause/1]).   48:- autoload(library(lists),[append/3]).   49:- autoload(library(occurs),[sub_term/2]).   50:- autoload(library(option),[option/3]).   51:- autoload(library(prolog_source),[read_source_term_at_location/3]).   52
   53
   54:- public                               % called from library(trace/clause)
   55    unify_term/2,
   56    make_varnames/5,
   57    do_make_varnames/3.   58
   59:- multifile
   60    unify_goal/5,                   % +Read, +Decomp, +M, +Pos, -Pos
   61    unify_clause_hook/5,
   62    make_varnames_hook/5,
   63    open_source/2.                  % +Input, -Stream
   64
   65:- predicate_options(prolog_clause:clause_info/5, 5,
   66                     [ head(-any),
   67                       body(-any),
   68                       variable_names(-list)
   69                     ]).

Get detailed source-information about a clause

This module started life as part of the GUI tracer. As it is generally useful for debugging purposes it has moved to the general Prolog library.

The tracer library library(trace/clause) adds caching and dealing with dynamic predicates using listing to XPCE objects to this. Note that clause_info/4 as below can be slow. */

 clause_info(+ClauseRef, -File, -TermPos, -VarOffsets) is semidet
 clause_info(+ClauseRef, -File, -TermPos, -VarOffsets, +Options) is semidet
Fetches source information for the given clause. File is the file from which the clause was loaded. TermPos describes the source layout in a format compatible to the subterm_positions option of read_term/2. VarOffsets provides access to the variable allocation in a stack-frame. See make_varnames/5 for details.

Note that positions are character positions, i.e., not bytes. Line endings count as a single character, regardless of whether the actual ending is \n or =|\r\n|_.

Defined options are:

variable_names(-Names)
Unify Names with the variable names list (Name=Var) as returned by read_term/3. This argument is intended for reporting source locations and refactoring based on analysis of the compiled code.
head(-Head)
body(-Body)
Get the head and body as terms. This is similar to clause/3, but a seperate call would break the variable identity.
  109clause_info(ClauseRef, File, TermPos, NameOffset) :-
  110    clause_info(ClauseRef, File, TermPos, NameOffset, []).
  111
  112clause_info(ClauseRef, File, TermPos, NameOffset, Options) :-
  113    (   debugging(clause_info)
  114    ->  clause_name(ClauseRef, Name),
  115        debug(clause_info, 'clause_info(~w) (~w)... ',
  116              [ClauseRef, Name])
  117    ;   true
  118    ),
  119    clause_property(ClauseRef, file(File)),
  120    File \== user,                  % loaded using ?- [user].
  121    '$clause'(Head0, Body, ClauseRef, VarOffset),
  122    option(head(Head0), Options, _),
  123    option(body(Body), Options, _),
  124    (   module_property(Module, file(File))
  125    ->  true
  126    ;   strip_module(user:Head0, Module, _)
  127    ),
  128    unqualify(Head0, Module, Head),
  129    (   Body == true
  130    ->  DecompiledClause = Head
  131    ;   DecompiledClause = (Head :- Body)
  132    ),
  133    clause_property(ClauseRef, line_count(LineNo)),
  134    debug(clause_info, 'from ~w:~d ... ', [File, LineNo]),
  135    read_term_at_line(File, LineNo, Module, Clause, TermPos0, VarNames),
  136    option(variable_names(VarNames), Options, _),
  137    debug(clause_info, 'read ...', []),
  138    unify_clause(Clause, DecompiledClause, Module, TermPos0, TermPos),
  139    debug(clause_info, 'unified ...', []),
  140    make_varnames(Clause, DecompiledClause, VarOffset, VarNames, NameOffset),
  141    debug(clause_info, 'got names~n', []),
  142    !.
  143
  144unqualify(Module:Head, Module, Head) :-
  145    !.
  146unqualify(Head, _, Head).
 unify_term(+T1, +T2)
Unify the two terms, where T2 is created by writing the term and reading it back in, but be aware that rounding problems may cause floating point numbers not to unify. Also, if the initial term has a string object, it is written as "..." and read as a code-list. We compensate for that.

NOTE: Called directly from library(trace/clause) for the GUI tracer.

  160unify_term(X, X) :- !.
  161unify_term(X1, X2) :-
  162    compound(X1),
  163    compound(X2),
  164    functor(X1, F, Arity),
  165    functor(X2, F, Arity),
  166    !,
  167    unify_args(0, Arity, X1, X2).
  168unify_term(X, Y) :-
  169    float(X), float(Y),
  170    !.
  171unify_term(X, '$BLOB'(_)) :-
  172    blob(X, _),
  173    \+ atom(X).
  174unify_term(X, Y) :-
  175    string(X),
  176    is_list(Y),
  177    string_codes(X, Y),
  178    !.
  179unify_term(_, Y) :-
  180    Y == '...',
  181    !.                          % elipses left by max_depth
  182unify_term(_:X, Y) :-
  183    unify_term(X, Y),
  184    !.
  185unify_term(X, _:Y) :-
  186    unify_term(X, Y),
  187    !.
  188unify_term(X, Y) :-
  189    format('[INTERNAL ERROR: Diff:~n'),
  190    portray_clause(X),
  191    format('~N*** <->~n'),
  192    portray_clause(Y),
  193    break.
  194
  195unify_args(N, N, _, _) :- !.
  196unify_args(I, Arity, T1, T2) :-
  197    A is I + 1,
  198    arg(A, T1, A1),
  199    arg(A, T2, A2),
  200    unify_term(A1, A2),
  201    unify_args(A, Arity, T1, T2).
 read_term_at_line(+File, +Line, +Module, -Clause, -TermPos, -VarNames) is semidet
Read a term from File at Line.
  209read_term_at_line(File, Line, Module, Clause, TermPos, VarNames) :-
  210    setup_call_cleanup(
  211        '$push_input_context'(clause_info),
  212        read_term_at_line_2(File, Line, Module, Clause, TermPos, VarNames),
  213        '$pop_input_context').
  214
  215read_term_at_line_2(File, Line, Module, Clause, TermPos, VarNames) :-
  216    catch(try_open_source(File, In), error(_,_), fail),
  217    set_stream(In, newline(detect)),
  218    call_cleanup(
  219        read_source_term_at_location(
  220            In, Clause,
  221            [ line(Line),
  222              module(Module),
  223              subterm_positions(TermPos),
  224              variable_names(VarNames)
  225            ]),
  226        close(In)).
 open_source(+File, -Stream) is semidet
Hook into clause_info/5 that opens the stream holding the source for a specific clause. Thus, the query must succeed. The default implementation calls open/3 on the File property.
clause_property(ClauseRef, file(File)),
prolog_clause:open_source(File, Stream)
  239:- public try_open_source/2.            % used by library(prolog_breakpoints).
  240
  241try_open_source(File, In) :-
  242    open_source(File, In),
  243    !.
  244try_open_source(File, In) :-
  245    open(File, read, In, [reposition(true)]).
 make_varnames(+ReadClause, +DecompiledClause, +Offsets, +Names, -Term) is det
Create a Term varnames(...) where each argument contains the name of the variable at that offset. If the read Clause is a DCG rule, name the two last arguments <DCG_list> and <DCG_tail>

This predicate calles the multifile predicate make_varnames_hook/5 with the same arguments to allow for user extensions. Extending this predicate is needed if a compiler adds additional arguments to the clause head that must be made visible in the GUI tracer.

Arguments:
Offsets- List of Offset=Var
Names- List of Name=Var
  264make_varnames(ReadClause, DecompiledClause, Offsets, Names, Term) :-
  265    make_varnames_hook(ReadClause, DecompiledClause, Offsets, Names, Term),
  266    !.
  267make_varnames((Head --> _Body), _, Offsets, Names, Bindings) :-
  268    !,
  269    functor(Head, _, Arity),
  270    In is Arity,
  271    memberchk(In=IVar, Offsets),
  272    Names1 = ['<DCG_list>'=IVar|Names],
  273    Out is Arity + 1,
  274    memberchk(Out=OVar, Offsets),
  275    Names2 = ['<DCG_tail>'=OVar|Names1],
  276    make_varnames(xx, xx, Offsets, Names2, Bindings).
  277make_varnames(_, _, Offsets, Names, Bindings) :-
  278    length(Offsets, L),
  279    functor(Bindings, varnames, L),
  280    do_make_varnames(Offsets, Names, Bindings).
  281
  282do_make_varnames([], _, _).
  283do_make_varnames([N=Var|TO], Names, Bindings) :-
  284    (   find_varname(Var, Names, Name)
  285    ->  true
  286    ;   Name = '_'
  287    ),
  288    AN is N + 1,
  289    arg(AN, Bindings, Name),
  290    do_make_varnames(TO, Names, Bindings).
  291
  292find_varname(Var, [Name = TheVar|_], Name) :-
  293    Var == TheVar,
  294    !.
  295find_varname(Var, [_|T], Name) :-
  296    find_varname(Var, T, Name).
 unify_clause(+Read, +Decompiled, +Module, +ReadTermPos, -RecompiledTermPos)
What you read isn't always what goes into the database. The task of this predicate is to establish the relation between the term read from the file and the result from decompiling the clause.

This predicate calls the multifile predicate unify_clause_hook/5 with the same arguments to support user extensions.

Arguments:
Module- is the source module that was active when loading this clause, which is the same as prolog_load_context/2 using the module context. If this cannot be established it is the module to which the clause itself is associated. The argument may be used to determine whether or not a specific user transformation is in scope. See also term_expansion/2,4 and goal_expansion/2,4.
To be done
- This really must be more flexible, dealing with much more complex source-translations, falling back to a heuristic method locating as much as possible.
  319unify_clause(Read, _, _, _, _) :-
  320    var(Read),
  321    !,
  322    fail.
  323unify_clause((RHead :- RBody), (CHead :- CBody), Module, TermPos1, TermPos) :-
  324    '$expand':f2_pos(TermPos1, HPos, BPos1,
  325                     TermPos2, HPos, BPos2),
  326    inlined_unification(RBody, CBody, RBody1, CBody1, RHead,
  327                        BPos1, BPos2),
  328    RBody1 \== RBody,
  329    !,
  330    unify_clause2((RHead :- RBody1), (CHead :- CBody1), Module,
  331                  TermPos2, TermPos).
  332unify_clause(Read, Decompiled, _, TermPos, TermPos) :-
  333    Read =@= Decompiled,
  334    !,
  335    Read = Decompiled.
  336unify_clause(Read, Decompiled, Module, TermPos0, TermPos) :-
  337    unify_clause_hook(Read, Decompiled, Module, TermPos0, TermPos),
  338    !.
  339                                        % XPCE send-methods
  340unify_clause(:->(Head, Body), (PlHead :- PlBody), M, TermPos0, TermPos) :-
  341    !,
  342    pce_method_clause(Head, Body, PlHead, PlBody, M, TermPos0, TermPos).
  343                                        % XPCE get-methods
  344unify_clause(:<-(Head, Body), (PlHead :- PlBody), M, TermPos0, TermPos) :-
  345    !,
  346    pce_method_clause(Head, Body, PlHead, PlBody, M, TermPos0, TermPos).
  347                                        % Unit test clauses
  348unify_clause((TH :- RBody), (CH :- !, CBody), Module, TP0, TP) :-
  349    plunit_source_head(TH),
  350    plunit_compiled_head(CH),
  351    !,
  352    TP0 = term_position(F,T,FF,FT,[HP,BP0]),
  353    ubody(RBody, CBody, Module, BP0, BP),
  354    TP  = term_position(F,T,FF,FT,[HP,term_position(0,0,0,0,[FF-FT,BP])]).
  355                                        % module:head :- body
  356unify_clause((Head :- Read),
  357             (Head :- _M:Compiled), Module, TermPos0, TermPos) :-
  358    unify_clause2((Head :- Read), (Head :- Compiled), Module, TermPos0, TermPos1),
  359    TermPos1 = term_position(TA,TZ,FA,FZ,[PH,PB]),
  360    TermPos  = term_position(TA,TZ,FA,FZ,
  361                             [ PH,
  362                               term_position(0,0,0,0,[0-0,PB])
  363                             ]).
  364                                        % DCG rules
  365unify_clause(Read, Compiled1, Module, TermPos0, TermPos) :-
  366    Read = (_ --> Terminal, _),
  367    is_list(Terminal),
  368    ci_expand(Read, Compiled2, Module, TermPos0, TermPos1),
  369    Compiled2 = (DH :- _),
  370    functor(DH, _, Arity),
  371    DArg is Arity - 1,
  372    append(Terminal, _Tail, List),
  373    arg(DArg, DH, List),
  374    TermPos1 = term_position(F,T,FF,FT,[ HP,
  375                                         term_position(_,_,_,_,[_,BP])
  376                                       ]),
  377    !,
  378    TermPos2 = term_position(F,T,FF,FT,[ HP, BP ]),
  379    match_module(Compiled2, Compiled1, Module, TermPos2, TermPos).
  380                                               % SSU rules
  381unify_clause((Head,RCond => Body), (CHead :- CCondAndBody), Module,
  382             term_position(F,T,FF,FT,
  383                           [ term_position(_,_,_,_,[HP,CP]),
  384                             BP
  385                           ]),
  386             TermPos) :-
  387    split_on_cut(CCondAndBody, CCond, CBody0),
  388    !,
  389    inlined_unification(RCond, CCond, RCond1, CCond1, Head, CP, CP1),
  390    TermPos1 = term_position(F,T,FF,FT, [HP, BP1]),
  391    BP2 = term_position(_,_,_,_, [FF-FT, BP]), % Represent (!, Body), placing
  392    (   CCond1 == true                         % ! at =>
  393    ->  BP1 = BP2,                             % Whole guard is inlined
  394        unify_clause2((Head :- !, Body), (CHead :- !, CBody0),
  395                      Module, TermPos1, TermPos)
  396    ;   mkconj_pos(RCond1, CP1, (!,Body), BP2, RBody, BP1),
  397        mkconj_npos(CCond1, (!,CBody0), CBody),
  398        unify_clause2((Head :- RBody), (CHead :- CBody),
  399                      Module, TermPos1, TermPos)
  400    ).
  401unify_clause((Head => Body), Compiled1, Module, TermPos0, TermPos) :-
  402    !,
  403    unify_clause2((Head :- Body), Compiled1, Module, TermPos0, TermPos).
  404unify_clause(Read, Decompiled, Module, TermPos0, TermPos) :-
  405    unify_clause2(Read, Decompiled, Module, TermPos0, TermPos).
  406
  407% mkconj, but also unify position info
  408mkconj_pos((A,B), term_position(F,T,FF,FT,[PA,PB]), Ex, ExPos, Code, Pos) =>
  409    Code = (A,B1),
  410    Pos = term_position(F,T,FF,FT,[PA,PB1]),
  411    mkconj_pos(B, PB, Ex, ExPos, B1, PB1).
  412mkconj_pos(Last, LastPos, Ex, ExPos, Code, Pos) =>
  413    Code = (Last,Ex),
  414    Pos = term_position(_,_,_,_,[LastPos,ExPos]).
  415
  416% similar to mkconj, but we should __not__ optimize `true` away.
  417mkconj_npos((A,B), Ex, Code) =>
  418    Code = (A,B1),
  419    mkconj_npos(B, Ex, B1).
  420mkconj_npos(A, Ex, Code) =>
  421    Code = (A,Ex).
 unify_clause2(+Read, +Decompiled, +Module, +TermPosIn, -TermPosOut)
Stratified version to be used after the first match
  427unify_clause2(Read, Decompiled, _, TermPos, TermPos) :-
  428    Read =@= Decompiled,
  429    !,
  430    Read = Decompiled.
  431unify_clause2(Read, Compiled1, Module, TermPos0, TermPos) :-
  432    ci_expand(Read, Compiled2, Module, TermPos0, TermPos1),
  433    match_module(Compiled2, Compiled1, Module, TermPos1, TermPos).
  434                                        % I don't know ...
  435unify_clause2(_, _, _, _, _) :-
  436    debug(clause_info, 'Could not unify clause', []),
  437    fail.
  438
  439unify_clause_head(H1, H2) :-
  440    strip_module(H1, _, H),
  441    strip_module(H2, _, H).
  442
  443plunit_source_head(test(_,_)) => true.
  444plunit_source_head(test(_)) => true.
  445plunit_source_head(_) => fail.
  446
  447plunit_compiled_head(_:'unit body'(_, _)) => true.
  448plunit_compiled_head('unit body'(_, _)) => true.
  449plunit_compiled_head(_) => fail.
 inlined_unification(+BodyRead, +BodyCompiled, -BodyReadOut, -BodyCompiledOut, +HeadRead, +BodyPosIn, -BodyPosOut) is det
  456inlined_unification((V=T,RBody0), (CV=CT,CBody0),
  457                    RBody, CBody, RHead, BPos1, BPos),
  458    inlineable_head_var(RHead, V2),
  459    V == V2,
  460    (V=T) =@= (CV=CT) =>
  461    argpos(2, BPos1, BPos2),
  462    inlined_unification(RBody0, CBody0, RBody, CBody, RHead, BPos2, BPos).
  463inlined_unification((V=T), (CV=CT),
  464                    RBody, CBody, RHead, BPos1, BPos),
  465    inlineable_head_var(RHead, V2),
  466    V == V2,
  467    (V=T) =@= (CV=CT) =>
  468    RBody = true,
  469    CBody = true,
  470    argpos(2, BPos1, BPos).
  471inlined_unification((V=T,RBody0), CBody0,
  472                    RBody, CBody, RHead, BPos1, BPos),
  473    inlineable_head_var(RHead, V2),
  474    V == V2,
  475    \+ (CBody0 = (G1,_), G1 =@= (V=T)) =>
  476    argpos(2, BPos1, BPos2),
  477    inlined_unification(RBody0, CBody0, RBody, CBody, RHead, BPos2, BPos).
  478inlined_unification((V=_), true,
  479                    RBody, CBody, RHead, BPos1, BPos),
  480    inlineable_head_var(RHead, V2),
  481    V == V2 =>
  482    RBody = true,
  483    CBody = true,
  484    argpos(2, BPos1, BPos).
  485inlined_unification(RBody0, CBody0, RBody, CBody, _RHead,
  486                    BPos0, BPos) =>
  487    RBody = RBody0,
  488    BPos  = BPos0,
  489    CBody = CBody0.
 inlineable_head_var(+Head, -Var) is nondet
True when Var is a variable in Head that may be used for inline unification. Currently we only inline direct arguments to the head.
  496inlineable_head_var(Head, Var) :-
  497    compound(Head),
  498    arg(_, Head, Var).
  499
  500split_on_cut((Cond0,!,Body0), Cond, Body) =>
  501    Cond = Cond0,
  502    Body = Body0.
  503split_on_cut((!,Body0), Cond, Body) =>
  504    Cond = true,
  505    Body = Body0.
  506split_on_cut((A,B), Cond, Body) =>
  507    Cond = (A,Cond1),
  508    split_on_cut(B, Cond1, Body).
  509split_on_cut(_, _, _) =>
  510    fail.
  511
  512ci_expand(Read, Compiled, Module, TermPos0, TermPos) :-
  513    catch(setup_call_cleanup(
  514              ( set_xref_flag(OldXRef),
  515                '$set_source_module'(Old, Module)
  516              ),
  517              expand_term(Read, TermPos0, Compiled, TermPos),
  518              ( '$set_source_module'(Old),
  519                set_prolog_flag(xref, OldXRef)
  520              )),
  521          E,
  522          expand_failed(E, Read)),
  523    compound(TermPos),                  % make sure somthing is filled.
  524    arg(1, TermPos, A1), nonvar(A1),
  525    arg(2, TermPos, A2), nonvar(A2).
  526
  527set_xref_flag(Value) :-
  528    current_prolog_flag(xref, Value),
  529    !,
  530    set_prolog_flag(xref, true).
  531set_xref_flag(false) :-
  532    create_prolog_flag(xref, true, [type(boolean)]).
  533
  534match_module((H1 :- B1), (H2 :- B2), Module, Pos0, Pos) :-
  535    !,
  536    unify_clause_head(H1, H2),
  537    unify_body(B1, B2, Module, Pos0, Pos).
  538match_module((H1 :- B1), H2, _Module, Pos0, Pos) :-
  539    B1 == true,
  540    unify_clause_head(H1, H2),
  541    Pos = Pos0,
  542    !.
  543match_module(H1, H2, _, Pos, Pos) :-    % deal with facts
  544    unify_clause_head(H1, H2).
 expand_failed(+Exception, +Term)
When debugging, indicate that expansion of the term failed.
  550expand_failed(E, Read) :-
  551    debugging(clause_info),
  552    message_to_string(E, Msg),
  553    debug(clause_info, 'Term-expand ~p failed: ~w', [Read, Msg]),
  554    fail.
 unify_body(+Read, +Decompiled, +Module, +Pos0, -Pos)
Deal with translations implied by the compiler. For example, compiling (a,b),c yields the same code as compiling a,b,c.

Pos0 and Pos still include the term-position of the head.

  563unify_body(B, C, _, Pos, Pos) :-
  564    B =@= C, B = C,
  565    does_not_dcg_after_binding(B, Pos),
  566    !.
  567unify_body(R, D, Module,
  568           term_position(F,T,FF,FT,[HP,BP0]),
  569           term_position(F,T,FF,FT,[HP,BP])) :-
  570    ubody(R, D, Module, BP0, BP).
 does_not_dcg_after_binding(+ReadBody, +ReadPos) is semidet
True if ReadPos/ReadPos does not contain DCG delayed unifications.
To be done
- We should pass that we are in a DCG; if we are not there is no reason for this test.
  580does_not_dcg_after_binding(B, Pos) :-
  581    \+ sub_term(brace_term_position(_,_,_), Pos),
  582    \+ (sub_term((Cut,_=_), B), Cut == !),
  583    !.
  584
  585
  586/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  587Some remarks.
  588
  589a --> { x, y, z }.
  590    This is translated into "(x,y),z), X=Y" by the DCG translator, after
  591    which the compiler creates "a(X,Y) :- x, y, z, X=Y".
  592- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 unify_goal(+Read, +Decompiled, +Module, +TermPosRead, -TermPosDecompiled) is semidet
This hook is called to fix up source code manipulations that result from goal expansions.
 ubody(+Read, +Decompiled, +Module, +TermPosRead, -TermPosForDecompiled)
Arguments:
Read- Clause read after expand_term/2
Decompiled- Decompiled clause
Module- Load module
TermPosRead- Sub-term positions of source
  607ubody(B, DB, _, P, P) :-
  608    var(P),                        % TBD: Create compatible pos term?
  609    !,
  610    B = DB.
  611ubody(B, C, _, P, P) :-
  612    B =@= C, B = C,
  613    does_not_dcg_after_binding(B, P),
  614    !.
  615ubody(X0, X, M, parentheses_term_position(_, _, P0), P) :-
  616    !,
  617    ubody(X0, X, M, P0, P).
  618ubody(X, Y, _,                    % X = call(X)
  619      Pos,
  620      term_position(From, To, From, To, [Pos])) :-
  621    nonvar(Y),
  622    Y = call(X),
  623    !,
  624    arg(1, Pos, From),
  625    arg(2, Pos, To).
  626ubody(A, B, _, P1, P2) :-
  627    nonvar(A), A = (_=_),
  628    nonvar(B), B = (LB=RB),
  629    A =@= (RB=LB),
  630    !,
  631    P1 = term_position(F,T, FF,FT, [PL,PR]),
  632    P2 = term_position(F,T, FF,FT, [PR,PL]).
  633ubody(A, B, _, P1, P2) :-
  634    nonvar(A), A = (_==_),
  635    nonvar(B), B = (LB==RB),
  636    A =@= (RB==LB),
  637    !,
  638    P1 = term_position(F,T, FF,FT, [PL,PR]),
  639    P2 = term_position(F,T, FF,FT, [PR,PL]).
  640ubody(B, D, _, term_position(_,_,_,_,[_,RP]), TPOut) :-
  641    nonvar(B), B = M:R,
  642    ubody(R, D, M, RP, TPOut).
  643ubody(B, D, M, term_position(_,_,_,_,[RP0,RP1]), TPOut) :-
  644    nonvar(B), B = (B0,B1),
  645    (   maybe_optimized(B0),
  646        ubody(B1, D, M, RP1, TPOut)
  647    ->  true
  648    ;   maybe_optimized(B1),
  649        ubody(B0, D, M, RP0, TPOut)
  650    ),
  651    !.
  652ubody(B0, B, M,
  653      brace_term_position(F,T,A0),
  654      Pos) :-
  655    B0 = (_,_=_),
  656    !,
  657    T1 is T - 1,
  658    ubody(B0, B, M,
  659          term_position(F,T,
  660                        F,T,
  661                        [A0,T1-T]),
  662          Pos).
  663ubody(B0, B, M,
  664      brace_term_position(F,T,A0),
  665      term_position(F,T,F,T,[A])) :-
  666    !,
  667    ubody(B0, B, M, A0, A).
  668ubody(C0, C, M, P0, P) :-
  669    nonvar(C0), nonvar(C),
  670    C0 = (_,_), C = (_,_),
  671    !,
  672    conj(C0, P0, GL, PL),
  673    mkconj(C, M, P, GL, PL).
  674ubody(Read, Decompiled, Module, TermPosRead, TermPosDecompiled) :-
  675    unify_goal(Read, Decompiled, Module, TermPosRead, TermPosDecompiled),
  676    !.
  677ubody(X0, X, M,
  678      term_position(F,T,FF,TT,PA0),
  679      term_position(F,T,FF,TT,PA)) :-
  680    callable(X0),
  681    callable(X),
  682    meta(M, X0, S),
  683    !,
  684    X0 =.. [_|A0],
  685    X  =.. [_|A],
  686    S =.. [_|AS],
  687    ubody_list(A0, A, AS, M, PA0, PA).
  688ubody(X0, X, M,
  689      term_position(F,T,FF,TT,PA0),
  690      term_position(F,T,FF,TT,PA)) :-
  691    expand_goal(X0, X1, M, PA0, PA),
  692    X1 =@= X,
  693    X1 = X.
  694
  695                                        % 5.7.X optimizations
  696ubody(_=_, true, _,                     % singleton = Any
  697      term_position(F,T,_FF,_TT,_PA),
  698      F-T) :- !.
  699ubody(_==_, fail, _,                    % singleton/firstvar == Any
  700      term_position(F,T,_FF,_TT,_PA),
  701      F-T) :- !.
  702ubody(A1=B1, B2=A2, _,                  % Term = Var --> Var = Term
  703      term_position(F,T,FF,TT,[PA1,PA2]),
  704      term_position(F,T,FF,TT,[PA2,PA1])) :-
  705    var(B1), var(B2),
  706    (A1==B1) =@= (B2==A2),
  707    !,
  708    A1 = A2, B1=B2.
  709ubody(A1==B1, B2==A2, _,                % const == Var --> Var == const
  710      term_position(F,T,FF,TT,[PA1,PA2]),
  711      term_position(F,T,FF,TT,[PA2,PA1])) :-
  712    var(B1), var(B2),
  713    (A1==B1) =@= (B2==A2),
  714    !,
  715    A1 = A2, B1=B2.
  716ubody(A is B - C, A is B + C2, _, Pos, Pos) :-
  717    integer(C),
  718    C2 =:= -C,
  719    !.
  720
  721ubody_list([], [], [], _, [], []).
  722ubody_list([G0|T0], [G|T], [AS|ASL], M, [PA0|PAT0], [PA|PAT]) :-
  723    ubody_elem(AS, G0, G, M, PA0, PA),
  724    ubody_list(T0, T, ASL, M, PAT0, PAT).
  725
  726ubody_elem(0, G0, G, M, PA0, PA) :-
  727    !,
  728    ubody(G0, G, M, PA0, PA).
  729ubody_elem(_, G, G, _, PA, PA).
 conj(+GoalTerm, +PositionTerm, -GoalList, -PositionList)
Turn a conjunctive body into a list of goals and their positions, i.e., removing the positions of the (,)/2 terms.
  736conj(Goal, Pos, GoalList, PosList) :-
  737    conj(Goal, Pos, GoalList, [], PosList, []).
  738
  739conj((A,B), term_position(_,_,_,_,[PA,PB]), GL, TG, PL, TP) :-
  740    !,
  741    conj(A, PA, GL, TGA, PL, TPA),
  742    conj(B, PB, TGA, TG, TPA, TP).
  743conj((A,B), brace_term_position(_,T,PA), GL, TG, PL, TP) :-
  744    B = (_=_),
  745    !,
  746    conj(A, PA, GL, TGA, PL, TPA),
  747    T1 is T - 1,
  748    conj(B, T1-T, TGA, TG, TPA, TP).
  749conj(A, parentheses_term_position(_,_,Pos), GL, TG, PL, TP) :-
  750    nonvar(Pos),
  751    !,
  752    conj(A, Pos, GL, TG, PL, TP).
  753conj((!,(S=SR)), F-T, [!,S=SR|TG], TG, [F-T,F1-T1|TP], TP) :-
  754    F1 is F+1,
  755    T1 is T+1.
  756conj(A, P, [A|TG], TG, [P|TP], TP).
 mkconj(+Decompiled, +Module, -Position, +ReadGoals, +ReadPositions)
  761mkconj(Goal, M, Pos, GoalList, PosList) :-
  762    mkconj(Goal, M, Pos, GoalList, [], PosList, []).
  763
  764mkconj(Conj, M, term_position(0,0,0,0,[PA,PB]), GL, TG, PL, TP) :-
  765    nonvar(Conj),
  766    Conj = (A,B),
  767    !,
  768    mkconj(A, M, PA, GL, TGA, PL, TPA),
  769    mkconj(B, M, PB, TGA, TG, TPA, TP).
  770mkconj(A0, M, P0, [A|TG], TG, [P|TP], TP) :-
  771    ubody(A, A0, M, P, P0),
  772    !.
  773mkconj(A0, M, P0, [RG|TG0], TG, [_|TP0], TP) :-
  774    maybe_optimized(RG),
  775    mkconj(A0, M, P0, TG0, TG, TP0, TP).
  776
  777maybe_optimized(debug(_,_,_)).
  778maybe_optimized(assertion(_)).
  779maybe_optimized(true).
 argpos(+N, +PositionTerm, -ArgPositionTerm) is det
Get the position for the nth argument of PositionTerm.
  785argpos(N, parentheses_term_position(_,_,PosIn), Pos) =>
  786    argpos(N, PosIn, Pos).
  787argpos(N, term_position(_,_,_,_,ArgPos), Pos) =>
  788    nth1(N, ArgPos, Pos).
  789argpos(_, _, _) => true.
  790
  791
  792                 /*******************************
  793                 *    PCE STUFF (SHOULD MOVE)   *
  794                 *******************************/
  795
  796/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  797        <method>(Receiver, ... Arg ...) :->
  798                Body
  799
  800mapped to:
  801
  802        send_implementation(Id, <method>(...Arg...), Receiver)
  803
  804- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  805
  806pce_method_clause(Head, Body, M:PlHead, PlBody, _, TermPos0, TermPos) :-
  807    !,
  808    pce_method_clause(Head, Body, PlBody, PlHead, M, TermPos0, TermPos).
  809pce_method_clause(Head, Body,
  810                  send_implementation(_Id, Msg, Receiver), PlBody,
  811                  M, TermPos0, TermPos) :-
  812    !,
  813    debug(clause_info, 'send method ...', []),
  814    arg(1, Head, Receiver),
  815    functor(Head, _, Arity),
  816    pce_method_head_arguments(2, Arity, Head, Msg),
  817    debug(clause_info, 'head ...', []),
  818    pce_method_body(Body, PlBody, M, TermPos0, TermPos).
  819pce_method_clause(Head, Body,
  820                  get_implementation(_Id, Msg, Receiver, Result), PlBody,
  821                  M, TermPos0, TermPos) :-
  822    !,
  823    debug(clause_info, 'get method ...', []),
  824    arg(1, Head, Receiver),
  825    debug(clause_info, 'receiver ...', []),
  826    functor(Head, _, Arity),
  827    arg(Arity, Head, PceResult),
  828    debug(clause_info, '~w?~n', [PceResult = Result]),
  829    pce_unify_head_arg(PceResult, Result),
  830    Ar is Arity - 1,
  831    pce_method_head_arguments(2, Ar, Head, Msg),
  832    debug(clause_info, 'head ...', []),
  833    pce_method_body(Body, PlBody, M, TermPos0, TermPos).
  834
  835pce_method_head_arguments(N, Arity, Head, Msg) :-
  836    N =< Arity,
  837    !,
  838    arg(N, Head, PceArg),
  839    PLN is N - 1,
  840    arg(PLN, Msg, PlArg),
  841    pce_unify_head_arg(PceArg, PlArg),
  842    debug(clause_info, '~w~n', [PceArg = PlArg]),
  843    NextArg is N+1,
  844    pce_method_head_arguments(NextArg, Arity, Head, Msg).
  845pce_method_head_arguments(_, _, _, _).
  846
  847pce_unify_head_arg(V, A) :-
  848    var(V),
  849    !,
  850    V = A.
  851pce_unify_head_arg(A:_=_, A) :- !.
  852pce_unify_head_arg(A:_, A).
  853
  854%       pce_method_body(+SrcBody, +DbBody, +M, +TermPos0, -TermPos
  855%
  856%       Unify the body of an XPCE method.  Goal-expansion makes this
  857%       rather tricky, especially as we cannot call XPCE's expansion
  858%       on an isolated method.
  859%
  860%       TermPos0 is the term-position term of the whole clause!
  861%
  862%       Further, please note that the body of the method-clauses reside
  863%       in another module than pce_principal, and therefore the body
  864%       starts with an I_CONTEXT call. This implies we need a
  865%       hypothetical term-position for the module-qualifier.
  866
  867pce_method_body(A0, A, M, TermPos0, TermPos) :-
  868    TermPos0 = term_position(F, T, FF, FT,
  869                             [ HeadPos,
  870                               BodyPos0
  871                             ]),
  872    TermPos  = term_position(F, T, FF, FT,
  873                             [ HeadPos,
  874                               term_position(0,0,0,0, [0-0,BodyPos])
  875                             ]),
  876    pce_method_body2(A0, A, M, BodyPos0, BodyPos).
  877
  878
  879pce_method_body2(::(_,A0), A, M, TermPos0, TermPos) :-
  880    !,
  881    TermPos0 = term_position(_, _, _, _, [_Cmt,BodyPos0]),
  882    TermPos  = BodyPos,
  883    expand_goal(A0, A, M, BodyPos0, BodyPos).
  884pce_method_body2(A0, A, M, TermPos0, TermPos) :-
  885    A0 =.. [Func,B0,C0],
  886    control_op(Func),
  887    !,
  888    A =.. [Func,B,C],
  889    TermPos0 = term_position(F, T, FF, FT,
  890                             [ BP0,
  891                               CP0
  892                             ]),
  893    TermPos  = term_position(F, T, FF, FT,
  894                             [ BP,
  895                               CP
  896                             ]),
  897    pce_method_body2(B0, B, M, BP0, BP),
  898    expand_goal(C0, C, M, CP0, CP).
  899pce_method_body2(A0, A, M, TermPos0, TermPos) :-
  900    expand_goal(A0, A, M, TermPos0, TermPos).
  901
  902control_op(',').
  903control_op((;)).
  904control_op((->)).
  905control_op((*->)).
  906
  907                 /*******************************
  908                 *     EXPAND_GOAL SUPPORT      *
  909                 *******************************/
  910
  911/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  912With the introduction of expand_goal, it  is increasingly hard to relate
  913the clause from the database to the actual  source. For one thing, we do
  914not know the compilation  module  of  the   clause  (unless  we  want to
  915decompile it).
  916
  917Goal expansion can translate  goals   into  control-constructs, multiple
  918clauses, or delete a subgoal.
  919
  920To keep track of the source-locations, we   have to redo the analysis of
  921the clause as defined in init.pl
  922- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  923
  924expand_goal(G, call(G), _, P, term_position(0,0,0,0,[P])) :-
  925    var(G),
  926    !.
  927expand_goal(G, G1, _, P, P) :-
  928    var(G),
  929    !,
  930    G1 = G.
  931expand_goal(M0, M, Module, P0, P) :-
  932    meta(Module, M0, S),
  933    !,
  934    P0 = term_position(F,T,FF,FT,PL0),
  935    P  = term_position(F,T,FF,FT,PL),
  936    functor(M0, Functor, Arity),
  937    functor(M,  Functor, Arity),
  938    expand_meta_args(PL0, PL, 1, S, Module, M0, M).
  939expand_goal(A, B, Module, P0, P) :-
  940    goal_expansion(A, B0, P0, P1),
  941    !,
  942    expand_goal(B0, B, Module, P1, P).
  943expand_goal(A, A, _, P, P).
  944
  945expand_meta_args([],      [],   _,  _, _,      _,  _).
  946expand_meta_args([P0|T0], [P|T], I, S, Module, M0, M) :-
  947    arg(I, M0, A0),
  948    arg(I, M,  A),
  949    arg(I, S,  AS),
  950    expand_arg(AS, A0, A, Module, P0, P),
  951    NI is I + 1,
  952    expand_meta_args(T0, T, NI, S, Module, M0, M).
  953
  954expand_arg(0, A0, A, Module, P0, P) :-
  955    !,
  956    expand_goal(A0, A, Module, P0, P).
  957expand_arg(_, A, A, _, P, P).
  958
  959meta(M, G, S) :- predicate_property(M:G, meta_predicate(S)).
  960
  961goal_expansion(send(R, Msg), send_class(R, _, SuperMsg), P, P) :-
  962    compound(Msg),
  963    Msg =.. [send_super, Selector | Args],
  964    !,
  965    SuperMsg =.. [Selector|Args].
  966goal_expansion(get(R, Msg, A), get_class(R, _, SuperMsg, A), P, P) :-
  967    compound(Msg),
  968    Msg =.. [get_super, Selector | Args],
  969    !,
  970    SuperMsg =.. [Selector|Args].
  971goal_expansion(send_super(R, Msg), send_class(R, _, Msg), P, P).
  972goal_expansion(get_super(R, Msg, V), get_class(R, _, Msg, V), P, P).
  973goal_expansion(SendSuperN, send_class(R, _, Msg), P, P) :-
  974    compound(SendSuperN),
  975    compound_name_arguments(SendSuperN, send_super, [R,Sel|Args]),
  976    Msg =.. [Sel|Args].
  977goal_expansion(SendN, send(R, Msg), P, P) :-
  978    compound(SendN),
  979    compound_name_arguments(SendN, send, [R,Sel|Args]),
  980    atom(Sel), Args \== [],
  981    Msg =.. [Sel|Args].
  982goal_expansion(GetSuperN, get_class(R, _, Msg, Answer), P, P) :-
  983    compound(GetSuperN),
  984    compound_name_arguments(GetSuperN, get_super, [R,Sel|AllArgs]),
  985    append(Args, [Answer], AllArgs),
  986    Msg =.. [Sel|Args].
  987goal_expansion(GetN, get(R, Msg, Answer), P, P) :-
  988    compound(GetN),
  989    compound_name_arguments(GetN, get, [R,Sel|AllArgs]),
  990    append(Args, [Answer], AllArgs),
  991    atom(Sel), Args \== [],
  992    Msg =.. [Sel|Args].
  993goal_expansion(G0, G, P, P) :-
  994    user:goal_expansion(G0, G),     % TBD: we need the module!
  995    G0 \== G.                       % \=@=?
  996
  997
  998                 /*******************************
  999                 *        INITIALIZATION        *
 1000                 *******************************/
 initialization_layout(+SourceLocation, ?InitGoal, -ReadGoal, -TermPos) is semidet
Find term-layout of :- initialization directives.
 1007initialization_layout(File:Line, M:Goal0, Goal, TermPos) :-
 1008    read_term_at_line(File, Line, M, Directive, DirectivePos, _),
 1009    Directive    = (:- initialization(ReadGoal)),
 1010    DirectivePos = term_position(_, _, _, _, [InitPos]),
 1011    InitPos      = term_position(_, _, _, _, [GoalPos]),
 1012    (   ReadGoal = M:_
 1013    ->  Goal = M:Goal0
 1014    ;   Goal = Goal0
 1015    ),
 1016    unify_body(ReadGoal, Goal, M, GoalPos, TermPos),
 1017    !.
 1018
 1019
 1020                 /*******************************
 1021                 *        PRINTABLE NAMES       *
 1022                 *******************************/
 1023
 1024:- module_transparent
 1025    predicate_name/2. 1026:- multifile
 1027    user:prolog_predicate_name/2,
 1028    user:prolog_clause_name/2. 1029
 1030hidden_module(user).
 1031hidden_module(system).
 1032hidden_module(pce_principal).           % should be config
 1033hidden_module(Module) :-                % SWI-Prolog specific
 1034    import_module(Module, system).
 1035
 1036thaffix(1, st) :- !.
 1037thaffix(2, nd) :- !.
 1038thaffix(_, th).
 predicate_name(:Head, -PredName:string) is det
Describe a predicate as [Module:]Name/Arity.
 1044predicate_name(Predicate, PName) :-
 1045    strip_module(Predicate, Module, Head),
 1046    (   user:prolog_predicate_name(Module:Head, PName)
 1047    ->  true
 1048    ;   functor(Head, Name, Arity),
 1049        (   hidden_module(Module)
 1050        ->  format(string(PName), '~q/~d', [Name, Arity])
 1051        ;   format(string(PName), '~q:~q/~d', [Module, Name, Arity])
 1052        )
 1053    ).
 clause_name(+Ref, -Name)
Provide a suitable description of the indicated clause.
 1059clause_name(Ref, Name) :-
 1060    user:prolog_clause_name(Ref, Name),
 1061    !.
 1062clause_name(Ref, Name) :-
 1063    nth_clause(Head, N, Ref),
 1064    !,
 1065    predicate_name(Head, PredName),
 1066    thaffix(N, Th),
 1067    format(string(Name), '~d-~w clause of ~w', [N, Th, PredName]).
 1068clause_name(Ref, Name) :-
 1069    clause_property(Ref, erased),
 1070    !,
 1071    clause_property(Ref, predicate(M:PI)),
 1072    format(string(Name), 'erased clause from ~q', [M:PI]).
 1073clause_name(_, '<meta-call>')