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
.
NULL
, the
current context module is used. If the target predicate does not exist a
handle to a new undefined predicate is returned. The predicate
may fail, returning (predicate_t)0
after setting a resource
exception, if the target module has a limit on the
program_space
, see set_module/1.
Currently aborts the process with a fatal error when out of
memory. Future versions may raise a resource exception and return (predicate_t)0
.NULL
, the current
context module is used. The predicate_t
handle may be
stored as global data and reused for future queries228PL_predicate()
involves 5 hash lookups (two to get the atoms, one to get the module,
one to get the functor and the final one to get the predicate associated
with the functor in the module) as illustrated below.
static predicate_t p = 0; ... if ( !p ) p = PL_predicate("is_a", 2, "database");
Note that PL_cleanup()
invalidates the predicate handle. Foreign libraries that use the above
mechanism must implement the module
uninstall() function to clear the predicate_t
global
variable.
NULL
. Currently always returns TRUE
.
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. Failure to do so results in "undefined behavior" (typically, a crash).
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 term reference t0 is the first of a vector of term
references as returned by
PL_new_term_refs(n).
Raise a resource exception and returns
(qid_t)0
on failure.
Every use of PL_open_query()
must have a corresponding call to
PL_cut_query()
or PL_close_query()
before the foreign predicate returns either TRUE
or FALSE
.
The flags arguments provides some additional options
concerning debugging and exception handling. It is a bitwise or
of the following values below. Note that exception propagation is
defined by the flags PL_Q_NORMAL
, PL_Q_CATCH_EXCEPTION
and
PL_Q_PASS_EXCEPTION
. Exactly one of these flags must be
specified (if none of them is specified, the behavior is as if
PL_Q_NODEBUG
is specified)..
PL_Q_NORMAL
PL_Q_NODEBUG
0
.PL_Q_CATCH_EXCEPTION
PL_exception(qid)
,
where qid
is the qid_t
returned by PL_open_query().
The exception is implicitly cleared from the environment when the query
is closed and the exception term returned from PL_exception(qid)
becomes invalid. Use
PL_Q_PASS_EXCEPTION
if you wish to propagate the exception.PL_Q_PASS_EXCEPTION
PL_Q_CATCH_EXCEPTION
, making the exception on the inner
environment available using PL_exception(0)
in the parent environment. If PL_next_solution()
returns FALSE
, you must call
PL_cut_query()
or PL_close_query().
After that you may verify whether failure was due to logical failure of
the called predicate or an exception by calling PL_exception(0)
.
If the predicate failed due to an exception you should return with FALSE
from the foreign predicate or call PL_clear_exception()
to clear it. If you wish to process the exception in C, it is advised to
use PL_Q_CATCH_EXCEPTION
instead, but only if you have no
need to raise an exception or re-raise the caught exception.
Note that PL_Q_PASS_EXCEPTION
is used by the debugger to
decide whether the exception is caught. If there is no matching
catch/3
call in the current query and the query was started using
PL_Q_PASS_EXCEPTION
the debugger searches the parent
queries until it either finds a matching catch/3,
a query with
PL_Q_CATCH_EXCEPTION
(in which case it considers the
exception handled by C) or the top of the query stack (in which case it
considers the exception uncaught). Uncaught exceptions use the
library(library(prolog_stack))
to add a backtrace to the
exception and start the debugger as soon as possible if the Prolog flag
debug_on_error
is true
.
PL_Q_ALLOW_YIELD
I_YIELD
instruction for engine-based
coroutining. See $engine_yield/2 in boot/init.pl
for
details.PL_Q_EXT_STATUS
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 (and makes the exception available
through PL_exception(0)
).
This function succeeds, even if the referenced predicate is not defined.
In this case, running the query using PL_next_solution()
may 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 or until PL_cleanup() is called (see PL_predicate() for details) 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_PASS_EXCEPTION, p, a0); ... }
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. The
return value
PL_S_NOT_INNER
is returned if qid is not the
innermost query.
If the PL_open_query()
had the flag PL_Q_EXT_STATUS
, there are additional return
values (see section
12.4.1.2).
FALSE
and the exception is accessible
through PL_exception(0)
.
An example of a handler that can trigger an exception in PL_cut_query() is:
test_setup_call_cleanup(X) :- setup_call_cleanup( true, between(1, 5, X), throw(error)).
where PL_next_solution()
returns TRUE
on the first result and the throw(error)
will only run when PL_cut_query()
or
PL_close_query()
is run. On the other hand, if the goal in
setup_call_cleanup/3
has completed (failure, exception, determinitic success), the cleanup
handler will have done its work before control gets back to Prolog and
therefore PL_next_solution()
will have generated the exception. The return value PL_S_NOT_INNER
is returned if qid is not the innermost query.
\+ \+ Goal
. This reduces the need for
garbage collection, but also rewinds side effects such as setting global
variables using b_setval/2.
The return value
PL_S_NOT_INNER
is returned if qid is not the
innermost query.0
if the
current thread is not executing any queries.TRUE
if the call succeeds, FALSE
otherwise. If the goal raises an exception the return value is
FALSE
and the exception term is available using
PL_exception(0).229Up
to version 9.1.11 the debugger was started and the exception was not
propagated.
Figure 7 shows
an example to obtain the number of defined atoms.