www

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

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