3. Manipulating Lisp objects
If you want to extend, fix or simply customize ECL for your own needs,
you should understand how the implementation works.
3.1 Objects representation
In ECL a lisp object is represented by a type called cl_object. This
type is a word which is long enough to host both an integer and a pointer. The
least significant bits of this word, also called the tag bits, determine
whether it is a pointer to a C structure representing a complex object, or
whether it is an immediate data, such as a fixnum or a character.
| | |-------------------|--|
| Fixnum value |01|
|-------------------|--|
|------------|------|--|
| Unused bits| char |10|
|------------|------|--|
|----------------------| |--------|--------|-----|--------|
| Pointer to cell |---->| word-1 | word-2 | ... | word-n |
|----------------------| |--------|--------|-----|--------|
| ...................00| | actual data of the object |
|----------------------| |--------------------------------|
|
The fixnums and characters are called immediate datatypes, because they require
no more than the cl_object datatype to store all information. All other
ECL objects are non-immediate and they are represented by a pointer to a
cell that is allocated on the heap. Each cell consists of several words of
memory and contains all the information related to that object. By storing data
in multiples of a word size, we make sure that the least significant bits of a
pointer are zero, which distinguishes pointers from immediate data.
In an immediate datatype, the tag bits determine the type of the object. In
non-immediate datatypes, the first byte in the cell contains the secondary type
indicator, and distinguishes between different types of non immediate data. The
use of the remaining bytes differs for each type of object. For instance, a
cons cell consists of three words:
| | |---------|----------|
|CONS| | |
|---------|----------|
| car-pointer |
|--------------------|
| cdr-pointer |
|--------------------|
|
There is one important function which tells the type of an object, plus several
macros which group several tests.
- C type: cl_object
- This is the type of a lisp object. For your C/C++ program, a
cl_object
can be either a fixnum, a character, or a pointer to a union of structures (See
the header `object.h'). The actual interpretation of that object can be
guessed with the macro type_of.
For example, if x is of type cl_object, and it is of type fixnum,
we may retrieve its value
| | if (type_of(x) == t_fixnum)
printf("Integer value: %d\n", fix(x));
|
If x is of type cl_object and it does not contain an immediate
datatype, you may inspect the cell associated to the lisp object using x
as a pointer. For example,
| | if (type_of(x) == t_cons)
printf("CAR = %x, CDR = %x\n", x->cons.car, x->cons.cdr);
else if (type_of(x) == t_string)
printf("String: %s\n", x->string.self);
|
You should see the following sections and the header `object.h' to learn
how to use the different fields of a cl_object pointer.
- C type: cl_type
- Enumeration type which distinguishes the different types of lisp objects. The
most important values are t_cons, t_fixnum, t_character, t_bignum, t_ratio,
t_shortfloat, t_longfloat, t_complex, t_symbol, t_package, t_hashtable,
t_array, t_vector, t_string, t_bitvector, t_stream, t_random, t_readtable,
t_pathname, t_bytecodes, t_cfun, t_cclosure, t_gfun, t_instance, t_foreign and
t_thread.
- Function: cl_type type_of (cl_object O)
- If O is a valid lisp object,
type_of(O) returns an integer
denoting the type that lisp object. That integer is one of the values of the
enumeration type cl_type.
- Function: bool FIXNUMP (cl_object o)
-
- Function: bool CHARACTERP (cl_object o)
-
- Function: bool CONSP (cl_object o)
-
- Function: bool LISTP (cl_object o)
-
- Function: bool ATOM (cl_object o)
-
- Function: bool ARRAYP (cl_object o)
-
- Function: bool VECTORP (cl_object o)
-
- Function: bool STRINGP (cl_object o)
Different macros that check whether o belongs to the specified type.
These checks have been optimized, and are preferred over several calls to
type_of.
- Function: bool IMMEDIATE (cl_object o)
- Tells whether o is an immediate datatype.
3.2 Constructing objects
On each of the following sections we will document the standard interface for
building objects of different types. For some objects, though, it is too
difficult to make a C interface that resembles all of the functionality in the
lisp environment. In those cases you need to
- build the objects from their textual representation, or
- use the evaluator to build these objects.
The first way makes use of a C or Lisp string to construct an object. The two
functions you need to know are the following ones.
- Function: cl_object c_string_to_object (const char *s)
-
- Function: cl_object string_to_object (cl_object o)
c_string_to_object builds a lisp object from a C string which contains a
suitable representation of a lisp object. string_to_object performs the
same task, but uses a lisp string, and therefore it is less useful. Two
examples of their use
| | /* Using a C string */
cl_object array1 = c_string_to_object("#(1 2 3 4)");
/* Using a Lisp string */
cl_object string = make_simple_string("#(1 2 3 4)");
cl_object array2 = string_to_object(string);
|
3.3 Integers
Common-Lisp
fixnum is a small integer, which ideally occupies only a word of memory and
which is between the values MOST-NEGATIVE-FIXNUM and
MOST-POSITIVE-FIXNUM. A bignum is any integer which is not a fixnum and
it is only constrained by the amount of memory available to represent it.
In ECL a fixnum is an integer that, together with the tag bits, fits in a
word of memory. The size of a word, and thus the size of a fixnum, varies from
one architecture to another, and you should refer to the types and constants in
the `ecl.h' header to make sure that your C extensions are portable.
All other integers are stored as bignums, they are not immediate objects, they
take up a variable amount of memory and the GNU Multiprecision Library is
required to create, manipulate and calculate with them.
- C type: cl_fixnum
- This is a C signed integer type capable of holding a whole fixnum without any
loss of precision. The opposite is not true, and you may create a
cl_fixnum which exceeds the limits of a fixnum and should be stored as a
bignum.
- C type: cl_index
- This is a C unsigned integer type capable of holding a nonnegative fixnum without
loss of precision. Typically, a
cl_index is used as an index into an array,
or into a proper list, etc.
- Constant: MOST_NEGATIVE_FIXNUM
-
- Constant: MOST_POSITIVE_FIXNUM
- These constants mark the limits of a fixnum.
- Function: bool FIXNUM_MINUSP (cl_object o)
-
- Function: bool FIXNUM_PLUSP (cl_object o)
- These functions perform the checks (o < 0) and (0 <= o),
respectively.
- Function: cl_object MAKE_FIXNUM (cl_fixnum n)
-
- Function: cl_fixnum fix (cl_object o)
MAKE_FIXNUM and fix convert from an integer to a lisp object
of fixnum type and vice versa. These functions no not check their arguments.
- Function: cl_fixnum fixint (cl_object o)
- Converts a lisp fixnum to a C integer of the appropriate size. Signals an error
if o is not of fixnum type.
- Function: cl_index fixnnint (cl_object o)
- Similar to
fixint but also ensures that o is not negative.
3.4 Characters
ECL has only one type of characters, which fits in the C type char.
The following constants and functions operate on characters.
- Constant: CHAR_CODE_LIMIT
- Each character is assigned an integer code which ranges from 0 to
(CHAR_CODE_LIMIT-1).
- Function: cl_fixnum CHAR_CODE (cl_object o)
-
- Function: cl_fixnum char_code (cl_object o)
- Returns the integer code associated to a lisp character. Only
char_code
checks its arguments.
- Function: cl_object CODE_CHAR (cl_fixnum o)
- Returns the lisp character associated to an integer code. It does not check
its arguments.
- Function: cl_object coerce_to_character (cl_object o)
- Coerces a lisp object to type character. Valid arguments are a character,
or a string designator of length 1. In all other cases an error is signaled.
- Function: bool char_eq (cl_object x, cl_object y)
-
- Function: bool char_equal (cl_object x, cl_object y)
- Compare two characters for equality.
char_eq take case into account and
char_equal ignores it.
- Function: int char_cmp (cl_object x, cl_object y)
-
- Function: int char_compare (cl_object x, cl_object y)
- Compare the relative order of two characters.
char_cmp takes care of
case and char_compare converts all characters to uppercase before
comparing them.
3.5 Arrays
An array is an aggregate of data of a common type, which can be accessed with
one or more nonnegative indices. ECL stores arrays as a C structure with a
pointer to the region of memory which contains the actual data. The cell
of an array datatype varies depending on whether it is a vector, a bytevector,
a multidimensional array or a string.
If x contains a vector, you can access the following fields:
x->vector.elttype
- The type of the elements of the vector.
x->vector.dim
- The maximum number of elements.
x->vector.fillp
- Actual number of elements in the vector or "fill pointer".
x->vector.self
- Union of pointers of different types. You should choose the right pointer
depending on
x->vector.elltype
x->vector.hasfillp
- Whether
x->vector.fillp can be smaller than x->vector.dim.
If x contains a multidimensional array, the cell elements become
x->array.elttype
- The type of the elements of the array.
x->array.dim
- Number of elements in the array.
x->array.rank
- Number of dimensions of the array.
x->array.dims[]
- Array with the dimensions of the array. The elements range from
x->array.dim[0] to x->array.dim[x->array.rank-1].
x->array.self
- Union of pointers to the actual data. You should choose the right pointer
depending on
x->array.elltype.
x->array.rank
- Whether
x->vector.fillp can be smaller than x->vector.dim.
Bitvectors and strings are treated separately.
Each array is of an specialized type which is the type of the elements of the
array. ECL has arrays only a few following specialized types, and for each
of these types there is a C integer which is the corresponding value of
x->array.elttype or x->vector.elttype. We list those types
together with the C constant that denotes that type:
- T
aet_object
- CHARACTER
aet_ch
- FIXNUM
aet_fix
- BIT
aet_bit
- SHORT-FLOAT
aet_sf
- LONG-FLOAT
aet_lf
- Function: cl_elttype array_elttype (cl_object o)
- Returns the element type of the array o, which can be a string, a
bitvector, vector, or a multidimensional array. For example, the code
array_elttype(c_string_to_object("\"AAA\"")) returns aet_ch,
while the array_elttype(c_string_to_object("#(A B C)")) returns
aet_object.
- Function: cl_object aref (cl_object array, cl_index index)
-
- Function: cl_object aset (cl_object array, cl_index index, cl_object value)
- These functions are used to retrieve and set the elements of an array. The
elements are accessed with one index, index, as in the lisp function
ROW-MAJOR-AREF. For example
| | cl_object array = c_string_to_object("#2A((1 2) (3 4))");
cl_object x = aref(array, 3);
clLprint(1, x); /* Outputs 4 */
aset(array, 3, MAKE_FIXNUM(5));
clLprint(1, array); /* Outputs #2A((1 2) (3 5)) */
|
- Function: cl_object aref1 (cl_object vector, cl_index index)
-
- Function: cl_object aset1 (cl_object vector, cl_index index, cl_object value)
- These functions are similar to
aref and aset, but they operate on
vectors.
| | cl_object array = c_string_to_object("#(1 2 3 4)");
cl_object x = aref1(array, 3);
clLprint(1, x); /* Outputs 4 */
aset1(array, 3, MAKE_FIXNUM(5));
clLprint(1, array); /* Outputs #(1 2 3 5) */
|
3.6 Strings
A string, both in Common-Lisp
characters. Therefore, almost everything mentioned in the section of arrays
remains valid here. The only important difference is that ECL stores
strings as a lisp object with a pointer to a zero terminated C string. Thus, if
a string has n characters, ECL will reserve n+1 bytes for the
string. This allows us to pass the string self pointer to any C
routine.
If x is a lisp object of type string, we can access the following fields:
x->string.dim
- Maximum number of characters that it can contain.
x->string.fillp
- Actual number of characters in the string.
x->string.self
- Pointer to the characters.
x->string.hasfillp
- True if
x->string.fillp can be smaller than x->string.dim.
- Function: cl_object make_simple_string (char *s)
-
- Function: cl_object make_string_copy (char *s)
- Both routines build a lisp string from a C string.
make_string_copy
allocates new space and copies the content of the string to
it. make_simple_string simply uses the memory pointed by s, which
should not be deallocated. Both routines use strlen to calculate the
length of the string.
3.7 Bitvectors
3.8 Streams
3.9 Structures
3.10 Instances
3.11 Bytecodes
A bytecodes object is a lisp object with a piece of code that can be
interpreted. The objects of type t_bytecode are implicitly constructed
by a call to eval, but can also be explicitly constructed with the
make_lambda function.
- Function: cl_object cl_safe_eval (cl_object form, cl_object env, cl_object err_value
-
- Function: cl_object cl_eval (cl_object form)
cl_safe_eval evaluates form in the lexical environment env,
which can be nil. Before evaluating it, the expression form must
be bytecompiled. cl_eval is the equivalent of cl_safe_eval but
without environment and with err_value set to nil. It exists only
for compatibility with previous versions.
| | cl_object form = c_string_to_object("(print 1)");
cl_safe_eval(form,Cnil);
cl_safe_eval(form, Cnil);
|
- Function: cl_object si_make_lambda (cl_object name, cl_object def)
- Builds an interpreted lisp function with name given by the symbol name
and body given by def. For instance, we would achieve the equivalent of
| | (funcall #'(lambda (x y) (block foo (+ x y)))
1 2)
|
with the following code
| | cl_object def = c_string_to_object("((x y) (+ x y))");
cl_object name = _intern("foo")
cl_object fun = si_make_lambda(name, def);
return funcall(fun, MAKE_FIXNUM(1), MAKE_FIXNUM(2));
|
Notice that si_safe_lambda performs a bytecodes compilation
of the definition and thus it may signal some errors. Such errors are not
handled by the routine itself you might consider using cl_safe_eval
or cl_eval instead:
| | cl_object def = c_string_to_object("#'(lambda-block foo (x y) (+ x y))");
cl_object fun = cl_eval(def);
return funcall(fun, MAKE_FIXNUM(1), MAKE_FIXNUM(2));
|
This document was generated
by Juan Jose Garcia Ripoll on May, 30 2005
using texi2html