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