A characteristic feature of Prolog and ECLiPSe is, that programs can be represented as data structures in a straightforward way. The compiler therefore provides the compile_term/1 and compile_term/2 interface predicates, which allow one to compile a list of terms. The compiler interprets these as clauses, directives and queries, similarly to what happens when the program source is being read from a file. For program generators, it is therefore not necessary to create a textual representation of generated code - the data structures can be compiled directly.
There are the following minor differences between compilation from textual sources and term compilation:
A variant of compile_term/2 is compile_term_annotated/3 which takes source terms with source position annotations. This can be used when compiling auxiliary code within inlining/goal expansions transformations, without losing the source position information which is needed by the debugger.
Mode declarations are a way for the user to give some additional information to the compiler, thus enabling it to do a better job. The ECLiPSe compiler makes use of the mode information mainly to improve indexing and to reduce code size.
Mode declarations are optional. They specify the argument instantiation patterns that a predicate will be called with at runtime, for example:
:- mode p(+), q(-), r(++, ?).
The possible argument modes and their meaning are:
+ the argument is instantiated, i.e., it is not a variable; ++ the argument is ground; - the argument is not instantiated, it must be a free variable without any constraints, especially it must not occur in any other argument and it cannot be a suspending variable; ? the mode is not known or it is neither of the above ones.
Note that, if the actual instantiation of a predicate call violates its mode declaration, the behaviour is undefined. Usually, an unexpected failure occurs in this case.
To improve efficiency, calls to user-defined predicates can be preprocessed and transformed at compile time. The directive inline/2, e.g.,
:- inline(mypred/1, mytranspred/2).
arranges for mytranspred/2 to be invoked at compile time for each call to the predicate mypred/1 before this call is being compiled.
The transformation predicate receives the original call to mypred/1 as its first argument, and is expected to return a replacement goal in its second argument. This replacement goal replaces the original call in the compiled code. Usually, the replacement goal would be semantically equivalent, but more efficient than the original goal. When the transformation predicate fails, the original goal is not replaced.
Typically, a predicate would be defined together with the corresponding inlining transformation predicate, e.g.,
:- inline(double/2, trans_double/2). double(X, Y) :- Y is 2*X. trans_double(double(X, Y), Y=Result) :- not nonground(X), % if X already known at compile time: Result is 2*X. % do calculation at compile time!
All compiled calls to double/2 will now be preprocessed by being passed to trans_double/2. For example, if we now compile the following predicate involving double/2:
sample :- double(12, Y), ..., double(Y, Z).
then the first call to double will be replaced by Y = 24 while the second one will be unaffected. The code that the compiler sees and compiles is therefore
sample :- Y = 24, ..., double(Y, Z).
Note that meta-calls (e.g., via call/1) are never preprocessed, they always go directly to the definition of double/2.
Transformation can be disabled for debugging purposes by adding
to the compiled file, or by setting the global flag
:- set_flag(goal_expansion, off).
Before compilation, the compiler also performs clause macro expansion (macro/3). This includes the DCG grammar rule expansion (section 13.3).