1 | ;;;; -*- Mode:Lisp -*- |
---|
2 | ;;;; |
---|
3 | ;;;; Copyright (c) 2007 - 2009 Tobias C. Rittweiler <tcr@freebits.de> |
---|
4 | ;;;; Copyright (c) 2007, Robert P. Goldman <rpgoldman@sift.info> and SIFT, LLC |
---|
5 | ;;;; |
---|
6 | ;;;; All rights reserved. |
---|
7 | ;;;; |
---|
8 | ;;;; See LICENSE for details. |
---|
9 | ;;;; |
---|
10 | |
---|
11 | (in-package :editor-hints.named-readtables) |
---|
12 | |
---|
13 | ;;; |
---|
14 | ;;; ``This is enough of a foothold to implement a more elaborate |
---|
15 | ;;; facility for using readtables in a localized way.'' |
---|
16 | ;;; |
---|
17 | ;;; (X3J13 Cleanup Issue IN-SYNTAX) |
---|
18 | ;;; |
---|
19 | |
---|
20 | ;;;;;; DEFREADTABLE &c. |
---|
21 | |
---|
22 | (defmacro defreadtable (name &body options) |
---|
23 | "Define a new named readtable, whose name is given by the symbol NAME. |
---|
24 | Or, if a readtable is already registered under that name, redefine |
---|
25 | that one. |
---|
26 | |
---|
27 | The readtable can be populated using the following OPTIONS: |
---|
28 | |
---|
29 | - `(:MERGE READTABLE-DESIGNATORS+)` |
---|
30 | |
---|
31 | Merge the readtables designated into the new readtable being |
---|
32 | defined as per MERGE-READTABLES-INTO. |
---|
33 | |
---|
34 | If no :MERGE clause is given, an empty readtable is used. See |
---|
35 | MAKE-READTABLE. |
---|
36 | |
---|
37 | - `(:FUSE READTABLE-DESIGNATORS+)` |
---|
38 | |
---|
39 | Like :MERGE except: |
---|
40 | |
---|
41 | Error conditions of type READER-MACRO-CONFLICT that are signaled |
---|
42 | during the merge operation will be silently _continued_. It |
---|
43 | follows that reader macros in earlier entries will be |
---|
44 | overwritten by later ones. For backward compatibility, :FUZE is |
---|
45 | accepted as an alias of :FUSE. |
---|
46 | |
---|
47 | - `(:DISPATCH-MACRO-CHAR MACRO-CHAR SUB-CHAR FUNCTION)` |
---|
48 | |
---|
49 | Define a new sub character `SUB-CHAR` for the dispatching macro |
---|
50 | character `MACRO-CHAR`, per SET-DISPATCH-MACRO-CHARACTER. You |
---|
51 | probably have to define `MACRO-CHAR` as a dispatching macro |
---|
52 | character by the following option first. |
---|
53 | |
---|
54 | - `(:MACRO-CHAR MACRO-CHAR FUNCTION [NON-TERMINATING-P])` |
---|
55 | |
---|
56 | Define a new macro character in the readtable, per |
---|
57 | SET-MACRO-CHARACTER. If `FUNCTION` is the keyword :DISPATCH, |
---|
58 | `MACRO-CHAR` is made a dispatching macro character, per |
---|
59 | MAKE-DISPATCH-MACRO-CHARACTER. |
---|
60 | |
---|
61 | - `(:SYNTAX-FROM FROM-READTABLE-DESIGNATOR FROM-CHAR TO-CHAR)` |
---|
62 | |
---|
63 | Set the character syntax of TO-CHAR in the readtable being |
---|
64 | defined to the same syntax as FROM-CHAR as per |
---|
65 | SET-SYNTAX-FROM-CHAR. |
---|
66 | |
---|
67 | - `(:CASE CASE-MODE)` |
---|
68 | |
---|
69 | Defines the _case sensitivity mode_ of the resulting readtable. |
---|
70 | |
---|
71 | Any number of option clauses may appear. The options are grouped by |
---|
72 | their type, but in each group the order the options appeared |
---|
73 | textually is preserved. The following groups exist and are executed |
---|
74 | in the following order: :MERGE and :FUSE (one |
---|
75 | group), :CASE, :MACRO-CHAR and :DISPATCH-MACRO-CHAR (one group), |
---|
76 | finally :SYNTAX-FROM. |
---|
77 | |
---|
78 | Notes: |
---|
79 | |
---|
80 | The readtable is defined at load-time. If you want to have it |
---|
81 | available at compilation time -- say to use its reader-macros in the |
---|
82 | same file as its definition -- you have to wrap the DEFREADTABLE |
---|
83 | form in an explicit EVAL-WHEN. |
---|
84 | |
---|
85 | On redefinition, the target readtable is made empty first before |
---|
86 | it's refilled according to the clauses. |
---|
87 | |
---|
88 | NIL, :STANDARD, :COMMON-LISP, :MODERN, and :CURRENT are |
---|
89 | preregistered readtable names." |
---|
90 | (check-type name symbol) |
---|
91 | (when (reserved-readtable-name-p name) |
---|
92 | (error "~A is the designator for a predefined readtable. ~ |
---|
93 | Not acceptable as a user-specified readtable name." name)) |
---|
94 | (flet ((process-option (option var) |
---|
95 | (destructure-case option |
---|
96 | ((:merge &rest readtable-designators) |
---|
97 | `(merge-readtables-into ,var ,@(mapcar #'(lambda (x) `',x) |
---|
98 | readtable-designators))) |
---|
99 | ((:fuse &rest readtable-designators) |
---|
100 | `(handler-bind ((reader-macro-conflict #'continue)) |
---|
101 | (merge-readtables-into ,var ,@(mapcar #'(lambda (x) `',x) |
---|
102 | readtable-designators)))) |
---|
103 | ;; alias for :FUSE |
---|
104 | ((:fuze &rest readtable-designators) |
---|
105 | `(handler-bind ((reader-macro-conflict #'continue)) |
---|
106 | (merge-readtables-into ,var ,@(mapcar #'(lambda (x) `',x) |
---|
107 | readtable-designators)))) |
---|
108 | ((:dispatch-macro-char disp-char sub-char function) |
---|
109 | `(set-dispatch-macro-character ,disp-char ,sub-char |
---|
110 | ,function ,var)) |
---|
111 | ((:macro-char char function &optional non-terminating-p) |
---|
112 | (if (eq function :dispatch) |
---|
113 | `(make-dispatch-macro-character ,char ,non-terminating-p ,var) |
---|
114 | `(set-macro-character ,char ,function |
---|
115 | ,non-terminating-p ,var))) |
---|
116 | ((:syntax-from from-rt-designator from-char to-char) |
---|
117 | `(set-syntax-from-char ,to-char ,from-char |
---|
118 | ,var (find-readtable ,from-rt-designator))) |
---|
119 | ((:case mode) |
---|
120 | `(setf (readtable-case ,var) ,mode)))) |
---|
121 | (remove-clauses (clauses options) |
---|
122 | (setq clauses (if (listp clauses) clauses (list clauses))) |
---|
123 | (remove-if-not #'(lambda (x) (member x clauses)) |
---|
124 | options :key #'first))) |
---|
125 | (let* ((merge-clauses (remove-clauses '(:merge :fuze :fuse) options)) |
---|
126 | (case-clauses (remove-clauses :case options)) |
---|
127 | (macro-clauses (remove-clauses '(:macro-char :dispatch-macro-char) |
---|
128 | options)) |
---|
129 | (syntax-clauses (remove-clauses :syntax-from options)) |
---|
130 | (other-clauses |
---|
131 | (set-difference options |
---|
132 | (append merge-clauses case-clauses |
---|
133 | macro-clauses syntax-clauses)))) |
---|
134 | (cond |
---|
135 | ((not (null other-clauses)) |
---|
136 | (error "Bogus DEFREADTABLE clauses: ~/PPRINT-LINEAR/" other-clauses)) |
---|
137 | (t |
---|
138 | `(eval-when (:load-toplevel :execute) |
---|
139 | ;; The (FIND-READTABLE ...) isqrt important for proper |
---|
140 | ;; redefinition semantics, as redefining has to modify the |
---|
141 | ;; already existing readtable object. |
---|
142 | (let ((readtable (find-readtable ',name))) |
---|
143 | (cond ((not readtable) |
---|
144 | (setq readtable (make-readtable ',name))) |
---|
145 | (t |
---|
146 | (setq readtable (%clear-readtable readtable)) |
---|
147 | (simple-style-warn |
---|
148 | "Overwriting already existing readtable ~S." |
---|
149 | readtable))) |
---|
150 | ,@(loop for option in merge-clauses |
---|
151 | collect (process-option option 'readtable)) |
---|
152 | ,@(loop for option in case-clauses |
---|
153 | collect (process-option option 'readtable)) |
---|
154 | ,@(loop for option in macro-clauses |
---|
155 | collect (process-option option 'readtable)) |
---|
156 | ,@(loop for option in syntax-clauses |
---|
157 | collect (process-option option 'readtable)) |
---|
158 | readtable))))))) |
---|
159 | |
---|
160 | (defmacro in-readtable (name) |
---|
161 | "Set *READTABLE* to the readtable referred to by the symbol NAME." |
---|
162 | (check-type name symbol) |
---|
163 | `(eval-when (:compile-toplevel :load-toplevel :execute) |
---|
164 | ;; NB. The :LOAD-TOPLEVEL is needed for cases like (DEFVAR *FOO* |
---|
165 | ;; (GET-MACRO-CHARACTER #\")) |
---|
166 | (setf *readtable* (ensure-readtable ',name)) |
---|
167 | (when (find-package :swank) |
---|
168 | (%frob-swank-readtable-alist *package* *readtable*)))) |
---|
169 | |
---|
170 | ;;; KLUDGE: [interim solution] |
---|
171 | ;;; |
---|
172 | ;;; We need support for this in Slime itself, because we want IN-READTABLE |
---|
173 | ;;; to work on a per-file basis, and not on a per-package basis. |
---|
174 | ;;; |
---|
175 | (defun %frob-swank-readtable-alist (package readtable) |
---|
176 | (let ((readtable-alist (find-symbol (string '#:*readtable-alist*) |
---|
177 | (find-package :swank)))) |
---|
178 | (when (boundp readtable-alist) |
---|
179 | (pushnew (cons (package-name package) readtable) |
---|
180 | (symbol-value readtable-alist) |
---|
181 | :test #'(lambda (entry1 entry2) |
---|
182 | (destructuring-bind (pkg-name1 . rt1) entry1 |
---|
183 | (destructuring-bind (pkg-name2 . rt2) entry2 |
---|
184 | (and (string= pkg-name1 pkg-name2) |
---|
185 | (eq rt1 rt2))))))))) |
---|
186 | |
---|
187 | (deftype readtable-designator () |
---|
188 | `(or null readtable)) |
---|
189 | |
---|
190 | (deftype named-readtable-designator () |
---|
191 | "Either a symbol or a readtable itself." |
---|
192 | `(or readtable-designator symbol)) |
---|
193 | |
---|
194 | ;;;;; Compiler macros |
---|
195 | |
---|
196 | ;;; Since the :STANDARD readtable is interned, and we can't enforce |
---|
197 | ;;; its immutability, we signal a style-warning for suspicious uses |
---|
198 | ;;; that may result in strange behaviour: |
---|
199 | |
---|
200 | ;;; Modifying the standard readtable would, obviously, lead to a |
---|
201 | ;;; propagation of this change to all places which use the :STANDARD |
---|
202 | ;;; readtable (and thus rendering this readtable to be non-standard, |
---|
203 | ;;; in fact.) |
---|
204 | (eval-when (:compile-toplevel :load-toplevel :execute) |
---|
205 | (defun constant-standard-readtable-expression-p (thing) |
---|
206 | (or (null thing) |
---|
207 | (eq thing :standard) |
---|
208 | (and (consp thing) |
---|
209 | (find thing |
---|
210 | '((find-readtable nil) |
---|
211 | (find-readtable :standard) |
---|
212 | (ensure-readtable nil) |
---|
213 | (ensure-readtable :standard)) |
---|
214 | :test #'equal)))) |
---|
215 | |
---|
216 | (defun signal-suspicious-registration-warning (name-expr readtable-expr) |
---|
217 | (when (constant-standard-readtable-expression-p readtable-expr) |
---|
218 | (simple-style-warn |
---|
219 | "Caution: ~<You're trying to register the :STANDARD readtable ~ |
---|
220 | under a new name ~S. As modification of the :STANDARD readtable ~ |
---|
221 | is not permitted, subsequent modification of ~S won't be ~ |
---|
222 | permitted either. You probably want to wrap COPY-READTABLE ~ |
---|
223 | around~@:>~% ~S" |
---|
224 | (list name-expr name-expr) readtable-expr)))) |
---|
225 | |
---|
226 | (define-compiler-macro register-readtable (&whole form name readtable) |
---|
227 | (signal-suspicious-registration-warning name readtable) |
---|
228 | form) |
---|
229 | |
---|
230 | (define-compiler-macro ensure-readtable (&whole form name &optional |
---|
231 | (default nil default-p)) |
---|
232 | (when default-p |
---|
233 | (signal-suspicious-registration-warning name default)) |
---|
234 | form) |
---|
235 | |
---|
236 | (declaim (special *standard-readtable* *empty-readtable*)) |
---|
237 | |
---|
238 | (define-api make-readtable |
---|
239 | (&optional (name nil name-supplied-p) &key merge) |
---|
240 | (&optional named-readtable-designator &key (:merge list) => readtable) |
---|
241 | "Creates and returns a new readtable under the specified |
---|
242 | NAME. |
---|
243 | |
---|
244 | MERGE takes a list of NAMED-READTABLE-DESIGNATORS and specifies the |
---|
245 | readtables the new readtable is created from. (See the :MERGE clause |
---|
246 | of DEFREADTABLE for details.) |
---|
247 | |
---|
248 | If MERGE is NIL, an empty readtable is used instead. |
---|
249 | |
---|
250 | If NAME is not given, an anonymous empty readtable is returned. |
---|
251 | |
---|
252 | Notes: |
---|
253 | |
---|
254 | An empty readtable is a readtable where each character's syntax is |
---|
255 | the same as in the _standard readtable_ except that each macro |
---|
256 | character has been made a constituent. Basically: whitespace stays |
---|
257 | whitespace, everything else is constituent." |
---|
258 | (cond ((not name-supplied-p) |
---|
259 | (copy-readtable *empty-readtable*)) |
---|
260 | ((reserved-readtable-name-p name) |
---|
261 | (error "~A is the designator for a predefined readtable. ~ |
---|
262 | Not acceptable as a user-specified readtable name." name)) |
---|
263 | ((let ((rt (find-readtable name))) |
---|
264 | (and rt (prog1 nil |
---|
265 | (cerror "Overwrite existing entry." |
---|
266 | 'readtable-does-already-exist :readtable-name name) |
---|
267 | ;; Explicitly unregister to make sure that we do |
---|
268 | ;; not hold on of any reference to RT. |
---|
269 | (unregister-readtable rt))))) |
---|
270 | (t (let ((result (apply #'merge-readtables-into |
---|
271 | ;; The first readtable specified in |
---|
272 | ;; the :merge list is taken as the |
---|
273 | ;; basis for all subsequent |
---|
274 | ;; (destructive!) modifications (and |
---|
275 | ;; hence it's copied.) |
---|
276 | (copy-readtable (if merge |
---|
277 | (ensure-readtable |
---|
278 | (first merge)) |
---|
279 | *empty-readtable*)) |
---|
280 | (rest merge)))) |
---|
281 | |
---|
282 | (register-readtable name result))))) |
---|
283 | |
---|
284 | (define-api rename-readtable |
---|
285 | (old-name new-name) |
---|
286 | (named-readtable-designator symbol => readtable) |
---|
287 | "Replaces the associated name of the readtable designated by |
---|
288 | OLD-NAME with NEW-NAME. If a readtable is already registered under |
---|
289 | NEW-NAME, an error of type READTABLE-DOES-ALREADY-EXIST is |
---|
290 | signaled." |
---|
291 | (when (find-readtable new-name) |
---|
292 | (cerror "Overwrite existing entry." |
---|
293 | 'readtable-does-already-exist :readtable-name new-name)) |
---|
294 | (let* ((readtable (ensure-readtable old-name)) |
---|
295 | (readtable-name (readtable-name readtable))) |
---|
296 | ;; We use the internal functions directly to omit repeated |
---|
297 | ;; type-checking. |
---|
298 | (%unassociate-name-from-readtable readtable-name readtable) |
---|
299 | (%unassociate-readtable-from-name readtable-name readtable) |
---|
300 | (%associate-name-with-readtable new-name readtable) |
---|
301 | (%associate-readtable-with-name new-name readtable) |
---|
302 | readtable)) |
---|
303 | |
---|
304 | (define-api merge-readtables-into |
---|
305 | (result-readtable &rest named-readtables) |
---|
306 | (named-readtable-designator &rest named-readtable-designator => readtable) |
---|
307 | "Copy the contents of each readtable in NAMED-READTABLES into |
---|
308 | RESULT-READTABLE. |
---|
309 | |
---|
310 | If a macro character appears in more than one of the readtables, |
---|
311 | i.e. if a conflict is discovered during the merge, an error of type |
---|
312 | READER-MACRO-CONFLICT is signaled." |
---|
313 | (flet ((merge-into (to from) |
---|
314 | (do-readtable ((char reader-fn non-terminating-p disp? table) from) |
---|
315 | (check-reader-macro-conflict from to char) |
---|
316 | (cond ((not disp?) |
---|
317 | (set-macro-character char reader-fn non-terminating-p to)) |
---|
318 | (t |
---|
319 | (ensure-dispatch-macro-character char non-terminating-p to) |
---|
320 | (loop for (subchar . subfn) in table do |
---|
321 | (check-reader-macro-conflict from to char subchar) |
---|
322 | (set-dispatch-macro-character char subchar |
---|
323 | subfn to))))) |
---|
324 | to)) |
---|
325 | (let ((result-table (ensure-readtable result-readtable))) |
---|
326 | (dolist (table (mapcar #'ensure-readtable named-readtables)) |
---|
327 | (merge-into result-table table)) |
---|
328 | result-table))) |
---|
329 | |
---|
330 | (defun ensure-dispatch-macro-character (char &optional non-terminating-p |
---|
331 | (readtable *readtable*)) |
---|
332 | (if (dispatch-macro-char-p char readtable) |
---|
333 | t |
---|
334 | (make-dispatch-macro-character char non-terminating-p readtable))) |
---|
335 | |
---|
336 | (define-api copy-named-readtable |
---|
337 | (named-readtable) |
---|
338 | (named-readtable-designator => readtable) |
---|
339 | "Like COPY-READTABLE but takes a NAMED-READTABLE-DESIGNATOR as argument." |
---|
340 | (copy-readtable (ensure-readtable named-readtable))) |
---|
341 | |
---|
342 | (define-api list-all-named-readtables () (=> list) |
---|
343 | "Returns a list of all registered readtables. The returned list is |
---|
344 | guaranteed to be fresh, but may contain duplicates." |
---|
345 | (mapcar #'ensure-readtable (%list-all-readtable-names))) |
---|
346 | |
---|
347 | |
---|
348 | (define-condition readtable-error (error) ()) |
---|
349 | |
---|
350 | (define-condition readtable-does-not-exist (readtable-error) |
---|
351 | ((readtable-name :initarg :readtable-name |
---|
352 | :initform (required-argument) |
---|
353 | :accessor missing-readtable-name |
---|
354 | :type named-readtable-designator)) |
---|
355 | (:report (lambda (condition stream) |
---|
356 | (format stream "A readtable named ~S does not exist." |
---|
357 | (missing-readtable-name condition))))) |
---|
358 | |
---|
359 | (define-condition readtable-does-already-exist (readtable-error) |
---|
360 | ((readtable-name :initarg :readtable-name |
---|
361 | :initform (required-argument) |
---|
362 | :accessor existing-readtable-name |
---|
363 | :type named-readtable-designator)) |
---|
364 | (:report (lambda (condition stream) |
---|
365 | (format stream "A readtable named ~S already exists." |
---|
366 | (existing-readtable-name condition)))) |
---|
367 | (:documentation "Continuable.")) |
---|
368 | |
---|
369 | (define-condition reader-macro-conflict (readtable-error) |
---|
370 | ((macro-char |
---|
371 | :initarg :macro-char |
---|
372 | :initform (required-argument) |
---|
373 | :accessor conflicting-macro-char |
---|
374 | :type character) |
---|
375 | (sub-char |
---|
376 | :initarg :sub-char |
---|
377 | :initform nil |
---|
378 | :accessor conflicting-dispatch-sub-char |
---|
379 | :type (or null character)) |
---|
380 | (from-readtable |
---|
381 | :initarg :from-readtable |
---|
382 | :initform (required-argument) |
---|
383 | :accessor from-readtable |
---|
384 | :type readtable) |
---|
385 | (to-readtable |
---|
386 | :initarg :to-readtable |
---|
387 | :initform (required-argument) |
---|
388 | :accessor to-readtable |
---|
389 | :type readtable)) |
---|
390 | (:report |
---|
391 | (lambda (condition stream) |
---|
392 | (format stream "~@<Reader macro conflict while trying to merge the ~ |
---|
393 | ~:[macro character~;dispatch macro characters~] ~ |
---|
394 | ~@C~@[ ~@C~] from ~A into ~A.~@:>" |
---|
395 | (conflicting-dispatch-sub-char condition) |
---|
396 | (conflicting-macro-char condition) |
---|
397 | (conflicting-dispatch-sub-char condition) |
---|
398 | (from-readtable condition) |
---|
399 | (to-readtable condition)))) |
---|
400 | (:documentation "Continuable. |
---|
401 | |
---|
402 | This condition is signaled during the merge process if a reader |
---|
403 | macro (be it a macro character or the sub character of a dispatch |
---|
404 | macro character) is present in the both source and the target |
---|
405 | readtable and the two respective reader macro functions differ.")) |
---|
406 | |
---|
407 | (defun check-reader-macro-conflict (from to char &optional subchar) |
---|
408 | (flet ((conflictp (from-fn to-fn) |
---|
409 | (assert from-fn () |
---|
410 | "Bug in readtable iterators or concurrent access?") |
---|
411 | (and to-fn (not (function= to-fn from-fn))))) |
---|
412 | (when (if subchar |
---|
413 | (conflictp (%get-dispatch-macro-character char subchar from) |
---|
414 | (%get-dispatch-macro-character char subchar to)) |
---|
415 | (conflictp (%get-macro-character char from) |
---|
416 | (%get-macro-character char to))) |
---|
417 | (cerror (format nil "Overwrite ~@C in ~A." char to) |
---|
418 | 'reader-macro-conflict |
---|
419 | :from-readtable from |
---|
420 | :to-readtable to |
---|
421 | :macro-char char |
---|
422 | :sub-char subchar)))) |
---|
423 | |
---|
424 | |
---|
425 | ;;; Although there is no way to get at the standard readtable in |
---|
426 | ;;; Common Lisp (cf. /standard readtable/, CLHS glossary), we make |
---|
427 | ;;; up the perception of its existence by interning a copy of it. |
---|
428 | ;;; |
---|
429 | ;;; We do this for reverse lookup (cf. READTABLE-NAME), i.e. for |
---|
430 | ;;; |
---|
431 | ;;; (equal (readtable-name (find-readtable :standard)) "STANDARD") |
---|
432 | ;;; |
---|
433 | ;;; holding true. |
---|
434 | ;;; |
---|
435 | ;;; We, however, inherit the restriction that the :STANDARD |
---|
436 | ;;; readtable _must not be modified_ (cf. CLHS 2.1.1.2), although it'd |
---|
437 | ;;; technically be feasible (as *STANDARD-READTABLE* will contain a |
---|
438 | ;;; mutable copy of the implementation-internal standard readtable.) |
---|
439 | ;;; We cannot enforce this restriction without shadowing |
---|
440 | ;;; CL:SET-MACRO-CHARACTER and CL:SET-DISPATCH-MACRO-FUNCTION which |
---|
441 | ;;; is out of scope of this library, though. So we just threaten |
---|
442 | ;;; with nasal demons. |
---|
443 | ;;; |
---|
444 | (defvar *standard-readtable* |
---|
445 | (%standard-readtable)) |
---|
446 | |
---|
447 | (defvar *empty-readtable* |
---|
448 | (%clear-readtable (copy-readtable nil))) |
---|
449 | |
---|
450 | (defvar *case-preserving-standard-readtable* |
---|
451 | (let ((readtable (copy-readtable nil))) |
---|
452 | (setf (readtable-case readtable) :preserve) |
---|
453 | readtable)) |
---|
454 | |
---|
455 | (defparameter *reserved-readtable-names* |
---|
456 | '(nil :standard :common-lisp :modern :current)) |
---|
457 | |
---|
458 | (defun reserved-readtable-name-p (name) |
---|
459 | (and (member name *reserved-readtable-names*) t)) |
---|
460 | |
---|
461 | ;;; In principle, we could DEFREADTABLE some of these. But we do |
---|
462 | ;;; reserved readtable lookup seperately, since we can't register a |
---|
463 | ;;; readtable for :CURRENT anyway. |
---|
464 | |
---|
465 | (defun find-reserved-readtable (reserved-name) |
---|
466 | (cond ((eq reserved-name nil) *standard-readtable*) |
---|
467 | ((eq reserved-name :standard) *standard-readtable*) |
---|
468 | ((eq reserved-name :common-lisp) *standard-readtable*) |
---|
469 | ((eq reserved-name :modern) *case-preserving-standard-readtable*) |
---|
470 | ((eq reserved-name :current) *readtable*) |
---|
471 | (t (error "Bug: no such reserved readtable: ~S" reserved-name)))) |
---|
472 | |
---|
473 | (define-api find-readtable |
---|
474 | (name) |
---|
475 | (named-readtable-designator => (or readtable null)) |
---|
476 | "Looks for the readtable specified by NAME and returns it if it is |
---|
477 | found. Returns NIL otherwise." |
---|
478 | (cond ((readtablep name) name) |
---|
479 | ((reserved-readtable-name-p name) |
---|
480 | (find-reserved-readtable name)) |
---|
481 | ((%find-readtable name)))) |
---|
482 | |
---|
483 | ;;; FIXME: This doesn't take a NAMED-READTABLE-DESIGNATOR, but only a |
---|
484 | ;;; STRING-DESIGNATOR. (When fixing, heed interplay with compiler |
---|
485 | ;;; macros below.) |
---|
486 | (defsetf find-readtable register-readtable) |
---|
487 | |
---|
488 | (define-api ensure-readtable |
---|
489 | (name &optional (default nil default-p)) |
---|
490 | (named-readtable-designator &optional (or named-readtable-designator null) |
---|
491 | => readtable) |
---|
492 | "Looks up the readtable specified by NAME and returns it if it's found. |
---|
493 | If it is not found, it registers the readtable designated by DEFAULT |
---|
494 | under the name represented by NAME; or if no default argument is |
---|
495 | given, it signals an error of type READTABLE-DOES-NOT-EXIST |
---|
496 | instead." |
---|
497 | (cond ((find-readtable name)) |
---|
498 | ((not default-p) |
---|
499 | (error 'readtable-does-not-exist :readtable-name name)) |
---|
500 | (t (setf (find-readtable name) (ensure-readtable default))))) |
---|
501 | |
---|
502 | |
---|
503 | (define-api register-readtable |
---|
504 | (name readtable) |
---|
505 | (symbol readtable => readtable) |
---|
506 | "Associate READTABLE with NAME. Returns the readtable." |
---|
507 | (assert (typep name '(not (satisfies reserved-readtable-name-p)))) |
---|
508 | (%associate-readtable-with-name name readtable) |
---|
509 | (%associate-name-with-readtable name readtable) |
---|
510 | readtable) |
---|
511 | |
---|
512 | (define-api unregister-readtable |
---|
513 | (named-readtable) |
---|
514 | (named-readtable-designator => boolean) |
---|
515 | "Remove the association of NAMED-READTABLE. Returns T if successfull, |
---|
516 | NIL otherwise." |
---|
517 | (let* ((readtable (find-readtable named-readtable)) |
---|
518 | (readtable-name (and readtable (readtable-name readtable)))) |
---|
519 | (if (not readtable-name) |
---|
520 | nil |
---|
521 | (prog1 t |
---|
522 | (check-type readtable-name |
---|
523 | (not (satisfies reserved-readtable-name-p))) |
---|
524 | (%unassociate-readtable-from-name readtable-name readtable) |
---|
525 | (%unassociate-name-from-readtable readtable-name readtable))))) |
---|
526 | |
---|
527 | (define-api readtable-name |
---|
528 | (named-readtable) |
---|
529 | (named-readtable-designator => symbol) |
---|
530 | "Returns the name of the readtable designated by NAMED-READTABLE, |
---|
531 | or NIL." |
---|
532 | (let ((readtable (ensure-readtable named-readtable))) |
---|
533 | (cond ((%readtable-name readtable)) |
---|
534 | ((eq readtable *readtable*) :current) |
---|
535 | ((eq readtable *standard-readtable*) :common-lisp) |
---|
536 | ((eq readtable *case-preserving-standard-readtable*) :modern) |
---|
537 | (t nil)))) |
---|