aboutsummaryrefslogtreecommitdiffstats
path: root/csc/macros.csc
blob: 41225aed5c95f5a9fe8dc2596197dc7e2af254fa (plain) (blame)
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
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
(define-library (csc macros)
  (export
    builtins-environment
    expand
    expand-body
    macro-syntax-error?
    make-environment)
  (import (scheme base)
          (only (csc assert) assert)
          (only (csc format) sprintf)
          (only (csc gensym)
            gensym
            gensym=?)
          (only (csc hash-map)
            alist->map
            hash-bytevector
            insert
            key-not-found-error?
            lookup
            map-for-each
            merge)
          (only (csc ir1)
            define-syntax-name
            define-syntax-transformer
            define-syntax?
            lexical-ref-gensym
            lexical-ref?
            library-define-expression
            library-define-ref
            library-define?
            library-ref-name
            library-ref?
            make-call
            make-constant
            make-lambda
            make-letrec
            make-lexical-ref
            make-library-define
            make-library-ref
            make-sequence
            sequence-head
            sequence-tail
            sequence?)
          (only (csc list)
            revappend
            unzip)
          (only (csc loop)
            loop
            return)
          (only (csc match) match)
          (only (csc strings) join)
          (only (csc vec)
            vec
            vec-append
            vec-length
            vec-ref))
  (begin


    (define-record-type <macro-transformer>
      (make-macro-transformer transformer)
      macro-transformer?
      (transformer transformer-function))


    (define-record-type <macro-syntax-error>
      (make-macro-syntax-error message irritants)
      macro-syntax-error?
      (message syntax-error-object-message)
      (irritants syntax-error-object-irritants))


    (define (raise-syntax-error message . irritants)
      (raise (make-macro-syntax-error message irritants)))


    ; symbols is a map with identifiers as keys, and the values can be one of:
    ; - <lexical-ref>,
    ; - <library-ref>,
    ; - or <macro-transformer>.
    ; The first two correspond to variables bound lexically or from a library,
    ; and the third represents a macro transformer bound in the
    ; current context.
    (define-record-type <environment>
      (make-environment symbols library)
      environment?
      (symbols environment-substitutions)
      (library environment-library))


    (define-record-type <syntax-object>
      (make-syntax-object expression environment marks)
      syntax-object?
      (expression syntax-object-expression)
      (environment syntax-object-environment)
      (marks syntax-object-marks))


    (define (wrap-syntax expression environment)
      (if (syntax-object? expression)
        expression
        (make-syntax-object expression environment '())))


    (define (syntax->expression s)
      (if (syntax-object? s)
        (syntax-object-expression s)
        s))


    (define (marks s)
      (if (syntax-object? s)
        (syntax-object-marks s)
        '()))


    (define (add-binding identifier binding environment)
      (make-environment
        (insert (environment-substitutions environment) identifier binding)
        (environment-library environment)))


    (define (with-binding identifier binding syntax)
      (make-syntax-object
        (syntax->expression syntax)
        (add-binding identifier binding (syntax-object-environment syntax))
        (marks syntax)))


    (define (identifier? s)
      (or (symbol? s)
          (and (syntax-object? s)
               (symbol? (syntax-object-expression s)))))


    (define (marks=? m1 m2)
      (and
        (= (length m1) (length m2))
        (let loop ((m1 m1)
                   (m2 m2))
          (if (null? m1)
            #t
            (and (= (car m1) (car m2)) (loop (cdr m1) (cdr m2)))))))


    (define (identifier-name s)
      (if (syntax-object? s)
        (syntax-object-expression s)
        s))


    (define (bound-identifier=? s1 s2)
      (and (symbol=? (identifier-name s1) (identifier-name s2))
           (marks=? (marks s1) (marks s2))))


    (define (binding=? b1 b2)
      (or (and (lexical-ref? b1)
               (lexical-ref? b2)
               (gensym=? (lexical-ref-gensym b1) (lexical-ref-gensym b2)))
          (and (library-ref? b1)
               (library-ref? b2)
               (symbol=? (library-ref-name b1) (library-ref-name b2)))
          (and (macro-transformer? b1)
               (macro-transformer? b2)
               (eq? (transformer-function b1) (transformer-function b2)))))


    ; free-identifier=? only works on wrapped syntax objects.
    (define (free-identifier=? s1 s2)
      (let ((s1-binding (guard (e ((key-not-found-error? e) #f))
                          (lookup (environment-substitutions (syntax-object-environment s1)) s1)))
            (s2-binding (guard (e ((key-not-found-error? e) #f))
                          (lookup (environment-substitutions (syntax-object-environment s2)) s2))))
        (or (and (not s1-binding)
                 (not s2-binding)
                 (symbol=? (identifier-name s1) (identifier-name s2)))
            (binding=? s1-binding s2-binding))))


    (define *next-mark* 0)


    (define (new-mark)
      (let ((m *next-mark*))
        (set! *next-mark* (+ 1 *next-mark*))
        m))


    (define (add-mark mark expression)
      (make-syntax-object
        (syntax-object-expression expression)
        (syntax-object-environment expression)
        (if (and (pair? (syntax-object-marks expression))
                 (not (car (syntax-object-marks expression))))  ; Anti-mark.
          (cdr (syntax-object-marks expression))
          (cons mark (syntax-object-marks expression)))))


    (define (add-marks marks expression)
      (let loop ((marks marks)
                 (expression expression))
        (match marks
          ('() expression)
          ((mark . marks)
           (loop marks (add-mark mark expression))))))


    (define (anti-mark expression)
      (add-mark #f expression))


    (define (decorate marks expression environment)
      (add-marks marks (wrap-syntax expression environment)))


    (define (with-wrap expression parent)
      (decorate (marks parent) expression (syntax-object-environment parent)))


    (define (syntax-map f expr)
      (with-wrap (f (syntax->expression expr)) expr))


    (define-record-type <syntax-case-no-match>
      (make-syntax-case-no-match)
      syntax-case-no-match?)


    (define-syntax syntax-case-match-pattern
      (syntax-rules (_ when)
        ((syntax-case-match-pattern x pattern when condition result result* ...)
          (syntax-case-match-pattern x pattern
            (if condition
              (let () result result* ...)
              (raise (make-syntax-case-no-match)))))
        ((syntax-case-match-pattern x _ result result* ...)
          (let () result result* ...))
        ((syntax-case-match-pattern x '() result result* ...)
          (if (null? (syntax->expression x))
            (let () result result* ...)
            (raise (make-syntax-case-no-match))))
        ((syntax-case-match-pattern x (pattern) result result* ...)
          (let ((y x))
            (if (pair? (syntax->expression y))
              (syntax-case-match-pattern (syntax-map car y) pattern
                (syntax-case-match-pattern (syntax-map cdr y) '() result result* ...))
              (raise (make-syntax-case-no-match)))))
        ((syntax-case-match-pattern x (pattern . rest) result result* ...)
          (let ((y x))
            (if (pair? (syntax->expression y))
              (syntax-case-match-pattern (syntax-map car y) pattern
                (syntax-case-match-pattern (syntax-map cdr y) rest result result* ...))
              (raise (make-syntax-case-no-match)))))
        ((syntax-case-match-pattern x ident result result* ...)
          (let ((ident x)) result result* ...))))


    ; Yes, I just defined syntax-case in terms of syntax-rules.
    ; Are we sure this won't create a black hole?
    (define-syntax syntax-case
      (syntax-rules ()
        ((syntax-case x (arm ...))
          (guard (e ((syntax-case-no-match? e) (error "no match in syntax case")))
            (syntax-case-match-pattern x arm ...)))
        ((syntax-case x (arm ...) clause clause* ...)
          (let ((y x))
            (guard (e ((syntax-case-no-match? e)
                        (syntax-case y clause clause* ...)))
              (syntax-case-match-pattern y arm ...))))))


    (define (expand-procedure-call procedure arguments)
      (let ((expanded-procedure (expand-syntax-object procedure))
            (expanded-arguments
              (let loop ((arguments arguments)
                         (expanded-arguments '()))
                (syntax-case arguments
                  ('() (reverse expanded-arguments))
                  ((argument . rest)
                    (let ((expanded-argument (expand-syntax-object argument)))
                      (loop
                        rest
                        (cons expanded-argument expanded-arguments))))
                  (_ (raise-syntax-error "arguments to a procedure call must be a list" procedure arguments))))))
        (make-call expanded-procedure expanded-arguments)))


    (define (resolve-identifier ident)
      (let* ((environment (syntax-object-environment ident))
             (substitutions (environment-substitutions environment)))
        (or
          ; Check whether the variable is lexically bound to a marked identifier.
          (guard (e ((key-not-found-error? e) #f))
            (lookup substitutions ident))
          ; Check whether the variable is bound to an unmarked identifier.
          (guard (e ((key-not-found-error? e) #f))
            (lookup substitutions (identifier-name ident)))
          ; Otherwise insert a library-ref
          (make-library-ref (identifier-name ident) (environment-library environment)))))


    (define (expand-syntax-object syntax)
      (syntax-case syntax
        ('() (raise-syntax-error "nil by itself is an error (did you mean to use quote?)" syntax))
        ((macro-name . tail) when (identifier? macro-name)
          (let ((macro-body (resolve-identifier macro-name)))
            (if (macro-transformer? macro-body)
              ((transformer-function macro-body) syntax)
              (expand-procedure-call macro-name tail))))
        ((procedure . arguments)
          (expand-procedure-call procedure arguments))
        (_ when (identifier? syntax)
          (let ((binding (resolve-identifier syntax)))
            (if (macro-transformer? binding)
              (raise-syntax-error "macro is not allowed in this context" syntax)
              binding)))
        (_ when (let ((expr (syntax->expression syntax)))
                  (or (boolean? expr)
                      (char? expr)
                      (number? expr)
                      (string? expr)
                      (vector? expr)))
          (make-constant (syntax->expression syntax)))
        (_ (raise-syntax-error "unexpected expression type" (clean-syntax syntax)))))


    ; expand can be thought of as a compiler from Scheme to IR1. Macros
    ; included in the environment can be used to extend the syntax. Returns an
    ; IR1 expression.
    (define (expand expression environment)
      (expand-syntax-object (wrap-syntax expression environment)))


    (define (clean-syntax s)
      (cond
        ((pair? s) (cons (clean-syntax (car s)) (clean-syntax (cdr s))))
        ((syntax-object? s) (clean-syntax (syntax->expression s)))
        (else s)))


    (define builtin-quote
      (make-macro-transformer
        (lambda (syntax)
          (syntax-case syntax
            ((_ datum) (make-constant (clean-syntax datum)))
            (_ (raise-syntax-error "invalid form for quote" (clean-syntax syntax)))))))


    (define (matches-literals literals object)
      (unless (list? (syntax->expression literals))
        (raise-syntax-error "invalid form in literals, expecting list" literals))
      (if (not (identifier? object))
        #f
        (let ((literal-identifiers
                (map
                  (lambda (lit) (with-wrap lit literals))
                  (syntax->expression literals))))
          (let loop ((literal-identifiers literal-identifiers))
            (match literal-identifiers
              ('() #f)
              ((lit . literals)
                (or (free-identifier=? lit object)
                    (loop literals))))))))


    (define (identifier-uuid i)
      ; Join marks by ( because symbols aren't allowed to have ( in the name.
      (sprintf "{}({}" (apply join "(" (map number->string (marks i))) (identifier-name i)))


    (define (hash-identifier i)
      (hash-bytevector (string->utf8 (identifier-uuid i))))


    (define (identifier<? i1 i2)
      (string<? (identifier-uuid i1) (identifier-uuid i2)))


    (define (alist->substitutions l)
      (alist->map hash-identifier identifier<? l))


    (define (is-underscore expression)
      (and
        (identifier? expression)
        (free-identifier=?
          expression
          (wrap-syntax
            '_
            (make-environment
              (alist->substitutions
                (list (cons '_ (make-library-ref '_ '(scheme base)))))
              '(scheme base))))))


    (define (syntax-improper-list-length l)
      (let loop ((l l)
                 (n 0))
        (syntax-case l
          ('() n)
          ((_ . rest) (loop rest (+ 1 n)))
          (_ (+ 1 n)))))


    ; objects is an n-dimensional vec, where n is nesting-level.
    (define-record-type <ellipsis-binding>
      (make-ellipsis-binding objects nesting-level)
      ellipsis-binding?
      (objects ellipsis-binding-objects)
      (nesting-level ellipsis-binding-nesting-level))


    (define (merge-bindings x y)
      (assert (= (ellipsis-binding-nesting-level x) (ellipsis-binding-nesting-level y)))
      (make-ellipsis-binding
        (vec-append (ellipsis-binding-objects x) (ellipsis-binding-objects y))
        (ellipsis-binding-nesting-level x)))


    (define (ellipsis-substitutions-merge s1 s2)
      (map-for-each
        (lambda (k v)
          (let-values (((s1-binding ok)
                         (guard (e ((key-not-found-error? e) (values #f #f)))
                           (values (lookup s1 k) #t))))
            (if ok
              (set! s1 (insert s1 k (merge-bindings s1-binding v)))
              (set! s1 (insert s1 k v)))))
        s2)
      s1)


    (define (map-ellipsis-binding substitutions)
      (let ((res (alist->substitutions '())))
        (map-for-each
          (lambda (k v)
            (if (ellipsis-binding? v)
              (set! res
                (insert
                  res
                  k
                  (make-ellipsis-binding
                    (vec (ellipsis-binding-objects v))
                    (+ 1 (ellipsis-binding-nesting-level v)))))
              (set! res
                (insert
                  res
                  k
                  (make-ellipsis-binding
                    (vec v)
                    1)))))
          substitutions)
        res))


    (define (pattern-bindings ellipsis literals pattern object)
      (syntax-case pattern
        (lit when (matches-literals literals lit)
          (if (and (identifier? object) (free-identifier=? object lit))
            (alist->substitutions '())
            #f))
        ((p ellip . p*) when (and (identifier? ellip) (not (matches-literals literals ellip)) (free-identifier=? ellipsis ellip))
          (let* ((n (syntax-improper-list-length object))
                 (m (syntax-improper-list-length p*))
                 (n-m (- n m)))
            (if (>= n m)
              (let loop ((i 0)
                         (object object)
                         (bindings (alist->substitutions '())))
                (if (< i n-m)
                  (let ((binding (pattern-bindings ellipsis literals p (syntax-map car object))))
                    (and binding
                         (loop
                           (+ 1 i)
                           (syntax-map cdr object)
                           (ellipsis-substitutions-merge bindings (map-ellipsis-binding binding)))))
                  (let ((bindings* (pattern-bindings ellipsis literals p* object)))
                    (and bindings* (merge bindings bindings*)))))
              #f)))
        (underscore when (is-underscore underscore)
          (alist->substitutions '()))
        (ident when (identifier? ident)
          ; Each time the expander encounters a macro use, it applies an
          ; antimark to the input form.
          ;
          ; We would apply it earlier, but the antimark breaks
          ; free-identifier=? to check for literals. -- rose
          (alist->substitutions (list (cons ident (anti-mark object)))))
        ('()
          (syntax-case object
            ('() (alist->substitutions '()))
            (_ #f)))
        ((p . p*)
          (syntax-case object
            ((e . e*)
              (define pbindings (pattern-bindings ellipsis literals p e))
              (define p*bindings (pattern-bindings ellipsis literals p* e*))
              (and pbindings p*bindings (merge pbindings p*bindings)))
            (_ #f)))
        (constant
          (if (equal? (syntax->expression constant) (syntax->expression object))
            (alist->substitutions '())
            #f))))


    (define-record-type <ellipsis-out-of-bounds>
      (make-ellipsis-out-of-bounds)
      ellipsis-out-of-bounds?)


    (define (ellipsis-ref v i)
      (if (< i (vec-length v))
        (vec-ref v i)
        (raise (make-ellipsis-out-of-bounds))))


    ; Can raise key-not-found-error? or ellipsis-out-of-bounds?.
    (define (ellipsis-lookup substitutions ellipsis-nesting key)
      (let ((n-d-vector (lookup substitutions key)))
        (if (ellipsis-binding? n-d-vector)
          (if (= (ellipsis-binding-nesting-level n-d-vector) (vec-length ellipsis-nesting))
            (let loop ((i 0)
                       (value (ellipsis-binding-objects n-d-vector)))
              (if (< i (vec-length ellipsis-nesting))
                (loop
                  (+ 1 i)
                  (ellipsis-ref value (vec-ref ellipsis-nesting i)))
                value))
            (raise-syntax-error "reference to pattern variable at incorrect ellipsis nesting level" (clean-syntax key) (vec-length ellipsis-nesting)))
          n-d-vector)))


    (define (syntax-append s1 s2)
      (syntax-case s1
        ('() (with-wrap s2 s1))
        ((head . tail)
          (with-wrap
            (cons head
                  (syntax-append tail s2))
            s1))))


    (define (expand-template ellipsis ellipsis-nesting substitutions template)
      (syntax-case template
        ('() (with-wrap '() template))
        ((head ellip . tail) when (and (identifier? ellip) (free-identifier=? ellip ellipsis))
          (let-values (((extra-ellipses tail)
                         (let loop ((tail tail)
                                    (extra-ellipses '()))
                           (syntax-case tail
                             ((ellip . tail) when (and (identifier? ellip) (free-identifier=? ellip ellipsis))
                               (loop tail (cons ellip extra-ellipses)))
                             (_ (values extra-ellipses tail))))))
            (let loop ((i 0)
                       (expansion (with-wrap '() template)))
              (guard (e ((ellipsis-out-of-bounds? e)
                          (if (= 0 i)
                            ; Failure was at a higher level.
                            (raise e)
                            (syntax-append expansion (expand-template ellipsis ellipsis-nesting substitutions tail)))))
                (loop
                  (+ 1 i)
                  (syntax-append
                    expansion
                    (expand-template
                      ellipsis
                      (vec-append ellipsis-nesting i)
                      substitutions
                      (with-wrap
                        (cons
                          head
                          extra-ellipses)
                        template))))))))
        ((head . tail)
          (with-wrap
            (cons (expand-template ellipsis ellipsis-nesting substitutions head)
                  (expand-template ellipsis ellipsis-nesting substitutions tail))
            template))
        (ident when (identifier? ident)
          (guard (e ((key-not-found-error? e) template))
            (ellipsis-lookup substitutions ellipsis-nesting template)))
        (_ template)))


    (define (syntax-match ellipsis literals all-rules object)
      (let loop ((rules all-rules))
        (syntax-case rules
          ('() (raise-syntax-error "form did not match any patterns in syntax-rules" object all-rules))
          ((((_ . pattern) template) . tail)
            (define bindings (pattern-bindings ellipsis literals pattern (syntax-map cdr object)))
            (if bindings
              ; We call expand-syntax-object immediately, since macros are
              ; allowed to be recursive.
              (expand-syntax-object
                ; Each time the expander encounters a macro use, it applies an
                ; antimark to the input form, invokes the associated
                ; transformer, then applies a fresh mark to the output.
                (add-mark (new-mark) (expand-template ellipsis (vec) bindings template)))
              (loop tail)))
          (_ (raise-syntax-error "unexpected form in syntax-rules" all-rules)))))


    (define default-ellipsis
      (wrap-syntax
        '...
        (make-environment
          (alist->substitutions
            (list (cons '...
                        (make-library-ref '... '(scheme base)))))
          '(scheme base))))


    (define builtin-syntax-rules
      (make-macro-transformer
        (lambda (syntax-rules-form)
          (make-macro-transformer
            (lambda (input-form)
              (syntax-case syntax-rules-form
                ((_ ellipsis literals . rules) when (identifier? ellipsis)
                  (syntax-match ellipsis literals rules input-form))
                ((_ literals . rules)
                  (syntax-match default-ellipsis literals rules input-form))
                (_ (raise-syntax-error "unexpected form in syntax-rules" syntax-rules-form))))))))


    (define builtin-let-syntax
      (make-macro-transformer
        (lambda (x)
          (syntax-case x
            ((_ (ident transformer-form) body-form) when (identifier? ident)
              (let* ((transformer (expand-syntax-object transformer-form))
                    (body (expand-syntax-object (with-binding ident transformer body-form))))
                body))
            (_ (raise-syntax-error "unexpected form in let-syntax"))))))


    (define (split-args-rest formals)
      (syntax-case formals
        ('()
          (values '() #f))
        ((var . vars) when (identifier? var)
          (let-values (((args rest) (split-args-rest vars)))
            (values (cons (identifier-name var) args) rest)))
        (var when (identifier? var)
          (values '() (identifier-name var)))
        (_ (raise-syntax-error "unexpected form in split-args-rest" formals))))


    (define (expand-lambda-body-rest body)
      (let loop ((body body)
                 (expanded-body (make-constant #f)))
        (syntax-case body
          ('()
            expanded-body)
          ((expr . expr*)
            (define expanded-expr (expand-syntax-object expr))
            (when (or (library-define? expanded-expr)
                      (define-syntax? expanded-expr))
              (raise-syntax-error "define not allowed here" body))
            (loop (syntax-map cdr body)
                  (make-sequence expanded-body expanded-expr))))))


    (define (expand-lambda-body body)
      (let loop ((body body)
                 (names '())
                 (gensyms '())
                 (expressions '()))
        (syntax-case body
          ('()
            (if (null? names)
              (make-constant #f)
              (make-letrec #t (reverse names) (reverse gensyms) (reverse expressions) (make-constant #f))))
          ((expr . expr*)
            (define expanded-expr (expand-syntax-object expr))
            (cond
              ((library-define? expanded-expr)
                (let ((name (library-ref-name (library-define-ref expanded-expr)))
                      (g (gensym)))
                  (loop (with-binding name (make-lexical-ref name g) (syntax-map cdr body))
                        (cons name names)
                        (cons g gensyms)
                        (cons (library-define-expression expanded-expr) expressions))))
              ((define-syntax? expanded-expr)
                (loop (with-binding (define-syntax-name expanded-expr) (define-syntax-transformer expanded-expr) (syntax-map cdr body))
                      names
                      gensyms
                      expressions))
              (else
                (if (null? names)
                  (expand-lambda-body-rest body)
                  (make-letrec #t (reverse names) (reverse gensyms) (reverse expressions) (expand-lambda-body-rest body)))))))))


    (define builtin-lambda
      (make-macro-transformer
        (lambda (x)
          (syntax-case x
            ((_ formals . body)
              (let-values (((args rest) (split-args-rest formals)))
                (make-lambda
                  (map (lambda (name)
                         (make-lexical-ref name (gensym)))
                       args)
                  (if rest
                    (make-lexical-ref rest (gensym))
                    #f)
                  (expand-lambda-body body))))
            (_ (raise-syntax-error "unexpected form in lambda" x))))))


    (define builtin-define
      (make-macro-transformer
        (lambda (x)
          (syntax-case x
            ((_ symbol expression)
              (make-library-define
                (make-library-ref
                  (identifier-name symbol)
                  (environment-library (syntax-object-environment x)))
                (expand-syntax-object expression)))
            (_ (raise-syntax-error "unexpected form in builtin-define" x))))))


    (define builtins-environment
      (make-environment
        (alist->substitutions
          (list (cons 'syntax-rules builtin-syntax-rules)
                (cons '_ (make-library-ref '_ '(scheme base)))
                (cons '... (make-library-ref '... '(scheme base)))
                (cons 'builtin-let-syntax builtin-let-syntax)
                (cons 'quote builtin-quote)
                (cons 'lambda builtin-lambda)
                (cons 'builtin-define builtin-define)))
        'main))


    ; Expands the body of a library, or top level.
    (define (expand-body body)
      (loop with environment = (syntax-object-environment body)
            for expr in (syntax->expression body)
            for expanded-expr = (expand expr environment)
            for res = expanded-expr then (make-sequence res expanded-expr)
            finally (return res)
            if (define-syntax? expanded-expr)
              do (set! environment
                   (add-binding
                     (define-syntax-name expanded-expr)
                     (define-syntax-transformer expanded-expr)
                     environment))))))