stxparse-info.scrbl (12512B)
1 #lang scribble/manual 2 @require[racket/require 3 @for-label[stxparse-info/parse 4 stxparse-info/parse/experimental/template 5 stxparse-info/case 6 stxparse-info/current-pvars 7 (subtract-in racket/syntax stxparse-info/case) 8 (subtract-in racket/base stxparse-info/case)] 9 version-case 10 @for-syntax[racket/base] 11 "ovl.rkt"] 12 13 @; Circumvent https://github.com/racket/scribble/issues/79 14 @(require scribble/struct 15 scribble/decode) 16 @(define (nested-inset . vs) 17 (nested #:style 'inset vs)) 18 19 @(version-case 20 [(version< (version) "6.4") 21 ] 22 [else 23 (require scribble/example) 24 (define ev ((make-eval-factory '(racket))))]) 25 26 @title{@racketmodname[stxparse-info]: Track @racket[syntax-parse] and @racket[syntax-case] pattern vars} 27 @author[@author+email["Suzanne Soy" "racket@suzanne.soy"]] 28 29 Source code: @url{https://github.com/jsmaniac/stxparse-info} 30 31 @defmodule[stxparse-info] 32 33 This library provides some patched versions of @orig:syntax-parse and of the 34 @orig:syntax-case family. These patched versions track which syntax pattern 35 variables are bound. This allows some libraries to change the way syntax 36 pattern variables work. 37 38 For example, @tt{subtemplate} automatically derives temporary 39 identifiers when a template contains @racket[yᵢ …], and @racket[xᵢ] is a 40 pattern variable. To know from which @racket[varᵢ] the @racket[yᵢ …] 41 identifiers must be derived, @tt{subtemplate} needs to know which 42 syntax pattern variables are within scope. 43 44 @section{Tracking currently-bound pattern variables with @racket[syntax-parse]} 45 46 @defmodule[stxparse-info/parse] 47 48 The module @racketmodname[stxparse-info/parse] provides patched versions of 49 @orig:syntax-parse, @orig:syntax-parser and @orig:define/syntax-parse which 50 track which syntax pattern variables are bound. 51 52 @(ovl syntax/parse 53 syntax-parse 54 syntax-parser 55 define/syntax-parse) 56 57 Additionally, the following identifiers are overridden as they are part of the 58 duplicated implementation of @racketmodname[syntax/parse]. 59 60 @(ovl #:wrapper nested-inset 61 syntax/parse 62 ...+ 63 attribute 64 boolean 65 char 66 character 67 define-conventions 68 define-eh-alternative-set 69 define-literal-set 70 define-splicing-syntax-class 71 define-syntax-class 72 exact-integer 73 exact-nonnegative-integer 74 exact-positive-integer 75 expr 76 expr/c 77 id 78 identifier 79 integer 80 kernel-literals 81 keyword 82 literal-set->predicate 83 nat 84 number 85 pattern 86 prop:syntax-class 87 static 88 str 89 syntax-parse-state-cons! 90 syntax-parse-state-ref 91 syntax-parse-state-set! 92 syntax-parse-state-update! 93 syntax-parse-track-literals 94 this-syntax 95 ~! 96 ~and 97 ~between 98 ~bind 99 ~commit 100 ~datum 101 ~delimit-cut 102 ~describe 103 ~do 104 ~fail 105 ~literal 106 ~not 107 ~once 108 ~optional 109 ~or 110 ~parse 111 ~peek 112 ~peek-not 113 ~post 114 ~rest 115 ~seq 116 ~undo 117 ~var) 118 119 @(version-case 120 [(version>= (version) "6.9.0.6") 121 (ovl #:wrapper nested-inset 122 syntax/parse 123 ~alt 124 ~or*)] 125 [else (begin)]) 126 127 @(ovl #:wrapper nested-inset 128 #:require (for-template syntax/parse) 129 syntax/parse 130 pattern-expander? 131 pattern-expander 132 prop:pattern-expander 133 syntax-local-syntax-parse-pattern-introduce) 134 135 @section{Tracking currently-bound pattern variables with @racket[syntax-case]} 136 137 @defmodule[stxparse-info/case] 138 139 The module @racketmodname[stxparse-info/case] provides patched versions of 140 @orig:syntax-case, @orig:syntax-case*, @orig:with-syntax, 141 @orig:define/with-syntax, @orig:datum-case and @orig:with-datum which 142 track which syntax or datum pattern variables are bound. 143 144 @(ovl racket/base 145 syntax-case 146 syntax-case* 147 with-syntax) 148 149 @(ovl syntax/datum 150 datum-case 151 with-datum) 152 153 @(ovl racket/syntax 154 define/with-syntax) 155 156 @section{Reading and updating the list of currently-bound pattern variables} 157 158 @defmodule[stxparse-info/current-pvars] 159 160 @defproc[#:kind "procedure at phase 1" 161 (current-pvars) (listof identifier?)]{ 162 This for-syntax procedure returns the list of syntax pattern variables which 163 are known to be bound. The most recently bound variables are at the beginning 164 of the list. 165 166 It is the responsibility of the reader to check that the identifiers are 167 bound, and that they are bound to syntax pattern variables, for example using 168 @racket[identifier-binding] and @racket[syntax-pattern-variable?]. This allows 169 libraries to also track variables bound by match-like forms, for example.} 170 171 @defproc[#:kind "procedure at phase 1" 172 (current-pvars+unique) (listof (pairof identifier? identifier?))]{ 173 This for-syntax procedure works like @racket[current-pvars], but associates 174 each syntax pattern variable with an identifier containing a unique symbol 175 which is generated at each execution of the code recording the pattern 176 variable via @racket[with-pvars] or @racket[define-pvars]. 177 178 The @racket[car] of each pair in the returned list is the syntax pattern 179 variable (as produced by @racket[current-pvars]). It is the responsibility of 180 the reader to check that the identifiers present in the @racket[car] of each 181 element of the returned list are bound, and that they are bound to syntax 182 pattern variables, for example using @racket[identifier-binding] and 183 @racket[syntax-pattern-variable?]. This allows libraries to also track 184 variables bound by match-like forms, for example. 185 186 The @racket[cdr] of each pair is the identifier of a temporary variable. 187 Reading that temporary variable produces a @racket[gensym]-ed symbol, which 188 was generated at run-time at the point where @racket[with-pvars] or 189 @racket[define-pvars] was used to record the corresponding pattern variable. 190 191 This can be used to associate run-time data with each syntax pattern 192 variable, via a weak hash table created with @racket[make-weak-hasheq]. For 193 example, the @tt{subtemplate} library implicitly derives 194 identifiers (similarly to @racket[generate-temporaries]) for uses of 195 @racket[yᵢ ...] from a @racket[xᵢ] pattern variable bearing the same 196 subscript. The generated identifiers are associated with @racket[xᵢ] via this 197 weak hash table mechanism, so that two uses of @racket[yᵢ ...] within the 198 scope of the same @racket[xᵢ] binding derive the same identifiers. 199 200 The code @racket[(with-pvars (v) body)] roughly expands to: 201 202 @racketblock[ 203 (let-values ([(tmp) (gensym 'v)]) 204 (letrec-syntaxes+values ([(shadow-current-pvars) 205 (list* (cons (quote-syntax v) 206 (quote-syntax tmp)) 207 old-current-pvars)]) 208 body))] 209 210 @bold{Caveat:} this entails that the fresh symbol stored in @racket[tmp] is 211 generated when @racket[with-pvars] or @racket[define-pvars] is called, not 212 when the syntax pattern variable is actually bound. For example: 213 214 @RACKETBLOCK[ 215 (define-syntax (get-current-pvars+unique stx) 216 #`'#,(current-pvars+unique)) 217 218 (require racket/private/sc) 219 (let ([my-valvar (quote-syntax x)]) 220 (let-syntax ([my-pvar (make-syntax-mapping 0 (quote-syntax my-valvar))]) 221 (with-pvars (x) 222 (get-current-pvars+unique)) (code:comment "'([x . g123])") 223 (with-pvars (x) 224 (get-current-pvars+unique)))) (code:comment "'([x . g124])")] 225 226 Under normal circumstances, @racket[with-pvars] @racket[define-pvars] should 227 be called immediately after binding the syntax pattern variable, but the code 228 above shows that it is technically possible to do otherwise. 229 230 This caveat is not meant to dissuade the use of 231 @racket[current-pvars+unique], it rather serves as an explanation of the 232 behaviour encountered when @racket[with-pvars] or @racket[define-pvars] are 233 incorrectly used more than once to record the same pattern variable.} 234 235 @defform[(with-pvars (pvar ...) . body) 236 #:contracts ([pvar identifier?])]{ 237 Prepends the given @racket[pvar ...] to the list of pattern variables which 238 are known to be bound. The @racket[pvar ...] are prepended in reverse order, 239 so within the body of 240 241 @racketblock[(with-pvars (v₁ v₂ v₃) . body)] 242 243 a call to the for-syntax function @racket[(current-pvars)] returns: 244 245 @racketblock[(list* (quote-syntax v₃) (quote-syntax v₂) (quote-syntax v₁) 246 old-current-pvars)] 247 248 This can be used to implement macros which work similarly to 249 @racket[syntax-parse] or @racket[syntax-case], and have them record the syntax 250 pattern variables which they bind. 251 252 Note that the identifiers @racket[pvar ...] must already be bound to syntax 253 pattern variables when @racket[with-pvars] is used, e.g. 254 255 @racketblock[ 256 (let-syntax ([v₁ (make-syntax-mapping depth (quote-syntax valvar))] 257 [v₂ (make-syntax-mapping depth (quote-syntax valvar))]) 258 (with-pvars (v₁ v₂) 259 code))] 260 261 instead of: 262 263 @racketblock[ 264 (with-pvars (v₁ v₂) 265 (let-syntax ([v₁ (make-syntax-mapping depth (quote-syntax valvar))] 266 [v₂ (make-syntax-mapping depth (quote-syntax valvar))]) 267 code))]} 268 269 @defform[(define-pvars pvar ...) 270 #:contracts ([pvar identifier?])]{ 271 272 Prepends the given @racket[pvar ...] to the list of pattern variables which 273 are known to be bound, in the same way as @racket[with-pvars]. Whereas 274 @racket[with-pvars] makes the modified list visible in the @racket[_body], 275 @racket[define-pvars] makes the modified list visible in the statements 276 following @racket[define-pvars]. @racket[define-pvars] can be used multiple 277 times within the same @racket[let] or equivalent. 278 279 This can be used to implement macros which work similarly to 280 @racket[define/syntax-parse] or @racket[define/with-syntax], and have them 281 record the syntax pattern variables which they bind. 282 283 @(version-case 284 [(version< (version) "6.4") 285 @RACKETBLOCK[ 286 (let () 287 (code:comment "Alternate version of define/syntax-parse which") 288 (code:comment "contains (define-pvars x) in its expanded form.") 289 (define/syntax-parse x #'1) 290 (define/syntax-parse y #'2) 291 (define-syntax (get-pvars stx) 292 #`'#,(current-pvars)) 293 (get-pvars)) 294 (code:comment "=> '(y x)")]] 295 [else 296 @examples[ 297 #:eval ev 298 #:hidden 299 (require stxparse-info/parse 300 stxparse-info/current-pvars 301 racket/syntax 302 (for-syntax racket/base))] 303 304 @examples[ 305 #:eval ev 306 #:escape UNSYNTAX 307 (eval:check 308 (let () 309 (code:comment "Alternate version of define/syntax-parse which") 310 (code:comment "contains (define-pvars x) in its expanded form.") 311 (define/syntax-parse x #'1) 312 (define/syntax-parse y #'2) 313 (define-syntax (get-pvars stx) 314 #`'#,(current-pvars)) 315 (get-pvars)) 316 '(y x))]])} 317 318 @section{Extensions to @racketmodname[syntax/parse/experimental/template]} 319 320 @defmodule[stxparse-info/parse/experimental/template] 321 322 @(orig syntax/parse/experimental/template 323 define-template-metafunction) 324 325 @defidform[define-template-metafunction]{ 326 Overloaded version of @orig:define-template-metafunction from 327 @racketmodname[syntax/parse/experimental/template]. 328 329 Note that currently, template metafunctions defined via 330 @racketmodname[stxparse-info/parse/experimental/template] are not compatible 331 with the forms from @racketmodname[syntax/parse/experimental/template], and 332 vice versa. There is a pending Pull Request which would make the necessary 333 primitives from @racketmodname[syntax/parse/experimental/template] public, so 334 hopefully this problem will be solved in future versions.} 335 336 @defform[(syntax-local-template-metafunction-introduce stx)]{ 337 Like @racket[syntax-local-introduce], but for 338 @tech[#:doc '(lib "syntax/scribblings/syntax.scrbl")]{template metafunctions}. 339 340 This change is also available in the package 341 @racketmodname{backport-template-pr1514}. It has been submitted as a Pull 342 Request to Racket, but can already be used in 343 @racketmodname[stxparse-info/parse/experimental/template] right now.} 344 345 @(ovl syntax/parse/experimental/template 346 template 347 quasitemplate 348 template/loc 349 quasitemplate/loc) 350 351 Additionally, the following identifiers are overridden as they are part of the 352 duplicated implementation of @racketmodname[syntax/parse]. 353 354 @(ovl #:wrapper nested-inset 355 syntax/parse/experimental/template 356 ?? 357 ?@)