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)  2011-2015, 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(arithmetic,
  36          [ arithmetic_function/1,              % +Name/Arity
  37            arithmetic_expression_value/2       % :Expression, -Value
  38          ]).
  39:- use_module(library(error)).
  40:- use_module(library(lists)).
  41:- set_prolog_flag(generate_debug_info, false).
  42
  43/** <module> Extensible arithmetic
  44
  45This module provides a  portable   partial  replacement  of SWI-Prolog's
  46user-defined  arithmetic  (evaluable)   functions.    It   defines   the
  47compatibility  directive  arithmetic_function/1  and  support  for  both
  48runtime and compile-time evaluation of expressions   that  are a mixture
  49between Prolog predicates  used  as   functions  and  built-in evaluable
  50terms.
  51*/
  52
  53:- meta_predicate
  54    arithmetic_function(:),
  55    arithmetic_expression_value(:, -).
  56:- multifile
  57    evaluable/2.                            % Term, Module
  58
  59%!  arithmetic_function(:NameArity) is det.
  60%
  61%   Declare a predicate as an arithmetic function.
  62%
  63%   @deprecated This function provides  a   partial  work around for
  64%   pure Prolog user-defined arithmetic  functions   that  has  been
  65%   dropped in SWI-Prolog  5.11.23.  Notably,   it  only  deals with
  66%   expression know at compile time.
  67
  68arithmetic_function(Term) :-
  69    throw(error(context_error(nodirective, arithmetic_function(Term)), _)).
  70
  71arith_decl_clauses(NameArity,
  72                   [(:- public(PI)),
  73                    arithmetic:evaluable(Term, Q)
  74                   ]) :-
  75    prolog_load_context(module, M),
  76    strip_module(M:NameArity, Q, Spec),
  77    (   Q == M
  78    ->  PI = Name/ImplArity
  79    ;   PI = Q:Name/ImplArity
  80    ),
  81    (   Spec = Name/Arity
  82    ->  functor(Term, Name, Arity),
  83        ImplArity is Arity+1
  84    ;   type_error(predicate_indicator, Term)
  85    ).
  86
  87%!  eval_clause(+Term, -Clause) is det.
  88%
  89%   Clause is a clause  for   evaluating  the  arithmetic expression
  90%   Term.
  91
  92eval_clause(Term, (eval(Gen, M, Result) :- Body)) :-
  93    functor(Term, Name, Arity),
  94    functor(Gen, Name, Arity),
  95    Gen =.. [_|Args],
  96    eval_args(Args, PlainArgs, M, Goals, [Result is NewTerm]),
  97    NewTerm =.. [Name|PlainArgs],
  98    list_conj(Goals, Body).
  99
 100eval_args([], [], _, Goals, Goals).
 101eval_args([E0|T0], [A0|T], M, [eval(E0, M, A0)|GT], RT) :-
 102    eval_args(T0, T, M, GT, RT).
 103
 104list_conj([One], One) :- !.
 105list_conj([H|T0], (H,T)) :-
 106    list_conj(T0, T).
 107
 108eval_clause(Clause) :-
 109    current_arithmetic_function(Term),
 110    eval_clause(Term, Clause).
 111
 112term_expansion(eval('$builtin', _, _), Clauses) :-
 113    findall(Clause, eval_clause(Clause), Clauses).
 114
 115
 116%!  arithmetic_expression_value(:Expression, -Result) is det.
 117%
 118%   True  when  Result  unifies  with    the  arithmetic  result  of
 119%   evaluating Expression.
 120
 121arithmetic_expression_value(M:Expression, Result) :-
 122    eval(Expression, M, Result).
 123
 124eval(Number, _, Result) :-
 125    number(Number),
 126    !,
 127    Result = Number.
 128eval(Term, M, Result) :-
 129    evaluable(Term, M2),
 130    visible(M, M2),
 131    !,
 132    call(M2:Term, Result).
 133eval('$builtin', _, _).
 134
 135
 136visible(M, M) :- !.
 137visible(M, Super) :-
 138    import_module(M, Parent),
 139    visible(Parent, Super).
 140
 141
 142                 /*******************************
 143                 *         COMPILE-TIME         *
 144                 *******************************/
 145
 146math_goal_expansion(A is Expr, Goal) :-
 147    expand_function(Expr, Native, Pre),
 148    tidy((Pre, A is Native), Goal).
 149math_goal_expansion(ExprA =:= ExprB, Goal) :-
 150    expand_function(ExprA, NativeA, PreA),
 151    expand_function(ExprB, NativeB, PreB),
 152    tidy((PreA, PreB, NativeA =:= NativeB), Goal).
 153math_goal_expansion(ExprA =\= ExprB, Goal) :-
 154    expand_function(ExprA, NativeA, PreA),
 155    expand_function(ExprB, NativeB, PreB),
 156    tidy((PreA, PreB, NativeA =\= NativeB), Goal).
 157math_goal_expansion(ExprA > ExprB, Goal) :-
 158    expand_function(ExprA, NativeA, PreA),
 159    expand_function(ExprB, NativeB, PreB),
 160    tidy((PreA, PreB, NativeA > NativeB), Goal).
 161math_goal_expansion(ExprA < ExprB, Goal) :-
 162    expand_function(ExprA, NativeA, PreA),
 163    expand_function(ExprB, NativeB, PreB),
 164    tidy((PreA, PreB, NativeA < NativeB), Goal).
 165math_goal_expansion(ExprA >= ExprB, Goal) :-
 166    expand_function(ExprA, NativeA, PreA),
 167    expand_function(ExprB, NativeB, PreB),
 168    tidy((PreA, PreB, NativeA >= NativeB), Goal).
 169math_goal_expansion(ExprA =< ExprB, Goal) :-
 170    expand_function(ExprA, NativeA, PreA),
 171    expand_function(ExprB, NativeB, PreB),
 172    tidy((PreA, PreB, NativeA =< NativeB), Goal).
 173
 174expand_function(Expression, NativeExpression, Goal) :-
 175    do_expand_function(Expression, NativeExpression, Goal0),
 176    tidy(Goal0, Goal).
 177
 178do_expand_function(X, X, true) :-
 179    evaluable(X),
 180    !.
 181do_expand_function(Function, Result, ArgCode) :-
 182    current_arithmetic_function(Function),
 183    !,
 184    Function =.. [Name|Args],
 185    expand_function_arguments(Args, ArgResults, ArgCode),
 186    Result =.. [Name|ArgResults].
 187do_expand_function(Function, Result, (ArgCode, Pred)) :-
 188    prolog_load_context(module, M),
 189    evaluable(Function, M2),
 190    visible(M, M2),
 191    !,
 192    Function =.. [Name|Args],
 193    expand_predicate_arguments(Args, ArgResults, ArgCode),
 194    append(ArgResults, [Result], PredArgs),
 195    Pred =.. [Name|PredArgs].
 196do_expand_function(Function, _, _) :-
 197    type_error(evaluable, Function).
 198
 199
 200expand_function_arguments([], [], true).
 201expand_function_arguments([H0|T0], [H|T], (A,B)) :-
 202    do_expand_function(H0, H, A),
 203    expand_function_arguments(T0, T, B).
 204
 205expand_predicate_arguments([], [], true).
 206expand_predicate_arguments([H0|T0], [H|T], (A,B)) :-
 207    do_expand_function(H0, H1, A0),
 208    (   callable(H1),
 209        current_arithmetic_function(H1)
 210    ->  A = (A0, H is H1)
 211    ;   A = A0,
 212        H = H1
 213    ),
 214    expand_predicate_arguments(T0, T, B).
 215
 216%!  evaluable(F) is semidet.
 217%
 218%   True if F and all its subterms are evaluable terms or variables.
 219
 220evaluable(F) :-
 221    var(F),
 222    !.
 223evaluable(F) :-
 224    number(F),
 225    !.
 226evaluable([_Code]) :- !.
 227evaluable(Func) :-                              % Funtional notation.
 228    functor(Func, ., 2),
 229    !.
 230evaluable(F) :-
 231    string(F),
 232    !,
 233    string_length(F, 1).
 234evaluable(F) :-
 235    current_arithmetic_function(F),
 236    (   compound(F)
 237    ->  forall(arg(_,F,A), evaluable(A))
 238    ;   true
 239    ).
 240
 241%!  tidy(+GoalIn, -GoalOut)
 242%
 243%   Cleanup the output from expand_function/3.
 244
 245tidy(A, A) :-
 246    var(A),
 247    !.
 248tidy(((A,B),C), R) :-
 249    !,
 250    tidy((A,B,C), R).
 251tidy((true,A), R) :-
 252    !,
 253    tidy(A, R).
 254tidy((A,true), R) :-
 255    !,
 256    tidy(A, R).
 257tidy((A, X is Y), R) :-
 258    var(X), var(Y),
 259    !,
 260    tidy(A, R),
 261    X = Y.
 262tidy((A,B), (TA,TB)) :-
 263    !,
 264    tidy(A, TA),
 265    tidy(B, TB).
 266tidy(A, A).
 267
 268
 269                 /*******************************
 270                 *        EXPANSION HOOK        *
 271                 *******************************/
 272
 273:- multifile
 274    system:term_expansion/2,
 275    system:goal_expansion/2.
 276
 277system:term_expansion((:- arithmetic_function(Term)), Clauses) :-
 278    arith_decl_clauses(Term, Clauses).
 279
 280system:goal_expansion(Math, MathGoal) :-
 281    math_goal_expansion(Math, MathGoal).