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.
Files changed (139) hide show
  1. data/LICENSE +1 -1
  2. data/README.md +4 -9
  3. data/lib/clucumber/clucumber-bootstrap.lisp +32 -0
  4. data/lib/clucumber/vendor/cl-interpol/alias.lisp +55 -0
  5. data/lib/clucumber/vendor/cl-interpol/cl-interpol.asd +56 -0
  6. data/lib/clucumber/vendor/cl-interpol/load.lisp +53 -0
  7. data/lib/clucumber/vendor/cl-interpol/packages.lisp +40 -0
  8. data/lib/clucumber/vendor/cl-interpol/read.lisp +716 -0
  9. data/lib/clucumber/vendor/cl-interpol/specials.lisp +113 -0
  10. data/lib/clucumber/vendor/cl-interpol/test/packages.lisp +34 -0
  11. data/lib/clucumber/vendor/cl-interpol/test/tests.lisp +128 -0
  12. data/lib/clucumber/vendor/cl-interpol/test.lisp +177 -0
  13. data/lib/clucumber/vendor/cl-interpol/test2.lisp +6254 -0
  14. data/lib/clucumber/vendor/cl-interpol/unicode.lisp +13912 -0
  15. data/lib/clucumber/vendor/cl-interpol/util.lisp +122 -0
  16. data/lib/clucumber/vendor/cl-ppcre/api.lisp +1262 -0
  17. data/lib/clucumber/vendor/cl-ppcre/charmap.lisp +152 -0
  18. data/lib/clucumber/vendor/cl-ppcre/charset.lisp +242 -0
  19. data/lib/clucumber/vendor/cl-ppcre/chartest.lisp +98 -0
  20. data/lib/clucumber/vendor/cl-ppcre/cl-ppcre-test.asd +34 -0
  21. data/lib/clucumber/vendor/cl-ppcre/cl-ppcre-unicode/packages.lisp +38 -0
  22. data/lib/clucumber/vendor/cl-ppcre/cl-ppcre-unicode/resolver.lisp +61 -0
  23. data/lib/clucumber/vendor/cl-ppcre/cl-ppcre-unicode.asd +58 -0
  24. data/lib/clucumber/vendor/cl-ppcre/cl-ppcre.asd +79 -0
  25. data/lib/clucumber/vendor/cl-ppcre/closures.lisp +471 -0
  26. data/lib/clucumber/vendor/cl-ppcre/convert.lisp +875 -0
  27. data/lib/clucumber/vendor/cl-ppcre/errors.lisp +84 -0
  28. data/lib/clucumber/vendor/cl-ppcre/lexer.lisp +737 -0
  29. data/lib/clucumber/vendor/cl-ppcre/lispworks-defsystem.lisp +57 -0
  30. data/lib/clucumber/vendor/cl-ppcre/load.lisp +67 -0
  31. data/lib/clucumber/vendor/cl-ppcre/optimize.lisp +578 -0
  32. data/lib/clucumber/vendor/cl-ppcre/packages.lisp +68 -0
  33. data/lib/clucumber/vendor/cl-ppcre/parser.lisp +319 -0
  34. data/lib/clucumber/vendor/cl-ppcre/ppcre-tests.lisp +269 -0
  35. data/lib/clucumber/vendor/cl-ppcre/regex-class-util.lisp +555 -0
  36. data/lib/clucumber/vendor/cl-ppcre/regex-class.lisp +259 -0
  37. data/lib/clucumber/vendor/cl-ppcre/repetition-closures.lisp +833 -0
  38. data/lib/clucumber/vendor/cl-ppcre/scanner.lisp +506 -0
  39. data/lib/clucumber/vendor/cl-ppcre/specials.lisp +172 -0
  40. data/lib/clucumber/vendor/cl-ppcre/test/packages.lisp +37 -0
  41. data/lib/clucumber/vendor/cl-ppcre/test/perl-tests.lisp +150 -0
  42. data/lib/clucumber/vendor/cl-ppcre/test/tests.lisp +159 -0
  43. data/lib/clucumber/vendor/cl-ppcre/test/unicode-tests.lisp +80 -0
  44. data/lib/clucumber/vendor/cl-ppcre/util.lisp +201 -0
  45. data/lib/clucumber/vendor/cl-unicode/alias.lisp +135 -0
  46. data/lib/clucumber/vendor/cl-unicode/api.lisp +412 -0
  47. data/lib/clucumber/vendor/cl-unicode/build/char-info.lisp +133 -0
  48. data/lib/clucumber/vendor/cl-unicode/build/dump.lisp +239 -0
  49. data/lib/clucumber/vendor/cl-unicode/build/read.lisp +280 -0
  50. data/lib/clucumber/vendor/cl-unicode/build/util.lisp +182 -0
  51. data/lib/clucumber/vendor/cl-unicode/cl-unicode.asd +90 -0
  52. data/lib/clucumber/vendor/cl-unicode/conditions.lisp +54 -0
  53. data/lib/clucumber/vendor/cl-unicode/derived.lisp +120 -0
  54. data/lib/clucumber/vendor/cl-unicode/hash-tables.lisp +20 -0
  55. data/lib/clucumber/vendor/cl-unicode/lists.lisp +10 -0
  56. data/lib/clucumber/vendor/cl-unicode/methods.lisp +15 -0
  57. data/lib/clucumber/vendor/cl-unicode/packages.lisp +76 -0
  58. data/lib/clucumber/vendor/cl-unicode/specials.lisp +135 -0
  59. data/lib/clucumber/vendor/cl-unicode/test/packages.lisp +34 -0
  60. data/lib/clucumber/vendor/cl-unicode/test/tests.lisp +150 -0
  61. data/lib/clucumber/vendor/cl-unicode/test-functions.lisp +94 -0
  62. data/lib/clucumber/vendor/cl-unicode/util.lisp +274 -0
  63. data/lib/clucumber/vendor/lift/compare/fiveam-tests.lisp +14 -0
  64. data/lib/clucumber/vendor/lift/compare/lift-tests.lisp +13 -0
  65. data/lib/clucumber/vendor/lift/compare/rt-tests.lisp +16 -0
  66. data/lib/clucumber/vendor/lift/compare/xlunit-tests.lisp +15 -0
  67. data/lib/clucumber/vendor/lift/dev/changes.lisp +61 -0
  68. data/lib/clucumber/vendor/lift/dev/config.lisp +354 -0
  69. data/lib/clucumber/vendor/lift/dev/copy-file.lisp +117 -0
  70. data/lib/clucumber/vendor/lift/dev/introspection.lisp +232 -0
  71. data/lib/clucumber/vendor/lift/dev/lift-interface.lisp +56 -0
  72. data/lib/clucumber/vendor/lift/dev/lift-notes.lisp +202 -0
  73. data/lib/clucumber/vendor/lift/dev/lift-randomized.lisp +45 -0
  74. data/lib/clucumber/vendor/lift/dev/lift.lisp +2383 -0
  75. data/lib/clucumber/vendor/lift/dev/macros.lisp +229 -0
  76. data/lib/clucumber/vendor/lift/dev/measuring.lisp +156 -0
  77. data/lib/clucumber/vendor/lift/dev/packages.lisp +161 -0
  78. data/lib/clucumber/vendor/lift/dev/port.lisp +151 -0
  79. data/lib/clucumber/vendor/lift/dev/prototypes.lisp +282 -0
  80. data/lib/clucumber/vendor/lift/dev/random-testing.lisp +124 -0
  81. data/lib/clucumber/vendor/lift/dev/report-locations.lisp +13 -0
  82. data/lib/clucumber/vendor/lift/dev/reports.lisp +916 -0
  83. data/lib/clucumber/vendor/lift/dev/utilities.lisp +242 -0
  84. data/lib/clucumber/vendor/lift/docs/package.lisp +6 -0
  85. data/lib/clucumber/vendor/lift/docs/setup.lisp +17 -0
  86. data/lib/clucumber/vendor/lift/examples/basic-examples.lisp +289 -0
  87. data/lib/clucumber/vendor/lift/examples/random-testing.lisp +32 -0
  88. data/lib/clucumber/vendor/lift/lift-documentation.asd +28 -0
  89. data/lib/clucumber/vendor/lift/lift-test.asd +35 -0
  90. data/lib/clucumber/vendor/lift/lift.asd +77 -0
  91. data/lib/clucumber/vendor/lift/test/equality-tests.lisp +40 -0
  92. data/lib/clucumber/vendor/lift/test/finding-tests.lisp +32 -0
  93. data/lib/clucumber/vendor/lift/test/lift-test.lisp +783 -0
  94. data/lib/clucumber/vendor/lift/test/order-of-operations.lisp +54 -0
  95. data/lib/clucumber/vendor/lift/test/packages.lisp +17 -0
  96. data/lib/clucumber/vendor/lift/test/test-config-files.lisp +20 -0
  97. data/lib/clucumber/vendor/lift/test/test-dynamic-variables.lisp +57 -0
  98. data/lib/clucumber/vendor/lift/test/test-maximum-problems.lisp +74 -0
  99. data/lib/clucumber/vendor/lift/test/test-prototypes.lisp +278 -0
  100. data/lib/clucumber/vendor/lift/test/test-timeout.lisp +37 -0
  101. data/lib/clucumber/vendor/lift/test/tests-in-progress.lisp +62 -0
  102. data/lib/clucumber/vendor/lift/test/testsuite-expects.lisp +60 -0
  103. data/lib/clucumber/vendor/lift/timeout/package.lisp +13 -0
  104. data/lib/clucumber/vendor/lift/timeout/with-timeout.lisp +123 -0
  105. data/lib/clucumber/vendor/lift/website/stuff/Temp.lisp +34 -0
  106. data/lib/clucumber/vendor/st-json/st-json.asd +3 -0
  107. data/lib/clucumber/vendor/st-json/st-json.lisp +310 -0
  108. data/lib/clucumber/vendor/trivial-backtrace/dev/backtrace.lisp +127 -0
  109. data/lib/clucumber/vendor/trivial-backtrace/dev/fallback.lisp +10 -0
  110. data/lib/clucumber/vendor/trivial-backtrace/dev/map-backtrace.lisp +103 -0
  111. data/lib/clucumber/vendor/trivial-backtrace/dev/mucking.lisp +75 -0
  112. data/lib/clucumber/vendor/trivial-backtrace/dev/packages.lisp +13 -0
  113. data/lib/clucumber/vendor/trivial-backtrace/dev/utilities.lisp +104 -0
  114. data/lib/clucumber/vendor/trivial-backtrace/test/packages.lisp +5 -0
  115. data/lib/clucumber/vendor/trivial-backtrace/test/test-setup.lisp +4 -0
  116. data/lib/clucumber/vendor/trivial-backtrace/test/tests.lisp +16 -0
  117. data/lib/clucumber/vendor/trivial-backtrace/trivial-backtrace-test.asd +22 -0
  118. data/lib/clucumber/vendor/trivial-backtrace/trivial-backtrace.asd +35 -0
  119. data/lib/clucumber/vendor/usocket/backend/allegro.lisp +199 -0
  120. data/lib/clucumber/vendor/usocket/backend/armedbear.lisp +491 -0
  121. data/lib/clucumber/vendor/usocket/backend/clisp.lisp +260 -0
  122. data/lib/clucumber/vendor/usocket/backend/cmucl.lisp +266 -0
  123. data/lib/clucumber/vendor/usocket/backend/lispworks.lisp +741 -0
  124. data/lib/clucumber/vendor/usocket/backend/mcl.lisp +369 -0
  125. data/lib/clucumber/vendor/usocket/backend/openmcl.lisp +206 -0
  126. data/lib/clucumber/vendor/usocket/backend/sbcl.lisp +424 -0
  127. data/lib/clucumber/vendor/usocket/backend/scl.lisp +261 -0
  128. data/lib/clucumber/vendor/usocket/condition.lisp +227 -0
  129. data/lib/clucumber/vendor/usocket/package.lisp +82 -0
  130. data/lib/clucumber/vendor/usocket/server.lisp +45 -0
  131. data/lib/clucumber/vendor/usocket/test/package.lisp +13 -0
  132. data/lib/clucumber/vendor/usocket/test/test-usocket.lisp +166 -0
  133. data/lib/clucumber/vendor/usocket/usocket-test.asd +26 -0
  134. data/lib/clucumber/vendor/usocket/usocket.asd +37 -0
  135. data/lib/clucumber/vendor/usocket/usocket.lisp +542 -0
  136. data/lib/clucumber/vendor/usocket/vendor/kqueue.lisp +1 -0
  137. data/lib/clucumber/vendor/usocket/vendor/split-sequence.lisp +245 -0
  138. data/lib/clucumber.rb +29 -7
  139. 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)))