[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

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  
3.2 Constructing objects  
3.3 Integers  
3.4 Characters  
3.5 Arrays  
3.6 Strings  
3.7 Bitvectors  
3.8 Streams  
3.9 Structures  
3.10 Instances  
3.11 Bytecodes  

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

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.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

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

  1. build the objects from their textual representation, or
  2. 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);

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

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.

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.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

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.

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.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

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:

The type of the elements of the vector.
The maximum number of elements.
Actual number of elements in the vector or "fill pointer".
Union of pointers of different types. You should choose the right pointer depending on x->vector.elltype
Whether x->vector.fillp can be smaller than x->vector.dim.

If x contains a multidimensional array, the cell elements become

The type of the elements of the array.
Number of elements in the array.
Number of dimensions of the array.
Array with the dimensions of the array. The elements range from x->array.dim[0] to x->array.dim[x->array.rank-1].
Union of pointers to the actual data. You should choose the right pointer depending on x->array.elltype.
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:


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) */

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

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:

Maximum number of characters that it can contain.
Actual number of characters in the string.
Pointer to the characters.
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.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

3.7 Bitvectors

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

3.8 Streams

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

3.9 Structures

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

3.10 Instances

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

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);

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));

[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by Juan Jose Garcia Ripoll on May, 30 2005 using texi2html