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(@Var, -Goals)
 119%
 120%   Unify Goals with the goals frozen on Var or true if no
 121%   goals are grozen on Var.
 122
 123frozen(Var, Goals) :-
 124    get_attr(Var, freeze, Goals0),
 125    !,
 126    make_conjunction(Goals0, Goals).
 127frozen(_, true).
 128
 129make_conjunction('$and'(A0, B0), (A, B)) :-
 130    !,
 131    make_conjunction(A0, A),
 132    make_conjunction(B0, B).
 133make_conjunction(G, G).
 134
 135
 136                 /*******************************
 137                 *             PORTRAY          *
 138                 *******************************/
 139
 140%!  portray_attvar(@Var)
 141%
 142%   Called from write_term/3 using the option attributes(portray) or
 143%   when the prolog flag write_attributes   equals portray. Its task
 144%   is the write the attributes in a human readable format.
 145
 146:- public
 147    portray_attvar/1.
 148
 149portray_attvar(Var) :-
 150    write('{'),
 151    get_attrs(Var, Attr),
 152    portray_attrs(Attr, Var),
 153    write('}').
 154
 155portray_attrs([], _).
 156portray_attrs(att(Name, Value, Rest), Var) :-
 157    portray_attr(Name, Value, Var),
 158    (   Rest == []
 159    ->  true
 160    ;   write(', '),
 161        portray_attrs(Rest, Var)
 162    ).
 163
 164portray_attr(freeze, Goal, Var) :-
 165    !,
 166    format('freeze(~w, ~W)', [ Var, Goal,
 167                               [ portray(true),
 168                                 quoted(true),
 169                                 attributes(ignore)
 170                               ]
 171                             ]).
 172portray_attr(Name, Value, Var) :-
 173    G = Name:attr_portray_hook(Value, Var),
 174    (   '$c_current_predicate'(_, G),
 175        G
 176    ->  true
 177    ;   format('~w = ...', [Name])
 178    ).
 179
 180
 181                 /*******************************
 182                 *          CALL RESIDUE        *
 183                 *******************************/
 184
 185%!  call_residue_vars(:Goal, -Vars)
 186%
 187%   If Goal is  true,  Vars  is   the  set  of  residual  attributed
 188%   variables created by Goal. Goal  is   called  as in call/1. This
 189%   predicate  is  for  debugging  constraint   programs.  Assume  a
 190%   constraint program that creates  conflicting   constraints  on a
 191%   variable that is not part of the   result  variables of Goal. If
 192%   the solver is powerful enough it   will  detect the conflict and
 193%   fail. If the solver is too  weak   however  it  will succeed and
 194%   residual attributed variables holding the conflicting constraint
 195%   form a witness of this problem.
 196
 197:- meta_predicate
 198    call_residue_vars(0, -).
 199
 200call_residue_vars(Goal, Vars) :-
 201    prolog_current_choice(Chp),
 202    setup_call_cleanup(
 203        '$call_residue_vars_start',
 204        run_crv(Goal, Chp, Vars, Det),
 205        '$call_residue_vars_end'),
 206    (   Det == true
 207    ->  !
 208    ;   true
 209    ).
 210call_residue_vars(_, _) :-
 211    fail.
 212
 213run_crv(Goal, Chp, Vars, Det) :-
 214    call(Goal),
 215    deterministic(Det),
 216    '$attvars_after_choicepoint'(Chp, Vars).
 217
 218%!  copy_term(+Term, -Copy, -Gs) is det.
 219%
 220%   Creates a regular term Copy  as  a   copy  of  Term (without any
 221%   attributes), and a list Gs of goals that when executed reinstate
 222%   all attributes onto Copy. The nonterminal attribute_goals//1, as
 223%   defined in the modules the  attributes   stem  from,  is used to
 224%   convert attributes to lists of goals.
 225
 226copy_term(Term, Copy, Gs) :-
 227    term_attvars(Term, Vs),
 228    (   Vs == []
 229    ->  Gs = [],
 230        copy_term(Term, Copy)
 231    ;   findall(Term-Gs,
 232                ( phrase(attvars_residuals(Vs), Gs),
 233                  delete_attributes(Term)
 234                ),
 235                [Copy-Gs])
 236    ).
 237
 238attvars_residuals([]) --> [].
 239attvars_residuals([V|Vs]) -->
 240    (   { get_attrs(V, As) }
 241    ->  attvar_residuals(As, V)
 242    ;   []
 243    ),
 244    attvars_residuals(Vs).
 245
 246attvar_residuals([], _) --> [].
 247attvar_residuals(att(Module,Value,As), V) -->
 248    (   { nonvar(V) }
 249    ->  % a previous projection predicate could have instantiated
 250        % this variable, for example, to avoid redundant goals
 251        []
 252    ;   (   { Module == freeze }
 253        ->  frozen_residuals(Value, V)
 254        ;   { current_predicate(Module:attribute_goals//1),
 255              phrase(Module:attribute_goals(V), Goals)
 256            }
 257        ->  list(Goals)
 258        ;   [put_attr(V, Module, Value)]
 259        )
 260    ),
 261    attvar_residuals(As, V).
 262
 263list([])     --> [].
 264list([L|Ls]) --> [L], list(Ls).
 265
 266delete_attributes(Term) :-
 267    term_attvars(Term, Vs),
 268    delete_attributes_(Vs).
 269
 270delete_attributes_([]).
 271delete_attributes_([V|Vs]) :-
 272    del_attrs(V),
 273    delete_attributes_(Vs).
 274
 275
 276%!  frozen_residuals(+FreezeAttr, +Var)// is det.
 277%
 278%   Instantiate  a  freeze  goal  for  each    member  of  the  $and
 279%   conjunction. Note that we cannot  map   this  into a conjunction
 280%   because  freeze(X,  a),  freeze(X,  !)  would  create  freeze(X,
 281%   (a,!)),  which  is  fundamentally  different.  We  could  create
 282%   freeze(X,  (call(a),  call(!)))  or  preform  a  more  eleborate
 283%   analysis to validate the semantics are not changed.
 284
 285frozen_residuals('$and'(X,Y), V) -->
 286    !,
 287    frozen_residuals(X, V),
 288    frozen_residuals(Y, V).
 289frozen_residuals(X, V) -->
 290    [ freeze(V, X) ].