login

4.3.1 Conditional compilation and program transformation

ISO Prolog defines no way for program transformations such as macro expansion or conditional compilation. Expansion through term_expansion/2 and expand_term/2 can be seen as part of the de-facto standard. This mechanism can do arbitrary translation between valid Prolog terms read from the source file to Prolog terms handed to the compiler. As term_expansion/2 can return a list, the transformation does not need to be term-to-term.

Various Prolog dialects provide the analogous goal_expansion/2 and expand_goal/2 that allow for translation of individual body terms, freeing the user of the task to disassemble each clause.

term_expansion(+Term1, -Term2)
Dynamic and multifile predicate, normally not defined. When defined by the user all terms read during consulting are given to this predicate. If the predicate succeeds Prolog will assert Term2 in the database rather than the read term (Term1). Term2 may be a term of the form ?- Goal. or :- Goal. Goal is then treated as a directive. If Term2 is a list, all terms of the list are stored in the database or called (for directives). If Term2 is of the form below, the system will assert Clause and record the indicated source location with it:
'$source_location'(<File>, <Line>):<Clause>

When compiling a module (see chapter 5 and the directive module/2), expand_term/2 will first try term_expansion/2 in the module being compiled to allow for term expansion rules that are local to a module. If there is no local definition, or the local definition fails to translate the term, expand_term/2 will try term_expansion/2 in module user. For compatibility with SICStus and Quintus Prolog, this feature should not be used. See also expand_term/2, goal_expansion/2 and expand_goal/2.

expand_term(+Term1, -Term2)
This predicate is normally called by the compiler on terms read from the input to perform preprocessing. It consists of four steps, where each step processes the output of the previous step.

  1. Test conditional compilation directives and translate all input to if we are in a `false branch' of the conditional compilation. See section 4.3.1.1.

  2. Call term_expansion/2. This predicate is first tried in the module that is being compiled and then in the module user.

  3. Call DCG expansion (dcg_translate_rule/2).

  4. Call expand_goal/2 on each body term that appears in the output of the previous steps.
goal_expansion(+Goal1, -Goal2)
Like term_expansion/2, goal_expansion/2 provides for macro expansion of Prolog source code. Between expand_term/2 and the actual compilation, the body of clauses analysed and the goals are handed to expand_goal/2, which uses the goal_expansion/2 hook to do user-defined expansion.

The predicate goal_expansion/2 is first called in the module that is being compiled, and then on the user module. If Goal is of the form Module:Goal where Module is instantiated, goal_expansion/2 is called on Goal using rules from module Module followed by user.

Only goals appearing in the body of clauses when reading a source file are expanded using this mechanism, and only if they appear literally in the clause, or as an argument to a defined meta-predicate that is annotated using `0' (see meta_predicate/1). Other cases need a real predicate definition.

expand_goal(+Goal1, -Goal2)
This predicate is normally called by the compiler to perform preprocessing using goal_expansion/2. The predicate computes a fixed-point by applying transformations until there are no more changes. If optimisation is enabled (see -O and optimise), expand_goal/2 simplifies the result by removing unneeded calls to true/0 and fail/0 as well as unreachable branches.
compile_aux_clauses(+Clauses)
Compile clauses on behalf of goal_expansion/2. This predicate compiles the argument clauses into static predicates, associating the predicates with the current file but avoids changing the notion of current predicate and therefore discontiguous warnings.
dcg_translate_rule(+In, -Out)
This predicate performs the translation of a term Head-->Body into a normal Prolog clause. Normally this functionality should be accessed using expand_term/2.
preprocessor(-Old, +New)
Read the input file via an external process that acts as preprocessor. A preprocessor is specified as an atom. The first occurrence of the string `%f' is replaced by the name of the file to be loaded. The standard output of the resulting command is loaded. To use the Unix C preprocessor one should define:
?- preprocessor(Old, '/lib/cpp -C -P %f'), consult(...).

Old = none

Using cpp for Prolog preprocessing is not ideal as the tokenization rules for comment and quoted strings differ between C and Prolog. Another problem is availability and compatibility with regard to option processing of cpp.

4.3.1.1 Conditional compilation

Conditional compilation builds on the same principle as term_expansion/2, goal_expansion/2 and the expansion of grammar rules to compile sections of the source code conditionally. One of the reasons for introducing conditional compilation is to simplify writing portable code. See section C for more information. Here is a simple example:

:- if(\+source_exports(library(lists), suffix/2)).

suffix(Suffix, List) :-
        append(_, Suffix, List).

:- endif.

Note that these directives can only appear as separate terms in the input. Typical usage scenarios include:

:- if(:Goal)
Compile subsequent code only if Goal succeeds. For enhanced portability, Goal is processed by expand_goal/2 before execution. If an error occurs, the error is printed and processing proceeds as if Goal has failed.
:- elif(:Goal)
Equivalent to :- else. :-if(Goal). ... :- endif. In a sequence as below, the section below the first matching elif is processed. If no test succeeds, the else branch is processed.
:- if(test1).
section_1.
:- elif(test2).
section_2.
:- elif(test3).
section_3.
:- else.
section_else.
:- endif.
:- else
Start `else' branch.
:- endif
End of conditional compilation.