-
Notifications
You must be signed in to change notification settings - Fork 3
/
parameterize-lexical-context.rkt
146 lines (131 loc) · 5.48 KB
/
parameterize-lexical-context.rkt
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
#lang racket
(require (for-template racket/base)
syntax/parse
syntax/id-table
(for-syntax syntax/parse
racket/syntax
syntax/parse/experimental/template)
debug-scopes)
(provide with-bindings
with-rec-bindings
tl-redirections
start-tl-redirections
f-start-tl-redirections
binding-table-find-best
binding-table-set!
make-binding-table)
(struct binding-table-struct (val))
(define/contract tl-redirections
(parameter/c (or/c binding-table-struct? #f))
(make-parameter #f))
(define (make-binding-table)
(-> binding-table-struct?)
(binding-table-struct (make-hasheq)))
(define/contract (binding-table-set! table id value)
(-> binding-table-struct? identifier? any/c void?)
(let ([group (hash-ref! (binding-table-struct-val table)
(syntax-e id)
(make-bound-id-table))])
(when (dict-has-key? group id)
(raise-syntax-error
'type-expander
"Attempted to re-bind the same identifier with the same scopes"
id))
(bound-id-table-set! group id value)))
(define (binding-table-find-best table id fallback)
(-> binding-table-struct? identifier? (or/c procedure? any/c) void?)
(define (scopes-of i)
(list->set (map (λ (v) (vector-ref v 0))
(hash-ref (syntax-debug-info i) 'context))))
(define scopes-of-id (scopes-of id))
(let* ([group (hash-ref (binding-table-struct-val table)
(syntax-e id)
(λ () (make-bound-id-table)))]
[candidates (filter (λ (other)
(subset? (car other) scopes-of-id))
(bound-id-table-map group
(λ (a b)
(list (scopes-of a) a b))))])
(if (= 0 (length candidates))
(if (procedure? fallback)
(fallback)
fallback)
(let* ([best-candidate (argmax (λ (c) (set-count (car c)))
candidates)])
(for ([c candidates])
(unless (subset? (car c) (car best-candidate))
(raise-syntax-error 'type-expander
(format "Ambiguous bindings: ~a"
(map (λ (c) (list (cadr c) (car c)))
candidates)))))
(caddr best-candidate)))))
(define-syntax-rule (start-tl-redirections . rest)
(parameterize ([tl-redirections (or (tl-redirections)
(make-binding-table))])
. rest))
(define-syntax-rule (f-start-tl-redirections f)
(λ l (start-tl-redirections (apply f l))))
(define-syntax with-bindings
(syntax-parser
[(_ [{~or v1:id (v* {~and ooo {~literal ...}})} e/es] x code ...+)
#:with vs (if (attribute ooo) #'(v* ooo) #'(v1))
#:with es (if (attribute ooo) #'e/es #'(list e/es))
(template
(let ()
(define ctx (make-syntax-introducer))
(invariant-assertion (λ (ll) (and (list? ll)
(andmap identifier? ll)))
(syntax->list #'vs))
(for ([binding (in-syntax #'vs)]
[value es])
(binding-table-set! (tl-redirections) (ctx binding) value))
(with-syntax ([(vs x)
(ctx #'(vs x))])
code ...)))]))
(define-syntax with-rec-bindings
(syntax-parser
[(_ [{~or v1:id (v* {~and ooo {~literal ...}})} func e/es] x code ...+)
#:with vs (if (attribute ooo) #'(v* ooo) #'(v1))
#:with es (if (attribute ooo) #'(e/es ooo) #'(e/es))
(template
(let ()
(define ctx (make-syntax-introducer))
(define ctx2 (make-syntax-introducer #t))
(invariant-assertion (λ (ll) (and (list? ll)
(andmap identifier? ll)))
(syntax->list #'vs))
(for ([binding (in-syntax #'vs)]
[stx-value (in-syntax #'es)])
(let ([vvv (func (ctx stx-value))])
(binding-table-set! (tl-redirections)
(ctx binding)
vvv)))
(with-syntax ([(vs x)
(ctx2 (ctx #'(vs x)))])
code ...)))]))
(provide trampoline-eval)
(define trampoline-result (make-parameter #f))
(define (trampoline-eval code)
(define result 'not-yet-result)
(parameterize ([trampoline-result (λ (v) (set! result v))])
(local-expand (syntax-local-introduce
#`(let-syntax ([tr ((trampoline-result) #,code)])
(void)))
'expression
'()))
result)
(module+ test
(require rackunit)
(check-equal? (let ()
(define tbl (make-binding-table))
(define id #'id)
(binding-table-set! tbl id 123)
(define ctx (make-syntax-introducer))
(binding-table-set! tbl (ctx id) 456)
(define ctx2 (make-syntax-introducer))
(list (binding-table-find-best tbl id #f)
(binding-table-find-best tbl (ctx id) #f)
(binding-table-find-best tbl (ctx2 id) #f)
(binding-table-find-best tbl (ctx2 (ctx id)) #f)
(binding-table-find-best tbl (ctx (ctx2 id)) #f)))
'(123 456 123 456 456)))