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