r"""
Library interface to Embeddable Common Lisp (ECL)
"""
include '../ext/interrupt.pxi'
include "../ext/cdefs.pxi"
from sage.rings.integer cimport Integer
from sage.rings.rational cimport Rational
from sage.rings.rational import Rational
cdef bint bint_symbolp(cl_object obj):
return not(cl_symbolp(obj) == Cnil)
cdef bint bint_numberp(cl_object obj):
return not(cl_numberp(obj) == Cnil)
cdef bint bint_integerp(cl_object obj):
return not(cl_integerp(obj) == Cnil)
cdef bint bint_rationalp(cl_object obj):
return not(cl_rationalp(obj) == Cnil)
cdef extern from "stdlib.h":
void abort()
cdef extern from "signal.h":
int signal_raise "raise"(int sig)
struct Sigaction "sigaction":
pass
int sigaction(int sig, Sigaction * act, Sigaction * oact)
int SIGINT, SIGBUS, SIGSEGV
cdef extern from "eclsig.c":
int ecl_sig_on() except 0
void ecl_sig_off()
cdef Sigaction ecl_sigint_handler
cdef Sigaction ecl_sigbus_handler
cdef Sigaction ecl_sigsegv_handler
cdef mpz_t* ecl_mpz_from_bignum(cl_object obj)
cdef cl_object ecl_bignum_from_mpz(mpz_t* num)
cdef cl_object string_to_object(char * s):
return ecl_read_from_cstring(s)
cdef cl_object insert_node_after(cl_object node,cl_object value):
cdef cl_object next,newnode
next=cl_cadr(node)
newnode=cl_cons(value,cl_cons(next,node))
cl_rplaca(cl_cdr(node),newnode)
if next != Cnil:
cl_rplacd(cl_cdr(next),newnode)
return newnode
cdef void remove_node(cl_object node):
cdef cl_object next, prev
next=cl_cadr(node)
prev=cl_cddr(node)
if next != Cnil:
cl_rplacd(cl_cdr(next),prev)
if prev != Cnil:
cl_rplaca(cl_cdr(prev),next)
cdef cl_object list_of_objects
cdef cl_object safe_eval_clobj
cdef cl_object safe_apply_clobj
cdef cl_object safe_funcall_clobj
cdef cl_object read_from_string_clobj
cdef bint ecl_has_booted = 0
def test_sigint_before_ecl_sig_on():
"""
TESTS:
If an interrupt arrives *before* ecl_sig_on(), we should get an
ordinary KeyboardInterrupt::
sage: from sage.libs.ecl import test_sigint_before_ecl_sig_on
sage: try:
... test_sigint_before_ecl_sig_on()
... except KeyboardInterrupt:
... print "Success!"
...
Success!
"""
signal_raise(SIGINT)
ecl_sig_on()
abort()
def init_ecl():
r"""
Internal function to initialize ecl. Do not call.
This function initializes the ECL library for use within Python.
This routine should only be called once and importing the ecl library
interface already does that, so do not call this yourself.
EXAMPLES::
sage: from sage.libs.ecl import *
At this point, init_ecl() has run. Explicitly executing it
gives an error::
sage: init_ecl()
Traceback (most recent call last):
...
RuntimeError: ECL is already initialized
"""
global list_of_objects
global safe_eval_clobj
global safe_apply_clobj
global safe_funcall_clobj
global read_from_string_clobj
global ecl_has_booted
cdef char *argv[1]
cdef Sigaction sage_action[32]
cdef int i
if ecl_has_booted:
raise RuntimeError, "ECL is already initialized"
ecl_set_option(ECL_OPT_SET_GMP_MEMORY_FUNCTIONS,0);
argv[0]="sage"
for i in range(1,32):
sigaction(i, NULL, &sage_action[i])
ecl_set_option(ECL_OPT_SIGNAL_HANDLING_THREAD, 0)
cl_boot(1, argv)
sigaction(SIGINT, NULL, &ecl_sigint_handler)
sigaction(SIGBUS, NULL, &ecl_sigbus_handler)
sigaction(SIGSEGV, NULL, &ecl_sigsegv_handler)
for i in range(1,32):
sigaction(i, &sage_action[i], NULL)
list_of_objects=cl_cons(Cnil,cl_cons(Cnil,Cnil))
cl_set(string_to_object("*SAGE-LIST-OF-OBJECTS*"),list_of_objects)
read_from_string_clobj=cl_eval(string_to_object("(symbol-function 'read-from-string)"))
cl_eval(string_to_object("""
(defun sage-safe-eval (form)
(handler-case
(values (eval form))
(serious-condition (cnd)
(values nil (princ-to-string cnd)))))
"""))
safe_eval_clobj=cl_eval(string_to_object("(symbol-function 'sage-safe-eval)"))
cl_eval(string_to_object("""
(defun sage-safe-apply (func args)
(handler-case
(values (apply func args))
(serious-condition (cnd)
(values nil (princ-to-string cnd)))))
"""))
safe_apply_clobj=cl_eval(string_to_object("(symbol-function 'sage-safe-apply)"))
cl_eval(string_to_object("""
(defun sage-safe-funcall (func arg)
(handler-case
(values (funcall func arg))
(serious-condition (cnd)
(values nil (princ-to-string cnd)))))
"""))
safe_funcall_clobj=cl_eval(string_to_object("(symbol-function 'sage-safe-funcall)"))
ecl_has_booted = 1
cdef cl_object ecl_safe_eval(cl_object form) except NULL:
"""
TESTS:
Test interrupts::
sage: from sage.libs.ecl import *
sage: from sage.tests.interrupt import *
sage: ecl_eval("(setf i 0)")
<ECL: 0>
sage: inf_loop=ecl_eval("(defun infinite() (loop (incf i)))")
sage: interrupt_after_delay(1000)
sage: inf_loop()
Traceback (most recent call last):
...
RuntimeError: ECL says: Console interrupt
"""
ecl_sig_on()
cl_funcall(2,safe_eval_clobj,form)
ecl_sig_off()
if ecl_nvalues > 1:
raise RuntimeError, "ECL says: "+ecl_base_string_pointer_safe(ecl_values(1))
else:
return ecl_values(0)
cdef cl_object ecl_safe_funcall(cl_object func, cl_object arg) except NULL:
cdef cl_object l
l = cl_cons(func,cl_cons(arg,Cnil));
ecl_sig_on()
cl_apply(2,safe_funcall_clobj,cl_cons(func,cl_cons(arg,Cnil)))
ecl_sig_off()
if ecl_nvalues > 1:
raise RuntimeError, "ECL says: "+ecl_base_string_pointer_safe(ecl_values(1))
else:
return ecl_values(0)
cdef cl_object ecl_safe_apply(cl_object func, cl_object args) except NULL:
ecl_sig_on()
cl_funcall(3,safe_apply_clobj,func,args)
ecl_sig_off()
if ecl_nvalues > 1:
raise RuntimeError, "ECL says: "+ecl_base_string_pointer_safe(ecl_values(1))
else:
return ecl_values(0)
cdef cl_object ecl_safe_read_string(char * s) except NULL:
cdef cl_object o
o = ecl_cstring_to_base_string_or_nil(s)
o = ecl_safe_funcall(read_from_string_clobj,o)
return o
def shutdown_ecl():
r"""
Shut down ecl. Do not call.
Given the way that ECL is used from python, it is very difficult to ensure
that no ECL objects exist at a particular time. Hence, destroying ECL is a
risky proposition.
EXAMPLE::
sage: from sage.libs.ecl import *
sage: shutdown_ecl()
"""
cl_shutdown()
def print_objects():
r"""
Print GC-protection list
Diagnostic function. ECL objects that are bound to Python objects need to
be protected from being garbage collected. We do this by including them
in a doubly linked list bound to the global ECL symbol
*SAGE-LIST-OF-OBJECTS*. Only non-immediate values get included, so
small integers do not get linked in. This routine prints the values
currently stored.
EXAMPLE::
sage: from sage.libs.ecl import *
sage: a=EclObject("hello")
sage: b=EclObject(10)
sage: c=EclObject("world")
sage: print_objects() #random because previous test runs can have left objects
NIL
WORLD
HELLO
"""
cdef cl_object c
c = list_of_objects
while True:
print ecl_base_string_pointer_safe(cl_write_to_string(1,cl_car(c)))
c=cl_cadr(c)
if c == Cnil:
break
cdef cl_object python_to_ecl(pyobj) except NULL:
cdef bytes s
cdef cl_object L, ptr
if isinstance(pyobj,bool):
if pyobj:
return Ct
else:
return Cnil
elif pyobj is None:
return Cnil
elif isinstance(pyobj,int):
return ecl_make_integer(pyobj)
elif isinstance(pyobj,long):
if pyobj >= MOST_NEGATIVE_FIXNUM and pyobj <= MOST_POSITIVE_FIXNUM:
return ecl_make_integer(pyobj)
else:
return python_to_ecl(Integer(pyobj))
elif isinstance(pyobj,float):
return ecl_make_doublefloat(pyobj)
elif isinstance(pyobj,unicode):
s=<bytes>(str(pyobj))
return ecl_safe_read_string(s)
elif isinstance(pyobj,bytes):
s=<bytes>pyobj
return ecl_safe_read_string(s)
elif isinstance(pyobj,Integer):
if pyobj >= MOST_NEGATIVE_FIXNUM and pyobj <= MOST_POSITIVE_FIXNUM:
return ecl_make_integer(pyobj)
else:
return ecl_bignum_from_mpz( (<Integer>pyobj).get_value() )
elif isinstance(pyobj,Rational):
return ecl_make_ratio(
python_to_ecl( (<Rational>pyobj).numerator() ),
python_to_ecl( (<Rational>pyobj).denominator()))
elif isinstance(pyobj,EclObject):
return (<EclObject>pyobj).obj
elif isinstance(pyobj,list):
if len(pyobj) == 0:
return Cnil
else:
L=cl_cons(python_to_ecl(pyobj[0]),Cnil)
ptr=L
for a in pyobj[1:]:
cl_rplacd(ptr,cl_cons(python_to_ecl(a),Cnil))
ptr=cl_cdr(ptr)
return L
elif isinstance(pyobj,tuple):
if len(pyobj) == 0:
return Cnil
elif len(pyobj) == 1:
return python_to_ecl(pyobj[0])
else:
L=cl_cons(python_to_ecl(pyobj[0]),Cnil)
ptr=L
for a in pyobj[1:-1]:
cl_rplacd(ptr,cl_cons(python_to_ecl(a),Cnil))
ptr=cl_cdr(ptr)
cl_rplacd(ptr,python_to_ecl(pyobj[-1]))
return L
else:
raise TypeError,"Unimplemented type for python_to_ecl"
cdef ecl_to_python(cl_object o):
cdef Integer N
if o == Cnil:
return None
elif bint_fixnump(o):
return Integer(ecl_fixint(o))
elif bint_integerp(o):
N = Integer.__new__(Integer)
N.set_from_mpz(ecl_mpz_from_bignum(o))
return N
elif bint_rationalp(o):
return Rational((ecl_to_python(cl_numerator(o)),ecl_to_python(cl_denominator(o))))
elif bint_floatp(o):
return ecl_to_double(o)
elif o == Ct:
return True
elif bint_consp(o):
L=[]
while o != Cnil:
L.append(ecl_to_python(cl_car(o)))
o = cl_cdr(o)
if not(bint_listp(o)):
L.append(ecl_to_python(o))
return tuple(L)
return L
else:
return ecl_base_string_pointer_safe(cl_write_to_string(1,o))
cdef class EclObject:
r"""
Python wrapper of ECL objects
The ``EclObject`` forms a wrapper around ECL objects. The wrapper ensures
that the data structure pointed to is protected from garbage collection in
ECL by installing a pointer to it from a global data structure within the
scope of the ECL garbage collector. This pointer is destroyed upon
destruction of the EclObject.
EclObject() takes a Python object and tries to find a representation of it
in Lisp.
EXAMPLES:
Python lists get mapped to LISP lists. None and Boolean values to
appropriate values in LISP::
sage: from sage.libs.ecl import *
sage: EclObject([None,true,false])
<ECL: (NIL T NIL)>
Numerical values are translated to the appropriate type in LISP::
sage: EclObject(1)
<ECL: 1>
sage: EclObject(10**40)
<ECL: 10000000000000000000000000000000000000000>
Floats in Python are IEEE double, which LISP has as well. However,
the printing of floating point types in LISP depends on settings::
sage: a = EclObject(float(10**40))
sage: ecl_eval("(setf *read-default-float-format* 'single-float)")
<ECL: SINGLE-FLOAT>
sage: a
<ECL: 9.999999999999999d39>
sage: ecl_eval("(setf *read-default-float-format* 'double-float)")
<ECL: DOUBLE-FLOAT>
sage: a
<ECL: 9.999999999999999e39>
Tuples are translated to dotted lists::
sage: EclObject( (false, true))
<ECL: (NIL . T)>
Strings are fed to the reader, so a string normally results in a symbol::
sage: EclObject("Symbol")
<ECL: SYMBOL>
But with proper quotation one can construct a lisp string object too::
sage: EclObject('"Symbol"')
<ECL: "Symbol">
EclObjects translate to themselves, so one can mix::
sage: EclObject([1,2,EclObject([3])])
<ECL: (1 2 (3))>
Calling an EclObject translates into the appropriate LISP ``apply'',
where the argument is transformed into an EclObject itself, so one can
flexibly apply LISP functions::
sage: car=EclObject("car")
sage: cdr=EclObject("cdr")
sage: car(cdr([1,2,3]))
<ECL: 2>
and even construct and evaluate arbitrary S-expressions::
sage: eval=EclObject("eval")
sage: quote=EclObject("quote")
sage: eval([car, [cdr, [quote,[1,2,3]]]])
<ECL: 2>
TESTS:
We check that multiprecision integers are converted correctly::
sage: i = 10 ^ (10 ^ 5)
sage: EclObject(i) == EclObject(str(i))
True
sage: EclObject(-i) == EclObject(str(-i))
True
sage: EclObject(i).python() == i
True
sage: EclObject(-i).python() == -i
True
"""
cdef cl_object obj
cdef cl_object node
cdef void set_obj(EclObject self, cl_object o):
if self.node:
remove_node(self.node)
self.node=NULL
self.obj=o
if not(bint_fixnump(o) or bint_characterp(o) or bint_nullp(o)):
self.node=insert_node_after(list_of_objects,o)
def __init__(self,*args):
r"""
Create an EclObject
See EclObject for full documentation.
EXAMPLES::
sage: from sage.libs.ecl import *
sage: EclObject([None,true,false])
<ECL: (NIL T NIL)>
"""
if len(args) != 0:
self.set_obj(python_to_ecl(args[0]))
def __reduce__(self):
r"""
This is used for pickling. Not implemented
Ecl does not natively support serialization of its objects, so the
python wrapper class EclObject does not support pickling. There are
independent efforts for developing serialization for Common Lisp, such as
CL-STORE. Look at those if you need serialization of ECL objects.
EXAMPLES::
sage: from sage.libs.ecl import *
sage: s=EclObject([1,2,3])
sage: s.__reduce__()
Traceback (most recent call last):
...
NotImplementedError: EclObjects do not have a pickling method
sage: s==loads(dumps(s))
Traceback (most recent call last):
...
NotImplementedError: EclObjects do not have a pickling method
"""
raise NotImplementedError, "EclObjects do not have a pickling method"
def python(self):
r"""
Convert an EclObject to a python object.
EXAMPLES::
sage: from sage.libs.ecl import *
sage: L=EclObject([1,2,("three",'"four"')])
sage: L.python()
[1, 2, ('THREE', '"four"')]
"""
return ecl_to_python(self.obj)
def __dealloc__(self):
r"""
Deallocate EclObject
It is important to remove the GC preventing reference to the object upon
deletion of the wrapper.
EXAMPLES::
sage: from sage.libs.ecl import *
sage: L=EclObject("symbol")
sage: del L
"""
if self.node:
remove_node(self.node)
def __repr__(self):
r"""
Produce a string representation suitable for interactive printing.
Converts the wrapped LISP object to a string, decorated in such a way that
it can be recognised as a LISP object.
EXAMPLES::
sage: from sage.libs.ecl import *
sage: L=EclObject("symbol")
sage: repr(L)
'<ECL: SYMBOL>'
"""
return "<ECL: "+str(self)+">"
def __str__(self):
r"""
Produce a string representation.
Converts the wrapped LISP object to a string and returns that as a Python
string.
EXAMPLES::
sage: from sage.libs.ecl import *
sage: L=EclObject("symbol")
sage: str(L)
'SYMBOL'
"""
cdef cl_object s
s = cl_write_to_string(1,self.obj)
return ecl_base_string_pointer_safe(s)
def __hash__(self):
r"""
Return a hash value of the object
Returns the hash value returned by SXHASH, which is a routine that is
specified in Common Lisp. According to the specification, lisp objects that
are EQUAL have the same SXHASH value. Since two EclObjects are equal if
their wrapped objects are EQUAL according to lisp, this is compatible with
Python's concept of hash values.
It is not possible to enforce immutability of lisp objects, so care should
be taken in using EclObjects as dictionary keys.
EXAMPLES::
sage: from sage.libs.ecl import *
sage: L=EclObject([1,2])
sage: L
<ECL: (1 2)>
sage: hash(L) #random
463816586
sage: L.rplacd(EclObject(3))
sage: L
<ECL: (1 . 3)>
sage: hash(L) #random
140404060
"""
return ecl_fixint(cl_sxhash(self.obj))
def __call__(self, *args):
r"""
Apply self to arguments.
EXAMPLES::
sage: from sage.libs.ecl import *
sage: sqr=EclObject("(lambda (x) (* x x))").eval()
sage: sqr(10)
<ECL: 100>
"""
lispargs = EclObject(list(args))
return ecl_wrap(ecl_safe_apply(self.obj,(<EclObject>lispargs).obj))
def __richcmp__(left, right, int op):
r"""
Comparison test.
An EclObject is not equal to any non-EclObject. Two EclObjects are equal
if their wrapped lisp objects are EQUAL. Since LISP has no univeral ordering,
less than and greater than tests are not implemented for EclObjects.
EXAMPLES::
sage: from sage.libs.ecl import *
sage: a=EclObject(1)
sage: b=EclObject(2)
sage: a==b
False
sage: a<b
Traceback (most recent call last):
...
NotImplementedError: EclObjects can only be compared for equality
sage: EclObject("<")(a,b)
<ECL: T>
"""
if op == 2:
if not(isinstance(left,EclObject)) or not(isinstance(right,EclObject)):
return False
else:
return bint_equal((<EclObject>left).obj,(<EclObject>right).obj)
elif op == 3:
if not(isinstance(left,EclObject)) or not(isinstance(right,EclObject)):
return True
else:
return not(bint_equal((<EclObject>left).obj,(<EclObject>right).obj))
raise NotImplementedError,"EclObjects can only be compared for equality"
def __iter__(self):
r"""
Implements the iterator protocol for EclObject.
EclObject implements the iterator protocol for lists. This means
one can use an EclObject in the context where an iterator is
expected (for instance, in a list comprehension or in a for loop).
The iterator produces EclObjects wrapping the members of the list that
the original EclObject wraps.
The wrappers returned are all newly constructed but refer to the
original members of the list iterated over. This is usually what is
intended but, just as in Python, can cause surprises if the original
object is changed between calls to the iterator.
Since EclObject translates Python Lists into LISP lists and Python
tuples into LISP "dotted" lists (lists for which the final CDR is not
necessarily NIL), and both these python structures are iterable, the
corresponding EclObjects are iterable as well.
EclObjects that are not lists are not iterable.
EXAMPLES::
sage: from sage.libs.ecl import *
sage: [i for i in EclObject("(1 2 3)")]
[<ECL: 1>, <ECL: 2>, <ECL: 3>]
sage: [i for i in EclObject("(1 2 . 3)")]
[<ECL: 1>, <ECL: 2>, <ECL: 3>]
sage: [i for i in EclObject("NIL")]
[]
TESTS:
These show that Python lists and tuples behave as
described above::
sage: [i for i in EclObject([1,2,3])]
[<ECL: 1>, <ECL: 2>, <ECL: 3>]
sage: [i for i in EclObject((1,2,3))]
[<ECL: 1>, <ECL: 2>, <ECL: 3>]
This tests that we cannot iterate EclObjects we shouldn't,
as described above::
sage: [i for i in EclObject("T")]
Traceback (most recent call last):
...
TypeError: ECL object is not iterable
"""
return EclListIterator(self)
def eval(self):
r"""
Evaluate object as an S-Expression
EXAMPLES::
sage: from sage.libs.ecl import *
sage: S=EclObject("(+ 1 2)")
sage: S
<ECL: (+ 1 2)>
sage: S.eval()
<ECL: 3>
"""
cdef cl_object o
o=ecl_safe_eval(self.obj)
if o == NULL:
raise RuntimeError,"ECL runtime error"
return ecl_wrap(o)
def cons(self,EclObject d):
r"""
apply cons to self and argument and return the result.
EXAMPLES::
sage: from sage.libs.ecl import *
sage: a=EclObject(1)
sage: b=EclObject(2)
sage: a.cons(b)
<ECL: (1 . 2)>
"""
return ecl_wrap(cl_cons(self.obj,d.obj))
def rplaca(self,EclObject d):
r"""
Destructively replace car(self) with d.
EXAMPLES::
sage: from sage.libs.ecl import *
sage: L=EclObject((1,2))
sage: L
<ECL: (1 . 2)>
sage: a=EclObject(3)
sage: L.rplaca(a)
sage: L
<ECL: (3 . 2)>
"""
if not(bint_consp(self.obj)):
raise TypeError,"rplaca can only be applied to a cons"
cl_rplaca(self.obj, d.obj)
def rplacd(self,EclObject d):
r"""
Destructively replace cdr(self) with d.
EXAMPLES::
sage: from sage.libs.ecl import *
sage: L=EclObject((1,2))
sage: L
<ECL: (1 . 2)>
sage: a=EclObject(3)
sage: L.rplacd(a)
sage: L
<ECL: (1 . 3)>
"""
if not(bint_consp(self.obj)):
raise TypeError,"rplacd can only be applied to a cons"
cl_rplacd(self.obj, d.obj)
def car(self):
r"""
Return the car of self
EXAMPLES::
sage: from sage.libs.ecl import *
sage: L=EclObject([[1,2],[3,4]])
sage: L.car()
<ECL: (1 2)>
sage: L.cdr()
<ECL: ((3 4))>
sage: L.caar()
<ECL: 1>
sage: L.cadr()
<ECL: (3 4)>
sage: L.cdar()
<ECL: (2)>
sage: L.cddr()
<ECL: NIL>
"""
if not(bint_consp(self.obj)):
raise TypeError,"car can only be applied to a cons"
return ecl_wrap(cl_car(self.obj))
def cdr(self):
r"""
Return the cdr of self
EXAMPLES::
sage: from sage.libs.ecl import *
sage: L=EclObject([[1,2],[3,4]])
sage: L.car()
<ECL: (1 2)>
sage: L.cdr()
<ECL: ((3 4))>
sage: L.caar()
<ECL: 1>
sage: L.cadr()
<ECL: (3 4)>
sage: L.cdar()
<ECL: (2)>
sage: L.cddr()
<ECL: NIL>
"""
if not(bint_consp(self.obj)):
raise TypeError,"cdr can only be applied to a cons"
return ecl_wrap(cl_cdr(self.obj))
def caar(self):
r"""
Return the caar of self
EXAMPLES::
sage: from sage.libs.ecl import *
sage: L=EclObject([[1,2],[3,4]])
sage: L.car()
<ECL: (1 2)>
sage: L.cdr()
<ECL: ((3 4))>
sage: L.caar()
<ECL: 1>
sage: L.cadr()
<ECL: (3 4)>
sage: L.cdar()
<ECL: (2)>
sage: L.cddr()
<ECL: NIL>
"""
if not(bint_consp(self.obj) and bint_consp(cl_car(self.obj))):
raise TypeError,"caar can only be applied to a cons"
return ecl_wrap(cl_caar(self.obj))
def cadr(self):
r"""
Return the cadr of self
EXAMPLES::
sage: from sage.libs.ecl import *
sage: L=EclObject([[1,2],[3,4]])
sage: L.car()
<ECL: (1 2)>
sage: L.cdr()
<ECL: ((3 4))>
sage: L.caar()
<ECL: 1>
sage: L.cadr()
<ECL: (3 4)>
sage: L.cdar()
<ECL: (2)>
sage: L.cddr()
<ECL: NIL>
"""
if not(bint_consp(self.obj) and bint_consp(cl_cdr(self.obj))):
raise TypeError,"cadr can only be applied to a cons"
return ecl_wrap(cl_cadr(self.obj))
def cdar(self):
r"""
Return the cdar of self
EXAMPLES::
sage: from sage.libs.ecl import *
sage: L=EclObject([[1,2],[3,4]])
sage: L.car()
<ECL: (1 2)>
sage: L.cdr()
<ECL: ((3 4))>
sage: L.caar()
<ECL: 1>
sage: L.cadr()
<ECL: (3 4)>
sage: L.cdar()
<ECL: (2)>
sage: L.cddr()
<ECL: NIL>
"""
if not(bint_consp(self.obj) and bint_consp(cl_car(self.obj))):
raise TypeError,"cdar can only be applied to a cons"
return ecl_wrap(cl_cdar(self.obj))
def cddr(self):
r"""
Return the cddr of self
EXAMPLES::
sage: from sage.libs.ecl import *
sage: L=EclObject([[1,2],[3,4]])
sage: L.car()
<ECL: (1 2)>
sage: L.cdr()
<ECL: ((3 4))>
sage: L.caar()
<ECL: 1>
sage: L.cadr()
<ECL: (3 4)>
sage: L.cdar()
<ECL: (2)>
sage: L.cddr()
<ECL: NIL>
"""
if not(bint_consp(self.obj) and bint_consp(cl_cdr(self.obj))):
raise TypeError,"cddr can only be applied to a cons"
return ecl_wrap(cl_cddr(self.obj))
def fixnump(self):
r"""
Return True if self is a fixnum, False otherwise
EXAMPLES::
sage: from sage.libs.ecl import *
sage: EclObject(2**3).fixnump()
True
sage: EclObject(2**200).fixnump()
False
"""
return bint_fixnump(self.obj)
def characterp(self):
r"""
Return True if self is a character, False otherwise
Strings are not characters
EXAMPLES:
sage: from sage.libs.ecl import *
sage: EclObject('"a"').characterp()
False
"""
return bint_characterp(self.obj)
def nullp(self):
r"""
Return True if self is NIL, False otherwise
EXAMPLES::
sage: from sage.libs.ecl import *
sage: EclObject([]).nullp()
True
sage: EclObject([[]]).nullp()
False
"""
return bint_nullp(self.obj)
def listp(self):
r"""
Return True if self is a list, False otherwise. NIL is a list.
EXAMPLES::
sage: from sage.libs.ecl import *
sage: EclObject([]).listp()
True
sage: EclObject([[]]).listp()
True
"""
return bint_listp(self.obj)
def consp(self):
r"""
Return True if self is a cons, False otherwise. NIL is not a cons.
EXAMPLES::
sage: from sage.libs.ecl import *
sage: EclObject([]).consp()
False
sage: EclObject([[]]).consp()
True
"""
return bint_consp(self.obj)
def atomp(self):
r"""
Return True if self is atomic, False otherwise.
EXAMPLES::
sage: from sage.libs.ecl import *
sage: EclObject([]).atomp()
True
sage: EclObject([[]]).atomp()
False
"""
return bint_atomp(self.obj)
def symbolp(self):
r"""
Return True if self is a symbol, False otherwise.
EXAMPLES::
sage: from sage.libs.ecl import *
sage: EclObject([]).symbolp()
True
sage: EclObject([[]]).symbolp()
False
"""
return bint_symbolp(self.obj)
cdef class EclListIterator:
r"""
Iterator object for an ECL list
This class is used to implement the iterator protocol for EclObject.
Do not instantiate this class directly but use the iterator method
on an EclObject instead. It is an error if the EclObject is not a list.
EXAMPLES::
sage: from sage.libs.ecl import *
sage: I=EclListIterator(EclObject("(1 2 3)"))
sage: type(I)
<type 'sage.libs.ecl.EclListIterator'>
sage: [i for i in I]
[<ECL: 1>, <ECL: 2>, <ECL: 3>]
sage: [i for i in EclObject("(1 2 3)")]
[<ECL: 1>, <ECL: 2>, <ECL: 3>]
sage: EclListIterator(EclObject("1"))
Traceback (most recent call last):
...
TypeError: ECL object is not iterable
"""
cdef EclObject current
def __init__(EclListIterator self, EclObject o):
r"""
Initialize EclListIterator
EXAMPLES::
sage: from sage.libs.ecl import *
sage: I=EclListIterator(EclObject("(1 2 3)"))
sage: type(I)
<type 'sage.libs.ecl.EclListIterator'>
"""
if not o.listp():
raise TypeError,"ECL object is not iterable"
self.current = ecl_wrap(o.obj)
def __iter__(EclListIterator self):
r"""
Return self
It seems standard that iterators return themselves if asked to produce
an iterator.
EXAMPLES::
sage: from sage.libs.ecl import *
sage: I=EclListIterator(EclObject("(1 2 3)"))
sage: id(I) == id(I.__iter__())
True
"""
return self
def __next__(EclListIterator self):
r"""
Get next element from iterator
EXAMPLES::
sage: from sage.libs.ecl import *
sage: I=EclListIterator(EclObject("(1 2 3)"))
sage: I.next()
<ECL: 1>
sage: I.next()
<ECL: 2>
sage: I.next()
<ECL: 3>
sage: I.next()
Traceback (most recent call last):
...
StopIteration
"""
if self.current.nullp():
raise StopIteration
elif self.current.consp():
r = self.current.car()
self.current = self.current.cdr()
else:
r = self.current
self.current = ecl_wrap(Cnil)
return r
cdef EclObject ecl_wrap(cl_object o):
cdef EclObject obj = EclObject.__new__(EclObject)
obj.set_obj(o)
return obj
cpdef EclObject ecl_eval(bytes s):
"""
Read and evaluate string in Lisp and return the result
EXAMPLES::
sage: from sage.libs.ecl import *
sage: ecl_eval("(defun fibo (n)(cond((= n 0) 0)((= n 1) 1)(T (+ (fibo (- n 1)) (fibo (- n 2))))))")
<ECL: FIBO>
sage: ecl_eval("(mapcar 'fibo '(1 2 3 4 5 6 7))")
<ECL: (1 1 2 3 5 8 13)>
"""
cdef cl_object o
o=ecl_safe_read_string(s)
o=ecl_safe_eval(o)
return ecl_wrap(o)
init_ecl()