From 1766ed5a2a92db4a1a7f9c25c2c782338a519690 Mon Sep 17 00:00:00 2001 From: Suzanne Soy Date: Wed, 3 Mar 2021 01:02:31 +0000 Subject: [PATCH] extra files --- 8-0/base_commit.txt | 1 + 8-0/stxparse-info.scrbl | 357 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 358 insertions(+) create mode 100644 8-0/base_commit.txt create mode 100644 8-0/stxparse-info.scrbl diff --git a/8-0/base_commit.txt b/8-0/base_commit.txt new file mode 100644 index 0000000..9c199de --- /dev/null +++ b/8-0/base_commit.txt @@ -0,0 +1 @@ +421dce228d5a98d89db2bddb27cd32211cdb1ae3 diff --git a/8-0/stxparse-info.scrbl b/8-0/stxparse-info.scrbl new file mode 100644 index 0000000..8cd3733 --- /dev/null +++ b/8-0/stxparse-info.scrbl @@ -0,0 +1,357 @@ +#lang scribble/manual +@require[racket/require + @for-label[stxparse-info/parse + stxparse-info/parse/experimental/template + stxparse-info/case + stxparse-info/current-pvars + (subtract-in racket/syntax stxparse-info/case) + (subtract-in racket/base stxparse-info/case)] + version-case + @for-syntax[racket/base] + "ovl.rkt"] + +@; Circumvent https://github.com/racket/scribble/issues/79 +@(require scribble/struct + scribble/decode) +@(define (nested-inset . vs) + (nested #:style 'inset vs)) + +@(version-case + [(version< (version) "6.4") + ] + [else + (require scribble/example) + (define ev ((make-eval-factory '(racket))))]) + +@title{@racketmodname[stxparse-info]: Track @racket[syntax-parse] and @racket[syntax-case] pattern vars} +@author[@author+email["Suzanne Soy" "racket@suzanne.soy"]] + +Source code: @url{https://github.com/jsmaniac/stxparse-info} + +@defmodule[stxparse-info] + +This library provides some patched versions of @orig:syntax-parse and of the +@orig:syntax-case family. These patched versions track which syntax pattern +variables are bound. This allows some libraries to change the way syntax +pattern variables work. + +For example, @tt{subtemplate} automatically derives temporary +identifiers when a template contains @racket[yᵢ …], and @racket[xᵢ] is a +pattern variable. To know from which @racket[varᵢ] the @racket[yᵢ …] +identifiers must be derived, @tt{subtemplate} needs to know which +syntax pattern variables are within scope. + +@section{Tracking currently-bound pattern variables with @racket[syntax-parse]} + +@defmodule[stxparse-info/parse] + +The module @racketmodname[stxparse-info/parse] provides patched versions of +@orig:syntax-parse, @orig:syntax-parser and @orig:define/syntax-parse which +track which syntax pattern variables are bound. + +@(ovl syntax/parse + syntax-parse + syntax-parser + define/syntax-parse) + +Additionally, the following identifiers are overridden as they are part of the +duplicated implementation of @racketmodname[syntax/parse]. + +@(ovl #:wrapper nested-inset + syntax/parse + ...+ + attribute + boolean + char + character + define-conventions + define-eh-alternative-set + define-literal-set + define-splicing-syntax-class + define-syntax-class + exact-integer + exact-nonnegative-integer + exact-positive-integer + expr + expr/c + id + identifier + integer + kernel-literals + keyword + literal-set->predicate + nat + number + pattern + prop:syntax-class + static + str + syntax-parse-state-cons! + syntax-parse-state-ref + syntax-parse-state-set! + syntax-parse-state-update! + syntax-parse-track-literals + this-syntax + ~! + ~and + ~between + ~bind + ~commit + ~datum + ~delimit-cut + ~describe + ~do + ~fail + ~literal + ~not + ~once + ~optional + ~or + ~parse + ~peek + ~peek-not + ~post + ~rest + ~seq + ~undo + ~var) + +@(version-case + [(version>= (version) "6.9.0.6") + (ovl #:wrapper nested-inset + syntax/parse + ~alt + ~or*)] + [else (begin)]) + +@(ovl #:wrapper nested-inset + #:require (for-template syntax/parse) + syntax/parse + pattern-expander? + pattern-expander + prop:pattern-expander + syntax-local-syntax-parse-pattern-introduce) + +@section{Tracking currently-bound pattern variables with @racket[syntax-case]} + +@defmodule[stxparse-info/case] + +The module @racketmodname[stxparse-info/case] provides patched versions of +@orig:syntax-case, @orig:syntax-case*, @orig:with-syntax, +@orig:define/with-syntax, @orig:datum-case and @orig:with-datum which +track which syntax or datum pattern variables are bound. + +@(ovl racket/base + syntax-case + syntax-case* + with-syntax) + +@(ovl syntax/datum + datum-case + with-datum) + +@(ovl racket/syntax + define/with-syntax) + +@section{Reading and updating the list of currently-bound pattern variables} + +@defmodule[stxparse-info/current-pvars] + +@defproc[#:kind "procedure at phase 1" + (current-pvars) (listof identifier?)]{ + This for-syntax procedure returns the list of syntax pattern variables which + are known to be bound. The most recently bound variables are at the beginning + of the list. + + It is the responsibility of the reader to check that the identifiers are + bound, and that they are bound to syntax pattern variables, for example using + @racket[identifier-binding] and @racket[syntax-pattern-variable?]. This allows + libraries to also track variables bound by match-like forms, for example.} + +@defproc[#:kind "procedure at phase 1" + (current-pvars+unique) (listof (pairof identifier? identifier?))]{ + This for-syntax procedure works like @racket[current-pvars], but associates + each syntax pattern variable with an identifier containing a unique symbol + which is generated at each execution of the code recording the pattern + variable via @racket[with-pvars] or @racket[define-pvars]. + + The @racket[car] of each pair in the returned list is the syntax pattern + variable (as produced by @racket[current-pvars]). It is the responsibility of + the reader to check that the identifiers present in the @racket[car] of each + element of the returned list are bound, and that they are bound to syntax + pattern variables, for example using @racket[identifier-binding] and + @racket[syntax-pattern-variable?]. This allows libraries to also track + variables bound by match-like forms, for example. + + The @racket[cdr] of each pair is the identifier of a temporary variable. + Reading that temporary variable produces a @racket[gensym]-ed symbol, which + was generated at run-time at the point where @racket[with-pvars] or + @racket[define-pvars] was used to record the corresponding pattern variable. + + This can be used to associate run-time data with each syntax pattern + variable, via a weak hash table created with @racket[make-weak-hasheq]. For + example, the @tt{subtemplate} library implicitly derives + identifiers (similarly to @racket[generate-temporaries]) for uses of + @racket[yᵢ ...] from a @racket[xᵢ] pattern variable bearing the same + subscript. The generated identifiers are associated with @racket[xᵢ] via this + weak hash table mechanism, so that two uses of @racket[yᵢ ...] within the + scope of the same @racket[xᵢ] binding derive the same identifiers. + + The code @racket[(with-pvars (v) body)] roughly expands to: + + @racketblock[ + (let-values ([(tmp) (gensym 'v)]) + (letrec-syntaxes+values ([(shadow-current-pvars) + (list* (cons (quote-syntax v) + (quote-syntax tmp)) + old-current-pvars)]) + body))] + + @bold{Caveat:} this entails that the fresh symbol stored in @racket[tmp] is + generated when @racket[with-pvars] or @racket[define-pvars] is called, not + when the syntax pattern variable is actually bound. For example: + + @RACKETBLOCK[ + (define-syntax (get-current-pvars+unique stx) + #`'#,(current-pvars+unique)) + + (require racket/private/sc) + (let ([my-valvar (quote-syntax x)]) + (let-syntax ([my-pvar (make-syntax-mapping 0 (quote-syntax my-valvar))]) + (with-pvars (x) + (get-current-pvars+unique)) (code:comment "'([x . g123])") + (with-pvars (x) + (get-current-pvars+unique)))) (code:comment "'([x . g124])")] + + Under normal circumstances, @racket[with-pvars] @racket[define-pvars] should + be called immediately after binding the syntax pattern variable, but the code + above shows that it is technically possible to do otherwise. + + This caveat is not meant to dissuade the use of + @racket[current-pvars+unique], it rather serves as an explanation of the + behaviour encountered when @racket[with-pvars] or @racket[define-pvars] are + incorrectly used more than once to record the same pattern variable.} + +@defform[(with-pvars (pvar ...) . body) + #:contracts ([pvar identifier?])]{ + Prepends the given @racket[pvar ...] to the list of pattern variables which + are known to be bound. The @racket[pvar ...] are prepended in reverse order, + so within the body of + + @racketblock[(with-pvars (v₁ v₂ v₃) . body)] + + a call to the for-syntax function @racket[(current-pvars)] returns: + + @racketblock[(list* (quote-syntax v₃) (quote-syntax v₂) (quote-syntax v₁) + old-current-pvars)] + + This can be used to implement macros which work similarly to + @racket[syntax-parse] or @racket[syntax-case], and have them record the syntax + pattern variables which they bind. + + Note that the identifiers @racket[pvar ...] must already be bound to syntax + pattern variables when @racket[with-pvars] is used, e.g. + + @racketblock[ + (let-syntax ([v₁ (make-syntax-mapping depth (quote-syntax valvar))] + [v₂ (make-syntax-mapping depth (quote-syntax valvar))]) + (with-pvars (v₁ v₂) + code))] + + instead of: + + @racketblock[ + (with-pvars (v₁ v₂) + (let-syntax ([v₁ (make-syntax-mapping depth (quote-syntax valvar))] + [v₂ (make-syntax-mapping depth (quote-syntax valvar))]) + code))]} + +@defform[(define-pvars pvar ...) + #:contracts ([pvar identifier?])]{ + + Prepends the given @racket[pvar ...] to the list of pattern variables which + are known to be bound, in the same way as @racket[with-pvars]. Whereas + @racket[with-pvars] makes the modified list visible in the @racket[_body], + @racket[define-pvars] makes the modified list visible in the statements + following @racket[define-pvars]. @racket[define-pvars] can be used multiple + times within the same @racket[let] or equivalent. + + This can be used to implement macros which work similarly to + @racket[define/syntax-parse] or @racket[define/with-syntax], and have them + record the syntax pattern variables which they bind. + + @(version-case + [(version< (version) "6.4") + @RACKETBLOCK[ + (let () + (code:comment "Alternate version of define/syntax-parse which") + (code:comment "contains (define-pvars x) in its expanded form.") + (define/syntax-parse x #'1) + (define/syntax-parse y #'2) + (define-syntax (get-pvars stx) + #`'#,(current-pvars)) + (get-pvars)) + (code:comment "=> '(y x)")]] + [else + @examples[ + #:eval ev + #:hidden + (require stxparse-info/parse + stxparse-info/current-pvars + racket/syntax + (for-syntax racket/base))] + + @examples[ + #:eval ev + #:escape UNSYNTAX + (eval:check + (let () + (code:comment "Alternate version of define/syntax-parse which") + (code:comment "contains (define-pvars x) in its expanded form.") + (define/syntax-parse x #'1) + (define/syntax-parse y #'2) + (define-syntax (get-pvars stx) + #`'#,(current-pvars)) + (get-pvars)) + '(y x))]])} + +@section{Extensions to @racketmodname[syntax/parse/experimental/template]} + +@defmodule[stxparse-info/parse/experimental/template] + +@(orig syntax/parse/experimental/template + define-template-metafunction) + +@defidform[define-template-metafunction]{ + Overloaded version of @orig:define-template-metafunction from + @racketmodname[syntax/parse/experimental/template]. + + Note that currently, template metafunctions defined via + @racketmodname[stxparse-info/parse/experimental/template] are not compatible + with the forms from @racketmodname[syntax/parse/experimental/template], and + vice versa. There is a pending Pull Request which would make the necessary + primitives from @racketmodname[syntax/parse/experimental/template] public, so + hopefully this problem will be solved in future versions.} + +@defform[(syntax-local-template-metafunction-introduce stx)]{ + Like @racket[syntax-local-introduce], but for + @tech[#:doc '(lib "syntax/scribblings/syntax.scrbl")]{template metafunctions}. + + This change is also available in the package + @racketmodname{backport-template-pr1514}. It has been submitted as a Pull + Request to Racket, but can already be used in + @racketmodname[stxparse-info/parse/experimental/template] right now.} + +@(ovl syntax/parse/experimental/template + template + quasitemplate + template/loc + quasitemplate/loc) + +Additionally, the following identifiers are overridden as they are part of the +duplicated implementation of @racketmodname[syntax/parse]. + +@(ovl #:wrapper nested-inset + syntax/parse/experimental/template + ?? + ?@)