From 72a7bbcefc668c17dcdaab4f81f2145936c808a4 Mon Sep 17 00:00:00 2001 From: Ryan Culpepper Date: Wed, 28 Feb 2018 14:37:41 +0100 Subject: [PATCH] update docs for syntax, template --- .../scribblings/reference/stx-patterns.scrbl | 192 ++++++++++++------ .../syntax/scribblings/parse/ex-kw-args.scrbl | 61 +++++- .../scribblings/parse/experimental.scrbl | 157 +------------- 3 files changed, 191 insertions(+), 219 deletions(-) diff --git a/pkgs/racket-doc/scribblings/reference/stx-patterns.scrbl b/pkgs/racket-doc/scribblings/reference/stx-patterns.scrbl index 005939f5f7..bef9d46626 100644 --- a/pkgs/racket-doc/scribblings/reference/stx-patterns.scrbl +++ b/pkgs/racket-doc/scribblings/reference/stx-patterns.scrbl @@ -1,5 +1,5 @@ #lang scribble/doc -@(require "mz.rkt") +@(require "mz.rkt" (for-label syntax/parse)) @(define lit-ellipsis (racket ...)) @@ -270,30 +270,37 @@ the individual @racket[stx-expr]. (math 3 1 4 1 5 9) ]} -@defform/subs[(syntax template) - ([template id - (template-elem ...) - (template-elem ...+ . template) - (code:line #,(tt "#")(template-elem ...)) - (code:line #,(tt "#&")template) - (code:line #,(tt "#s")(key-datum template-elem ...)) - (ellipsis stat-template) - const] - [template-elem (code:line template ellipsis ...)] - [stat-template id - (stat-template ...) - (stat-template ... . stat-template) - (code:line #,(tt "#")(stat-template ...)) - (code:line #,(tt "#&")stat-template) - (code:line #,(tt "#s")(key-datum stat-template ...)) - const] - [ellipsis #,lit-ellipsis])]{ +@defform[#:literals (?? ?@) (syntax template) + #:grammar + ([template id + (head-template ...) + (head-template ...+ . template) + (code:line #,(tt "#")(head-template ...)) + (code:line #,(tt "#&")template) + (code:line #,(tt "#s")(key-datum head-template ...)) + (?? template template) + (ellipsis stat-template) + const] + [head-template template + (code:line head-template ellipsis ...+) + (?@ . template) + (?? head-template head-template) + (?? head-template)] + [stat-template @#,elem{like @svar{template}, but without @|lit-ellipsis|, + @racket[??], and @racket[?@]}] + [ellipsis #,lit-ellipsis])]{ Constructs a syntax object based on a @racket[template], which can include @tech{pattern variables} bound by @racket[syntax-case] or @racket[with-syntax]. -Template forms produce a syntax object as follows: +A @svar[template] produces a single syntax object. A +@svar[head-template] produces a sequence of zero or more syntax +objects. A @svar[stat-template] is like a @svar[template], except that +@|lit-ellipsis|, @racket[??], and @racket[?@] are interpreted as +constants instead of template forms. + +A @svar[template] produces a syntax object as follows: @specsubform[id]{ @@ -314,54 +321,21 @@ Template forms produce a syntax object as follows: If @racket[id] is not bound as a pattern variable, then @racket[id] as a template produces @racket[(quote-syntax id)].} - @specsubform[(template-elem ...)]{ + @specsubform[(head-template ...)]{ Produces a syntax object whose datum is a list, and where the elements of the list correspond to syntax objects produced by the - @racket[template-elem]s. + @racket[head-template]s.} - A @racket[template-elem] is a sub-@racket[template] replicated by any - number of @racket[ellipsis]es: - - @itemize[ - - @item{If the sub-@racket[template] is replicated by no - @racket[ellipsis]es, then it generates a single syntax object to - incorporate into the result syntax object.} - - @item{If the sub-@racket[template] is replicated by one - @racket[ellipsis], then it generates a sequence of syntax objects - that is ``inlined'' into the resulting syntax object. - - The number of generated elements depends on the values of - @tech{pattern variables} referenced within the - sub-@racket[template]. There must be at least one @tech{pattern - variable} whose value has a @tech{depth marker} less than the - number of @racket[ellipsis]es after the pattern variable within the - sub-@racket[template]. - - If a @tech{pattern variable} is replicated by more - @racket[ellipsis]es in a @racket[template] than the @tech{depth - marker} of its binding, then the @tech{pattern variable}'s result - is determined normally for inner @racket[ellipsis]es (up to the - binding's @tech{depth marker}), and then the result is replicated - as necessary to satisfy outer @racket[ellipsis]es.} - - @item{For each @racket[ellipsis] after the first one, the preceding - element (with earlier replicating @racket[ellipsis]es) is - conceptually wrapped with parentheses for generating output, and - then the wrapping parentheses are removed in the resulting syntax - object.}]} - - @specsubform[(template-elem ... . template)]{ + @specsubform[(head-template ... . template)]{ Like the previous form, but the result is not necessarily a list; instead, the place of the empty list in the resulting syntax object's datum is taken by the syntax object produced by @racket[template].} - @specsubform[(code:line #,(tt "#")(template-elem ...))]{ + @specsubform[(code:line #,(tt "#")(head-template ...))]{ - Like the @racket[(template-elem ...)] form, but producing a syntax + Like the @racket[(head-template ...)] form, but producing a syntax object whose datum is a vector instead of a list.} @specsubform[(code:line #,(tt "#&")template)]{ @@ -369,18 +343,37 @@ Template forms produce a syntax object as follows: Produces a syntax object whose datum is a box holding the syntax object produced by @racket[template].} - @specsubform[(code:line #,(tt "#s")(key-datum template-elem ...))]{ + @specsubform[(code:line #,(tt "#s")(key-datum head-template ...))]{ - Like the @racket[(template-elem ...)] form, but producing a syntax + Like the @racket[(head-template ...)] form, but producing a syntax object whose datum is a @tech{prefab} structure instead of a list. The @racket[key-datum] must correspond to a valid first argument of @racket[make-prefab-struct].} + @specsubform[#:literals (??) (?? template1 template2)]{ + + Produces the result of @racket[template1] if @racket[template1] has no + pattern variables with ``missing values''; otherwise, produces the result of + @racket[template2]. + + A pattern variable bound by @racket[syntax-case] never has a missing value, but + pattern variables bound by @racket[syntax-parse] (for example, @racket[~or] or + @racket[~optional] patterns) can. + + @examples[#:eval (let ([ev (syntax-eval)]) (ev '(require syntax/parse/pre)) ev) + (syntax-parse #'(m 1 2 3) + [(_ (~optional (~seq #:op op:expr)) arg:expr ...) + #'((?? op +) arg ...)]) + (syntax-parse #'(m #:op max 1 2 3) + [(_ (~optional (~seq #:op op:expr)) arg:expr ...) + #'((?? op +) arg ...)]) + ]} + @specsubform[(ellipsis stat-template)]{ Produces the same result as @racket[stat-template], which is like a - @racket[template], but @racket[...] is treated like an @racket[id] - (with no pattern binding).} + @racket[template], but @racket[...], @racket[??], and @racket[?@] + are treated like an @racket[id] (with no pattern binding).} @specsubform[const]{ @@ -388,11 +381,74 @@ Template forms produce a syntax object as follows: preceding cases, and it produces the result @racket[(quote-syntax const)].} +A @racket[head-template] produces a sequence of syntax objects; that sequence is +``inlined'' into the result of the enclosing @racket[template]. The result of a +@racket[head-template] is defined as follows: + + @specsubform[template]{ + + Produces one syntax object, according to the rules for @svar[template] + above.} + + @specsubform[(code:line head-template ellipsis ...+)]{ + + Generates a sequence of syntax objects by ``@racket[map]ping'' the + @racket[head-template] over the values of its pattern variables. The number of + iterations depends on the values of the @tech{pattern variables} referenced + within the sub-template. + + To be more precise: Let @racket[_outer] be @racket[_inner] followed by one + ellipsis. A @tech{pattern variable} is an @deftech{iteration pattern variable} + for @racket[_outer] if occurs at a depth equal to its @tech{depth + marker}. There must be at least one; otherwise, an error is raised. If there + are multiple iteration variables, then all of their values must be lists of + the same length. The result for @racket[_outer] is produced by + @racket[map]ping the @racket[_inner] template over the @tech{iteration pattern + variable} values and decreasing their effective @tech{depth markers} by 1 + within @racket[_inner]. The @racket[_outer] result is formed by appending the + @racket[_inner] results. + + Consequently, if a @tech{pattern variable} occurs at a depth greater than its + @tech{depth marker}, it is used as an @tech{iteration pattern variable} for + the innermost ellipses but not the outermost. A @tech{pattern variable} must + not occur at a depth less than its @tech{depth marker}; otherwise, an error is + raised.} + + @specsubform[#:literals (?@) (?@ . template)]{ + + Produces the sequence of elements in the syntax list produced by + @racket[template]. If @racket[template] does not produce a proper syntax list, + an exception is raised. + + @examples[#:eval (syntax-eval) + (with-syntax ([(key ...) #'('a 'b 'c)] + [(val ...) #'(1 2 3)]) + #'(hash (?@ key val) ...)) + (with-syntax ([xs #'(2 3 4)]) + #'(list 1 (?@ . xs) 5)) + ]} + + @specsubform[#:literals (??) (?? head-template1 head-template2)]{ + + Produces the result of @racket[head-template1] if none of its pattern + variables have ``missing values''; otherwise produces the result of + @racket[head-template2]. } + + @specsubform[#:literals (??) (?? head-template)]{ + + Produces the result of @racket[head-template] if none of its pattern + variables have ``missing values''; otherwise produces nothing. + + Equivalent to @racket[(?? head-template (?@))]. } + A @racket[(#,(racketkeywordfont "syntax") template)] form is normally abbreviated as @racket[#'template]; see also @secref["parse-quote"]. If @racket[template] contains no pattern variables, then @racket[#'template] is equivalent to -@racket[(quote-syntax template)].} +@racket[(quote-syntax template)]. + +@history[#:changed "6.90.0.18" @elem{Added @racket[?@] and @racket[??].}] +} @defform[(quasisyntax template)]{ @@ -512,6 +568,16 @@ used as an expression. This binding is useful only in syntax patterns, where it indicates a pattern that matches any syntax object. See @racket[syntax-case].} +@deftogether[[ +@defidform[??] +@defidform[?@] +]]{ + +The @racket[??] and @racket[?@] transformer bindings prohibit these forms from +being used as an expression. The bindings are useful only in syntax templates. +See @racket[syntax]. + +@history[#:added "6.90.0.18"]} @defproc[(syntax-pattern-variable? [v any/c]) boolean?]{ diff --git a/pkgs/racket-doc/syntax/scribblings/parse/ex-kw-args.scrbl b/pkgs/racket-doc/syntax/scribblings/parse/ex-kw-args.scrbl index cc59c245b6..98e6b0d4d8 100644 --- a/pkgs/racket-doc/syntax/scribblings/parse/ex-kw-args.scrbl +++ b/pkgs/racket-doc/syntax/scribblings/parse/ex-kw-args.scrbl @@ -27,7 +27,8 @@ Here's one way to do it: @interaction[#:eval the-eval (define-syntax (mycond stx) (syntax-parse stx - [(mycond (~or* (~seq #:error-on-fallthrough who:expr) (~seq)) + [(mycond (~or* (~seq #:error-on-fallthrough who:expr) + (~seq)) clause ...) (with-syntax ([error? (if (attribute who) #'#t #'#f)] [who (or (attribute who) #'#f)]) @@ -43,11 +44,11 @@ Here's one way to do it: (void)])) ] -We cannot write @racket[#'who] in the macro's right-hand side, because -the @racket[who] attribute does not receive a value if the keyword -argument is omitted. Instead we must write @racket[(attribute who)], -which produces @racket[#f] if matching did not assign a value to the -attribute. +We cannot simply write @racket[#'who] in the macro's right-hand side, +because the @racket[who] attribute does not receive a value if the +keyword argument is omitted. Instead we must first check the attribute +using @racket[(attribute who)], which produces @racket[#f] if matching +did not assign a value to the attribute. @interaction[#:eval the-eval (mycond [(even? 13) 'blue] @@ -62,8 +63,48 @@ There's a simpler way of writing the @racket[~or*] pattern above: (~optional (~seq #:error-on-fallthrough who:expr)) ] + +@section{Optional Arguments with @racket[??]} + +The @racket[??] template form provides a compact alternative to +explicitly testing attribute values. Here's one way to do it: + +@interaction[#:eval the-eval +(define-syntax (mycond stx) + (syntax-parse stx + [(mycond (~optional (~seq #:error-on-fallthrough who:expr)) + clause ...) + #'(mycond* (?? (?@ #t who) (?@ #f #f)) clause ...)])) +] + +If @racket[who] matched, then the @racket[??] subtemplate splices in +the two terms @racket[#t who] into the enclosing template (@racket[?@] +is the template splicing form). Otherwise, it splices in @racket[#f #f]. + +Here's an alternative definition that re-uses Racket's @racket[cond] macro: + +@interaction[#:eval the-eval +(define-syntax (mycond stx) + (syntax-parse stx + [(mycond (~optional (~seq #:error-on-fallthrough who:expr)) + clause ...) + #'(cond clause ... (?? [else (error 'who "no clause matched")] (?@)))])) +] + +In this version, we optionally insert an @racket[else] clause at the +end to signal the error; otherwise we use @racket[cond]'s fall-through +behavior (that is, returning @racket[(void)]). + +If the second subtemplate of a @racket[??] template is +@racket[(?@)]---that is, it produces no terms at all---the second +subtemplate can be omitted. + + +@section{Optional Arguments with @racket[define-splicing-syntax-class]} + Yet another way is to introduce a @tech{splicing syntax class}, which is like an ordinary syntax class but for head patterns. + @interaction[#:eval the-eval (define-syntax (mycond stx) @@ -82,9 +123,9 @@ is like an ordinary syntax class but for head patterns. Defining a splicing syntax class also makes it easy to eliminate the case analysis we did before using @racket[attribute] by defining @racket[error?] and @racket[who] as attributes within both of the -syntax class's variants. (This is possible to do in the inline pattern -version too, using @racket[~and] and @racket[~parse], just less -convenient.) Splicing syntax classes also closely parallel the style -of grammars in macro documentation. +syntax class's variants. This is possible to do in the inline pattern +version too, using @racket[~and] and @racket[~parse], but it is less +convenient. Splicing syntax classes also closely parallel the style of +grammars in macro documentation. @(close-eval the-eval) diff --git a/pkgs/racket-doc/syntax/scribblings/parse/experimental.scrbl b/pkgs/racket-doc/syntax/scribblings/parse/experimental.scrbl index 9d21c54b69..40fd11ac9a 100644 --- a/pkgs/racket-doc/syntax/scribblings/parse/experimental.scrbl +++ b/pkgs/racket-doc/syntax/scribblings/parse/experimental.scrbl @@ -277,136 +277,20 @@ patterns as @racket[target-stxclass-id] but with the given @defmodule[syntax/parse/experimental/template] -@(define literal-ellipsis (racket ...)) - -@defform[#:literals (?? ?@) - (template tmpl) - #:grammar - ([tmpl pattern-variable-id - (head-tmpl . tmpl) - (head-tmpl ellipsis ...+ . tmpl) - (metafunction-id . tmpl) - (?? tmpl tmpl) - #(@#,svar[head-tmpl] ...) - #s(prefab-struct-key @#,svar[head-tmpl] ...) - #&@#,svar[tmpl] - constant-term] - [head-templ tmpl - (?? head-tmpl) - (?? head-tmpl head-tmpl) - (?@ . tmpl)] - [ellipsis @#,literal-ellipsis])]{ - -Constructs a syntax object from a syntax template, like -@racket[syntax], but provides additional templating forms for dealing -with optional terms and splicing sequences of terms. Only the -additional forms are described here; see @racket[syntax] for -descriptions of pattern variables, etc. - -As in @racket[syntax], a template can be ``escaped'' with ellipses, -like @racket[(... _escaped-tmpl)]. Within the escaped template, -ellipses (@racket[...]), the @racket[??] and @racket[?@] forms, and -metafunctions are treated as constants rather than interpreted as -template forms. - -@specsubform[#:literals (??) - (?? tmpl alt-tmpl)]{ - -Produces @racket[tmpl] unless any attribute used in @racket[tmpl] has -an absent value; in that case, @racket[alt-tmpl] is used instead. - -@examples[#:eval the-eval -(syntax-parse #'(m 1 2 3) - [(_ (~optional (~seq #:op op:expr)) arg:expr ...) - (template ((?? op +) arg ...))]) -(syntax-parse #'(m #:op max 1 2 3) - [(_ (~optional (~seq #:op op:expr)) arg:expr ...) - (template ((?? op +) arg ...))]) -] - -If @racket[??] is used as a head-template, then its sub-templates may -also be head-templates. - -@examples[#:eval the-eval -(syntax-parse #'(m 1) - [(_ x:expr (~optional y:expr)) - (template (m2 x (?? (?@ #:y y) (?@ #:z 0))))]) -(syntax-parse #'(m 1 2) - [(_ x:expr (~optional y:expr)) - (template (m2 x (?? (?@ #:y y) (?@ #:z 0))))]) -] -} - -@specsubform[#:literals (??) - (?? head-tmpl)]{ - -Produces @racket[head-tmpl] unless any attribute used in -@racket[head-tmpl] has an absent value; in that case, the term is -omitted. Can only occur in head position in a template. - -Equivalent to @racket[(?? head-tmpl (?@))]. - -@examples[#:eval the-eval -(syntax-parse #'(m 1) - [(_ x:expr (~optional y:expr)) - (template (m2 x (?? y)))]) -(syntax-parse #'(m 1 2) - [(_ x:expr (~optional y:expr)) - (template (m2 x (?? y)))]) -(syntax-parse #'(m 1 2) - [(_ x:expr (~optional y:expr)) - (template (m2 x (?? (?@ #:y y))))]) -] -} - -@specsubform[#:literals (?@) - (?@ . tmpl)]{ - -Similar to @racket[unquote-splicing], splices the result of -@racket[tmpl] (which must produce a syntax list) into the surrounding -template. Can only occur in head position in a template. - -@examples[#:eval the-eval -(syntax-parse #'(m #:a 1 #:b 2 3 4 #:e 5) - [(_ (~alt pos:expr (~seq kw:keyword kwarg:expr)) ...) - (template (m2 (?@ kw kwarg) ... pos ...))]) -] - -The @racket[tmpl] must produce a proper syntax list, but it does not -need to be expressed as a proper list. For example, to unpack pattern -variables that contain syntax lists, use a ``dotted'' template: - -@examples[#:eval the-eval -(with-syntax ([x #'(a b c)]) - (template ((?@ . x) d))) -(with-syntax ([(x ...) #'((1 2 3) (4 5))]) - (template ((?@ . x) ...))) -] -} - -@specsubform[(metafunction-id . tmpl)]{ - -Applies the template metafunction named @racket[metafunction-id] to -the result of the template (including @racket[metafunction-id] -itself). See @racket[define-template-metafunction] for examples. -} - -The @racket[??] and @racket[?@] forms and metafunction applications -are disabled in an ``escaped template'' (see @racket[_stat-template] -under @racket[syntax]). - -@examples[#:eval the-eval -(template (... ((?@ a b c) d))) -] -} - @deftogether[[ -@defidform[??] -@defidform[?@] +@defform[(template tmpl)] +@defform[(template/loc loc-expr tmpl)] +@defform[(quasitemplate tmpl)] +@defform[(quasitemplate/loc loc-expr tmpl)] ]]{ -Auxiliary forms used by @racket[template]. They may not be used as -expressions. +Equivalent to @racket[syntax], @racket[syntax/loc], +@racket[quasisyntax], and @racket[quasisyntax/loc], respectively. +} + +@defform[(datum-template tmpl)]{ + +Equivalent to @racket[datum]. } @defform*[[(define-template-metafunction metafunction-id expr) @@ -453,23 +337,4 @@ the context above; instead, @racket[let-values] would report an invalid binding list. } -@deftogether[[ -@defform[(template/loc loc-expr tmpl)] -@defform[(quasitemplate tmpl)] -@defform[(quasitemplate/loc loc-expr tmpl)] -]]{ - -Like @racket[syntax/loc], @racket[quasisyntax], and -@racket[quasisyntax/loc], respectively, but with the additional -features of @racket[template]. -} - -@defform[(datum-template tmpl)]{ - -Like @racket[datum] but with some of the additional features of -@racket[template]: @racket[?@] and @racket[??] are supported (although -@racket[??] is useless, since @racket[datum-case] cannot bind -``absent'' variables), but template metafunctions are not allowed. -} - @(close-eval the-eval)