nendo 0.5.0 → 0.5.1
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/emacs/nendo-mode.el +2 -0
- data/lib/init.nnd +51 -35
- data/lib/init.nndc +3371 -2920
- data/lib/nendo.rb +463 -194
- data/lib/srfi-2.nndc +48 -165
- data/lib/srfi-26.nndc +142 -511
- data/lib/text/html-lite.nndc +23 -1
- data/lib/util/combinations.nnd +290 -0
- data/lib/util/combinations.nndc +7218 -0
- data/lib/util/list.nndc +138 -387
- data/lib/util/match.nnd +672 -0
- data/lib/util/match.nndc +81024 -0
- data/test/match-test.nnd +186 -0
- data/test/nendo-util-test.nnd +5 -7
- data/test/nendo_spec.rb +697 -235
- data/test/syntax_spec.rb +561 -52
- data/test/util-combinations-test.nnd +383 -0
- metadata +9 -4
- data/example/scratch.nnd +0 -119
data/lib/util/match.nnd
ADDED
@@ -0,0 +1,672 @@
|
|
1
|
+
;;;-*- mode: nendo; syntax: scheme -*-;;
|
2
|
+
;;;
|
3
|
+
;;;; match.scm -- portable hygienic pattern matcher
|
4
|
+
;;
|
5
|
+
;; This code is written by Alex Shinn and placed in the
|
6
|
+
;; Public Domain. All warranties are disclaimed.
|
7
|
+
|
8
|
+
;; This is a full superset of the popular MATCH package by Andrew
|
9
|
+
;; Wright, written in fully portable SYNTAX-RULES (R5RS only, breaks
|
10
|
+
;; in R6RS SYNTAX-RULES), and thus preserving hygiene.
|
11
|
+
|
12
|
+
;; This is a simple generative pattern matcher - each pattern is
|
13
|
+
;; expanded into the required tests, calling a failure continuation if
|
14
|
+
;; the tests fail. This makes the logic easy to follow and extend,
|
15
|
+
;; but produces sub-optimal code in cases where you have many similar
|
16
|
+
;; clauses due to repeating the same tests. Nonetheless a smart
|
17
|
+
;; compiler should be able to remove the redundant tests. For
|
18
|
+
;; MATCH-LET and DESTRUCTURING-BIND type uses there is no performance
|
19
|
+
;; hit.
|
20
|
+
|
21
|
+
;; The original version was written on 2006/11/29 and described in the
|
22
|
+
;; following Usenet post:
|
23
|
+
;; http://groups.google.com/group/comp.lang.scheme/msg/0941234de7112ffd
|
24
|
+
;; and is still available at
|
25
|
+
;; http://synthcode.com/scheme/match-simple.scm
|
26
|
+
;; It's just 80 lines for the core MATCH, and an extra 40 lines for
|
27
|
+
;; MATCH-LET, MATCH-LAMBDA and other syntactic sugar.
|
28
|
+
;;
|
29
|
+
;; A variant of this file which uses COND-EXPAND in a few places for
|
30
|
+
;; performance can be found at
|
31
|
+
;; http://synthcode.com/scheme/match-cond-expand.scm
|
32
|
+
;;
|
33
|
+
;; 2009/11/25 - adding `***' tree search patterns
|
34
|
+
;; 2008/03/20 - fixing bug where (a ...) matched non-lists
|
35
|
+
;; 2008/03/15 - removing redundant check in vector patterns
|
36
|
+
;; 2008/03/06 - you can use `...' portably now (thanks to Taylor Campbell)
|
37
|
+
;; 2007/09/04 - fixing quasiquote patterns
|
38
|
+
;; 2007/07/21 - allowing ellipse patterns in non-final list positions
|
39
|
+
;; 2007/04/10 - fixing potential hygiene issue in match-check-ellipse
|
40
|
+
;; (thanks to Taylor Campbell)
|
41
|
+
;; 2007/04/08 - clean up, commenting
|
42
|
+
;; 2006/12/24 - bugfixes
|
43
|
+
;; 2006/12/01 - non-linear patterns, shared variables in OR, get!/set!
|
44
|
+
|
45
|
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
46
|
+
;; force compile-time syntax errors with useful messages
|
47
|
+
|
48
|
+
(define-syntax match-syntax-error
|
49
|
+
(syntax-rules ()
|
50
|
+
((_) (match-syntax-error "invalid match-syntax-error usage"))))
|
51
|
+
|
52
|
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
53
|
+
|
54
|
+
;; The basic interface. MATCH just performs some basic syntax
|
55
|
+
;; validation, binds the match expression to a temporary variable `v',
|
56
|
+
;; and passes it on to MATCH-NEXT. It's a constant throughout the
|
57
|
+
;; code below that the binding `v' is a direct variable reference, not
|
58
|
+
;; an expression.
|
59
|
+
|
60
|
+
(define-syntax match
|
61
|
+
(syntax-rules ()
|
62
|
+
((match)
|
63
|
+
(match-syntax-error "missing match expression"))
|
64
|
+
((match atom)
|
65
|
+
(match-syntax-error "no match clauses"))
|
66
|
+
((match (app ...) (pat . body) ...)
|
67
|
+
(let ((v (app ...)))
|
68
|
+
(match-next v ((app ...) (set! (app ...))) (pat . body) ...)))
|
69
|
+
((match #(vec ...) (pat . body) ...)
|
70
|
+
(let ((v #(vec ...)))
|
71
|
+
(match-next v (v (set! v)) (pat . body) ...)))
|
72
|
+
((match atom (pat . body) ...)
|
73
|
+
(match-next atom (atom (set! atom)) (pat . body) ...))
|
74
|
+
))
|
75
|
+
|
76
|
+
;; MATCH-NEXT passes each clause to MATCH-ONE in turn with its failure
|
77
|
+
;; thunk, which is expanded by recursing MATCH-NEXT on the remaining
|
78
|
+
;; clauses. `g+s' is a list of two elements, the get! and set!
|
79
|
+
;; expressions respectively.
|
80
|
+
|
81
|
+
(define-syntax match-next
|
82
|
+
(syntax-rules (=>)
|
83
|
+
;; no more clauses, the match failed
|
84
|
+
((match-next v g+s)
|
85
|
+
(error "Error: match: no matching pattern"))
|
86
|
+
;; named failure continuation
|
87
|
+
((match-next v g+s (pat (=> failure) . body) . rest)
|
88
|
+
(let ((failure (lambda () (match-next v g+s . rest))))
|
89
|
+
;; match-one analyzes the pattern for us
|
90
|
+
(match-one v pat g+s (match-drop-ids (begin . body)) (failure) ())))
|
91
|
+
;; anonymous failure continuation, give it a dummy name
|
92
|
+
((match-next v g+s (pat . body) . rest)
|
93
|
+
(match-next v g+s (pat (=> failure) . body) . rest))))
|
94
|
+
|
95
|
+
;; MATCH-ONE first checks for ellipse patterns, otherwise passes on to
|
96
|
+
;; MATCH-TWO.
|
97
|
+
|
98
|
+
(define-syntax match-one
|
99
|
+
(syntax-rules ()
|
100
|
+
;; If it's a list of two or more values, check to see if the
|
101
|
+
;; second one is an ellipse and handle accordingly, otherwise go
|
102
|
+
;; to MATCH-TWO.
|
103
|
+
((match-one v (p q . r) g+s sk fk i)
|
104
|
+
(match-check-ellipse
|
105
|
+
q
|
106
|
+
(match-extract-vars p (match-gen-ellipses v p r g+s sk fk i) i ())
|
107
|
+
(match-two v (p q . r) g+s sk fk i)))
|
108
|
+
;; Go directly to MATCH-TWO.
|
109
|
+
((match-one . x)
|
110
|
+
(match-two . x))))
|
111
|
+
|
112
|
+
;; This is the guts of the pattern matcher. We are passed a lot of
|
113
|
+
;; information in the form:
|
114
|
+
;;
|
115
|
+
;; (match-two var pattern getter setter success-k fail-k (ids ...))
|
116
|
+
;;
|
117
|
+
;; usually abbreviated
|
118
|
+
;;
|
119
|
+
;; (match-two v p g+s sk fk i)
|
120
|
+
;;
|
121
|
+
;; where VAR is the symbol name of the current variable we are
|
122
|
+
;; matching, PATTERN is the current pattern, getter and setter are the
|
123
|
+
;; corresponding accessors (e.g. CAR and SET-CAR! of the pair holding
|
124
|
+
;; VAR), SUCCESS-K is the success continuation, FAIL-K is the failure
|
125
|
+
;; continuation (which is just a thunk call and is thus safe to expand
|
126
|
+
;; multiple times) and IDS are the list of identifiers bound in the
|
127
|
+
;; pattern so far.
|
128
|
+
|
129
|
+
(define-syntax match-two
|
130
|
+
(syntax-rules (_ ___ *** quote quasiquote ? $ = and or not set! get!)
|
131
|
+
((match-two v () g+s (sk ...) fk i)
|
132
|
+
(if (null? v) (sk ... i) fk))
|
133
|
+
((match-two v (quote p) g+s (sk ...) fk i)
|
134
|
+
(if (equal? v 'p) (sk ... i) fk))
|
135
|
+
((match-two v (quasiquote p) . x)
|
136
|
+
(match-quasiquote v p . x))
|
137
|
+
((match-two v (and) g+s (sk ...) fk i) (sk ... i))
|
138
|
+
((match-two v (and p q ...) g+s sk fk i)
|
139
|
+
(match-one v p g+s (match-one v (and q ...) g+s sk fk) fk i))
|
140
|
+
((match-two v (or) g+s sk fk i) fk)
|
141
|
+
((match-two v (or p) . x)
|
142
|
+
(match-one v p . x))
|
143
|
+
((match-two v (or p ...) g+s sk fk i)
|
144
|
+
(match-extract-vars (or p ...) (match-gen-or v (p ...) g+s sk fk i) i ()))
|
145
|
+
((match-two v (not p) g+s (sk ...) fk i)
|
146
|
+
(match-one v p g+s (match-drop-ids fk) (sk ... i) i))
|
147
|
+
((match-two v (get! getter) (g s) (sk ...) fk i)
|
148
|
+
(let ((getter (lambda () g))) (sk ... i)))
|
149
|
+
((match-two v (set! setter) (g (s ...)) (sk ...) fk i)
|
150
|
+
(let ((setter (lambda (x) (s ... x)))) (sk ... i)))
|
151
|
+
((match-two v (? pred . p) g+s sk fk i)
|
152
|
+
(if (pred v) (match-one v (and . p) g+s sk fk i) fk))
|
153
|
+
((match-two v (= proc p) . x)
|
154
|
+
(let ((w (proc v))) (match-one w p . x)))
|
155
|
+
((match-two v (p ___ . r) g+s sk fk i)
|
156
|
+
(match-extract-vars p (match-gen-ellipses v p r g+s sk fk i) i ()))
|
157
|
+
((match-two v (p) g+s sk fk i)
|
158
|
+
(if (and (pair? v) (null? (cdr v)))
|
159
|
+
(let ((w (car v)))
|
160
|
+
(match-one w p ((car v) (set-car! v)) sk fk i))
|
161
|
+
fk))
|
162
|
+
((match-two v (p *** q) g+s sk fk i)
|
163
|
+
(match-extract-vars p (match-gen-search v p q g+s sk fk i) i ()))
|
164
|
+
((match-two v (p *** . q) g+s sk fk i)
|
165
|
+
(match-syntax-error "invalid use of ***" (p *** . q)))
|
166
|
+
((match-two v (p . q) g+s sk fk i)
|
167
|
+
(if (pair? v)
|
168
|
+
(let ((w (car v)) (x (cdr v)))
|
169
|
+
(match-one w p ((car v) (set-car! v))
|
170
|
+
(match-one x q ((cdr v) (set-cdr! v)) sk fk)
|
171
|
+
fk
|
172
|
+
i))
|
173
|
+
fk))
|
174
|
+
((match-two v #(p ...) g+s . x)
|
175
|
+
(match-vector v 0 () (p ...) . x))
|
176
|
+
((match-two v _ g+s (sk ...) fk i) (sk ... i))
|
177
|
+
;; Not a pair or vector or special literal, test to see if it's a
|
178
|
+
;; new symbol, in which case we just bind it, or if it's an
|
179
|
+
;; already bound symbol or some other literal, in which case we
|
180
|
+
;; compare it with EQUAL?.
|
181
|
+
((match-two v x g+s (sk ...) fk (id ...))
|
182
|
+
(let-syntax
|
183
|
+
((new-sym?
|
184
|
+
(syntax-rules (id ...)
|
185
|
+
((new-sym? x sk2 fk2) sk2)
|
186
|
+
((new-sym? y sk2 fk2) fk2))))
|
187
|
+
(new-sym? random-sym-to-match
|
188
|
+
(let ((x v)) (sk ... (id ... x)))
|
189
|
+
(if (equal? v x) (sk ... (id ...)) fk))))
|
190
|
+
))
|
191
|
+
|
192
|
+
;; QUASIQUOTE patterns
|
193
|
+
|
194
|
+
(define-syntax match-quasiquote
|
195
|
+
(syntax-rules (unquote unquote-splicing quasiquote)
|
196
|
+
((_ v (unquote p) g+s sk fk i)
|
197
|
+
(match-one v p g+s sk fk i))
|
198
|
+
((_ v ((unquote-splicing p) . rest) g+s sk fk i)
|
199
|
+
(if (pair? v)
|
200
|
+
(match-one v
|
201
|
+
(p . tmp)
|
202
|
+
(match-quasiquote tmp rest g+s sk fk)
|
203
|
+
fk
|
204
|
+
i)
|
205
|
+
fk))
|
206
|
+
((_ v (quasiquote p) g+s sk fk i . depth)
|
207
|
+
(match-quasiquote v p g+s sk fk i #f . depth))
|
208
|
+
((_ v (unquote p) g+s sk fk i x . depth)
|
209
|
+
(match-quasiquote v p g+s sk fk i . depth))
|
210
|
+
((_ v (unquote-splicing p) g+s sk fk i x . depth)
|
211
|
+
(match-quasiquote v p g+s sk fk i . depth))
|
212
|
+
((_ v (p . q) g+s sk fk i . depth)
|
213
|
+
(if (pair? v)
|
214
|
+
(let ((w (car v)) (x (cdr v)))
|
215
|
+
(match-quasiquote
|
216
|
+
w p g+s
|
217
|
+
(match-quasiquote-step x q g+s sk fk depth)
|
218
|
+
fk i . depth))
|
219
|
+
fk))
|
220
|
+
((_ v #(elt ...) g+s sk fk i . depth)
|
221
|
+
(if (vector? v)
|
222
|
+
(let ((ls (vector->list v)))
|
223
|
+
(match-quasiquote ls (elt ...) g+s sk fk i . depth))
|
224
|
+
fk))
|
225
|
+
((_ v x g+s sk fk i . depth)
|
226
|
+
(match-one v 'x g+s sk fk i))))
|
227
|
+
|
228
|
+
(define-syntax match-quasiquote-step
|
229
|
+
(syntax-rules ()
|
230
|
+
((match-quasiquote-step x q g+s sk fk depth i)
|
231
|
+
(match-quasiquote x q g+s sk fk i . depth))))
|
232
|
+
|
233
|
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
234
|
+
;; Utilities
|
235
|
+
|
236
|
+
;; Takes two values and just expands into the first.
|
237
|
+
(define-syntax match-drop-ids
|
238
|
+
(syntax-rules ()
|
239
|
+
((_ expr ids ...) expr)))
|
240
|
+
|
241
|
+
(define-syntax match-drop-first-arg
|
242
|
+
(syntax-rules ()
|
243
|
+
((_ arg expr) expr)))
|
244
|
+
|
245
|
+
;; To expand an OR group we try each clause in succession, passing the
|
246
|
+
;; first that succeeds to the success continuation. On failure for
|
247
|
+
;; any clause, we just try the next clause, finally resorting to the
|
248
|
+
;; failure continuation fk if all clauses fail. The only trick is
|
249
|
+
;; that we want to unify the identifiers, so that the success
|
250
|
+
;; continuation can refer to a variable from any of the OR clauses.
|
251
|
+
|
252
|
+
(define-syntax match-gen-or
|
253
|
+
(syntax-rules ()
|
254
|
+
((_ v p g+s (sk ...) fk (i ...) ((id id-ls) ...))
|
255
|
+
(let ((sk2 (lambda (id ...) (sk ... (i ... id ...)))))
|
256
|
+
(match-gen-or-step v p g+s (match-drop-ids (sk2 id ...)) fk (i ...))))))
|
257
|
+
|
258
|
+
(define-syntax match-gen-or-step
|
259
|
+
(syntax-rules ()
|
260
|
+
((_ v () g+s sk fk . x)
|
261
|
+
;; no OR clauses, call the failure continuation
|
262
|
+
fk)
|
263
|
+
((_ v (p) . x)
|
264
|
+
;; last (or only) OR clause, just expand normally
|
265
|
+
(match-one v p . x))
|
266
|
+
((_ v (p . q) g+s sk fk i)
|
267
|
+
;; match one and try the remaining on failure
|
268
|
+
(match-one v p g+s sk (match-gen-or-step v q g+s sk fk i) i))
|
269
|
+
))
|
270
|
+
|
271
|
+
;; We match a pattern (p ...) by matching the pattern p in a loop on
|
272
|
+
;; each element of the variable, accumulating the bound ids into lists.
|
273
|
+
|
274
|
+
;; Look at the body of the simple case - it's just a named let loop,
|
275
|
+
;; matching each element in turn to the same pattern. The only trick
|
276
|
+
;; is that we want to keep track of the lists of each extracted id, so
|
277
|
+
;; when the loop recurses we cons the ids onto their respective list
|
278
|
+
;; variables, and on success we bind the ids (what the user input and
|
279
|
+
;; expects to see in the success body) to the reversed accumulated
|
280
|
+
;; list IDs.
|
281
|
+
|
282
|
+
(define-syntax match-gen-ellipses
|
283
|
+
(syntax-rules ()
|
284
|
+
((_ v p () g+s (sk ...) fk i ((id id-ls) ...))
|
285
|
+
(match-check-identifier p
|
286
|
+
;; simplest case equivalent to (p ...), just bind the list
|
287
|
+
(let ((p v))
|
288
|
+
(if (list? p)
|
289
|
+
(sk ... i)
|
290
|
+
fk))
|
291
|
+
;; simple case, match all elements of the list
|
292
|
+
(let loop ((ls v) (id-ls '()) ...)
|
293
|
+
(cond
|
294
|
+
((null? ls)
|
295
|
+
(let ((id (reverse id-ls)) ...) (sk ... i)))
|
296
|
+
((pair? ls)
|
297
|
+
(let ((w (car ls)))
|
298
|
+
(match-one w p ((car ls) (set-car! ls))
|
299
|
+
(match-drop-ids (loop (cdr ls) (cons id id-ls) ...))
|
300
|
+
fk i)))
|
301
|
+
(else
|
302
|
+
fk)))))
|
303
|
+
((_ v p r g+s (sk ...) fk i ((id id-ls) ...))
|
304
|
+
;; general case, trailing patterns to match, keep track of the
|
305
|
+
;; remaining list length so we don't need any backtracking
|
306
|
+
(match-verify-no-ellipses
|
307
|
+
r
|
308
|
+
(let* ((tail-len (length 'r))
|
309
|
+
(ls v)
|
310
|
+
(len (length ls)))
|
311
|
+
(if (< len tail-len)
|
312
|
+
fk
|
313
|
+
(let loop ((ls ls) (n len) (id-ls '()) ...)
|
314
|
+
(cond
|
315
|
+
((= n tail-len)
|
316
|
+
(let ((id (reverse id-ls)) ...)
|
317
|
+
(match-one ls r (#f #f) (sk ... i) fk i)))
|
318
|
+
((pair? ls)
|
319
|
+
(let ((w (car ls)))
|
320
|
+
(match-one w p ((car ls) (set-car! ls))
|
321
|
+
(match-drop-ids
|
322
|
+
(loop (cdr ls) (- n 1) (cons id id-ls) ...))
|
323
|
+
fk
|
324
|
+
i)))
|
325
|
+
(else
|
326
|
+
fk)))))))))
|
327
|
+
|
328
|
+
;; This is just a safety check. Although unlike syntax-rules we allow
|
329
|
+
;; trailing patterns after an ellipses, we explicitly disable multiple
|
330
|
+
;; ellipses at the same level. This is because in the general case
|
331
|
+
;; such patterns are exponential in the number of ellipses, and we
|
332
|
+
;; don't want to make it easy to construct very expensive operations
|
333
|
+
;; with simple looking patterns. For example, it would be O(n^2) for
|
334
|
+
;; patterns like (a ... b ...) because we must consider every trailing
|
335
|
+
;; element for every possible break for the leading "a ...".
|
336
|
+
|
337
|
+
(define-syntax match-verify-no-ellipses
|
338
|
+
(syntax-rules ()
|
339
|
+
((_ (x . y) sk)
|
340
|
+
(match-check-ellipse
|
341
|
+
x
|
342
|
+
(match-syntax-error
|
343
|
+
"multiple ellipse patterns not allowed at same level")
|
344
|
+
(match-verify-no-ellipses y sk)))
|
345
|
+
((_ () sk)
|
346
|
+
sk)
|
347
|
+
((_ x sk)
|
348
|
+
(match-syntax-error "dotted tail not allowed after ellipse" x))))
|
349
|
+
|
350
|
+
;; Matching a tree search pattern is only slightly more complicated.
|
351
|
+
;; Here we allow patterns of the form
|
352
|
+
;;
|
353
|
+
;; (x *** y)
|
354
|
+
;;
|
355
|
+
;; to represent the pattern y located somewhere in a tree where the
|
356
|
+
;; path from the current object to y can be seen as a list of the form
|
357
|
+
;; (X ...). Y can immediately match the current object in which case
|
358
|
+
;; the path is the empty list. In a sense it's a 2-dimensional
|
359
|
+
;; version of the ... pattern.
|
360
|
+
;;
|
361
|
+
;; As a common case the pattern (_ *** y) can be used to search for Y
|
362
|
+
;; anywhere in a tree, regardless of the path used.
|
363
|
+
;;
|
364
|
+
;; To implement the search, we use two recursive procedures. TRY
|
365
|
+
;; attempts to match Y once, and on success it calls the normal SK on
|
366
|
+
;; the accumulated list ids as in MATCH-GEN-ELLIPSES. On failure, we
|
367
|
+
;; call NEXT which first checks if the current value is a list
|
368
|
+
;; beginning with X, then calls TRY on each remaining element of the
|
369
|
+
;; list. Since TRY will recursively call NEXT again on failure, this
|
370
|
+
;; effects a full depth-first search.
|
371
|
+
;;
|
372
|
+
;; The failure continuation throughout is a jump to the next step in
|
373
|
+
;; the tree search, initialized with the original failure continuation
|
374
|
+
;; FK.
|
375
|
+
|
376
|
+
(define-syntax match-gen-search
|
377
|
+
(syntax-rules ()
|
378
|
+
((match-gen-search v p q g+s sk fk i ((id id-ls) ...))
|
379
|
+
(letrec ((try (lambda (w fail id-ls ...)
|
380
|
+
(match-one w q g+s
|
381
|
+
(match-drop-ids
|
382
|
+
(let ((id (reverse id-ls)) ...)
|
383
|
+
sk))
|
384
|
+
(next w fail id-ls ...) i)))
|
385
|
+
(next (lambda (w fail id-ls ...)
|
386
|
+
(if (not (pair? w))
|
387
|
+
(fail)
|
388
|
+
(let ((u (car w)))
|
389
|
+
(match-one
|
390
|
+
u p ((car w) (set-car! w))
|
391
|
+
(match-drop-ids
|
392
|
+
;; accumulate the head variables from
|
393
|
+
;; the p pattern, and loop over the tail
|
394
|
+
(let ((id-ls (cons id id-ls)) ...)
|
395
|
+
(let lp ((ls (cdr w)))
|
396
|
+
(if (pair? ls)
|
397
|
+
(try (car ls)
|
398
|
+
(lambda () (lp (cdr ls)))
|
399
|
+
id-ls ...)
|
400
|
+
(fail)))))
|
401
|
+
(fail) i))))))
|
402
|
+
;; the initial id-ls binding here is a dummy to get the right
|
403
|
+
;; number of '()s
|
404
|
+
(let ((id-ls '()) ...)
|
405
|
+
(try v (lambda () fk) id-ls ...))))))
|
406
|
+
|
407
|
+
;; Vector patterns are just more of the same, with the slight
|
408
|
+
;; exception that we pass around the current vector index being
|
409
|
+
;; matched.
|
410
|
+
|
411
|
+
(define-syntax match-vector
|
412
|
+
(syntax-rules (___)
|
413
|
+
((_ v n pats (p q) . x)
|
414
|
+
(match-check-ellipse q
|
415
|
+
(match-gen-vector-ellipses v n pats p . x)
|
416
|
+
(match-vector-two v n pats (p q) . x)))
|
417
|
+
((_ v n pats (p ___) sk fk i)
|
418
|
+
(match-gen-vector-ellipses v n pats p sk fk i))
|
419
|
+
((_ . x)
|
420
|
+
(match-vector-two . x))))
|
421
|
+
|
422
|
+
;; Check the exact vector length, then check each element in turn.
|
423
|
+
|
424
|
+
(define-syntax match-vector-two
|
425
|
+
(syntax-rules ()
|
426
|
+
((_ v n ((pat index) ...) () sk fk i)
|
427
|
+
(if (vector? v)
|
428
|
+
(let ((len (vector-length v)))
|
429
|
+
(if (= len n)
|
430
|
+
(match-vector-step v ((pat index) ...) sk fk i)
|
431
|
+
fk))
|
432
|
+
fk))
|
433
|
+
((_ v n (pats ...) (p . q) . x)
|
434
|
+
(match-vector v (+ n 1) (pats ... (p n)) q . x))))
|
435
|
+
|
436
|
+
(define-syntax match-vector-step
|
437
|
+
(syntax-rules ()
|
438
|
+
((_ v () (sk ...) fk i) (sk ... i))
|
439
|
+
((_ v ((pat index) . rest) sk fk i)
|
440
|
+
(let ((w (vector-ref v index)))
|
441
|
+
(match-one w pat ((vector-ref v index) (vector-set! v index))
|
442
|
+
(match-vector-step v rest sk fk)
|
443
|
+
fk i)))))
|
444
|
+
|
445
|
+
;; With a vector ellipse pattern we first check to see if the vector
|
446
|
+
;; length is at least the required length.
|
447
|
+
|
448
|
+
(define-syntax match-gen-vector-ellipses
|
449
|
+
(syntax-rules ()
|
450
|
+
((_ v n ((pat index) ...) p sk fk i)
|
451
|
+
(if (vector? v)
|
452
|
+
(let ((len (vector-length v)))
|
453
|
+
(if (>= len n)
|
454
|
+
(match-vector-step v ((pat index) ...)
|
455
|
+
(match-vector-tail v p n len sk fk)
|
456
|
+
fk i)
|
457
|
+
fk))
|
458
|
+
fk))))
|
459
|
+
|
460
|
+
(define-syntax match-vector-tail
|
461
|
+
(syntax-rules ()
|
462
|
+
((_ v p n len sk fk i)
|
463
|
+
(match-extract-vars p (match-vector-tail-two v p n len sk fk i) i ()))))
|
464
|
+
|
465
|
+
(define-syntax match-vector-tail-two
|
466
|
+
(syntax-rules ()
|
467
|
+
((_ v p n len (sk ...) fk i ((id id-ls) ...))
|
468
|
+
(let loop ((j n) (id-ls '()) ...)
|
469
|
+
(if (>= j len)
|
470
|
+
(let ((id (reverse id-ls)) ...) (sk ... i))
|
471
|
+
(let ((w (vector-ref v j)))
|
472
|
+
(match-one w p ((vector-ref v j) (vetor-set! v j))
|
473
|
+
(match-drop-ids (loop (+ j 1) (cons id id-ls) ...))
|
474
|
+
fk i)))))))
|
475
|
+
|
476
|
+
;; Extract all identifiers in a pattern. A little more complicated
|
477
|
+
;; than just looking for symbols, we need to ignore special keywords
|
478
|
+
;; and non-pattern forms (such as the predicate expression in ?
|
479
|
+
;; patterns), and also ignore previously bound identifiers.
|
480
|
+
;;
|
481
|
+
;; Calls the continuation with all new vars as a list of the form
|
482
|
+
;; ((orig-var tmp-name) ...), where tmp-name can be used to uniquely
|
483
|
+
;; pair with the original variable (e.g. it's used in the ellipse
|
484
|
+
;; generation for list variables).
|
485
|
+
;;
|
486
|
+
;; (match-extract-vars pattern continuation (ids ...) (new-vars ...))
|
487
|
+
|
488
|
+
(define-syntax match-extract-vars
|
489
|
+
(syntax-rules (_ ___ *** ? $ = quote quasiquote and or not get! set!)
|
490
|
+
((match-extract-vars (? pred . p) . x)
|
491
|
+
(match-extract-vars p . x))
|
492
|
+
((match-extract-vars ($ rec . p) . x)
|
493
|
+
(match-extract-vars p . x))
|
494
|
+
((match-extract-vars (= proc p) . x)
|
495
|
+
(match-extract-vars p . x))
|
496
|
+
((match-extract-vars (quote x) (k ...) i v)
|
497
|
+
(k ... v))
|
498
|
+
((match-extract-vars (quasiquote x) k i v)
|
499
|
+
(match-extract-quasiquote-vars x k i v (#t)))
|
500
|
+
((match-extract-vars (and . p) . x)
|
501
|
+
(match-extract-vars p . x))
|
502
|
+
((match-extract-vars (or . p) . x)
|
503
|
+
(match-extract-vars p . x))
|
504
|
+
((match-extract-vars (not . p) . x)
|
505
|
+
(match-extract-vars p . x))
|
506
|
+
;; A non-keyword pair, expand the CAR with a continuation to
|
507
|
+
;; expand the CDR.
|
508
|
+
((match-extract-vars (p q . r) k i v)
|
509
|
+
(match-check-ellipse
|
510
|
+
q
|
511
|
+
(match-extract-vars (p . r) k i v)
|
512
|
+
(match-extract-vars p (match-extract-vars-step (q . r) k i v) i ())))
|
513
|
+
((match-extract-vars (p . q) k i v)
|
514
|
+
(match-extract-vars p (match-extract-vars-step q k i v) i ()))
|
515
|
+
((match-extract-vars #(p ...) . x)
|
516
|
+
(match-extract-vars (p ...) . x))
|
517
|
+
((match-extract-vars _ (k ...) i v) (k ... v))
|
518
|
+
((match-extract-vars ___ (k ...) i v) (k ... v))
|
519
|
+
((match-extract-vars *** (k ...) i v) (k ... v))
|
520
|
+
;; This is the main part, the only place where we might add a new
|
521
|
+
;; var if it's an unbound symbol.
|
522
|
+
((match-extract-vars p (k ...) (i ...) v)
|
523
|
+
(let-syntax
|
524
|
+
((new-sym?
|
525
|
+
(syntax-rules (i ...)
|
526
|
+
((new-sym? p sk fk) sk)
|
527
|
+
((new-sym? x sk fk) fk))))
|
528
|
+
(new-sym? random-sym-to-match
|
529
|
+
(k ... ((p p-ls) . v))
|
530
|
+
(k ... v))))
|
531
|
+
))
|
532
|
+
|
533
|
+
;; Stepper used in the above so it can expand the CAR and CDR
|
534
|
+
;; separately.
|
535
|
+
|
536
|
+
(define-syntax match-extract-vars-step
|
537
|
+
(syntax-rules ()
|
538
|
+
((_ p k i v ((v2 v2-ls) ...))
|
539
|
+
(match-extract-vars p k (v2 ... . i) ((v2 v2-ls) ... . v)))
|
540
|
+
))
|
541
|
+
|
542
|
+
(define-syntax match-extract-quasiquote-vars
|
543
|
+
(syntax-rules (quasiquote unquote unquote-splicing)
|
544
|
+
((match-extract-quasiquote-vars (quasiquote x) k i v d)
|
545
|
+
(match-extract-quasiquote-vars x k i v (#t . d)))
|
546
|
+
((match-extract-quasiquote-vars (unquote-splicing x) k i v d)
|
547
|
+
(match-extract-quasiquote-vars (unquote x) k i v d))
|
548
|
+
((match-extract-quasiquote-vars (unquote x) k i v (#t))
|
549
|
+
(match-extract-vars x k i v))
|
550
|
+
((match-extract-quasiquote-vars (unquote x) k i v (#t . d))
|
551
|
+
(match-extract-quasiquote-vars x k i v d))
|
552
|
+
((match-extract-quasiquote-vars (x . y) k i v (#t . d))
|
553
|
+
(match-extract-quasiquote-vars
|
554
|
+
x
|
555
|
+
(match-extract-quasiquote-vars-step y k i v d) i ()))
|
556
|
+
((match-extract-quasiquote-vars #(x ...) k i v (#t . d))
|
557
|
+
(match-extract-quasiquote-vars (x ...) k i v d))
|
558
|
+
((match-extract-quasiquote-vars x (k ...) i v (#t . d))
|
559
|
+
(k ... v))
|
560
|
+
))
|
561
|
+
|
562
|
+
(define-syntax match-extract-quasiquote-vars-step
|
563
|
+
(syntax-rules ()
|
564
|
+
((_ x k i v d ((v2 v2-ls) ...))
|
565
|
+
(match-extract-quasiquote-vars x k (v2 ... . i) ((v2 v2-ls) ... . v) d))
|
566
|
+
))
|
567
|
+
|
568
|
+
|
569
|
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
570
|
+
;; Gimme some sugar baby.
|
571
|
+
|
572
|
+
(define-syntax match-lambda
|
573
|
+
(syntax-rules ()
|
574
|
+
((_ clause ...) (lambda (expr) (match expr clause ...)))))
|
575
|
+
|
576
|
+
(define-syntax match-lambda*
|
577
|
+
(syntax-rules ()
|
578
|
+
((_ clause ...) (lambda expr (match expr clause ...)))))
|
579
|
+
|
580
|
+
(define-syntax match-let
|
581
|
+
(syntax-rules ()
|
582
|
+
((_ (vars ...) . body)
|
583
|
+
(match-let/helper let () () (vars ...) . body))
|
584
|
+
((_ loop . rest)
|
585
|
+
(match-named-let loop () . rest))))
|
586
|
+
|
587
|
+
(define-syntax match-letrec
|
588
|
+
(syntax-rules ()
|
589
|
+
((_ vars . body) (match-let/helper letrec () () vars . body))))
|
590
|
+
|
591
|
+
(define-syntax match-let/helper
|
592
|
+
(syntax-rules ()
|
593
|
+
((_ let ((var expr) ...) () () . body)
|
594
|
+
(let ((var expr) ...) . body))
|
595
|
+
((_ let ((var expr) ...) ((pat tmp) ...) () . body)
|
596
|
+
(let ((var expr) ...)
|
597
|
+
(match-let* ((pat tmp) ...)
|
598
|
+
. body)))
|
599
|
+
((_ let (v ...) (p ...) (((a . b) expr) . rest) . body)
|
600
|
+
(match-let/helper
|
601
|
+
let (v ... (tmp expr)) (p ... ((a . b) tmp)) rest . body))
|
602
|
+
((_ let (v ...) (p ...) ((#(a ...) expr) . rest) . body)
|
603
|
+
(match-let/helper
|
604
|
+
let (v ... (tmp expr)) (p ... (#(a ...) tmp)) rest . body))
|
605
|
+
((_ let (v ...) (p ...) ((a expr) . rest) . body)
|
606
|
+
(match-let/helper let (v ... (a expr)) (p ...) rest . body))))
|
607
|
+
|
608
|
+
(define-syntax match-named-let
|
609
|
+
(syntax-rules ()
|
610
|
+
((_ loop ((pat expr var) ...) () . body)
|
611
|
+
(let loop ((var expr) ...)
|
612
|
+
(match-let ((pat var) ...)
|
613
|
+
. body)))
|
614
|
+
((_ loop (v ...) ((pat expr) . rest) . body)
|
615
|
+
(match-named-let loop (v ... (pat expr tmp)) rest . body))))
|
616
|
+
|
617
|
+
(define-syntax match-let*
|
618
|
+
(syntax-rules ()
|
619
|
+
((_ () . body)
|
620
|
+
(begin . body))
|
621
|
+
((_ ((pat expr) . rest) . body)
|
622
|
+
(match expr (pat (match-let* rest . body))))))
|
623
|
+
|
624
|
+
|
625
|
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
626
|
+
;; Otherwise COND-EXPANDed bits.
|
627
|
+
|
628
|
+
;; This *should* work, but doesn't :(
|
629
|
+
;; (define-syntax match-check-ellipse
|
630
|
+
;; (syntax-rules (...)
|
631
|
+
;; ((_ ... sk fk) sk)
|
632
|
+
;; ((_ x sk fk) fk)))
|
633
|
+
|
634
|
+
;; This is a little more complicated, and introduces a new let-syntax,
|
635
|
+
;; but should work portably in any R[56]RS Scheme. Taylor Campbell
|
636
|
+
;; originally came up with the idea.
|
637
|
+
(define-syntax match-check-ellipse
|
638
|
+
(syntax-rules ()
|
639
|
+
;; these two aren't necessary but provide fast-case failures
|
640
|
+
((match-check-ellipse (a . b) success-k failure-k) failure-k)
|
641
|
+
((match-check-ellipse #(a ...) success-k failure-k) failure-k)
|
642
|
+
;; matching an atom
|
643
|
+
((match-check-ellipse id success-k failure-k)
|
644
|
+
(let-syntax ((ellipse? (syntax-rules ()
|
645
|
+
;; iff `id' is `...' here then this will
|
646
|
+
;; match a list of any length
|
647
|
+
((ellipse? (foo id) sk fk) sk)
|
648
|
+
((ellipse? other sk fk) fk))))
|
649
|
+
;; this list of three elements will only many the (foo id) list
|
650
|
+
;; above if `id' is `...'
|
651
|
+
(ellipse? (a b c) success-k failure-k)))))
|
652
|
+
|
653
|
+
|
654
|
+
;; This is portable but can be more efficient with non-portable
|
655
|
+
;; extensions. This trick was originally discovered by Oleg Kiselyov.
|
656
|
+
|
657
|
+
(define-syntax match-check-identifier
|
658
|
+
(syntax-rules ()
|
659
|
+
;; fast-case failures, lists and vectors are not identifiers
|
660
|
+
((_ (x . y) success-k failure-k) failure-k)
|
661
|
+
((_ #(x ...) success-k failure-k) failure-k)
|
662
|
+
;; x is an atom
|
663
|
+
((_ x success-k failure-k)
|
664
|
+
(let-syntax
|
665
|
+
((sym?
|
666
|
+
(syntax-rules ()
|
667
|
+
;; if the symbol `abracadabra' matches x, then x is a
|
668
|
+
;; symbol
|
669
|
+
((sym? x sk fk) sk)
|
670
|
+
;; otherwise x is a non-symbol datum
|
671
|
+
((sym? y sk fk) fk))))
|
672
|
+
(sym? abracadabra success-k failure-k)))))
|