source: trunk/abcl/contrib/named-readtables/src/named-readtables.lisp

Last change on this file was 15019, checked in by Mark Evenson, 7 years ago

abcl-contrib: add NAMED-READTABLES

From <https://github.com/melisgl/named-readtables>.

c.f. <https://github.com/melisgl/named-readtables/issues/10>

File size: 21.9 KB
Line 
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))))
Note: See TracBrowser for help on using the repository browser.