The Prolog engine can be called from C. There are two interfaces for
this. For the first, a term is created that could be used as an argument
to call/1,
and then PL_call()
is used to call Prolog. This system is simple, but does not allow to
inspect the different answers to a non-deterministic goal and is
relatively slow as the runtime system needs to find the predicate. The
other interface is based on
PL_open_query(), PL_next_solution()
and PL_cut_query()
or
PL_close_query().
This mechanism is more powerful, but also more complicated to use.
This section discusses the functions used to communicate about
predicates. Though a Prolog predicate may be defined or not, redefined,
etc., a Prolog predicate has a handle that is neither destroyed nor
moved. This handle is known by the type predicate_t
.
- predicate_t PL_pred(functor_t
f, module_t m)
- Return a handle to a predicate for the specified name/arity in the given
module. This function always succeeds, creating a handle for an
undefined predicate if no handle was available. If the module argument
m is
NULL
, the current context module is used.
- predicate_t PL_predicate(const
char *name, int arity, const char* module)
- Same as PL_pred(),
but provides a more convenient interface to the C programmer.
- void PL_predicate_info(predicate_t
p, atom_t *n, size_t *a, module_t *m)
- Return information on the predicate p. The name is stored
over
n, the arity over a, while m receives
the definition module. Note that the latter need not be the same as
specified with
PL_predicate().
If the predicate is imported into the module given to
PL_predicate(),
this function will return the module where the predicate is defined. Any
of the arguments n, a and m can be
NULL
.
This section discusses the functions for creating and manipulating
queries from C. Note that a foreign context can have at most one active
query. This implies that it is allowed to make strictly nested calls
between C and Prolog (Prolog calls C, calls Prolog, calls C, etc.), but
it is not allowed to open multiple queries and start
generating solutions for each of them by calling PL_next_solution().
Be sure to call PL_cut_query()
or PL_close_query()
on any query you opened before opening the next or returning control
back to Prolog.
- qid_t PL_open_query(module_t
ctx, int flags, predicate_t p, term_t +t0)
-
Opens a query and returns an identifier for it. ctx is the context
module of the goal. When NULL
, the context module of
the calling context will be used, or user
if there is no
calling context (as may happen in embedded systems). Note that the
context module only matters for meta-predicates. See meta_predicate/1,
context_module/1
and module_transparent/1.
The p argument specifies the predicate, and should be the
result of a call to PL_pred()
or PL_predicate().
Note that it is allowed to store this handle as global data and reuse it
for future queries. The term reference t0 is the first of a
vector of term references as returned by
PL_new_term_refs(n).
The flags arguments provides some additional options
concerning debugging and exception handling. It is a bitwise or
of the following values:
PL_Q_NORMAL
- Normal operation. The debugger inherits its settings from the
environment. If an exception occurs that is not handled in Prolog, a
message is printed and the tracer is started to debug the error.198Do
not pass the integer 0 for normal operation, as this is interpreted as
PL_Q_NODEBUG
for backward compatibility reasons.
PL_Q_NODEBUG
- Switch off the debugger while executing the goal. This option is used by
many calls to hook-predicates to avoid tracing the hooks. An example is print/1
calling portray/1
from foreign code.
PL_Q_CATCH_EXCEPTION
- If an exception is raised while executing the goal, do not report it,
but make it available for PL_exception().
PL_Q_PASS_EXCEPTION
- As
PL_Q_CATCH_EXCEPTION
, but do not invalidate the
exception-term while calling PL_close_query().
This option is experimental.
PL_Q_ALLOW_YIELD
- Support the
I_YIELD
instruction for engine-based
coroutining. See $engine_yield/2 in boot/init.pl
for
details.
PL_Q_EXT_STATUS
- Make PL_next_solution()
return extended status. Instead of only
TRUE
or FALSE
extended status as illustrated
in the following table:
Extended | Normal |
PL_S_EXCEPTION | FALSE | Exception
available through PL_exception() |
PL_S_FALSE | FALSE | Query failed |
PL_S_TRUE | TRUE | Query succeeded with choicepoint |
PL_S_LAST | TRUE | Query succeeded without
choicepoint |
PL_open_query()
can return the query identifier‘0' if there is not enough space on
the environment stack. This function succeeds, even if the referenced
predicate is not defined. In this case, running the query using PL_next_solution()
will return an existence_error. See
PL_exception().
The example below opens a query to the predicate is_a/2
to find the ancestor of‘me'. The reference to the predicate is
valid for the duration of the process and may be cached by the client.
char *
ancestor(const char *me)
{ term_t a0 = PL_new_term_refs(2);
static predicate_t p;
if ( !p )
p = PL_predicate("is_a", 2, "database");
PL_put_atom_chars(a0, me);
PL_open_query(NULL, PL_Q_NORMAL, p, a0);
...
}
- int PL_next_solution(qid_t
qid)
- Generate the first (next) solution for the given query. The return value
is
TRUE
if a solution was found, or FALSE
to
indicate the query could not be proven. This function may be called
repeatedly until it fails to generate all solutions to the query.
- int PL_cut_query(qid_t
qid)
- Discards the query, but does not delete any of the data created by the
query. It just invalidates qid, allowing for a new call to
PL_open_query()
in this context. PL_cut_query()
may invoke cleanup handlers (see setup_call_cleanup/3)
and therefore may experience exceptions. If an exception occurs the
return value is
FALSE
and the exception is accessible
through PL_exception(0)
.
- int PL_close_query(qid_t
qid)
- As PL_cut_query(),
but all data and bindings created by the query are destroyed.
- qid_t PL_current_query(void)
- Returns the query id of of the current query or
0
if the
current thread is not executing any queries.
- int PL_call_predicate(module_t
m, int flags, predicate_t pred, term_t +t0)
- Shorthand for PL_open_query(), PL_next_solution(), PL_cut_query(),
generating a single solution. The arguments are the same as for
PL_open_query(),
the return value is the same as PL_next_solution().
- int PL_call(term_t
t, module_t m)
- Call term t just like the Prolog predicate once/1. t
is called in the module m, or in the context module if m
== NULL. Returns
TRUE
if the call succeeds, FALSE
otherwise.
Figure 7 shows
an example to obtain the number of defined atoms. All checks are omitted
to improve readability.