Quintus
Prolog Manual
Quintus Prolog provides tools making it possible to call Prolog predicates from foreign languages. This is useful for a number of reasons.
To take advantage of Quintus Prolog's ability to be called from foreign functions, you must first know how to do either of the following:
Currently, only functions written in C can call Prolog predicates directly; this restriction may be lifted in future releases of Quintus Prolog. However, other languages such as Pascal and FORTRAN can call Prolog by virtue of their ability to call C. The interface between C and Prolog supports direct exchange of Prolog's atomic data types (atoms, integers or database reference). The data is automatically converted between Prolog's internal representation and the internal representation of the foreign language. The interface also supports passing any Prolog term from C and returning any Prolog term to C.
Following is a summary of the steps which enable you to call a Prolog predicate from a C function:
Any Prolog predicate can be made callable from foreign functions using the declaration extern/1. This includes built-in predicates, as well as predicates that are currently undefined and dynamic predicates that currently have no clauses. An extern/1 declaration not only makes the predicate known to foreign languages, but also specifies how arguments will be passed to and from it. When a predicate is declared callable using extern/1 declaration, it becomes available to foreign functions as soon as the declaration is loaded. This is equally true of extern/1 declarations occurring in files that are loaded from source form, pre-compiled QOF files whose source files contained extern/1 declarations, and certain QOF files (e.g. those created using save_program/1) that retain callability information (see {manual(g-5-4)}). A Prolog predicate that has been made callable from foreign functions is not otherwise changed in any way. It can still be abolished or redefined just like any other predicate. There is no performance penalty associated with making a predicate callable from foreign functions. A predicate can be redeclared by loading a new or modified extern/1 declaration. When a predicate is made callable from foreign code, a new and closely related Prolog predicate called an interface predicate is created in the module in which extern/1 was declared. The interface predicate has the same arity as the callable predicate, and its name is the name of the declared predicate with an underscore prepended to it. The interface predicate provides the link between foreign languages and its Prolog predicate. It can be abolished or saved just as any other predicate, but because they can only be created using an extern/1 declaration, valid interface predicates cannot be made dynamic or multifile. The purpose of the interface predicate is to supply an entry point into Prolog for foreign functions, and a handle on the property of callability of Prolog procedures for manipulation in Prolog. It is possible to call an interface predicate from Prolog, but the call will simply fail.
An extern/1 declaration has the form
:- extern(+CallSpecification)
CallSpecification is a Prolog term specifying how calls from C will pass arguments to and receive arguments from the Prolog predicate. Handling arguments passed from other languages is discussed in {manual(i-4-7)}. CallSpecification is of the form:
PredicateName(ArgSpec, ArgSpec, ...)
where PredicateName is the name of the Prolog predicate and each ArgSpec is an argument specification for the corresponding argument of the predicate. ArgSpec must be one of the following list
+integer +float +single +double +atom +string +term +address(T) -integer -float -single -double -atom -string -term -address(T)
where T is a foreign type name. The argument type address can always be used instead of address(T). Argument specifications used when declaring Prolog predicates callable from C are equivalent to those used when specifying an interface for C functions that are to be callable from Prolog. Here are some example extern/1 declarations: Examples:
:- extern(write(+integer)). :- extern(call(+term)). :- extern(my_proc(+atom,+integer,-term,-integer)).
The foreign function interface automatically converts between C's representation of data and the representation of atomic data types expected by Prolog. Thus the calling function does not need to know how Prolog represents atoms, integers, floats or addresses in order to communicate with Prolog. This feature simplifies the integration of Prolog with foreign code; in particular, it makes it easier to interface directly with already-written functions in libraries and other programs. It also allows for compatibility with later versions of Quintus Prolog and with versions of Quintus Prolog running on other hardware.
ASYMMETRY NOTE: When C calls Prolog, in contrast to Prolog calling foreign code, there is no Prolog datum passed as the function return value. Instead, the return value supplies the calling function with information as to whether the Prolog call succeeded or failed, or whether there was an exception raised.
Arguments are passed from C functions to Prolog predicates in the same order as they appear in the Prolog call. Prolog assumes that C functions will call Prolog predicates with the number and type of arguments as declared by the extern/1 declarations; if it does not, the results are unpredictable. Certain types of inputs (for example, atoms) can be checked for validity when the query to Prolog is made, and an error value is returned if the type is incorrect. Outputs are passed to Prolog as pointers to storage for results. Prolog will internally create unbound variables with which to calculate the results. The outputs will then be automatically converted and written into the C storage according to the calling specification. If the result Prolog computes is inconsistent with the specified output type, an exception is signaled.
ASYMMETRY NOTE: When Prolog calls foreign code, outputs are unified with items supplied by the calling function; with C calling Prolog, assignment is used instead.
Prolog: +integer C: long int x;
The C long int is converted to a 32-bit Prolog integer, which is passed to the Prolog call. If the C integer contains garbage when it is passed, Prolog will receive that garbage as an integer.
Prolog: -integer C: long int *x;
A pointer to a C long int is passed to the foreign interface. When Prolog returns a solution, a Prolog integer is expected in the corresponding argument of the call. The foreign interface converts that integer into a C long int and writes it at the location supplied. The previous contents of the location are destroyed. If the Prolog call does not return an integer in the appropriate position, a type error is raised and the contents of the location is unchanged.
Prolog: +float C: double x;
The C double-precision float is converted to a Prolog float, which is passed to the Prolog call. If the C double contains garbage, Prolog will receive that garbage as a double-precision floating-point number. Many C compilers will allow the parameter declaration to be 'float' instead of 'double' because they always convert single-precision floating-point arguments to double-precision. However, C compilers conforming to the new ANSI standard will not do this, so it is recommended that 'double' be used.
Prolog: +single ANSI C: float x;
The C single-precision float is converted to a Prolog float, which is passed to the Prolog call. If the C float contains garbage, Prolog will receive that garbage as a single-precision floating-point number.
Normally, this type of argument is not used; however, C compilers conforming to the new ANSI standard can pass single precision floats to Prolog without first converting them to double. It is not recommended that floats be passed as 'single' until you have verified that your C compiler behaves as desired.
Prolog: +double C: double x;
The C double-precision float is converted to a Prolog float, which is passed to the Prolog call. If the C float contains garbage, Prolog will receive that garbage as a double-precision floating-point number.
Prolog: -float C: float *x;
A pointer to a C float is passed to the foreign interface. When Prolog returns a solution, a Prolog floating-point number is expected in the corresponding argument of the call. The foreign interface converts that number into a C float and writes it at the location supplied. The previous contents of the location are destroyed. If the Prolog call does not return a floating-point number in the appropriate position, a type error is raised and the contents of the location is unchanged.
Prolog: -single C: float *x;
A pointer to a C float is passed to the foreign interface. When Prolog returns a solution, a Prolog floating-point number is expected in the corresponding argument of the call. The foreign interface converts that number into a C float and writes it at the location supplied. The previous contents of the location are destroyed. If the Prolog call does not return a floating-point number in the appropriate position, a type error is raised and the contents of the location is unchanged.
When the foreign language calling Prolog is C, this type of argument is not normally used; however, C compilers conforming to the new ANSI standard can return single precision floats from Prolog without first converting them to double. It is not recommended that floats be passed as 'single' until you have verified that your C compiler behaves as desired.
Prolog: -double C: double *x;
A pointer to a C double is passed to the foreign interface. When Prolog returns a solution, a Prolog floating-point number is expected in the corresponding argument of the call. The foreign interface converts that number into a C double and writes it at the location supplied. The previous contents of the location will be destroyed. If the Prolog call does not return a floating-point number in the appropriate position, a type error is signaled and the contents of the location is unchanged.
It is assumed that the interface will overwrite this float with Prolog's result. When Prolog returns, its floating-point number is converted to double-precision and written onto the space for the foreign double. The previous contents of the C double will be lost. If the Prolog call does not return a floating-point number, a type error is raised and the result is unchanged.
The foreign function interface allows Prolog atoms to be passed from C functions to Prolog either in a canonical form as unsigned integers, or as pointers to character strings. This section describes passing atoms in canonical form. For each Prolog atom there is a single canonical representation. Programs can rely on the property that identical atoms have identical canonical representations. Note, however, that the canonical form of an atom is not necessarily identical across different invocations of the program. This means that canonical atom representations should not be used in files or interprogram communication. For these purposes strings should be used (see {manual(i-4-4-1)}). Foreign functions can store canonical atoms in data structures, pass them around, access their strings using QP_string_from_atom() and pass them back to Prolog, but they should not attempt any other operations on them.
Prolog: +atom C: unsigned long x;
The unsigned long must be a valid Prolog atom, otherwise the function attempting to pass the atom parameter (QP_query() or QP_open_query()) will return QP_ERROR. The C unsigned long is passed to Prolog, where it will appear as a normal Prolog atom. Atoms can be converted to strings using the functions QP_string_from_atom() or QP_atom_from_padded_string() (see {manual(i-3-7-4)}).
Prolog: -atom C: unsigned long *x
A pointer to a C unsigned long is passed to the foreign interface. When Prolog returns a solution, a Prolog atom is expected in the corresponding argument of the call. This atom might be one obtained from Prolog, or one generated through the function QP_atom_from_string() or QP_atom_from_padded_string() (see {manual(i-3-7-4)}). The foreign interface simply writes that atom at the location supplied. The previous contents of the location are destroyed. If the Prolog call does not return an atom in the appropriate position, a type error is raised and the contents of the location is unchanged. Also see {manual(i-4-4)} for discussion of conversion between atoms and strings.
The foreign function interface allows Prolog atoms to be passed from C functions to Prolog either in a canonical form as unsigned integers, or as pointers to character strings. This section describes passing atoms as pointers to null-terminated character strings. Strings are always identical across different invocations of a program, so are the correct atom representation to use when writing to files or using interprogram communication. For other uses, atoms in the canonical form may be appropriate (see {manual(i-4-3-3)}). If, in a later release of Quintus Prolog, it is possible to call Prolog directly from FORTRAN or Pascal, it will additionally be possible to pass atoms as fixed-length, blank-padded strings (as when Prolog calls FORTRAN or Pascal).
Prolog: +string C: char *x
The argument passed from the C function is a null-terminated character string. The foreign interface automatically converts the string to a Prolog atom, and passes it to the Prolog predicate.
Prolog: -string C: char **x;
A pointer to a C string pointer is passed to the foreign interface. When Prolog returns a solution, a Prolog atom is expected in the corresponding argument of the call. This atom might be one obtained from Prolog, or one generated through the function QP_atom_from_string() or QP_atom_from_padded_string() (see {manual(i-3-7-4)}). The foreign interface writes a pointer to the string for that atom at the location supplied. The previous contents of the location are destroyed. This string must not be overwritten by the C function. If the Prolog call does not return an atom in the appropriate position, a type error is raised and the contents of the location is unchanged. See also {manual(i-4-4)} for discussion of conversion between atoms and strings.
The foreign function interface allows Prolog terms to be passed from C functions. Like most of the simple data types that may be passed to and from Prolog, terms to be passed can originate either in Prolog or in C (see {manual(i-3-8)}). Like terms in Prolog, terms that are passed to C are automatically made safe from damage by operations that might change their absolute position in Prolog memory, like stack shifting and garbage collection.
Prolog: +term C: QP_term_ref
The argument passed from the C function is a QP_term_ref initialized to a Prolog term. If something other than a term reference is passed to Prolog, the results are undefined.
Prolog: -term C: QP_term_ref
A QP_term_ref is passed to the foreign interface. When Prolog returns a solution, the foreign interface writes a reference to the Prolog term in the corresponding argument of the call into the QP_term_ref. The previous contents of the QP_term_ref are destroyed. Unlike the other passing types, there are no associated type errors when passing terms. Note that the output term, as well as the input term is represented in the C code by a QP_term_ref. This contrasts with other output types which are usually represented in C as pointers to the corresponding input type.
Previous releases of Quintus Prolog had the restriction that integers of greater than 29 bits could not be represented as Prolog integers. Certain platforms, however, have pointers which use some of the four most significant bits; for these machines, pointers could not be represented as Prolog integers. This problem motivated the "address", which could be treated specially, as a distinct data type that can be passed through the foreign interface. With Release 3 the restriction of integers to 29 bits has been lifted; however, the internal representation of integers of more than 29 bits is more bulky and somewhat slower to manipulate than that of smaller integers. While this is not a problem in normal programs, it could penalize programs that manipulate pointers in Prolog on certain platforms whose pointers require more than 29 bits. Quintus has chosen to retain the address data type in Release 3 so that such penalties can be avoided where possible, as well as for backward compatibility. Addresses can be passed both to and from Prolog from foreign functions, and to and from foreign functions from Prolog. (See {manual(i-3-9)}.) As when calling foreign code from Prolog, pointers should be passed through the interface using the type specification
address(typename)
as described in more detail below. typename should be the name used in the foreign language to identify the type of object named by the pointer.
Prolog: +address(typename) C: typename *x;
The C pointer is converted to a 32-bit Prolog integer, which is passed to the Prolog call. If the C pointer contains garbage when it is passed, Prolog will receive that garbage as an integer.
Prolog: -address(typename) C: typename **x;
A pointer to a C pointer is passed to the foreign interface. When Prolog returns a solution, a Prolog integer is expected in the corresponding argument of the call. If the argument is 0, the foreign function writes the NULL pointer at the location supplied. Otherwise, the foreign interface converts the integer into a C pointer and writes it at the location. The previous contents of the location will be destroyed. If the Prolog call does not return an integer in the appropriate position, a type error is signaled and the contents of the location is undefined.
Prolog: +address C: char *x
This is equivalent to +address(char).
Prolog: -address C: char **x
This is equivalent to -address(char). Using +address in place of +address(typename), or -address in place of -address(typename), has no effect on the execution of the program; however, doing so can reduce the readability of the program and compromise program checking tools.
A Prolog predicate that has been made callable from foreign code can be invoked in two ways: determinately or nondeterminately. A determinate query asks for the first solution and the first solution only. A nondeterminate query allows Prolog to backtrack, possibly returning multiple solutions to the calling foreign function. Note that the terms "determinate" and "nondeterminate" do not refer to the Prolog predicate being called, but rather to the query. It is perfectly reasonable to ask for only the first solution of a Prolog predicate that is nondeterminate, or to attempt to return all solutions to a predicate that in fact has just one. Multiple solutions, if any, are returned in the Prolog solution order. When only a single solution is desired a determinate call is preferred, as it is more efficient and concise.
Before a Prolog predicate can be called from a foreign language it must be looked up. The C functions QP_predicate() and QP_pred() perform this function. The lookup step could have been folded into the functions that make the query, but if a predicate was to be called many times the redundant, if hidden, predicate lookup would be a source of unnecessary overhead. Instead, QP_predicate() or QP_pred() can be called just once per predicate. The result can then be stored in a variable and used as necessary. Both QP_predicate() and QP_pred return a QP_pred_ref, which represents a Prolog predicate. The type definition for QP_pred_ref is found in the file quintus.h in the embed directory.
QP_pred_ref QP_predicate(name_string, arity, module_string)
char *name_string;
long int arity;
char *module_string;
QP_predicate() is the most convenient way of looking up a callable Prolog predicate. It is simply passed the name and module of the predicate to be called as strings, the arity as an integer, and returns a QP_pred_ref which is used to make the actual call to Prolog. QP_predicate() can only be used to look up predicates that have been declared callable from foreign code. If some other predicate is looked up, QP_ERROR is returned. Checking the return value protects you from attempting to call a predicate that isn't yet ready to be called.
QP_pred_ref QP_pred(name_atom, arity, module_atom)
unsigned long name_atom;
long int arity;
unsigned long module_atom;
QP_pred() is a less convenient, but faster, means of looking up a callable Prolog predicate. Unlike QP_predicate(), it has its name and module arguments passed as Prolog atoms. These may have been returned to C from Prolog, or may have been built in the foreign language using QP_atom_from_string(). One additional difference is that the name passed is NOT the name of the Prolog predicate to be called, but rather the name of the interface predicate constructed when the Prolog predicate was made callable from foreign code {manual(i-4-2)}. Much of the cost of QP_predicate() is from having to look up Prolog atoms for its name and module arguments. By avoiding doing this unnecessarily, what QP_pred() gives up in convenience is returned in performance. Like QP_predicate(), QP_pred() returns a QP_pred_ref which is used to make the actual call to Prolog. If a predicate that is not an interface predicate is looked up, QP_pred() returns QP_ERROR. QP_pred() can only be used to look up predicates that have been declared callable from foreign code. If some other predicate, or a predicate that does not exist, is looked up, QP_ERROR is returned. This protects you from attempting to call a predicate that isn't yet ready to be called.
A determinate query can be made in a single C function call using QP_query. The first argument passed to QP_query() is a QP_pred_ref for the predicate to be called. Any arguments after the first represent parameters to be passed to and from the Prolog predicate. The foreign language interface will interpret arguments passed to the Prolog predicate according to the call specification given when the predicate was made callable. Hence, it is important that the arguments to be passed to and from the Prolog predicate should correspond to that call specification. For certain parameter types (passing Prolog atoms in canonica form) it is possible to detect inconsistencies between data supplied to QP_query() and the call specification, but for the most part this is impossible. Calls that are inconsistent with their call specifications will produce undefined results. QP_query() returns one of three values: , indicating that the query was made and a solution to the query was computed, , meaning that the query was made but no solution could be found, and , which says that either the query could not be made, or that an exception was signaled from Prolog but not caught. In this case, see the reference page for QP_exception_term(). Only when the return value is QP_SUCCESS should the values in variables passed as outputs from Prolog be considered valid. Otherwise, their contents are undefined. It is important that a valid QP_pred_ref is passed to QP_query(); in particular, it is advisable to check for an error return from QP_predicate() or QP_pred() before calling QP_query().
For a nondeterminate query, multiple solutions to the query may be successively returned to the calling foreign function. Nondeterminate queries are made in three steps: the query is first initiated, or "opened", using QP_open_query(). Solutions are then requested using QP_next_solution(). When all desired solutions have been returned, or there are no more solutions, the query must be terminated by calling QP_cut_query() or QP_close_query(). The C function QP_open_query() is used to initiate a nondeterminate Prolog query. The arguments passed to QP_open_query() are identical to those that would be passed to QP_query(); however, QP_open_query() does not compute a solution to the query. Its effect is to prepare Prolog for the computation of solutions to the query, which are requested by calls to the function QP_next_solution(). For consistency checking, QP_open_query() returns a QP_qid, which represents the Prolog query. The type definition for QP_qid is found in the file quintus.h in the embed directory. The QP_qid returned by a call to QP_open_query() must be passed to each call to QP_next_solution() for that query, as well as to QP_cut_query() or QP_close_query() when terminating the query. If an invalid QP_qid is passed to one of these functions, the function has no effect except to return QP_ERROR. When requesting solutions from an open nondeterminate query, input and output parameters are NOT passed. The effect of QP_open_query() is to pass inputs to Prolog, which subsequently maintains them. It also tells Prolog where storage for outputs has been reserved. This storage will be written later, when solutions are returned. If an error occurs when attempting to open a query, QP_ERROR is returned and the query is automatically terminated. It is important that a valid QP_pred_ref is passed to QP_open_query(); in particular, it is advisable to check for an error return from QP_predicate() or QP_pred() before calling QP_open_query().
The function QP_next_solution() is used to return a solution from an open nondeterminate Prolog query. Solutions are computed on demand, and multiple solutions are returned in the normal Prolog order. QP_next_solution() is passed the QP_qid returned by QP_open_query() when the nondeterminate query was opened. No additional input or output parameters are passed: after a call to QP_open_query(), Prolog manages inputs itself, and has been told where storage for outputs has been reserved. Each time QP_next_solution() computes a new solution it writes it on the output storage for the foreign function to use as it likes. Each new solution overwrites the old memory, destroying the previous solution, so it is important that the foreign function copies solutions elsewhere if it wants to accumulate them. Important restriction: only the innermost, i.e. the most recent, open query can be asked to compute a solution. A new query can be made at any point whether or not other non-determinate queries are open; however, while the new query remains open only it will be able to return solutions.
QP_close_query() and QP_cut_query() are functions that are used to terminate an open nondeterminate Prolog query. They differ only in their effect on the Prolog heap, which can be reflected in the solutions Prolog has returned to C. The difference between QP_close_query() and QP_cut_query() can best be understood with reference to Prolog's control flow. QP_close_query() is equivalent to the Prolog call:
!, fail.
The cut renders the current computation determinate, removing the possibility of future backtracking. The following call to fail/0 then initiates backtracking to the next previous parent goal with outstanding alternatives. In doing so it pops the Prolog heap to its state when the parent goal succeeded, in effect throwing away any terms created since that parent goal. In contrast, just calling "!" in Prolog renders the computation determinate without initiating backtracking. Any terms created since the parent goal are retained. In the context of calling Prolog from foreign languages, terminating a query using QP_close_query() generally means throwing away the most recent solution that was calculated, unless that solution has been copied into a more permanent place. (Of course, any previous solutions must also be assumed to have been overwritten by subsequent solutions unless copied elsewhere!) The converse of this behavior is that closing a query using QP_close_query() automatically frees up the Prolog memory that holds the last solution. Terminating a query using QP_cut_query() renders the computation determinate, but as it is not failed over the Prolog heap is not popped. Thus when terminating a query using QP_cut_query() more space is retained, but so is the most recent solution.
Any Prolog predicate can be made callable from foreign code, including system built-ins. An especially useful case of this generally useful ability is making the built-in call/1 callable. call/1 is declared callable like any other predicate, and is passed the Prolog term to be called. The term may have originated in Prolog, or may have been constructed in C using the supplied term manipulation functions (see {manual(i-3-8)}). In this particular example, we pass a term from Prolog to C, then C calls call/1 with that term. This lets us concentrate on the calling rather than on the construction of the term to be called. On the Prolog side of the interface, the following declaration is loaded:
:- extern(call(+term)).
On the C side, the following function is defined, compiled and either loaded into Prolog using the dynamic foreign interface or statically linked with Prolog:
#include <quintus/quintus.h>
call_prolog(t)
QP_term_ref t;
{
QP_pred_ref call = QP_predicate("call", 1, "user");
QP_query(call, t);
}
This done, any goal that can be called from Prolog can also be called from C by passing it to call_prolog/1.
Prolog and foreign languages are generally intercallable in Quintus Prolog; in particular, arbitrarily nested calling is permitted. The following example uses recursive calling between Prolog and C to generate Fibonacci numbers: IN THE PROLOG CODE:
fib :-
int(I),
fib(I, F),
write(fib(I,F)), nl,
fail.
int(I) :- int(0, I).
int(I, I).
int(I, K) :-
J is I+1,
int(J, K).
fib(N, F) :-
( N =< 1 ->
F = 1.0
; N1 is N-1,
N2 is N-2,
c_fib(N1, F1),
c_fib(N2, F2),
F is F1+F2
).
:- extern(fib(+integer, -float)).
foreign(c_fib, c_fib(+integer, [-float])).
foreign_file('fib.o', [c_fib]).
:- load_foreign_files('fib.o', []).
IN THE C CODE:
#include <quintus/quintus.h>
float c_fib(i)
int i;
{
float f1, f2;
QP_pred_ref fib = QP_predicate("fib", 2, "user");
if (i <= 1) {
return 1.0;
} else {
QP_query(fib, i-1, &f1);
QP_query(fib, i-2, &f2);
return f1+f2;
}
}
This example shows how a non-determinate query can be made from C. It also shows how you can get at the exception terms raised from a Prolog query from C. IN THE PROLOG CODE: (In a file called brothers.pl)
foreign(brothers, c, brothers).
foreign_file('brothers.o', [brothers]).
:- load_foreign_files('brothers.o', []),
abolish([foreign/3, foreign_file/2]).
:- extern(write(+term)). % Make write/1 callable from C
:- extern(karamazov(-atom)). % Make karamazov/1 callable from C
karamazov('Fyodor').
karamazov('Dmitri').
karamazov('Ivan').
karamazov('Alyosha').
IN THE C CODE: (In a file called brothers.c)
#include <quintus/quintus.h>
/* lookup_predicate() is just a wrapper around QP_predicate()
that prints an error message if QP_predicate() fails.
It returns 1 if QP_predicate() succeeds and 0 if
QP_predicate() fails
*/
int lookup_predicate(name, arity, module, predref)
char * name;
int arity;
char * module;
QP_pred_ref * predref;
{
*predref = QP_predicate(name, arity, module);
if (*predref == QP_BAD_PREDREF) {
printf("%s:%s/%-d is not callable from C\n",
module, name, arity);
return 0;
} else {
return 1;
}
}
/* brothers() is called from Prolog */
void brothers()
{
QP_pred_ref karam, write;
QP_qid query;
QP_atom bro;
int status;
if (!lookup_predicate("karamazov", 1, "user", &karam)) {
return;
}
if ((query = QP_open_query(karam, &bro)) == QP_BAD_QID) {
printf("Cannot open query\n");
return;
}
/* Get solutions one at a time */
while ((status = QP_next_solution(query)) == QP_SUCCESS) {
printf("%10s is a Karamazov\n",
QP_string_from_atom(bro));
}
QP_close_query(query);
if (status == QP_ERROR) {
/* Query raised an exception */
QP_term_ref error = QP_new_term_ref();
printf("Query signalled an exception\n");
if (QP_exception_term(error) == QP_ERROR) {
printf("Could not get at exception term\n");
return;
}
if (lookup_predicate("write", 1, "user", &write)) {
/* Call Prolog builtin write/1 to print the
exception term */
if (QP_query(write, error) != QP_SUCCESS) {
printf("Couldnt write exception term\n");
} else {
return;
}
}
}
return;
}
To test the QP_exception_term() part of this example add a clause for karamazov/1 like:
karamazov(_) :- raise_exception(karamazov(error)).
This example demonstrates how you can have nested queries to Prolog from C. For brevity sake, we don't check the statuses returned by all the calls to QP_next_solution() for error values. This is not advised in real applications. This example also shows the use of QP_cut_query(). IN THE PROLOG CODE: (In a file called books.pl)
foreign(print_books, c, print_books).
foreign_file('books.o', [print_books]).
:- load_foreign_files('books.o', []),
abolish([foreign/3, foreign_file/2]).
:- extern(author(-atom)).
:- extern(book(+atom,-atom)).
author(hesse).
author(kafka).
author(dostoyevski).
book(dostoyevski, idiot).
book(dostoyevski, gambler).
book(hesse, steppenwolf).
book(hesse, sidhdhartha).
book(hesse, demian).
book(kafka, america).
book(kafka, trial).
book(kafka, castle).
book(kafka, metamorphosis).
IN THE C CODE: (In a file called books.c)
#include <quintus/quintus.h>
#define MAX_BOOKS 3
void print_books()
{
QP_pred_ref author, book;
QP_qid q1, q2;
QP_atom a, b;
int count;
if (!(lookup_predicate("author", 1, "user", &author)))
return;
if (!(lookup_predicate("book", 2, "user", &book)))
return;
if ((q1 = QP_open_query(author, &a)) == QP_BAD_QID) {
printf("Cant open outer query\n");
return;
}
while (QP_next_solution(q1) == QP_SUCCESS) {
/* For each solution returned by author(X) do */
if ((q2 = QP_open_query(book, a, &b)) == QP_BAD_QID) {
printf("Cant open inner query\n");
break;
}
printf("Books by %s:\n", QP_string_from_atom(a));
count = 0;
while ((count < MAX_BOOKS) &&
(QP_next_solution(q2) == QP_SUCCESS)) {
/* Find atmost MAX_BOOKS solns for books(X,Y) */
printf("\t\t%s\n",QP_string_from_atom(b));
count++;
}
QP_close_query(q2);
}
QP_close_query(q1);
}
It is possible to call Prolog predicates from Pascal and FORTRAN using C as an intermediary language. Your Pascal or FORTRAN manual will tell you how to make your code call C. Then call Prolog from C using the procedures described in this chapter.
Reference pages for the following provide further detail on the material in this chapter.
contact: product
support sales information