www

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

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