clucumber 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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)))
|