1 ;;;; This software is part of the SBCL system. See the README file for
4 ;;;; This software is derived from the CMU CL system, which was
5 ;;;; written at Carnegie Mellon University and released into the
6 ;;;; public domain. The software is in the public domain and is
7 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
8 ;;;; files for more information.
10 (in-package "SB!IMPL")
12 (declaim (special *read-suppress* *standard-readtable* *bq-vector-flag*))
14 ;;; FIXME: Is it standard to ignore numeric args instead of raising errors?
15 (defun ignore-numarg (sub-char numarg)
17 (warn "A numeric argument was ignored in #~W~A." numarg sub-char)))
19 ;;;; reading arrays and vectors: the #(, #*, and #A readmacros
21 (defun sharp-left-paren (stream ignore length)
22 (declare (ignore ignore) (special *backquote-count*))
23 (let* ((list (read-list stream nil))
24 (listlength (handler-case (length list)
27 (declare (ignore error))
28 (%reader-error stream "improper list in #(): ~S"
32 (cond (*read-suppress* nil)
33 ((zerop *backquote-count*)
35 (cond ((> listlength (the fixnum length))
38 "vector longer than specified length: #~S~S"
41 (fill (the simple-vector
42 (replace (the simple-vector
47 (coerce list 'vector)))
48 (t (cons *bq-vector-flag* list)))))
50 (defun sharp-star (stream ignore numarg)
51 (declare (ignore ignore))
52 (multiple-value-bind (bstring escape-appearedp) (read-extended-token stream)
53 (declare (simple-string bstring))
54 (cond (*read-suppress* nil)
56 (%reader-error stream "An escape character appeared after #*"))
57 ((and numarg (zerop (length bstring)) (not (zerop numarg)))
60 "You have to give a little bit for non-zero #* bit-vectors."))
61 ((or (null numarg) (>= (the fixnum numarg) (length bstring)))
62 (let* ((len1 (length bstring))
64 (len2 (or numarg len1))
65 (bvec (make-array len2 :element-type 'bit
67 (declare (fixnum len1 last1 len2))
72 (setq char (elt bstring (if (< i len1) i last1)))
74 (cond ((char= char #\0) 0)
79 "illegal element given for bit-vector: ~S"
84 "Bit vector is longer than specified length #~A*~A"
87 (defun sharp-A (stream ignore dimensions)
88 (declare (ignore ignore))
91 (return-from sharp-A nil))
92 (unless dimensions (%reader-error stream "no dimensions argument to #A"))
94 (let* ((contents (read stream t nil t))
96 (dotimes (axis dimensions
97 (make-array (dims) :initial-contents contents))
98 (unless (typep seq 'sequence)
100 "#~WA axis ~W is not a sequence:~% ~S"
101 dimensions axis seq))
102 (let ((len (length seq)))
104 (unless (or (= axis (1- dimensions))
105 ;; ANSI: "If some dimension of the array whose
106 ;; representation is being parsed is found to be
107 ;; 0, all dimensions to the right (i.e., the
108 ;; higher numbered dimensions) are also
109 ;; considered to be 0."
111 (setq seq (elt seq 0))))))))
113 ;;;; reading structure instances: the #S readmacro
115 (defun sharp-S (stream sub-char numarg)
116 (ignore-numarg sub-char numarg)
117 (when *read-suppress*
118 (read stream t nil t)
119 (return-from sharp-S nil))
120 (let ((body (if (char= (read-char stream t) #\( )
121 (read-list stream nil)
122 (%reader-error stream "non-list following #S"))))
124 (%reader-error stream "non-list following #S: ~S" body))
125 (unless (symbolp (car body))
126 (%reader-error stream "Structure type is not a symbol: ~S" (car body)))
127 (let ((classoid (find-classoid (car body) nil)))
128 (unless (typep classoid 'structure-classoid)
129 (%reader-error stream "~S is not a defined structure type."
131 (let ((def-con (dd-default-constructor
133 (classoid-layout classoid)))))
136 stream "The ~S structure does not have a default constructor."
138 (when (and (atom (rest body))
139 (not (null (rest body))))
141 stream "improper list for #S: ~S." body))
142 (apply (fdefinition def-con)
143 (loop for tail on (rest body) by #'cddr
144 with slot-name = (and (consp tail) (car tail))
146 (when (null (cdr tail))
149 "the arglist for the ~S constructor in #S ~
150 has an odd length: ~S."
151 (car body) (rest body)))
152 (when (or (atom (cdr tail))
153 (and (atom (cddr tail))
154 (not (null (cddr tail)))))
157 "the arglist for the ~S constructor in #S ~
159 (car body) (rest body)))
160 (when (not (typep (car tail) 'string-designator))
163 "a slot name in #S is not a string ~
166 (when (not (keywordp slot-name))
167 (warn 'structure-initarg-not-keyword
169 "in #S ~S, the use of non-keywords ~
170 as slot specifiers is deprecated: ~S."
172 (list (car body) slot-name))))
173 collect (intern (string (car tail)) *keyword-package*)
174 collect (cadr tail)))))))
176 ;;;; reading numbers: the #B, #C, #O, #R, and #X readmacros
178 (defun sharp-B (stream sub-char numarg)
179 (ignore-numarg sub-char numarg)
180 (sharp-R stream sub-char 2))
182 (defun sharp-C (stream sub-char numarg)
183 (ignore-numarg sub-char numarg)
184 ;; The next thing had better be a list of two numbers.
185 (let ((cnum (read stream t nil t)))
186 (when *read-suppress* (return-from sharp-C nil))
187 (if (and (listp cnum) (= (length cnum) 2))
188 (complex (car cnum) (cadr cnum))
189 (%reader-error stream "illegal complex number format: #C~S" cnum))))
191 (defun sharp-O (stream sub-char numarg)
192 (ignore-numarg sub-char numarg)
193 (sharp-R stream sub-char 8))
195 (defun sharp-R (stream sub-char radix)
196 (cond (*read-suppress*
197 (read-extended-token stream)
200 (%reader-error stream "radix missing in #R"))
201 ((not (<= 2 radix 36))
202 (%reader-error stream "illegal radix for #R: ~D." radix))
204 (let ((res (let ((*read-base* radix))
205 (read stream t nil t))))
206 (unless (typep res 'rational)
207 (%reader-error stream
208 "#~A (base ~D.) value is not a rational: ~S."
214 (defun sharp-X (stream sub-char numarg)
215 (ignore-numarg sub-char numarg)
216 (sharp-R stream sub-char 16))
218 ;;;; reading circular data: the #= and ## readmacros
220 ;;; objects already seen by CIRCLE-SUBST
221 (defvar *sharp-equal-circle-table*)
222 (declaim (type hash-table *sharp-equal-circle-table*))
224 ;; This function is kind of like NSUBLIS, but checks for circularities and
225 ;; substitutes in arrays and structures as well as lists. The first arg is an
226 ;; alist of the things to be replaced assoc'd with the things to replace them.
227 (defun circle-subst (old-new-alist tree)
228 (cond ((not (typep tree
229 '(or cons (array t) structure-object standard-object)))
230 (let ((entry (find tree old-new-alist :key #'second)))
231 (if entry (third entry) tree)))
232 ((null (gethash tree *sharp-equal-circle-table*))
233 (setf (gethash tree *sharp-equal-circle-table*) t)
234 (cond ((typep tree '(or structure-object standard-object))
236 (end (%instance-length tree)))
238 (let* ((old (%instance-ref tree i))
239 (new (circle-subst old-new-alist old)))
241 (setf (%instance-ref tree i) new)))))
243 (with-array-data ((data tree) (start) (end))
244 (declare (fixnum start end))
245 (do ((i start (1+ i)))
247 (let* ((old (aref data i))
248 (new (circle-subst old-new-alist old)))
250 (setf (aref data i) new))))))
252 (let ((a (circle-subst old-new-alist (car tree)))
253 (d (circle-subst old-new-alist (cdr tree))))
254 (unless (eq a (car tree))
256 (unless (eq d (cdr tree))
261 ;;; Sharp-equal works as follows. When a label is assigned (i.e. when
262 ;;; #= is called) we GENSYM a symbol is which is used as an
263 ;;; unforgeable tag. *SHARP-SHARP-ALIST* maps the integer tag to this
266 ;;; When SHARP-SHARP encounters a reference to a label, it returns the
267 ;;; symbol assoc'd with the label. Resolution of the reference is
268 ;;; deferred until the read done by #= finishes. Any already resolved
269 ;;; tags (in *SHARP-EQUAL-ALIST*) are simply returned.
271 ;;; After reading of the #= form is completed, we add an entry to
272 ;;; *SHARP-EQUAL-ALIST* that maps the gensym tag to the resolved
273 ;;; object. Then for each entry in the *SHARP-SHARP-ALIST, the current
274 ;;; object is searched and any uses of the gensysm token are replaced
275 ;;; with the actual value.
276 (defvar *sharp-sharp-alist* ())
278 (defun sharp-equal (stream ignore label)
279 (declare (ignore ignore))
280 (when *read-suppress* (return-from sharp-equal (values)))
282 (%reader-error stream "missing label for #=" label))
283 (when (or (assoc label *sharp-sharp-alist*)
284 (assoc label *sharp-equal-alist*))
285 (%reader-error stream "multiply defined label: #~D=" label))
286 (let* ((tag (gensym))
287 (*sharp-sharp-alist* (acons label tag *sharp-sharp-alist*))
288 (obj (read stream t nil t)))
290 (%reader-error stream
291 "must tag something more than just #~D#"
293 (push (list label tag obj) *sharp-equal-alist*)
294 (let ((*sharp-equal-circle-table* (make-hash-table :test 'eq :size 20)))
295 (circle-subst *sharp-equal-alist* obj))))
297 (defun sharp-sharp (stream ignore label)
298 (declare (ignore ignore))
299 (when *read-suppress* (return-from sharp-sharp nil))
301 (%reader-error stream "missing label for ##" label))
303 (let ((entry (assoc label *sharp-equal-alist*)))
306 (let ((pair (assoc label *sharp-sharp-alist*)))
308 (%reader-error stream "object is not labelled #~S#" label))
311 ;;;; conditional compilation: the #+ and #- readmacros
313 (flet ((guts (stream not-p)
314 (unless (if (let ((*package* *keyword-package*)
315 (*read-suppress* nil))
316 (featurep (read stream t nil t)))
319 (let ((*read-suppress* t))
320 (read stream t nil t)))
323 (defun sharp-plus (stream sub-char numarg)
324 (ignore-numarg sub-char numarg)
327 (defun sharp-minus (stream sub-char numarg)
328 (ignore-numarg sub-char numarg)
331 ;;;; reading miscellaneous objects: the #P, #\, and #| readmacros
333 (defun sharp-P (stream sub-char numarg)
334 (ignore-numarg sub-char numarg)
335 (let ((namestring (read stream t nil t)))
336 (unless *read-suppress*
337 (parse-namestring namestring))))
339 (defun sharp-backslash (stream backslash numarg)
340 (ignore-numarg backslash numarg)
341 (let ((charstring (read-extended-token-escaped stream)))
342 (declare (simple-string charstring))
343 (cond (*read-suppress* nil)
344 ((= (the fixnum (length charstring)) 1)
346 ((name-char charstring))
348 (%reader-error stream "unrecognized character name: ~S"
351 (defun sharp-vertical-bar (stream sub-char numarg)
352 (ignore-numarg sub-char numarg)
354 ((character-decoding-error
355 #'(lambda (decoding-error)
356 (declare (ignorable decoding-error))
357 (style-warn "Character decoding error in a #|-comment at position ~A reading source file ~A, resyncing." (file-position stream) stream)
358 (invoke-restart 'attempt-resync))))
359 (let ((stream (in-synonym-of stream)))
360 (if (ansi-stream-p stream)
361 (prepare-for-fast-read-char stream
363 (prev (fast-read-char) char)
364 (char (fast-read-char) (fast-read-char)))
366 (cond ((and (char= prev #\|) (char= char #\#))
367 (setq level (1- level))
369 (done-with-fast-read-char)
371 (setq char (fast-read-char)))
372 ((and (char= prev #\#) (char= char #\|))
373 (setq char (fast-read-char))
374 (setq level (1+ level))))))
375 ;; fundamental-stream
377 (prev (read-char stream t) char)
378 (char (read-char stream t) (read-char stream t)))
380 (cond ((and (char= prev #\|) (char= char #\#))
381 (setq level (1- level))
384 (setq char (read-char stream t)))
385 ((and (char= prev #\#) (char= char #\|))
386 (setq char (read-char stream t))
387 (setq level (1+ level)))))))))
389 ;;;; a grab bag of other sharp readmacros: #', #:, and #.
391 (defun sharp-quote (stream sub-char numarg)
392 (ignore-numarg sub-char numarg)
393 ;; The fourth arg tells READ that this is a recursive call.
394 `(function ,(read stream t nil t)))
396 (defun sharp-colon (stream sub-char numarg)
397 (ignore-numarg sub-char numarg)
398 (multiple-value-bind (token escapep colon) (read-extended-token stream)
399 (declare (simple-string token) (ignore escapep))
401 (*read-suppress* nil)
403 (%reader-error stream
404 "The symbol following #: contains a package marker: ~S"
407 (make-symbol token)))))
409 (defvar *read-eval* t
411 "If false, then the #. read macro is disabled.")
413 (defun sharp-dot (stream sub-char numarg)
414 (ignore-numarg sub-char numarg)
415 (let ((token (read stream t nil t)))
416 (unless *read-suppress*
418 (%reader-error stream "can't read #. while *READ-EVAL* is NIL"))
421 (defun sharp-illegal (stream sub-char ignore)
422 (declare (ignore ignore))
423 (%reader-error stream "illegal sharp macro character: ~S" sub-char))
425 ;;; for cold init: Install SHARPM stuff in the current *READTABLE*.
426 (defun !sharpm-cold-init ()
427 (make-dispatch-macro-character #\# t)
428 (set-dispatch-macro-character #\# #\\ #'sharp-backslash)
429 (set-dispatch-macro-character #\# #\' #'sharp-quote)
430 (set-dispatch-macro-character #\# #\( #'sharp-left-paren)
431 (set-dispatch-macro-character #\# #\* #'sharp-star)
432 (set-dispatch-macro-character #\# #\: #'sharp-colon)
433 (set-dispatch-macro-character #\# #\. #'sharp-dot)
434 (set-dispatch-macro-character #\# #\R #'sharp-R)
435 (set-dispatch-macro-character #\# #\r #'sharp-R)
436 (set-dispatch-macro-character #\# #\B #'sharp-B)
437 (set-dispatch-macro-character #\# #\b #'sharp-B)
438 (set-dispatch-macro-character #\# #\O #'sharp-O)
439 (set-dispatch-macro-character #\# #\o #'sharp-O)
440 (set-dispatch-macro-character #\# #\X #'sharp-X)
441 (set-dispatch-macro-character #\# #\x #'sharp-X)
442 (set-dispatch-macro-character #\# #\A #'sharp-A)
443 (set-dispatch-macro-character #\# #\a #'sharp-A)
444 (set-dispatch-macro-character #\# #\S #'sharp-S)
445 (set-dispatch-macro-character #\# #\s #'sharp-S)
446 (set-dispatch-macro-character #\# #\= #'sharp-equal)
447 (set-dispatch-macro-character #\# #\# #'sharp-sharp)
448 (set-dispatch-macro-character #\# #\+ #'sharp-plus)
449 (set-dispatch-macro-character #\# #\- #'sharp-minus)
450 (set-dispatch-macro-character #\# #\C #'sharp-C)
451 (set-dispatch-macro-character #\# #\c #'sharp-C)
452 (set-dispatch-macro-character #\# #\| #'sharp-vertical-bar)
453 (set-dispatch-macro-character #\# #\p #'sharp-P)
454 (set-dispatch-macro-character #\# #\P #'sharp-P)
455 (set-dispatch-macro-character #\# #\) #'sharp-illegal)
456 (set-dispatch-macro-character #\# #\< #'sharp-illegal)
457 (set-dispatch-macro-character #\# #\Space #'sharp-illegal)
458 (dolist (cc '#.(list tab-char-code form-feed-char-code return-char-code
459 line-feed-char-code backspace-char-code))
460 (set-dispatch-macro-character #\# (code-char cc) #'sharp-illegal)))