Skip to content

Commit b9e9f60

Browse files
Copilotjackfirth
andauthored
Add expanded identifier table utility (#710)
Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: jackfirth <[email protected]>
1 parent e05681a commit b9e9f60

File tree

3 files changed

+160
-67
lines changed

3 files changed

+160
-67
lines changed

default-recommendations/analyzers/identifier-usage.rkt

Lines changed: 28 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,11 @@
99
[identifier-usage-analyzer expansion-analyzer?]))
1010

1111

12-
(require racket/hash
13-
racket/list
14-
racket/set
12+
(require racket/list
1513
racket/stream
16-
racket/treelist
17-
rebellion/collection/hash
14+
rebellion/collection/entry
1815
rebellion/streaming/transducer
16+
resyntax/default-recommendations/analyzers/private/expanded-id-table
1917
resyntax/private/analyzer
2018
resyntax/private/syntax-path
2119
resyntax/private/syntax-property-bundle
@@ -34,17 +32,6 @@
3432
;@----------------------------------------------------------------------------------------------------
3533

3634

37-
(define (append-all-id-maps id-maps)
38-
(for/fold ([combined (hash)])
39-
([map id-maps])
40-
(hash-union combined map #:combine treelist-append)))
41-
42-
43-
(define (id-map-shift-phase id-map levels)
44-
(for/hash ([(phase ids) (in-hash id-map)])
45-
(values (and phase (+ phase levels)) ids)))
46-
47-
4835
;; Label syntax with phase information
4936
(define (syntax-label-id-phases expanded-stx)
5037
(let loop ([expanded-stx expanded-stx] [phase 0] [skip? #false])
@@ -166,56 +153,41 @@
166153
(stream (attribute id))])))
167154

168155

169-
(define (phase-binding-table bound-ids used-ids #:phase phase)
170-
(define initial-map
171-
(for/fold ([map (make-immutable-free-id-table #:phase phase)])
172-
([bound bound-ids])
173-
(free-id-table-set map bound '())))
174-
(for*/fold ([map initial-map])
175-
([bound bound-ids]
176-
[used used-ids]
177-
#:when (free-identifier=? bound used))
178-
(free-id-table-update map bound (λ (previous) (cons used previous)) '())))
179-
180-
181-
(define (identifier-binding-table bound-ids-by-phase used-ids-by-phase)
182-
(for/hash
183-
([phase
184-
(in-set (set-union (hash-key-set bound-ids-by-phase) (hash-key-set used-ids-by-phase)))])
185-
(define bound-ids (hash-ref bound-ids-by-phase phase '()))
186-
(define used-ids (hash-ref used-ids-by-phase phase '()))
187-
(values phase (phase-binding-table bound-ids used-ids #:phase phase))))
188-
189-
190156
(define (fully-expanded-syntax-binding-table stx)
191157
(define labeled-stx (syntax-label-id-phases (syntax-label-paths stx 'expanded-path)))
192158

193-
;; Get bound identifiers and group by phase
194-
(define bound-ids-by-phase
195-
(for/fold ([result (hash)])
196-
([id (in-stream (binding-site-identifiers labeled-stx))])
197-
(define id-phase (syntax-property id 'phase))
198-
(hash-update result id-phase (λ (prev) (treelist-add prev id)) (treelist))))
159+
;; Create expanded-id-table to track bound identifiers with empty usage lists
160+
(define table (make-expanded-id-table))
161+
162+
;; Initialize all bound identifiers with empty usage lists
163+
(for ([id (in-stream (binding-site-identifiers labeled-stx))])
164+
(define id-phase (syntax-property id 'phase))
165+
(expanded-id-table-set! table (expanded-identifier id id-phase) '()))
199166

200-
;; Get used identifiers and group by phase
201-
(define used-ids-by-phase
202-
(for/fold ([result (hash)])
203-
([id (in-stream (usage-site-identifiers labeled-stx))])
204-
(define id-phase (syntax-property id 'phase))
205-
(hash-update result id-phase (λ (prev) (treelist-add prev id)) (treelist))))
167+
;; For each usage, find its binding within the same phase and add it to the usage list
168+
(for ([used-id (in-stream (usage-site-identifiers labeled-stx))])
169+
(define used-phase (syntax-property used-id 'phase))
170+
(for ([bound-entry (in-expanded-id-table-phase table used-phase)])
171+
(define bound-expanded-id (entry-key bound-entry))
172+
(define bound-id (expanded-identifier-syntax bound-expanded-id))
173+
(when (free-identifier=? bound-id used-id)
174+
(define current-usages (entry-value bound-entry))
175+
(expanded-id-table-set! table bound-expanded-id (cons used-id current-usages)))))
206176

207-
(identifier-binding-table bound-ids-by-phase used-ids-by-phase))
177+
table)
208178

209179

210180
(define identifier-usage-analyzer
211181
(make-expansion-analyzer
212182
#:name 'identifier-usage-analyzer
213183
(λ (expanded-stx)
214184
(define table (fully-expanded-syntax-binding-table expanded-stx))
215-
(transduce (in-hash-values table)
216-
(append-mapping
217-
(λ (id-table)
218-
(for/stream ([(bound-id usages) (in-free-id-table id-table)])
219-
(define exp-path (syntax-property bound-id 'expanded-path))
220-
(syntax-property-entry exp-path 'usage-count (length usages)))))
185+
(transduce (in-expanded-id-table table)
186+
(mapping
187+
(λ (entry)
188+
(define expanded-id (entry-key entry))
189+
(define usages (entry-value entry))
190+
(define bound-id (expanded-identifier-syntax expanded-id))
191+
(define exp-path (syntax-property bound-id 'expanded-path))
192+
(syntax-property-entry exp-path 'usage-count (length usages))))
221193
#:into into-syntax-property-bundle))))
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
#lang racket/base
2+
3+
4+
(require racket/contract/base)
5+
6+
7+
(provide
8+
(struct-out expanded-identifier)
9+
(contract-out
10+
[expanded-id-table? (-> any/c boolean?)]
11+
[make-expanded-id-table (-> expanded-id-table?)]
12+
[expanded-id-table-ref
13+
(->* (expanded-id-table? expanded-identifier?) (failure-result/c) any/c)]
14+
[expanded-id-table-set! (-> expanded-id-table? expanded-identifier? any/c void?)]
15+
[in-expanded-id-table
16+
(-> expanded-id-table? (sequence/c (entry/c expanded-identifier? any/c)))]
17+
[in-expanded-id-table-phase
18+
(-> expanded-id-table? (or/c exact-nonnegative-integer? #false) (sequence/c (entry/c expanded-identifier? any/c)))]))
19+
20+
21+
(require guard
22+
racket/contract/base
23+
racket/dict
24+
racket/match
25+
racket/sequence
26+
racket/stream
27+
rebellion/base/result
28+
rebellion/collection/entry
29+
syntax/id-table)
30+
31+
32+
(module+ test
33+
(require (submod "..")
34+
rackunit))
35+
36+
37+
;@----------------------------------------------------------------------------------------------------
38+
39+
40+
(struct expanded-identifier (syntax phase)
41+
#:transparent
42+
#:guard (struct-guard/c identifier? (or/c exact-nonnegative-integer? #false)))
43+
44+
45+
(struct expanded-id-table (table))
46+
47+
48+
(define (make-expanded-id-table)
49+
(expanded-id-table (make-hasheq)))
50+
51+
52+
(define (expanded-id-table-ref table id [failure-result (λ () (error 'expanded-id-table-ref "no mapping for ~a" id))])
53+
(define phase (expanded-identifier-phase id))
54+
(define stx (expanded-identifier-syntax id))
55+
(define phase-table (hash-ref (expanded-id-table-table table) phase #false))
56+
(if phase-table
57+
(free-id-table-ref phase-table stx failure-result)
58+
(if (procedure? failure-result)
59+
(failure-result)
60+
failure-result)))
61+
62+
63+
(define (expanded-id-table-set! table id value)
64+
(define phase (expanded-identifier-phase id))
65+
(define stx (expanded-identifier-syntax id))
66+
(define phase-table
67+
(hash-ref! (expanded-id-table-table table)
68+
phase
69+
(λ () (make-free-id-table #:phase phase))))
70+
(free-id-table-set! phase-table stx value))
71+
72+
73+
(define (in-expanded-id-table table)
74+
(for*/stream ([(phase phase-table) (in-hash (expanded-id-table-table table))]
75+
[(stx value) (in-free-id-table phase-table)])
76+
(entry (expanded-identifier stx phase) value)))
77+
78+
79+
(define (in-expanded-id-table-phase table phase)
80+
(define phase-table (hash-ref (expanded-id-table-table table) phase #false))
81+
(if phase-table
82+
(for/stream ([(stx value) (in-free-id-table phase-table)])
83+
(entry (expanded-identifier stx phase) value))
84+
(stream)))
85+
86+
87+
;@----------------------------------------------------------------------------------------------------
88+
89+
90+
(module+ test
91+
(test-case "expanded-id-table"
92+
93+
(test-case "make-expanded-id-table creates empty table"
94+
(define table (make-expanded-id-table))
95+
(check-pred expanded-id-table? table))
96+
97+
(test-case "expanded-id-table-set! and expanded-id-table-ref"
98+
(define table (make-expanded-id-table))
99+
(define id1 (expanded-identifier #'x 0))
100+
(expanded-id-table-set! table id1 'foo)
101+
(check-equal? (expanded-id-table-ref table id1) 'foo))
102+
103+
(test-case "identifiers at different phases are distinct"
104+
(define table (make-expanded-id-table))
105+
(define id-phase0 (expanded-identifier #'x 0))
106+
(define id-phase1 (expanded-identifier #'x 1))
107+
(expanded-id-table-set! table id-phase0 'phase0-value)
108+
(expanded-id-table-set! table id-phase1 'phase1-value)
109+
(check-equal? (expanded-id-table-ref table id-phase0) 'phase0-value)
110+
(check-equal? (expanded-id-table-ref table id-phase1) 'phase1-value))
111+
112+
(test-case "in-expanded-id-table iterates over entries"
113+
(define table (make-expanded-id-table))
114+
(define id1 (expanded-identifier #'x 0))
115+
(define id2 (expanded-identifier #'y 0))
116+
(define id3 (expanded-identifier #'z 1))
117+
(expanded-id-table-set! table id1 'val1)
118+
(expanded-id-table-set! table id2 'val2)
119+
(expanded-id-table-set! table id3 'val3)
120+
(define entries (for/list ([e (in-expanded-id-table table)]) e))
121+
(check-equal? (length entries) 3))))

default-recommendations/analyzers/variable-mutability.rkt

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@
99
[variable-mutability-analyzer expansion-analyzer?]))
1010

1111

12-
(require racket/dict
13-
racket/list
12+
(require racket/list
1413
racket/match
1514
racket/stream
15+
rebellion/collection/entry
1616
rebellion/streaming/transducer
17+
resyntax/default-recommendations/analyzers/private/expanded-id-table
1718
resyntax/private/analyzer
1819
resyntax/private/syntax-path
1920
resyntax/private/syntax-property-bundle
@@ -104,20 +105,19 @@
104105

105106
(define (variable-mutability stx)
106107
(define labeled-stx (syntax-label-id-phases (syntax-label-paths stx 'expanded-path)))
107-
(define variable-table (make-hash))
108+
(define variable-table (make-expanded-id-table))
108109
(for ([id (in-stream (binding-site-variables labeled-stx))])
109110
(define phase (syntax-property id 'phase))
110-
(define phase-specific-table
111-
(hash-ref! variable-table phase (λ () (make-free-id-table #:phase phase))))
112-
(free-id-table-set! phase-specific-table id 'immutable))
111+
(expanded-id-table-set! variable-table (expanded-identifier id phase) 'immutable))
113112
(for ([id (in-stream (mutated-variables labeled-stx))])
114-
(define phase-specific-table (hash-ref variable-table (syntax-property id 'phase)))
115-
(free-id-table-set! phase-specific-table id 'mutable))
116-
(transduce (in-hash-values variable-table)
117-
(append-mapping in-dict-pairs)
113+
(define phase (syntax-property id 'phase))
114+
(expanded-id-table-set! variable-table (expanded-identifier id phase) 'mutable))
115+
(transduce (in-expanded-id-table variable-table)
118116
(mapping
119117
(λ (e)
120-
(match-define (cons id mode) e)
118+
(define expanded-id (entry-key e))
119+
(define mode (entry-value e))
120+
(define id (expanded-identifier-syntax expanded-id))
121121
(define path (syntax-property id 'expanded-path))
122122
(syntax-property-entry path 'variable-mutability mode)))
123123
#:into into-syntax-property-bundle))

0 commit comments

Comments
 (0)