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