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
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
|
(define-library (csc macros)
(export
builtins-environment
expand-body
macro-syntax-error?
macro-transformer?)
(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
make-comparer
make-map
map-for-each
merge)
(only (csc ir1)
%call
%call-builtin
%constant
%define-syntax
%if
%lambda
%letrec
%lexical-ref
%lexical-set
%library-define
%library-ref
%sequence
define-syntax-name
define-syntax-transformer
define-syntax?
lexical-ref-gensym
lexical-ref-name
lexical-ref?
library-define-expression
library-define-ref
library-define?
library-ref-library
library-ref-name
library-ref?
make-call
make-call-builtin
make-constant
make-define-syntax
make-if
make-lambda
make-letrec
make-lexical-ref
make-lexical-set
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 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 (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 s1-binding
s2-binding
(binding=? s1-binding s2-binding))
(and (not s1-binding)
(not s2-binding)
(symbol=? (identifier-name s1) (identifier-name s2))))))
(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)))))
; It's worth considering a more efficient algorithm.
(define (add-marks ms expression)
(loop for m in ms
with res = expression
unless (loop for m* in (marks res)
if (eqv? m* m)
return #t
finally (return #f))
do (set! res (add-mark m res))
finally (return res)))
(define (anti-mark expression)
(add-mark #f expression))
(define (decorate ms expression environment)
(add-marks ms (wrap-syntax expression environment)))
(define (with-wrap expression parent)
(if (syntax-object? parent)
(decorate (marks parent) expression (syntax-object-environment parent))
expression))
(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 env)
(let ((expanded-procedure (expand procedure env))
(expanded-arguments
(let loop ((arguments arguments)
(expanded-arguments '()))
(syntax-case arguments
('() (reverse expanded-arguments))
((argument . rest)
(let ((expanded-argument (expand argument env)))
(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 (strip-mark ident)
(make-syntax-object
(identifier-name ident)
(syntax-object-environment ident)
(cdr (marks ident))))
(define (lookup-complicated ident env)
(define e (environment-substitutions env))
(or (lookup e ident #f)
(and (pair? (marks ident))
(lookup-complicated (strip-mark ident) env))))
(define (resolve-identifier ident lexical-env)
(or
(lookup-complicated ident lexical-env)
(and (syntax-object? ident)
(lookup-complicated ident (syntax-object-environment ident)))
(make-library-ref (identifier-name ident) (environment-library lexical-env))))
(define (expand syntax env)
(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 env)))
(if (macro-transformer? macro-body)
((transformer-function macro-body) syntax env)
(expand-procedure-call macro-name tail env))))
((procedure . arguments)
(expand-procedure-call procedure arguments env))
(_ when (identifier? syntax)
(let ((binding (resolve-identifier syntax env)))
(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)))))
(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 env)
(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)
(sprintf "{}" (list (identifier-name i) (marks i))))
(define (hash-identifier i)
(hash-bytevector (string->utf8 (identifier-uuid i))))
(define (marks<? m1 m2)
(loop for m1* in m1
for m2* in m2
if (< m1* m2*)
return #t
else if (> m1* m2*)
return #f
finally (return (< (length m1) (length m2)))))
(define compare-identifiers
(make-comparer
hash-identifier
(lambda (i1 i2)
(cond
((bound-identifier=? i1 i2) 0)
((or (string<? (symbol->string (identifier-name i1)) (symbol->string (identifier-name i2)))
(marks<? (marks i1) (marks i2)))
-1)
(else 1)))))
(define (alist->substitutions l)
(alist->map compare-identifiers 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)
(and
(pair? (syntax->expression object))
(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)
(define keys '())
(map-for-each (lambda (k v)
(set! keys (cons (marks k) keys)))
substitutions)
(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)
(define keys '())
(map-for-each (lambda (k v)
(set! keys (cons (identifier-name k) keys)))
substitutions)
(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 env object)
(let loop ((rules all-rules))
(syntax-case rules
('() (raise-syntax-error "form did not match any patterns in syntax-rules" (clean-syntax object) (clean-syntax all-rules)))
((((_ . pattern) template) . tail)
(define bindings (pattern-bindings ellipsis literals pattern (wrap-syntax (syntax-map cdr object) env)))
(if bindings
; We call expand immediately, since macros are
; allowed to be recursive.
(expand
; 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))
env)
(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 env1)
; Merge the lexical and toplevel environments.
(define toplevel-env (if (syntax-object? syntax-rules-form)
(environment-substitutions (syntax-object-environment syntax-rules-form))
(make-map compare-identifiers)))
(set! syntax-rules-form
(make-syntax-object (syntax->expression syntax-rules-form)
(make-environment
(merge toplevel-env (environment-substitutions env1))
(environment-library env1))
(marks syntax-rules-form)))
(make-macro-transformer
(lambda (input-form env)
(syntax-case syntax-rules-form
((_ ellipsis literals . rules) when (identifier? ellipsis)
(syntax-match ellipsis literals rules env input-form))
((_ literals . rules)
(syntax-match default-ellipsis literals rules env input-form))
(_ (raise-syntax-error "unexpected form in syntax-rules" (clean-syntax syntax-rules-form)))))))))
(define builtin-let-syntax
(make-macro-transformer
(lambda (x env)
(syntax-case x
((_ bindings body-form)
(loop with env* = env
for bindings* = bindings
then (syntax-case bindings*
((_)
(return (expand body-form env*)))
((_ . rest) rest)
(_ (raise-syntax-error "unexpected form in let-syntax loop" (clean-syntax x))))
do (syntax-case bindings*
(((ident transformer-form) . _) when (identifier? ident)
(set! env* (add-binding ident (expand transformer-form env) env*)))
(_ (raise-syntax-error "unexpected form in let-syntax binding" (clean-syntax x))))))
(_ (raise-syntax-error "unexpected form in let-syntax" (clean-syntax x)))))))
(define (expand-lambda-body-rest body env)
(syntax-case body
('() (make-constant #f))
(_ (loop for body* = body then (syntax-map cdr body*)
for expanded-expr = (expand (syntax-map car body*) env)
for expanded-body = expanded-expr then (make-sequence expanded-body expanded-expr)
finally (return expanded-body)
when (or (library-define? expanded-expr)
(define-syntax? expanded-expr))
do (raise-syntax-error "define not allowed here" body)
until (syntax-case body*
((_) #t)
(_ #f))))))
(define (library-ref->string r)
(sprintf "{}" (list (library-ref-name r) (library-ref-library r))))
(define compare-library-refs
(make-comparer
(lambda (r)
(hash-bytevector (string->utf8 (library-ref->string r))))
(lambda (r1 r2)
(cond
((and (symbol=? (library-ref-name r1) (library-ref-name r2))
(equal? (library-ref-library r1) (library-ref-library r2)))
0)
((string<? (library-ref->string r1) (library-ref->string r2))
-1)
(else 1)))))
(define (fix-names name-map expr)
(let fix ((expr expr))
(match expr
((% %library-ref . _)
(lookup name-map expr expr))
((% %constant . _) expr)
((% %lexical-ref . _) expr)
((% %lexical-set ref expr)
(make-lexical-set ref (fix expr)))
((% %library-define ref expr)
(make-library-define ref (fix expr)))
((% %define-syntax . _) expr)
((% %if test a b)
(make-if (fix test) (fix a) (fix b)))
((% %call proc args)
(make-call (fix proc) (fix args)))
((% %call-builtin op args)
(make-call-builtin op (map fix args)))
((% %sequence a b)
(make-sequence (fix a) (fix b)))
((% %lambda args body)
(make-lambda args (fix body)))
((% %letrec in-order? names gensyms exprs body)
(make-letrec in-order? names gensyms (map fix exprs) (fix body)))
(_ (error "unexpected form in fix-names" expr)))))
(define (expand-lambda-body body env)
(let continue ((body body)
(env env)
(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 expr env))
(define continue?
(let add-bindings ((expanded-expr expanded-expr))
(match expanded-expr
((% %library-define (% %library-ref name _) expr)
(define g (gensym))
(set! env (add-binding name (make-lexical-ref name g) env))
(set! names (cons name names))
(set! gensyms (cons g gensyms))
(set! expressions (cons expr expressions))
#t)
((% %define-syntax name transformer)
(set! env (add-binding name transformer env))
#t)
((% %sequence head tail)
(and (add-bindings head)
(add-bindings tail)))
(_ #f))))
(cond
(continue?
(continue (syntax-map cdr body) env names gensyms expressions))
((null? names)
(expand-lambda-body-rest body env))
(else
(let* ((name-map (loop for name in names
for g in gensyms
with m = (make-map compare-library-refs)
do (set! m (insert m (make-library-ref name (environment-library env))
(make-lexical-ref name g)))
finally (return m)))
(fixed-exprs (loop for expr in expressions
collect (fix-names name-map expr))))
(make-letrec #t (reverse names) (reverse gensyms) (reverse fixed-exprs)
(expand-lambda-body-rest body env)))))))))
(define builtin-lambda
(make-macro-transformer
(lambda (x env)
(syntax-case x
((_ args . body) when (identifier? args)
(define ref (make-lexical-ref (identifier-name args) (gensym)))
(set! env (add-binding args ref env))
(make-lambda
ref
(expand-lambda-body body env)))))))
(define builtin-define
(make-macro-transformer
(lambda (x env)
(syntax-case x
((_ symbol expression) when (identifier? symbol)
(make-library-define
(make-library-ref
(identifier-name symbol)
(environment-library env))
(expand expression env)))
(_ (raise-syntax-error "unexpected form in builtin-define" (clean-syntax x)))))))
(define builtin-define-syntax
(make-macro-transformer
(lambda (x env)
(syntax-case x
((_ ident transformer-form) when (identifier? ident)
(make-define-syntax (identifier-name ident)
(expand transformer-form env)))
(_ (raise-syntax-error "unexpected form in builtin-define-syntax" x))))))
(define builtin-call-builtin
(make-macro-transformer
(lambda (x env)
(syntax-case x
((_ op . args) when (identifier? op)
(make-call-builtin (identifier-name op)
(let loop ((args args)
(expanded-args '()))
(syntax-case args
('() (reverse expanded-args))
((head . tail)
(loop tail
(cons (expand head env)
expanded-args)))
(_ (raise-syntax-error "unexpected form in call-builtin"))))))
(_ (raise-syntax-error "unexpected form in call-builtin" x))))))
(define builtin-set
(make-macro-transformer
(lambda (x env)
(syntax-case x
((_ var value) when (identifier? var)
(define var* (expand var env))
(define val* (expand value env))
(cond
((lexical-ref? var*)
(make-lexical-set var* val*))
((library-ref? var*)
(make-library-define var* val*))
(else
(error "unknown variable form in builtin-set" var*))))
(_ (raise-syntax-error "unexpected form in builtin-set" x))))))
(define builtin-if
(make-macro-transformer
(lambda (x env)
(syntax-case x
((_ test true false)
(make-if (expand test env)
(expand true env)
(expand false env)))
(_ (raise-syntax-error "unexpected form in builtin-if"))))))
(define builtin-sequence
(make-macro-transformer
(lambda (x env)
(syntax-case x
((_ head tail)
(define head* (expand head env))
(match head*
((% %define-syntax name transformer)
(set! env (add-binding name transformer env))))
(make-sequence head* (expand tail env)))
(_ (raise-syntax-error "unexpected form in builtin-sequence"))))))
(define builtins-environment
(alist->substitutions
(list (cons 'syntax-rules builtin-syntax-rules)
(cons '_ (make-library-ref '_ '(scheme base)))
(cons '... (make-library-ref '... '(scheme base)))
(cons 'let-syntax builtin-let-syntax)
(cons 'quote builtin-quote)
(cons 'builtin-lambda builtin-lambda)
(cons 'builtin-define builtin-define)
(cons 'define-syntax builtin-define-syntax)
(cons 'call-builtin builtin-call-builtin)
(cons 'set! builtin-set)
(cons 'builtin-if builtin-if)
(cons 'builtin-sequence builtin-sequence))))
; Expands the body of a library, or top level. expand-body 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-body name body env)
(define ident-map (make-map compare-identifiers))
(map-for-each (lambda (k v)
(set! ident-map (insert ident-map k v)))
env)
(define environment (make-environment ident-map name))
(if (null? body)
(make-constant #f)
(loop for expr in 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)))))))
|