X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fcode%2Freader.lisp;h=f4352e68945f50cdd298bc5b72c6178d69166dc0;hb=cb296ae5a022a5b0f1fd573584301b0d2a9493f9;hp=69d1a6f9df0da2b50e3a2c486ddb99aefab03575;hpb=c713eb2b521b048ff2c927ec52b861787d289f85;p=sbcl.git diff --git a/src/code/reader.lisp b/src/code/reader.lisp index 69d1a6f..f4352e6 100644 --- a/src/code/reader.lisp +++ b/src/code/reader.lisp @@ -61,24 +61,46 @@ (defmacro get-cat-entry (char rt) ;; KLUDGE: Only give this side-effect-free args. ;; FIXME: should probably become inline function - `(elt (character-attribute-table ,rt) - (char-code ,char))) + `(if (typep ,char 'base-char) + (elt (character-attribute-array ,rt) (char-code ,char)) + (gethash ,char (character-attribute-hash-table ,rt) +char-attr-constituent+))) (defun set-cat-entry (char newvalue &optional (rt *readtable*)) - (setf (elt (character-attribute-table rt) - (char-code char)) - newvalue)) - -;;; FIXME: could be SB!XC:DEFMACRO inside EVAL-WHEN (COMPILE EVAL) -(defmacro get-cmt-entry (char rt) - `(the function - (elt (the simple-vector (character-macro-table ,rt)) - (char-code ,char)))) - -(defun set-cmt-entry (char newvalue &optional (rt *readtable*)) - (setf (elt (the simple-vector (character-macro-table rt)) - (char-code char)) - (coerce newvalue 'function))) + (if (typep char 'base-char) + (setf (elt (character-attribute-array rt) (char-code char)) newvalue) + ;; FIXME: could REMHASH if we're setting to + ;; +CHAR-ATTR-CONSTITUENT+ + (setf (gethash char (character-attribute-hash-table rt)) newvalue))) + +;;; the value actually stored in the character macro table. As per +;;; ANSI #'GET-MACRO-CHARACTER and #'SET-MACRO-CHARACTER, this can +;;; be either a function or NIL. +(eval-when (:compile-toplevel :execute) + (sb!xc:defmacro get-raw-cmt-entry (char readtable) + `(if (typep ,char 'base-char) + (svref (character-macro-array ,readtable) (char-code ,char)) + ;; Note: DEFAULT here is NIL, not #'UNDEFINED-MACRO-CHAR, so + ;; that everything above the base-char range is a non-macro + ;; constituent by default. + (gethash ,char (character-macro-hash-table ,readtable) nil)))) + +;;; the value represented by whatever is stored in the character macro +;;; table. As per ANSI #'GET-MACRO-CHARACTER and #'SET-MACRO-CHARACTER, +;;; a function value represents itself, and a NIL value represents the +;;; default behavior. +(defun get-coerced-cmt-entry (char readtable) + (the function + (or (get-raw-cmt-entry char readtable) + #'read-token))) + +(defun set-cmt-entry (char new-value-designator &optional (rt *readtable*)) + (if (typep char 'base-char) + (setf (svref (character-macro-array rt) (char-code char)) + (and new-value-designator + (%coerce-callable-to-fun new-value-designator))) + (setf (gethash char (character-macro-hash-table rt)) + (and new-value-designator + (%coerce-callable-to-fun new-value-designator))))) (defun undefined-macro-char (stream char) (unless *read-suppress* @@ -125,7 +147,7 @@ (defun !cold-init-secondary-attribute-table () (setq *secondary-attribute-table* - (make-array char-code-limit :element-type '(unsigned-byte 8) + (make-array base-char-code-limit :element-type '(unsigned-byte 8) :initial-element +char-attr-constituent+)) (!set-secondary-attribute #\: +char-attr-package-delimiter+) (!set-secondary-attribute #\| +char-attr-multiple-escape+) ; |) [for EMACS] @@ -153,17 +175,29 @@ ;;;; readtable operations +(defun shallow-replace/eql-hash-table (to from) + (maphash (lambda (k v) (setf (gethash k to) v)) from)) + (defun copy-readtable (&optional (from-readtable *readtable*) to-readtable) (let ((really-from-readtable (or from-readtable *standard-readtable*)) (really-to-readtable (or to-readtable (make-readtable)))) - (replace (character-attribute-table really-to-readtable) - (character-attribute-table really-from-readtable)) - (replace (character-macro-table really-to-readtable) - (character-macro-table really-from-readtable)) + (replace (character-attribute-array really-to-readtable) + (character-attribute-array really-from-readtable)) + (shallow-replace/eql-hash-table + (character-attribute-hash-table really-to-readtable) + (character-attribute-hash-table really-from-readtable)) + (replace (character-macro-array really-to-readtable) + (character-macro-array really-from-readtable)) + (shallow-replace/eql-hash-table + (character-macro-hash-table really-to-readtable) + (character-macro-hash-table really-from-readtable)) (setf (dispatch-tables really-to-readtable) - (mapcar (lambda (pair) (cons (car pair) - (copy-seq (cdr pair)))) + (mapcar (lambda (pair) + (cons (car pair) + (let ((table (make-hash-table))) + (shallow-replace/eql-hash-table table (cdr pair)) + table))) (dispatch-tables really-from-readtable))) (setf (readtable-case really-to-readtable) (readtable-case really-from-readtable)) @@ -178,7 +212,7 @@ FROM-TABLE defaults to the standard Lisp readtable when NIL." (let ((really-from-readtable (or from-readtable *standard-readtable*))) ;; Copy FROM-CHAR entries to TO-CHAR entries, but make sure that if - ;; from char is a constituent you don't copy non-movable secondary + ;; FROM-CHAR is a constituent you don't copy non-movable secondary ;; attributes (constituent types), and that said attributes magically ;; appear if you transform a non-constituent to a constituent. (let ((att (get-cat-entry from-char really-from-readtable))) @@ -186,42 +220,51 @@ (setq att (get-secondary-attribute to-char))) (set-cat-entry to-char att to-readtable) (set-cmt-entry to-char - (get-cmt-entry from-char really-from-readtable) + (get-raw-cmt-entry from-char really-from-readtable) to-readtable))) t) (defun set-macro-character (char function &optional - (non-terminatingp nil) (rt *readtable*)) + (non-terminatingp nil) + (readtable *readtable*)) #!+sb-doc - "Causes CHAR to be a macro character which invokes FUNCTION when - seen by the reader. The NON-TERMINATINGP flag can be used to - make the macro character non-terminating. The optional readtable - argument defaults to the current readtable. SET-MACRO-CHARACTER - returns T." - (let ((rt (or rt *standard-readtable*))) - (if non-terminatingp - (set-cat-entry char (get-secondary-attribute char) rt) - (set-cat-entry char +char-attr-terminating-macro+ rt)) - (set-cmt-entry char function rt) - T)) - -(defun get-macro-character (char &optional (rt *readtable*)) + "Causes CHAR to be a macro character which invokes FUNCTION when seen + by the reader. The NON-TERMINATINGP flag can be used to make the macro + character non-terminating, i.e. embeddable in a symbol name." + (let ((designated-readtable (or readtable *standard-readtable*))) + (set-cat-entry char + (if non-terminatingp + (get-secondary-attribute char) + +char-attr-terminating-macro+) + designated-readtable) + (set-cmt-entry char function designated-readtable) + t)) ; (ANSI-specified return value) + +(defun get-macro-character (char &optional (readtable *readtable*)) #!+sb-doc "Return the function associated with the specified CHAR which is a macro - character. The optional readtable argument defaults to the current - readtable." - (let ((rt (or rt *standard-readtable*))) - ;; Check macro syntax, return associated function if it's there. - ;; Returns a value for all constituents. - (cond ((constituentp char) - (values (get-cmt-entry char rt) t)) - ((terminating-macrop char) - (values (get-cmt-entry char rt) nil)) - (t nil)))) + character, or NIL if there is no such function. As a second value, return + T if CHAR is a macro character which is non-terminating, i.e. which can + be embedded in a symbol name." + (let* ((designated-readtable (or readtable *standard-readtable*)) + ;; the first return value: a FUNCTION if CHAR is a macro + ;; character, or NIL otherwise + (fun-value (get-raw-cmt-entry char designated-readtable))) + (values fun-value + ;; NON-TERMINATING-P return value: + (if fun-value + (or (constituentp char) + (not (terminating-macrop char))) + ;; ANSI's definition of GET-MACRO-CHARACTER says this + ;; value is NIL when CHAR is not a macro character. + ;; I.e. this value means not just "non-terminating + ;; character?" but "non-terminating macro character?". + nil)))) ;;;; definitions to support internal programming conventions -(defmacro eofp (char) `(eq ,char *eof-object*)) +(defmacro eofp (char) + `(eq ,char *eof-object*)) (defun flush-whitespace (stream) ;; This flushes whitespace chars, returning the last char it read (a @@ -229,17 +272,27 @@ (let ((stream (in-synonym-of stream))) (if (ansi-stream-p stream) (prepare-for-fast-read-char stream - (do ((attribute-table (character-attribute-table *readtable*)) + (do ((attribute-array (character-attribute-array *readtable*)) + (attribute-hash-table + (character-attribute-hash-table *readtable*)) (char (fast-read-char t) (fast-read-char t))) - ((/= (the fixnum (aref attribute-table (char-code char))) + ((/= (the fixnum + (if (typep char 'base-char) + (aref attribute-array (char-code char)) + (gethash char attribute-hash-table +char-attr-constituent+))) +char-attr-whitespace+) (done-with-fast-read-char) char))) - ;; fundamental-stream - (do ((attribute-table (character-attribute-table *readtable*)) - (char (stream-read-char stream) (stream-read-char stream))) + ;; CLOS stream + (do ((attribute-array (character-attribute-array *readtable*)) + (attribute-hash-table + (character-attribute-hash-table *readtable*)) + (char (read-char stream nil :eof) (read-char stream nil :eof))) ((or (eq char :eof) - (/= (the fixnum (aref attribute-table (char-code char))) + (/= (the fixnum + (if (typep char 'base-char) + (aref attribute-array (char-code char)) + (gethash char attribute-hash-table +char-attr-constituent+))) +char-attr-whitespace+)) (if (eq char :eof) (error 'end-of-file :stream stream) @@ -249,35 +302,40 @@ (defun !cold-init-standard-readtable () (setq *standard-readtable* (make-readtable)) - ;; All characters default to "constituent" in MAKE-READTABLE. - ;; *** un-constituent-ize some of these *** + ;; All characters get boring defaults in MAKE-READTABLE. Now we + ;; override the boring defaults on characters which need more + ;; interesting behavior. (let ((*readtable* *standard-readtable*)) - (set-cat-entry (code-char tab-char-code) +char-attr-whitespace+) - (set-cat-entry #\linefeed +char-attr-whitespace+) - (set-cat-entry #\space +char-attr-whitespace+) - (set-cat-entry (code-char form-feed-char-code) +char-attr-whitespace+) - (set-cat-entry (code-char return-char-code) +char-attr-whitespace+) + + (flet ((whitespaceify (char) + (set-cmt-entry char nil) + (set-cat-entry char +char-attr-whitespace+))) + (whitespaceify (code-char tab-char-code)) + (whitespaceify #\linefeed) + (whitespaceify #\space) + (whitespaceify (code-char form-feed-char-code)) + (whitespaceify (code-char return-char-code))) + (set-cat-entry #\\ +char-attr-escape+) - (set-cmt-entry #\\ #'read-token) - (set-cat-entry (code-char rubout-char-code) +char-attr-whitespace+) - (set-cmt-entry #\: #'read-token) - (set-cmt-entry #\| #'read-token) - ;; macro definitions + (set-cmt-entry #\\ nil) + + ;; Easy macro-character definitions are in this source file. (set-macro-character #\" #'read-string) - ;; * # macro (set-macro-character #\' #'read-quote) (set-macro-character #\( #'read-list) (set-macro-character #\) #'read-right-paren) (set-macro-character #\; #'read-comment) - ;; * backquote + ;; (The hairier macro-character definitions, for #\# and #\`, are + ;; defined elsewhere, in their own source files.) + ;; all constituents (do ((ichar 0 (1+ ichar)) (char)) - ((= ichar #O200)) + ((= ichar base-char-code-limit)) (setq char (code-char ichar)) (when (constituentp char *standard-readtable*) - (set-cat-entry char (get-secondary-attribute char)) - (set-cmt-entry char #'read-token))))) + (set-cat-entry char (get-secondary-attribute char)) + (set-cmt-entry char nil))))) ;;;; implementation of the read buffer @@ -291,7 +349,7 @@ (defvar *ouch-ptr*) (declaim (type index *read-buffer-length* *inch-ptr* *ouch-ptr*)) -(declaim (simple-string *read-buffer*)) +(declaim (type (simple-array character (*)) *read-buffer*)) (defmacro reset-read-buffer () ;; Turn *READ-BUFFER* into an empty read buffer. @@ -381,18 +439,19 @@ "Read from STREAM and return the value read, preserving any whitespace that followed the object." (if recursivep - ;; a loop for repeating when a macro returns nothing - (loop - (let ((char (read-char stream eof-error-p *eof-object*))) - (cond ((eofp char) (return eof-value)) - ((whitespacep char)) - (t - (let* ((macrofun (get-cmt-entry char *readtable*)) - (result (multiple-value-list - (funcall macrofun stream char)))) - ;; Repeat if macro returned nothing. - (if result (return (car result)))))))) - (let ((*sharp-equal-alist* nil)) + ;; a loop for repeating when a macro returns nothing + (loop + (let ((char (read-char stream eof-error-p *eof-object*))) + (cond ((eofp char) (return eof-value)) + ((whitespacep char)) + (t + (let* ((macrofun (get-coerced-cmt-entry char *readtable*)) + (result (multiple-value-list + (funcall macrofun stream char)))) + ;; Repeat if macro returned nothing. + (when result + (return (unless *read-suppress* (car result))))))))) + (let ((*sharp-equal-alist* nil)) (read-preserving-whitespace stream eof-error-p eof-value t)))) ;;; Return NIL or a list with one thing, depending. @@ -401,7 +460,9 @@ ;;; past them. We assume CHAR is not whitespace. (defun read-maybe-nothing (stream char) (let ((retval (multiple-value-list - (funcall (get-cmt-entry char *readtable*) stream char)))) + (funcall (get-coerced-cmt-entry char *readtable*) + stream + char)))) (if retval (rplacd retval nil)))) (defun read (&optional (stream *standard-input*) @@ -414,9 +475,9 @@ eof-error-p eof-value recursivep))) - ;; (This function generally discards trailing whitespace. If you + ;; This function generally discards trailing whitespace. If you ;; don't want to discard trailing whitespace, call - ;; CL:READ-PRESERVING-WHITESPACE instead.) + ;; CL:READ-PRESERVING-WHITESPACE instead. (unless (or (eql result eof-value) recursivep) (let ((next-char (read-char stream nil nil))) (unless (or (null next-char) @@ -435,7 +496,7 @@ (do ((char (flush-whitespace input-stream) (flush-whitespace input-stream)) (retlist ())) - ((char= char endchar) (nreverse retlist)) + ((char= char endchar) (unless *read-suppress* (nreverse retlist))) (setq retlist (nconc (read-maybe-nothing input-stream char) retlist)))) ;;;; basic readmacro definitions @@ -456,8 +517,8 @@ (fast-read-char nil nil))) ((or (not char) (char= char #\newline)) (done-with-fast-read-char)))) - ;; FUNDAMENTAL-STREAM - (do ((char (stream-read-char stream) (stream-read-char stream))) + ;; CLOS stream + (do ((char (read-char stream nil :eof) (read-char stream nil :eof))) ((or (eq char :eof) (char= char #\newline)))))) ;; Don't return anything. (values)) @@ -520,13 +581,13 @@ (done-with-fast-read-char)) (if (escapep char) (setq char (fast-read-char t))) (ouch-read-buffer char))) - ;; FUNDAMENTAL-STREAM - (do ((char (stream-read-char stream) (stream-read-char stream))) + ;; CLOS stream + (do ((char (read-char stream nil :eof) (read-char stream nil :eof))) ((or (eq char :eof) (char= char closech)) (if (eq char :eof) (error 'end-of-file :stream stream))) (when (escapep char) - (setq char (stream-read-char stream)) + (setq char (read-char stream nil :eof)) (if (eq char :eof) (error 'end-of-file :stream stream))) (ouch-read-buffer char)))) @@ -593,8 +654,13 @@ ;;;; character classes ;;; Return the character class for CHAR. -(defmacro char-class (char attable) - `(let ((att (aref ,attable (char-code ,char)))) +;;; +;;; FIXME: why aren't these ATT-getting forms using GET-CAT-ENTRY? +;;; Because we've cached the readtable tables? +(defmacro char-class (char attarray atthash) + `(let ((att (if (typep ,char 'base-char) + (aref ,attarray (char-code ,char)) + (gethash ,char ,atthash +char-attr-constituent+)))) (declare (fixnum att)) (if (<= att +char-attr-terminating-macro+) +char-attr-delimiter+ @@ -602,8 +668,10 @@ ;;; Return the character class for CHAR, which might be part of a ;;; rational number. -(defmacro char-class2 (char attable) - `(let ((att (aref ,attable (char-code ,char)))) +(defmacro char-class2 (char attarray atthash) + `(let ((att (if (typep ,char 'base-char) + (aref ,attarray (char-code ,char)) + (gethash ,char ,atthash +char-attr-constituent+)))) (declare (fixnum att)) (if (<= att +char-attr-terminating-macro+) +char-attr-delimiter+ @@ -616,8 +684,10 @@ ;;; Return the character class for a char which might be part of a ;;; rational or floating number. (Assume that it is a digit if it ;;; could be.) -(defmacro char-class3 (char attable) - `(let ((att (aref ,attable (char-code ,char)))) +(defmacro char-class3 (char attarray atthash) + `(let ((att (if (typep ,char 'base-char) + (aref ,attarray (char-code ,char)) + (gethash ,char ,atthash +char-attr-constituent+)))) (declare (fixnum att)) (if possibly-rational (setq possibly-rational @@ -631,8 +701,10 @@ +char-attr-delimiter+ (if (digit-char-p ,char (max *read-base* 10)) (if (digit-char-p ,char *read-base*) - +char-attr-constituent-digit+ - +char-attr-constituent+) + (if (= att +char-attr-constituent-expt+) + +char-attr-constituent-digit-or-expt+ + +char-attr-constituent-digit+) + +char-attr-constituent-decimal-digit+) att)))) ;;;; token fetching @@ -704,17 +776,25 @@ (when *read-suppress* (internal-read-extended-token stream firstchar nil) (return-from read-token nil)) - (let ((attribute-table (character-attribute-table *readtable*)) + (let ((attribute-array (character-attribute-array *readtable*)) + (attribute-hash-table (character-attribute-hash-table *readtable*)) (package-designator nil) (colons 0) (possibly-rational t) + (seen-digit-or-expt nil) (possibly-float t) - (escapes ())) + (was-possibly-float nil) + (escapes ()) + (seen-multiple-escapes nil)) (reset-read-buffer) (prog ((char firstchar)) - (case (char-class3 char attribute-table) + (case (char-class3 char attribute-array attribute-hash-table) (#.+char-attr-constituent-sign+ (go SIGN)) (#.+char-attr-constituent-digit+ (go LEFTDIGIT)) + (#.+char-attr-constituent-digit-or-expt+ + (setq seen-digit-or-expt t) + (go LEFTDIGIT)) + (#.+char-attr-constituent-decimal-digit+ (go LEFTDECIMALDIGIT)) (#.+char-attr-constituent-dot+ (go FRONTDOT)) (#.+char-attr-escape+ (go ESCAPE)) (#.+char-attr-package-delimiter+ (go COLON)) @@ -727,8 +807,12 @@ (unless char (go RETURN-SYMBOL)) (setq possibly-rational t possibly-float t) - (case (char-class3 char attribute-table) + (case (char-class3 char attribute-array attribute-hash-table) (#.+char-attr-constituent-digit+ (go LEFTDIGIT)) + (#.+char-attr-constituent-digit-or-expt+ + (setq seen-digit-or-expt t) + (go LEFTDIGIT)) + (#.+char-attr-constituent-decimal-digit+ (go LEFTDECIMALDIGIT)) (#.+char-attr-constituent-dot+ (go SIGNDOT)) (#.+char-attr-escape+ (go ESCAPE)) (#.+char-attr-package-delimiter+ (go COLON)) @@ -739,12 +823,43 @@ (ouch-read-buffer char) (setq char (read-char stream nil nil)) (unless char (return (make-integer))) - (case (char-class3 char attribute-table) + (setq was-possibly-float possibly-float) + (case (char-class3 char attribute-array attribute-hash-table) (#.+char-attr-constituent-digit+ (go LEFTDIGIT)) + (#.+char-attr-constituent-decimal-digit+ (if possibly-float + (go LEFTDECIMALDIGIT) + (go SYMBOL))) (#.+char-attr-constituent-dot+ (if possibly-float (go MIDDLEDOT) (go SYMBOL))) - (#.+char-attr-constituent-expt+ (go EXPONENT)) + (#.+char-attr-constituent-digit-or-expt+ + (if (or seen-digit-or-expt (not was-possibly-float)) + (progn (setq seen-digit-or-expt t) (go LEFTDIGIT)) + (progn (setq seen-digit-or-expt t) (go LEFTDIGIT-OR-EXPT)))) + (#.+char-attr-constituent-expt+ + (if was-possibly-float + (go EXPONENT) + (go SYMBOL))) + (#.+char-attr-constituent-slash+ (if possibly-rational + (go RATIO) + (go SYMBOL))) + (#.+char-attr-delimiter+ (unread-char char stream) + (return (make-integer))) + (#.+char-attr-escape+ (go ESCAPE)) + (#.+char-attr-multiple-escape+ (go MULT-ESCAPE)) + (#.+char-attr-package-delimiter+ (go COLON)) + (t (go SYMBOL))) + LEFTDIGIT-OR-EXPT + (ouch-read-buffer char) + (setq char (read-char stream nil nil)) + (unless char (return (make-integer))) + (case (char-class3 char attribute-array attribute-hash-table) + (#.+char-attr-constituent-digit+ (go LEFTDIGIT)) + (#.+char-attr-constituent-decimal-digit+ (bug "impossible!")) + (#.+char-attr-constituent-dot+ (go SYMBOL)) + (#.+char-attr-constituent-digit-or-expt+ (go LEFTDIGIT)) + (#.+char-attr-constituent-expt+ (go SYMBOL)) + (#.+char-attr-constituent-sign+ (go EXPTSIGN)) (#.+char-attr-constituent-slash+ (if possibly-rational (go RATIO) (go SYMBOL))) @@ -754,12 +869,29 @@ (#.+char-attr-multiple-escape+ (go MULT-ESCAPE)) (#.+char-attr-package-delimiter+ (go COLON)) (t (go SYMBOL))) + LEFTDECIMALDIGIT ; saw "[sign] {decimal-digit}+" + (aver possibly-float) + (ouch-read-buffer char) + (setq char (read-char stream nil nil)) + (unless char (go RETURN-SYMBOL)) + (case (char-class char attribute-array attribute-hash-table) + (#.+char-attr-constituent-digit+ (go LEFTDECIMALDIGIT)) + (#.+char-attr-constituent-dot+ (go MIDDLEDOT)) + (#.+char-attr-constituent-expt+ (go EXPONENT)) + (#.+char-attr-constituent-slash+ (aver (not possibly-rational)) + (go SYMBOL)) + (#.+char-attr-delimiter+ (unread-char char stream) + (go RETURN-SYMBOL)) + (#.+char-attr-escape+ (go ESCAPE)) + (#.+char-attr-multiple-escape+ (go MULT-ESCAPE)) + (#.+char-attr-package-delimiter+ (go COLON)) + (t (go SYMBOL))) MIDDLEDOT ; saw "[sign] {digit}+ dot" (ouch-read-buffer char) (setq char (read-char stream nil nil)) (unless char (return (let ((*read-base* 10)) (make-integer)))) - (case (char-class char attribute-table) + (case (char-class char attribute-array attribute-hash-table) (#.+char-attr-constituent-digit+ (go RIGHTDIGIT)) (#.+char-attr-constituent-expt+ (go EXPONENT)) (#.+char-attr-delimiter+ @@ -770,16 +902,16 @@ (#.+char-attr-multiple-escape+ (go MULT-ESCAPE)) (#.+char-attr-package-delimiter+ (go COLON)) (t (go SYMBOL))) - RIGHTDIGIT ; saw "[sign] {digit}* dot {digit}+" + RIGHTDIGIT ; saw "[sign] {decimal-digit}* dot {digit}+" (ouch-read-buffer char) (setq char (read-char stream nil nil)) - (unless char (return (make-float))) - (case (char-class char attribute-table) + (unless char (return (make-float stream))) + (case (char-class char attribute-array attribute-hash-table) (#.+char-attr-constituent-digit+ (go RIGHTDIGIT)) (#.+char-attr-constituent-expt+ (go EXPONENT)) (#.+char-attr-delimiter+ (unread-char char stream) - (return (make-float))) + (return (make-float stream))) (#.+char-attr-escape+ (go ESCAPE)) (#.+char-attr-multiple-escape+ (go MULT-ESCAPE)) (#.+char-attr-package-delimiter+ (go COLON)) @@ -788,7 +920,7 @@ (ouch-read-buffer char) (setq char (read-char stream nil nil)) (unless char (go RETURN-SYMBOL)) - (case (char-class char attribute-table) + (case (char-class char attribute-array attribute-hash-table) (#.+char-attr-constituent-digit+ (go RIGHTDIGIT)) (#.+char-attr-delimiter+ (unread-char char stream) (go RETURN-SYMBOL)) (#.+char-attr-escape+ (go ESCAPE)) @@ -798,7 +930,7 @@ (ouch-read-buffer char) (setq char (read-char stream nil nil)) (unless char (%reader-error stream "dot context error")) - (case (char-class char attribute-table) + (case (char-class char attribute-array attribute-hash-table) (#.+char-attr-constituent-digit+ (go RIGHTDIGIT)) (#.+char-attr-constituent-dot+ (go DOTS)) (#.+char-attr-delimiter+ (%reader-error stream "dot context error")) @@ -810,7 +942,8 @@ (ouch-read-buffer char) (setq char (read-char stream nil nil)) (unless char (go RETURN-SYMBOL)) - (case (char-class char attribute-table) + (setq possibly-float t) + (case (char-class char attribute-array attribute-hash-table) (#.+char-attr-constituent-sign+ (go EXPTSIGN)) (#.+char-attr-constituent-digit+ (go EXPTDIGIT)) (#.+char-attr-delimiter+ (unread-char char stream) (go RETURN-SYMBOL)) @@ -822,7 +955,7 @@ (ouch-read-buffer char) (setq char (read-char stream nil nil)) (unless char (go RETURN-SYMBOL)) - (case (char-class char attribute-table) + (case (char-class char attribute-array attribute-hash-table) (#.+char-attr-constituent-digit+ (go EXPTDIGIT)) (#.+char-attr-delimiter+ (unread-char char stream) (go RETURN-SYMBOL)) (#.+char-attr-escape+ (go ESCAPE)) @@ -832,12 +965,12 @@ EXPTDIGIT ; got to EXPONENT, saw "[sign] {digit}+" (ouch-read-buffer char) (setq char (read-char stream nil nil)) - (unless char (return (make-float))) - (case (char-class char attribute-table) + (unless char (return (make-float stream))) + (case (char-class char attribute-array attribute-hash-table) (#.+char-attr-constituent-digit+ (go EXPTDIGIT)) (#.+char-attr-delimiter+ (unread-char char stream) - (return (make-float))) + (return (make-float stream))) (#.+char-attr-escape+ (go ESCAPE)) (#.+char-attr-multiple-escape+ (go MULT-ESCAPE)) (#.+char-attr-package-delimiter+ (go COLON)) @@ -846,7 +979,7 @@ (ouch-read-buffer char) (setq char (read-char stream nil nil)) (unless char (go RETURN-SYMBOL)) - (case (char-class2 char attribute-table) + (case (char-class2 char attribute-array attribute-hash-table) (#.+char-attr-constituent-digit+ (go RATIODIGIT)) (#.+char-attr-delimiter+ (unread-char char stream) (go RETURN-SYMBOL)) (#.+char-attr-escape+ (go ESCAPE)) @@ -856,12 +989,12 @@ RATIODIGIT ; saw "[sign] {digit}+ slash {digit}+" (ouch-read-buffer char) (setq char (read-char stream nil nil)) - (unless char (return (make-ratio))) - (case (char-class2 char attribute-table) + (unless char (return (make-ratio stream))) + (case (char-class2 char attribute-array attribute-hash-table) (#.+char-attr-constituent-digit+ (go RATIODIGIT)) (#.+char-attr-delimiter+ (unread-char char stream) - (return (make-ratio))) + (return (make-ratio stream))) (#.+char-attr-escape+ (go ESCAPE)) (#.+char-attr-multiple-escape+ (go MULT-ESCAPE)) (#.+char-attr-package-delimiter+ (go COLON)) @@ -870,7 +1003,7 @@ (ouch-read-buffer char) (setq char (read-char stream nil nil)) (unless char (%reader-error stream "too many dots")) - (case (char-class char attribute-table) + (case (char-class char attribute-array attribute-hash-table) (#.+char-attr-constituent-dot+ (go DOTS)) (#.+char-attr-delimiter+ (unread-char char stream) @@ -888,7 +1021,7 @@ (ouch-read-buffer char) (setq char (fast-read-char nil nil)) (unless char (go RETURN-SYMBOL)) - (case (char-class char attribute-table) + (case (char-class char attribute-array attribute-hash-table) (#.+char-attr-escape+ (done-with-fast-read-char) (go ESCAPE)) (#.+char-attr-delimiter+ (done-with-fast-read-char) @@ -899,15 +1032,15 @@ (#.+char-attr-package-delimiter+ (done-with-fast-read-char) (go COLON)) (t (go SYMBOL-LOOP))))) - ;; fundamental-stream + ;; CLOS stream (prog () SYMBOL-LOOP (ouch-read-buffer char) - (setq char (stream-read-char stream)) + (setq char (read-char stream nil :eof)) (when (eq char :eof) (go RETURN-SYMBOL)) - (case (char-class char attribute-table) + (case (char-class char attribute-array attribute-hash-table) (#.+char-attr-escape+ (go ESCAPE)) - (#.+char-attr-delimiter+ (stream-unread-char stream char) + (#.+char-attr-delimiter+ (unread-char char stream) (go RETURN-SYMBOL)) (#.+char-attr-multiple-escape+ (go MULT-ESCAPE)) (#.+char-attr-package-delimiter+ (go COLON)) @@ -922,13 +1055,14 @@ (ouch-read-buffer nextchar)) (setq char (read-char stream nil nil)) (unless char (go RETURN-SYMBOL)) - (case (char-class char attribute-table) + (case (char-class char attribute-array attribute-hash-table) (#.+char-attr-delimiter+ (unread-char char stream) (go RETURN-SYMBOL)) (#.+char-attr-escape+ (go ESCAPE)) (#.+char-attr-multiple-escape+ (go MULT-ESCAPE)) (#.+char-attr-package-delimiter+ (go COLON)) (t (go SYMBOL))) MULT-ESCAPE + (setq seen-multiple-escapes t) (do ((char (read-char stream t) (read-char stream t))) ((multiple-escape-p char)) (if (escapep char) (setq char (read-char stream t))) @@ -936,7 +1070,7 @@ (ouch-read-buffer char)) (setq char (read-char stream nil nil)) (unless char (go RETURN-SYMBOL)) - (case (char-class char attribute-table) + (case (char-class char attribute-array attribute-hash-table) (#.+char-attr-delimiter+ (unread-char char stream) (go RETURN-SYMBOL)) (#.+char-attr-escape+ (go ESCAPE)) (#.+char-attr-multiple-escape+ (go MULT-ESCAPE)) @@ -956,12 +1090,14 @@ ;; a FIND-PACKAGE* function analogous to INTERN* ;; and friends? (read-buffer-to-string) - *keyword-package*)) + (if seen-multiple-escapes + (read-buffer-to-string) + *keyword-package*))) (reset-read-buffer) (setq escapes ()) (setq char (read-char stream nil nil)) (unless char (reader-eof-error stream "after reading a colon")) - (case (char-class char attribute-table) + (case (char-class char attribute-array attribute-hash-table) (#.+char-attr-delimiter+ (unread-char char stream) (%reader-error stream @@ -976,7 +1112,7 @@ (setq char (read-char stream nil nil)) (unless char (reader-eof-error stream "after reading a colon")) - (case (char-class char attribute-table) + (case (char-class char attribute-array attribute-hash-table) (#.+char-attr-delimiter+ (unread-char char stream) (%reader-error stream @@ -1120,7 +1256,7 @@ (the index (* num base)))))))) (setq number (+ num (* number base-power))))))) -(defun make-float () +(defun make-float (stream) ;; Assume that the contents of *read-buffer* are a legal float, with nothing ;; else after it. (read-unwind-read-buffer) @@ -1154,7 +1290,8 @@ (cond ((eofp char) ;; If not, we've read the whole number. (let ((num (make-float-aux number divisor - *read-default-float-format*))) + *read-default-float-format* + stream))) (return-from make-float (if negative-fraction (- num) num)))) ((exponent-letterp char) (setq float-char char) @@ -1172,63 +1309,28 @@ ((not dig) (setq exponent (if negative-exponent (- exponent) exponent))) (setq exponent (+ (* exponent 10) dig))) - ;; Generate and return the float, depending on float-char: + ;; Generate and return the float, depending on FLOAT-CHAR: (let* ((float-format (case (char-upcase float-char) (#\E *read-default-float-format*) (#\S 'short-float) (#\F 'single-float) (#\D 'double-float) (#\L 'long-float))) - num) - ;; toy@rtp.ericsson.se: We need to watch out if the - ;; exponent is too small or too large. We add enough to - ;; EXPONENT to make it within range and scale NUMBER - ;; appropriately. This should avoid any unnecessary - ;; underflow or overflow problems. - (multiple-value-bind (min-expo max-expo) - ;; FIXME: These #. forms are broken w.r.t. - ;; cross-compilation portability. Maybe expressions - ;; like - ;; (LOG SB!XC:MOST-POSITIVE-SHORT-FLOAT 10s0) - ;; could be used instead? Or perhaps some sort of - ;; load-time-form magic? - (case float-format - (short-float - (values - #.(log least-positive-normalized-short-float 10s0) - #.(log most-positive-short-float 10s0))) - (single-float - (values - #.(log least-positive-normalized-single-float 10f0) - #.(log most-positive-single-float 10f0))) - (double-float - (values - #.(log least-positive-normalized-double-float 10d0) - #.(log most-positive-double-float 10d0))) - (long-float - (values - #.(log least-positive-normalized-long-float 10L0) - #.(log most-positive-long-float 10L0)))) - (let ((correction (cond ((<= exponent min-expo) - (ceiling (- min-expo exponent))) - ((>= exponent max-expo) - (floor (- max-expo exponent))) - (t - 0)))) - (incf exponent correction) - (setf number (/ number (expt 10 correction))) - (setq num (make-float-aux number divisor float-format)) - (setq num (* num (expt 10 exponent))) - (return-from make-float (if negative-fraction - (- num) - num)))))) - ;; should never happen + (result (make-float-aux (* (expt 10 exponent) number) + divisor float-format stream))) + (return-from make-float + (if negative-fraction (- result) result)))) (t (bug "bad fallthrough in floating point reader"))))) -(defun make-float-aux (number divisor float-format) - (coerce (/ number divisor) float-format)) +(defun make-float-aux (number divisor float-format stream) + (handler-case + (coerce (/ number divisor) float-format) + (type-error (c) + (error 'reader-impossible-number-error + :error c :stream stream + :format-control "failed to build float")))) -(defun make-ratio () +(defun make-ratio (stream) ;; Assume *READ-BUFFER* contains a legal ratio. Build the number from ;; the string. ;; @@ -1252,13 +1354,18 @@ (dig ())) ((or (eofp ch) (not (setq dig (digit-char-p ch *read-base*))))) (setq denominator (+ (* denominator *read-base*) dig))) - (let ((num (/ numerator denominator))) + (let ((num (handler-case + (/ numerator denominator) + (arithmetic-error (c) + (error 'reader-impossible-number-error + :error c :stream stream + :format-control "failed to build ratio"))))) (if negative-number (- num) num)))) ;;;; cruft for dispatch macros (defun make-char-dispatch-table () - (make-array char-code-limit :initial-element #'dispatch-char-error)) + (make-hash-table)) (defun dispatch-char-error (stream sub-char ignore) (declare (ignore ignore)) @@ -1297,9 +1404,7 @@ (dpair (find disp-char (dispatch-tables rt) :test #'char= :key #'car))) (if dpair - (setf (elt (the simple-vector (cdr dpair)) - (char-code sub-char)) - (coerce function 'function)) + (setf (gethash sub-char (cdr dpair)) (coerce function 'function)) (error "~S is not a dispatch char." disp-char)))) (defun get-dispatch-macro-character (disp-char sub-char @@ -1307,15 +1412,13 @@ #!+sb-doc "Return the macro character function for SUB-CHAR under DISP-CHAR or NIL if there is no associated function." - (unless (digit-char-p sub-char) - (let* ((sub-char (char-upcase sub-char)) - (rt (or rt *standard-readtable*)) - (dpair (find disp-char (dispatch-tables rt) - :test #'char= :key #'car))) - (if dpair - (elt (the simple-vector (cdr dpair)) - (char-code sub-char)) - (error "~S is not a dispatch char." disp-char))))) + (let* ((sub-char (char-upcase sub-char)) + (rt (or rt *standard-readtable*)) + (dpair (find disp-char (dispatch-tables rt) + :test #'char= :key #'car))) + (if dpair + (values (gethash sub-char (cdr dpair))) + (error "~S is not a dispatch char." disp-char)))) (defun read-dispatch-char (stream char) ;; Read some digits. @@ -1338,8 +1441,7 @@ :test #'char= :key #'car))) (if dpair (funcall (the function - (elt (the simple-vector (cdr dpair)) - (char-code sub-char))) + (gethash sub-char (cdr dpair) #'dispatch-char-error)) stream sub-char (if numargp numarg nil)) (%reader-error stream "no dispatch table for dispatch char"))))) @@ -1350,7 +1452,7 @@ #!+sb-doc "A resource of string streams for Read-From-String.") -(defun read-from-string (string &optional eof-error-p eof-value +(defun read-from-string (string &optional (eof-error-p t) eof-value &key (start 0) end preserve-whitespace) #!+sb-doc @@ -1358,14 +1460,15 @@ and the lisp object built by the reader is returned. Macro chars will take effect." (declare (string string)) + (with-array-data ((string string) (start start) - (end (or end (length string)))) + (end (%check-vector-sequence-bounds string start end))) (unless *read-from-string-spares* - (push (internal-make-string-input-stream "" 0 0) - *read-from-string-spares*)) + (push (make-string-input-stream "" 0 0) *read-from-string-spares*)) (let ((stream (pop *read-from-string-spares*))) - (setf (string-input-stream-string stream) string) + (setf (string-input-stream-string stream) + (coerce string '(simple-array character (*)))) (setf (string-input-stream-current stream) start) (setf (string-input-stream-end stream) end) (unwind-protect @@ -1383,51 +1486,55 @@ (default to the beginning and end of the string) It skips over whitespace characters and then tries to parse an integer. The radix parameter must be between 2 and 36." - (with-array-data ((string string) - (start start) - (end (or end (length string)))) - (let ((index (do ((i start (1+ i))) - ((= i end) - (if junk-allowed - (return-from parse-integer (values nil end)) - (error "no non-whitespace characters in number"))) - (declare (fixnum i)) - (unless (whitespacep (char string i)) (return i)))) - (minusp nil) - (found-digit nil) - (result 0)) - (declare (fixnum index)) - (let ((char (char string index))) - (cond ((char= char #\-) - (setq minusp t) - (incf index)) - ((char= char #\+) - (incf index)))) - (loop - (when (= index end) (return nil)) - (let* ((char (char string index)) - (weight (digit-char-p char radix))) - (cond (weight - (setq result (+ weight (* result radix)) - found-digit t)) - (junk-allowed (return nil)) - ((whitespacep char) - (do ((jndex (1+ index) (1+ jndex))) - ((= jndex end)) - (declare (fixnum jndex)) - (unless (whitespacep (char string jndex)) - (error "junk in string ~S" string))) - (return nil)) - (t - (error "junk in string ~S" string)))) - (incf index)) - (values - (if found-digit - (if minusp (- result) result) - (if junk-allowed - nil - (error "no digits in string ~S" string))) - index)))) + (macrolet ((parse-error (format-control) + `(error 'simple-parse-error + :format-control ,format-control + :format-arguments (list string)))) + (with-array-data ((string string :offset-var offset) + (start start) + (end (%check-vector-sequence-bounds string start end))) + (let ((index (do ((i start (1+ i))) + ((= i end) + (if junk-allowed + (return-from parse-integer (values nil end)) + (parse-error "no non-whitespace characters in string ~S."))) + (declare (fixnum i)) + (unless (whitespacep (char string i)) (return i)))) + (minusp nil) + (found-digit nil) + (result 0)) + (declare (fixnum index)) + (let ((char (char string index))) + (cond ((char= char #\-) + (setq minusp t) + (incf index)) + ((char= char #\+) + (incf index)))) + (loop + (when (= index end) (return nil)) + (let* ((char (char string index)) + (weight (digit-char-p char radix))) + (cond (weight + (setq result (+ weight (* result radix)) + found-digit t)) + (junk-allowed (return nil)) + ((whitespacep char) + (loop + (incf index) + (when (= index end) (return)) + (unless (whitespacep (char string index)) + (parse-error "junk in string ~S"))) + (return nil)) + (t + (parse-error "junk in string ~S")))) + (incf index)) + (values + (if found-digit + (if minusp (- result) result) + (if junk-allowed + nil + (parse-error "no digits in string ~S"))) + (- index offset)))))) ;;;; reader initialization code