• Keine Ergebnisse gefunden

Analysing Terms via the Foreign Interface

Im Dokument SWI-Prolog 5.0 (Seite 164-171)

6.6 The Foreign Include File

6.6.3 Analysing Terms via the Foreign Interface

Each argument of a foreign function (except for the control argument) is of typeterm t, an opaque handle to a Prolog term. Three groups of functions are available for the analysis of terms. The first just validates the type, like the Prolog predicatesvar/1,atom/1, etc and are calledPL is *().

The second group attempts to translate the argument into a C primitive type. These predicates take a term tand a pointer to the appropriate C-type and returnTRUEorFALSEdepending on successful or unsuccessful translation. If the translation fails, the pointed-to data is never modified.

2Otherwise asynchronous atom garbage collection might destroy the atom before it is used.

6.6. THE FOREIGN INCLUDE FILE 165

Testing the type of a term intPL term type(term t)

Obtain the type of a term, which should be a term returned by one of the other interface pred-icates or passed as an argument. The function returns the type of the Prolog term. The type identifiers are listed below. Note that the extraction functions PL ge t*() also validate the type and thus the two sections below are equivalent.

if ( PL_is_atom(t) ) { char *s;

PL_get_atom_chars(t, &s);

...;

} or

char *s;

if ( PL_get_atom_chars(t, &s) ) { ...;

}

PL VARIABLE An unbound variable. The value of term as such is a unique identifier for the variable.

PL ATOM A Prolog atom.

PL STRING A Prolog string.

PL INTEGER A Prolog integer.

PL FLOAT A Prolog floating point number.

PL TERM A compound term. Note that a list is a compound term ./2.

The functions PL is htypei are an alternative to PL term type(). The test PL is variable(term) is equivalent to PL term type(term) == PL VARIABLE, but the first is considerably faster. On the other hand, using a switch overPL term type()is faster and more readable then using an if-then-else using the functions below. All these functions return eitherTRUEorFALSE.

intPL is variable(term t)

Returns non-zero if term is a variable.

intPL is atom(term t)

Returns non-zero if term is an atom.

intPL is string(term t)

Returns non-zero if term is a string.

intPL is integer(term t)

Returns non-zero if term is an integer.

intPL is float(term t)

Returns non-zero if term is a float.

intPL is compound(term t)

Returns non-zero if term is a compound term.

intPL is functor(term t, functor t)

Returns non-zero if term is compound and its functor is functor. This test is equivalent to PL get functor(), followed by testing the functor, but easier to write and faster.

intPL is list(term t)

Returns non-zero if term is a compound term with functor ./2 or the atom[].

intPL is atomic(term t)

Returns non-zero if term is atomic (not variable or compound).

intPL is number(term t)

Returns non-zero if term is an integer or float.

Reading data from a term

The functionsPL get *()read information from a Prolog term. Most of them take two arguments.

The first is the input term and the second is a pointer to the output value or a term-reference.

intPL get atom(term t +t, atom t *a)

If t is an atom, store the unique atom identifier over a. See also PL atom chars() and PL new atom(). If there is no need to access the data (characters) of an atom, it is ad-vised to manipulate atoms using their handle. As the atom is referenced by t, it will live at least as long as t does. If longer live-time is required, the atom should be locked using PL register atom().

intPL get atom chars(term t +t, char **s)

If t is an atom, store a pointer to a 0-terminated C-string in s. It is explicitly not allowed to modify the contents of this string. Some built-in atoms may have the string allocated in read-only memory, so ‘temporary manipulation’ can cause an error.

intPL get string chars(term t +t, char **s, int *len)

If t is a string object, store a pointer to a 0-terminated C-string in s and the length of the string in len. Note that this pointer is invalidated by backtracking, garbage-collection and stack-shifts, so generally the only save operations are to pass it immediately to a C-function that doesn’t involve Prolog.

intPL get chars(term t +t, char **s, unsigned flags)

Convert the argument term t to a 0-terminated C-string. flags is a bitwise disjunction from two groups of constants. The first specifies which term-types should converted and the second how the argument is stored. Below is a specification of these constants. BUF RINGimplies, if the data is not static (as from an atom), the data is copied to the next buffer from a ring of 16 buffers.

This is a convenient way of converting multiple arguments passed to a foreign predicate to C-strings. If BUF MALLOC is used, the data must be freed using free() when not needed any longer.

6.6. THE FOREIGN INCLUDE FILE 167

CVT ATOM Convert if term is an atom CVT STRING Convert if term is a string

CVT LIST Convert if term is a list of integers between 1 and 255 CVT INTEGER Convert if term is an integer (using%d)

CVT FLOAT Convert if term is a float (using%f) CVT NUMBER Convert if term is a integer or float CVT ATOMIC Convert if term is atomic

CVT VARIABLE Convert variable to print-name

CVT WRITE Convert any term that is not converted by any of the other flags using write/1. If no BUF * is provided, BUF RINGis implied.

CVT ALL Convert if term is any of the above, except for CVT VARIABLEandCVT WRITE

BUF DISCARDABLE Data must copied immediately BUF RING Data is stored in a ring of buffers

BUF MALLOC Data is copied to a new buffer returned bymalloc(3) intPL get list chars(+term t l, char **s, unsigned flags)

Same as PL get chars(l, s, CVT LIST—flags), provided flags contains no of the CVT * flags.

intPL get integer(+term t t, int *i)

If t is a Prolog integer, assign its value over i. On 32-bit machines, this is the same as PL get long(), but avoids a warning from the compiler. See alsoPL get long().

intPL get long(term t +t, long *i)

If t is a Prolog integer, assign its value over i. Note that Prolog integers have limited value-range. If t is a floating point number that can be represented as a long, this function succeeds as well.

intPL get bool(term t +t, int *val)

If t has the valuetrueorfalse, set val to the C constantTRUEorFALSEand return success.

otherwise return failure.

intPL get pointer(term t +t, void **ptr)

In the current system, pointers are represented by Prolog integers, but need some manip-ulation to make sure they do not get truncated due to the limited Prolog integer range.

PL put pointer()/PL get pointer()guarantees pointers in the range of malloc() are handled without truncating.

intPL get float(term t +t, double *f)

If t is a float or integer, its value is assigned over f.

intPL get functor(term t +t, functor t *f)

If t is compound or an atom, the Prolog representation of the name-arity pair will be assigned over f. See alsoPL get name arity()andPL is functor().

intPL get name arity(term t +t, atom t *name, int *arity)

If t is compound or an atom, the functor-name will be assigned over name and the arity over arity. See alsoPL get functor()andPL is functor().

intPL get module(term t +t, module t *module)

If t is an atom, the system will lookup or create the corresponding module and assign an opaque pointer to it over module,.

intPL get arg(int index, term t +t, term t -a)

If t is compound and index is between 1 and arity (including), assign a with a term-reference to the argument.

int PL get arg(int index, term t +t, term t -a)

Same asPL get arg(), but no checking is performed, nor whether t is actually a term, nor whether index is a valid argument-index.

Exchanging text using length and string

All internal text-representation of SWI-Prolog is represented usingchar *plus length and allow for 0-bytes in them. The foreign library supports this by implementing a * nchars() function for each applicable * chars() function. Below we briefly present the signatures of these functions. For full documentation consult the * chars() function.

intPL get atom nchars(term t t, unsigned int len, char **s) SeePL get atom chars().

intPL get list nchars(term t t, unsigned int len, char **s) SeePL get list chars().

intPL get nchars(term t t, unsigned int *len, char **s, unsigned int flags) SeePL get chars().

intPL put atom nchars(term t t, unsigned int len, const char *s) SeePL put atom chars().

intPL put string nchars(term t t, unsigned int len, const char *s) SeePL put string chars().

intPL put list ncodes(term t t, unsigned int len, const char *s) SeePL put list codes().

intPL put list nchars(term t t, unsigned int len, const char *s) SeePL put list chars().

intPL unify atom nchars(term t t, unsigned int len, const char *s) SeePL unify atom chars().

intPL unify string nchars(term t t, unsigned int len, const char *s) SeePL unify string chars().

intPL unify list ncodes(term t t, unsigned int len, const char *s) SeePL unify codes().

intPL unify list nchars(term t t, unsigned int len, const char *s) SeePL unify list chars().

6.6. THE FOREIGN INCLUDE FILE 169

In addition, the following functions are available for creating and inspecting atoms:

atom tPL new atom nchars(unsigned int len, const char *s)

Create a new atom asPL new atom(), but from length and characters.

const char *PL atom nchars(atom t a, unsigned int *len) Extract text and length of an atom.

Reading a list

The functions from this section are intended to read a Prolog list from C. Suppose we expect a list of atoms, the following code will print the atoms, each on a line:

foreign_t

pl_write_atoms(term_t l)

{ term_t head = PL_new_term_ref(); /* variable for the ele-ments */

term_t list = PL_copy_term_ref(l); /* copy as we need to write */

while( PL_get_list(list, head, list) ) { char *s;

if ( PL_get_atom_chars(head, &s) ) Sprintf("%s\n", s);

else

PL_fail;

}

return PL_get_nil(list); /* test end for [] */

}

intPL get list(term t +l, term t -h, term t -t)

If l is a list and not[]assign a term-reference to the head to h and to the tail to t.

intPL get head(term t +l, term t -h)

If l is a list and not[]assign a term-reference to the head to h.

intPL get tail(term t +l, term t -t)

If l is a list and not[]assign a term-reference to the tail to t.

intPL get nil(term t +l)

Succeeds if represents the atom[].

An example: definingwrite/1in C

Figure6.3shows a simplified definition ofwrite/1to illustrate the described functions. This sim-plified version does not deal with operators. It is calleddisplay/1, because it mimics closely the behaviour of this Edinburgh predicate.

foreign_t

pl_display(term_t t) { functor_t functor;

int arity, len, n;

char *s;

switch( PL_term_type(t) ) { case PL_VARIABLE:

case PL_ATOM:

case PL_INTEGER:

case PL_FLOAT:

PL_get_chars(t, &s, CVT_ALL);

Sprintf("%s", s);

break;

case PL_STRING:

PL_get_string_chars(t, &s, &len);

Sprintf("\"%s\"", s);

break;

case PL_TERM:

{ term_t a = PL_new_term_ref();

PL_get_name_arity(t, &name, &arity);

Sprintf("%s(", PL_atom_chars(name));

for(n=1; n<=arity; n++) { PL_get_arg(n, t, a);

if ( n > 1 ) Sprintf(", ");

pl_display(a);

}

Sprintf(")");

break;

default:

PL_fail; /* should not happen */

}

PL_succeed;

}

Figure 6.3: A Foreign definition ofdisplay/1

6.6. THE FOREIGN INCLUDE FILE 171

Im Dokument SWI-Prolog 5.0 (Seite 164-171)