View source with formatted 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)  2004-2016, University of Amsterdam
    7                              VU University Amsterdam
    8    All rights reserved.
    9
   10    Redistribution and use in source and binary forms, with or without
   11    modification, are permitted provided that the following conditions
   12    are met:
   13
   14    1. Redistributions of source code must retain the above copyright
   15       notice, this list of conditions and the following disclaimer.
   16
   17    2. Redistributions in binary form must reproduce the above copyright
   18       notice, this list of conditions and the following disclaimer in
   19       the documentation and/or other materials provided with the
   20       distribution.
   21
   22    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   23    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   24    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   25    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   26    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   27    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   28    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   29    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   30    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   31    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   32    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   33    POSSIBILITY OF SUCH DAMAGE.
   34*/
   35
   36:- module('$attvar',
   37          [ '$wakeup'/1,                % +Wakeup list
   38            freeze/2,                   % +Var, :Goal
   39            frozen/2,                   % @Var, -Goal
   40            call_residue_vars/2,        % :Goal, -Vars
   41            copy_term/3                 % +Term, -Copy, -Residue
   42          ]).   43
   44/** <module> Attributed variable handling
   45
   46Attributed  variable  and  coroutining  support    based  on  attributed
   47variables. This module is complemented with C-defined predicates defined
   48in pl-attvar.c
   49*/
   50
   51%!  '$wakeup'(+List)
   52%
   53%   Called from the kernel if assignments have been made to
   54%   attributed variables.
   55
   56'$wakeup'([]).
   57'$wakeup'(wakeup(Attribute, Value, Rest)) :-
   58    call_all_attr_uhooks(Attribute, Value),
   59    '$wakeup'(Rest).
   60
   61call_all_attr_uhooks([], _).
   62call_all_attr_uhooks(att(Module, AttVal, Rest), Value) :-
   63    uhook(Module, AttVal, Value),
   64    call_all_attr_uhooks(Rest, Value).
   65
   66
   67%!  uhook(+AttributeName, +AttributeValue, +Value)
   68%
   69%   Run the unify hook for attributed named AttributeName after
   70%   assigning an attvar with attribute AttributeValue the value
   71%   Value.
   72%
   73%   This predicate deals with reserved attribute names to avoid
   74%   the meta-call overhead.
   75
   76uhook(freeze, Goal, Y) :-
   77    !,
   78    (   attvar(Y)
   79    ->  (   get_attr(Y, freeze, G2)
   80        ->  put_attr(Y, freeze, '$and'(G2, Goal))
   81        ;   put_attr(Y, freeze, Goal)
   82        )
   83    ;   unfreeze(Goal)
   84    ).
   85uhook(Module, AttVal, Value) :-
   86    Module:attr_unify_hook(AttVal, Value).
   87
   88
   89%!  unfreeze(+ConjunctionOrGoal)
   90%
   91%   Handle  unfreezing  of  conjunctions.  As  meta-calling  control
   92%   structures is slower than meta-interpreting them   we do this in
   93%   Prolog. Another advantage is that   having unfreeze/1 in between
   94%   makes the stacktrace and profiling   easier  to intepret. Please
   95%   note that we cannot use a direct conjunction as this would break
   96%   freeze(X, (a, !, b)).
   97
   98unfreeze('$and'(A,B)) :-
   99    !,
  100    unfreeze(A),
  101    unfreeze(B).
  102unfreeze(Goal) :-
  103    Goal.
  104
  105%!  freeze(@Var, :Goal)
  106%
  107%   Suspend execution of Goal until Var is unbound.
  108
  109:- meta_predicate
  110    freeze(?, 0).  111
  112freeze(Var, Goal) :-
  113    '$freeze'(Var, Goal),
  114    !.        % Succeeds if delayed
  115freeze(_, Goal) :-
  116    Goal.
  117
  118%!  frozen(@Term, -Goal)
  119%
  120%   Unify Goals with the goals frozen on  Var   or  true if no goals are
  121%   frozen on Var.
  122%
  123%   Note that attribute_goals//1 may   destructively  update attributes,
  124%   often used to simplify the produced attributes. For frozen/2 however
  125%   we must keep  the  original  variables.   Ideally  we  would  demand
  126%   attribute_goals//1 to not modify any  attributes.   As  that is hard
  127%   given where we are we now copy   the  result and fail, restoring the
  128%   bindings. This is a simplified version of bagof/3.
  129
  130frozen(Term, Goal) :-
  131    term_attvars(Term, AttVars),
  132    (   AttVars == []
  133    ->  Goal = true
  134    ;   sort(AttVars, AttVars2),
  135        '$term_attvar_variables'(Term, KVars),
  136        Keep =.. [v|KVars],
  137        findall(Keep+Goal0,
  138                frozen_residuals(AttVars2, Goal0),
  139                [Kept+Goal]),
  140        rebind_vars(Keep, Kept)
  141    ).
  142
  143frozen_residuals(AttVars, Goal) :-
  144    phrase(attvars_residuals(AttVars), GoalList0),
  145    sort(GoalList0, GoalList),
  146    make_conjunction(GoalList, Goal).
  147
  148
  149%!  rebind_vars(+Keep, +Kept) is det.
  150%
  151%   Rebind the variables that have been copied and possibly instantiated
  152%   by attribute_goals//1. Note that library(clpfd)   may  bind internal
  153%   variables to e.g., `processed`. We do   not rebind such variables as
  154%   that would trigger constraints. These variables should not appear in
  155%   the produced goal anyway. If  both   are  attvars, unifying may also
  156%   re-trigger. Therefore, we remove the variables  from the copy before
  157%   rebinding. This should be ok as all variable identifies are properly
  158%   restored.
  159
  160rebind_vars(Keep, Kept) :-
  161    functor(Keep, _, Arity),
  162    rebind_vars(1, Arity, Keep, Kept).
  163
  164rebind_vars(I, Arity, KeepT, KeptT) :-
  165    I =< Arity,
  166    !,
  167    arg(I, KeepT, Keep),
  168    arg(I, KeptT, Kept),
  169    (   attvar(Keep), attvar(Kept)
  170    ->  del_attrs(Kept),
  171        Keep = Kept
  172    ;   var(Kept)
  173    ->  Keep = Kept
  174    ;   true
  175    ),
  176    I2 is I+1,
  177    rebind_vars(I2, Arity, KeepT, KeptT).
  178rebind_vars(_, _, _, _).
  179
  180make_conjunction([], true).
  181make_conjunction([H|T], Goal) :-
  182    (   T == []
  183    ->  Goal = H
  184    ;   Goal = (H,G),
  185        make_conjunction(T, G)
  186    ).
  187
  188
  189                 /*******************************
  190                 *             PORTRAY          *
  191                 *******************************/
  192
  193%!  portray_attvar(@Var)
  194%
  195%   Called from write_term/3 using the option attributes(portray) or
  196%   when the prolog flag write_attributes   equals portray. Its task
  197%   is the write the attributes in a human readable format.
  198
  199:- public
  200    portray_attvar/1.  201
  202portray_attvar(Var) :-
  203    write('{'),
  204    get_attrs(Var, Attr),
  205    portray_attrs(Attr, Var),
  206    write('}').
  207
  208portray_attrs([], _).
  209portray_attrs(att(Name, Value, Rest), Var) :-
  210    portray_attr(Name, Value, Var),
  211    (   Rest == []
  212    ->  true
  213    ;   write(', '),
  214        portray_attrs(Rest, Var)
  215    ).
  216
  217portray_attr(freeze, Goal, Var) :-
  218    !,
  219    Options = [ portray(true),
  220                quoted(true),
  221                attributes(ignore)
  222              ],
  223    format('freeze(~W, ~W)', [ Var, Options, Goal, Options
  224                             ]).
  225portray_attr(Name, Value, Var) :-
  226    G = Name:attr_portray_hook(Value, Var),
  227    (   '$c_current_predicate'(_, G),
  228        G
  229    ->  true
  230    ;   format('~w = ...', [Name])
  231    ).
  232
  233
  234                 /*******************************
  235                 *          CALL RESIDUE        *
  236                 *******************************/
  237
  238%!  call_residue_vars(:Goal, -Vars)
  239%
  240%   If Goal is  true,  Vars  is   the  set  of  residual  attributed
  241%   variables created by Goal. Goal  is   called  as in call/1. This
  242%   predicate  is  for  debugging  constraint   programs.  Assume  a
  243%   constraint program that creates  conflicting   constraints  on a
  244%   variable that is not part of the   result  variables of Goal. If
  245%   the solver is powerful enough it   will  detect the conflict and
  246%   fail. If the solver is too  weak   however  it  will succeed and
  247%   residual attributed variables holding the conflicting constraint
  248%   form a witness of this problem.
  249
  250:- meta_predicate
  251    call_residue_vars(0, -).  252
  253call_residue_vars(Goal, Vars) :-
  254    prolog_current_choice(Chp),
  255    setup_call_cleanup(
  256        '$call_residue_vars_start',
  257        run_crv(Goal, Chp, Vars, Det),
  258        '$call_residue_vars_end'),
  259    (   Det == true
  260    ->  !
  261    ;   true
  262    ).
  263call_residue_vars(_, _) :-
  264    fail.
  265
  266run_crv(Goal, Chp, Vars, Det) :-
  267    call(Goal),
  268    deterministic(Det),
  269    '$attvars_after_choicepoint'(Chp, Vars).
  270
  271%!  copy_term(+Term, -Copy, -Gs) is det.
  272%
  273%   Creates a regular term Copy  as  a   copy  of  Term (without any
  274%   attributes), and a list Gs of goals that when executed reinstate
  275%   all attributes onto Copy. The nonterminal attribute_goals//1, as
  276%   defined in the modules the  attributes   stem  from,  is used to
  277%   convert attributes to lists of goals.
  278
  279copy_term(Term, Copy, Gs) :-
  280    term_attvars(Term, Vs),
  281    (   Vs == []
  282    ->  Gs = [],
  283        copy_term(Term, Copy)
  284    ;   sort(Vs, Vs2),
  285        findall(Term-Gs,
  286                ( phrase(attvars_residuals(Vs2), Gs),
  287                  delete_attributes(Term)
  288                ),
  289                [Copy-Gs])
  290    ).
  291
  292attvars_residuals([]) --> [].
  293attvars_residuals([V|Vs]) -->
  294    (   { get_attrs(V, As) }
  295    ->  attvar_residuals(As, V)
  296    ;   []
  297    ),
  298    attvars_residuals(Vs).
  299
  300attvar_residuals([], _) --> [].
  301attvar_residuals(att(Module,Value,As), V) -->
  302    (   { nonvar(V) }
  303    ->  % a previous projection predicate could have instantiated
  304        % this variable, for example, to avoid redundant goals
  305        []
  306    ;   (   { Module == freeze }
  307        ->  frozen_residuals(Value, V)
  308        ;   { current_predicate(Module:attribute_goals//1),
  309              phrase(Module:attribute_goals(V), Goals)
  310            }
  311        ->  list(Goals)
  312        ;   [put_attr(V, Module, Value)]
  313        )
  314    ),
  315    attvar_residuals(As, V).
  316
  317list([])     --> [].
  318list([L|Ls]) --> [L], list(Ls).
  319
  320delete_attributes(Term) :-
  321    term_attvars(Term, Vs),
  322    delete_attributes_(Vs).
  323
  324delete_attributes_([]).
  325delete_attributes_([V|Vs]) :-
  326    del_attrs(V),
  327    delete_attributes_(Vs).
  328
  329
  330%!  frozen_residuals(+FreezeAttr, +Var)// is det.
  331%
  332%   Instantiate  a  freeze  goal  for  each    member  of  the  $and
  333%   conjunction. Note that we cannot  map   this  into a conjunction
  334%   because  freeze(X,  a),  freeze(X,  !)  would  create  freeze(X,
  335%   (a,!)),  which  is  fundamentally  different.  We  could  create
  336%   freeze(X,  (call(a),  call(!)))  or  preform  a  more  eleborate
  337%   analysis to validate the semantics are not changed.
  338
  339frozen_residuals('$and'(X,Y), V) -->
  340    !,
  341    frozen_residuals(X, V),
  342    frozen_residuals(Y, V).
  343frozen_residuals(X, V) -->
  344    [ freeze(V, X) ]