generated from jackfirth/racket-package-template
-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathbase.rkt
221 lines (178 loc) · 8.3 KB
/
base.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
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
#lang racket/base
(require racket/contract/base)
(provide
~replacement
~splicing-replacement
~focus-replacement-on
define-refactoring-suite
define-refactoring-rule
define-definition-context-refactoring-rule
(contract-out
[refactoring-rule? (-> any/c boolean?)]
[refactoring-rule-description (-> refactoring-rule? immutable-string?)]
[refactoring-suite? (-> any/c boolean?)]
[refactoring-suite
(->* ()
(#:rules (sequence/c refactoring-rule?) #:name (or/c interned-symbol? #false))
refactoring-suite?)]
[refactoring-suite-rules (-> refactoring-suite? (listof refactoring-rule?))]))
(module+ private
(provide
(contract-out
[refactoring-rule-refactor
(-> refactoring-rule? syntax? source? (option/c syntax-replacement?))])))
(require (for-syntax racket/base racket/syntax resyntax/private/more-syntax-parse-classes)
racket/sequence
rebellion/base/immutable-string
rebellion/base/option
rebellion/base/symbol
rebellion/type/object
resyntax/default-recommendations/private/definition-context
resyntax/private/source
resyntax/private/syntax-neighbors
resyntax/private/syntax-replacement
syntax/parse
syntax/parse/define
syntax/parse/experimental/template)
;@----------------------------------------------------------------------------------------------------
(define-template-metafunction (~replacement stx)
(syntax-parse stx
[(_ new-stx #:original orig-syntax)
(syntax-property #'new-stx 'replacement-for #'orig-syntax)]
[(_ new-stx #:original-splice (first-orig orig-syntax ... last-orig))
(syntax-property (syntax-property #'new-stx 'head-replacement-for #'first-orig)
'tail-replacement-for #'last-orig)]
[(_ new-stx #:original-splice (only-orig-syntax))
(syntax-property (syntax-property #'new-stx 'head-replacement-for #'only-orig-syntax)
'tail-replacement-for #'only-orig-syntax)]))
(define-template-metafunction (~splicing-replacement stx)
(syntax-parse stx
[(_ (~and new-stx (first-subform subform ... last-subform)) #:original orig-syntax)
(define first-with-prop (syntax-property #'first-subform 'head-replacement-for #'orig-syntax))
(define last-with-prop (syntax-property #'last-subform 'tail-replacement-for #'orig-syntax))
(define new-stx-with-subform-props
(datum->syntax #'new-stx
#`(#,first-with-prop subform ... #,last-with-prop)
#'new-stx
#'new-stx))
(syntax-property new-stx-with-subform-props 'replacement-for #'orig-syntax)]
[(_ (~and new-stx (only-subform)) #:original orig-syntax)
(define subform-with-props
(syntax-property (syntax-property #'only-subform 'head-replacement-for #'orig-syntax)
'tail-replacement-for
#'orig-syntax))
(define new-stx-with-subform-props
(datum->syntax #'new-stx #`(#,subform-with-props) #'new-stx #'new-stx))
(syntax-property new-stx-with-subform-props 'replacement-for #'orig-syntax)]
[(_ (~and new-stx ()) #:original orig-syntax)
(syntax-property #'new-stx 'replacement-for #'orig-syntax)]))
(define-template-metafunction (~focus-replacement-on stx)
(syntax-parse stx
[(_ (~and new-stx (substx ...)))
#:cut
(define substxs-with-prop
(for/list ([sub (in-list (attribute substx))])
(syntax-property sub 'focus-replacement-on #true)))
(syntax-property (datum->syntax #'new-stx substxs-with-prop #'new-stx #'new-stx)
'focus-replacement-on #true)]
[(_ new-stx) (syntax-property #'new-stx 'focus-replacement-on #true)]))
(define-object-type refactoring-rule (transformer description)
#:omit-root-binding
#:constructor-name constructor:refactoring-rule)
(define (refactoring-rule-refactor rule syntax source)
;; Before refactoring the input syntax, we do two things: create a new scope and add it, and
;; traverse the syntax object making a note of each subform's original neighbors. Combined,
;; these two things allow us to tell when two neighboring subforms within the output syntax object
;; are originally from the input and were originally next to each other in the input. This allows
;; Resyntax to preserve any formatting and comments between those two subform when rendering the
;; resulting syntax replacement into a string transformation.
(define rule-introduction-scope (make-syntax-introducer))
(define prepared-syntax (rule-introduction-scope (syntax-mark-original-neighbors syntax)))
(option-map
((refactoring-rule-transformer rule) prepared-syntax)
(λ (new-syntax)
(syntax-replacement
#:source source
#:original-syntax syntax
#:new-syntax (rule-introduction-scope new-syntax)
#:introduction-scope rule-introduction-scope))))
(define-syntax-parse-rule
(define-refactoring-rule id:id
#:description description
parse-option:syntax-parse-option ...
pattern
pattern-directive:syntax-parse-pattern-directive ...
replacement)
#:declare description (expr/c #'string?)
(define id
(constructor:refactoring-rule
#:name 'id
#:description (string->immutable-string description.c)
#:transformer
(λ (stx)
(syntax-parse stx
(~@ . parse-option) ...
[pattern (~@ . pattern-directive) ... (present #'replacement)]
[_ absent])))))
(define-syntax-parse-rule
(define-definition-context-refactoring-rule id:id
#:description (~var description (expr/c #'string?))
parse-option:syntax-parse-option ...
splicing-pattern
pattern-directive:syntax-parse-pattern-directive ...
(splicing-replacement ...))
;; These identifiers are macro-introduced, but we use format-id on them anyway so that the expanded
;; code is more readable and it's clearer which refactoring rule these syntax classes are derived
;; from.
#:with body-matching-id (format-id #'macro-introduced-context "body-matching-~a" #'id)
#:with expression-matching-id (format-id #'macro-introduced-context "expression-matching-~a" #'id)
(begin
(define-splicing-syntax-class body-matching-id
#:attributes ([refactored 1])
(~@ . parse-option) ...
(pattern splicing-pattern
(~@ . pattern-directive) ...
#:with (refactored (... ...)) #'(splicing-replacement ...)))
(define-syntax-class expression-matching-id
#:attributes (refactored)
(pattern ((~var header header-form-allowing-internal-definitions) (~var body body-matching-id))
#:cut
#:with refactored #'(header.original (... ...) body.refactored (... ...)))
(pattern ((~var branching-header branching-form-allowing-internal-definitions-within-clauses)
clause-before (... ...)
(~and original-clause [clause-header (~var body body-matching-id)])
clause-after (... ...))
#:cut
#:with refactored
#'(branching-header.original
(... ...)
clause-before (... ...)
(~replacement [clause-header body.refactored (... ...)] #:original original-clause)
clause-after (... ...))))
(define-refactoring-rule id
#:description description
(~var expression expression-matching-id)
expression.refactored)))
(define-object-type refactoring-suite (rules)
#:constructor-name constructor:refactoring-suite
#:omit-root-binding)
(define (refactoring-suite #:rules [rules '()] #:name [name #false])
(constructor:refactoring-suite #:rules (sequence->list rules) #:name name))
(begin-for-syntax
(define-splicing-syntax-class rules-list
#:attributes (as-list-expr)
(pattern (~seq) #:with as-list-expr #'(list))
(pattern (~seq #:rules ~! (rule ...))
#:declare rule (expr/c #'refactoring-rule?)
#:with as-list-expr #'(list rule.c ...)))
(define-splicing-syntax-class suites-list
#:attributes (as-list-expr)
(pattern (~seq) #:with as-list-expr #'(list))
(pattern (~seq #:suites ~! (suite ...))
#:declare suite (expr/c #'refactoring-suite?)
#:with as-list-expr #'(append (refactoring-suite-rules suite.c) ...))))
(define-syntax-parse-rule (define-refactoring-suite id:id rules:rules-list suites:suites-list)
(define id
(refactoring-suite
#:name 'id
#:rules (append rules.as-list-expr suites.as-list-expr))))