-
Notifications
You must be signed in to change notification settings - Fork 1
/
eiod+mde.scm
657 lines (615 loc) · 28.4 KB
/
eiod+mde.scm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
;; Mixing definition groups and expressions within bodies (SRFI-251)
;; No error checks; identifier visibility constraint is not enforced
;; Copyright notice is left intact below; code changes are marked with +MDE/-MDE
;;
;; SPDX-License-Identifier: GPL-2.0-or-later
;; eiod.scm: eval-in-one-define
;; $Id: eiod.scm,v 1.17 2005/03/26 19:57:44 al Exp $
;; A minimal implementation of r5rs eval, null-environment, and
;; scheme-report-environment. (And SRFI-46 extensions, too.)
;; Copyright 2002, 2004, 2005 Al Petrofsky <[email protected]>
;; You may redistribute and/or modify this software under the terms of
;; the GNU General Public License as published by the Free Software
;; Foundation (fsf.org); either version 2, or (at your option) any
;; later version.
;; Feel free to ask me for different licensing terms.
;; DISCLAIMER:
;; This is only intended as a demonstration of the minimum
;; implementation effort required for an r5rs eval. It serves as a
;; simple, working example of one way to implement the r5rs macro
;; system (and SRFI-46) . Among the reasons that it is ill-suited for
;; production use is the complete lack of error-checking.
;; DATA STRUCTURES:
;; An environment is a procedure that accepts any identifier and
;; returns a denotation. The denotation of an unbound identifier is
;; its name (as a symbol). A bound identifier's denotation is its
;; binding, which is a list of the current value, the binding's type
;; (keyword or variable), and the identifier's name (needed by quote).
;; identifier: [symbol | thunk]
;; denotation: [symbol | binding]
;; binding: [variable-binding | keyword-binding]
;; variable-binding: (value #f symbol)
;; keyword-binding: (special-form #t symbol)
;; special-form: [builtin | transformer]
;; A value is any arbitrary scheme value. Special forms are either a
;; symbol naming a builtin, or a transformer procedure that takes two
;; arguments: a macro use and the environment of the macro use.
;; An explicit-renaming low-level macro facility is supported, upon
;; which syntax-rules is implemented. When a syntax-rules template
;; containing a literal identifier is transcribed, the output will
;; contain a fresh identifier, which is an eq?-unique thunk that when
;; invoked returns the old identifier's denotation in the environment
;; of the macro's definition. When one of these "renamed" identifiers
;; is looked up in an environment that has no binding for it, the
;; thunk is invoked and the old denotation is returned. (The thunk
;; actually returns the old denotation wrapped inside a unique pair,
;; which is immediately unwrapped. This is necessary to ensure that
;; different rename thunks of the same denotation do not compare eq?.)
;; This environment and denotation model is similar to the one
;; described in the 1991 paper "Macros that Work" by Clinger and Rees.
;; The base environment contains eight keyword bindings and two
;; variable bindings:
;; lambda, set!, and begin are as in the standard.
;; q is like quote, but it does not handle pairs or vectors.
;; def is like define, but it does not handle the (f . args) format.
;; define-syntax makes internal syntax definitions.
;; (get-env) returns the local environment.
;; (syntax x) is like quote, but does not convert identifiers to symbols.
;; The id? procedure is a predicate for identifiers.
;; The new-id procedure takes a denotation and returns a fresh identifier.
;; Quote-and-evaluate captures all the code into the list eiod-source
;; so that we can have fun feeding eval to itself, as in
;; ((eval `(let () ,@eiod-source repl) (scheme-report-environment 5))).
;; [Note: using (and even starting) a doubly evaled repl will be *very* slow.]
(define-syntax quote-and-evaluate
(syntax-rules () ((quote-and-evaluate var . x) (begin (define var 'x) . x))))
;; The matching close parenthesis is at the end of the file.
(quote-and-evaluate eiod-source
(define (eval sexp env)
(define (new-id den) (define p (list den)) (lambda () p))
(define (old-den id) (car (id)))
(define (id? x) (or (symbol? x) (procedure? x)))
(define (id->sym id) (if (symbol? id) id (den->sym (old-den id))))
(define (den->sym den) (if (symbol? den) den (get-sym den)))
(define (empty-env id) (if (symbol? id) id (old-den id)))
(define (extend env id binding) (lambda (i) (if (eq? id i) binding (env i))))
(define (add-var var val env) (extend env var (list val #f (id->sym var))))
(define (add-key key val env) (extend env key (list val #t (id->sym key))))
(define (get-val binding) (car binding))
(define (special? binding) (cadr binding))
(define (get-sym binding) (caddr binding))
(define (set-val! binding val) (set-car! binding val))
(define (make-builtins-env)
(do ((specials '(lambda set! begin q def define-syntax syntax get-env)
(cdr specials))
(env empty-env (add-key (car specials) (car specials) env)))
((null? specials) (add-var 'new-id new-id (add-var 'id? id? env)))))
(define (eval sexp env)
(let eval-here ((sexp sexp))
(cond ((id? sexp) (get-val (env sexp)))
((not (pair? sexp)) sexp)
(else (let ((head (car sexp)) (tail (cdr sexp)))
(let ((head-binding (and (id? head) (env head))))
(if (and head-binding (special? head-binding))
(let ((special (get-val head-binding)))
(case special
((get-env) env)
((syntax) (car tail))
((lambda) (eval-lambda tail env))
((begin) (eval-seq tail env))
((set!) (set-val! (env (car tail))
(eval-here (cadr tail))))
((q) (let ((x (car tail)))
(if (id? x) (id->sym x) x)))
(else (eval-here (special sexp env)))))
(apply (eval-here head)
(map1 eval-here tail)))))))))
;; Don't use standard map because it might not be continuationally correct.
(define (map1 f l)
(if (null? l)
'()
(cons (f (car l)) (map1 f (cdr l)))))
(define (eval-seq tail env)
;; Don't use for-each because we must tail-call the last expression.
(do ((sexps tail (cdr sexps)))
((null? (cdr sexps)) (eval (car sexps) env))
(eval (car sexps) env)))
(define (eval-lambda tail env)
(lambda args
(define ienv (do ((args args (cdr args))
(vars (car tail) (cdr vars))
(ienv env (add-var (car vars) (car args) ienv)))
((not (pair? vars))
(if (null? vars) ienv (add-var vars args ienv)))))
(let loop ((ienv ienv) (ids '()) (inits '()) (body (cdr tail)))
(let ((first (car body)) (rest (cdr body)))
(let* ((head (and (pair? first) (car first)))
(binding (and (id? head) (ienv head)))
(special (and binding (special? binding) (get-val binding))))
(if (procedure? special)
(loop ienv ids inits (cons (special first ienv) rest))
(case special
((begin) (loop ienv ids inits (append (cdr first) rest)))
((def define-syntax)
(let ((id (cadr first)) (init (caddr first)))
(let* ((adder (if (eq? special 'def) add-var add-key))
(ienv (adder id 'undefined ienv)))
(loop ienv (cons id ids) (cons init inits) rest))))
(else (let ((ieval (lambda (init) (eval init ienv))))
(for-each set-val! (map ienv ids) (map1 ieval inits))
;;; +MDE (main change)
(if (null? rest)
(eval first ienv) ;; tail call
(begin
(eval first ienv)
(loop ienv '() '() rest))))))))))))
;;; -MDE
;;; (eval-seq body ienv))))))))))
;; We make a copy of the initial input to ensure that subsequent
;; mutation of it does not affect eval's result. [1]
(eval (let copy ((x sexp))
(cond ((string? x) (string-copy x))
((pair? x) (cons (copy (car x)) (copy (cdr x))))
((vector? x) (list->vector (copy (vector->list x))))
(else x)))
(or env (make-builtins-env))))
(define null-environment
(let ()
;; Syntax-rules is implemented as a macro that expands into a call
;; to the syntax-rules* procedure, which returns a transformer
;; procedure. The arguments to syntax-rules* are the arguments to
;; syntax-rules plus the current environment, which is captured
;; with get-env. Syntax-rules** is called once with some basics
;; from the base environment. It creates and returns
;; syntax-rules*.
(define (syntax-rules** id? new-id denotation-of-default-ellipsis)
(define (syntax-rules* mac-env ellipsis pat-literals rules)
(define (pat-literal? id) (memq id pat-literals))
(define (not-pat-literal? id) (not (pat-literal? id)))
(define (ellipsis-pair? x) (and (pair? x) (ellipsis? (car x))))
(define (ellipsis? x)
(if ellipsis
(eq? x ellipsis)
(and (id? x)
(eq? (mac-env x) denotation-of-default-ellipsis))))
;; List-ids returns a list of the non-ellipsis ids in a
;; pattern or template for which (pred? id) is true. If
;; include-scalars is false, we only include ids that are
;; within the scope of at least one ellipsis.
(define (list-ids x include-scalars pred?)
(let collect ((x x) (inc include-scalars) (l '()))
(cond ((id? x) (if (and inc (pred? x)) (cons x l) l))
((vector? x) (collect (vector->list x) inc l))
((pair? x)
(if (ellipsis-pair? (cdr x))
(collect (car x) #t (collect (cddr x) inc l))
(collect (car x) inc (collect (cdr x) inc l))))
(else l))))
;; Returns #f or an alist mapping each pattern var to a part of
;; the input. Ellipsis vars are mapped to lists of parts (or
;; lists of lists ...).
(define (match-pattern pat use use-env)
(call-with-current-continuation
(lambda (return)
(define (fail) (return #f))
(let match ((pat (cdr pat)) (sexp (cdr use)) (bindings '()))
(define (continue-if condition) (if condition bindings (fail)))
(cond
((id? pat)
(if (pat-literal? pat)
(continue-if (and (id? sexp)
(eq? (use-env sexp) (mac-env pat))))
(cons (cons pat sexp) bindings)))
((vector? pat)
(or (vector? sexp) (fail))
(match (vector->list pat) (vector->list sexp) bindings))
((not (pair? pat)) (continue-if (equal? pat sexp)))
((ellipsis-pair? (cdr pat))
(let* ((tail-len (length (cddr pat)))
(sexp-len (if (list? sexp) (length sexp) (fail)))
(seq-len (- sexp-len tail-len))
(sexp-tail (begin (if (negative? seq-len) (fail))
(list-tail sexp seq-len)))
(seq (reverse (list-tail (reverse sexp) tail-len)))
(vars (list-ids (car pat) #t not-pat-literal?)))
(define (match1 sexp) (map cdr (match (car pat) sexp '())))
(append (apply map list vars (map match1 seq))
(match (cddr pat) sexp-tail bindings))))
((pair? sexp) (match (car pat) (car sexp)
(match (cdr pat) (cdr sexp) bindings)))
(else (fail)))))))
(define (expand-template pat tmpl top-bindings)
;; New-literals is an alist mapping each literal id in the
;; template to a fresh id for inserting into the output. It
;; might have duplicate entries mapping an id to two different
;; fresh ids, but that's okay because when we go to retrieve a
;; fresh id, assq will always retrieve the first one.
(define new-literals
(map (lambda (id) (cons id (new-id (mac-env id))))
(list-ids tmpl #t (lambda (id)
(not (assq id top-bindings))))))
(define ellipsis-vars (list-ids (cdr pat) #f not-pat-literal?))
(define (list-ellipsis-vars subtmpl)
(list-ids subtmpl #t (lambda (id) (memq id ellipsis-vars))))
(let expand ((tmpl tmpl) (bindings top-bindings))
(let expand-part ((tmpl tmpl))
(cond
((id? tmpl) (cdr (or (assq tmpl bindings)
(assq tmpl top-bindings)
(assq tmpl new-literals))))
((vector? tmpl)
(list->vector (expand-part (vector->list tmpl))))
((pair? tmpl)
(if (ellipsis-pair? (cdr tmpl))
(let ((vars-to-iterate (list-ellipsis-vars (car tmpl))))
(define (lookup var) (cdr (assq var bindings)))
(define (expand-using-vals . vals)
(expand (car tmpl) (map cons vars-to-iterate vals)))
(let ((val-lists (map lookup vars-to-iterate)))
(append (apply map expand-using-vals val-lists)
(expand-part (cddr tmpl)))))
(cons (expand-part (car tmpl)) (expand-part (cdr tmpl)))))
(else tmpl)))))
(lambda (use use-env)
(let loop ((rules rules))
(let* ((rule (car rules)) (pat (car rule)) (tmpl (cadr rule)))
(cond ((match-pattern pat use use-env) =>
(lambda (bindings) (expand-template pat tmpl bindings)))
(else (loop (cdr rules))))))))
syntax-rules*)
(define macro-defs
'((define-syntax quote
(syntax-rules ()
('(x . y) (cons 'x 'y))
('#(x ...) (list->vector '(x ...)))
('x (q x))))
(define-syntax quasiquote
(syntax-rules (unquote unquote-splicing quasiquote)
(`,x x)
(`(,@x . y) (append x `y))
((_ `x . d) (cons 'quasiquote (quasiquote (x) d)))
((_ ,x d) (cons 'unquote (quasiquote (x) . d)))
((_ ,@x d) (cons 'unquote-splicing (quasiquote (x) . d)))
((_ (x . y) . d)
(cons (quasiquote x . d) (quasiquote y . d)))
((_ #(x ...) . d)
(list->vector (quasiquote (x ...) . d)))
((_ x . d) 'x)))
(define-syntax do
(syntax-rules ()
((_ ((var init . step) ...)
ending
expr ...)
(let loop ((var init) ...)
(cond ending (else expr ... (loop (begin var . step) ...)))))))
(define-syntax letrec* ;; +MDE
(syntax-rules ()
((_ ((var init) ...) . body)
(let () (def var init) ... (let () . body)))))
(define-syntax letrec
(syntax-rules ()
((_ ((var init) ...) . body)
(let () (def var init) ... (let () . body)))))
(define-syntax letrec-syntax
(syntax-rules ()
((_ ((key trans) ...) . body)
(let () (define-syntax key trans) ... (let () . body)))))
(define-syntax let-syntax
(syntax-rules ()
((_ () . body) (let () . body))
((_ ((key trans) . bindings) . body)
(letrec-syntax ((temp trans))
(let-syntax bindings (letrec-syntax ((key temp)) . body))))))
(define-syntax let*
(syntax-rules ()
((_ () . body) (let () . body))
((_ (first . more) . body)
(let (first) (let* more . body)))))
(define-syntax let
(syntax-rules ()
((_ ((var init) ...) . body)
((lambda (var ...) . body)
init ...))
((_ name ((var init) ...) . body)
((letrec ((name (lambda (var ...) . body)))
name)
init ...))))
(define-syntax case
(syntax-rules ()
((_ x (test . exprs) ...)
(let ((key x))
(cond ((case-test key test) . exprs)
...)))))
(define-syntax case-test
(syntax-rules (else) ((_ k else) #t) ((_ k atoms) (memv k 'atoms))))
(define-syntax cond
(syntax-rules (else =>)
((_) #f)
((_ (else . exps)) (begin #f . exps))
((_ (x) . rest) (or x (cond . rest)))
((_ (x => proc) . rest)
(let ((tmp x)) (cond (tmp (proc tmp)) . rest)))
((_ (x . exps) . rest)
(if x (begin . exps) (cond . rest)))))
(define-syntax and
(syntax-rules ()
((_) #t)
((_ test) test)
((_ test . tests) (if test (and . tests) #f))))
(define-syntax or
(syntax-rules ()
((_) #f)
((_ test) test)
((_ test . tests) (let ((x test)) (if x x (or . tests))))))
(define-syntax define
(syntax-rules ()
((_ (var . args) . body)
(define var (lambda args . body)))
((_ var init) (def var init))))
(define-syntax if
(syntax-rules () ((_ x y ...) (if* x (lambda () y) ...))))
(define-syntax delay
(syntax-rules () ((_ x) (delay* (lambda () x)))))))
(define (if* a b . c) (if a (b) (if (pair? c) ((car c)))))
(define (delay* thunk) (delay (thunk)))
(define (null-env)
((eval `(lambda (cons append list->vector memv delay* if* syntax-rules**)
((lambda (syntax-rules*)
(define-syntax syntax-rules
(syntax-rules* (get-env) #f (syntax ())
(syntax (((_ (lit ...) . rules)
(syntax-rules #f (lit ...) . rules))
((_ ellipsis lits . rules)
(syntax-rules* (get-env) (syntax ellipsis)
(syntax lits) (syntax rules)))))))
((lambda () ,@macro-defs (get-env))))
(syntax-rules** id? new-id ((get-env) (syntax ...)))))
#f)
cons append list->vector memv delay* if* syntax-rules**))
(define promise (delay (null-env)))
(lambda (version)
(if (= version 5)
(force promise)
(open-input-file "sheep-herders/r^-1rs.ltx")))))
(define scheme-report-environment
(let-syntax
((extend-env
(syntax-rules ()
((extend-env env . names)
((eval '(lambda names (get-env)) env)
. names)))))
(let ()
(define (r5-env)
(extend-env (null-environment 5)
eqv? eq? equal?
number? complex? real? rational? integer? exact? inexact?
= < > <= >= zero? positive? negative? odd? even?
max min + * - /
abs quotient remainder modulo gcd lcm numerator denominator
floor ceiling truncate round rationalize
exp log sin cos tan asin acos atan sqrt expt
make-rectangular make-polar real-part imag-part magnitude angle
exact->inexact inexact->exact
number->string string->number
not boolean?
pair? cons car cdr set-car! set-cdr! caar cadr cdar cddr
caaar caadr cadar caddr cdaar cdadr cddar cdddr
caaaar caaadr caadar caaddr cadaar cadadr caddar cadddr
cdaaar cdaadr cdadar cdaddr cddaar cddadr cdddar cddddr
null? list? list length append reverse list-tail list-ref
memq memv member assq assv assoc
symbol? symbol->string string->symbol
char? char=? char<? char>? char<=? char>=?
char-ci=? char-ci<? char-ci>? char-ci<=? char-ci>=?
char-alphabetic? char-numeric? char-whitespace?
char-upper-case? char-lower-case?
char->integer integer->char char-upcase char-downcase
string? make-string string string-length string-ref string-set!
string=? string-ci=? string<? string>? string<=? string>=?
string-ci<? string-ci>? string-ci<=? string-ci>=?
substring string-append string->list list->string
string-copy string-fill!
vector? make-vector vector vector-length vector-ref vector-set!
vector->list list->vector vector-fill!
procedure? apply map for-each force
call-with-current-continuation
values call-with-values dynamic-wind
eval scheme-report-environment null-environment
call-with-input-file call-with-output-file
input-port? output-port? current-input-port current-output-port
with-input-from-file with-output-to-file
open-input-file open-output-file close-input-port close-output-port
read read-char peek-char eof-object? char-ready?
write display newline write-char))
(define promise (delay (r5-env)))
(lambda (version)
(if (= version 5)
(force promise)
(open-input-file "sheep-herders/r^-1rs.ltx"))))))
;; [1] Some claim that this is not required, and that it is compliant for
;;
;; (let* ((x (string #\a))
;; (y (eval x (null-environment 5))))
;; (string-set! x 0 #\b)
;; y)
;;
;; to return "b", but I say that's as bogus as if
;;
;; (let* ((x (string #\1))
;; (y (string->number x)))
;; (string-set! x 0 #\2)
;; y)
;;
;; returned 2. Most implementations disagree with me, however.
;;
;; Note: it would be fine to pass through those strings (and pairs and
;; vectors) that are immutable, but we can't portably detect them.
;; Repl provides a simple read-eval-print loop. It semi-supports
;; top-level definitions and syntax definitions, but each one creates
;; a new binding whose region does not include anything that came
;; before the definition, so if you want mutually recursive top-level
;; procedures, you have to do it the hard way:
;; (define f #f)
;; (define (g) (f))
;; (set! f (lambda () (g)))
;; Repl does not support macro uses that expand into top-level definitions.
(define (repl)
(let repl ((env (scheme-report-environment 5)))
(display "eiod> ")
(let ((exp (read)))
(if (not (eof-object? exp))
(case (and (pair? exp) (car exp))
((define define-syntax) (repl (eval `(let () ,exp (get-env))
env)))
(else
(for-each (lambda (val) (write val) (newline))
(call-with-values (lambda () (eval exp env))
list))
(repl env)))))))
(define (tests noisy)
(define env (scheme-report-environment 5))
(define fail-count 0) ;; +MDE
(for-each
(lambda (x)
(let* ((exp (car x))
(expected (cadr x)))
(if noisy (begin (display "Trying: ") (write exp) (newline)))
(let* ((result (eval exp env))
(success (equal? result expected)))
(if (not success)
(begin (set! fail-count (+ 1 fail-count))
(display "Failed: ")
(if (not noisy) (write exp))
(display " returned ")
(write result)
(display ", not ")
(write expected)
(newline))))))
'((1 1)
(#t #t)
("hi" "hi")
(#\a #\a)
('1 1)
('foo foo)
('(a b) (a b))
('#(a b) #(a b))
(((lambda (x) x) 1) 1)
((+ 1 2) 3)
(((lambda (x) (set! x 2) x) 1) 2)
(((lambda () (define x 1) x)) 1)
(((lambda () (define (x) 1) (x))) 1)
((begin 1 2) 2)
(((lambda () (begin (define x 1)) x)) 1)
(((lambda () (begin) 1)) 1)
((let-syntax ((f (syntax-rules () ((_) 1)))) (f)) 1)
((letrec-syntax ((f (syntax-rules () ((_) (f 1)) ((_ x) x)))) (f)) 1)
((let-syntax ((f (syntax-rules () ((_ x ...) '(x ...))))) (f 1 2)) (1 2))
((let-syntax ((f (syntax-rules ()
((_ (x y) ...) '(x ... y ...))
((_ x ...) '(x ...)))))
(f (x1 y1) (x2 y2)))
(x1 x2 y1 y2))
((let-syntax ((let (syntax-rules ()
((_ ((var init) ...) . body)
'((lambda (var ...) . body) init ...)))))
(let ((x 1) (y 2)) (+ x y)))
((lambda (x y) (+ x y)) 1 2))
((let ((x 1)) x) 1)
((let* ((x 1) (x (+ x 1))) x) 2)
((let ((call/cc call-with-current-continuation))
(letrec ((x (call/cc list)) (y (call/cc list)))
(if (procedure? x) (x (pair? y)))
(if (procedure? y) (y (pair? x)))
(let ((x (car x)) (y (car y)))
(and (call/cc x) (call/cc y) (call/cc x)))))
#t)
((if 1 2) 2)
((if #f 2 3) 3)
((and 1 #f 2) #f)
((force (delay 1)) 1)
((let* ((x 0) (p (delay (begin (set! x (+ x 1)) x)))) (force p) (force p))
1)
((let-syntax
((foo (syntax-rules ()
((_ (x ...) #(y z ...) ...)
'((z ...) ... #((x y) ...))))))
(foo (a b c) #(1 i j) #(2 k l) #(3 m n)))
((i j) (k l) (m n) #((a 1) (b 2) (c 3))))
((do ((vec (make-vector 5))
(i 0 (+ i 1)))
((= i 5) vec)
(vector-set! vec i i))
#(0 1 2 3 4))
((let-syntax ((f (syntax-rules (x) ((_ x) 1) ((_ y) 2))))
(define x (f x))
x)
2)
((let-syntax ((f (syntax-rules () ((_) 'x)))) (f))
x)
((let-syntax ((f (syntax-rules ()
((_) (let ((x 1))
(let-syntax ((f (syntax-rules () ((_) 'x))))
(f)))))))
(f))
x)
((let-syntax
((f (syntax-rules ()
((f e a ...)
(let-syntax
((g (syntax-rules ::: ()
((g n :::) '((a e n :::) ...)))))
(g 1 2 3))))))
(f ::: x y z))
((x ::: 1 2 3) (y ::: 1 2 3) (z ::: 1 2 3)))
((let-syntax ((m (syntax-rules () ((m x ... y) (y x ...)))))
(m 1 2 3 -))
-4)
;;; +MDE specific tests
((let ((x 0) (l '()))
(set! x 1)
(define (pushv v) (set! l (cons v l)))
(pushv "the result is")
(define (foo) x)
(define x 42)
(pushv ": ")
(pushv (foo))
(reverse l))
("the result is" ": " 42))
((let ((x 0) (l '()))
(set! x 1)
(define (pushv v) (set! l (cons v l)))
(pushv "the result is")
(define (foo) x)
(define xx 42)
(pushv ": ")
(pushv (foo))
(reverse l))
("the result is" ": " 1))
((let ((x 0) (l '()))
(set! x 1)
(define-syntax define-thunk
(syntax-rules ()
((_ i v) (define (i) v))))
(define (pushv v) (set! l (cons v l)))
(pushv "the result is")
(define-thunk foo x)
(define xx 42)
(pushv ": ")
(pushv (foo))
(reverse l))
("the result is" ": " 1))
((let ((x 0) (l '()))
(set! x 1)
(define-syntax incx (syntax-rules () ((_ v) (set! x (+ x v)))))
(define (pushv v) (set! l (cons v l)))
(define-syntax pushx (syntax-rules () ((_) (pushv x))))
(pushx)
(incx 14)
(pushx)
l)
(15 1))
))
(if noisy (begin (write fail-count) (display " failures.") (newline)))) ;; +MDE
;; matching close paren for quote-and-evaluate at beginning of file.
)
(tests #t) ;; +MDE