1/*  Part of Extended Libraries for SWI-Prolog
    2
    3    Author:        Edison Mera
    4    E-mail:        efmera@gmail.com
    5    WWW:           https://github.com/edisonm/xlibrary
    6    Copyright (C): 2014, Process Design Center, Breda, The Netherlands.
    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(compound_expand,
   36          [ before/1,
   37            after/1,
   38            init_expansors/0,
   39            op(1, fx, '$compound_expand') % Used to detect expansion modules
   40          ]).   41
   42/* <module> Compound expansions
   43
   44   This module  allows to define  compositional term and goal  expansions, using
   45   this   module   in   a   module   that   already   defines   the   predicates
   46   term_expansion/2/4 and goal_expansion/2/4 but don't export them.
   47
   48   The  composition  of  expansions  is   instrumental  to  grammar  and  syntax
   49   extensions,  which is  the key  point of  Ciao Prolog,  but not  supported in
   50   SWI-Prolog. We  do not  need to deal  with all the  complexity that  the Ciao
   51   package  system  has, so  with  this  helper the  port  of  Ciao Packages  to
   52   SWI-Prolog  can be  achieved smoothly  and such  modules can  be used  in SWI
   53   Programs that do not require the Ciao dialect.
   54
   55   Notes:
   56
   57   - Use   reexport(library(compound_expand))  in   order  for   this  to   work
   58     efficiently, otherwise you  will have to import compound_expand  on each of
   59     the dependent expansions,  but also you should avoid to  import this in the
   60     user module.
   61
   62   - Expansions are not applied to the module where they are implemented, but to
   63     the modules that import them.  This  is a bit different from how expansions
   64     work in SWI-Prolog, but it has a more clear behavior.
   65
   66@author Edison Mera
   67*/
   68
   69:- use_module(library(def_modules), []).   70
   71% The most  efficient way  to implement  the compound  expansions library  is to
   72% redefine the  predicate '$def_modules'/2, which  is only called  in expand.pl,
   73% but for some  reason I don't know why  we can not redefine it,  so instead the
   74% next lines are in place:
   75
   76% :- redefine_system_predicate('$def_modules'(_,_)). % This does not work
   77:- abolish('$expand':'$def_modules'/2),
   78   use_module('$expand':library(def_modules), ['$def_modules'/2]).   79
   80:- use_module(library(expansion_module)).   81:- use_module(library(partsort)).   82:- use_module(library(lists)).   83:- use_module(library(apply)).   84:- use_module(library(option)).   85
   86:- multifile
   87    system:term_expansion/4,
   88    system:goal_expansion/4,
   89    before/2.
 before(+Module) is det
Declaration to say that the expansion provided by this Module must happen before the expansion in Module
   96before(_).
 after(+Module) is det
Declaration to say that the expansion provided by this Module must happen after the expansion in Module
  103after(_).
  104
  105:- public implemented_pi/1.  106:- meta_predicate implemented_pi(:).  107implemented_pi(M:F/A) :-
  108    functor(H, F, A),
  109    % Can not use current_module/1 at this stage: --EMM
  110    once(predicate_property(M:H, visible)),
  111    \+ predicate_property(M:H, imported_from(_)).
  112
  113expansion_order(>, M1-_, M2-_) :-
  114    expansion_order_gt(M1, M2),
  115    !.
  116expansion_order(=, X, X) :- !.
  117
  118% Control the expansion orders via reexport, i.e., first the transformations in
  119% the current library and later the transformation in the reexported one.
  120expansion_order_gt(M1, M2) :-
  121    before(M2, M1), % let programmers decide
  122    !.
  123expansion_order_gt(M1, M2) :-
  124    before(M1, M2), % overrule reexport
  125    !,
  126    fail.
  127expansion_order_gt(M1, M2) :-
  128    module_property(M1, file(File)),
  129    current_op(1, fx, M1:'$compound_expand'),
  130    '$load_context_module'(File, M2, Options),
  131    option(reexport(true), Options),
  132    !.
  133expansion_order_gt(M, M2) :-
  134    '$load_context_module'(File1, M2, Options),
  135    option(reexport(true), Options),
  136    module_property(M1, file(File1)),
  137    current_op(1, fx, M1:'$compound_expand'),
  138    expansion_order_gt(M, M1),
  139    !.
  140
  141collect_expansor(EM, ExpansorName) -->
  142    ( {implemented_pi(EM:ExpansorName)}
  143    ->[ExpansorName]
  144    ; []
  145    ).
  146
  147collect_expansors(ExpansorNameL, M, ML) :-
  148    findall(EM-PIL,
  149            ( expansion_module(M, EM),
  150              foldl(collect_expansor(EM), ExpansorNameL, PIL, []),
  151              PIL \= []
  152            ), MU),
  153    partsort(expansion_order, MU, ML).
 init_expansors is det
Declaration to say that the compound expansion definition has finish and now the expansions can begin. This is required to speed up the compilation, since the predicate collect_expansors/3 is expensive but its solution doesn't change once no more expansors are added.
  163init_expansors.
  164
  165no_more_expansions_after_init(Source) :-
  166    member(Expansors,
  167           [[term_expansion/4, term_expansion/2],
  168            [goal_expansion/4, goal_expansion/2]]),
  169    collect_expansors(Expansors, Source, TN),
  170    ( '$defined_predicate'(Source:'$module_expansors'(_, _, _))
  171    ->Source:'$module_expansors'(Expansors, TL, []),
  172      subtract(TN, TL, EL),
  173      EL \= [],
  174      print_message(warning, format("More expansors added after :- init_expansors declaration: ~w", [EL]))
  175    ; TN \= []
  176    ->print_message(warning, format("Missing :- init_expansors declaration, but expansors present: ~w", [TN]))
  177    ).
  178system:term_expansion(end_of_file, _) :-
  179    '$current_source_module'(Source),
  180    module_property(Source, file(File)),
  181    prolog_load_context(source, File),
  182    no_more_expansions_after_init(Source),
  183    fail.
  184/* NOTE: this is commented out to let expansions in module qualified literals
  185 * work, since you need to keep the expansions available --EMM
  186
  187stop_expansors :-
  188    '$current_source_module'(Source),
  189    abolish(Source:'$module_expansors'/3).
  190
  191system:term_expansion(end_of_file, _) :-
  192    '$current_source_module'(Source),
  193    module_property(Source, file(File)),
  194    prolog_load_context(source, File),
  195    stop_expansors,
  196    fail.
  197*/
  198system:term_expansion(:- before(B), compound_expand:before(A, B)) :-
  199    '$current_source_module'(A).
  200system:term_expansion(:- after( B), compound_expand:before(B, A)) :-
  201    '$current_source_module'(A).
  202system:term_expansion((:- init_expansors), []) :-
  203    '$current_source_module'(Source),
  204    dynamic(Source:'$module_expansors'/3),
  205    public(Source:'$module_expansors'/3),
  206    retractall(Source:'$module_expansors'(_, _, _)),
  207    forall(member(Expansors,
  208                  [[term_expansion/4, term_expansion/2],
  209                   [goal_expansion/4, goal_expansion/2]]),
  210           ( collect_expansors(Expansors, Source, TH),
  211             '$append'(TH, TT, TL),
  212             assertz(Source:'$module_expansors'(Expansors, TL, TT))
  213           ))