predicate_options.pl -- Access and analyse predicate options
This module provides the developers interface for the directive
predicate_options/3. This directive allows us to specify that, e.g.,
open/4 processes options using the 4th argument and supports the option
type
using the values text
and binary
. Declaring options that are
processed allows for more reliable handling of predicate options and
simplifies porting applications. This library provides the following
functionality:
- Query supported options through current_predicate_option/3 or current_predicate_options/3. This is intended to support conditional compilation and an IDE.
- Derive additional declarations through dataflow analysis using derive_predicate_options/0.
- Perform a compile-time analysis of the entire loaded program using check_predicate_options/0.
Below, we describe some use-cases.
- Quick check of a program
-
This scenario is useful as an occasional check or to assess problems
with option-handling for porting an application to SWI-Prolog. It
consists of three steps: loading the program (1 and 2), deriving
option handling for application predicates (3) and running the
checker (4).
1 ?- [load]. 2 ?- autoload. 3 ?- derive_predicate_options. 4 ?- check_predicate_options.
- Add declarations to your program
-
Adding declarations about option processes improves the quality of
the checking. The analysis of derive_predicate_options/0 may miss
options and does not derive the types for options that are processed
in Prolog code. The process is similar to the above. In steps 4 and
further, the inferred declarations are listed, inspected and added to
the source code of the module.
1 ?- [load]. 2 ?- autoload. 3 ?- derive_predicate_options. 4 ?- derived_predicate_options(module_1). 5 ?- derived_predicate_options(module_2). 6 ?- ...
- Declare option processing requirements
-
If an application requires that open/4 needs to support
lock(write)
, it may do so using the directive below. This directive raises an exception when loaded on a Prolog implementation that does not support this option.:- current_predicate_option(open/4, 4, lock(write)).
- predicate_options(:PI, +Arg, +Options) is det
- Declare that the predicate PI processes options on Arg. Options
is a list of options processed. Each element is one of:
- Option(ModeAndType) PI processes Option. The option-value must comply to ModeAndType. Mode is one of + or - and Type is a type as accepted by must_be/2.
- pass_to(:PI,Arg) The option-list is passed to the indicated predicate.
Below is an example that processes the option
header(boolean)
and passes all options to open/4::- predicate_options(write_xml_file/3, 3, [ header(boolean), pass_to(open/4, 4) ]). write_xml_file(File, XMLTerm, Options) :- open(File, write, Out, Options), ( option(header(true), Option, true) -> write_xml_header(Out) ; true ), ...
This predicate may only be used as a directive and is processed by expand_term/2. Option processing can be specified at runtime using assert_predicate_options/3, which is intended to support program analysis.
- assert_predicate_options(:PI, +Arg, +Options, ?New) is semidet
- As predicate_options(:PI, +Arg, +Options). New is a boolean
indicating whether the declarations have changed. If New is
provided and
false
, the predicate becomes semidet and fails without modifications if modifications are required. - current_option_arg(:PI, ?Arg) is nondet
- True when Arg of PI processes predicate options. Which options are processed can be accessed using current_predicate_option/3.
- current_predicate_option(:PI, ?Arg, ?Option) is nondet
- True when Arg of PI processes Option. For example, the following
is true:
?- current_predicate_option(open/4, 4, type(text)). true.
This predicate is intended to support conditional compilation using if/1 ... endif/0. The predicate current_predicate_options/3 can be used to access the full capabilities of a predicate.
- check_predicate_option(:PI, +Arg, +Option) is det
- Verify predicate options at runtime. Similar to current_predicate_option/3, but intended to support runtime checking.
- current_predicate_options(:PI, ?Arg, ?Options) is nondet
- True when Options is the current active option declaration for PI on Arg. See predicate_options/3 for the argument descriptions. If PI is ground and refers to an undefined predicate, the autoloader is used to obtain a definition of the predicate.
- derived_predicate_options(:PI, ?Arg, ?Options) is nondet
- Derive option arguments using static analysis. True when Options is the current derived active option declaration for PI on Arg.
- expand_pass_to_options(+OptionsIn, +Module, -OptionsOut)// is det[private]
- Expand the options of
pass_to(PI,Arg)
if PI does not refer to a public predicate. - derived_predicate_options(+Module) is det
- Derive predicate option declarations for a module. The derived
options are printed to the
current_output
stream. - retractall_predicate_options is det
- Remove all dynamically (derived) predicate options.
- check_predicate_options is det
- Analyse loaded program for erroneous options. This predicate decompiles the current program and searches for calls to predicates that process options. For each option list, it validates whether the provided options are supported and validates the argument type. This predicate performs partial dataflow analysis to track option-lists inside a clause.
- derive_predicate_options is det
- Derive new predicate option declarations. This predicate analyses the loaded program to find clauses that process options using one of the predicates from library(option) or passes options to other predicates that are known to process options. The process is repeated until no new declarations are retrieved.
- check_predicate_options(:PredicateIndicator) is det
- Verify calls to predicates that have options in all clauses of the predicate indicated by PredicateIndicator.
- check_clause(+Clause, +Module, +Ref, +Action) is det[private]
- Action is one of
- decl
- Create additional declarations
- check
- Produce error messages
- check_body(+Body, +Module, +TermPos, +Action)[private]
- check_called_by(+CalledBy, +M, +Action) is det[private]
- Handle results from prolog:called_by/2.
- check_options(:Predicate, +OptionArg, +Options, +ArgPos, +Action)[private]
- Verify the list Options, that is passed into Predicate on
argument OptionArg. ArgPos is a term-position term describing
the location of the Options list. If Options is a partial list,
the tail is annotated with
pass_to(PI, OptArg)
. - annotate(+Var, +Term) is det[private]
- Use constraints to accumulate annotations about variables. If two annotated variables are unified, the attributes are joined.
- option_decl(:Head, +Action) is det[private]
- Add new declarations based on attributes left by the analysis pass. We do not add declarations for system modules or modules that already contain static declarations.
- resolve_module(:PI, -DefPI) is det[private]
- Find the real predicate indicator pointing to the definition module of PI. This is similar to using predicate_property/3 with the property imported_from, but using '$get_predicate_attribute'/3 avoids auto-importing the predicate.