www

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

stxparse-info.scrbl (12454B)


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