diff options
author | Matthias Andreas Benkard <matthias@benkard.de> | 2008-07-31 09:33:25 +0200 |
---|---|---|
committer | Matthias Andreas Benkard <matthias@benkard.de> | 2008-07-31 09:33:25 +0200 |
commit | 0f383318a079bd0c7bb23c909f30771b1c20b29c (patch) | |
tree | bc4e2e9a4d5670c4d2dd3886637d11f7f4d5581c /Sacla/reader.lisp | |
parent | 563dd3a5963fb34903e2e209833d66a19e691d96 (diff) |
Add Sacla to the repository.
Diffstat (limited to 'Sacla/reader.lisp')
-rw-r--r-- | Sacla/reader.lisp | 797 |
1 files changed, 797 insertions, 0 deletions
diff --git a/Sacla/reader.lisp b/Sacla/reader.lisp new file mode 100644 index 0000000..2c87418 --- /dev/null +++ b/Sacla/reader.lisp @@ -0,0 +1,797 @@ +;; Copyright (C) 2002-2004, Yuji Minejima <ggb01164@nifty.ne.jp> +;; ALL RIGHTS RESERVED. +;; +;; $Id: reader.lisp,v 1.13 2004/07/22 06:06:33 yuji Exp $ +;; +;; Redistribution and use in source and binary forms, with or without +;; modification, are permitted provided that the following conditions +;; are met: +;; +;; * Redistributions of source code must retain the above copyright +;; notice, this list of conditions and the following disclaimer. +;; * Redistributions in binary form must reproduce the above copyright +;; notice, this list of conditions and the following disclaimer in +;; the documentation and/or other materials provided with the +;; distribution. +;; +;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +;; "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +;; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +;; A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +;; OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +;; SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +;; LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +;; OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +(defstruct (readtable (:predicate readtablep) (:copier nil)) + "Map characters into syntax types and reader macro functions." + (syntax (make-hash-table) :type hash-table) + (case :upcase :type (member :upcase :downcase :preserve :invert))) + +(defvar *read-base* '10 + "The radix in which integers and ratios are to be read by the Lisp reader.") +(defvar *read-default-float-format* 'single-float + "Used for a floating-point number having no exponent marker or e or E.") +(defvar *read-eval* 't + "If true, the #. reader macro has its normal effect. If false, reader-error.") +(defvar *read-suppress* 'nil + "If true, the reader reads an object and returns a primary value of nil.") +(defvar *readtable* nil + "The current readtable. Control the parsing behavior of the Lisp reader.") + + +(defvar *sharp-equal-alist* nil) +(defvar *sharp-sharp-alist* nil) +(defvar *consing-dot-allowed* nil) +(defvar *consing-dot* (gensym)) +(defvar *preserve-whitespace-p* nil) +(defvar *input-stream* nil) +(defvar *backquote-level* 0) +(defvar *dispatch-macro-char* nil) +(defvar *standard-readtable*) + +(define-condition invalid-character-error (reader-error) + ((character :type character :reader invalid-character-error-character + :initarg :character)) + (:report + (lambda (condition stream) + (format stream "Invalid character ~S is read." + (invalid-character-error-character condition))))) + +(defun reader-error (&optional format-control &rest format-arguments) + (error 'reader-error + :format-control format-control :format-arguments format-arguments)) + +(defun copy-readtable (&optional (from-readtable *readtable*) to-readtable) + "Copy FROM-READTABLE. If TO-READTABLE is nil, a new table is to be created." + (flet ((copy-syntax (src) + (let ((new (make-hash-table))) + (maphash + #'(lambda (k v) + (let ((plist (copy-list v))) + (setf (gethash k new) plist) + (when (getf plist :dispatch-table) + (let ((hash (make-hash-table))) + (maphash #'(lambda (k v) (setf (gethash k hash) v)) + (getf plist :dispatch-table)) + (setf (getf plist :dispatch-table) hash))))) + src) + new))) + (let ((from (or from-readtable *standard-readtable*))) + (if to-readtable + (prog1 to-readtable + (setf (readtable-syntax to-readtable) + (copy-syntax (readtable-syntax from))) + (setf (readtable-case to-readtable) (readtable-case from))) + (make-readtable :syntax (copy-syntax (readtable-syntax from)) + :case (readtable-case from)))))) + +(defun syntax-type (char &optional (readtable *readtable*)) + (let ((plist (gethash char (readtable-syntax readtable)))) + (getf plist :syntax :constituent))) + +(defun get-macro-character (char &optional (readtable *readtable*)) + "Return a macro function for CHAR and non-terminating-p as the secondary value." + (unless readtable (setq readtable *standard-readtable*)) + (let ((plist (gethash char (readtable-syntax readtable)))) + (case (syntax-type char readtable) + (:terminating-macro-char (values (getf plist :macro-function) nil)) + (:non-terminating-macro-char (values (getf plist :macro-function) t)) + (t (values nil nil))))) + +(defun set-macro-character (char new-function + &optional non-terminating-p (readtable *readtable*)) + "Make CHAR a macro character associated with NEW-FUNCTION in READTABLE." + (check-type char character) + (check-type new-function function-designator) + (when (null readtable) + (error "Standard readtable must not be changed.")) + (let ((plist (gethash char (readtable-syntax readtable)))) + (setf (getf plist :syntax) (if non-terminating-p + :non-terminating-macro-char + :terminating-macro-char) + (getf plist :macro-function) new-function + (gethash char (readtable-syntax readtable)) plist)) + t) + +(defun get-dispatch-macro-character (disp-char sub-char + &optional (readtable *readtable*)) + "Retrieve the dispatch function for DISP-CHAR and SUB-CHAR in READTABLE." + (unless readtable (setq readtable *standard-readtable*)) + (unless (eq (get-macro-character disp-char readtable) + 'dispatch-macro-character) + (error "~S is not a dispatching macro character." disp-char)) + (let* ((syntax-table (readtable-syntax readtable)) + (dispatch-table (getf (gethash disp-char syntax-table) + :dispatch-table)) + (sub-char (char-upcase sub-char))) + (multiple-value-bind (value present-p) (gethash sub-char dispatch-table) + (cond + ((digit-char-p sub-char 10) nil) + (present-p value) + (t + #'(lambda (stream sub-char number) + (declare (ignore stream number)) + (reader-error "No dispatch function defined for ~S." + sub-char))))))) + +(defun set-dispatch-macro-character (disp-char sub-char new-function + &optional (readtable *readtable*)) + "Install NEW-FUNCTION as the dispatch function for DISP-CHAR and SUB-CHAR." + (when (null readtable) (error "Standard readtable must not be changed.")) + (unless (eq (get-macro-character disp-char readtable) + 'dispatch-macro-character) + (error "~S is not a dispatch character." disp-char)) + (let* ((syntax-table (readtable-syntax readtable)) + (dispatch-table (getf (gethash disp-char syntax-table) + :dispatch-table)) + (sub-char (char-upcase sub-char))) + (setf (gethash sub-char dispatch-table) new-function) + t)) + +(defun make-dispatch-macro-character (char &optional non-terminating-p + (readtable *readtable*)) + "Make CHAR be a dispatching macro character in READTABLE." + (when (null readtable) (error "Standard readtable must not be changed.")) + (set-macro-character char 'dispatch-macro-character + non-terminating-p readtable) + + (setf (getf (gethash char (readtable-syntax readtable)) :dispatch-table) + (make-hash-table)) + t) + +(defun dispatch-macro-character (stream char) + (let ((n (when (digit-char-p (peek-char nil stream t nil t) 10) + (loop + with n = 0 + for digit = (read-char stream t nil t) + do (setq n (+ (* n 10) (digit-char-p digit 10))) + while (digit-char-p (peek-char nil stream t nil t) 10) + finally (return n)))) + (*dispatch-macro-char* char) + (sub-char (char-upcase (read-char stream t nil t)))) + (funcall (get-dispatch-macro-character char sub-char) stream sub-char n))) + +(defun set-syntax-from-char (to-char from-char + &optional (to-readtable *readtable*) + (from-readtable *standard-readtable*)) + "Make the syntax of TO-CHAR in TO-READTABLE be the same as that of FROM-CHAR." + (check-type to-char character) + (check-type from-char character) + (check-type to-readtable readtable) + (unless from-readtable (setq from-readtable *standard-readtable*)) + (check-type from-readtable readtable) + (let ((plist (copy-list (gethash from-char + (readtable-syntax from-readtable))))) + (when (getf plist :dispatch-table) + (let ((hash (make-hash-table))) + (maphash #'(lambda (k v) (setf (gethash k hash) v)) + (getf plist :dispatch-table)) + (setf (getf plist :dispatch-table) hash))) + (setf (gethash to-char (readtable-syntax to-readtable)) plist) + t)) + +(defun read-preserving-whitespace (&optional (input-stream *standard-input*) + (eof-error-p t) eof-value recursive-p) + "Read an object but preserves any whitespace character after it." + (let ((*preserve-whitespace-p* (if recursive-p *preserve-whitespace-p* t))) + (read-lisp-object input-stream eof-error-p eof-value recursive-p))) + +(defun read (&optional (input-stream *standard-input*) + (eof-error-p t) eof-value recursive-p) + "Parse a printed representation from INPUT-STREAM and return the object." + (let ((*preserve-whitespace-p* (when recursive-p *preserve-whitespace-p*))) + (read-lisp-object input-stream eof-error-p eof-value recursive-p))) + +(defun read-from-string (string &optional (eof-error-p t) eof-value + &key (start 0) end preserve-whitespace) + "Read an object from the subsequence of string bounded by START and END." + (let ((index nil)) + (values (with-input-from-string (stream string :index index + :start start :end end) + (funcall (if preserve-whitespace + #'read-preserving-whitespace + #'read) + stream eof-error-p eof-value)) + index))) + +(defun make-str (chars) + (make-array (length chars) :element-type 'character :initial-contents chars)) + +(defun sharp-equal (stream sub-char n) + (declare (ignore sub-char)) + (if *read-suppress* + (values) + (let* ((this (gensym)) + (object (progn + (setq *sharp-sharp-alist* (acons n this + *sharp-sharp-alist*)) + (read stream t nil t)))) + (when (null n) + (reader-error "Missing label number for #=.")) + (when (assoc this *sharp-equal-alist*) + (reader-error "#~D= is already defined." n)) + (when (eq object this) + (reader-error "need to tag something more than just #~D#." n)) + (setq *sharp-equal-alist* (acons this object *sharp-equal-alist*)) + object))) + +(defun sharp-sharp (stream sub-char n) + (declare (ignore sub-char stream)) + (unless *read-suppress* + (unless n (reader-error "Label is missing for ##.")) + (let ((assoc (assoc n *sharp-sharp-alist*))) + (unless assoc + (reader-error "No object labeled ~D is seen." n)) + (cdr assoc)))) + +(defun read-delimited-list (char &optional (stream *standard-input*) + recursive-p) + "Read objects until CHAR appears, then return a list of objects read." + (let ((list (read-list char stream :allow-consing-dot nil))) + (unless *read-suppress* list))) + +(defun read-list (char &optional (stream *standard-input*) + &key allow-consing-dot) + (let ((*consing-dot-allowed* allow-consing-dot) + c stack values list) + (loop + (setq c (peek-char t stream t nil t)) + (when (char= char c) ; found the closing parenthesis. + (when (eq (first stack) *consing-dot*) + (error "Nothing appears after . in list.")) + (read-char stream t nil t) + (setq list (if (eq (second stack) *consing-dot*) + (nreconc (cddr stack) (first stack)) + (nreverse stack))) + (return)) + (when (setq values (multiple-value-list (lisp-object? stream t nil t))) + (if (eq (second stack) *consing-dot*) + (error "More than one object follows . in list.") + (push (car values) stack)))) + list)) + +(defun lisp-object? (stream eof-error-p eof-value recursive-p) + (loop + (let* ((c (read-char stream eof-error-p eof-value recursive-p))) + (when (and (not eof-error-p) (eq c eof-value)) (return eof-value)) + (ecase (syntax-type c) + (:invalid (error 'invalid-character-error :character c)) + (:whitespace 'skip) + ((:single-escape :multiple-escape :constituent) + (return (read-number-or-symbol stream c))) + ((:terminating-macro-char :non-terminating-macro-char) + (return (funcall (get-macro-character c) stream c))))))) + +(defmethod general-nsublis ((alist list) (object t)) + object) + +(defmethod general-nsublis :around ((alist cons) (object t)) + (let ((assoc (assoc object alist :test #'eq))) + (if assoc + (cdr assoc) + (call-next-method alist object)))) + +(defmethod general-nsublis ((alist cons) (object cons)) + (setf (car object) (general-nsublis alist (car object))) + (setf (cdr object) (general-nsublis alist (cdr object))) + object) + +(defmethod general-nsublis ((alist cons) (object array)) + (do ((i 0 (1+ i))) + ((= i (array-total-size object)) object) + (setf (row-major-aref object i) + (general-nsublis alist (row-major-aref object i))))) + +(defun read-lisp-object (stream eof-error-p eof-value recursive-p) + (unless recursive-p + (setq *sharp-equal-alist* nil + *sharp-sharp-alist* nil)) + (let ((object (loop + (let ((values (multiple-value-list + (lisp-object? stream + eof-error-p eof-value + recursive-p)))) + (when values + (return (unless *read-suppress* (car values)))))))) + (if (and (not recursive-p) *sharp-equal-alist*) + (general-nsublis *sharp-equal-alist* object) + object))) + +(defun read-ch () (read-char *input-stream* nil nil t)) +(defun read-ch-or-die () (read-char *input-stream* t nil t)) +(defun unread-ch (c) (unread-char c *input-stream*)) + +(defun collect-escaped-lexemes (c) + (ecase (syntax-type c) + (:invalid (error 'invalid-character-error :character c)) + (:multiple-escape nil) + (:single-escape (cons (read-ch-or-die) + (collect-escaped-lexemes (read-ch-or-die)))) + ((:constituent + :whitespace :terminating-macro-char :non-terminating-macro-char) + (cons c (collect-escaped-lexemes (read-ch-or-die)))))) + +(defun collect-lexemes (c &optional (stream *input-stream*)) + (let ((*input-stream* stream)) + (when c + (ecase (syntax-type c) + (:invalid (error 'invalid-character-error :character c)) + (:whitespace (when *preserve-whitespace-p* (unread-ch c))) + (:terminating-macro-char (unread-ch c)) + (:multiple-escape (cons (collect-escaped-lexemes (read-ch-or-die)) + (collect-lexemes (read-ch)))) + (:single-escape (cons (list (read-ch-or-die)) + (collect-lexemes (read-ch)))) + ((:constituent :non-terminating-macro-char) + (cons c (collect-lexemes (read-ch)))))))) + +;; integer ::= [sign] decimal-digit+ decimal-point +;; | [sign] digit+ +;; ratio ::= [sign] {digit}+ slash {digit}+ +;; float ::= [sign] {decimal-digit}* decimal-point {decimal-digit}+ [exponent] +;; | [sign] {decimal-digit}+ [decimal-point {decimal-digit}*] exponent +;; exponent::= exponent-marker [sign] {digit}+ + +(defun construct-number (chars) + (labels ((sign () + (let ((c (and chars (car chars)))) + (cond + ((eql c #\-) (pop chars) -1) + ((eql c #\+) (pop chars) +1) + (t +1)))) + (digit* (&optional (base *read-base*)) + (let ((pos (or (position-if-not #'(lambda (d) + (digit-char-p d base)) + chars) + (length chars)))) + (prog1 (subseq chars 0 pos) + (setq chars (subseq chars pos))))) + (int? (sign digits &optional (base *read-base*)) + (when (and digits + (every #'(lambda (d) (digit-char-p d base)) digits)) + (* sign (reduce #'(lambda (a b) (+ (* base a) b)) + (mapcar #'(lambda (d) (digit-char-p d base)) + digits))))) + (float? (sign) + (let* ((int (digit* 10)) + (fraction (when (eql (car chars) #\.) + (pop chars) (digit* 10))) + (exp-marker (when (and chars + (find (char-upcase (car chars)) + '(#\D #\E #\F #\L #\S))) + (char-upcase (pop chars)))) + (exp-sign (and exp-marker (sign))) + (exp-digits (and exp-sign (digit*)))) + (when (and (null chars) + (or fraction (and int exp-marker exp-digits))) + (float (* (int? sign (append int fraction) 10) + (expt 10 (- (or (int? exp-sign exp-digits 10) 0) + (length fraction)))) + (ecase (or exp-marker *read-default-float-format*) + (#\E 1.0e0) + ((#\D double-float) 1.0d0) + ((#\F single-float) 1.0f0) + ((#\L long-float) 1.0l0) + ((#\S short-float) 1.0s0))))))) + (let ((sign (sign)) + pos numerator denominator) + (when chars + (or + ;; [sign] digit+ + (int? sign chars) + ;; [sign] decimal-digit+ decimal-point + (and (eql (car (last chars)) #\.) (int? sign (butlast chars) 10)) + ;; [sign] {digit}+ slash {digit}+ + (and (setq pos (position #\/ chars)) + (setq numerator (int? sign (subseq chars 0 pos))) + (setq denominator (int? 1 (subseq chars (1+ pos)))) + (not (zerop denominator)) + (/ numerator denominator)) + ;; [sign] {decimal-digit}* decimal-point {decimal-digit}+ [exponent] + ;; [sign] {decimal-digit}+ [decimal-point {decimal-digit}*] exponent + (float? sign)))))) + + + +(defun ensure-external-symbol (name package) + (multiple-value-bind (symbol status) (find-symbol name package) + (unless (eq status :external) + (cerror (if (null status) + "Intern and export symbol ~S in package ~S." + "Export symbol ~S in package ~S.") + "There is no external symbol by the name of ~S in package ~S." + name package) + (export (setq symbol (intern name package)) package)) + symbol)) + +(defun construct-symbol (lexemes &key uninterned-symbol-wanted) + (labels ((up (x) (if (listp x) (copy-list x) (list (char-upcase x)))) + (down (x) (if (listp x) (copy-list x) (list (char-downcase x)))) + (chars (lexemes) + (ecase (readtable-case *readtable*) + (:upcase (mapcan #'up lexemes)) + (:downcase (mapcan #'down lexemes)) + (:invert + (let ((unescaped (remove-if-not #'alpha-char-p + (remove-if #'listp lexemes)))) + (mapcan (cond + ((every #'upper-case-p unescaped) #'down) + ((every #'lower-case-p unescaped) #'up) + (t #'(lambda (x) + (if (listp x) (copy-list x) (list x))))) + lexemes))) + (:preserve (mapcan #'(lambda (x) + (if (listp x) (copy-list x) (list x))) + lexemes)))) + (name (lexemes) + (when (find #\: lexemes) (error "Too many package markers.")) + (make-str (chars lexemes)))) + (let* ((pos (position #\: lexemes)) + (external-p (and pos (not (eql (nth (1+ pos) lexemes) #\:)))) + (package (when pos (name (subseq lexemes 0 pos)))) + (name (name (subseq lexemes + (if pos (+ pos (if external-p 1 2)) 0))))) + (values (cond + (uninterned-symbol-wanted + (if package + (reader-error) + (make-symbol name))) + (external-p + (ensure-external-symbol name package)) + (t (intern name (or package *package*)))))))) + +(defun read-number-or-symbol (stream c) + (let ((lexemes (collect-lexemes c stream))) + (assert lexemes) + (unless *read-suppress* + (cond + ((and lexemes (every #'(lambda (x) (eql x #\.)) lexemes)) + (when (rest lexemes) + (reader-error "Tokens consisting of only dots are invalid.")) + (when (not *consing-dot-allowed*) + (reader-error "Consing dot is not allowed.")) + *consing-dot*) + (t + (or (and (every #'characterp lexemes) (construct-number lexemes)) + (construct-symbol lexemes))))))) + + +;; backquote + +(defconstant backquote (gensym)) +(defconstant backquote-comma (gensym)) +(defconstant backquote-comma-at (gensym)) +(defconstant backquote-comma-dot (gensym)) +(defun backquoted-expression-type (exp) + (if (atom exp) + :normal + (cond + ((eq (first exp) backquote-comma) :comma) + ((eq (first exp) backquote-comma-at) :comma-at) + ((eq (first exp) backquote-comma-dot) :comma-dot) + (t :normal)))) + +(defmacro backquote (object) + (if (atom object) + (if (simple-vector-p object) + (list 'apply #'vector (list backquote (concatenate 'list object))) + (list 'quote object)) + (let* ((list (copy-list object)) + (last (loop for x = list then (cdr x) + until (or (atom (cdr x)) + (find (cadr x) (list backquote + backquote-comma + backquote-comma-at + backquote-comma-dot))) + finally (return (prog1 (cdr x) (setf (cdr x) nil))))) + (types (mapcar #'backquoted-expression-type list))) + (append + (cons (if (notany #'(lambda (x) (eq x :comma-at)) types) 'nconc 'append) + (mapcar #'(lambda (x) + (ecase (backquoted-expression-type x) + (:normal (list 'list (list 'backquote x))) + (:comma (list 'list x)) + ((:comma-at :comma-dot) x))) + list)) + (list (ecase (backquoted-expression-type last) + (:normal (list 'quote last)) + (:comma last) + (:comma-at (error ",@ after dot")) + (:comma-dot (error ",. after dot")))))))) + +(defmacro backquote-comma (obj) obj) +(setf (macro-function backquote) (macro-function 'backquote)) +(setf (macro-function backquote-comma) (macro-function 'backquote-comma)) +(setf (macro-function backquote-comma-at) (macro-function 'backquote-comma)) +(setf (macro-function backquote-comma-dot) (macro-function 'backquote-comma)) + + +(defun read-comma-form (stream c) + (declare (ignore c)) + (unless (> *backquote-level* 0) + (error "Comma must be used in a backquoted expression.")) + (let ((*backquote-level* (1- *backquote-level*))) + (case (peek-char t stream t nil t) + (#\@ (read-char stream t nil t) + (list backquote-comma-at (read stream t nil t))) + (#\. (read-char stream t nil t) + (list backquote-comma-dot (read stream t nil t))) + (t (list backquote-comma (read stream t nil t)))))) + +(defun read-backquoted-expression (stream c) + (declare (ignore c)) + (let ((*backquote-level* (1+ *backquote-level*))) + (list backquote (read stream t nil t)))) + + +(defun sharp-backslash (stream sub-char n) + (declare (ignore n)) + (let* ((lexemes (collect-lexemes sub-char stream)) + (str (make-str (mapcan #'(lambda (x) + (if (listp x) (copy-list x) (list x))) + lexemes)))) + (unless *read-suppress* + (cond + ((= 1 (length str)) (char str 0)) + ((name-char str)) + (t (reader-error "Unrecognized character name: ~S" str)))))) + +(defun sharp-single-quote (stream sub-char n) + (declare (ignore sub-char n)) + `(function ,(read stream t nil t))) + +(defun sharp-left-parenthesis (stream sub-char n) + (declare (ignore sub-char)) + (let ((list (read-delimited-list #\) stream t))) + (unless *read-suppress* + (when (and n (> (length list) n)) + (reader-error "vector is longer than specified length #~A*~A." + n list)) + (apply #'vector + (if (and n (< (length list) n)) + (append list (make-list (- n (length list)) + :initial-element (car (last list)))) + list))))) + +(defun sharp-asterisk (stream sub-char n) + (declare (ignore sub-char)) + (let* ((*input-stream* stream) + (lexemes (collect-lexemes (read-ch))) + (bits (mapcar #'(lambda (d) + (unless (characterp d) + (error "Binary digit must be given")) + (digit-char-p d 2)) lexemes))) + (unless *read-suppress* + (unless (every #'(lambda (d) (digit-char-p d 2)) lexemes) + (reader-error "Illegal bit vector format.")) + (when (and n (> (length bits) n)) + (reader-error "Bit vector is longer than specified length #~A*~A." + n (make-str lexemes))) + (when (and n (> n 0) (zerop (length bits))) + (reader-error + "At least one bit must be given for non-zero #* bit-vectors.")) + (make-array (or n (length bits)) :element-type 'bit + :initial-contents + (if (and n (< (length bits) n)) + (append bits + (make-list (- n (length bits)) + :initial-element (car (last bits)))) + bits))))) + +(defun sharp-colon (stream sub-char n) + (declare (ignore sub-char n)) + (let* ((*input-stream* stream) + (lexemes (collect-lexemes (read-ch)))) + (unless *read-suppress* + (construct-symbol lexemes :uninterned-symbol-wanted t)))) + +(defun sharp-dot (stream sub-char n) + (declare (ignore sub-char n)) + (let ((object (read stream t nil t))) + (unless *read-suppress* + (unless *read-eval* + (reader-error "Attempt to read #. while *READ-EVAL* is bound to NIL.")) + (eval object)))) + +(defun sharp-b (stream sub-char n) + (declare (ignore n)) + (sharp-r stream sub-char 2)) + +(defun sharp-o (stream sub-char n) + (declare (ignore n)) + (sharp-r stream sub-char 8)) + +(defun sharp-x (stream sub-char n) + (declare (ignore n)) + (sharp-r stream sub-char 16)) + +(defun sharp-r (stream sub-char n) + (cond + (*read-suppress* (read stream t nil t)) + ((not n) (reader-error "Radix missing in #R.")) + ((not (<= 2 n 36)) (reader-error "Illegal radix for #R: ~D." n)) + (t (let ((rational (let ((*read-base* n)) (read stream t nil t)))) + (unless (typep rational 'rational) + (reader-error "#~A (base ~D) value is not a rational: ~S." + sub-char n rational)) + rational)))) + + +(defun sharp-c (stream sub-char n) + (declare (ignore sub-char n)) + (let ((pair (read stream t nil t))) + (unless *read-suppress* + (unless (and (listp pair) (= (length pair) 2)) + (reader-error "Illegal complex number format: #C~S" pair)) + (complex (first pair) (second pair))))) + +(defun sharp-a (stream sub-char rank) + (declare (ignore sub-char)) + (cond + (*read-suppress* (read stream t nil t)) + ((null rank) + (reader-error "Rank for #A notation is missing.")) + (t (let* ((contents (read stream t nil t)) + (dimensions (loop repeat rank + for x = contents then (first x) + collect (length x)))) + (make-array dimensions :initial-contents contents))))) + + +(defun find-default-constructor (name) + (declare (ignore name))) + +(defun sharp-s (stream sub-char n) + (declare (ignore sub-char n)) + (let ((structure-spec (read stream t nil t))) + (unless *read-suppress* + (unless (listp structure-spec) + (reader-error "Non list follows #S.")) + (unless (symbolp (first structure-spec)) + (reader-error "Structure type is not a symbol: ~S" + (car structure-spec))) + (let* ((name (first structure-spec)) + (plist (loop + for list on (rest structure-spec) by #'cddr + append (list (intern (string (first list)) "KEYWORD") + (second list)))) + (class (find-class name nil))) + (unless (typep class 'structure-class) + (reader-error "~S is not a defined structure type." name)) + (let ((constructor (find-default-constructor name))) + (apply constructor plist)))))) + +(defun sharp-p (stream sub-char n) + (declare (ignore sub-char n)) + (let ((namestring (read stream t nil t))) + (unless *read-suppress* (parse-namestring namestring)))) + + + +(defun featurep (x) + (if (atom x) + (member x *features*) + (ecase (first x) + (:not (not (featurep (second x)))) + (:and (every #'featurep (rest x))) + (:or (some #'featurep (rest x)))))) + +(defun read-feature-test (stream) + (let ((*package* (or (find-package "KEYWORD") + (error "KEYWORD package not found.")))) + (read stream t nil t))) + +(defun sharp-plus (stream sub-char n) + (declare (ignore sub-char n)) + (if (featurep (read-feature-test stream)) + (read stream t nil t) + (let ((*read-suppress* t)) (read stream t nil t) (values)))) + +(defun sharp-minus (stream sub-char n) + (declare (ignore sub-char n)) + (if (not (featurep (read-feature-test stream))) + (read stream t nil t) + (let ((*read-suppress* t)) (read stream t nil t) (values)))) + +(defun sharp-vertical-bar (stream sub-char n) + (declare (ignore sub-char n)) + (loop for c = (read-char stream t nil t) + if (and (char= c #\#) (char= (read-char stream t nil t) #\|)) + do (sharp-vertical-bar stream #\| nil) + until (and (char= c #\|) (char= (read-char stream t nil t) #\#))) + (values)) + + +(defvar *standard-syntax-table* + (let ((table (make-hash-table))) + (mapc #'(lambda (x) + (let ((syntax (first x)) + (chars (rest x))) + (dolist (c chars) + (setf (gethash c table) `(:syntax ,syntax))))) + '((:whitespace #\Tab #\Newline #\Linefeed #\Page #\Return #\Space) + (:single-escape #\\) + (:multiple-escape #\|))) + table)) + +(setq *standard-readtable* (make-readtable :syntax *standard-syntax-table*)) + +(set-macro-character #\` 'read-backquoted-expression nil *standard-readtable*) +(set-macro-character #\, 'read-comma-form nil *standard-readtable*) + +(set-macro-character #\( #'(lambda (stream char) + (declare (ignore char)) + (read-list #\) stream :allow-consing-dot t)) + nil *standard-readtable*) + +(set-macro-character #\) #'(lambda (stream char) + (declare (ignore stream char)) + (error "Unmatched close parenthesis.")) + nil *standard-readtable*) + +(set-macro-character #\' #'(lambda (stream char) + (declare (ignore char)) + `(quote ,(read stream t nil t))) + nil *standard-readtable*) + +(set-macro-character #\; #'(lambda (stream char) + (declare (ignore char)) + (loop + for c = (read-char stream nil nil t) + until (or (null c) (eql c #\Newline))) + (values)) + nil *standard-readtable*) + +(set-macro-character #\" #'(lambda (stream char) + (declare (ignore char)) + (loop + for c = (read-char stream t nil t) + until (char= c #\") + if (eq :single-escape (syntax-type c)) + collect (read-char stream t nil t) into chars + else + collect c into chars + finally + (return (make-array (length chars) + :element-type 'character + :initial-contents chars)))) + nil *standard-readtable*) + + +(make-dispatch-macro-character #\# t *standard-readtable*) +(mapc + #'(lambda (pair) + (set-dispatch-macro-character #\# (first pair) (second pair) + *standard-readtable*)) + '((#\\ sharp-backslash) (#\' sharp-single-quote) (#\( sharp-left-parenthesis) + (#\* sharp-asterisk) (#\: sharp-colon) (#\. sharp-dot) (#\b sharp-b) + (#\o sharp-o) (#\x sharp-x) (#\r sharp-r) (#\c sharp-c) (#\a sharp-a) + (#\s sharp-s) (#\p sharp-p) (#\= sharp-equal) (#\# sharp-sharp) + (#\+ sharp-plus) (#\- sharp-minus) (#\| sharp-vertical-bar))) + +(setq *readtable* (copy-readtable nil)) |