1 ;;; -*- mode: lisp; syntax: common-lisp; coding: utf-8; package: cl-user; -*-
5 (defpackage #:counted-feature-tests
9 #:disable-counted-feature-tests-syntax
10 #:locally-disable-counted-feature-tests-syntax
11 #:enable-counted-feature-tests-syntax
12 #:locally-enable-counted-feature-tests-syntax
13 #:file-enable-counted-feature-tests-syntax
14 #:restore-counted-feature-tests-syntax-state))
16 (in-package #:counted-feature-tests)
18 ;;; the goal is to enable #+ and #- to ignore multiple expressions by
19 ;;; supplying an additional numeric prefix
21 ;;; zero is kind of pointless, but would be accepted, possibly with a
22 ;;; warning, one is the default
24 ;;; note that you can't enter a negative value in the first place, so that
27 ;;; discarding all READ forms until the end of file should probably not
28 ;;; be mashed together with this, as it's not an extension of the
29 ;;; otherwise fairly obvious semantics of this extension
31 ;;; since the number of discarded expressions must exactly match the
32 ;;; numeric prefix, #999+(or) can not be used to achieve this
35 ;;; as a prerequisite, let us define a function for evaluating feature
36 ;;; expressions - should probably be a standard function as well
38 ;;; (featurep 'x) then means roughly the same as #+x T
40 ;; sbcl/src/code/early-extensions.lisp:963 has the implementation for SBCL
41 ;; slightly changed for portability, i.e. MEMQ to MEMBER
43 ;;; If X is a symbol, see whether it is present in *FEATURES*. Also
44 ;;; handle arbitrary combinations of atoms using NOT, AND, OR.
52 (error "too many subexpressions in feature expression: ~S" x))
54 (error "too few subexpressions in feature expression: ~S" x))
55 (t (not (featurep (cadr x))))))
56 ((:and and) (every #'featurep (cdr x)))
57 ((:or or) (some #'featurep (cdr x)))
59 (error "unknown operator in feature expression: ~S" x))))
60 (symbol (not (null (member x *features* :test #'eq))))
62 (error "invalid feature expression: ~S" x))))
64 ;;; now we can use this to implement the two reader macros
65 ;;; implementation-independently
67 ;; sbcl/src/code/sharpm.lisp:354 has the implementation for SBCL
68 ;; slightly changed, i.e. uses FIND-PACKAGE
69 ;; furthermore it now just READ and discards N times instead of once
71 ;;;; conditional compilation: the #+ and #- readmacros
73 (flet ((guts (stream count not-p)
74 (unless (if (let ((*package* #.(find-package '#:keyword))
75 (*read-suppress* nil))
76 (featurep (read stream t nil t)))
79 (let ((*read-suppress* t))
80 (dotimes (i (or count 1))
81 (read stream t nil t))))
84 (defun sharp-plus (stream sub-char numarg)
85 (declare (ignore sub-char))
86 (guts stream numarg nil))
88 (defun sharp-minus (stream sub-char numarg)
89 (declare (ignore sub-char))
90 (guts stream numarg t)))
92 (defvar *original-readtable* NIL)
94 (defvar *restore-counted-feature-tests-syntax* NIL)
96 (defmacro disable-counted-feature-tests-syntax ()
97 '(eval-when (:compile-toplevel :load-toplevel :execute)
98 (setf *restore-reader-syntax* NIL)
99 (%disable-counted-feature-tests-syntax)))
101 (defmacro locally-disable-counted-feature-tests-syntax ()
102 '(eval-when (:compile-toplevel :load-toplevel :execute)
103 (%disable-counted-feature-tests-syntax)))
105 (defun %disable-counted-feature-tests-syntax ()
106 (when *original-readtable*
107 (setf *readtable* *original-readtable*
108 *original-readtable* NIL))
111 (defmacro enable-counted-feature-tests-syntax ()
112 '(eval-when (:compile-toplevel :load-toplevel :execute)
113 (setf *restore-reader-syntax* T)
114 (%enable-counted-feature-tests-syntax)))
116 (defmacro locally-enable-counted-feature-tests-syntax ()
117 '(eval-when (:compile-toplevel :load-toplevel :execute)
118 (%enable-counted-feature-tests-syntax)))
120 (defmacro file-enable-counted-feature-tests-syntax ()
121 '(eval-when (:compile-toplevel :load-toplevel :execute)
122 (%enable-counted-feature-tests-syntax NIL)))
124 (defun %enable-counted-feature-tests-syntax (&optional (save-original-p T))
125 (when save-original-p
126 (setf *original-readtable* (copy-readtable)))
127 (when (or (not save-original-p) *original-readtable*)
128 (setf *readtable* (copy-readtable))
129 (set-dispatch-macro-character #\# #\+ #'sharp-plus)
130 (set-dispatch-macro-character #\# #\- #'sharp-minus))
133 (defmacro restore-counted-feature-tests-syntax-state ()
134 '(eval-when (:compile-toplevel :load-toplevel :execute)
135 (if *restore-counted-feature-tests-syntax*
136 (%enable-counted-feature-tests-syntax)
137 (%disable-counted-feature-tests-syntax))))
139 ;;; which is then used like follows, #- is of course analogous
142 ;; :a-keyword-argument and-its-value