Optimize MAKE-ARRAY on unknown element-type.
[sbcl.git] / src / code / reader.lisp
index b5c5e73..8f885fa 100644 (file)
 ;;; E for an exponent marker"
 (defvar *read-default-float-format* 'single-float)
 (declaim (type (member short-float single-float double-float long-float)
-              *read-default-float-format*))
+               *read-default-float-format*))
 
 (defvar *readtable*)
 (declaim (type readtable *readtable*))
 #!+sb-doc
 (setf (fdocumentation '*readtable* 'variable)
-       "Variable bound to current readtable.")
+      "Variable bound to current readtable.")
 
-;;; a standard Lisp readtable. This is for recovery from broken
-;;; read-tables (and for WITH-STANDARD-IO-SYNTAX), and should not
-;;; normally be user-visible.
-(defvar *standard-readtable*)
+;;; A standard Lisp readtable (once cold-init is through). This is for
+;;; recovery from broken read-tables (and for
+;;; WITH-STANDARD-IO-SYNTAX), and should not normally be user-visible.
+(defvar *standard-readtable* nil)
 
 (defvar *old-package* nil
   #!+sb-doc
 
 (defun reader-eof-error (stream context)
   (error 'reader-eof-error
-        :stream stream
-        :context context))
-
-(defun %reader-error (stream control &rest args)
-  (error 'reader-error
-        :stream stream
-        :format-control control
-        :format-arguments args))
+         :stream stream
+         :context context))
+
+;;; If The Gods didn't intend for us to use multiple namespaces, why
+;;; did They specify them?
+(defun simple-reader-error (stream control &rest args)
+  (error 'simple-reader-error
+         :stream stream
+         :format-control control
+         :format-arguments args))
 \f
 ;;;; macros and functions for character tables
 
-;;; FIXME: could be SB!XC:DEFMACRO inside EVAL-WHEN (COMPILE EVAL)
-(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)))
+(defun get-cat-entry (char rt)
+  (declare (readtable rt))
+  (if (typep char 'base-char)
+      (elt (character-attribute-array rt) (char-code char))
+      (values (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))
+  (declare (readtable rt))
+  (if (typep char 'base-char)
+      (setf (elt (character-attribute-array rt) (char-code char)) newvalue)
+      (if (= newvalue +char-attr-constituent+)
+          ;; Default value for the C-A-HASH-TABLE is +CHAR-ATTR-CONSTITUENT+.
+          (%remhash char (character-attribute-hash-table rt))
+          (setf (gethash char (character-attribute-hash-table rt)) newvalue)))
+  (values))
 
 ;;; 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)
-    `(svref (character-macro-table ,readtable)
-           (char-code ,char))))
+(defun get-raw-cmt-entry (char readtable)
+  (declare (readtable 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.
+      (values (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 
+  (the function
     (or (get-raw-cmt-entry char readtable)
-       #'read-token)))
+        #'read-token)))
 
 (defun set-cmt-entry (char new-value-designator &optional (rt *readtable*))
-  (setf (svref (character-macro-table rt)
-              (char-code char))
-       (and new-value-designator
-            (%coerce-callable-to-fun new-value-designator))))
+  (let ((new (when new-value-designator
+               (%coerce-callable-to-fun new-value-designator))))
+    (if (typep char 'base-char)
+        (setf (svref (character-macro-array rt) (char-code char)) new)
+        (setf (gethash char (character-macro-hash-table rt)) new))))
 
 (defun undefined-macro-char (stream char)
   (unless *read-suppress*
-    (%reader-error stream "undefined read-macro character ~S" char)))
+    (simple-reader-error stream "undefined read-macro character ~S" char)))
 
 ;;; The character attribute table is a CHAR-CODE-LIMIT vector of integers.
 
 
 ;;; predicates for testing character attributes
 
-#!-sb-fluid (declaim (inline whitespacep))
-(defun whitespacep (char &optional (rt *readtable*))
+#!-sb-fluid
+(progn
+  (declaim (inline whitespace[1]p whitespace[2]p))
+  (declaim (inline constituentp terminating-macrop))
+  (declaim (inline single-escape-p multiple-escape-p))
+  (declaim (inline token-delimiterp)))
+
+;;; the [1] and [2] here refer to ANSI glossary entries for
+;;; "whitespace".
+(defun whitespace[1]p (char)
+  (test-attribute char +char-attr-whitespace+ *standard-readtable*))
+(defun whitespace[2]p (char &optional (rt *readtable*))
   (test-attribute char +char-attr-whitespace+ rt))
 
-(defmacro constituentp (char &optional (rt '*readtable*))
-  `(>= (get-cat-entry ,char ,rt) +char-attr-constituent+))
+(defun constituentp (char &optional (rt *readtable*))
+  (test-attribute char +char-attr-constituent+ rt))
 
-(defmacro terminating-macrop (char &optional (rt '*readtable*))
-  `(test-attribute ,char +char-attr-terminating-macro+ ,rt))
+(defun terminating-macrop (char &optional (rt *readtable*))
+  (test-attribute char +char-attr-terminating-macro+ rt))
 
-(defmacro escapep (char &optional (rt '*readtable*))
-  `(test-attribute ,char +char-attr-escape+ ,rt))
+(defun single-escape-p (char &optional (rt *readtable*))
+  (test-attribute char +char-attr-single-escape+ rt))
 
-(defmacro multiple-escape-p (char &optional (rt '*readtable*))
-  `(test-attribute ,char +char-attr-multiple-escape+ ,rt))
+(defun multiple-escape-p (char &optional (rt *readtable*))
+  (test-attribute char +char-attr-multiple-escape+ rt))
 
-(defmacro token-delimiterp (char &optional (rt '*readtable*))
-  ;; depends on actual attribute numbering above.
-  `(<= (get-cat-entry ,char ,rt) +char-attr-terminating-macro+))
+(defun token-delimiterp (char &optional (rt *readtable*))
+  ;; depends on actual attribute numbering in readtable.lisp.
+  (<= (get-cat-entry char rt) +char-attr-terminating-macro+))
 \f
-;;;; secondary attribute table
+;;;; constituent traits (see ANSI 2.1.4.2)
 
 ;;; There are a number of "secondary" attributes which are constant
 ;;; properties of characters (as long as they are constituents).
 
-(defvar *secondary-attribute-table*)
-(declaim (type attribute-table *secondary-attribute-table*))
-
-(defun !set-secondary-attribute (char attribute)
-  (setf (elt *secondary-attribute-table* (char-code char))
-       attribute))
-
-(defun !cold-init-secondary-attribute-table ()
-  (setq *secondary-attribute-table*
-       (make-array 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]
-  (!set-secondary-attribute #\. +char-attr-constituent-dot+)
-  (!set-secondary-attribute #\+ +char-attr-constituent-sign+)
-  (!set-secondary-attribute #\- +char-attr-constituent-sign+)
-  (!set-secondary-attribute #\/ +char-attr-constituent-slash+)
+(defvar *constituent-trait-table*)
+(declaim (type attribute-table *constituent-trait-table*))
+
+(defun !set-constituent-trait (char trait)
+  (aver (typep char 'base-char))
+  (setf (elt *constituent-trait-table* (char-code char))
+        trait))
+
+(defun !cold-init-constituent-trait-table ()
+  (setq *constituent-trait-table*
+        (make-array base-char-code-limit :element-type '(unsigned-byte 8)
+                    :initial-element +char-attr-constituent+))
+  (!set-constituent-trait #\: +char-attr-package-delimiter+)
+  (!set-constituent-trait #\. +char-attr-constituent-dot+)
+  (!set-constituent-trait #\+ +char-attr-constituent-sign+)
+  (!set-constituent-trait #\- +char-attr-constituent-sign+)
+  (!set-constituent-trait #\/ +char-attr-constituent-slash+)
   (do ((i (char-code #\0) (1+ i)))
       ((> i (char-code #\9)))
-    (!set-secondary-attribute (code-char i) +char-attr-constituent-digit+))
-  (!set-secondary-attribute #\E +char-attr-constituent-expt+)
-  (!set-secondary-attribute #\F +char-attr-constituent-expt+)
-  (!set-secondary-attribute #\D +char-attr-constituent-expt+)
-  (!set-secondary-attribute #\S +char-attr-constituent-expt+)
-  (!set-secondary-attribute #\L +char-attr-constituent-expt+)
-  (!set-secondary-attribute #\e +char-attr-constituent-expt+)
-  (!set-secondary-attribute #\f +char-attr-constituent-expt+)
-  (!set-secondary-attribute #\d +char-attr-constituent-expt+)
-  (!set-secondary-attribute #\s +char-attr-constituent-expt+)
-  (!set-secondary-attribute #\l +char-attr-constituent-expt+))
-
-(defmacro get-secondary-attribute (char)
-  `(elt *secondary-attribute-table*
-       (char-code ,char)))
+    (!set-constituent-trait (code-char i) +char-attr-constituent-digit+))
+  (!set-constituent-trait #\E +char-attr-constituent-expt+)
+  (!set-constituent-trait #\F +char-attr-constituent-expt+)
+  (!set-constituent-trait #\D +char-attr-constituent-expt+)
+  (!set-constituent-trait #\S +char-attr-constituent-expt+)
+  (!set-constituent-trait #\L +char-attr-constituent-expt+)
+  (!set-constituent-trait #\e +char-attr-constituent-expt+)
+  (!set-constituent-trait #\f +char-attr-constituent-expt+)
+  (!set-constituent-trait #\d +char-attr-constituent-expt+)
+  (!set-constituent-trait #\s +char-attr-constituent-expt+)
+  (!set-constituent-trait #\l +char-attr-constituent-expt+)
+  (!set-constituent-trait #\Space +char-attr-invalid+)
+  (!set-constituent-trait #\Newline +char-attr-invalid+)
+  (dolist (c (list backspace-char-code tab-char-code form-feed-char-code
+                   return-char-code rubout-char-code))
+    (!set-constituent-trait (code-char c) +char-attr-invalid+)))
+
+(declaim (inline get-constituent-trait))
+(defun get-constituent-trait (char)
+  (if (typep char 'base-char)
+      (elt *constituent-trait-table* (char-code char))
+      +char-attr-constituent+))
 \f
-;;;; readtable operations
+;;;; Readtable Operations
+
+(defun assert-not-standard-readtable (readtable operation)
+  (when (eq readtable *standard-readtable*)
+    (cerror "Frob it anyway!" 'standard-readtable-modified-error
+            :operation operation)))
+
+(defun readtable-case (readtable)
+  (%readtable-case readtable))
 
-(defun copy-readtable (&optional (from-readtable *readtable*)
-                                to-readtable)
+(defun (setf readtable-case) (case readtable)
+  (assert-not-standard-readtable readtable '(setf readtable-case))
+  (setf (%readtable-case readtable) case))
+
+(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)
+  (assert-not-standard-readtable to-readtable 'copy-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))))
-                 (dispatch-tables really-from-readtable)))
+          (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))
+          (readtable-case really-from-readtable))
     really-to-readtable))
 
 (defun set-syntax-from-char (to-char from-char &optional
-                                    (to-readtable *readtable*)
-                                    (from-readtable ()))
+                             (to-readtable *readtable*) (from-readtable nil))
   #!+sb-doc
-  "Causes the syntax of TO-CHAR to be the same as FROM-CHAR in the
-  optional readtable (defaults to the current readtable). The
-  FROM-TABLE defaults to the standard Lisp readtable when NIL."
+  "Causes the syntax of TO-CHAR to be the same as FROM-CHAR in the optional
+readtable (defaults to the current readtable). The FROM-TABLE defaults to the
+standard Lisp readtable when NIL."
+  (assert-not-standard-readtable to-readtable 'set-syntax-from-char)
   (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
-    ;; 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)))
-      (if (constituentp from-char really-from-readtable)
-         (setq att (get-secondary-attribute to-char)))
+    (let ((att (get-cat-entry from-char really-from-readtable))
+          (mac (get-raw-cmt-entry from-char really-from-readtable))
+          (from-dpair (find from-char (dispatch-tables really-from-readtable)
+                            :test #'char= :key #'car))
+          (to-dpair (find to-char (dispatch-tables to-readtable)
+                          :test #'char= :key #'car)))
       (set-cat-entry to-char att to-readtable)
-      (set-cmt-entry to-char
-                    (get-raw-cmt-entry from-char really-from-readtable)
-                    to-readtable)))
+      (set-cmt-entry to-char mac to-readtable)
+      (cond ((and (not from-dpair) (not to-dpair)))
+            ((and (not from-dpair) to-dpair)
+             (setf (dispatch-tables to-readtable)
+                   (remove to-dpair (dispatch-tables to-readtable))))
+            (to-dpair
+             (let ((table (cdr to-dpair)))
+               (clrhash table)
+               (shallow-replace/eql-hash-table table (cdr from-dpair))))
+            (t
+             (let ((pair (cons to-char (make-hash-table))))
+               (shallow-replace/eql-hash-table (cdr pair) (cdr from-dpair))
+               (setf (dispatch-tables to-readtable)
+                     (push pair (dispatch-tables to-readtable))))))))
   t)
 
 (defun set-macro-character (char function &optional
-                                (non-terminatingp nil)
-                                (readtable *readtable*))
+                                 (non-terminatingp nil)
+                                 (rt-designator *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, 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)
+  (let ((designated-readtable (or rt-designator *standard-readtable*))
+        (function (%coerce-callable-to-fun function)))
+    (assert-not-standard-readtable designated-readtable 'set-macro-character)
+    (set-cat-entry char (if non-terminatingp
+                            +char-attr-constituent+
+                            +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*))
+(defun get-macro-character (char &optional (rt-designator *readtable*))
   #!+sb-doc
   "Return the function associated with the specified CHAR which is a macro
   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)))
+  (let* ((designated-readtable (or rt-designator *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))))
+            ;; NON-TERMINATING-P return value:
+            (if fun-value
+                (or (constituentp char designated-readtable)
+                    (not (terminating-macrop char designated-readtable)))
+                ;; 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))))
+
+
+(defun make-char-dispatch-table ()
+  (make-hash-table))
+
+(defun make-dispatch-macro-character (char &optional
+                                      (non-terminating-p nil)
+                                      (rt *readtable*))
+  #!+sb-doc
+  "Cause CHAR to become a dispatching macro character in readtable (which
+   defaults to the current readtable). If NON-TERMINATING-P, the char will
+   be non-terminating."
+  ;; Checks already for standard readtable modification.
+  (set-macro-character char #'read-dispatch-char non-terminating-p rt)
+  (let* ((dalist (dispatch-tables rt))
+         (dtable (cdr (find char dalist :test #'char= :key #'car))))
+    (cond (dtable
+           (error "The dispatch character ~S already exists." char))
+          (t
+           (setf (dispatch-tables rt)
+                 (push (cons char (make-char-dispatch-table)) dalist)))))
+  t)
+
+(defun set-dispatch-macro-character (disp-char sub-char function
+                                     &optional (rt-designator *readtable*))
+  #!+sb-doc
+  "Cause FUNCTION to be called whenever the reader reads DISP-CHAR
+   followed by SUB-CHAR."
+  ;; Get the dispatch char for macro (error if not there), diddle
+  ;; entry for sub-char.
+  (let* ((sub-char (char-upcase sub-char))
+         (readtable (or rt-designator *standard-readtable*)))
+    (assert-not-standard-readtable readtable 'set-dispatch-macro-character)
+    (when (digit-char-p sub-char)
+      (error "SUB-CHAR must not be a decimal digit: ~S" sub-char))
+    (let ((dpair (find disp-char (dispatch-tables readtable)
+                       :test #'char= :key #'car)))
+      (if dpair
+          (setf (gethash sub-char (cdr dpair)) (coerce function 'function))
+          (error "~S is not a dispatch char." disp-char))))
+  t)
+
+(defun get-dispatch-macro-character (disp-char sub-char
+                                     &optional (rt-designator *readtable*))
+  #!+sb-doc
+  "Return the macro character function for SUB-CHAR under DISP-CHAR
+   or NIL if there is no associated function."
+  (let* ((sub-char  (char-upcase sub-char))
+         (readtable (or rt-designator *standard-readtable*))
+         (dpair     (find disp-char (dispatch-tables readtable)
+                          :test #'char= :key #'car)))
+    (if dpair
+        (values (gethash sub-char (cdr dpair)))
+        (error "~S is not a dispatch char." disp-char))))
+
 \f
 ;;;; definitions to support internal programming conventions
 
-(defmacro eofp (char)
-  `(eq ,char *eof-object*))
+(declaim (inline eofp))
+(defun eofp (char)
+  (eq char *eof-object*))
 
 (defun flush-whitespace (stream)
   ;; This flushes whitespace chars, returning the last char it read (a
   ;; non-white one). It always gets an error on end-of-file.
   (let ((stream (in-synonym-of stream)))
     (if (ansi-stream-p stream)
-       (prepare-for-fast-read-char stream
-         (do ((attribute-table (character-attribute-table *readtable*))
-              (char (fast-read-char t) (fast-read-char t)))
-             ((/= (the fixnum (aref attribute-table (char-code char)))
-                  +char-attr-whitespace+)
-              (done-with-fast-read-char)
-              char)))
-       ;; CLOS stream
-       (do ((attribute-table (character-attribute-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)))
-                    +char-attr-whitespace+))
-            (if (eq char :eof)
-                (error 'end-of-file :stream stream)
-                char))))))
+        (prepare-for-fast-read-char stream
+          (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
+                     (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)))
+        ;; 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
+                       (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)
+                 char))))))
 \f
 ;;;; temporary initialization hack
 
+;; Install the (easy) standard macro-chars into *READTABLE*.
 (defun !cold-init-standard-readtable ()
-  (setq *standard-readtable* (make-readtable))
+  (/show0 "entering !cold-init-standard-readtable")
   ;; 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*))
-
-    (flet ((whitespaceify (char)
-            (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)
-
-    ;; Easy macro-character definitions are in this source file.
-    (set-macro-character #\" #'read-string)
-    (set-macro-character #\' #'read-quote)
-    (set-macro-character #\( #'read-list)
-    (set-macro-character #\) #'read-right-paren)
-    (set-macro-character #\; #'read-comment)
-    ;; (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))
-      (setq char (code-char ichar))
-      (when (constituentp char *standard-readtable*)
-           (set-cat-entry char (get-secondary-attribute char))
-           (set-cmt-entry char nil)))))
+  (flet ((whitespaceify (char)
+           (set-cmt-entry char nil)
+           (set-cat-entry char +char-attr-whitespace+)))
+    (whitespaceify (code-char tab-char-code))
+    (whitespaceify #\Newline)
+    (whitespaceify #\Space)
+    (whitespaceify (code-char form-feed-char-code))
+    (whitespaceify (code-char return-char-code)))
+
+  (set-cat-entry #\\ +char-attr-single-escape+)
+  (set-cmt-entry #\\ nil)
+
+  (set-cat-entry #\| +char-attr-multiple-escape+)
+  (set-cmt-entry #\| nil)
+
+  ;; Easy macro-character definitions are in this source file.
+  (set-macro-character #\" #'read-string)
+  (set-macro-character #\' #'read-quote)
+  (set-macro-character #\( #'read-list)
+  (set-macro-character #\) #'read-right-paren)
+  (set-macro-character #\; #'read-comment)
+  ;; (The hairier macro-character definitions, for #\# and #\`, are
+  ;; defined elsewhere, in their own source files.)
+
+  ;; all constituents
+  (do ((ichar 0 (1+ ichar))
+       (char))
+      ((= ichar base-char-code-limit))
+    (setq char (code-char ichar))
+    (when (constituentp char)
+      (set-cmt-entry char nil)))
+
+  (/show0 "leaving !cold-init-standard-readtable"))
 \f
 ;;;; implementation of the read buffer
 
 (defvar *read-buffer*)
-(defvar *read-buffer-length*)
-;;; FIXME: Is it really helpful to have *READ-BUFFER-LENGTH* be a
-;;; separate variable instead of just calculating it on the fly as
-;;; (LENGTH *READ-BUFFER*)?
 
-(defvar *inch-ptr*)
-(defvar *ouch-ptr*)
+(defvar *inch-ptr*) ; *OUCH-PTR* always points to next char to write.
+(defvar *ouch-ptr*) ; *INCH-PTR* always points to next char to read.
 
-(declaim (type index *read-buffer-length* *inch-ptr* *ouch-ptr*))
+(declaim (type index *inch-ptr* *ouch-ptr*))
 (declaim (type (simple-array character (*)) *read-buffer*))
 
-(defmacro reset-read-buffer ()
+(declaim (inline reset-read-buffer))
+(defun reset-read-buffer ()
   ;; Turn *READ-BUFFER* into an empty read buffer.
-  `(progn
-     ;; *OUCH-PTR* always points to next char to write.
-     (setq *ouch-ptr* 0)
-     ;; *INCH-PTR* always points to next char to read.
-     (setq *inch-ptr* 0)))
-
-(defun !cold-init-read-buffer ()
-  (setq *read-buffer* (make-string 512)) ; initial bufsize
-  (setq *read-buffer-length* 512)
-  (reset-read-buffer))
-
-;;; FIXME I removed "THE FIXNUM"'s from OUCH-READ-BUFFER and
-;;; OUCH-UNREAD-BUFFER, check to make sure that Python really is smart
-;;; enough to make good code without them. And while I'm at it,
-;;; converting them from macros to inline functions might be good,
-;;; too.
-
-(defmacro ouch-read-buffer (char)
-  `(progn
-     ;; When buffer overflow
-     (when (>= *ouch-ptr* *read-buffer-length*)
-       ;; Size should be doubled.
-       (grow-read-buffer))
-     (setf (elt (the simple-string *read-buffer*) *ouch-ptr*) ,char)
-     (setq *ouch-ptr* (1+ *ouch-ptr*))))
-
-;;; macro to move *ouch-ptr* back one.
-(defmacro ouch-unread-buffer ()
-  '(when (> *ouch-ptr* *inch-ptr*)
-     (setq *ouch-ptr* (1- (the fixnum *ouch-ptr*)))))
+  (setq *ouch-ptr* 0)
+  (setq *inch-ptr* 0))
+
+(declaim (inline ouch-read-buffer))
+(defun ouch-read-buffer (char)
+  ;; When buffer overflow
+  (let ((op *ouch-ptr*))
+    (declare (optimize (sb!c::insert-array-bounds-checks 0)))
+    (when (>= op (length *read-buffer*))
+    ;; Size should be doubled.
+      (grow-read-buffer))
+    (setf (elt *read-buffer* op) char)
+    (setq *ouch-ptr* (1+ op))))
 
 (defun grow-read-buffer ()
-  (let ((rbl (length (the simple-string *read-buffer*))))
-    (setq *read-buffer*
-         (concatenate 'simple-string
-                      *read-buffer*
-                      (make-string rbl)))
-    (setq *read-buffer-length* (* 2 rbl))))
-
-(defun inchpeek-read-buffer ()
-  (if (>= (the fixnum *inch-ptr*) (the fixnum *ouch-ptr*))
-      *eof-object*
-      (elt *read-buffer* *inch-ptr*)))
+  (let* ((rbl (length *read-buffer*))
+         (new-length (* 2 rbl))
+         (new-buffer (make-string new-length)))
+    (setq *read-buffer* (replace new-buffer *read-buffer*))))
 
 (defun inch-read-buffer ()
   (if (>= *inch-ptr* *ouch-ptr*)
       *eof-object*
       (prog1
-         (elt *read-buffer* *inch-ptr*)
-       (incf *inch-ptr*))))
+          (elt *read-buffer* *inch-ptr*)
+        (incf *inch-ptr*))))
 
-(defmacro unread-buffer ()
-  `(decf *inch-ptr*))
+(declaim (inline unread-buffer))
+(defun unread-buffer ()
+  (decf *inch-ptr*))
 
+(declaim (inline read-unwind-read-buffer))
 (defun read-unwind-read-buffer ()
   ;; Keep contents, but make next (INCH..) return first character.
   (setq *inch-ptr* 0))
 
 (defun read-buffer-to-string ()
   (subseq *read-buffer* 0 *ouch-ptr*))
+
+(defmacro with-read-buffer (() &body body)
+  `(let* ((*read-buffer* (make-string 128))
+          (*ouch-ptr* 0)
+          (*inch-ptr* 0))
+     ,@body))
+
+(declaim (inline read-buffer-boundp))
+(defun read-buffer-boundp ()
+  (and (boundp '*read-buffer*)
+       (boundp '*ouch-ptr*)
+       (boundp '*inch-ptr*)))
+
+(defun check-for-recursive-read (stream recursive-p operator-name)
+  (when (and recursive-p (not (read-buffer-boundp)))
+    (simple-reader-error
+     stream
+     "~A was invoked with RECURSIVE-P being true outside ~
+      of a recursive read operation."
+     `(,operator-name))))
 \f
 ;;;; READ-PRESERVING-WHITESPACE, READ-DELIMITED-LIST, and READ
 
 
 (declaim (special *standard-input*))
 
+;;; Like READ-PRESERVING-WHITESPACE, but doesn't check the read buffer
+;;; for being set up properly.
+(defun %read-preserving-whitespace (stream eof-error-p eof-value recursive-p)
+  (if recursive-p
+      ;; 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))
+               ((whitespace[2]p 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))
+        (with-read-buffer ()
+          (%read-preserving-whitespace stream eof-error-p eof-value t)))))
+
 ;;; READ-PRESERVING-WHITESPACE behaves just like READ, only it makes
 ;;; sure to leave terminating whitespace in the stream. (This is a
 ;;; COMMON-LISP exported symbol.)
 (defun read-preserving-whitespace (&optional (stream *standard-input*)
-                                            (eof-error-p t)
-                                            (eof-value nil)
-                                            (recursivep nil))
+                                             (eof-error-p t)
+                                             (eof-value nil)
+                                             (recursive-p nil))
   #!+sb-doc
   "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-coerced-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))
-       (read-preserving-whitespace stream eof-error-p eof-value t))))
+  (check-for-recursive-read stream recursive-p 'read-preserving-whitespace)
+  (%read-preserving-whitespace stream eof-error-p eof-value recursive-p))
 
 ;;; Return NIL or a list with one thing, depending.
 ;;;
 ;;; past them. We assume CHAR is not whitespace.
 (defun read-maybe-nothing (stream char)
   (let ((retval (multiple-value-list
-                (funcall (get-coerced-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*)
-                      (eof-error-p t)
-                      (eof-value ())
-                      (recursivep ()))
+                       (eof-error-p t)
+                       (eof-value nil)
+                       (recursive-p nil))
   #!+sb-doc
   "Read the next Lisp value from STREAM, and return it."
-  (let ((result (read-preserving-whitespace stream
-                                           eof-error-p
-                                           eof-value
-                                           recursivep)))
-    ;; (This function generally discards trailing whitespace. If you
+  (check-for-recursive-read stream recursive-p 'read)
+  (let ((result (%read-preserving-whitespace stream eof-error-p eof-value
+                                             recursive-p)))
+    ;; This function generally discards trailing whitespace. If you
     ;; don't want to discard trailing whitespace, call
-    ;; CL:READ-PRESERVING-WHITESPACE instead.)
-    (unless (or (eql result eof-value) recursivep)
+    ;; CL:READ-PRESERVING-WHITESPACE instead.
+    (unless (or (eql result eof-value) recursive-p)
       (let ((next-char (read-char stream nil nil)))
-       (unless (or (null next-char)
-                   (whitespacep next-char))
-         (unread-char next-char stream))))
+        (unless (or (null next-char)
+                    (whitespace[2]p next-char))
+          (unread-char next-char stream))))
     result))
 
 ;;; (This is a COMMON-LISP exported symbol.)
 (defun read-delimited-list (endchar &optional
-                                   (input-stream *standard-input*)
-                                   recursive-p)
+                                    (input-stream *standard-input*)
+                                    recursive-p)
   #!+sb-doc
   "Read Lisp values from INPUT-STREAM until the next character after a
    value's representation is ENDCHAR, and return the objects as a list."
-  (declare (ignore recursive-p))
-  (do ((char (flush-whitespace input-stream)
-            (flush-whitespace input-stream))
-       (retlist ()))
-      ((char= char endchar) (nreverse retlist))
-    (setq retlist (nconc (read-maybe-nothing input-stream char) retlist))))
+  (check-for-recursive-read input-stream recursive-p 'read-delimited-list)
+  (flet ((%read-delimited-list (endchar input-stream)
+           (do ((char (flush-whitespace input-stream)
+                      (flush-whitespace input-stream))
+                (retlist ()))
+               ((char= char endchar)
+                (unless *read-suppress* (nreverse retlist)))
+             (setq retlist (nconc (read-maybe-nothing input-stream char)
+                                  retlist)))))
+    (declare (inline %read-delimited-list))
+    (if recursive-p
+        (%read-delimited-list endchar input-stream)
+        (with-read-buffer ()
+          (%read-delimited-list endchar input-stream)))))
 \f
 ;;;; basic readmacro definitions
 ;;;;
 
 (defun read-comment (stream ignore)
   (declare (ignore ignore))
-  (let ((stream (in-synonym-of stream)))
-    (if (ansi-stream-p stream)
-       (prepare-for-fast-read-char stream
-         (do ((char (fast-read-char nil nil)
-                    (fast-read-char nil nil)))
-             ((or (not char) (char= char #\newline))
-              (done-with-fast-read-char))))
-       ;; CLOS stream
-       (do ((char (read-char stream nil :eof) (read-char stream nil :eof)))
-           ((or (eq char :eof) (char= char #\newline))))))
+  (handler-bind
+      ((character-decoding-error
+        #'(lambda (decoding-error)
+            (declare (ignorable decoding-error))
+            (style-warn
+             'sb!kernel::character-decoding-error-in-macro-char-comment
+             :position (file-position stream) :stream stream)
+            (invoke-restart 'attempt-resync))))
+    (let ((stream (in-synonym-of stream)))
+      (if (ansi-stream-p stream)
+          (prepare-for-fast-read-char stream
+           (do ((char (fast-read-char nil nil)
+                      (fast-read-char nil nil)))
+               ((or (not char) (char= char #\newline))
+                (done-with-fast-read-char))))
+          ;; 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))
 
 (defun read-list (stream ignore)
   (declare (ignore ignore))
   (let* ((thelist (list nil))
-        (listtail thelist))
+         (listtail thelist))
     (do ((firstchar (flush-whitespace stream) (flush-whitespace stream)))
-       ((char= firstchar #\) ) (cdr thelist))
+        ((char= firstchar #\) ) (cdr thelist))
       (when (char= firstchar #\.)
-           (let ((nextchar (read-char stream t)))
-             (cond ((token-delimiterp nextchar)
-                    (cond ((eq listtail thelist)
-                           (%reader-error
-                            stream
-                            "Nothing appears before . in list."))
-                          ((whitespacep nextchar)
-                           (setq nextchar (flush-whitespace stream))))
-                    (rplacd listtail
-                            ;; Return list containing last thing.
-                            (car (read-after-dot stream nextchar)))
-                    (return (cdr thelist)))
-                   ;; Put back NEXTCHAR so that we can read it normally.
-                   (t (unread-char nextchar stream)))))
+            (let ((nextchar (read-char stream t)))
+              (cond ((token-delimiterp nextchar)
+                     (cond ((eq listtail thelist)
+                            (unless *read-suppress*
+                              (simple-reader-error
+                               stream
+                               "Nothing appears before . in list.")))
+                           ((whitespace[2]p nextchar)
+                            (setq nextchar (flush-whitespace stream))))
+                     (rplacd listtail
+                             ;; Return list containing last thing.
+                             (car (read-after-dot stream nextchar)))
+                     (return (cdr thelist)))
+                    ;; Put back NEXTCHAR so that we can read it normally.
+                    (t (unread-char nextchar stream)))))
       ;; Next thing is not an isolated dot.
       (let ((listobj (read-maybe-nothing stream firstchar)))
-       ;; allows the possibility that a comment was read
-       (when listobj
-             (rplacd listtail listobj)
-             (setq listtail listobj))))))
+        ;; allows the possibility that a comment was read
+        (when listobj
+              (rplacd listtail listobj)
+              (setq listtail listobj))))))
 
 (defun read-after-dot (stream firstchar)
   ;; FIRSTCHAR is non-whitespace!
   (let ((lastobj ()))
     (do ((char firstchar (flush-whitespace stream)))
-       ((char= char #\) )
-        (%reader-error stream "Nothing appears after . in list."))
+        ((char= char #\) )
+         (if *read-suppress*
+             (return-from read-after-dot nil)
+             (simple-reader-error stream "Nothing appears after . in list.")))
       ;; See whether there's something there.
       (setq lastobj (read-maybe-nothing stream char))
       (when lastobj (return t)))
     ;; At least one thing appears after the dot.
     ;; Check for more than one thing following dot.
     (do ((lastchar (flush-whitespace stream)
-                  (flush-whitespace stream)))
-       ((char= lastchar #\) ) lastobj) ;success!
+                   (flush-whitespace stream)))
+        ((char= lastchar #\) ) lastobj) ;success!
       ;; Try reading virtual whitespace.
-      (if (read-maybe-nothing stream lastchar)
-         (%reader-error stream "More than one object follows . in list.")))))
+      (if (and (read-maybe-nothing stream lastchar)
+               (not *read-suppress*))
+          (simple-reader-error stream
+                               "More than one object follows . in list.")))))
 
 (defun read-string (stream closech)
   ;; This accumulates chars until it sees same char that invoked it.
   (reset-read-buffer)
   (let ((stream (in-synonym-of stream)))
     (if (ansi-stream-p stream)
-       (prepare-for-fast-read-char stream
-         (do ((char (fast-read-char t) (fast-read-char t)))
-             ((char= char closech)
-              (done-with-fast-read-char))
-           (if (escapep char) (setq char (fast-read-char t)))
-           (ouch-read-buffer char)))
-       ;; 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 (read-char stream nil :eof))
-           (if (eq char :eof)
-               (error 'end-of-file :stream stream)))
-         (ouch-read-buffer char))))
+        (prepare-for-fast-read-char stream
+          (do ((char (fast-read-char t) (fast-read-char t)))
+              ((char= char closech)
+               (done-with-fast-read-char))
+            (if (single-escape-p char) (setq char (fast-read-char t)))
+            (ouch-read-buffer char)))
+        ;; 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 (single-escape-p char)
+            (setq char (read-char stream nil :eof))
+            (if (eq char :eof)
+                (error 'end-of-file :stream stream)))
+          (ouch-read-buffer char))))
   (read-buffer-to-string))
 
 (defun read-right-paren (stream ignore)
   (declare (ignore ignore))
-  (%reader-error stream "unmatched close parenthesis"))
+  (simple-reader-error stream "unmatched close parenthesis"))
 
 ;;; Read from the stream up to the next delimiter. Leave the resulting
 ;;; token in *READ-BUFFER*, and return two values:
   (do ((char firstchar (read-char stream nil *eof-object*))
        (colon nil))
       ((cond ((eofp char) t)
-            ((token-delimiterp char)
-             (unread-char char stream)
-             t)
-            (t nil))
+             ((token-delimiterp char)
+              (unread-char char stream)
+              t)
+             (t nil))
        (values escapes colon))
-    (cond ((escapep char)
-          ;; It can't be a number, even if it's 1\23.
-          ;; Read next char here, so it won't be casified.
-          (push *ouch-ptr* escapes)
-          (let ((nextchar (read-char stream nil *eof-object*)))
-            (if (eofp nextchar)
-                (reader-eof-error stream "after escape character")
-                (ouch-read-buffer nextchar))))
-         ((multiple-escape-p char)
-          ;; Read to next multiple-escape, escaping single chars
-          ;; along the way.
-          (loop
-            (let ((ch (read-char stream nil *eof-object*)))
-              (cond
-               ((eofp ch)
-                (reader-eof-error stream "inside extended token"))
-               ((multiple-escape-p ch) (return))
-               ((escapep ch)
-                (let ((nextchar (read-char stream nil *eof-object*)))
-                  (cond ((eofp nextchar)
-                         (reader-eof-error stream "after escape character"))
-                        (t
-                         (push *ouch-ptr* escapes)
-                         (ouch-read-buffer nextchar)))))
-               (t
-                (push *ouch-ptr* escapes)
-                (ouch-read-buffer ch))))))
-         (t
-          (when (and (constituentp char)
-                       (eql (get-secondary-attribute char)
-                             +char-attr-package-delimiter+)
-                     (not colon))
-            (setq colon *ouch-ptr*))
-          (ouch-read-buffer char))))))
+    (cond ((single-escape-p char)
+           ;; It can't be a number, even if it's 1\23.
+           ;; Read next char here, so it won't be casified.
+           (push *ouch-ptr* escapes)
+           (let ((nextchar (read-char stream nil *eof-object*)))
+             (if (eofp nextchar)
+                 (reader-eof-error stream "after escape character")
+                 (ouch-read-buffer nextchar))))
+          ((multiple-escape-p char)
+           ;; Read to next multiple-escape, escaping single chars
+           ;; along the way.
+           (loop
+             (let ((ch (read-char stream nil *eof-object*)))
+               (cond
+                ((eofp ch)
+                 (reader-eof-error stream "inside extended token"))
+                ((multiple-escape-p ch) (return))
+                ((single-escape-p ch)
+                 (let ((nextchar (read-char stream nil *eof-object*)))
+                   (cond ((eofp nextchar)
+                          (reader-eof-error stream "after escape character"))
+                         (t
+                          (push *ouch-ptr* escapes)
+                          (ouch-read-buffer nextchar)))))
+                (t
+                 (push *ouch-ptr* escapes)
+                 (ouch-read-buffer ch))))))
+          (t
+           (when (and (constituentp char)
+                      (eql (get-constituent-trait char)
+                           +char-attr-package-delimiter+)
+                      (not colon))
+             (setq colon *ouch-ptr*))
+           (ouch-read-buffer char))))))
 \f
 ;;;; 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+
-        att)))
+     (cond
+       ((<= att +char-attr-terminating-macro+) +char-attr-delimiter+)
+       ((< att +char-attr-constituent+) att)
+       (t (setf att (get-constituent-trait ,char))
+          (if (= att +char-attr-invalid+)
+              (simple-reader-error stream "invalid constituent")
+              att)))))
 
 ;;; 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+
-        (if (digit-char-p ,char *read-base*)
-            +char-attr-constituent-digit+
-            (if (= att +char-attr-constituent-digit+)
-                +char-attr-constituent+
-                att)))))
+     (cond
+       ((<= att +char-attr-terminating-macro+) +char-attr-delimiter+)
+       ((< att +char-attr-constituent+) att)
+       (t (setf att (get-constituent-trait ,char))
+          (cond
+            ((digit-char-p ,char *read-base*) +char-attr-constituent-digit+)
+            ((= att +char-attr-constituent-digit+) +char-attr-constituent+)
+            ((= att +char-attr-invalid+)
+             (simple-reader-error stream "invalid constituent"))
+            (t att))))))
 
 ;;; 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
-              (or (digit-char-p ,char *read-base*)
-                  (= att +char-attr-constituent-slash+))))
-     (if possibly-float
-        (setq possibly-float
-              (or (digit-char-p ,char 10)
-                  (= att +char-attr-constituent-dot+))))
-     (if (<= att +char-attr-terminating-macro+)
-        +char-attr-delimiter+
-        (if (digit-char-p ,char (max *read-base* 10))
-            (if (digit-char-p ,char *read-base*)
-                (if (= att +char-attr-constituent-expt+)
-                    +char-attr-constituent-digit-or-expt+
-                    +char-attr-constituent-digit+)
-                +char-attr-constituent-decimal-digit+)
-            att))))
+     (cond
+       ((<= att +char-attr-terminating-macro+) +char-attr-delimiter+)
+       ((< att +char-attr-constituent+) att)
+       (t (setf att (get-constituent-trait ,char))
+          (when possibly-rational
+            (setq possibly-rational
+                  (or (digit-char-p ,char *read-base*)
+                      (= att +char-attr-constituent-slash+))))
+          (when possibly-float
+            (setq possibly-float
+                  (or (digit-char-p ,char 10)
+                      (= att +char-attr-constituent-dot+))))
+          (cond
+            ((digit-char-p ,char (max *read-base* 10))
+             (if (digit-char-p ,char *read-base*)
+                 (if (= att +char-attr-constituent-expt+)
+                     +char-attr-constituent-digit-or-expt+
+                     +char-attr-constituent-digit+)
+                 +char-attr-constituent-decimal-digit+))
+            ((= att +char-attr-invalid+)
+             (simple-reader-error stream "invalid constituent"))
+            (t att))))))
 \f
 ;;;; token fetching
 
   (let ((case (readtable-case *readtable*)))
     (cond
      ((and (null escapes) (eq case :upcase))
-      (dotimes (i *ouch-ptr*)
-       (setf (schar *read-buffer* i)
-             (char-upcase (schar *read-buffer* i)))))
+      ;; Pull the special variable access out of the loop.
+      (let ((buffer *read-buffer*))
+        (dotimes (i *ouch-ptr*)
+          (declare (optimize (sb!c::insert-array-bounds-checks 0)))
+          (setf (schar buffer i) (char-upcase (schar buffer i))))))
      ((eq case :preserve))
      (t
       (macrolet ((skip-esc (&body body)
-                  `(do ((i (1- *ouch-ptr*) (1- i))
-                        (escapes escapes))
-                       ((minusp i))
-                     (declare (fixnum i))
-                     (when (or (null escapes)
-                               (let ((esc (first escapes)))
-                                 (declare (fixnum esc))
-                                 (cond ((< esc i) t)
-                                       (t
-                                        (aver (= esc i))
-                                        (pop escapes)
-                                        nil))))
-                       (let ((ch (schar *read-buffer* i)))
-                         ,@body)))))
-       (flet ((lower-em ()
-                (skip-esc (setf (schar *read-buffer* i) (char-downcase ch))))
-              (raise-em ()
-                (skip-esc (setf (schar *read-buffer* i) (char-upcase ch)))))
-         (ecase case
-           (:upcase (raise-em))
-           (:downcase (lower-em))
-           (:invert
-            (let ((all-upper t)
-                  (all-lower t))
-              (skip-esc
-                (when (both-case-p ch)
-                  (if (upper-case-p ch)
-                      (setq all-lower nil)
-                      (setq all-upper nil))))
-              (cond (all-lower (raise-em))
-                    (all-upper (lower-em))))))))))))
+                   `(do ((i (1- *ouch-ptr*) (1- i))
+                         (buffer *read-buffer*)
+                         (escapes escapes))
+                        ((minusp i))
+                      (declare (fixnum i)
+                               (optimize (sb!c::insert-array-bounds-checks 0)))
+                      (when (or (null escapes)
+                                (let ((esc (first escapes)))
+                                  (declare (fixnum esc))
+                                  (cond ((< esc i) t)
+                                        (t
+                                         (aver (= esc i))
+                                         (pop escapes)
+                                         nil))))
+                        (let ((ch (schar buffer i)))
+                          ,@body)))))
+        (flet ((lower-em ()
+                 (skip-esc (setf (schar buffer i) (char-downcase ch))))
+               (raise-em ()
+                 (skip-esc (setf (schar buffer i) (char-upcase ch)))))
+          (ecase case
+            (:upcase (raise-em))
+            (:downcase (lower-em))
+            (:invert
+             (let ((all-upper t)
+                   (all-lower t))
+               (skip-esc
+                 (when (both-case-p ch)
+                   (if (upper-case-p ch)
+                       (setq all-lower nil)
+                       (setq all-upper nil))))
+               (cond (all-lower (raise-em))
+                     (all-upper (lower-em))))))))))))
+
+(defvar *reader-package* nil)
 
 (defun read-token (stream firstchar)
   #!+sb-doc
-  "This function is just an fsm that recognizes numbers and symbols."
+  "Default readmacro function. Handles numbers, symbols, and SBCL's
+extended <package-name>::<form-in-package> syntax."
   ;; Check explicitly whether FIRSTCHAR has an entry for
   ;; NON-TERMINATING in CHARACTER-ATTRIBUTE-TABLE and
   ;; READ-DOT-NUMBER-SYMBOL in CMT. Report an error if these are
   (when *read-suppress*
     (internal-read-extended-token stream firstchar nil)
     (return-from read-token nil))
-  (let ((attribute-table (character-attribute-table *readtable*))
-       (package-designator nil)
-       (colons 0)
-       (possibly-rational t)
-       (seen-digit-or-expt nil)
-       (possibly-float t)
-       (was-possibly-float nil)
-       (escapes ())
-       (seen-multiple-escapes nil))
+  (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)
+        (was-possibly-float nil)
+        (escapes ())
+        (seen-multiple-escapes nil))
     (reset-read-buffer)
     (prog ((char firstchar))
-      (case (char-class3 char attribute-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))
-       (#.+char-attr-multiple-escape+ (go MULT-ESCAPE))
-       ;; can't have eof, whitespace, or terminating macro as first char!
-       (t (go SYMBOL)))
+      (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-single-escape+ (go SINGLE-ESCAPE))
+        (#.+char-attr-package-delimiter+ (go COLON))
+        (#.+char-attr-multiple-escape+ (go MULT-ESCAPE))
+        (#.+char-attr-invalid+ (simple-reader-error stream
+                                                    "invalid constituent"))
+        ;; can't have eof, whitespace, or terminating macro as first char!
+        (t (go SYMBOL)))
      SIGN ; saw "sign"
       (ouch-read-buffer char)
       (setq char (read-char stream nil nil))
       (unless char (go RETURN-SYMBOL))
       (setq possibly-rational t
-           possibly-float t)
-      (case (char-class3 char attribute-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))
-       (#.+char-attr-multiple-escape+ (go MULT-ESCAPE))        
-       (#.+char-attr-delimiter+ (unread-char char stream) (go RETURN-SYMBOL))
-       (t (go SYMBOL)))
+            possibly-float t)
+      (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-single-escape+ (go SINGLE-ESCAPE))
+        (#.+char-attr-package-delimiter+ (go COLON))
+        (#.+char-attr-multiple-escape+ (go MULT-ESCAPE))
+        (#.+char-attr-delimiter+ (unread-char char stream) (go RETURN-SYMBOL))
+        (t (go SYMBOL)))
      LEFTDIGIT ; saw "[sign] {digit}+"
       (ouch-read-buffer char)
       (setq char (read-char stream nil nil))
       (unless char (return (make-integer)))
       (setq was-possibly-float possibly-float)
-      (case (char-class3 char attribute-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-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)))
+      (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-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-single-escape+ (go SINGLE-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-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)))
-       (#.+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)))
+      (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)))
+        (#.+char-attr-delimiter+ (unread-char char stream)
+                                 (return (make-integer)))
+        (#.+char-attr-single-escape+ (go SINGLE-ESCAPE))
+        (#.+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-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)))
+      (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-single-escape+ (go SINGLE-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)
-       (#.+char-attr-constituent-digit+ (go RIGHTDIGIT))
-       (#.+char-attr-constituent-expt+ (go EXPONENT))
-       (#.+char-attr-delimiter+
-        (unread-char char stream)
-        (return (let ((*read-base* 10))
-                  (make-integer))))
-       (#.+char-attr-escape+ (go ESCAPE))
-       (#.+char-attr-multiple-escape+ (go MULT-ESCAPE))
-       (#.+char-attr-package-delimiter+ (go COLON))
-       (t (go SYMBOL)))
+                             (make-integer))))
+      (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 (let ((*read-base* 10))
+                   (make-integer))))
+        (#.+char-attr-single-escape+ (go SINGLE-ESCAPE))
+        (#.+char-attr-multiple-escape+ (go MULT-ESCAPE))
+        (#.+char-attr-package-delimiter+ (go COLON))
+        (t (go SYMBOL)))
      RIGHTDIGIT ; saw "[sign] {decimal-digit}* dot {digit}+"
       (ouch-read-buffer char)
       (setq char (read-char stream nil nil))
       (unless char (return (make-float stream)))
-      (case (char-class char attribute-table)
-       (#.+char-attr-constituent-digit+ (go RIGHTDIGIT))
-       (#.+char-attr-constituent-expt+ (go EXPONENT))
-       (#.+char-attr-delimiter+
-        (unread-char char stream)
-        (return (make-float stream)))
-       (#.+char-attr-escape+ (go ESCAPE))
-       (#.+char-attr-multiple-escape+ (go MULT-ESCAPE))
-       (#.+char-attr-package-delimiter+ (go COLON))
-       (t (go SYMBOL)))
+      (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 stream)))
+        (#.+char-attr-single-escape+ (go SINGLE-ESCAPE))
+        (#.+char-attr-multiple-escape+ (go MULT-ESCAPE))
+        (#.+char-attr-package-delimiter+ (go COLON))
+        (t (go SYMBOL)))
      SIGNDOT ; saw "[sign] dot"
       (ouch-read-buffer char)
       (setq char (read-char stream nil nil))
       (unless char (go RETURN-SYMBOL))
-      (case (char-class char attribute-table)
-       (#.+char-attr-constituent-digit+ (go RIGHTDIGIT))
-       (#.+char-attr-delimiter+ (unread-char char stream) (go RETURN-SYMBOL))
-       (#.+char-attr-escape+ (go ESCAPE))
-       (#.+char-attr-multiple-escape+ (go MULT-ESCAPE))
-       (t (go SYMBOL)))
+      (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-single-escape+ (go SINGLE-ESCAPE))
+        (#.+char-attr-multiple-escape+ (go MULT-ESCAPE))
+        (t (go SYMBOL)))
      FRONTDOT ; saw "dot"
       (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)
-       (#.+char-attr-constituent-digit+ (go RIGHTDIGIT))
-       (#.+char-attr-constituent-dot+ (go DOTS))
-       (#.+char-attr-delimiter+  (%reader-error stream "dot context error"))
-       (#.+char-attr-escape+ (go ESCAPE))
-       (#.+char-attr-multiple-escape+ (go MULT-ESCAPE))
-       (#.+char-attr-package-delimiter+ (go COLON))
-       (t (go SYMBOL)))
+      (unless char (simple-reader-error stream "dot context error"))
+      (case (char-class char attribute-array attribute-hash-table)
+        (#.+char-attr-constituent-digit+ (go RIGHTDIGIT))
+        (#.+char-attr-constituent-dot+ (go DOTS))
+        (#.+char-attr-delimiter+  (simple-reader-error stream
+                                                       "dot context error"))
+        (#.+char-attr-single-escape+ (go SINGLE-ESCAPE))
+        (#.+char-attr-multiple-escape+ (go MULT-ESCAPE))
+        (#.+char-attr-package-delimiter+ (go COLON))
+        (t (go SYMBOL)))
      EXPONENT
       (ouch-read-buffer char)
       (setq char (read-char stream nil nil))
       (unless char (go RETURN-SYMBOL))
       (setq possibly-float t)
-      (case (char-class char attribute-table)
-       (#.+char-attr-constituent-sign+ (go EXPTSIGN))
-       (#.+char-attr-constituent-digit+ (go EXPTDIGIT))
-       (#.+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)))
+      (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))
+        (#.+char-attr-single-escape+ (go SINGLE-ESCAPE))
+        (#.+char-attr-multiple-escape+ (go MULT-ESCAPE))
+        (#.+char-attr-package-delimiter+ (go COLON))
+        (t (go SYMBOL)))
      EXPTSIGN ; got to EXPONENT, and saw a sign character
       (ouch-read-buffer char)
       (setq char (read-char stream nil nil))
       (unless char (go RETURN-SYMBOL))
-      (case (char-class char attribute-table)
-       (#.+char-attr-constituent-digit+ (go EXPTDIGIT))
-       (#.+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)))
+      (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-single-escape+ (go SINGLE-ESCAPE))
+        (#.+char-attr-multiple-escape+ (go MULT-ESCAPE))
+        (#.+char-attr-package-delimiter+ (go COLON))
+        (t (go SYMBOL)))
      EXPTDIGIT ; got to EXPONENT, saw "[sign] {digit}+"
       (ouch-read-buffer char)
       (setq char (read-char stream nil nil))
       (unless char (return (make-float stream)))
-      (case (char-class char attribute-table)
-       (#.+char-attr-constituent-digit+ (go EXPTDIGIT))
-       (#.+char-attr-delimiter+
-        (unread-char char stream)
-        (return (make-float stream)))
-       (#.+char-attr-escape+ (go ESCAPE))
-       (#.+char-attr-multiple-escape+ (go MULT-ESCAPE))
-       (#.+char-attr-package-delimiter+ (go COLON))
-       (t (go SYMBOL)))
+      (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 stream)))
+        (#.+char-attr-single-escape+ (go SINGLE-ESCAPE))
+        (#.+char-attr-multiple-escape+ (go MULT-ESCAPE))
+        (#.+char-attr-package-delimiter+ (go COLON))
+        (t (go SYMBOL)))
      RATIO ; saw "[sign] {digit}+ slash"
       (ouch-read-buffer char)
       (setq char (read-char stream nil nil))
       (unless char (go RETURN-SYMBOL))
-      (case (char-class2 char attribute-table)
-       (#.+char-attr-constituent-digit+ (go RATIODIGIT))
-       (#.+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)))
+      (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-single-escape+ (go SINGLE-ESCAPE))
+        (#.+char-attr-multiple-escape+ (go MULT-ESCAPE))
+        (#.+char-attr-package-delimiter+ (go COLON))
+        (t (go SYMBOL)))
      RATIODIGIT ; saw "[sign] {digit}+ slash {digit}+"
       (ouch-read-buffer char)
       (setq char (read-char stream nil nil))
       (unless char (return (make-ratio stream)))
-      (case (char-class2 char attribute-table)
-       (#.+char-attr-constituent-digit+ (go RATIODIGIT))
-       (#.+char-attr-delimiter+
-        (unread-char char stream)
-        (return (make-ratio stream)))
-       (#.+char-attr-escape+ (go ESCAPE))
-       (#.+char-attr-multiple-escape+ (go MULT-ESCAPE))
-       (#.+char-attr-package-delimiter+ (go COLON))
-       (t (go SYMBOL)))
+      (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 stream)))
+        (#.+char-attr-single-escape+ (go SINGLE-ESCAPE))
+        (#.+char-attr-multiple-escape+ (go MULT-ESCAPE))
+        (#.+char-attr-package-delimiter+ (go COLON))
+        (t (go SYMBOL)))
      DOTS ; saw "dot {dot}+"
       (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)
-       (#.+char-attr-constituent-dot+ (go DOTS))
-       (#.+char-attr-delimiter+
-        (unread-char char stream)
-        (%reader-error stream "too many dots"))
-       (#.+char-attr-escape+ (go ESCAPE))
-       (#.+char-attr-multiple-escape+ (go MULT-ESCAPE))
-       (#.+char-attr-package-delimiter+ (go COLON))
-       (t (go SYMBOL)))
+      (unless char (simple-reader-error stream "too many dots"))
+      (case (char-class char attribute-array attribute-hash-table)
+        (#.+char-attr-constituent-dot+ (go DOTS))
+        (#.+char-attr-delimiter+
+         (unread-char char stream)
+         (simple-reader-error stream "too many dots"))
+        (#.+char-attr-single-escape+ (go SINGLE-ESCAPE))
+        (#.+char-attr-multiple-escape+ (go MULT-ESCAPE))
+        (#.+char-attr-package-delimiter+ (go COLON))
+        (t (go SYMBOL)))
      SYMBOL ; not a dot, dots, or number
       (let ((stream (in-synonym-of stream)))
-       (if (ansi-stream-p stream)
-           (prepare-for-fast-read-char stream
-             (prog ()
-              SYMBOL-LOOP
-              (ouch-read-buffer char)
-              (setq char (fast-read-char nil nil))
-              (unless char (go RETURN-SYMBOL))
-              (case (char-class char attribute-table)
-                (#.+char-attr-escape+ (done-with-fast-read-char)
-                                      (go ESCAPE))
-                (#.+char-attr-delimiter+ (done-with-fast-read-char)
-                                         (unread-char char stream)
-                                         (go RETURN-SYMBOL))
-                (#.+char-attr-multiple-escape+ (done-with-fast-read-char)
-                                               (go MULT-ESCAPE))
-                (#.+char-attr-package-delimiter+ (done-with-fast-read-char)
-                                                 (go COLON))
-                (t (go SYMBOL-LOOP)))))
-           ;; CLOS stream
-           (prog ()
-            SYMBOL-LOOP
-            (ouch-read-buffer char)
-            (setq char (read-char stream nil :eof))
-            (when (eq char :eof) (go RETURN-SYMBOL))
-            (case (char-class char attribute-table)
-              (#.+char-attr-escape+ (go ESCAPE))
-              (#.+char-attr-delimiter+ (unread-char char stream)
-                           (go RETURN-SYMBOL))
-              (#.+char-attr-multiple-escape+ (go MULT-ESCAPE))
-              (#.+char-attr-package-delimiter+ (go COLON))
-              (t (go SYMBOL-LOOP))))))
-     ESCAPE ; saw an escape
-      ;; Don't put the escape in the read buffer.
+        (if (ansi-stream-p stream)
+            (prepare-for-fast-read-char stream
+              (prog ()
+               SYMBOL-LOOP
+               (ouch-read-buffer char)
+               (setq char (fast-read-char nil nil))
+               (unless char (go RETURN-SYMBOL))
+               (case (char-class char attribute-array attribute-hash-table)
+                 (#.+char-attr-single-escape+ (done-with-fast-read-char)
+                                              (go SINGLE-ESCAPE))
+                 (#.+char-attr-delimiter+ (done-with-fast-read-char)
+                                          (unread-char char stream)
+                                          (go RETURN-SYMBOL))
+                 (#.+char-attr-multiple-escape+ (done-with-fast-read-char)
+                                                (go MULT-ESCAPE))
+                 (#.+char-attr-package-delimiter+ (done-with-fast-read-char)
+                                                  (go COLON))
+                 (t (go SYMBOL-LOOP)))))
+            ;; CLOS stream
+            (prog ()
+             SYMBOL-LOOP
+             (ouch-read-buffer char)
+             (setq char (read-char stream nil :eof))
+             (when (eq char :eof) (go RETURN-SYMBOL))
+             (case (char-class char attribute-array attribute-hash-table)
+               (#.+char-attr-single-escape+ (go SINGLE-ESCAPE))
+               (#.+char-attr-delimiter+ (unread-char char stream)
+                            (go RETURN-SYMBOL))
+               (#.+char-attr-multiple-escape+ (go MULT-ESCAPE))
+               (#.+char-attr-package-delimiter+ (go COLON))
+               (t (go SYMBOL-LOOP))))))
+     SINGLE-ESCAPE ; saw a single-escape
+      ;; Don't put the escape character in the read buffer.
       ;; READ-NEXT CHAR, put in buffer (no case conversion).
       (let ((nextchar (read-char stream nil nil)))
-       (unless nextchar
-         (reader-eof-error stream "after escape character"))
-       (push *ouch-ptr* escapes)
-       (ouch-read-buffer nextchar))
+        (unless nextchar
+          (reader-eof-error stream "after single-escape character"))
+        (push *ouch-ptr* escapes)
+        (ouch-read-buffer nextchar))
       (setq char (read-char stream nil nil))
       (unless char (go RETURN-SYMBOL))
-      (case (char-class char attribute-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)))
+      (case (char-class char attribute-array attribute-hash-table)
+        (#.+char-attr-delimiter+ (unread-char char stream) (go RETURN-SYMBOL))
+        (#.+char-attr-single-escape+ (go SINGLE-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)))
-       (push *ouch-ptr* escapes)
-       (ouch-read-buffer char))
+          ((multiple-escape-p char))
+        (if (single-escape-p char) (setq char (read-char stream t)))
+        (push *ouch-ptr* escapes)
+        (ouch-read-buffer char))
       (setq char (read-char stream nil nil))
       (unless char (go RETURN-SYMBOL))
-      (case (char-class char attribute-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)))
+      (case (char-class char attribute-array attribute-hash-table)
+        (#.+char-attr-delimiter+ (unread-char char stream) (go RETURN-SYMBOL))
+        (#.+char-attr-single-escape+ (go SINGLE-ESCAPE))
+        (#.+char-attr-multiple-escape+ (go MULT-ESCAPE))
+        (#.+char-attr-package-delimiter+ (go COLON))
+        (t (go SYMBOL)))
       COLON
       (casify-read-buffer escapes)
       (unless (zerop colons)
-       (%reader-error stream "too many colons in ~S"
-                     (read-buffer-to-string)))
+        (simple-reader-error stream
+                             "too many colons in ~S"
+                             (read-buffer-to-string)))
       (setq colons 1)
       (setq package-designator
-           (if (plusp *ouch-ptr*)
-               ;; FIXME: It seems inefficient to cons up a package
-               ;; designator string every time we read a symbol with an
-               ;; explicit package prefix. Perhaps we could implement
-               ;; a FIND-PACKAGE* function analogous to INTERN*
-               ;; and friends?
-               (read-buffer-to-string)
-               (if seen-multiple-escapes
-                   (read-buffer-to-string)
-                   *keyword-package*)))
+            (if (plusp *ouch-ptr*)
+                ;; FIXME: It seems inefficient to cons up a package
+                ;; designator string every time we read a symbol with an
+                ;; explicit package prefix. Perhaps we could implement
+                ;; a FIND-PACKAGE* function analogous to INTERN*
+                ;; and friends?
+                (read-buffer-to-string)
+                (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)
-       (#.+char-attr-delimiter+
-        (unread-char char stream)
-        (%reader-error stream
-                       "illegal terminating character after a colon: ~S"
-                       char))
-       (#.+char-attr-escape+ (go ESCAPE))
-       (#.+char-attr-multiple-escape+ (go MULT-ESCAPE))
-       (#.+char-attr-package-delimiter+ (go INTERN))
-       (t (go SYMBOL)))
+      (case (char-class char attribute-array attribute-hash-table)
+        (#.+char-attr-delimiter+
+         (unread-char char stream)
+         (simple-reader-error stream
+                              "illegal terminating character after a colon: ~S"
+                              char))
+        (#.+char-attr-single-escape+ (go SINGLE-ESCAPE))
+        (#.+char-attr-multiple-escape+ (go MULT-ESCAPE))
+        (#.+char-attr-package-delimiter+ (go INTERN))
+        (t (go SYMBOL)))
       INTERN
       (setq colons 2)
       (setq char (read-char stream nil nil))
       (unless char
-       (reader-eof-error stream "after reading a colon"))
-      (case (char-class char attribute-table)
-       (#.+char-attr-delimiter+
-        (unread-char char stream)
-        (%reader-error stream
-                       "illegal terminating character after a colon: ~S"
-                       char))
-       (#.+char-attr-escape+ (go ESCAPE))
-       (#.+char-attr-multiple-escape+ (go MULT-ESCAPE))
-       (#.+char-attr-package-delimiter+
-        (%reader-error stream
-                       "too many colons after ~S name"
-                       package-designator))
-       (t (go SYMBOL)))
+        (reader-eof-error stream "after reading a colon"))
+      (case (char-class char attribute-array attribute-hash-table)
+        (#.+char-attr-delimiter+
+         (unread-char char stream)
+         (if package-designator
+             (let* ((*reader-package* (%find-package-or-lose package-designator)))
+               (return (read stream t nil t)))
+             (simple-reader-error stream
+                                  "illegal terminating character after a double-colon: ~S"
+                                  char)))
+        (#.+char-attr-single-escape+ (go SINGLE-ESCAPE))
+        (#.+char-attr-multiple-escape+ (go MULT-ESCAPE))
+        (#.+char-attr-package-delimiter+
+         (simple-reader-error stream
+                              "too many colons after ~S name"
+                              package-designator))
+        (t (go SYMBOL)))
       RETURN-SYMBOL
       (casify-read-buffer escapes)
       (let ((found (if package-designator
-                      (find-package package-designator)
-                      (sane-package))))
-       (unless found
-         (error 'reader-package-error :stream stream
-                :format-arguments (list package-designator)
-                :format-control "package ~S not found"))
-
-       (if (or (zerop colons) (= colons 2) (eq found *keyword-package*))
-           (return (intern* *read-buffer* *ouch-ptr* found))
-           (multiple-value-bind (symbol test)
-               (find-symbol* *read-buffer* *ouch-ptr* found)
-             (when (eq test :external) (return symbol))
-             (let ((name (read-buffer-to-string)))
-               (with-simple-restart (continue "Use symbol anyway.")
-                 (error 'reader-package-error :stream stream
-                        :format-arguments (list name (package-name found))
-                        :format-control
-                        (if test
-                            "The symbol ~S is not external in the ~A package."
-                            "Symbol ~S not found in the ~A package.")))
-               (return (intern name found)))))))))
+                       (or (find-package package-designator)
+                           (error 'simple-reader-package-error
+                                  :package package-designator
+                                  :stream stream
+                                  :format-control "Package ~A does not exist."
+                                  :format-arguments (list package-designator)))
+                       (or *reader-package* (sane-package)))))
+        (if (or (zerop colons) (= colons 2) (eq found *keyword-package*))
+            (return (intern* *read-buffer* *ouch-ptr* found))
+            (multiple-value-bind (symbol test)
+                (find-symbol* *read-buffer* *ouch-ptr* found)
+              (when (eq test :external) (return symbol))
+              (let ((name (read-buffer-to-string)))
+                (with-simple-restart (continue "Use symbol anyway.")
+                  (error 'simple-reader-package-error
+                         :package found
+                         :stream stream
+                         :format-arguments (list name (package-name found))
+                         :format-control
+                         (if test
+                             "The symbol ~S is not external in the ~A package."
+                             "Symbol ~S not found in the ~A package.")))
+                (return (intern name found)))))))))
 
 ;;; for semi-external use:
 ;;;
 (defun read-extended-token (stream &optional (*readtable* *readtable*))
   (let ((first-char (read-char stream nil nil t)))
     (cond (first-char
-          (multiple-value-bind (escapes colon)
+           (multiple-value-bind (escapes colon)
                (internal-read-extended-token stream first-char nil)
-            (casify-read-buffer escapes)
-            (values (read-buffer-to-string) (not (null escapes)) colon)))
-         (t
-          (values "" nil nil)))))
+             (casify-read-buffer escapes)
+             (values (read-buffer-to-string) (not (null escapes)) colon)))
+          (t
+           (values "" nil nil)))))
 
 ;;; for semi-external use:
 ;;;
   #!+sb-doc
   "the largest fixnum power of the base for MAKE-INTEGER")
 (declaim (simple-vector *integer-reader-safe-digits*
-                       *integer-reader-base-power*))
+                        *integer-reader-base-power*))
 #|
 (defun !cold-init-integer-reader ()
   (do ((base 2 (1+ base)))
       ((> base 36))
     (let ((digits
-         (do ((fix (truncate most-positive-fixnum base)
-                   (truncate fix base))
-              (digits 0 (1+ digits)))
-             ((zerop fix) digits))))
+          (do ((fix (truncate most-positive-fixnum base)
+                    (truncate fix base))
+               (digits 0 (1+ digits)))
+              ((zerop fix) digits))))
       (setf (aref *integer-reader-safe-digits* base)
-           digits
-           (aref *integer-reader-base-power* base)
-           (expt base digits)))))
+            digits
+            (aref *integer-reader-base-power* base)
+            (expt base digits)))))
 |#
 
 (defun make-integer ()
   "Minimizes bignum-fixnum multiplies by reading a 'safe' number of digits,
   then multiplying by a power of the base and adding."
   (let* ((base *read-base*)
-        (digits-per (aref *integer-reader-safe-digits* base))
-        (base-power (aref *integer-reader-base-power* base))
-        (negativep nil)
-        (number 0))
+         (digits-per (aref *integer-reader-safe-digits* base))
+         (base-power (aref *integer-reader-base-power* base))
+         (negativep nil)
+         (number 0))
     (declare (type index digits-per base-power))
     (read-unwind-read-buffer)
     (let ((char (inch-read-buffer)))
       (cond ((char= char #\-)
-            (setq negativep t))
-           ((char= char #\+))
-           (t (unread-buffer))))
+             (setq negativep t))
+            ((char= char #\+))
+            (t (unread-buffer))))
     (loop
      (let ((num 0))
        (declare (type index num))
        (dotimes (digit digits-per)
-        (let* ((ch (inch-read-buffer)))
-          (cond ((or (eofp ch) (char= ch #\.))
-                 (return-from make-integer
-                              (let ((res
-                                     (if (zerop number) num
-                                         (+ num (* number
-                                                   (expt base digit))))))
-                                (if negativep (- res) res))))
-                (t (setq num (+ (digit-char-p ch base)
-                                (the index (* num base))))))))
+         (let* ((ch (inch-read-buffer)))
+           (cond ((or (eofp ch) (char= ch #\.))
+                  (return-from make-integer
+                               (let ((res
+                                      (if (zerop number) num
+                                          (+ num (* number
+                                                    (expt base digit))))))
+                                 (if negativep (- res) res))))
+                 (t (setq num (+ (digit-char-p ch base)
+                                 (the index (* num base))))))))
        (setq number (+ num (* number base-power)))))))
 
+(defun truncate-exponent (exponent number divisor)
+  "Truncate exponent if it's too large for a float"
+  ;; Work with base-2 logarithms to avoid conversions to floats,
+  ;; and convert to base-10 conservatively at the end.
+  ;; Use the least positive float, because denormalized exponent
+  ;; can be larger than normalized.
+  (let* ((max-exponent
+          #!-long-float
+          (+ sb!vm:double-float-digits sb!vm:double-float-bias))
+         (number-magnitude (integer-length number))
+         (divisor-magnitude (1- (integer-length divisor)))
+         (magnitude (- number-magnitude divisor-magnitude)))
+    (if (minusp exponent)
+        (max exponent (ceiling (- (+ max-exponent magnitude))
+                               #.(floor (log 10 2))))
+        (min exponent (floor (- max-exponent magnitude)
+                             #.(floor (log 10 2)))))))
+
 (defun make-float (stream)
   ;; Assume that the contents of *read-buffer* are a legal float, with nothing
   ;; else after it.
   (read-unwind-read-buffer)
   (let ((negative-fraction nil)
-       (number 0)
-       (divisor 1)
-       (negative-exponent nil)
-       (exponent 0)
-       (float-char ())
-       (char (inch-read-buffer)))
+        (number 0)
+        (divisor 1)
+        (negative-exponent nil)
+        (exponent 0)
+        (float-char ())
+        (char (inch-read-buffer)))
     (if (cond ((char= char #\+) t)
-             ((char= char #\-) (setq negative-fraction t)))
-       ;; Flush it.
-       (setq char (inch-read-buffer)))
+              ((char= char #\-) (setq negative-fraction t)))
+        ;; Flush it.
+        (setq char (inch-read-buffer)))
     ;; Read digits before the dot.
     (do* ((ch char (inch-read-buffer))
-         (dig (digit-char-p ch) (digit-char-p ch)))
-        ((not dig) (setq char ch))
+          (dig (digit-char-p ch) (digit-char-p ch)))
+         ((not dig) (setq char ch))
       (setq number (+ (* number 10) dig)))
     ;; Deal with the dot, if it's there.
     (when (char= char #\.)
       (setq char (inch-read-buffer))
       ;; Read digits after the dot.
       (do* ((ch char (inch-read-buffer))
-           (dig (and (not (eofp ch)) (digit-char-p ch))
-                (and (not (eofp ch)) (digit-char-p ch))))
-          ((not dig) (setq char ch))
-       (setq divisor (* divisor 10))
-       (setq number (+ (* number 10) dig))))
+            (dig (and (not (eofp ch)) (digit-char-p ch))
+                 (and (not (eofp ch)) (digit-char-p ch))))
+           ((not dig) (setq char ch))
+        (setq divisor (* divisor 10))
+        (setq number (+ (* number 10) dig))))
     ;; Is there an exponent letter?
     (cond ((eofp char)
-          ;; If not, we've read the whole number.
-          (let ((num (make-float-aux number divisor
-                                     *read-default-float-format*
-                                     stream)))
-            (return-from make-float (if negative-fraction (- num) num))))
-         ((exponent-letterp char)
-          (setq float-char char)
-          ;; Build exponent.
-          (setq char (inch-read-buffer))
-          ;; Check leading sign.
-          (if (cond ((char= char #\+) t)
-                    ((char= char #\-) (setq negative-exponent t)))
-              ;; Flush sign.
-              (setq char (inch-read-buffer)))
-          ;; Read digits for exponent.
-          (do* ((ch char (inch-read-buffer))
-                (dig (and (not (eofp ch)) (digit-char-p ch))
-                     (and (not (eofp ch)) (digit-char-p ch))))
-               ((not dig)
-                (setq exponent (if negative-exponent (- exponent) exponent)))
-            (setq exponent (+ (* exponent 10) dig)))
-          ;; 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)))
-                 (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")))))
+           ;; If not, we've read the whole number.
+           (let ((num (make-float-aux number divisor
+                                      *read-default-float-format*
+                                      stream)))
+             (return-from make-float (if negative-fraction (- num) num))))
+          ((exponent-letterp char)
+           (setq float-char char)
+           ;; Build exponent.
+           (setq char (inch-read-buffer))
+           ;; Check leading sign.
+           (if (cond ((char= char #\+) t)
+                     ((char= char #\-) (setq negative-exponent t)))
+               ;; Flush sign.
+               (setq char (inch-read-buffer)))
+           ;; Read digits for exponent.
+           (do* ((ch char (inch-read-buffer))
+                 (dig (and (not (eofp ch)) (digit-char-p ch))
+                      (and (not (eofp ch)) (digit-char-p ch))))
+                ((not dig)
+                 (setq exponent (if negative-exponent (- exponent) exponent)))
+             (setq exponent (+ (* exponent 10) dig)))
+           ;; 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)))
+                  (exponent (truncate-exponent exponent number divisor))
+                  (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 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"))))
+             :error c :stream stream
+             :format-control "failed to build float from ~a"
+             :format-arguments (list (read-buffer-to-string))))))
 
 (defun make-ratio (stream)
   ;; Assume *READ-BUFFER* contains a legal ratio. Build the number from
     (read-unwind-read-buffer)
     (setq char (inch-read-buffer))
     (cond ((char= char #\+)
-          (setq char (inch-read-buffer)))
-         ((char= char #\-)
-          (setq char (inch-read-buffer))
-          (setq negative-number t)))
+           (setq char (inch-read-buffer)))
+          ((char= char #\-)
+           (setq char (inch-read-buffer))
+           (setq negative-number t)))
     ;; Get numerator.
     (do* ((ch char (inch-read-buffer))
-         (dig (digit-char-p ch *read-base*)
-              (digit-char-p ch *read-base*)))
-        ((not dig))
-        (setq numerator (+ (* numerator *read-base*) dig)))
+          (dig (digit-char-p ch *read-base*)
+               (digit-char-p ch *read-base*)))
+         ((not dig))
+         (setq numerator (+ (* numerator *read-base*) dig)))
     ;; Get denominator.
     (do* ((ch (inch-read-buffer) (inch-read-buffer))
-         (dig ()))
-        ((or (eofp ch) (not (setq dig (digit-char-p ch *read-base*)))))
-        (setq denominator (+ (* denominator *read-base*) dig)))
+          (dig ()))
+         ((or (eofp ch) (not (setq dig (digit-char-p ch *read-base*)))))
+         (setq denominator (+ (* denominator *read-base*) dig)))
     (let ((num (handler-case
-                  (/ numerator denominator)
-                (arithmetic-error (c)
-                  (error 'reader-impossible-number-error
-                         :error c :stream stream
-                         :format-control "failed to build ratio")))))
+                   (/ 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))))
 \f
-;;;; cruft for dispatch macros
-
-(defun make-char-dispatch-table ()
-  (make-array char-code-limit :initial-element #'dispatch-char-error))
+;;;; General reader for dispatch macros
 
 (defun dispatch-char-error (stream sub-char ignore)
   (declare (ignore ignore))
   (if *read-suppress*
       (values)
-      (%reader-error stream "no dispatch function defined for ~S" sub-char)))
-
-(defun make-dispatch-macro-character (char &optional
-                                          (non-terminating-p nil)
-                                          (rt *readtable*))
-  #!+sb-doc
-  "Cause CHAR to become a dispatching macro character in readtable (which
-   defaults to the current readtable). If NON-TERMINATING-P, the char will
-   be non-terminating."
-  (set-macro-character char #'read-dispatch-char non-terminating-p rt)
-  (let* ((dalist (dispatch-tables rt))
-        (dtable (cdr (find char dalist :test #'char= :key #'car))))
-    (cond (dtable
-          (error "The dispatch character ~S already exists." char))
-         (t
-          (setf (dispatch-tables rt)
-                (push (cons char (make-char-dispatch-table)) dalist)))))
-  t)
-
-(defun set-dispatch-macro-character (disp-char sub-char function
-                                               &optional (rt *readtable*))
-  #!+sb-doc
-  "Cause FUNCTION to be called whenever the reader reads DISP-CHAR
-   followed by SUB-CHAR."
-  ;; Get the dispatch char for macro (error if not there), diddle
-  ;; entry for sub-char.
-  (when (digit-char-p sub-char)
-    (error "SUB-CHAR must not be a decimal digit: ~S" 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
-       (setf (elt (the simple-vector (cdr dpair))
-                  (char-code sub-char))
-             (coerce function 'function))
-       (error "~S is not a dispatch char." disp-char))))
-
-(defun get-dispatch-macro-character (disp-char sub-char
-                                     &optional (rt *readtable*))
-  #!+sb-doc
-  "Return the macro character function for SUB-CHAR under DISP-CHAR
-   or NIL if there is no associated function."
-  (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
-        (let ((dispatch-fun (elt (the simple-vector (cdr dpair))
-                                 (char-code sub-char))))
-         ;; Digits are also initialized in a dispatch table to
-         ;; #'dispatch-char-error; READ-DISPATCH-CHAR handles them
-         ;; separately. - CSR, 2002-04-12
-          (if (eq dispatch-fun #'dispatch-char-error)
-              nil
-              dispatch-fun))
-        (error "~S is not a dispatch char." disp-char))))
+      (simple-reader-error stream
+                           "no dispatch function defined for ~S"
+                           sub-char)))
 
 (defun read-dispatch-char (stream char)
   ;; Read some digits.
   (let ((numargp nil)
-       (numarg 0)
-       (sub-char ()))
+        (numarg 0)
+        (sub-char ()))
     (do* ((ch (read-char stream nil *eof-object*)
-             (read-char stream nil *eof-object*))
-         (dig ()))
-        ((or (eofp ch)
-             (not (setq dig (digit-char-p ch))))
-         ;; Take care of the extra char.
-         (if (eofp ch)
-             (reader-eof-error stream "inside dispatch character")
-             (setq sub-char (char-upcase ch))))
+              (read-char stream nil *eof-object*))
+          (dig ()))
+         ((or (eofp ch)
+              (not (setq dig (digit-char-p ch))))
+          ;; Take care of the extra char.
+          (if (eofp ch)
+              (reader-eof-error stream "inside dispatch character")
+              (setq sub-char (char-upcase ch))))
       (setq numargp t)
       (setq numarg (+ (* numarg 10) dig)))
     ;; Look up the function and call it.
     (let ((dpair (find char (dispatch-tables *readtable*)
-                      :test #'char= :key #'car)))
+                       :test #'char= :key #'car)))
       (if dpair
-         (funcall (the function
-                       (elt (the simple-vector (cdr dpair))
-                            (char-code sub-char)))
-                  stream sub-char (if numargp numarg nil))
-         (%reader-error stream "no dispatch table for dispatch char")))))
+          (funcall (the function
+                     (gethash sub-char (cdr dpair) #'dispatch-char-error))
+                   stream sub-char (if numargp numarg nil))
+          (simple-reader-error stream
+                               "no dispatch table for dispatch char")))))
 \f
 ;;;; READ-FROM-STRING
 
-;;; FIXME: Is it really worth keeping this pool?
-(defvar *read-from-string-spares* ()
-  #!+sb-doc
-  "A resource of string streams for Read-From-String.")
-
-(defun read-from-string (string &optional eof-error-p eof-value
-                               &key (start 0) end
-                               preserve-whitespace)
+(defun maybe-note-read-from-string-signature-issue (eof-error-p)
+  ;; The interface is so unintuitive that we explicitly check for the common
+  ;; error.
+  (when (member eof-error-p '(:start :end :preserve-whitespace))
+    (style-warn "~@<~S as EOF-ERROR-P argument to ~S: probable error. ~
+               Two optional arguments must be provided before the ~
+               first keyword argument.~:@>"
+                eof-error-p 'read-from-string)
+    t))
+
+(declaim (ftype (sfunction (string t t index (or null index) t) (values t index))
+                %read-from-string))
+(defun %read-from-string (string eof-error-p eof-value start end preserve-whitespace)
+  (with-array-data ((string string :offset-var offset)
+                    (start start)
+                    (end end)
+                    :check-fill-pointer t)
+    (let ((stream (make-string-input-stream string start end)))
+      (values (if preserve-whitespace
+                  (%read-preserving-whitespace stream eof-error-p eof-value nil)
+                  (read stream eof-error-p eof-value))
+              (- (string-input-stream-current stream) offset)))))
+
+(defun read-from-string (string &optional (eof-error-p t) eof-value
+                                &key (start 0) end preserve-whitespace)
   #!+sb-doc
   "The characters of string are successively given to the lisp reader
    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 (%check-vector-sequence-bounds string start end)))
-    (unless *read-from-string-spares*
-      (push (internal-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-current stream) start)
-      (setf (string-input-stream-end stream) end)
-      (unwind-protect
-         (values (if preserve-whitespace
-                     (read-preserving-whitespace stream eof-error-p eof-value)
-                     (read stream eof-error-p eof-value))
-                 (string-input-stream-current stream))
-       (push stream *read-from-string-spares*)))))
+  (maybe-note-read-from-string-signature-issue eof-error-p)
+  (%read-from-string string eof-error-p eof-value start end preserve-whitespace))
+
+(define-compiler-macro read-from-string (&whole form string &rest args)
+  ;; Check this at compile-time, and rewrite it so we're silent at runtime.
+  (destructuring-bind (&optional (eof-error-p t) eof-value &rest keys)
+      args
+    (cond ((maybe-note-read-from-string-signature-issue eof-error-p)
+           `(read-from-string ,string t ,eof-value ,@keys))
+          (t
+           (let* ((start (gensym "START"))
+                  (end (gensym "END"))
+                  (preserve-whitespace (gensym "PRESERVE-WHITESPACE"))
+                  bind seen ignore)
+             (do ()
+                 ((not (cdr keys))
+                  ;; Odd number of keys, punt.
+                  (when keys (return-from read-from-string form)))
+               (let* ((key (pop keys))
+                      (value (pop keys))
+                      (var (case key
+                             (:start start)
+                             (:end end)
+                             (:preserve-whitespace preserve-whitespace)
+                             (otherwise
+                              (return-from read-from-string form)))))
+                 (when (member key seen)
+                   (setf var (gensym "IGNORE"))
+                   (push var ignore))
+                 (push key seen)
+                 (push (list var value) bind)))
+             (dolist (default (list (list start 0)
+                                    (list end nil)
+                                    (list preserve-whitespace nil)))
+               (unless (assoc (car default) bind)
+                 (push default bind)))
+             (once-only ((string string))
+               `(let ,(nreverse bind)
+                  ,@(when ignore `((declare (ignore ,@ignore))))
+                  (%read-from-string ,string ,eof-error-p ,eof-value
+                                     ,start ,end ,preserve-whitespace))))))))
 \f
 ;;;; PARSE-INTEGER
 
   whitespace characters and then tries to parse an integer. The
   radix parameter must be between 2 and 36."
   (macrolet ((parse-error (format-control)
-              `(error 'simple-parse-error
-                      :format-control ,format-control
-                      :format-arguments (list string))))
+               `(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)))
+                      (start start)
+                      (end end)
+                      :check-fill-pointer t)
       (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)
+                       ((= 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 (whitespace[1]p (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))
+                 ((whitespace[1]p 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))))))
+                   (unless (whitespace[1]p (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))))))
 \f
 ;;;; reader initialization code
 
 (defun !reader-cold-init ()
-  (!cold-init-read-buffer)
-  (!cold-init-secondary-attribute-table)
+  (!cold-init-constituent-trait-table)
   (!cold-init-standard-readtable)
   ;; FIXME: This was commented out, but should probably be restored.
   #+nil (!cold-init-integer-reader))