www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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       ?@)