Skip to content

Commit feb63c9

Browse files
author
Release Manager
committed
Trac #30105: sage.libs.ecl: Make it possible to convert Python strings to Lisp strings
`sage.libs.ecl` exposes no Python function that converts a Python string to a Lisp string as an `EclObject`. (`python_to_ecl`, which is used by `EclObject.__init__`, handles strings by `READ`ing from them, so `EclObject("abc")` returns an interned symbol, not a string.) We add an argument to `python_to_ecl` and `EclObject.__init__` that controls how strings are handled. URL: https://trac.sagemath.org/30105 Reported by: mkoeppe Ticket author(s): Matthias Koeppe Reviewer(s): Dima Pasechnik
2 parents d60d20b + b6cea1a commit feb63c9

File tree

1 file changed

+60
-22
lines changed

1 file changed

+60
-22
lines changed

src/sage/libs/ecl.pyx

+60-22
Original file line numberDiff line numberDiff line change
@@ -424,12 +424,13 @@ def print_objects():
424424
if c == Cnil:
425425
break
426426

427-
cdef cl_object python_to_ecl(pyobj) except NULL:
427+
cdef cl_object python_to_ecl(pyobj, bint read_strings) except NULL:
428428
# conversion of a python object into an ecl object
429429
# most conversions are straightforward. Noteworthy are:
430430
# python lists -> lisp (NIL terminated) lists
431431
# tuples -> dotted lists
432-
# strings ->parsed by lisp reader
432+
# strings -> if read_strings is true, parsed by lisp reader
433+
# otherwise creates a simple-string
433434

434435
cdef bytes s
435436
cdef cl_object L, ptr, o
@@ -445,7 +446,7 @@ cdef cl_object python_to_ecl(pyobj) except NULL:
445446
if pyobj >= MOST_NEGATIVE_FIXNUM and pyobj <= MOST_POSITIVE_FIXNUM:
446447
return ecl_make_integer(pyobj)
447448
else:
448-
return python_to_ecl(Integer(pyobj))
449+
return python_to_ecl(Integer(pyobj), read_strings)
449450
elif isinstance(pyobj,int):
450451
return ecl_make_integer(pyobj)
451452
elif isinstance(pyobj,float):
@@ -455,46 +456,53 @@ cdef cl_object python_to_ecl(pyobj) except NULL:
455456
s = str_to_bytes(pyobj, 'ascii')
456457
except UnicodeEncodeError:
457458
o = cl_funcall(2, make_unicode_string_clobj,
458-
python_to_ecl([ord(c) for c in pyobj]))
459+
python_to_ecl([ord(c) for c in pyobj], read_strings))
459460
else:
460461
o = ecl_cstring_to_base_string_or_nil(s)
461-
return ecl_safe_funcall(read_from_string_clobj, o)
462+
463+
if read_strings:
464+
return ecl_safe_funcall(read_from_string_clobj, o)
465+
else:
466+
return o
462467
elif isinstance(pyobj,bytes):
463468
s=<bytes>pyobj
464-
return ecl_safe_read_string(s)
469+
if read_strings:
470+
return ecl_safe_read_string(s)
471+
else:
472+
return ecl_cstring_to_base_string_or_nil(s)
465473
elif isinstance(pyobj,Integer):
466474
if pyobj >= MOST_NEGATIVE_FIXNUM and pyobj <= MOST_POSITIVE_FIXNUM:
467475
return ecl_make_integer(pyobj)
468476
else:
469477
return ecl_bignum_from_mpz( (<Integer>pyobj).value )
470478
elif isinstance(pyobj,Rational):
471479
return ecl_make_ratio(
472-
python_to_ecl( (<Rational>pyobj).numerator() ),
473-
python_to_ecl( (<Rational>pyobj).denominator()))
480+
python_to_ecl( (<Rational>pyobj).numerator(), read_strings ),
481+
python_to_ecl( (<Rational>pyobj).denominator(), read_strings ))
474482
elif isinstance(pyobj,EclObject):
475483
return (<EclObject>pyobj).obj
476484
elif isinstance(pyobj, list):
477485
if not pyobj:
478486
return Cnil
479487
else:
480-
L=cl_cons(python_to_ecl(pyobj[0]),Cnil)
481-
ptr=L
488+
L = cl_cons(python_to_ecl(pyobj[0], read_strings),Cnil)
489+
ptr = L
482490
for a in pyobj[1:]:
483-
cl_rplacd(ptr,cl_cons(python_to_ecl(a),Cnil))
484-
ptr=cl_cdr(ptr)
491+
cl_rplacd(ptr, cl_cons(python_to_ecl(a, read_strings), Cnil))
492+
ptr = cl_cdr(ptr)
485493
return L
486494
elif isinstance(pyobj, tuple):
487495
if not pyobj:
488496
return Cnil
489497
elif len(pyobj) == 1:
490-
return python_to_ecl(pyobj[0])
498+
return python_to_ecl(pyobj[0], read_strings)
491499
else:
492-
L=cl_cons(python_to_ecl(pyobj[0]),Cnil)
493-
ptr=L
500+
L = cl_cons(python_to_ecl(pyobj[0], read_strings), Cnil)
501+
ptr = L
494502
for a in pyobj[1:-1]:
495-
cl_rplacd(ptr,cl_cons(python_to_ecl(a),Cnil))
496-
ptr=cl_cdr(ptr)
497-
cl_rplacd(ptr,python_to_ecl(pyobj[-1]))
503+
cl_rplacd(ptr, cl_cons(python_to_ecl(a, read_strings), Cnil))
504+
ptr = cl_cdr(ptr)
505+
cl_rplacd(ptr, python_to_ecl(pyobj[-1], read_strings))
498506
return L
499507
else:
500508
raise TypeError("Unimplemented type for python_to_ecl")
@@ -591,6 +599,8 @@ cdef class EclObject:
591599
592600
sage: EclObject( (false, true))
593601
<ECL: (NIL . T)>
602+
sage: EclObject( (1, 2, 3) )
603+
<ECL: (1 2 . 3)>
594604
595605
Strings are fed to the reader, so a string normally results in a symbol::
596606
@@ -602,6 +612,28 @@ cdef class EclObject:
602612
sage: EclObject('"Symbol"')
603613
<ECL: "Symbol">
604614
615+
Or any other object that the Lisp reader can construct::
616+
617+
sage: EclObject('#("I" am "just" a "simple" vector)')
618+
<ECL: #("I" AM "just" A "simple" VECTOR)>
619+
620+
By means of Lisp reader macros, you can include arbitrary objects::
621+
622+
sage: EclObject([ 1, 2, '''#.(make-hash-table :test #'equal)''', 4])
623+
<ECL: (1 2 #<hash-table ...> 4)>
624+
625+
Using an optional argument, you can control how strings are handled::
626+
627+
sage: EclObject("String", False)
628+
<ECL: "String">
629+
sage: EclObject('#(I may look like a vector but I am a string)', False)
630+
<ECL: "#(I may look like a vector but I am a string)">
631+
632+
This also affects strings within nested lists and tuples ::
633+
634+
sage: EclObject([1, 2, "String", 4], False)
635+
<ECL: (1 2 "String" 4)>
636+
605637
EclObjects translate to themselves, so one can mix::
606638
607639
sage: EclObject([1,2,EclObject([3])])
@@ -661,7 +693,7 @@ cdef class EclObject:
661693
if not(bint_fixnump(o) or bint_characterp(o) or bint_nullp(o)):
662694
self.node=insert_node_after(list_of_objects,o)
663695

664-
def __init__(self,*args):
696+
def __init__(self, *args):
665697
r"""
666698
Create an EclObject
667699
@@ -674,8 +706,14 @@ cdef class EclObject:
674706
<ECL: (NIL T NIL)>
675707
676708
"""
677-
if len(args) != 0:
678-
self.set_obj(python_to_ecl(args[0]))
709+
if not args:
710+
return
711+
elif len(args) == 1:
712+
self.set_obj(python_to_ecl(args[0], True))
713+
elif len(args) == 2:
714+
self.set_obj(python_to_ecl(args[0], args[1]))
715+
else:
716+
raise TypeError('EclObject.__init__ received a wrong number of arguments')
679717

680718
def __reduce__(self):
681719
r"""
@@ -1354,7 +1392,7 @@ cpdef EclObject ecl_eval(str s):
13541392
13551393
"""
13561394
cdef cl_object o
1357-
o=ecl_safe_eval(python_to_ecl(s))
1395+
o=ecl_safe_eval(python_to_ecl(s, True))
13581396
return ecl_wrap(o)
13591397

13601398
init_ecl()

0 commit comments

Comments
 (0)