clucumber 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +1 -1
- data/README.md +4 -9
- data/lib/clucumber/clucumber-bootstrap.lisp +32 -0
- data/lib/clucumber/vendor/cl-interpol/alias.lisp +55 -0
- data/lib/clucumber/vendor/cl-interpol/cl-interpol.asd +56 -0
- data/lib/clucumber/vendor/cl-interpol/load.lisp +53 -0
- data/lib/clucumber/vendor/cl-interpol/packages.lisp +40 -0
- data/lib/clucumber/vendor/cl-interpol/read.lisp +716 -0
- data/lib/clucumber/vendor/cl-interpol/specials.lisp +113 -0
- data/lib/clucumber/vendor/cl-interpol/test/packages.lisp +34 -0
- data/lib/clucumber/vendor/cl-interpol/test/tests.lisp +128 -0
- data/lib/clucumber/vendor/cl-interpol/test.lisp +177 -0
- data/lib/clucumber/vendor/cl-interpol/test2.lisp +6254 -0
- data/lib/clucumber/vendor/cl-interpol/unicode.lisp +13912 -0
- data/lib/clucumber/vendor/cl-interpol/util.lisp +122 -0
- data/lib/clucumber/vendor/cl-ppcre/api.lisp +1262 -0
- data/lib/clucumber/vendor/cl-ppcre/charmap.lisp +152 -0
- data/lib/clucumber/vendor/cl-ppcre/charset.lisp +242 -0
- data/lib/clucumber/vendor/cl-ppcre/chartest.lisp +98 -0
- data/lib/clucumber/vendor/cl-ppcre/cl-ppcre-test.asd +34 -0
- data/lib/clucumber/vendor/cl-ppcre/cl-ppcre-unicode/packages.lisp +38 -0
- data/lib/clucumber/vendor/cl-ppcre/cl-ppcre-unicode/resolver.lisp +61 -0
- data/lib/clucumber/vendor/cl-ppcre/cl-ppcre-unicode.asd +58 -0
- data/lib/clucumber/vendor/cl-ppcre/cl-ppcre.asd +79 -0
- data/lib/clucumber/vendor/cl-ppcre/closures.lisp +471 -0
- data/lib/clucumber/vendor/cl-ppcre/convert.lisp +875 -0
- data/lib/clucumber/vendor/cl-ppcre/errors.lisp +84 -0
- data/lib/clucumber/vendor/cl-ppcre/lexer.lisp +737 -0
- data/lib/clucumber/vendor/cl-ppcre/lispworks-defsystem.lisp +57 -0
- data/lib/clucumber/vendor/cl-ppcre/load.lisp +67 -0
- data/lib/clucumber/vendor/cl-ppcre/optimize.lisp +578 -0
- data/lib/clucumber/vendor/cl-ppcre/packages.lisp +68 -0
- data/lib/clucumber/vendor/cl-ppcre/parser.lisp +319 -0
- data/lib/clucumber/vendor/cl-ppcre/ppcre-tests.lisp +269 -0
- data/lib/clucumber/vendor/cl-ppcre/regex-class-util.lisp +555 -0
- data/lib/clucumber/vendor/cl-ppcre/regex-class.lisp +259 -0
- data/lib/clucumber/vendor/cl-ppcre/repetition-closures.lisp +833 -0
- data/lib/clucumber/vendor/cl-ppcre/scanner.lisp +506 -0
- data/lib/clucumber/vendor/cl-ppcre/specials.lisp +172 -0
- data/lib/clucumber/vendor/cl-ppcre/test/packages.lisp +37 -0
- data/lib/clucumber/vendor/cl-ppcre/test/perl-tests.lisp +150 -0
- data/lib/clucumber/vendor/cl-ppcre/test/tests.lisp +159 -0
- data/lib/clucumber/vendor/cl-ppcre/test/unicode-tests.lisp +80 -0
- data/lib/clucumber/vendor/cl-ppcre/util.lisp +201 -0
- data/lib/clucumber/vendor/cl-unicode/alias.lisp +135 -0
- data/lib/clucumber/vendor/cl-unicode/api.lisp +412 -0
- data/lib/clucumber/vendor/cl-unicode/build/char-info.lisp +133 -0
- data/lib/clucumber/vendor/cl-unicode/build/dump.lisp +239 -0
- data/lib/clucumber/vendor/cl-unicode/build/read.lisp +280 -0
- data/lib/clucumber/vendor/cl-unicode/build/util.lisp +182 -0
- data/lib/clucumber/vendor/cl-unicode/cl-unicode.asd +90 -0
- data/lib/clucumber/vendor/cl-unicode/conditions.lisp +54 -0
- data/lib/clucumber/vendor/cl-unicode/derived.lisp +120 -0
- data/lib/clucumber/vendor/cl-unicode/hash-tables.lisp +20 -0
- data/lib/clucumber/vendor/cl-unicode/lists.lisp +10 -0
- data/lib/clucumber/vendor/cl-unicode/methods.lisp +15 -0
- data/lib/clucumber/vendor/cl-unicode/packages.lisp +76 -0
- data/lib/clucumber/vendor/cl-unicode/specials.lisp +135 -0
- data/lib/clucumber/vendor/cl-unicode/test/packages.lisp +34 -0
- data/lib/clucumber/vendor/cl-unicode/test/tests.lisp +150 -0
- data/lib/clucumber/vendor/cl-unicode/test-functions.lisp +94 -0
- data/lib/clucumber/vendor/cl-unicode/util.lisp +274 -0
- data/lib/clucumber/vendor/lift/compare/fiveam-tests.lisp +14 -0
- data/lib/clucumber/vendor/lift/compare/lift-tests.lisp +13 -0
- data/lib/clucumber/vendor/lift/compare/rt-tests.lisp +16 -0
- data/lib/clucumber/vendor/lift/compare/xlunit-tests.lisp +15 -0
- data/lib/clucumber/vendor/lift/dev/changes.lisp +61 -0
- data/lib/clucumber/vendor/lift/dev/config.lisp +354 -0
- data/lib/clucumber/vendor/lift/dev/copy-file.lisp +117 -0
- data/lib/clucumber/vendor/lift/dev/introspection.lisp +232 -0
- data/lib/clucumber/vendor/lift/dev/lift-interface.lisp +56 -0
- data/lib/clucumber/vendor/lift/dev/lift-notes.lisp +202 -0
- data/lib/clucumber/vendor/lift/dev/lift-randomized.lisp +45 -0
- data/lib/clucumber/vendor/lift/dev/lift.lisp +2383 -0
- data/lib/clucumber/vendor/lift/dev/macros.lisp +229 -0
- data/lib/clucumber/vendor/lift/dev/measuring.lisp +156 -0
- data/lib/clucumber/vendor/lift/dev/packages.lisp +161 -0
- data/lib/clucumber/vendor/lift/dev/port.lisp +151 -0
- data/lib/clucumber/vendor/lift/dev/prototypes.lisp +282 -0
- data/lib/clucumber/vendor/lift/dev/random-testing.lisp +124 -0
- data/lib/clucumber/vendor/lift/dev/report-locations.lisp +13 -0
- data/lib/clucumber/vendor/lift/dev/reports.lisp +916 -0
- data/lib/clucumber/vendor/lift/dev/utilities.lisp +242 -0
- data/lib/clucumber/vendor/lift/docs/package.lisp +6 -0
- data/lib/clucumber/vendor/lift/docs/setup.lisp +17 -0
- data/lib/clucumber/vendor/lift/examples/basic-examples.lisp +289 -0
- data/lib/clucumber/vendor/lift/examples/random-testing.lisp +32 -0
- data/lib/clucumber/vendor/lift/lift-documentation.asd +28 -0
- data/lib/clucumber/vendor/lift/lift-test.asd +35 -0
- data/lib/clucumber/vendor/lift/lift.asd +77 -0
- data/lib/clucumber/vendor/lift/test/equality-tests.lisp +40 -0
- data/lib/clucumber/vendor/lift/test/finding-tests.lisp +32 -0
- data/lib/clucumber/vendor/lift/test/lift-test.lisp +783 -0
- data/lib/clucumber/vendor/lift/test/order-of-operations.lisp +54 -0
- data/lib/clucumber/vendor/lift/test/packages.lisp +17 -0
- data/lib/clucumber/vendor/lift/test/test-config-files.lisp +20 -0
- data/lib/clucumber/vendor/lift/test/test-dynamic-variables.lisp +57 -0
- data/lib/clucumber/vendor/lift/test/test-maximum-problems.lisp +74 -0
- data/lib/clucumber/vendor/lift/test/test-prototypes.lisp +278 -0
- data/lib/clucumber/vendor/lift/test/test-timeout.lisp +37 -0
- data/lib/clucumber/vendor/lift/test/tests-in-progress.lisp +62 -0
- data/lib/clucumber/vendor/lift/test/testsuite-expects.lisp +60 -0
- data/lib/clucumber/vendor/lift/timeout/package.lisp +13 -0
- data/lib/clucumber/vendor/lift/timeout/with-timeout.lisp +123 -0
- data/lib/clucumber/vendor/lift/website/stuff/Temp.lisp +34 -0
- data/lib/clucumber/vendor/st-json/st-json.asd +3 -0
- data/lib/clucumber/vendor/st-json/st-json.lisp +310 -0
- data/lib/clucumber/vendor/trivial-backtrace/dev/backtrace.lisp +127 -0
- data/lib/clucumber/vendor/trivial-backtrace/dev/fallback.lisp +10 -0
- data/lib/clucumber/vendor/trivial-backtrace/dev/map-backtrace.lisp +103 -0
- data/lib/clucumber/vendor/trivial-backtrace/dev/mucking.lisp +75 -0
- data/lib/clucumber/vendor/trivial-backtrace/dev/packages.lisp +13 -0
- data/lib/clucumber/vendor/trivial-backtrace/dev/utilities.lisp +104 -0
- data/lib/clucumber/vendor/trivial-backtrace/test/packages.lisp +5 -0
- data/lib/clucumber/vendor/trivial-backtrace/test/test-setup.lisp +4 -0
- data/lib/clucumber/vendor/trivial-backtrace/test/tests.lisp +16 -0
- data/lib/clucumber/vendor/trivial-backtrace/trivial-backtrace-test.asd +22 -0
- data/lib/clucumber/vendor/trivial-backtrace/trivial-backtrace.asd +35 -0
- data/lib/clucumber/vendor/usocket/backend/allegro.lisp +199 -0
- data/lib/clucumber/vendor/usocket/backend/armedbear.lisp +491 -0
- data/lib/clucumber/vendor/usocket/backend/clisp.lisp +260 -0
- data/lib/clucumber/vendor/usocket/backend/cmucl.lisp +266 -0
- data/lib/clucumber/vendor/usocket/backend/lispworks.lisp +741 -0
- data/lib/clucumber/vendor/usocket/backend/mcl.lisp +369 -0
- data/lib/clucumber/vendor/usocket/backend/openmcl.lisp +206 -0
- data/lib/clucumber/vendor/usocket/backend/sbcl.lisp +424 -0
- data/lib/clucumber/vendor/usocket/backend/scl.lisp +261 -0
- data/lib/clucumber/vendor/usocket/condition.lisp +227 -0
- data/lib/clucumber/vendor/usocket/package.lisp +82 -0
- data/lib/clucumber/vendor/usocket/server.lisp +45 -0
- data/lib/clucumber/vendor/usocket/test/package.lisp +13 -0
- data/lib/clucumber/vendor/usocket/test/test-usocket.lisp +166 -0
- data/lib/clucumber/vendor/usocket/usocket-test.asd +26 -0
- data/lib/clucumber/vendor/usocket/usocket.asd +37 -0
- data/lib/clucumber/vendor/usocket/usocket.lisp +542 -0
- data/lib/clucumber/vendor/usocket/vendor/kqueue.lisp +1 -0
- data/lib/clucumber/vendor/usocket/vendor/split-sequence.lisp +245 -0
- data/lib/clucumber.rb +29 -7
- metadata +151 -5
|
@@ -0,0 +1,716 @@
|
|
|
1
|
+
;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-INTERPOL; Base: 10 -*-
|
|
2
|
+
;;; $Header: /usr/local/cvsrep/cl-interpol/read.lisp,v 1.31 2008/07/23 15:13:08 edi Exp $
|
|
3
|
+
|
|
4
|
+
;;; Copyright (c) 2003-2008, Dr. Edmund Weitz. All rights reserved.
|
|
5
|
+
|
|
6
|
+
;;; Redistribution and use in source and binary forms, with or without
|
|
7
|
+
;;; modification, are permitted provided that the following conditions
|
|
8
|
+
;;; are met:
|
|
9
|
+
|
|
10
|
+
;;; * Redistributions of source code must retain the above copyright
|
|
11
|
+
;;; notice, this list of conditions and the following disclaimer.
|
|
12
|
+
|
|
13
|
+
;;; * Redistributions in binary form must reproduce the above
|
|
14
|
+
;;; copyright notice, this list of conditions and the following
|
|
15
|
+
;;; disclaimer in the documentation and/or other materials
|
|
16
|
+
;;; provided with the distribution.
|
|
17
|
+
|
|
18
|
+
;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
|
|
19
|
+
;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
20
|
+
;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
21
|
+
;;; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
22
|
+
;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
23
|
+
;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
|
24
|
+
;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
25
|
+
;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
26
|
+
;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
27
|
+
;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
28
|
+
;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
29
|
+
|
|
30
|
+
(in-package :cl-interpol)
|
|
31
|
+
|
|
32
|
+
(defun read-while (predicate &key max)
|
|
33
|
+
"Reads characters from *STREAM* while PREDICATE returns a true value
|
|
34
|
+
for each character. Returns at most MAX characters if MAX is true."
|
|
35
|
+
(when (eql max 0)
|
|
36
|
+
(return-from read-while ""))
|
|
37
|
+
(let ((collector (make-collector)))
|
|
38
|
+
(loop for count of-type fixnum from 1
|
|
39
|
+
for c = (peek-char*)
|
|
40
|
+
while (and (or (not max)
|
|
41
|
+
(<= count max))
|
|
42
|
+
c
|
|
43
|
+
(funcall predicate c))
|
|
44
|
+
do (vector-push-extend (read-char*) collector)
|
|
45
|
+
finally (return collector))))
|
|
46
|
+
|
|
47
|
+
(declaim (inline get-number))
|
|
48
|
+
(defun get-number (&key (radix 10) max)
|
|
49
|
+
"Reads and consumes the number *STREAM* is currently looking at and
|
|
50
|
+
returns it. Returns NIL if no number could be identified. RADIX is
|
|
51
|
+
used as in PARSE-INTEGER. If MAX is not NIL we'll read at most the
|
|
52
|
+
next MAX characters."
|
|
53
|
+
(parse-integer (read-while (lambda (c)
|
|
54
|
+
(digit-char-p c radix))
|
|
55
|
+
:max max)
|
|
56
|
+
:radix radix
|
|
57
|
+
:junk-allowed t))
|
|
58
|
+
|
|
59
|
+
(defun resolve-unicode-name (name)
|
|
60
|
+
"Tries to return a character which was encoded as \\N<NAME>."
|
|
61
|
+
(or (character-named name)
|
|
62
|
+
(gethash (canonicalize-name name) *unicode-aliases*)))
|
|
63
|
+
|
|
64
|
+
(defun get-char-from-unicode-name ()
|
|
65
|
+
"Parses and returns a named character after \"\\N\" has already been
|
|
66
|
+
read. This function reads from *STREAM*."
|
|
67
|
+
(let ((next-char (read-char*)))
|
|
68
|
+
(unless (char= next-char #\{)
|
|
69
|
+
(signal-reader-error "Expected { after \\N"))
|
|
70
|
+
(let ((name (read-while (lambda (c)
|
|
71
|
+
(and (char/= c #\})
|
|
72
|
+
(char/= c *term-char*))))))
|
|
73
|
+
(let ((next-char (read-char*)))
|
|
74
|
+
(unless (char= next-char #\})
|
|
75
|
+
(signal-reader-error "Expected } after Unicode character name")))
|
|
76
|
+
(or (resolve-unicode-name name)
|
|
77
|
+
(signal-reader-error "Could not find character with name '~A'"
|
|
78
|
+
name)))))
|
|
79
|
+
|
|
80
|
+
(defun unescape-char (regex-mode)
|
|
81
|
+
"Convert the characters(s) on *STREAM* following a backslash into a
|
|
82
|
+
character which is returned. This function is to be called when the
|
|
83
|
+
backslash has already been consumed."
|
|
84
|
+
(let ((chr (read-char*)))
|
|
85
|
+
;; certain escape sequences are left as is when in regex mode
|
|
86
|
+
(when (or (and (eq regex-mode :in-char-class)
|
|
87
|
+
(find chr "pPwWsSdD" :test #'char=))
|
|
88
|
+
(and (eq regex-mode t)
|
|
89
|
+
(find chr "kpPwWsSdDbBAZz" :test #'char=)))
|
|
90
|
+
(return-from unescape-char
|
|
91
|
+
(concatenate 'string "\\" (string chr))))
|
|
92
|
+
(let ((result
|
|
93
|
+
(case chr
|
|
94
|
+
((#\N)
|
|
95
|
+
;; named Unicode chars
|
|
96
|
+
(get-char-from-unicode-name))
|
|
97
|
+
((#\c)
|
|
98
|
+
;; \cx means control-x
|
|
99
|
+
(when (char= (peek-char*) *term-char*)
|
|
100
|
+
(signal-reader-error "String ended after \\c"))
|
|
101
|
+
(code-char (logxor #x40
|
|
102
|
+
(char-code (char-upcase (read-char*))))))
|
|
103
|
+
((#\x)
|
|
104
|
+
(cond ((char= (peek-char*) #\{)
|
|
105
|
+
;; "wide" hex char, i.e. hexadecimal number is
|
|
106
|
+
;; enclosed in curly brackets
|
|
107
|
+
(read-char*)
|
|
108
|
+
(prog1
|
|
109
|
+
(let ((code (or (get-number :radix 16)
|
|
110
|
+
;; allow for empty string
|
|
111
|
+
0)))
|
|
112
|
+
(or (and (< code char-code-limit)
|
|
113
|
+
(code-char code))
|
|
114
|
+
(signal-reader-error
|
|
115
|
+
"No character for char-code #x~X" code)))
|
|
116
|
+
(unless (char= (peek-char*) #\})
|
|
117
|
+
(signal-reader-error "Expected } after hex code"))
|
|
118
|
+
(read-char*)))
|
|
119
|
+
(t
|
|
120
|
+
;; \x should be followed by a hexadecimal char
|
|
121
|
+
;; code, two digits or less; note that it is
|
|
122
|
+
;; OK if \x is followed by zero digits
|
|
123
|
+
(make-char-from-code (get-number :radix 16 :max 2)))))
|
|
124
|
+
((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)
|
|
125
|
+
(cond ((and (eq regex-mode t)
|
|
126
|
+
(char/= chr #\0))
|
|
127
|
+
;; leave as is if we're in regex mode (and not
|
|
128
|
+
;; within in a character class)
|
|
129
|
+
(concatenate 'string "\\" (string chr)))
|
|
130
|
+
((or (char= chr #\8)
|
|
131
|
+
(char= chr #\9))
|
|
132
|
+
;; outside of regex mode "\8" is "8" (in regex
|
|
133
|
+
;; mode it is read like "\08"...)
|
|
134
|
+
chr)
|
|
135
|
+
(t
|
|
136
|
+
(unread-char chr *stream*)
|
|
137
|
+
;; now \x should be followed by an octal char
|
|
138
|
+
;; code, three digits or less
|
|
139
|
+
(make-char-from-code (get-number :radix 8 :max 3)))))
|
|
140
|
+
;; the following five character names are
|
|
141
|
+
;; 'semi-standard' according to the CLHS but I'm not
|
|
142
|
+
;; aware of any implementation that doesn't implement
|
|
143
|
+
;; them
|
|
144
|
+
((#\t)
|
|
145
|
+
#\Tab)
|
|
146
|
+
((#\n)
|
|
147
|
+
#\Newline)
|
|
148
|
+
((#\r)
|
|
149
|
+
#\Return)
|
|
150
|
+
((#\f)
|
|
151
|
+
#\Page)
|
|
152
|
+
((#\b)
|
|
153
|
+
#\Backspace)
|
|
154
|
+
((#\a)
|
|
155
|
+
(code-char 7)) ; ASCII bell
|
|
156
|
+
((#\e)
|
|
157
|
+
(code-char 27)) ; ASCII escape
|
|
158
|
+
(otherwise
|
|
159
|
+
;; all other characters aren't affected by a backslash
|
|
160
|
+
chr))))
|
|
161
|
+
(cond ((and (characterp result)
|
|
162
|
+
;; some characters must be 'protected' from CL-PPCRE
|
|
163
|
+
(or (and (eq regex-mode :in-char-class)
|
|
164
|
+
(find result "\\^[]-" :test #'char=))
|
|
165
|
+
(and (eq regex-mode t)
|
|
166
|
+
(find result "\\^[]-.$|()*+?" :test #'char=))))
|
|
167
|
+
(concatenate 'string "\\" (string result)))
|
|
168
|
+
(t result)))))
|
|
169
|
+
|
|
170
|
+
(declaim (inline normal-name-char-p)
|
|
171
|
+
(inline never-name-char-p))
|
|
172
|
+
|
|
173
|
+
(defun normal-name-char-p (c)
|
|
174
|
+
(and c (or (alphanumericp c)
|
|
175
|
+
(member c '(#\_ #\- #\+ #\*)))))
|
|
176
|
+
|
|
177
|
+
(defun never-name-char-p (c)
|
|
178
|
+
(or (not c)
|
|
179
|
+
(get-macro-character c)
|
|
180
|
+
(member c '(#\$ #\@))))
|
|
181
|
+
|
|
182
|
+
(defvar quell-warnings-form
|
|
183
|
+
#+sbcl '(declare (optimize (sb-ext:inhibit-warnings 3)))
|
|
184
|
+
#-sbcl nil
|
|
185
|
+
"A declaration form to quiet warnings about unbound variables
|
|
186
|
+
within a lexical environment.")
|
|
187
|
+
|
|
188
|
+
(defun read-longest-name ()
|
|
189
|
+
(coerce
|
|
190
|
+
(loop until (never-name-char-p (peek-char nil *stream* nil nil t))
|
|
191
|
+
collect (read-char*))
|
|
192
|
+
'string))
|
|
193
|
+
|
|
194
|
+
(defun read-optional-delimited ()
|
|
195
|
+
"Read the stuff following an optional delimiter, returning a form
|
|
196
|
+
that tries to deal correctly with lexical variables."
|
|
197
|
+
(flet ((try-pos (name i form)
|
|
198
|
+
(let ((ostr (gensym)))
|
|
199
|
+
`(handler-case
|
|
200
|
+
(with-output-to-string (,ostr)
|
|
201
|
+
(princ ,(read-from-string (subseq name 0 i)) ,ostr)
|
|
202
|
+
(princ ,(subseq name i) ,ostr)
|
|
203
|
+
,ostr)
|
|
204
|
+
(unbound-variable () ,form)))))
|
|
205
|
+
|
|
206
|
+
(loop
|
|
207
|
+
with name = (read-longest-name)
|
|
208
|
+
with form = `(error ,(format nil "Interpolation error in ~s~%" name))
|
|
209
|
+
with ostr = (gensym)
|
|
210
|
+
for i = (position-if-not #'normal-name-char-p name)
|
|
211
|
+
then (position-if-not #'normal-name-char-p name :start (1+ i))
|
|
212
|
+
|
|
213
|
+
unless i
|
|
214
|
+
return `(let () ,quell-warnings-form
|
|
215
|
+
(handler-case
|
|
216
|
+
(with-output-to-string (,ostr)
|
|
217
|
+
(princ ,(read-from-string name) ,ostr)
|
|
218
|
+
,ostr)
|
|
219
|
+
(unbound-variable () ,form)))
|
|
220
|
+
|
|
221
|
+
if (> i 0)
|
|
222
|
+
do (setq form (try-pos name i form))
|
|
223
|
+
|
|
224
|
+
if (< i (length name))
|
|
225
|
+
do (setq form (try-pos name (1+ i) form)))))
|
|
226
|
+
|
|
227
|
+
(declaim (inline read-form))
|
|
228
|
+
(defun read-form ()
|
|
229
|
+
"Reads and returns one or more Lisp forms from *STREAM* if the
|
|
230
|
+
character we're looking at is a valid inner delimiter. Otherwise
|
|
231
|
+
returns NIL."
|
|
232
|
+
(let* ((start-delimiter (peek-char*))
|
|
233
|
+
(end-delimiter (get-end-delimiter start-delimiter *inner-delimiters*)))
|
|
234
|
+
(cond ((null end-delimiter)
|
|
235
|
+
(if *optional-delimiters-p*
|
|
236
|
+
(read-optional-delimited)
|
|
237
|
+
nil))
|
|
238
|
+
(t
|
|
239
|
+
`(progn
|
|
240
|
+
,@(progn
|
|
241
|
+
(read-char*)
|
|
242
|
+
(let ((*readtable* (copy-readtable*)))
|
|
243
|
+
;; temporarily change the readtable
|
|
244
|
+
(set-syntax-from-char end-delimiter #\))
|
|
245
|
+
(read-delimited-list end-delimiter *stream* t))))))))
|
|
246
|
+
|
|
247
|
+
(defun interpol-reader (*stream* char arg)
|
|
248
|
+
"The actual reader function for the 'sub-character' #\?."
|
|
249
|
+
(declare (ignore arg char))
|
|
250
|
+
(let ((*start-char* (read-char*))
|
|
251
|
+
;; REGEX-MODE is true if we're in regular expression mode; it
|
|
252
|
+
;; can have one of the values :START-OF-CHAR-CLASS,
|
|
253
|
+
;; :START-OF-NEGATED-CHAR-CLASS, or :IN-CHAR-CLASS if we're
|
|
254
|
+
;; inside of a character class or just about to start one -
|
|
255
|
+
;; otherwise the value is T
|
|
256
|
+
regex-mode
|
|
257
|
+
;; EXTENDED-MODE is true if we're in extended regular
|
|
258
|
+
;; expression mode
|
|
259
|
+
extended-mode)
|
|
260
|
+
(when (char-equal *start-char* #\r)
|
|
261
|
+
(setq regex-mode t
|
|
262
|
+
*start-char* (read-char*)))
|
|
263
|
+
(when (char-equal *start-char* #\x)
|
|
264
|
+
(setq extended-mode t
|
|
265
|
+
*start-char* (read-char*)))
|
|
266
|
+
(when (and (not regex-mode)
|
|
267
|
+
(find *start-char* *regex-delimiters* :test #'char=))
|
|
268
|
+
(setq regex-mode t))
|
|
269
|
+
(unless regex-mode
|
|
270
|
+
(setq extended-mode nil))
|
|
271
|
+
(let ((*term-char* (get-end-delimiter *start-char*
|
|
272
|
+
*outer-delimiters*
|
|
273
|
+
:errorp t))
|
|
274
|
+
(*pair-level* 0)
|
|
275
|
+
(*inner-delimiters* (if regex-mode
|
|
276
|
+
(intersection *inner-delimiters*
|
|
277
|
+
'((#\{ . #\}))
|
|
278
|
+
:test #'equal)
|
|
279
|
+
*inner-delimiters*))
|
|
280
|
+
*saw-backslash*
|
|
281
|
+
*readtable-copy*)
|
|
282
|
+
(prog1
|
|
283
|
+
(inner-reader regex-mode extended-mode nil nil)
|
|
284
|
+
;; consume the closing outer delimiter
|
|
285
|
+
(read-char*)))))
|
|
286
|
+
|
|
287
|
+
(defun inner-reader (regex-mode extended-mode quote-mode case-mode)
|
|
288
|
+
"Helper function for INTERPOL-READER which does all the work. May
|
|
289
|
+
call itself recursively."
|
|
290
|
+
;; REGEX-MODE and EXTENDED-MODE as described above; QUOTE-MODE is
|
|
291
|
+
;; true if we're inside a \Q scope; CASE-MODE is true if we're
|
|
292
|
+
;; inside a \L or \U scope
|
|
293
|
+
(let* ((string-stream (gensym)) ;; the string stream
|
|
294
|
+
;; we use for WITH-OUTPUT-TO-STRING
|
|
295
|
+
;; if this is not a constant string
|
|
296
|
+
(collector (make-collector)) ;; we collect
|
|
297
|
+
;; characters into this
|
|
298
|
+
;; extentable string
|
|
299
|
+
result ;; a list of all characters, strings, and forms
|
|
300
|
+
;; so far (in reverse order while withing the loop)
|
|
301
|
+
handle-next-char)
|
|
302
|
+
(block main-loop ;; we need this name so we can leave the LOOP below
|
|
303
|
+
(flet ((compute-result ()
|
|
304
|
+
;; local function used to leave the loop and compute
|
|
305
|
+
;; the final RESULT
|
|
306
|
+
(setq result
|
|
307
|
+
(nreverse
|
|
308
|
+
(if (plusp (length collector))
|
|
309
|
+
;; add COLLECTOR if it's not empty
|
|
310
|
+
(cons collector result)
|
|
311
|
+
result)))
|
|
312
|
+
(return-from main-loop))
|
|
313
|
+
(parse-with-case-mode (action-name)
|
|
314
|
+
;; local function used to read while in a \U or \L scope
|
|
315
|
+
(let ((string-to-modify
|
|
316
|
+
;; read until \E, \L, \U, or end of string
|
|
317
|
+
(inner-reader regex-mode extended-mode regex-mode t)))
|
|
318
|
+
(if (stringp string-to-modify)
|
|
319
|
+
;; modify directly if constant string
|
|
320
|
+
(funcall action-name string-to-modify)
|
|
321
|
+
;; otherwise create a form to do that at run time
|
|
322
|
+
`(write-string
|
|
323
|
+
(,action-name ,string-to-modify)
|
|
324
|
+
,string-stream)))))
|
|
325
|
+
(loop
|
|
326
|
+
(let ((next-char (read-char*)))
|
|
327
|
+
(when regex-mode
|
|
328
|
+
;; when in regex mode make sure where we are with
|
|
329
|
+
;; respect to character classes
|
|
330
|
+
(setq regex-mode
|
|
331
|
+
(case next-char
|
|
332
|
+
((#\[)
|
|
333
|
+
(ecase regex-mode
|
|
334
|
+
((:start-of-char-class
|
|
335
|
+
:start-of-negated-char-class
|
|
336
|
+
:in-char-class) :in-char-class)
|
|
337
|
+
((t) :start-of-char-class)))
|
|
338
|
+
((#\^)
|
|
339
|
+
(ecase regex-mode
|
|
340
|
+
((:start-of-char-class) :start-of-negated-char-class)
|
|
341
|
+
((:start-of-negated-char-class
|
|
342
|
+
:in-char-class) :in-char-class)
|
|
343
|
+
((t) t)))
|
|
344
|
+
((#\])
|
|
345
|
+
(ecase regex-mode
|
|
346
|
+
((:start-of-char-class
|
|
347
|
+
:start-of-negated-char-class) :in-char-class)
|
|
348
|
+
((:in-char-class t) t)))
|
|
349
|
+
(otherwise
|
|
350
|
+
(ecase regex-mode
|
|
351
|
+
((:start-of-char-class
|
|
352
|
+
:start-of-negated-char-class
|
|
353
|
+
:in-char-class) :in-char-class)
|
|
354
|
+
((t) t))))))
|
|
355
|
+
(when (and (char= next-char *start-char*)
|
|
356
|
+
(char/= *start-char* *term-char*))
|
|
357
|
+
;; if we see, say, #\( and our closing delimiter is #\)
|
|
358
|
+
;; we increment *PAIR-LEVEL* so the parentheses can next
|
|
359
|
+
;; without ending the string
|
|
360
|
+
(incf *pair-level*))
|
|
361
|
+
(let ((interpolation
|
|
362
|
+
(cond ((and (char= next-char *term-char*)
|
|
363
|
+
(plusp *pair-level*))
|
|
364
|
+
;; although this is the outer closing
|
|
365
|
+
;; delimiter we don't stop parsing because
|
|
366
|
+
;; we're insided a nested pair of
|
|
367
|
+
;; bracketing characters
|
|
368
|
+
(decf *pair-level*)
|
|
369
|
+
*term-char*)
|
|
370
|
+
((char= next-char *term-char*)
|
|
371
|
+
;; now we really stop - but we don't
|
|
372
|
+
;; consume the closing delimiter because
|
|
373
|
+
;; we may need it again to end another
|
|
374
|
+
;; scope
|
|
375
|
+
(unread-char next-char *stream*)
|
|
376
|
+
(compute-result))
|
|
377
|
+
(t
|
|
378
|
+
(case next-char
|
|
379
|
+
((#\L)
|
|
380
|
+
(cond ((not *saw-backslash*)
|
|
381
|
+
;; a normal #\L, no 'pending'
|
|
382
|
+
;; backslash
|
|
383
|
+
#\L)
|
|
384
|
+
(case-mode
|
|
385
|
+
;; a backslashed #\L which
|
|
386
|
+
;; we've seen before but we
|
|
387
|
+
;; still have to close at
|
|
388
|
+
;; least one \Q/\L/\E scope
|
|
389
|
+
(unread-char #\L *stream*)
|
|
390
|
+
(compute-result))
|
|
391
|
+
(t
|
|
392
|
+
;; all scopes are closed, now
|
|
393
|
+
;; read and downcase 'till \E
|
|
394
|
+
;; or somesuch
|
|
395
|
+
(setq *saw-backslash* nil)
|
|
396
|
+
(parse-with-case-mode 'string-downcase))))
|
|
397
|
+
((#\U)
|
|
398
|
+
;; see comments for #\L above
|
|
399
|
+
(cond ((not *saw-backslash*)
|
|
400
|
+
#\U)
|
|
401
|
+
(case-mode
|
|
402
|
+
(unread-char #\U *stream*)
|
|
403
|
+
(compute-result))
|
|
404
|
+
(t
|
|
405
|
+
(setq *saw-backslash* nil)
|
|
406
|
+
(parse-with-case-mode 'string-upcase))))
|
|
407
|
+
((#\Space #\Tab #\Linefeed #\Return #\Page)
|
|
408
|
+
(cond ((and extended-mode
|
|
409
|
+
(not (eq regex-mode :in-char-class)))
|
|
410
|
+
;; in extended mode (if not in
|
|
411
|
+
;; a character class)
|
|
412
|
+
;; whitespace is removed
|
|
413
|
+
"")
|
|
414
|
+
(t next-char)))
|
|
415
|
+
((#\()
|
|
416
|
+
(cond ((and (eq regex-mode t)
|
|
417
|
+
(null quote-mode)
|
|
418
|
+
(char/= *term-char* #\?)
|
|
419
|
+
(eql (peek-char*) #\?))
|
|
420
|
+
;; this could start an
|
|
421
|
+
;; embedded comment in regex
|
|
422
|
+
;; mode (and we're /not/
|
|
423
|
+
;; inside of a \Q scope or a
|
|
424
|
+
;; character class)
|
|
425
|
+
(read-char*)
|
|
426
|
+
(cond ((and (char/= *term-char* #\#)
|
|
427
|
+
(eql (peek-char*) #\#))
|
|
428
|
+
;; yes, it's a
|
|
429
|
+
;; comment, so consume
|
|
430
|
+
;; characters 'till #\)
|
|
431
|
+
(read-while
|
|
432
|
+
(lambda (char)
|
|
433
|
+
(and (char/= char #\))
|
|
434
|
+
(char/= char *term-char*))))
|
|
435
|
+
(cond ((char= (read-char*) *term-char*)
|
|
436
|
+
(signal-reader-error
|
|
437
|
+
"Incomplete regex comment starting with '(#'"))
|
|
438
|
+
((not (digit-char-p (peek-char*) 16))
|
|
439
|
+
"")
|
|
440
|
+
;; special case
|
|
441
|
+
;; if next
|
|
442
|
+
;; character
|
|
443
|
+
;; could
|
|
444
|
+
;; potentially
|
|
445
|
+
;; continue an
|
|
446
|
+
;; octal or
|
|
447
|
+
;; hexadecimal
|
|
448
|
+
;; representation
|
|
449
|
+
(t "(?:)")))
|
|
450
|
+
;; no, wasn't a comment
|
|
451
|
+
(t "(?")))
|
|
452
|
+
(t #\()))
|
|
453
|
+
((#\#)
|
|
454
|
+
(cond ((and (eq regex-mode t)
|
|
455
|
+
extended-mode
|
|
456
|
+
(null quote-mode))
|
|
457
|
+
;; we're in extended regex
|
|
458
|
+
;; mode and not inside of a \Q
|
|
459
|
+
;; scope or a character class,
|
|
460
|
+
;; so this is a comment and we
|
|
461
|
+
;; consume it 'till #\Newline
|
|
462
|
+
;; or *TERM-CHAR*
|
|
463
|
+
(read-while
|
|
464
|
+
(lambda (char)
|
|
465
|
+
(and (char/= char #\Newline)
|
|
466
|
+
(char/= char *term-char*))))
|
|
467
|
+
(when (char= (peek-char*) #\Newline)
|
|
468
|
+
(read-char*))
|
|
469
|
+
(cond ((not (digit-char-p (peek-char*)
|
|
470
|
+
16))
|
|
471
|
+
"")
|
|
472
|
+
;; special case, see above
|
|
473
|
+
(t "(?:)")))
|
|
474
|
+
(t #\#)))
|
|
475
|
+
((#\\)
|
|
476
|
+
(case (peek-char*)
|
|
477
|
+
((#\Q)
|
|
478
|
+
;; \Q - start a new quote scope
|
|
479
|
+
(read-char*)
|
|
480
|
+
(let ((string-to-quote
|
|
481
|
+
(inner-reader regex-mode
|
|
482
|
+
extended-mode
|
|
483
|
+
t case-mode)))
|
|
484
|
+
(if (stringp string-to-quote)
|
|
485
|
+
;; if we got a constant string
|
|
486
|
+
;; we modify it directly
|
|
487
|
+
(quote-meta-chars string-to-quote)
|
|
488
|
+
;; otherwise we expand into code
|
|
489
|
+
`(write-string
|
|
490
|
+
(quote-meta-chars ,string-to-quote)
|
|
491
|
+
,string-stream))))
|
|
492
|
+
((#\L)
|
|
493
|
+
;; \L - start a new case-modifying
|
|
494
|
+
;; scope
|
|
495
|
+
(cond (case-mode
|
|
496
|
+
;; if we're already in
|
|
497
|
+
;; this mode we have to
|
|
498
|
+
;; end all previous scopes
|
|
499
|
+
;; first - we set
|
|
500
|
+
;; *SAW-BACKSLASH* to T so
|
|
501
|
+
;; the #\L is read until
|
|
502
|
+
;; all scopes are finished
|
|
503
|
+
(setq *saw-backslash* t)
|
|
504
|
+
(compute-result))
|
|
505
|
+
(t
|
|
506
|
+
;; all scopes are closed, now
|
|
507
|
+
;; read and downcase 'till \E
|
|
508
|
+
;; or somesuch
|
|
509
|
+
(setq *saw-backslash* nil)
|
|
510
|
+
(read-char*)
|
|
511
|
+
(parse-with-case-mode 'string-downcase))))
|
|
512
|
+
((#\U)
|
|
513
|
+
;; see comments for #\L above
|
|
514
|
+
(cond (case-mode
|
|
515
|
+
(setq *saw-backslash* t)
|
|
516
|
+
(compute-result))
|
|
517
|
+
(t
|
|
518
|
+
(setq *saw-backslash* nil)
|
|
519
|
+
(read-char*)
|
|
520
|
+
(parse-with-case-mode 'string-upcase))))
|
|
521
|
+
((#\E)
|
|
522
|
+
;; \E - ends exactly one scope
|
|
523
|
+
(read-char*)
|
|
524
|
+
(if (or quote-mode case-mode)
|
|
525
|
+
(compute-result)
|
|
526
|
+
""))
|
|
527
|
+
((#\l)
|
|
528
|
+
;; \l - downcase next character
|
|
529
|
+
(read-char*)
|
|
530
|
+
;; remember that we have to do this
|
|
531
|
+
(setq handle-next-char :downcase)
|
|
532
|
+
nil)
|
|
533
|
+
((#\u)
|
|
534
|
+
;; \u - upcase next character
|
|
535
|
+
(read-char*)
|
|
536
|
+
;; remember that we have to do this
|
|
537
|
+
(setq handle-next-char :upcase)
|
|
538
|
+
nil)
|
|
539
|
+
(otherwise
|
|
540
|
+
;; otherwise this is a
|
|
541
|
+
;; backslash-escaped character
|
|
542
|
+
(unescape-char regex-mode))))
|
|
543
|
+
((#\$)
|
|
544
|
+
;; #\$ - might be an interpolation
|
|
545
|
+
(let ((form (read-form)))
|
|
546
|
+
(cond ((null form)
|
|
547
|
+
;; no, just dollar sign
|
|
548
|
+
#\$)
|
|
549
|
+
(handle-next-char
|
|
550
|
+
;; yes, and we have to
|
|
551
|
+
;; modify the first
|
|
552
|
+
;; character
|
|
553
|
+
(prog1
|
|
554
|
+
(let ((string (gensym)))
|
|
555
|
+
`(let ((,string (format nil "~A"
|
|
556
|
+
,form)))
|
|
557
|
+
(when (plusp (length ,string))
|
|
558
|
+
(setf (char ,string 0)
|
|
559
|
+
(,(if (eq handle-next-char
|
|
560
|
+
:downcase)
|
|
561
|
+
'char-downcase
|
|
562
|
+
'char-upcase)
|
|
563
|
+
(char ,string 0))))
|
|
564
|
+
(write-string ,string ,string-stream)))
|
|
565
|
+
(setq handle-next-char nil)))
|
|
566
|
+
(t
|
|
567
|
+
;; no modification, just
|
|
568
|
+
;; insert a form to PRINC
|
|
569
|
+
;; this interpolation
|
|
570
|
+
`(princ ,form ,string-stream)))))
|
|
571
|
+
((#\@)
|
|
572
|
+
;; #\Q - might be an interpolation
|
|
573
|
+
(let ((form (read-form))
|
|
574
|
+
(element (gensym))
|
|
575
|
+
(first (gensym)))
|
|
576
|
+
(cond ((null form)
|
|
577
|
+
;; no, just at-sign
|
|
578
|
+
#\@)
|
|
579
|
+
(handle-next-char
|
|
580
|
+
;; yes, and we have to
|
|
581
|
+
;; modify the first
|
|
582
|
+
;; character
|
|
583
|
+
(prog1
|
|
584
|
+
(let ((string (gensym)))
|
|
585
|
+
`(loop for ,first = t then nil
|
|
586
|
+
for ,element in ,form
|
|
587
|
+
unless ,first do
|
|
588
|
+
(princ *list-delimiter*
|
|
589
|
+
,string-stream)
|
|
590
|
+
if ,first do
|
|
591
|
+
(let ((,string
|
|
592
|
+
(format nil "~A"
|
|
593
|
+
,element)))
|
|
594
|
+
(when (plusp (length ,string))
|
|
595
|
+
(setf (char ,string 0)
|
|
596
|
+
(,(if (eq handle-next-char
|
|
597
|
+
:downcase)
|
|
598
|
+
'char-downcase
|
|
599
|
+
'char-upcase)
|
|
600
|
+
(char ,string 0))))
|
|
601
|
+
(write-string ,string ,string-stream))
|
|
602
|
+
else do
|
|
603
|
+
(princ ,element ,string-stream)))
|
|
604
|
+
(setq handle-next-char nil)))
|
|
605
|
+
(t
|
|
606
|
+
;; no modification, just
|
|
607
|
+
;; insert a form to PRINC
|
|
608
|
+
;; this interpolated list
|
|
609
|
+
;; (including the list
|
|
610
|
+
;; delimiters inbetween)
|
|
611
|
+
`(loop for ,first = t then nil
|
|
612
|
+
for ,element in ,form
|
|
613
|
+
unless ,first do (princ *list-delimiter*
|
|
614
|
+
,string-stream)
|
|
615
|
+
do (princ ,element ,string-stream))))))
|
|
616
|
+
;; just a 'normal' character
|
|
617
|
+
(otherwise next-char))))))
|
|
618
|
+
(when interpolation
|
|
619
|
+
;; INTERPOLATION is NIL if we just saw #\l or #\u
|
|
620
|
+
(when (and handle-next-char
|
|
621
|
+
(consp interpolation)
|
|
622
|
+
(eq (first interpolation)
|
|
623
|
+
'write-string))
|
|
624
|
+
;; if we have to upcase or downcase the following
|
|
625
|
+
;; character and we just collected a form (from a
|
|
626
|
+
;; \Q/\L/\U scope) we have to insert code for the
|
|
627
|
+
;; modification
|
|
628
|
+
(setf (second interpolation)
|
|
629
|
+
(let ((string (gensym)))
|
|
630
|
+
`(let ((,string ,(second interpolation)))
|
|
631
|
+
(when (plusp (length ,string))
|
|
632
|
+
(setf (char ,string 0)
|
|
633
|
+
(,(if (eq handle-next-char :downcase)
|
|
634
|
+
'char-downcase
|
|
635
|
+
'char-upcase)
|
|
636
|
+
(char ,string 0))))
|
|
637
|
+
,string)))
|
|
638
|
+
(setq handle-next-char nil))
|
|
639
|
+
(cond ((characterp interpolation)
|
|
640
|
+
;; add one character to COLLECTOR and handle
|
|
641
|
+
;; it according to HANDLE-NEXT-CHAR
|
|
642
|
+
(vector-push-extend (case handle-next-char
|
|
643
|
+
((:downcase)
|
|
644
|
+
(setq handle-next-char nil)
|
|
645
|
+
(char-downcase interpolation))
|
|
646
|
+
((:upcase)
|
|
647
|
+
(setq handle-next-char nil)
|
|
648
|
+
(char-upcase interpolation))
|
|
649
|
+
(otherwise
|
|
650
|
+
interpolation))
|
|
651
|
+
collector))
|
|
652
|
+
((stringp interpolation)
|
|
653
|
+
;; add a string to COLLECTOR and handle its
|
|
654
|
+
;; first character according to
|
|
655
|
+
;; HANDLE-NEXT-CHAR
|
|
656
|
+
(loop for char across interpolation
|
|
657
|
+
do (vector-push-extend (case handle-next-char
|
|
658
|
+
((:downcase)
|
|
659
|
+
(setq handle-next-char nil)
|
|
660
|
+
(char-downcase char))
|
|
661
|
+
((:upcase)
|
|
662
|
+
(setq handle-next-char nil)
|
|
663
|
+
(char-upcase char))
|
|
664
|
+
(otherwise
|
|
665
|
+
char))
|
|
666
|
+
collector)))
|
|
667
|
+
((plusp (length collector))
|
|
668
|
+
;; add code (to be executed at runtime) but
|
|
669
|
+
;; make sure to empty COLLECTOR first
|
|
670
|
+
(push collector result)
|
|
671
|
+
(push interpolation result)
|
|
672
|
+
;; reset collector
|
|
673
|
+
(setf collector (make-collector)))
|
|
674
|
+
(t
|
|
675
|
+
;; same but COLLECTOR is empty
|
|
676
|
+
(push interpolation result)))))))))
|
|
677
|
+
(if (every #'stringp result)
|
|
678
|
+
;; if all elements of RESULT are strings we can return a
|
|
679
|
+
;; constant string
|
|
680
|
+
(string-list-to-string result)
|
|
681
|
+
;; otherwise we have to wrap the PRINCs emitted above into a
|
|
682
|
+
;; WITH-OUTPUT-TO-STRING form
|
|
683
|
+
`(with-output-to-string (,string-stream)
|
|
684
|
+
,@(loop for interpolation in result
|
|
685
|
+
if (stringp interpolation)
|
|
686
|
+
collect `(write-string ,interpolation ,string-stream)
|
|
687
|
+
else
|
|
688
|
+
collect interpolation)))))
|
|
689
|
+
|
|
690
|
+
(defun %enable-interpol-syntax ()
|
|
691
|
+
"Internal function used to enable reader syntax and store current
|
|
692
|
+
readtable on stack."
|
|
693
|
+
(push *readtable*
|
|
694
|
+
*previous-readtables*)
|
|
695
|
+
(setq *readtable* (copy-readtable))
|
|
696
|
+
(set-dispatch-macro-character #\# #\? #'interpol-reader)
|
|
697
|
+
(values))
|
|
698
|
+
|
|
699
|
+
(defun %disable-interpol-syntax ()
|
|
700
|
+
"Internal function used to restore previous readtable."
|
|
701
|
+
(if *previous-readtables*
|
|
702
|
+
(setq *readtable* (pop *previous-readtables*))
|
|
703
|
+
(setq *readtable* (copy-readtable nil)))
|
|
704
|
+
(values))
|
|
705
|
+
|
|
706
|
+
(defmacro enable-interpol-syntax ()
|
|
707
|
+
"Enable CL-INTERPOL reader syntax."
|
|
708
|
+
`(eval-when (:compile-toplevel :load-toplevel :execute)
|
|
709
|
+
(%enable-interpol-syntax)))
|
|
710
|
+
|
|
711
|
+
(defmacro disable-interpol-syntax ()
|
|
712
|
+
"Restore readtable which was active before last call to
|
|
713
|
+
ENABLE-INTERPOL-SYNTAX. If there was no such call, the standard
|
|
714
|
+
readtable is used."
|
|
715
|
+
`(eval-when (:compile-toplevel :load-toplevel :execute)
|
|
716
|
+
(%disable-interpol-syntax)))
|