1/*  Part of Assertion Reader for SWI-Prolog
    2
    3    Author:        Edison Mera
    4    E-mail:        efmera@gmail.com
    5    WWW:           https://github.com/edisonm/assertions
    6    Copyright (C): 2017, 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(assertions,
   36          [asr_head/2,
   37           assrt_type/1,
   38           assrt_status/1,
   39           expand_assertion/4,
   40           asr_head_prop/8,
   41           curr_prop_asr/4,
   42           asr_aprop/4,
   43           aprop_asr/4,
   44           prop_asr/4,
   45           prop_asr/7,
   46           current_decomposed_assertion_1/12,
   47           decompose_assertion_head_body/13]).   48
   49:- discontiguous '$exported_op'/3.   50:- reexport(library(compound_expand)).   51:- reexport(library(assertions_op)).   52:- use_module(library(extend_args)).   53:- use_module(library(apply)).   54:- use_module(library(pairs)).   55:- use_module(library(extra_messages), []).   56:- use_module(library(filepos_line)).   57:- use_module(library(neck)).   58:- use_module(library(termpos)).   59:- use_module(library(lists)).   60:- use_module(library(list_sequence)).   61:- use_module(library(prolog_codewalk), []).   62:- init_expansors.

Assertion reader for SWI-Prolog

@note: asr_* declared multifile to allow extensibility. At this point you extend concrete assertions (not abstractions or fake ones, since they will be collected by the run-time checker, for instance)

@note: Next syntax is ambiguous, but shorter:

  :- det callable.

is equivalent to:

  :- true prop callable/1 is det

but can be confused with:

:- true prop det(callable)
:- true prop det(X) :: callable(X).

in any case this is syntax sugar so we can always write the long version of the assertion to avoid ambiguities

*/

   90:- multifile
   91    asr_head_prop/8,
   92    asr_comm/3,
   93    asr_glob/4,
   94    asr_comp/4,
   95    asr_call/4,
   96    asr_succ/4,
   97    doc_db/4,
   98    nodirective_error_hook/1.   99
  100% asr_* declared dynamic to facilitate cleaning
  101:- dynamic
  102    asr_head_prop/8,
  103    asr_comm/3,
  104    asr_glob/4,
  105    asr_comp/4,
  106    asr_call/4,
  107    asr_succ/4,
  108    doc_db/4,
  109    nodirective_error_hook/1.  110
  111%  These predicates are intended not to be called directly by user-applications:
  112
  113:- public
  114       asr_comm/3,
  115       asr_comp/4,
  116       asr_call/4,
  117       asr_succ/4,
  118       asr_glob/4.
 asr_head(?Asr, ?Head) is det
Extract the Head for a given assertion identifier. Note that the first and second arguments of the Assertion identifier should contain the module and the head respectively.
  126asr_head(Asr, M:Head) :-
  127    ( nonvar(Asr)
  128    ->arg(1, Asr, M),
  129      arg(2, Asr, Head)
  130    ; true
  131    ).
  132
  133curr_prop_asr(head, M:P, From, Asr) :- asr_head_prop(Asr, M, P, _, _, _, _, From).
  134curr_prop_asr(stat,   P, From, Asr) :- asr_head_prop(Asr, _, _, P, _, _, _, From).
  135curr_prop_asr(type,   P, From, Asr) :- asr_head_prop(Asr, _, _, _, P, _, _, From).
  136curr_prop_asr(dict,   D, From, Asr) :- asr_head_prop(Asr, _, _, _, _, D, _, From).
  137curr_prop_asr(comm,   C, From, Asr) :- asr_comm(Asr,    C, From).
  138curr_prop_asr(comp, M:P, From, Asr) :- asr_comp(Asr, M, P, From).
  139curr_prop_asr(call, M:P, From, Asr) :- asr_call(Asr, M, P, From).
  140curr_prop_asr(succ, M:P, From, Asr) :- asr_succ(Asr, M, P, From).
  141curr_prop_asr(glob, M:P, From, Asr) :- asr_glob(Asr, M, P, From).
 asr_aprop(+Asr, +Key, ?Prop, -From)
Extensible accessor to assertion properties, ideal to have different views of assertions, to extend the assertions or to create ancillary assertions (see module assrt_meta.pl for an example). The first argument is wrapped to facilitate indexing. Note that it is recommended that multiple clauses of this predicate be mutually exclusive.
  151:- multifile asr_aprop/4.  152
  153prop_asr(H, M, Stat, Type, Dict, From, Asr) :-
  154    asr_head_prop(Asr, C, H, Stat, Type, Dict, _, From),
  155    predicate_property(C:H, implementation_module(IM)),
  156    match_modules(H, M, IM).
  157
  158match_modules(_, M, M) :- !.
  159match_modules(H, M, IM) :- predicate_property(M:H, implementation_module(IM)).
  160
  161:- meta_predicate
  162       prop_asr(?, 0, +, +),
  163       aprop_asr(?, 0, +, +).  164
  165prop_asr(Key, M:P, From, Asr) :-
  166    curr_prop_asr(Key, C:P, From, Asr),
  167    predicate_property(C:P, implementation_module(IM)),
  168    match_modules(P, M, IM).
  169
  170aprop_asr(Key, M:P, From, Asr) :-
  171    asr_aprop(Asr, Key, C:P, From),
  172    predicate_property(C:P, implementation_module(IM)),
  173    match_modules(P, M, IM).
  174
  175add_arg(_, G1, G2, _, _) :-
  176    var(G1),
  177    var(G2),
  178    !,
  179    assertion(fail),
  180    fail.
  181add_arg(H, G1, G2, PPos1, PPos2) :-
  182    \+ ( var(PPos1),
  183         var(PPos2)
  184       ),
  185    PPos1 = parentheses_term_position(From, To, Pos1),
  186    PPos2 = parentheses_term_position(From, To, Pos2),
  187    !,
  188    add_arg(H, G1, G2, Pos1, Pos2).
  189add_arg(H, M:G1, M:G2,
  190        term_position(From, To, FFrom, FTo, [MPos, Pos1]),
  191        term_position(From, To, FFrom, FTo, [MPos, Pos2])) :-
  192    !,
  193    add_arg(H, G1, G2, Pos1, Pos2).
  194add_arg(H, G1, G2, Pos1, Pos2) :-
  195    ( var(Pos1)
  196    ->true
  197    ; ( Pos1 = term_position(From, To, FFrom, FTo, PosL1)
  198      ->( nonvar(PosL1)
  199        ->append(PosL1, [0-0 ], PosL)
  200        ; true
  201        )
  202      ; Pos1 = From-To
  203      ->FFrom = From,
  204        FTo = To,
  205        PosL = [0-0 ]
  206      ),
  207      Pos2 = term_position(From, To, FFrom, FTo, PosL)
  208    ),
  209    extend_args(G1, [H], G2).
 decompose_assertion_body(+Body, +Pos, -Head, -Compat, -Call, -Succ, -Glob, Comment, -HPos, -CmpPos, -CPos, -SPos, -GPos, -ComPos)
Extract the different sections from the Body of an assertion. Note that this is expanded during compilation to contain extra arguments with the term locations for each section of the assertion from:
  decompose_assertion_body(+Body, +Pos, -Head, -Compat, -Call, -Succ, -Glob, Comment)

SWI-Extensions with respect to the Ciao Assertion Language:

  225:- add_termpos(decompose_assertion_body(+, -, -, -, -)).  226:- add_termpos(decompose_assertion_body(+, -, -, -, -, -)).  227:- add_termpos(decompose_assertion_body(+, -, -, -, -, -, -)).  228
  229% ----------------------- AB C  D    E- -AB--C-----D-----E----- %CDE
  230decompose_assertion_body((AB:C=>D  + E), AB, C,    D,    E   ) :- valid_cp(C). %111
  231decompose_assertion_body((AB:C=>D is E), AB, C,    D,    E   ) :- valid_cp(C). %111
  232% WARNING: Next is ambiguous if AB is a module-qualification, use [Module:Head] => D to deambiguate
  233decompose_assertion_body((AB:C=>D     ), AB, C,    D,    true) :- valid_cp(C). %110
  234decompose_assertion_body((AB:C     + E), AB, C,    true, E   ) :- valid_cp(C). %101
  235decompose_assertion_body((AB:C    is E), AB, C,    true, E   ) :- valid_cp(C). %101
  236decompose_assertion_body((AB:C        ), AB, C,    true, true) :- valid_cp(C). %100
  237decompose_assertion_body((AB  =>D  + E), AB, true, D,    E   ). %011
  238decompose_assertion_body((AB  =>D is E), AB, true, D,    E   ). %011
  239decompose_assertion_body((AB  =>D     ), AB, true, D,    true). %010
  240decompose_assertion_body((AB       + E), AB, true, true, E   ). %001
  241decompose_assertion_body((AB      is E), AB, true, true, E   ). %001
  242decompose_assertion_body((AB          ), AB, true, true, true). %000
  243
  244decompose_assertion_body((BO#F), A, B, C, D, E, F ) :- decompose_assertion_body(BO, A, B, C, D, E).
  245decompose_assertion_body(BO,     A, B, C, D, E, "") :- decompose_assertion_body(BO, A, B, C, D, E).
  246
  247decompose_assertion_body((A::BO), A, B,    C, D, E) :- decompose_assertion_body(BO, B, C, D, E).
  248decompose_assertion_body(BO,      A, true, C, D, E) :- decompose_assertion_body(BO, A, C, D, E).
  249
  250valid_cp(C) :- \+ invalid_cp(C).
  251
  252invalid_cp(_/_).
 validate_body_sections(+Type:assrt_type, -Compat:list(pair), -Calls:list(pair), -Success:list(pair), -Global:list(pair), -MustBeEmpty:list(pair), -MustNotBeEmpty:list(pair)) is det
Unifies MustBeEmpty with a list of sections that must be empty, and MustNotBeEmpty with a list of sections that must not be empty. The elements of both lists are pairs like Section-List, where section could be compatibility, preconditions, postconditions or global.
  261validate_body_sections(pred,     _,  _,     _,  _, [], []).
  262validate_body_sections(prop,     _,  _,     _,  _, [], []).
  263validate_body_sections(calls,   Cp, Ca,    Su, Gl,
  264                       [compatibility-Cp, postconditions-Su, globalprops-Gl],
  265                       [preconditions-Ca]).
  266validate_body_sections(success, Cp,  _,    Su, Gl,
  267                       [compatibility-Cp, globalprops-Gl],
  268                       [postconditions-Su]).
  269validate_body_sections(comp,    Cp,  _,    Su, Gl,
  270                       [compatibiltiy-Cp, postconditions-Su],
  271                       [globalprops-Gl]).
 assrt_type(Type)
The type of assertion, could have the following values:

calls - Specifies the properties at call time.

success - Specifies the properties on success, but only for external calls.

comp - Assertion type comp, specifies computational or global properties.

prop - States that the predicate is a property

pred - Union of calls, success and comp assertion types

  287assrt_type(Type) :-
  288    validate_body_sections(Type, _, _, _, _, _, _),
  289    neck.
 assrt_status(Status)
The status of an assertion. Be careful, since they are not compatible with those found in Ciao-Prolog. Could have the following values:

check - Assertion should be checked statically or with the rtcheck tracer (default)

true - Assertion is true, provided by the user

false - Assertion is false, provided by the user (not implemented yet)

debug - Assertion should be checked only at development time

static - Assertion is always instrumented in the code via a wrapper, in other words, it is considered part of the implementation.

@note: For static, such instrumentation can be removed only if a static analysis prove it is always true (not implemented yet).

To be done
- : The next are intended to be used internally, once the system be able to infer new assertions:

right: inferred by the static analysis trust: Ciao-Prolog like, provided by the user fail: false, inferred by the static analyss.

  318assrt_status(check).
  319assrt_status(true).
  320assrt_status(false).
  321assrt_status(debug).
  322assrt_status(static).
  323
  324:- add_termpos(decompose_status_and_type_1(+,?,?,-)).  325:- add_termpos(decompose_status_and_type(+,?,?,-)).  326
  327decompose_status_and_type_1(Assertions, check, AssrtType, UBody) :-
  328    assrt_type(AssrtType),
  329    Assertions =.. [AssrtType, UBody],
  330    neck.
  331decompose_status_and_type_1(Assertions, AssrtStatus, AssrtType, UBody) :-
  332    assrt_type(AssrtType),
  333    Assertions =.. [AssrtType, AssrtStatus, UBody],
  334    neck.
  335
  336decompose_status_and_type(Assertions, APos, AssrtStatus, AssrtType, UBody, BPos) :-
  337    cleanup_parentheses(APos, Pos),
  338    decompose_status_and_type_1(Assertions, Pos, AssrtStatus, AssrtType, UBody, BPos),
  339    assrt_status(AssrtStatus).
  340
  341cleanup_parentheses(Pos1, Pos) :-
  342    nonvar(Pos1),
  343    Pos1 = parentheses_term_position(_, _, Pos2),
  344    !,
  345    cleanup_parentheses(Pos2, Pos).
  346cleanup_parentheses(Pos, Pos).
  347
  348% To Avoid attempts to execute asertions (must be declarations):
  349
  350:- assrt_type(Type),
  351   member(Arity, [1, 2]),
  352   neck,
  353   export(Type/Arity).  354
  355Assr :-
  356    decompose_status_and_type_1(Assr, _, Status, Type, _, _),
  357    functor(Assr, Type, Arity),
  358    Body1 = ignore(nodirective_error_hook(Assr)),
  359    ( Arity = 1
  360    ->Body = Body1
  361    ; Body = (assrt_status(Status), Body1)
  362    ),
  363    neck,
  364    Body.
  365
  366is_decl_global(Head, M) :-
  367    is_decl_global(Head, _, _, M).
  368
  369is_decl_global(Head, Status, Type, M) :-
  370    forall(Head = HM:_, (var(HM);atom(HM))),
  371    prop_asr(head, M:Head, _, Asr),
  372    ( ( prop_asr(glob, metaprops:declaration(Status, _), _, Asr)
  373      ; Status = true,
  374        prop_asr(glob, metaprops:declaration(_), _, Asr)
  375      )
  376    ->( prop_asr(glob, metaprops:global(Type, _), _, Asr)
  377      ; Type = (pred)
  378      )
  379    ; ( prop_asr(glob, metaprops:global(Type, _), _, Asr)
  380      ; Type = (pred),
  381        prop_asr(glob, metaprops:global(_), _, Asr)
  382      ),
  383      Status = true
  384    ),
  385    !.
  386
  387:- add_termpos(current_decomposed_assertion(+,?,?,?,?,?,?,?,?,-,?)).  388:- add_termpos(current_decomposed_assertion_1(+,?,?,?,-,?,?,-,?)).  389:- add_termpos(current_decomposed_assertion_2(+,?,?,?,-,?,?,-,?)).  390:- add_termpos(propdef(+,?,?,?)).  391:- add_termpos(merge_comments(-,-,+)).  392:- add_termpos(decompose_assertion_head_body(+,?,?,+,?,?,?,?,-,?)).  393:- add_termpos(decompose_assertion_head_body_(+,?,?,+,?,?,?,?,-,?)).  394:- add_termpos(decompose_assertion_head(+,?,?,+,-,?,?,?,?,?)).  395:- add_termpos(decompose_assertion_head_(+,?,?,+,-,?,?,?,?,?)).  396
  397merge_comments("",  C,       C).
  398merge_comments(C,  "",       C).
  399merge_comments(C1, C2, [C1, C2]).
  400
  401current_decomposed_assertion(Assertions, PPos, M, Pred, Status, Type, Cp, Ca, Su, Gl, Co, CoPos, RPos) :-
  402    current_decomposed_assertion_1(Assertions, PPos, M, Status, Type, BodyS, BSPos, Gl, Gl2, Co, CoPos, RPos),
  403    decompose_assertion_head_body(BodyS, BSPos, M, Pred, true, _, Cp, Ca, Su, Gl2, Co, CoPos, RPos),
  404    validate_body_sections(Type, Cp, Ca, Su, Gl, MustBeEmpty, MustNotBeEmpty),
  405    maplist(report_must_be_empty(Type), MustBeEmpty),
  406    maplist(report_must_not_be_empty(Type, RPos), MustNotBeEmpty).
  407
  408current_decomposed_assertion_1(Assertions, PPos, M, Status, Type, BodyS, BSPos, Gl1, Gl, Co, CoPos, RPos) :-
  409    cleanup_parentheses(PPos, APos),
  410    current_decomposed_assertion_2(Assertions, APos, M, Status, Type, BodyS, BSPos, Gl1, Gl, Co, CoPos, RPos).
  411
  412current_decomposed_assertion_2(AssertionsBGl, M, Status, Type, BodyS, Gl1, Gl, Co, RPos) :-
  413    member(AssertionsBGl, [Assertions  + BGl,
  414                           Assertions is BGl]),
  415    necki,
  416    propdef(BGl, M, Gl1, Gl2),
  417    current_decomposed_assertion_1(Assertions, M, Status, Type, BodyS, Gl2, Gl, Co, RPos).
  418current_decomposed_assertion_2(Assertions # Co2, M, Status, Type, BodyS, Gl1, Gl, Co, RPos) :-
  419    !,
  420    current_decomposed_assertion_1(Assertions, M, Status, Type, BodyS, Gl1, Gl, Co1, RPos),
  421    once(merge_comments(Co1, Co2, Co)).
  422current_decomposed_assertion_2(Assertions, M, Status, Type, BodyS, Gl1, Gl, Co, RPos) :-
  423    ( is_decl_global(Assertions, DStatus, DType, M)
  424    ->once(decompose_status_and_type_1(Term, DStatus, DType, Assertions)),
  425      current_decomposed_assertion_2(Term, M, Status, Type, BodyS, Gl1, Gl, Co, RPos)
  426    ; decompose_status_and_type(Assertions, Status, Type, BodyS),
  427      Gl = Gl1
  428    ).
  429
  430report_must_be_empty(Type, Section-Props) :-
  431    maplist(report_must_be_empty(Type, Section), Props).
  432
  433termpos_location(Pos, Loc) :-
  434    ignore(source_location(File, Line)),
  435    ( nonvar(File)
  436    ->( nonvar(Pos)
  437      ->Loc = file_term_position(File, Pos)
  438      ; nonvar(Line)
  439      ->Loc = file(File, Line, -1, _)
  440      ; true
  441      )
  442    ; true
  443    ).
  444
  445report_must_be_empty(Type, Section, Prop-Pos) :-
  446    termpos_location(Pos, Loc),
  447    print_message(
  448        warning,
  449        at_location(
  450            Loc,
  451            format("In '~w' assertion, '~w' section, '~w' will be ignored",
  452                   [Type, Section, Prop]))).
  453
  454report_must_not_be_empty(Type, Pos, Section-Prop) :-
  455    ( Prop = []
  456    ->termpos_location(Pos, Loc),
  457      print_message(
  458          warning,
  459          at_location(
  460              Loc,
  461              format("In '~w' assertion, missing properties in '~w' section",
  462                     [Type, Section])))
  463    ; true
  464    ).
  465
  466combine_pi_comp(N, Head, PosL1, PosL, BCp, PCp) :-
  467    cleanup_parentheses(PCp, Pos),
  468    combine_pi_comp_(N, Head, PosL1, PosL, BCp, Pos).
  469
  470combine_pi_comp_(N1, Head, PosL1, PosL, (H * P), term_position(_, _, _, _, [TPos, Pos])) :-
  471    arg(N1, Head, P),
  472    !,
  473    succ(N, N1),
  474    combine_pi_comp(N, Head, [Pos|PosL1], PosL, H, TPos).
  475combine_pi_comp_(N, Head, PosL, [Pos|PosL], P, Pos) :-
  476    arg(N, Head, P).
  477
  478% EMM: Support for grouped properties
  479
  480merge_props(BCp1, _,    BCp,  PCp, BCp, PCp) :- strip_module(BCp1, _, true).
  481merge_props(BCp,  PCp,  BCp2, _,   BCp, PCp) :- strip_module(BCp2, _, true).
  482merge_props(BCp1, PCp1, BCp2, PCp2, (BCp1, BCp2), term_position(_, _, _, _, [PCp1, PCp2])).
  483
  484decompose_assertion_head_body(B, P1, M, Pred, BCp, PCp, Cp, Ca, Su, Gl, Co, CoPos, RPos) :-
  485    cleanup_parentheses(P1, P),
  486    decompose_assertion_head_body_(B, P, M, Pred, BCp, PCp, Cp, Ca, Su, Gl, Co, CoPos, RPos).
  487
  488decompose_assertion_head_body_(Var, _, _, _, _, _, _, _, _, _) :-
  489    var(Var),
  490    !,
  491    fail.
  492decompose_assertion_head_body_((B1, B2), M, Pred, BCp, Cp, Ca, Su, Gl, Co, RPos) :-
  493    !,
  494    ( decompose_assertion_head_body(B1, M, Pred, BCp, Cp, Ca, Su, Gl, Co, RPos)
  495    ; decompose_assertion_head_body(B2, M, Pred, BCp, Cp, Ca, Su, Gl, Co, RPos)
  496    ).
  497decompose_assertion_head_body_([B1|B2], M,
  498                              Pred, BCp, Cp, Ca, Su, Gl, Co, RPos) :-
  499    !,
  500    ( decompose_assertion_head_body(B1, M, Pred, BCp, Cp, Ca, Su, Gl, Co, RPos)
  501    ; decompose_assertion_head_body(B2, M, Pred, BCp, Cp, Ca, Su, Gl, Co, RPos)
  502    ).
  503decompose_assertion_head_body_(M:Body, CM, Pred, BCp, Cp, Ca, Su, Gl, Co, RPos) :-
  504    atom(M),
  505    callable(Body),
  506    !,
  507    % Switching the body context does not implies switching the context of the
  508    % compatibility properties, that is why CM should be preserved in the
  509    % compatibility section:
  510    decompose_assertion_head_body(Body, M, Pred, CM:BCp, Cp, Ca, Su, Gl, Co, RPos).
  511decompose_assertion_head_body_([], _, _, _, _, _, _, _, _, _) :-
  512    !,
  513    fail.
  514decompose_assertion_head_body_(Body, BPos, M, Pred, BCp, PCp, Cp, Ca, Su, Gl, Co, CoPos, RPos) :-
  515    is_decl_global(Body, M),
  516    Body =.. [F, Head|GArgL],
  517    nonvar(Head),
  518    !,
  519    ( nonvar(BPos)
  520    ->BPos = term_position(_, _, FF, FT, [HPos|PArgL]),
  521      NPos = term_position(_, _, _, _, [HPos, term_position(_, _, FF, FT, [PArgL])])
  522    ; true
  523    ),
  524    BGl =.. [F|GArgL],
  525    decompose_assertion_head_body(Head + BGl, NPos, M, Pred, BCp, PCp, Cp, Ca, Su, Gl, Co, CoPos, RPos).
  526decompose_assertion_head_body_(Body, BPos, M, Pred, BCp2, PCp2, Cp, Ca, Su, Gl, Co, CoPos, RPos) :-
  527    ( once(decompose_assertion_body(Body, BPos, BH1, PH1, BCp1, PCp1,
  528                                    BCa, PCa, BSu, PSu, BGl, PGl, BCo, PCo)),
  529      Body \= BH1
  530    ->propdef(BGl, PGl, M, Gl, Gl1),
  531      once(merge_props(BCp1, PCp1, BCp2, PCp2, BCp, PCp)),
  532      decompose_assertion_head_body(BH1, PH1, M, Pred, BCp, PCp, Cp, Ca1, Su1, Gl1, BCo1, PCo1, RPos),
  533      apropdef(Pred, M, BCa, PCa, Ca, Ca1),
  534      apropdef(Pred, M, BSu, PSu, Su, Su1),
  535      once(merge_comments(BCo1, PCo1, BCo, PCo, Co, CoPos))
  536    ; decompose_assertion_head(Body, BPos, M, Pred, BCp2, PCp2, BCp, PCp, Cp1, Ca, Su, Gl, RPos),
  537      apropdef(Pred, M, BCp, PCp, Cp, Cp1),
  538      Co = ""
  539    ).
  540
  541decompose_assertion_head(Head, PPos, M, Pred, BCp1, PCp1, BCp, PCp, Cp, Ca, Su, Gl, HPos) :-
  542    cleanup_parentheses(PPos, Pos),
  543    decompose_assertion_head_(Head, Pos, M, Pred, BCp1, PCp1, BCp, PCp, Cp, Ca, Su, Gl, HPos).
  544
  545decompose_assertion_head_(M:H, _, P, BCp1, BCp, Cp, Ca, Su, Gl, RP) :-
  546    atom(M),
  547    !,
  548    decompose_assertion_head(H, M, P, BCp1, BCp, Cp, Ca, Su, Gl, RP).
  549decompose_assertion_head_(F/A, HPos, M, M:Pred, BCp1, PCp1, BCp, PCp, Cp, Ca, Su, Gl, Pos) :-
  550    !,
  551    atom(F),
  552    integer(A),
  553    functor(Head, F, A),
  554    ( once(( BCp1 = CM:BCp2,
  555             PCp1 = term_position(_, _, _, _, [_, PCp2])
  556           ; BCp1 = BCp2,
  557             PCp1 = PCp2,
  558             CM = M
  559           )),
  560      BCp2 \= true,
  561      combine_pi_comp(A, Head, [], PosL, BCp2, PCp2)
  562    ->( nonvar(HPos)
  563      ->HPos = term_position(From, To, _, _, [FPos, APos]),
  564        ( nonvar(FPos)
  565        ->arg(1, FPos, FFrom),
  566          arg(2, FPos, FTo)
  567        ; true
  568        )
  569      ; true
  570      ),
  571      decompose_assertion_head_(Head, term_position(From, To, FFrom, FTo, PosL), CM,
  572                                CM:Pred, true, APos, BCp, PCp, Cp, Ca, Su, Gl, Pos)
  573    ; Pred = Head,
  574      Cp = [],
  575      Ca = [],
  576      Su = [],
  577      Gl = [],
  578      HPos = Pos,
  579      BCp = BCp1,
  580      PCp = PCp1
  581    ).
  582decompose_assertion_head_(Head, Pos, M, M:Pred, BCp, PCp, BCp, PCp, Cp, Ca, Su, Gl, Pos) :-
  583    compound(Head),
  584    !,
  585    functor(Head, F, A),
  586    functor(Pred, F, A),
  587    ignore(Pos = term_position(_, _, _, _, PosL)),
  588    decompose_args(PosL, 1, Head, M, Pred, Cp, Ca, Su, Gl).
  589decompose_assertion_head_(Head, Pos, M, M:Head, BCp, PCp, BCp, PCp, [], [], [], [], Pos) :-
  590    atom(Head).
  591
  592decompose_args([Pos|PosL], N1, Head, M, Pred, Cp1, Ca1, Su1, Gl1) :-
  593    arg(N1, Head, HArg),
  594    !,
  595    resolve_types_modes(HArg, Pos, M, PArg, Cp1, Ca1, Su1, Gl1, Cp2, Ca2, Su2, Gl2),
  596    arg(N1, Pred, PArg),
  597    succ(N1, N),
  598    decompose_args(PosL, N, Head, M, Pred, Cp2, Ca2, Su2, Gl2).
  599decompose_args([], _, _, _, _, [], [], [], []).
  600
  601:- add_termpos(resolve_types_modes_(+,?,?,?,?,?,?,?,?,?,?)).  602:- add_termpos(do_modedef(+,?,?,-,?,?,?,?,?,?,?,?,?,?)).  603:- add_termpos(modedef(+,?,?,-,?,?,?,?,?,?,?,?,?,?)).  604:- add_termpos(do_propdef(+,?,?,?,?)).  605:- add_termpos(hpropdef(+,?,?,?,?)).  606
  607resolve_types_modes(A,     _, _, A, Cp,  Ca,  Su,  Gl,  Cp, Ca, Su, Gl) :- var(A), !.
  608resolve_types_modes(A1, PPos, M, A, Cp1, Ca1, Su1, Gl1, Cp, Ca, Su, Gl) :-
  609    cleanup_parentheses(PPos, Pos),
  610    resolve_types_modes_(A1, Pos, M, A, Cp1, Ca1, Su1, Gl1, Cp, Ca, Su, Gl).
  611
  612resolve_types_modes_(A1:T, term_position(_, _, _, _, [PA1, PT]), M, A, Cp1, Ca1, Su1, Gl1, Cp, Ca, Su, Gl) :-
  613    do_propdef(T, PT, M, A, Pr1, Pr2),
  614    cleanup_parentheses(PA1, PA11),
  615    do_modedef(A1, PA11, M, A, A2, PA2, Cp1, Ca1, Su1, Gl1, Cp, Ca, Su, Gl, Pr1, Pr),
  616    !,
  617    do_propdef(A2, PA2, M, A, Pr2, Pr).
  618resolve_types_modes_(A1, M, A, Cp1, Ca1, Su1, Gl1, Cp, Ca, Su, Gl) :-
  619    do_modedef(A1, M, A, A2, Cp1, Ca1, Su1, Gl1, Cp, Ca, Su, Gl, Pr1, Pr),
  620    do_propdef(A2, M, A, Pr1, Pr).
  621
  622do_propdef(A,  _, A, Cp,  Cp) :- var(A), !.
  623do_propdef(A1, M, A, Cp1, Cp) :-
  624    hpropdef(A1, M, A, Cp1, Cp).
  625
  626do_modedef(A1, M, A, A2, Cp1, Ca1, Su1, Gl1, Cp, Ca, Su, Gl, Pr1, Pr) :-
  627    nonvar(A1),
  628    modedef(A1, M, A, A2, Cp1, Ca1, Su1, Gl1, Cp, Ca, Su, Gl, Pr1, Pr),
  629    !.
  630do_modedef(A1, APos, M, A, A2, PA1, Cp1, Ca1, Su1, Gl1, Cp, Ca, Su, Gl, Pr1, Pr) :-
  631    atom(A1),
  632    A3 =.. [A1, A],
  633    ( var(APos) -> true ; APos = From-To, Pos = term_position(From, To, From, To, [To-To]) ),
  634    modedef(A3, Pos, M, A, A2, PA1, Cp1, Ca1, Su1, Gl1, Cp, Ca, Su, Gl, Pr1, Pr),
  635    !.
  636do_modedef(A1, From-To, M, A, A2, PA1, Cp1, Ca1, Su1, Gl1, Cp, Ca, Su, Gl, Pr1, Pr) :-
  637    integer(A1),
  638    ( A1 >= 0
  639    ->A3 = goal_in(A1, A)
  640    ; A4 is -A1,
  641      A3 = goal_go(A4, A)
  642    ),
  643    modedef(A3, term_position(From, To, From, From, [From-From, From-To]), M, A, A2,
  644            PA1, Cp1, Ca1, Su1, Gl1, Cp, Ca, Su, Gl, Pr1, Pr),
  645    !.
  646do_modedef(A1, PA1, _, _, A1, PA1, Cp1, Ca, Su, Gl, Cp, Ca, Su, Gl, Cp1, Cp).
  647
  648:- add_termpos(put_prop(+,?,?,?)).  649
  650put_prop(_, Pos, Prop) --> [Prop-Pos].
  651
  652% Support for modes are hard-wired here:
  653% ISO Modes
  654modedef(+A, M, B, A, Cp, Ca1, Su, Gl, Cp, Ca, Su, Gl, Ca2, Ca) :-
  655    ( var(A), var(Ca2)
  656    ->put_prop(A, M:nonvar(B), Ca1, Ca2)
  657    ; Ca1 = Ca2
  658    ). % A bit confuse hack, Ca1 comes instantiated to optimize the expression
  659modedef(-A,   M, B, A, Cp,  Ca2, Su1, Gl,  Cp, Ca, Su, Gl, Su1, Su) :- put_prop(A, M:var(B), Ca2, Ca).
  660% Less restrictive - uses further instantiated:
  661% modedef(-(A),         _, A, B, Pos, PA, Cp,                       Ca,                Su1,  [(globprops:fi(B))-Pos|Gl], Cp, Ca, Su, Gl, Su1, Su) :- Pos = term_position(_, _, _, _, [PA]).
  662modedef(?A,   _, _, A, Cp1, Ca,  Su,  Gl,  Cp, Ca, Su, Gl, Cp1, Cp).
  663modedef(@(A), _, B, A, Cp1, Ca,  Su,  Gl1, Cp, Ca, Su, Gl, Cp1, Cp) :- put_prop(A, globprops:nfi(B), Gl1, Gl).
  664% PlDoc (SWI) Modes
  665modedef(:A1, Pos, M, B, A, PA, Cp, Ca1, Su, Gl, Cp, Ca, Su, Gl, Ca2, Ca) :-
  666    Pos = term_position(From, To, FFrom, FTo, [PA1]),
  667    % The first part of this check is not redundant if we forgot the meta_predicate declaration
  668    ( var(A1),
  669      var(Ca2)
  670    ->put_prop(A, Pos, M:mod_qual(B), Ca1, Ca2),
  671      A1 = A,
  672      PA = PA1
  673    ; Ca1 = Ca2,
  674      A = M:mod_qual(A1, B),
  675      PA = term_position(From, To, FFrom, FTo, [PA1, From-From])
  676    ).
  677modedef(goal_in(N,A), _, B, A, Cp,  Ca2,  Su,  Gl, Cp, Ca, Su, Gl, Ca1, Ca) :- put_prop(A, typeprops:goal(N,B), Ca2, Ca1).
  678modedef(goal_go(N,A), _, B, A, Cp,  Ca,   Su2, Gl, Cp, Ca, Su, Gl, Su1, Su) :- put_prop(A, typeprops:goal(N,B), Su2, Su1).
  679modedef(!A,           M, B, A, Cp1, Ca2,  Su,  Gl, Cp, Ca, Su, Gl, Cp1, Cp) :- put_prop(A, M:compound(B),       Ca2, Ca). % May be modified using setarg/3 or nb_setarg/3 (mutable)
  680% Ciao Modes:
  681modedef(in(A),  M, B, A, Cp,  Ca2, Su,  Gl,  Cp, Ca, Su, Gl, Ca1, Ca) :- put_prop(A, M:ground(B), Ca2, Ca1).
  682modedef(out(A), M, B, A, Cp,  Ca2, Su2, Gl,  Cp, Ca, Su, Gl, Su1, Su) :- put_prop(A, M:var(B), Ca2, Ca), put_prop(A, M:gnd(B), Su2, Su1).
  683modedef(go(A),  M, B, A, Cp1, Ca,  Su2, Gl,  Cp, Ca, Su, Gl, Cp1, Cp) :- put_prop(A, M:gnd(B), Su2, Su).
  684% Additional Modes (See Coding Guidelines for Prolog, Michael A. Covington, 2009):
  685modedef(*A,     M, B, A, Cp,  Ca2, Su,  Gl,  Cp, Ca, Su, Gl, Ca1, Ca) :- put_prop(A, M:ground(B), Ca2, Ca1).
  686modedef(=A,     _, B, A, Cp1, Ca,  Su,  Gl1, Cp, Ca, Su, Gl, Cp1, Cp) :- put_prop(A, globprops:nfi(B), Gl1, Gl). % The state of A is preserved
  687modedef(/A,     M, B, A, Cp,  Ca1, Su1, Gl1, Cp, Ca, Su, Gl, Su1, Su) :- put_prop(A, M:var(B), Ca1, Ca), put_prop(A, globprops:nsh(B), Gl1, Gl). % Like '-' but also A don't share with any other argument
  688modedef(>A,     _, _, A, Cp,  Ca,  Su1, Gl,  Cp, Ca, Su, Gl, Su1, Su). % Like output but A might be nonvar on entry
  689
  690% nfi == not_further_inst
  691% nsh == not_shared
  692
  693:- multifile prolog:error_message/3.  694
  695prolog:error_message(assertion(il_formed_assertion, Term)) -->
  696    [ 'Il formed assertion, check term ~w'-[Term]].
  697
  698hpropdef(A1, PA1, M, A, Cp1, Cp) :-
  699    term_variables(A1, V),
  700    ( member(X, V), X==A ->
  701      Cp1 = [(M:A1)-PA1|Cp]
  702    ; aprops_arg(A1, M, A, PA1, Cp1, Cp)
  703    ).
  704
  705apropdef_2(0, _, _, _, _) --> !, {fail}.
  706apropdef_2(N, Head, M, A, PPos) -->
  707    {cleanup_parentheses(PPos, Pos)},
  708    !,
  709    apropdef_2_(N, Head, M, A, Pos).
  710
  711apropdef_2_(1, Head, M, A, APos) -->
  712    {arg(1, Head, V)},
  713    !,
  714    hpropdef(A, APos, M, V).
  715apropdef_2_(N1, Head, M, (P * A), term_position(_, _, _, _, [PPos, APos])) -->
  716    {arg(N1, Head, V)},
  717    !,
  718    {succ(N, N1)},
  719    apropdef_2(N, Head, M, P, PPos),
  720    hpropdef(A, APos, M, V).
  721
  722apropdef(Var, _, _, _) --> {var(Var), !, fail}.
  723apropdef(_:Head, M, A, APos) -->
  724    {functor(Head, _, N)},
  725    apropdef_2(N, Head, M, A, APos),
  726    !.
  727apropdef(_, M, A, APos) --> propdef(A, APos, M).
  728
  729propdef(A, APos, M) --> props_args(A, M, push, APos).
  730
  731push(A, M, Pos) --> [(M:A)-Pos].
  732
  733aprops_arg(A, M, V, PPos) -->
  734    {cleanup_parentheses(PPos, Pos)},
  735    aprops_arg_(A, M, V, Pos).
  736
  737aprops_arg_({A}, M, V, brace_term_position(_, _, Pos)) --> !, aprops_args(A, M, V, Pos).
  738aprops_arg_(A,   M, V, Pos) --> aprops_args(A, M, V, Pos).
  739
  740aprops_args(A, M, V, Pos) --> props_args(A, M, prop_arg(V), Pos).
  741
  742:- meta_predicate props_args(?, ?, 5, ?, ?, ?).  743
  744props_args(true,   _, _, _) --> !, [].
  745props_args(A, M, V, PPos) -->
  746    {cleanup_parentheses(PPos, Pos)},
  747    !,
  748    props_args_(A, M, V, Pos).
  749
  750props_args_((A, B), M, V, term_position(_, _, _, _, [PA, PB])) -->
  751    !,
  752    props_args(A, M, V, PA),
  753    props_args(B, M, V, PB).
  754props_args_((A; B), M, V, Pos) -->
  755    !,
  756    { Pos = term_position(_, _, _, _, [PA, PB]),
  757      props_args(A, M, V, PA, P1, []),
  758      pairs_keys(P1, ML1),
  759      maplist(cleanup_mod(M), ML1, L1),
  760      list_sequence(L1, C1),
  761      props_args(B, M, V, PB, P2, []),
  762      pairs_keys(P2, ML2),
  763      maplist(cleanup_mod(M), ML2, L2),
  764      list_sequence(L2, C2)
  765    },
  766    [(M:(C1;C2))-Pos].
  767props_args_(M:A, _, V, term_position(_, _, _, _, [_, PA])) -->
  768    {atom(M)},
  769    !,
  770    props_args(A, M, V, PA).
  771props_args_(A, M, V, Pos) --> call(V, A, M, Pos).
  772
  773cleanup_mod(M, M:C, C) :- !.
  774cleanup_mod(_, MC, MC).
  775
  776prop_arg(V, A, M, Pos) -->
  777    {add_arg(V, A, P, Pos, PPos)},
  778    [(M:P)-PPos].
  779
  780expand_assertion_helper(Match, a(Match, Record, Pos), Record, Pos).
  781
  782expand_assertion(M, Dict, Decl, PPos, Records, RPos) :-
  783    cleanup_parentheses(PPos, Pos),
  784    !,
  785    expand_assertion_(M, Dict, Decl, Pos, Records, RPos).
  786
  787expand_assertion_(_, Dict, M:Decl, term_position(_, _, _, _, [_, DPos]),
  788                  Records, RPos) :-
  789    atom(M),
  790    !,
  791    expand_assertion(M, Dict, Decl, DPos, Records, RPos).
  792expand_assertion_(M, Dict, doc(Key, Doc),
  793                  term_position(From, To, FFrom, FTo, [KPos, DPos]),
  794                  assertions:doc_db(Key, M, Doc, Dict),
  795                  term_position(0, 0, 0, 0,
  796                                [0-0,
  797                                 term_position(From, To, FFrom, FTo,
  798                                               [KPos, 0-0, DPos, 0-0 ])])) :- !.
  799% Note: We MUST save the full location (File, HPos), because later we will not
  800% have access to source_location/2, and this will fails for further created
  801% clauses --EMM
  802expand_assertion_(CM, Dict, Assertions, APos, Records, RPos) :-
  803    Match=(Assertions-Dict),
  804    findall(a(Match, Clause, HPos),
  805            assertion_record_each(CM, Dict, Assertions, APos, Clause, HPos),
  806            ARecords),
  807    ARecords \= [],
  808    maplist(expand_assertion_helper(Match), ARecords, Records, RPos).
 assertion_record_each(CM, Dict, Assertions, APos, Clause, TermPos) is multi
Unifies clause with each one of the records that defines the assertion in the assertion database. Note that the last argument of Clause contains the locator, this is needed to get more precision, since the location is defined as file(File, Line, Pos, _), instead of the term '$source_location'(File,Line):Clause
  818assertion_record_each(CM, Dict, Assertions, APos, Clause, TermPos) :-
  819    ignore(source_location(File, Line1)),
  820    ( nonvar(File)
  821    ->Loc = file(File, Line, Pos, _),
  822      ( var(APos)
  823      ->Line = Line1,
  824        Pos = -1
  825      ; true
  826      )
  827    ; true
  828    ),
  829    current_decomposed_assertion(Assertions, APos, CM, M:Head, Status, Type, CpL, CaL, SuL, GlL, Co, CoPos, HPos),
  830    with_mutex('get_sequence_and_inc/1', get_sequence_and_inc(Count)),
  831    term_variables(t(Co, CpL, CaL, SuL, GlL), ShareL),
  832    atom_number(AIdx, Count),
  833    Asr =.. [AIdx, M, Head|ShareL], % Asr also contains variable bindings. By
  834                                    % convention, M is in the 1st position and
  835                                    % Head in the 2nd, to facilitate work
  836    ( Clause = assertions:asr_head_prop(Asr, M, Head, Status, Type, Dict, CM, Loc),
  837      SubPos = HPos,
  838      ( nonvar(SubPos)
  839      ->arg(1, SubPos, From),
  840        arg(2, SubPos, To),
  841        TermPos = term_position(From, To, From, To,
  842                                [SubPos, 0-0, 0-0, 0-0, _, _, _])
  843      ; true
  844      )
  845    ; Co \= "",
  846      Clause = assertions:asr_comm(Asr, Co, Loc),
  847      SubPos = CoPos,
  848      ( nonvar(SubPos)
  849      ->arg(1, SubPos, From),
  850        arg(2, SubPos, To),
  851        TermPos = term_position(From, To, From, To, [_, SubPos, _])
  852      ; true
  853      )
  854    ; ( Clause = assertions:AClause,
  855        member(AClause-PrL,
  856               [asr_comp(Asr, PM, Pr, Loc)-CpL,
  857                asr_call(Asr, PM, Pr, Loc)-CaL,
  858                asr_succ(Asr, PM, Pr, Loc)-SuL
  859               ]),
  860        member(MPr-SubPos, PrL),
  861        strip_module(MPr, PM, Pr)
  862      ; Clause = assertions:asr_glob(Asr, PM, Pr, Loc),
  863        member(MGl-GPos, GlL),
  864        strip_module(MGl, PM, Gl),
  865        add_arg(_, Gl, Pr, GPos, SubPos)
  866      ;
  867        once(( member(MGl-GPos, GlL),
  868               member(Gl, [declaration, declaration(_)]),
  869               strip_module(MGl, PM, Gl),
  870               add_arg(_, Gl, Pr, GPos, _),
  871               predicate_property(PM:Pr, implementation_module(metaprops)),
  872               functor(Head, Op, 1)
  873             )),
  874        Clause = (:- '$export_ops'([op(1125, fy, Op)], PM, File))
  875      ; member(MGl-_, GlL),
  876        member(Gl, [declaration,
  877                    declaration(_),
  878                    global,
  879                    global(_)]),
  880        strip_module(MGl, PM, Gl),
  881        add_arg(_, Gl, Pr, _, _),
  882        predicate_property(PM:Pr, implementation_module(metaprops))
  883      ->functor(Head, Fn, N),
  884        ( \+ predicate_property(M:Head, meta_predicate(_)),
  885          functor(Meta, Fn, N),
  886          Meta =.. [_|ArgL],
  887          once(append(ArgL1, [0], ArgL)),
  888          maplist(=(?), ArgL1),
  889          Clause = (:- meta_predicate Meta)
  890        )
  891      ),
  892      ( nonvar(SubPos)
  893      ->arg(1, SubPos, From),
  894        arg(2, SubPos, To),
  895        TermPos = term_position(From, To, From, To, [_, 0-0, SubPos, _])
  896      ; true
  897      )
  898    ),
  899    ( var(Pos),
  900      nonvar(File)
  901    ->( nonvar(SubPos),
  902        integer(From)
  903      ->filepos_line(File, From, Line, Pos)
  904      ; Line = Line1,
  905        Pos = -1
  906      )
  907    ; true
  908    ).
 expand_assertion(+Decl, DPos, -Records, RPos) is semidet
Process a Declaration as an assertion. This is called in a term_expansion/2 of the assertion module. Fails if Decl is not a valid assertion.
  915expand_assertion(Decl, DPos, Records, RPos) :-
  916    '$current_source_module'(M),
  917    expand_assertion(M, Dict, Decl, DPos, Records, RPos),
  918    % Dict Must be assigned after expand_assertion/6 to avoid performance
  919    % issues --EMM
  920    ( nb_current('$variable_names', Dict)
  921    ->true
  922    ; Dict = []
  923    ).
  924
  925:- dynamic sequence/1.  926sequence(1).
  927
  928get_sequence_and_inc(S) :-
  929    retract(sequence(S)),
  930    succ(S, S2),
  931    assertz(sequence(S2)).
  932
  933% ----------------------------------------------------------------------------
  934
  935term_expansion_decl(Decl, PPos, Records, RPos) :-
  936    nonvar(PPos),
  937    PPos = parentheses_term_position(_, _, Pos),
  938    !,
  939    term_expansion_decl(Decl, Pos, Records, RPos).
  940term_expansion_decl(Decl, PPos, Records, RPos) :-
  941    ( nonvar(PPos)
  942    ->PPos = term_position(_, _, _, _, [DPos])
  943    ; true
  944    ),
  945    expand_assertion(Decl, DPos, Records, RPos).
  946
  947term_expansion((:- Decl), DPos, Records, RPos) :-
  948    term_expansion_decl(Decl, DPos, Records, RPos)