1
2;;----------------------------------------------------------------------
3;; more-scheme : case, do, etc. - remaining syntax
4
5(module more-scheme '#%kernel
6  (#%require "define-et-al.rkt" "qq-and-or.rkt" "cond.rkt" "define.rkt" '#%paramz "case.rkt" "logger.rkt"
7             "member.rkt"
8             (for-syntax '#%kernel "stx.rkt" "define-et-al.rkt" "qq-and-or.rkt" "cond.rkt" "stxcase-scheme.rkt" "qqstx.rkt"))
9
10  ;; For `old-case`:
11  (define-syntax case-test
12    (lambda (x)
13      (syntax-case x ()
14        ;; For up to 3 elements, inline `eqv?' tests:
15	[(_ x (k))
16         (syntax (eqv? x 'k))]
17	[(_ x (k1 k2))
18         (syntax (let ([tmp x]) (if (eqv? tmp 'k1) #t (eqv? tmp 'k2))))]
19	[(_ x (k1 k2 k3))
20         (syntax (let ([tmp x]) (if (eqv? tmp 'k1) #t (if (eqv? tmp 'k2) #t (eqv? tmp 'k3)))))]
21	[(_ x (k ...))
22	 (syntax (memv x '(k ...)))])))
23
24  ;; Mostly from Dybvig:
25  (define-syntax (old-case x)
26    (syntax-case* x (else) (let ([else-stx (datum->syntax #f 'else)])
27                             (lambda (a b) (free-identifier=? a else-stx)))
28      ((_ v)
29       (syntax (#%expression (begin v (void)))))
30      ((_ v (else e1 e2 ...))
31       (syntax/loc x (#%expression (begin v (let-values () e1 e2 ...)))))
32      ((_ v ((k ...) e1 e2 ...))
33       (syntax/loc x (if (case-test v (k ...)) (let-values () e1 e2 ...) (void))))
34      ((self v ((k ...) e1 e2 ...) c1 c2 ...)
35       (syntax/loc x (let ((x v))
36                       (if (case-test x (k ...))
37                           (let-values () e1 e2 ...)
38                           (self x c1 c2 ...)))))
39      ((_ v (bad e1 e2 ...) . rest)
40       (raise-syntax-error
41        #f
42        "bad syntax (not a datum sequence)"
43        x
44        (syntax bad)))
45      ((_ v clause . rest)
46       (raise-syntax-error
47        #f
48        "bad syntax (missing expression after datum sequence)"
49        x
50        (syntax clause)))
51      ((_ . v)
52       (not (null? (syntax-e (syntax v))))
53       (raise-syntax-error
54        #f
55        "bad syntax (illegal use of `.')"
56        x))))
57
58  ;; From Dybvig:
59  (define-syntax do
60    (lambda (orig-x)
61      (syntax-case orig-x ()
62	((_ ((var init . step) ...) (e0 e1 ...) c ...)
63	 (with-syntax (((step ...)
64			(map (lambda (v s)
65			       (syntax-case s ()
66				 (() v)
67				 ((e) (syntax e))
68				 (_ (raise-syntax-error
69				     #f
70				     "bad variable syntax"
71				     orig-x))))
72			     (syntax->list (syntax (var ...)))
73			     (syntax->list (syntax (step ...))))))
74	   (syntax/loc orig-x
75             (let doloop ((var init) ...)
76               (if e0
77                   (begin (void) e1 ...)
78                   (begin c ... (doloop step ...))))))))))
79
80  (define-syntax parameterize
81    (lambda (stx)
82      (syntax-case stx ()
83	[(_ () expr1 expr ...)
84	 (syntax (let () expr1 expr ...))]
85	[(_ ([param val] ...) expr1 expr ...)
86	 (with-syntax ([(p/v ...)
87			(apply append
88			       (map list
89				    (syntax->list #'(param ...))
90				    (syntax->list #'(val ...))))])
91           (syntax-protect
92            (syntax/loc stx
93              (with-continuation-mark
94                  parameterization-key
95                  (extend-parameterization
96                   (continuation-mark-set-first #f parameterization-key)
97                   p/v ...)
98                (let ()
99                  expr1
100                  expr ...)))))])))
101
102  (define-syntax parameterize*
103    (syntax-rules ()
104      [(_ () body1 body ...)
105       (let () body1 body ...)]
106      [(_ ([lhs1 rhs1] [lhs rhs] ...) body1 body ...)
107       (parameterize ([lhs1 rhs1])
108         (parameterize* ([lhs rhs] ...)
109                        body1 body ...))]))
110
111  (define (current-parameterization)
112    (continuation-mark-set-first #f parameterization-key))
113
114  (define (call-with-parameterization paramz thunk)
115    (unless (parameterization? paramz)
116      (raise-argument-error 'call-with-parameterization "parameterization?" 0 paramz thunk))
117    (unless (and (procedure? thunk)
118		 (procedure-arity-includes? thunk 0))
119      (raise-argument-error 'call-with-parameterization "(-> any)" 1 paramz thunk))
120    (with-continuation-mark
121	parameterization-key
122	paramz
123      (thunk)))
124
125  (define-syntax parameterize-break
126    (lambda (stx)
127      (syntax-case stx ()
128	[(_ bool-expr expr1 expr ...)
129         (syntax-protect
130          (syntax/loc stx
131            (with-continuation-mark
132                break-enabled-key
133                (make-thread-cell (and bool-expr #t))
134              (begin
135                (check-for-break)
136                (let ()
137                  expr1
138                  expr ...)))))])))
139
140  (define-values (struct:break-paramz make-break-paramz break-paramz? break-paramz-ref break-paramz-set!)
141    (make-struct-type 'break-parameterization #f 1 0 #f))
142
143  (-define-struct break-parameterization (cell))
144
145  (define (current-break-parameterization)
146    (make-break-paramz (continuation-mark-set-first #f break-enabled-key)))
147
148  (define (call-with-break-parameterization paramz thunk)
149    (unless (break-paramz? paramz)
150      (raise-argument-error 'call-with-break-parameterization "break-parameterization?" 0 paramz thunk))
151    (unless (and (procedure? thunk)
152		 (procedure-arity-includes? thunk 0))
153      (raise-argument-error 'call-with-parameterization "(-> any)" 1 paramz thunk))
154    (begin0
155     (with-continuation-mark
156	 break-enabled-key
157	 (break-paramz-ref paramz 0)
158       (begin
159	 (check-for-break)
160	 (thunk)))
161     (check-for-break)))
162
163  (define (select-handler/no-breaks e bpz l)
164    (with-continuation-mark
165        break-enabled-key
166        ;; make a fresh thread cell so that the shared one isn't mutated
167        (make-thread-cell #f)
168      (let loop ([l l])
169        (cond
170         [(null? l)
171          (raise e)]
172         [((caar l) e)
173          (begin0
174           ((cdar l) e)
175           (with-continuation-mark
176               break-enabled-key
177               bpz
178             (check-for-break)))]
179         [else
180          (loop (cdr l))]))))
181
182  (define (select-handler/breaks-as-is e bpz l)
183    (cond
184     [(null? l)
185      (raise e)]
186     [((caar l) e)
187      (with-continuation-mark
188	  break-enabled-key
189	  bpz
190	(begin
191	  (check-for-break)
192	  ((cdar l) e)))]
193     [else
194      (select-handler/breaks-as-is e bpz (cdr l))]))
195
196  (define false-thread-cell (make-thread-cell #f))
197
198
199  (define (check-with-handlers-in-context handler-prompt-key)
200    (unless (continuation-prompt-available? handler-prompt-key)
201      (error 'with-handlers
202             "exception handler used out of context")))
203
204  (define handler-prompt-key (make-continuation-prompt-tag 'handler-prompt-tag))
205
206  (define (call-handled-body bpz handle-proc body-thunk)
207    ;; Disable breaks here, so that when the exception handler jumps
208    ;;  to run a handler, breaks are disabled for the handler
209    (with-continuation-mark
210        break-enabled-key
211        false-thread-cell
212      (call-with-continuation-prompt
213       (lambda (bpz body-thunk)
214         ;; Restore the captured break parameterization for
215         ;;  evaluating the `with-handlers' body. In this
216         ;;  special case, no check for breaks is needed,
217         ;;  because bpz is quickly restored past call/ec.
218         ;;  Thus, `with-handlers' can evaluate its body in
219         ;;  tail position.
220         (with-continuation-mark
221             break-enabled-key
222             bpz
223           (with-continuation-mark
224               exception-handler-key
225               (lambda (e)
226                 ;; Deliver the exception to the escape handler:
227                 (abort-current-continuation
228                  handler-prompt-key
229                  e))
230             (body-thunk))))
231       handler-prompt-key
232       handle-proc
233       bpz body-thunk)))
234
235  (define-syntaxes (with-handlers with-handlers*)
236    (let ([wh
237	   (lambda (disable-break?)
238	     (lambda (stx)
239	       (syntax-case stx ()
240		 [(_ () expr1 expr ...) (syntax/loc stx (let () expr1 expr ...))]
241		 [(_ ([pred handler] ...) expr1 expr ...)
242		  (with-syntax ([(pred-name ...) (generate-temporaries (map (lambda (x) 'with-handlers-predicate)
243									    (syntax->list #'(pred ...))))]
244				[(handler-name ...) (generate-temporaries (map (lambda (x) 'with-handlers-handler)
245									       (syntax->list #'(handler ...))))])
246                    (syntax-protect
247                     (quasisyntax/loc stx
248                       (let-values ([(pred-name) pred] ...
249                                    [(handler-name) handler] ...)
250                         ;; Capture current break parameterization, so we can use it to
251                         ;;  evaluate the body
252                         (let ([bpz (continuation-mark-set-first #f break-enabled-key)])
253                           (call-handled-body
254                            bpz
255                            (lambda (e)
256                              (#,(if disable-break?
257                                     #'select-handler/no-breaks
258                                     #'select-handler/breaks-as-is)
259                               e bpz
260                               (list (cons pred-name handler-name) ...)))
261                            #,(syntax/loc stx
262                                (lambda ()
263                                  expr1 expr ...))))))))])))])
264      (values (wh #t) (wh #f))))
265
266  (define (call-with-exception-handler exnh thunk)
267    ;; The `begin0' ensures that we don't overwrite an enclosing
268    ;;  exception handler.
269    (begin0
270     (with-continuation-mark
271         exception-handler-key
272         exnh
273       (thunk))
274     (void)))
275
276  (define-syntax set!-values
277    (lambda (stx)
278      (syntax-case stx ()
279	[(_ () expr) (syntax (let-values ([() expr]) (void)))]
280	[(_ (id) expr) (identifier? (syntax id)) (syntax (set! id expr))]
281	[(_ (id ...) expr)
282	 (let ([ids (stx->list (syntax (id ...)))])
283	   (for-each
284	    (lambda (id)
285	      (unless (identifier? id)
286		(raise-syntax-error #f
287				    "not an identifier"
288				    stx
289				    id)))
290	    ids)
291	   (let ([dup (check-duplicate-identifier ids)])
292	     (when dup
293	       (raise-syntax-error #f
294				   "duplicate identifier"
295				   stx
296				   dup))))
297	 (with-syntax ([(temp ...) (generate-temporaries (syntax (id ...)))])
298	   (syntax/loc
299	    stx
300	    (let-values ([(temp ...) expr])
301	      (set! id temp) ...)))])))
302
303  (define-values (call/cc) call-with-current-continuation)
304
305  (define-syntax let/cc
306    (lambda (stx)
307      (syntax-case stx ()
308	[(_ var body1 body ...)
309	 (syntax/loc stx (call/cc (lambda (var) body1 body ...)))])))
310
311  (define-syntax fluid-let
312    (lambda (stx)
313      (syntax-case stx ()
314	[(_ () body1 body ...) (syntax/loc stx (let () body1 body ...))]
315	[(_ ([name val] ...) body1 body ...)
316	 (with-syntax ([(tmp ...) (generate-temporaries (syntax (name ...)))])
317	   (syntax/loc
318	    stx
319	    (let ([tmp val] ...)
320	      (let ([swap
321		     (lambda ()
322		       (let ([s tmp])
323			 (set! tmp name)
324			 (set! name s))
325		       ...)])
326		(dynamic-wind
327		    swap
328		    (lambda () body1 body ...)
329		    swap)))))])))
330
331  (define-syntax time
332    (lambda (stx)
333      (syntax-case stx ()
334	[(_ expr1 expr ...)
335	 (syntax/loc
336	  stx
337	  (let-values ([(v cpu user gc) (time-apply (lambda () expr1 expr ...) null)])
338	    (printf "cpu time: ~s real time: ~s gc time: ~s\n" cpu user gc)
339	    (apply values v)))])))
340
341  (define not-there (gensym))
342
343  (define (do-hash-update who mut? set ht key xform default)
344    (unless (variable-reference-from-unsafe? (#%variable-reference))
345      (unless (and (hash? ht)
346                   (if mut?
347                       (not (immutable? ht))
348                       (immutable? ht)))
349        (raise-argument-error who (if mut? "(and/c hash? (not/c immutable?))" "(and/c hash? immutable?)") ht))
350      (unless (and (procedure? xform)
351                   (procedure-arity-includes? xform 1))
352        (raise-argument-error who "(any/c . -> . any/c)" xform)))
353    (let ([v (hash-ref ht key default)])
354      (if (eq? v not-there)
355          (raise-mismatch-error who "no value found for key: " key)
356          (set ht key (xform v)))))
357
358  (define hash-update
359    (case-lambda
360      [(ht key xform default)
361       (do-hash-update 'hash-update #f hash-set ht key xform default)]
362      [(ht key xform)
363       (hash-update ht key xform not-there)]))
364
365  (define hash-update!
366    (case-lambda
367      [(ht key xform default)
368       (do-hash-update 'hash-update! #t hash-set! ht key xform default)]
369      [(ht key xform)
370       (hash-update! ht key xform not-there)]))
371
372  (define (hash-has-key? ht key)
373    (unless (hash? ht)
374      (raise-argument-error 'hash-has-key? "hash?" 0 ht key))
375    (not (eq? not-there (hash-ref ht key not-there))))
376
377  (define (hash-ref! ht key new)
378    (unless (and (hash? ht)
379                 (not (immutable? ht)))
380      (raise-argument-error 'hash-ref! "(and/c hash? (not/c immutable?))" 0 ht key new))
381    (let ([v (hash-ref ht key not-there)])
382      (if (eq? not-there v)
383          (let ([n (if (procedure? new) (new) new)])
384            (hash-set! ht key n)
385            n)
386          v)))
387
388  (#%provide case old-case do
389             parameterize parameterize* current-parameterization call-with-parameterization
390             parameterize-break current-break-parameterization call-with-break-parameterization
391             (rename break-paramz? break-parameterization?)
392             with-handlers with-handlers* call-with-exception-handler
393             set!-values
394             let/cc call/cc fluid-let time
395             log-fatal log-error log-warning log-info log-debug define-logger
396             hash-ref! hash-has-key? hash-update hash-update!))
397