1# Copyright © 2007-2019 Jakub Wilk <jwilk@jwilk.net> 2# 3# This file is part of python-djvulibre. 4# 5# python-djvulibre is free software; you can redistribute it and/or modify it 6# under the terms of the GNU General Public License version 2 as published by 7# the Free Software Foundation. 8# 9# python-djvulibre is distributed in the hope that it will be useful, but 10# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12# more details. 13 14#cython: autotestdict=False 15#cython: language_level=2 16 17''' 18DjVuLibre bindings: module for handling Lisp S-expressions 19''' 20 21cimport cython 22 23include 'common.pxi' 24 25cdef extern from 'libdjvu/miniexp.h': 26 int cexpr_is_int 'miniexp_numberp'(cexpr_t sexp) nogil 27 int cexpr_to_int 'miniexp_to_int'(cexpr_t sexp) nogil 28 cexpr_t int_to_cexpr 'miniexp_number'(int n) nogil 29 30 int cexpr_is_symbol 'miniexp_symbolp'(cexpr_t sexp) nogil 31 char* cexpr_to_symbol 'miniexp_to_name'(cexpr_t sexp) nogil 32 cexpr_t symbol_to_cexpr 'miniexp_symbol'(char* name) nogil 33 34 cexpr_t cexpr_nil 'miniexp_nil' 35 cexpr_t cexpr_dummy 'miniexp_dummy' 36 int cexpr_is_list 'miniexp_listp'(cexpr_t exp) nogil 37 int cexpr_is_nonempty_list 'miniexp_consp'(cexpr_t exp) nogil 38 int cexpr_length 'miniexp_length'(cexpr_t exp) nogil 39 cexpr_t cexpr_head 'miniexp_car'(cexpr_t exp) nogil 40 cexpr_t cexpr_tail 'miniexp_cdr'(cexpr_t exp) nogil 41 cexpr_t cexpr_nth 'miniexp_nth'(int n, cexpr_t exp) nogil 42 cexpr_t pair_to_cexpr 'miniexp_cons'(cexpr_t head, cexpr_t tail) nogil 43 cexpr_t cexpr_replace_head 'miniexp_rplaca'(cexpr_t exp, cexpr_t new_head) nogil 44 cexpr_t cexpr_replace_tail 'miniexp_rplacd'(cexpr_t exp, cexpr_t new_tail) nogil 45 cexpr_t cexpr_reverse_list 'miniexp_reverse'(cexpr_t exp) nogil 46 47 int cexpr_is_str 'miniexp_stringp'(cexpr_t cexpr) nogil 48 const char * cexpr_to_str 'miniexp_to_str'(cexpr_t cexpr) nogil 49 cexpr_t str_to_cexpr 'miniexp_string'(const char *s) nogil 50 cexpr_t cexpr_substr 'miniexp_substring'(const char *s, int n) nogil 51 cexpr_t cexpr_concat 'miniexp_concat'(cexpr_t cexpr_list) nogil 52 53 cexpr_t gc_lock 'minilisp_acquire_gc_lock'(cexpr_t cexpr) nogil 54 cexpr_t gc_unlock 'minilisp_release_gc_lock'(cexpr_t cexpr) nogil 55 56 cvar_t* cvar_new 'minivar_alloc'() nogil 57 void cvar_free 'minivar_free'(cvar_t* v) nogil 58 cexpr_t* cvar_ptr 'minivar_pointer'(cvar_t* v) nogil 59 60 IF HAVE_MINIEXP_IO_T: 61 ctypedef cexpr_io_s cexpr_io_t 'miniexp_io_t' 62 struct cexpr_io_s 'miniexp_io_s': 63 int (*puts 'fputs')(cexpr_io_t*, char*) 64 int (*getc 'fgetc')(cexpr_io_t*) 65 int (*ungetc)(cexpr_io_t*, int) 66 void *data[4] 67 int *p_flags 68 void cexpr_io_init 'miniexp_io_init'(cexpr_io_t *cio) 69 enum: 70 cexpr_io_print7bits 'miniexp_io_print7bits' 71 cexpr_t cexpr_read 'miniexp_read_r'(cexpr_io_t *cio) 72 cexpr_t cexpr_print 'miniexp_prin_r'(cexpr_io_t *cio, cexpr_t cexpr) 73 cexpr_t cexpr_printw 'miniexp_pprin_r'(cexpr_io_t *cio, cexpr_t cexpr, int width) 74 ELSE: 75 int io_7bit 'minilisp_print_7bits' 76 int (*io_puts 'minilisp_puts')(char *s) 77 int (*io_getc 'minilisp_getc')() 78 int (*io_ungetc 'minilisp_ungetc')(int c) 79 cexpr_t cexpr_read 'miniexp_read'() 80 cexpr_t cexpr_print 'miniexp_prin'(cexpr_t cexpr) 81 cexpr_t cexpr_printw 'miniexp_pprin'(cexpr_t cexpr, int width) 82 83cdef extern from 'stdio.h': 84 int EOF 85 86cdef object sys 87import sys 88 89cdef object format_exc 90from traceback import format_exc 91 92cdef object StringIO 93IF PY3K: 94 from io import StringIO 95ELSE: 96 from cStringIO import StringIO 97 98cdef object BytesIO 99from io import BytesIO 100 101cdef object weakref 102import weakref 103 104cdef object symbol_dict 105symbol_dict = weakref.WeakValueDictionary() 106 107cdef object codecs 108import codecs 109 110IF not HAVE_MINIEXP_IO_T: 111 cdef Lock _myio_lock 112 _myio_lock = allocate_lock() 113 114cdef class _ExpressionIO: 115 IF HAVE_MINIEXP_IO_T: 116 cdef cexpr_io_t cio 117 cdef int flags 118 ELSE: 119 cdef int (*backup_io_puts)(const char *s) 120 cdef int (*backup_io_getc)() 121 cdef int (*backup_io_ungetc)(int c) 122 cdef int backup_io_7bit 123 cdef object stdin 'stdin_fp' 124 cdef object stdout 'stdout_fp' 125 cdef int stdout_binary 126 cdef object buffer 127 cdef object exc 128 129 _reentrant = HAVE_MINIEXP_IO_T 130 131 def __init__(self, object stdin=None, object stdout=None, int escape_unicode=True): 132 IF not HAVE_MINIEXP_IO_T: 133 global io_7bit, io_puts, io_getc, io_ungetc 134 global _myio 135 with nogil: 136 acquire_lock(_myio_lock, WAIT_LOCK) 137 self.backup_io_7bit = io_7bit 138 self.backup_io_puts = io_puts 139 self.backup_io_getc = io_getc 140 self.backup_io_ungetc = io_ungetc 141 self.stdin = stdin 142 self.stdout = stdout 143 IF PY3K: 144 self.stdout_binary = not hasattr(stdout, 'encoding') 145 ELSE: 146 # In Python 2, sys.stdout has the encoding attribute, 147 # even though it accepts byte strings. 148 # Let's only make a special-case for codecs. 149 self.stdout_binary = not isinstance(stdout, codecs.StreamReaderWriter) 150 self.buffer = [] 151 self.exc = None 152 IF HAVE_MINIEXP_IO_T: 153 cexpr_io_init(&self.cio) 154 self.cio.data[0] = <void*>self 155 self.cio.getc = _myio_getc 156 self.cio.ungetc = _myio_ungetc 157 self.cio.puts = _myio_puts 158 if escape_unicode: 159 self.flags = cexpr_io_print7bits 160 else: 161 self.flags = 0 162 self.cio.p_flags = &self.flags 163 ELSE: 164 io_getc = _myio_getc 165 io_ungetc = _myio_ungetc 166 io_puts = _myio_puts 167 io_7bit = escape_unicode 168 _myio = self 169 170 @cython.final 171 cdef close(self): 172 IF not HAVE_MINIEXP_IO_T: 173 global io_7bit, io_puts, io_getc, io_ungetc 174 global _myio 175 _myio = None 176 self.stdin = None 177 self.stdout = None 178 self.buffer = None 179 IF not HAVE_MINIEXP_IO_T: 180 io_7bit = self.backup_io_7bit 181 io_puts = self.backup_io_puts 182 io_getc = self.backup_io_getc 183 io_ungetc = self.backup_io_ungetc 184 try: 185 if self.exc is not None: 186 raise self.exc[0], self.exc[1], self.exc[2] 187 finally: 188 IF not HAVE_MINIEXP_IO_T: 189 release_lock(_myio_lock) 190 self.exc = None 191 192 IF HAVE_MINIEXP_IO_T: 193 194 @cython.final 195 cdef cexpr_t read(self): 196 return cexpr_read(&self.cio) 197 198 @cython.final 199 cdef cexpr_t print_(self, cexpr_t cexpr): 200 return cexpr_print(&self.cio, cexpr) 201 202 @cython.final 203 cdef cexpr_t printw(self, cexpr_t cexpr, int width): 204 return cexpr_printw(&self.cio, cexpr, width) 205 206 ELSE: 207 208 @cython.final 209 cdef cexpr_t read(self): 210 return cexpr_read() 211 212 @cython.final 213 cdef cexpr_t print_(self, cexpr_t cexpr): 214 return cexpr_print(cexpr) 215 216 @cython.final 217 cdef cexpr_t printw(self, cexpr_t cexpr, int width): 218 return cexpr_printw(cexpr, width) 219 220IF HAVE_MINIEXP_IO_T: 221 222 cdef int _myio_puts(cexpr_io_t* cio, const char *s): 223 cdef _ExpressionIO io 224 xio = <_ExpressionIO> cio.data[0] 225 try: 226 if xio.stdout_binary: 227 xio.stdout.write(s) 228 else: 229 xio.stdout.write(decode_utf8(s)) 230 except: 231 xio.exc = sys.exc_info() 232 return EOF 233 234 cdef int _myio_getc(cexpr_io_t* cio): 235 cdef _ExpressionIO xio 236 cdef int result 237 xio = <_ExpressionIO> cio.data[0] 238 if xio.buffer: 239 return xio.buffer.pop() 240 try: 241 s = xio.stdin.read(1) 242 if not s: 243 return EOF 244 if is_unicode(s): 245 s = encode_utf8(s) 246 IF PY3K: 247 xio.buffer += reversed(s) 248 ELSE: 249 xio.buffer += map(ord, reversed(s)) 250 return xio.buffer.pop() 251 except: 252 xio.exc = sys.exc_info() 253 return EOF 254 255 cdef int _myio_ungetc(cexpr_io_t* cio, int c): 256 cdef _ExpressionIO io 257 xio = <_ExpressionIO> cio.data[0] 258 list_append(xio.buffer, c) 259 260ELSE: 261 262 cdef _ExpressionIO _myio 263 264 cdef int _myio_puts(const char *s): 265 try: 266 if _myio.stdout_binary: 267 _myio.stdout.write(s) 268 else: 269 _myio.stdout.write(decode_utf8(s)) 270 except: 271 _myio.exc = sys.exc_info() 272 return EOF 273 274 cdef int _myio_getc(): 275 cdef int result 276 if _myio.buffer: 277 return _myio.buffer.pop() 278 try: 279 s = _myio.stdin.read(1) 280 if not s: 281 return EOF 282 if is_unicode(s): 283 s = encode_utf8(s) 284 IF PY3K: 285 _myio.buffer += reversed(s) 286 ELSE: 287 _myio.buffer += map(ord, reversed(s)) 288 return _myio.buffer.pop() 289 except: 290 _myio.exc = sys.exc_info() 291 return EOF 292 293 cdef int _myio_ungetc(int c): 294 list_append(_myio.buffer, c) 295 296cdef object the_sentinel 297the_sentinel = object() 298 299cdef class _WrappedCExpr: 300 301 def __cinit__(self, object sentinel): 302 if sentinel is not the_sentinel: 303 raise_instantiation_error(type(self)) 304 self.cvar = cvar_new() 305 306 cdef cexpr_t cexpr(self): 307 return cvar_ptr(self.cvar)[0] 308 309 cdef object print_into(self, object stdout, object width, bint escape_unicode): 310 cdef cexpr_t cexpr 311 cdef _ExpressionIO xio 312 if width is None: 313 pass 314 elif not is_int(width): 315 raise TypeError('width must be an integer') 316 elif width <= 0: 317 raise ValueError('width <= 0') 318 cexpr = self.cexpr() 319 xio = _ExpressionIO(stdout=stdout, escape_unicode=escape_unicode) 320 try: 321 if width is None: 322 xio.print_(cexpr) 323 else: 324 xio.printw(cexpr, width) 325 finally: 326 xio.close() 327 328 cdef object as_string(self, object width, bint escape_unicode): 329 stdout = StringIO() 330 try: 331 self.print_into(stdout, width, escape_unicode) 332 return stdout.getvalue() 333 finally: 334 stdout.close() 335 336 def __dealloc__(self): 337 cvar_free(self.cvar) 338 339cdef _WrappedCExpr wexpr(cexpr_t cexpr): 340 cdef _WrappedCExpr wexpr 341 wexpr = _WrappedCExpr(sentinel = the_sentinel) 342 cvar_ptr(wexpr.cvar)[0] = cexpr 343 return wexpr 344 345cdef class _MissingCExpr(_WrappedCExpr): 346 347 cdef object print_into(self, object stdout, object width, bint escape_unicode): 348 raise NotImplementedError 349 350 cdef object as_string(self, object width, bint escape_unicode): 351 raise NotImplementedError 352 353cdef _MissingCExpr wexpr_missing(): 354 return _MissingCExpr(the_sentinel) 355 356 357cdef class BaseSymbol: 358 359 cdef object __weakref__ 360 cdef object _bytes 361 362 def __cinit__(self, bytes): 363 cdef char *cbytes 364 cbytes = bytes 365 self._bytes = cbytes 366 367 def __repr__(self): 368 IF PY3K: 369 try: 370 string = self._bytes.decode('UTF-8') 371 except UnicodeDecodeError: 372 string = self._bytes 373 ELSE: 374 string = self._bytes 375 return '{tp}({s!r})'.format(tp=get_type_name(_Symbol_), s=string) 376 377 def __richcmp__(self, object other, int op): 378 cdef BaseSymbol _self, _other 379 if not typecheck(self, BaseSymbol) or not typecheck(other, BaseSymbol): 380 return NotImplemented 381 _self = self 382 _other = other 383 return richcmp(_self._bytes, _other._bytes, op) 384 385 def __hash__(self): 386 return hash(self._bytes) 387 388 property bytes: 389 def __get__(self): 390 return self._bytes 391 392 IF not PY3K: 393 def __str__(self): 394 return self._bytes 395 396 IF PY3K: 397 def __str__(self): 398 return self._bytes.decode('UTF-8') 399 ELSE: 400 def __unicode__(self): 401 return self._bytes.decode('UTF-8') 402 403 def __reduce__(self): 404 return (Symbol, (self._bytes,)) 405 406class Symbol(BaseSymbol): 407 408 @staticmethod 409 def __new__(cls, name): 410 ''' 411 Symbol(name) -> a symbol 412 ''' 413 self = None 414 if is_unicode(name): 415 name = encode_utf8(name) 416 try: 417 if cls is _Symbol_: 418 self = symbol_dict[name] 419 except KeyError: 420 pass 421 if self is None: 422 if not is_bytes(name): 423 name = str(name) 424 IF PY3K: 425 name = encode_utf8(name) 426 self = BaseSymbol.__new__(cls, name) 427 if cls is _Symbol_: 428 symbol_dict[name] = self 429 return self 430 431cdef object _Symbol_ 432_Symbol_ = Symbol 433 434def _expression_from_string(s): 435 ''' 436 Expression.from_string(s) -> an expression 437 438 Read an expression from a string. 439 ''' 440 if is_unicode(s): 441 s = encode_utf8(s) 442 stdin = BytesIO(s) 443 try: 444 return _Expression_.from_stream(stdin) 445 finally: 446 stdin.close() 447 448class Expression(BaseExpression): 449 450 ''' 451 Notes about the textual representation of S-expressions 452 ------------------------------------------------------- 453 454 Special characters are: 455 456 * the parenthesis '(' and ')', 457 * the double quote '"', 458 * the vertical bar '|'. 459 460 Symbols are represented by their name. Vertical bars | can be used to 461 delimit names that contain blanks, special characters, non printable 462 characters, non-ASCII characters, or can be confused as a number. 463 464 Numbers follow the syntax specified by the C function strtol() with 465 base=0. 466 467 Strings are delimited by double quotes. All C string escapes are 468 recognized. Non-printable ASCII characters must be escaped. 469 470 List are represented by an open parenthesis '(' followed by the space 471 separated list elements, followed by a closing parenthesis ')'. 472 473 When the cdr of the last pair is non zero, the closed parenthesis is 474 preceded by a space, a dot '.', a space, and the textual representation 475 of the cdr. (This is only partially supported by Python bindings.) 476 477 ''' 478 479 @staticmethod 480 def __new__(cls, value): 481 ''' 482 Expression(value) -> an expression 483 ''' 484 if typecheck(value, _Expression_) and (not typecheck(value, ListExpression) or not value): 485 return value 486 if is_int(value): 487 return IntExpression(value) 488 elif typecheck(value, _Symbol_): 489 return SymbolExpression(value) 490 elif is_unicode(value): 491 return StringExpression(encode_utf8(value)) 492 elif is_bytes(value): 493 if PY3K: 494 return StringExpression(bytes(value)) 495 else: 496 return StringExpression(str(value)) 497 else: 498 return ListExpression(iter(value)) 499 500 @staticmethod 501 def from_stream(stdin): 502 ''' 503 Expression.from_stream(stream) -> an expression 504 505 Read an expression from a stream. 506 ''' 507 cdef _ExpressionIO xio 508 try: 509 xio = _ExpressionIO(stdin=stdin) 510 try: 511 return _c2py(xio.read()) 512 except InvalidExpression: 513 raise ExpressionSyntaxError 514 finally: 515 xio.close() 516 517 from_string = staticmethod(_expression_from_string) 518 519cdef object _Expression_ 520_Expression_ = Expression 521 522cdef object BaseExpression_richcmp(object left, object right, int op): 523 if not typecheck(left, BaseExpression): 524 return NotImplemented 525 elif not typecheck(right, BaseExpression): 526 return NotImplemented 527 return richcmp(left.value, right.value, op) 528 529cdef class BaseExpression: 530 ''' 531 Don't use this class directly. Use the Expression class instead. 532 ''' 533 534 cdef _WrappedCExpr wexpr 535 536 def __cinit__(self, *args, **kwargs): 537 self.wexpr = wexpr_missing() 538 539 def print_into(self, stdout, width=None, escape_unicode=True): 540 ''' 541 expr.print_into(file, width=None, escape_unicode=True) -> None 542 543 Print the expression into the file. 544 ''' 545 self.wexpr.print_into(stdout, width, escape_unicode) 546 547 def as_string(self, width=None, escape_unicode=True): 548 ''' 549 expr.as_string(width=None, escape_unicode=True) -> a string 550 551 Return a string representation of the expression. 552 ''' 553 return self.wexpr.as_string(width, escape_unicode) 554 555 def __str__(self): 556 return self.as_string() 557 558 IF not PY3K: 559 def __unicode__(self): 560 return self.as_string().decode('UTF-8') 561 562 property value: 563 ''' 564 The "pythonic" value of the expression. 565 Lisp lists as mapped to Python tuples. 566 ''' 567 def __get__(self): 568 return self._get_value() 569 570 property lvalue: 571 ''' 572 The "pythonic" value of the expression. 573 Lisp lists as mapped to Python lists. 574 ''' 575 def __get__(self): 576 return self._get_lvalue() 577 578 def _get_value(self): 579 return self._get_lvalue() 580 581 def _get_lvalue(self): 582 raise NotImplementedError 583 584 def __richcmp__(self, other, int op): 585 return BaseExpression_richcmp(self, other, op) 586 587 def __repr__(self): 588 return '{tp}({expr!r})'.format(tp=get_type_name(_Expression_), expr=self.lvalue) 589 590 def __copy__(self): 591 # Most of S-expressions are immutable. 592 # Mutable S-expressions should override this method. 593 return self 594 595 def __deepcopy__(self, memo): 596 # Most of S-expressions are immutable. 597 # Mutable S-expressions should override this method. 598 return self 599 600 def __reduce__(self): 601 return (_expression_from_string, (self.as_string(),)) 602 603class IntExpression(_Expression_): 604 605 ''' 606 IntExpression can represent any integer in range(-2 ** 29, 2 ** 29). 607 608 To create objects of this class, use the Expression class constructor. 609 ''' 610 611 @staticmethod 612 def __new__(cls, value): 613 ''' 614 IntExpression(n) -> an integer expression 615 ''' 616 cdef BaseExpression self 617 self = BaseExpression.__new__(cls) 618 if typecheck(value, _WrappedCExpr): 619 self.wexpr = value 620 elif is_int(value): 621 if -1 << 29 <= value < 1 << 29: 622 self.wexpr = wexpr(int_to_cexpr(value)) 623 else: 624 raise ValueError('value not in range(-2 ** 29, 2 ** 29)') 625 else: 626 raise TypeError('value must be an integer') 627 return self 628 629 IF PY3K: 630 def __bool__(self): 631 return bool(self.value) 632 ELSE: 633 def __nonzero__(self): 634 return bool(self.value) 635 636 def __int__(self): 637 return self.value 638 639 def __long__(self): 640 return long(self.value) 641 642 def __float__(self): 643 return 0.0 + self.value 644 645 def _get_lvalue(BaseExpression self not None): 646 return cexpr_to_int(self.wexpr.cexpr()) 647 648 def __richcmp__(self, other, int op): 649 return BaseExpression_richcmp(self, other, op) 650 651 def __hash__(self): 652 return hash(self.value) 653 654class SymbolExpression(_Expression_): 655 ''' 656 To create objects of this class, use the Expression class constructor. 657 ''' 658 659 @staticmethod 660 def __new__(cls, value): 661 ''' 662 SymbolExpression(Symbol(s)) -> a symbol expression 663 ''' 664 cdef BaseExpression self 665 cdef BaseSymbol symbol 666 self = BaseExpression.__new__(cls) 667 if typecheck(value, _WrappedCExpr): 668 self.wexpr = value 669 elif typecheck(value, _Symbol_): 670 symbol = value 671 self.wexpr = wexpr(symbol_to_cexpr(symbol._bytes)) 672 else: 673 raise TypeError('value must be a Symbol') 674 return self 675 676 def _get_lvalue(BaseExpression self not None): 677 return _Symbol_(cexpr_to_symbol(self.wexpr.cexpr())) 678 679 def __richcmp__(self, other, int op): 680 return BaseExpression_richcmp(self, other, op) 681 682 def __hash__(self): 683 return hash(self.value) 684 685class StringExpression(_Expression_): 686 ''' 687 To create objects of this class, use the Expression class constructor. 688 ''' 689 690 @staticmethod 691 def __new__(cls, value): 692 ''' 693 SymbolExpression(s) -> a string expression 694 ''' 695 cdef BaseExpression self 696 self = BaseExpression.__new__(cls) 697 if typecheck(value, _WrappedCExpr): 698 self.wexpr = value 699 elif is_bytes(value): 700 gc_lock(NULL) # protect from collecting a just-created object 701 try: 702 self.wexpr = wexpr(str_to_cexpr(value)) 703 finally: 704 gc_unlock(NULL) 705 else: 706 raise TypeError('value must be a byte string') 707 return self 708 709 @property 710 def bytes(BaseExpression self not None): 711 return cexpr_to_str(self.wexpr.cexpr()) 712 713 def _get_lvalue(BaseExpression self not None): 714 cdef const char *bytes 715 bytes = cexpr_to_str(self.wexpr.cexpr()) 716 IF PY3K: 717 return decode_utf8(bytes) 718 ELSE: 719 return bytes 720 721 IF PY3K: 722 def __repr__(BaseExpression self not None): 723 cdef const char *bytes 724 bytes = cexpr_to_str(self.wexpr.cexpr()) 725 try: 726 string = decode_utf8(bytes) 727 except UnicodeDecodeError: 728 string = bytes 729 return '{tp}({s!r})'.format(tp=get_type_name(_Expression_), s=string) 730 731 def __richcmp__(self, other, int op): 732 return BaseExpression_richcmp(self, other, op) 733 734 def __hash__(self): 735 return hash(self.value) 736 737class InvalidExpression(ValueError): 738 pass 739 740class ExpressionSyntaxError(Exception): 741 ''' 742 Invalid expression syntax. 743 ''' 744 pass 745 746cdef _WrappedCExpr public_py2c(object o): 747 cdef BaseExpression pyexpr 748 pyexpr = _Expression_(o) 749 if pyexpr is None: 750 raise TypeError 751 return pyexpr.wexpr 752 753cdef object public_c2py(cexpr_t cexpr): 754 return _c2py(cexpr) 755 756cdef BaseExpression _c2py(cexpr_t cexpr): 757 if cexpr == cexpr_dummy: 758 raise InvalidExpression 759 _wexpr = wexpr(cexpr) 760 if cexpr_is_int(cexpr): 761 result = IntExpression(_wexpr) 762 elif cexpr_is_symbol(cexpr): 763 result = SymbolExpression(_wexpr) 764 elif cexpr_is_list(cexpr): 765 result = ListExpression(_wexpr) 766 elif cexpr_is_str(cexpr): 767 result = StringExpression(_wexpr) 768 else: 769 raise InvalidExpression 770 return result 771 772cdef _WrappedCExpr _build_list_cexpr(object items): 773 cdef cexpr_t cexpr 774 cdef BaseExpression citem 775 gc_lock(NULL) # protect from collecting a just-created object 776 try: 777 cexpr = cexpr_nil 778 for item in items: 779 if typecheck(item, BaseExpression): 780 citem = item 781 else: 782 citem = _Expression_(item) 783 if citem is None: 784 raise TypeError 785 cexpr = pair_to_cexpr(citem.wexpr.cexpr(), cexpr) 786 cexpr = cexpr_reverse_list(cexpr) 787 return wexpr(cexpr) 788 finally: 789 gc_unlock(NULL) 790 791 792class ListExpression(_Expression_): 793 ''' 794 To create objects of this class, use the Expression class constructor. 795 ''' 796 797 @staticmethod 798 def __new__(cls, items): 799 ''' 800 ListExpression(iterable) -> a list expression 801 ''' 802 cdef BaseExpression self 803 self = BaseExpression.__new__(cls) 804 if typecheck(items, _WrappedCExpr): 805 self.wexpr = items 806 else: 807 self.wexpr = _build_list_cexpr(items) 808 return self 809 810 IF PY3K: 811 def __bool__(BaseExpression self not None): 812 return self.wexpr.cexpr() != cexpr_nil 813 ELSE: 814 def __nonzero__(BaseExpression self not None): 815 return self.wexpr.cexpr() != cexpr_nil 816 817 def __len__(BaseExpression self not None): 818 cdef cexpr_t cexpr 819 cdef int n 820 cexpr = self.wexpr.cexpr() 821 n = 0 822 while cexpr != cexpr_nil: 823 cexpr = cexpr_tail(cexpr) 824 n = n + 1 825 return n 826 827 def __getitem__(BaseExpression self not None, key): 828 cdef cexpr_t cexpr 829 cdef int n 830 cexpr = self.wexpr.cexpr() 831 if is_int(key): 832 n = key 833 if n < 0: 834 n = n + len(self) 835 if n < 0: 836 raise IndexError('list index of out range') 837 while True: 838 if cexpr == cexpr_nil: 839 raise IndexError('list index of out range') 840 if n > 0: 841 n = n - 1 842 cexpr = cexpr_tail(cexpr) 843 else: 844 cexpr = cexpr_head(cexpr) 845 break 846 elif is_slice(key): 847 if (is_int(key.start) or key.start is None) and key.stop is None and key.step is None: 848 n = key.start or 0 849 if n < 0: 850 n = n + len(self) 851 while n > 0 and cexpr != cexpr_nil: 852 cexpr = cexpr_tail(cexpr) 853 n = n - 1 854 else: 855 raise NotImplementedError('only [n:] slices are supported') 856 else: 857 raise TypeError('key must be an integer or a slice') 858 return _c2py(cexpr) 859 860 def __setitem__(BaseExpression self not None, key, value): 861 cdef cexpr_t cexpr 862 cdef cexpr_t prev_cexpr 863 cdef cexpr_t new_cexpr 864 cdef int n 865 cdef BaseExpression pyexpr 866 cexpr = self.wexpr.cexpr() 867 pyexpr = _Expression_(value) 868 new_cexpr = pyexpr.wexpr.cexpr() 869 if is_int(key): 870 n = key 871 if n < 0: 872 n = n + len(self) 873 if n < 0: 874 raise IndexError('list index of out range') 875 while True: 876 if cexpr == cexpr_nil: 877 raise IndexError('list index of out range') 878 if n > 0: 879 n = n - 1 880 cexpr = cexpr_tail(cexpr) 881 else: 882 cexpr_replace_head(cexpr, new_cexpr) 883 break 884 elif is_slice(key): 885 if not cexpr_is_list(new_cexpr): 886 raise TypeError('can only assign a list expression') 887 if (is_int(key.start) or key.start is None) and key.stop is None and key.step is None: 888 n = key.start or 0 889 if n < 0: 890 n = n + len(self) 891 prev_cexpr = cexpr_nil 892 while n > 0 and cexpr != cexpr_nil: 893 prev_cexpr = cexpr 894 cexpr = cexpr_tail(cexpr) 895 n = n - 1 896 if prev_cexpr == cexpr_nil: 897 self.wexpr = wexpr(new_cexpr) 898 else: 899 cexpr_replace_tail(prev_cexpr, new_cexpr) 900 else: 901 raise NotImplementedError('only [n:] slices are supported') 902 else: 903 raise TypeError('key must be an integer or a slice') 904 905 def __delitem__(BaseExpression self not None, key): 906 if is_int(key): 907 self.pop(key) 908 elif is_slice(key): 909 self[key] = () 910 else: 911 raise TypeError('key must be an integer or a slice') 912 913 def extend(self, iterable): 914 iter(iterable) 915 self[len(self):] = iterable 916 917 def __iadd__(self, iterable): 918 iter(iterable) 919 self[len(self):] = iterable 920 return self 921 922 def insert(BaseExpression self not None, long index, item): 923 cdef cexpr_t cexpr, new_cexpr 924 cdef BaseExpression citem 925 cexpr = self.wexpr.cexpr() 926 if index < 0: 927 index += len(self) 928 if index < 0: 929 index = 0 930 if typecheck(item, BaseExpression): 931 citem = item 932 else: 933 citem = _Expression_(item) 934 if citem is None: 935 raise TypeError 936 if index == 0 or cexpr == cexpr_nil: 937 gc_lock(NULL) # protect from collecting a just-created object 938 try: 939 new_cexpr = pair_to_cexpr(citem.wexpr.cexpr(), cexpr) 940 self.wexpr = wexpr(new_cexpr) 941 finally: 942 gc_unlock(NULL) 943 return 944 while True: 945 assert cexpr != cexpr_nil 946 if index > 1 and cexpr_tail(cexpr) != cexpr_nil: 947 index = index - 1 948 cexpr = cexpr_tail(cexpr) 949 else: 950 gc_lock(NULL) # protect from collecting a just-created object 951 try: 952 new_cexpr = pair_to_cexpr(citem.wexpr.cexpr(), cexpr_tail(cexpr)) 953 cexpr_replace_tail(cexpr, new_cexpr) 954 finally: 955 gc_unlock(NULL) 956 break 957 958 def append(BaseExpression self not None, item): 959 return self.insert(len(self), item) 960 961 def reverse(BaseExpression self not None): 962 cdef cexpr_t cexpr, new_cexpr 963 gc_lock(NULL) # protect from collecting a just-created object 964 try: 965 new_cexpr = cexpr_reverse_list(self.wexpr.cexpr()) 966 self.wexpr = wexpr(new_cexpr) 967 finally: 968 gc_unlock(NULL) 969 970 def pop(BaseExpression self not None, long index=-1): 971 cdef cexpr_t cexpr, citem 972 cexpr = self.wexpr.cexpr() 973 if cexpr == cexpr_nil: 974 raise IndexError('pop from empty list') 975 if index < 0: 976 index += len(self) 977 if index < 0: 978 raise IndexError('pop index of out range') 979 if index == 0: 980 result = _c2py(cexpr_head(cexpr)) 981 self.wexpr = wexpr(cexpr_tail(cexpr)) 982 return result 983 while cexpr_tail(cexpr) != cexpr_nil: 984 if index > 1: 985 index = index - 1 986 cexpr = cexpr_tail(cexpr) 987 else: 988 result = _c2py(cexpr_head(cexpr_tail(cexpr))) 989 cexpr_replace_tail(cexpr, cexpr_tail(cexpr_tail(cexpr))) 990 return result 991 raise IndexError('pop index of out range') 992 993 def remove(BaseExpression self not None, item): 994 cdef cexpr_t cexpr 995 cdef BaseExpression citem 996 cexpr = self.wexpr.cexpr() 997 if cexpr == cexpr_nil: 998 raise IndexError('item not in list') 999 if _c2py(cexpr_head(cexpr)) == item: 1000 self.wexpr = wexpr(cexpr_tail(cexpr)) 1001 return 1002 while True: 1003 assert cexpr != cexpr_nil 1004 if cexpr_tail(cexpr) == cexpr_nil: 1005 raise IndexError('item not in list') 1006 if _c2py(cexpr_head(cexpr_tail(cexpr))) == item: 1007 cexpr_replace_tail(cexpr, cexpr_tail(cexpr_tail(cexpr))) 1008 return 1009 cexpr = cexpr_tail(cexpr) 1010 1011 def index(self, value): 1012 # TODO: optimize 1013 for i, v in enumerate(self): 1014 if v == value: 1015 return i 1016 raise ValueError('value not in list') 1017 1018 def count(self, value): 1019 # TODO: optimize 1020 cdef long counter = 0 1021 for v in self: 1022 if v == value: 1023 counter += 1 1024 return counter 1025 1026 def __iter__(self): 1027 return _ListExpressionIterator(self) 1028 1029 __hash__ = None 1030 1031 def _get_value(BaseExpression self not None): 1032 cdef cexpr_t current 1033 current = self.wexpr.cexpr() 1034 result = [] 1035 while current != cexpr_nil: 1036 list_append(result, _c2py(cexpr_head(current))._get_value()) 1037 current = cexpr_tail(current) 1038 return tuple(result) 1039 1040 def _get_lvalue(BaseExpression self not None): 1041 cdef cexpr_t current 1042 current = self.wexpr.cexpr() 1043 result = [] 1044 while current != cexpr_nil: 1045 list_append(result, _c2py(cexpr_head(current))._get_lvalue()) 1046 current = cexpr_tail(current) 1047 return result 1048 1049 def __copy__(self): 1050 return _Expression_(self) 1051 1052 def __deepcopy__(self, memo): 1053 return _Expression_(self._get_value()) 1054 1055if sys.version_info >= (3, 3): 1056 import collections.abc as collections_abc 1057else: 1058 import collections as collections_abc 1059collections_abc.MutableSequence.register(ListExpression) 1060del collections_abc 1061 1062cdef class _ListExpressionIterator: 1063 1064 cdef BaseExpression expression 1065 cdef cexpr_t cexpr 1066 1067 def __cinit__(self, BaseExpression expression not None): 1068 self.expression = expression 1069 self.cexpr = expression.wexpr.cexpr() 1070 1071 def __next__(self): 1072 cdef cexpr_t cexpr 1073 cexpr = self.cexpr 1074 if cexpr == cexpr_nil: 1075 raise StopIteration 1076 self.cexpr = cexpr_tail(cexpr) 1077 cexpr = cexpr_head(cexpr) 1078 return _c2py(cexpr) 1079 1080 def __iter__(self): 1081 return self 1082 1083 1084__all__ = ('Symbol', 'Expression', 'IntExpression', 'SymbolExpression', 'StringExpression', 'ListExpression', 'InvalidExpression', 'ExpressionSyntaxError') 1085__author__ = 'Jakub Wilk <jwilk@jwilk.net>' 1086IF PY3K: 1087 __version__ = decode_utf8(PYTHON_DJVULIBRE_VERSION) 1088ELSE: 1089 __version__ = str(PYTHON_DJVULIBRE_VERSION) 1090 1091# vim:ts=4 sts=4 sw=4 et ft=pyrex 1092