commit 68f5d389021aa4858a0b02c17a7a788dfd886903
parent 3b99a0ce021369cf46b6107e2d6ff180a4f2cea4
Author: Georges Dupéron <georges.duperon@gmail.com>
Date: Fri, 3 Feb 2017 15:45:49 +0100
Documentation, changed (begin x ...) to produce a splicing list, not a list (and documented accordingly)
Diffstat:
9 files changed, 452 insertions(+), 80 deletions(-)
diff --git a/info.rkt b/info.rkt
@@ -9,7 +9,8 @@
"alexis-util"
"scope-operations"))
(define build-deps '("scribble-lib"
- "racket-doc"))
+ "racket-doc"
+ "scribble-math"))
(define scribblings '(("scribblings/subtemplate.scrbl" () (parsing-library))))
(define pkg-desc "Various enhancements on syntax templates")
(define version "1.0")
diff --git a/light.rkt b/light.rkt
@@ -0,0 +1,4 @@
+#lang racket
+(require subtemplate/private/template-subscripts)
+(provide subtemplate
+ quasisubtemplate)
+\ No newline at end of file
diff --git a/main.rkt b/main.rkt
@@ -4,23 +4,30 @@
stxparse-info/parse
subtemplate/private/unsyntax-preparse
subtemplate/private/top-subscripts
+ (except-in stxparse-info/parse/experimental/template
+ template
+ quasitemplate
+ template/loc
+ quasitemplate/loc
+ ?@
+ ??)
(only-in racket/base [... …])
(only-in stxparse-info/parse [...+ …+]))
(provide
;; ddd-forms
- ?@ ?@@ ?? ?attr ?cond ?if begin define let #%app #%intef-begin
+ ?@ ?@@ ?? ?attr ?cond ?if begin let #%app #%intdef-begin
;; stxparse-info/case
(all-from-out stxparse-info/case)
;; stxparse-info/parse
(all-from-out stxparse-info/parse)
+ ;; stxparse-info/parse/experimental/template
+ (all-from-out stxparse-info/parse/experimental/template)
;; subtemplate/private/unsyntax-preparse
(rename-out
[template-ddd template]
[subtemplate-ddd subtemplate]
[quasitemplate-ddd quasitemplate]
- [quasisubtemplate-ddd quasisubtemplate]
- [subtemplate-ddd syntax]
- [quasisubtemplate-ddd quasisyntax])
+ [quasisubtemplate-ddd quasisubtemplate])
;; subtemplate/private/top-subscripts
;; => #%top
(all-from-out subtemplate/private/top-subscripts)
diff --git a/override.rkt b/override.rkt
@@ -0,0 +1,5 @@
+#lang racket/base
+(require subtemplate)
+(provide (all-from-out subtemplate)
+ (rename-out [subtemplate syntax]
+ [quasisubtemplate quasisyntax]))
diff --git a/private/ddd-forms.rkt b/private/ddd-forms.rkt
@@ -1,8 +1,7 @@
#lang racket/base
(provide begin
- define
let
- (rename-out [begin #%intef-begin])
+ #%intdef-begin
(rename-out [app #%app])
??
?if
@@ -91,15 +90,17 @@
#,(nest* (ddd %) e ooo*)))
(pattern {~seq e :ooo+}
;#:with expanded #`(apply values #,(ddd* e ooo*))
- #:with expanded (ddd* e ooo*))
+ #:with expanded #`(splicing-list #,(ddd* e ooo*)))
(pattern other
#:with expanded #'other)))
(define-syntax/parse (begin stmt:stmt …)
(template (-begin (?@ stmt.expanded) …)))
+(define-syntax #%intdef-begin (make-rename-transformer #'begin))
+
(define-syntax/parse (let {~optional name:id} ([var . val] …) . body)
- (template (-let (?? name) ([var (begin . val)] …) (begin . body))))
+ (template (-let (?? name) ([var (begin . val)] …) (#%intdef-begin . body))))
(begin-for-syntax
(define-splicing-syntax-class arg
diff --git a/scribblings/light.scrbl b/scribblings/light.scrbl
@@ -0,0 +1,83 @@
+#lang scribble/manual
+@require[scriblib/footnote
+ @for-label[subtemplate/light
+ syntax/parse/experimental/template
+ racket/base]]
+
+@(begin
+ (module m racket/base
+ (require scribble/manual
+ (for-template subtemplate)
+ (for-syntax racket/base
+ racket/syntax))
+ (define-syntax (mk stx)
+ (syntax-case stx ()
+ [(_ id)
+ (with-syntax ([full: (format-id #'id "full:~a" #'id)])
+ #'(begin
+ (define full: @racket[id])
+ (provide full:)))]))
+ (define-syntax-rule (mk* id ...) (begin (mk id) ...))
+ (mk* subtemplate ?@@ ?attr ?cond ?if))
+ (require 'm))
+
+@title{Lightweight Subtemplate}
+
+@defmodule[subtemplate/light]{
+
+ This module only provides stripped-down versions of @racket[subtemplate] and
+ @racket[quasisubtemplate], without overriding @racket[syntax] and
+ @racket[quasisyntax]. Note that some features will not work when using these
+ versions. Prefer using @racket[(require subtemplate)] instead.
+
+ Another limitation is that subscripted identifiers are not searched for
+ within unquoted parts of the template.
+
+ Note that you need to require @racketmodname[stxparse-info/parse] and
+ @racketmodname[stxparse-info/case], otherwise @racket[subtemplate] and
+ @racket[quasisubtemplate] will not be able to detect which pattern variables
+ are bound (and therefore will be unable to know from which @racket[xᵢ] an
+ @racket[yᵢ] should be derived.}
+
+@defform*[{(subtemplate template)
+ (subtemplate template #:properties (prop ...))}
+ #:contracts
+ ([prop identifier?])]{
+
+ Like @full:subtemplate from @racketmodname[subtemplate], but with a few
+ features missing (@full:?@@ @full:?attr @full:?cond @full:?if).}
+
+@defform*[{(subtemplate template)
+ (subtemplate template #:properties (prop ...))}
+ #:contracts
+ ([prop identifier?])]{
+
+ Like @full:subtemplate from @racketmodname[subtemplate], but with a few
+ features missing. The utilities @full:?@@ @full:?attr @full:?cond @full:?if
+ are not taken into account, and @racket[unsyntax] completely escapes the
+ ellipses.
+
+ Note that the syntax pattern variables must be matched with one of the
+ patched forms from @racketmodname[stxparse-info/parse] or
+ @racketmodname[stxparse-info/case], instead of the syntax pattern-matching
+ forms from @racketmodname[syntax/parse] or @racketmodname[racket/base],
+ respectively.}
+
+@defform*[{(quasisubtemplate template)
+ (quasisubtemplate template #:properties (prop ...))}
+ #:contracts
+ ([prop identifier?])]{
+
+ Like @full:subtemplate from @racketmodname[subtemplate], but with a few
+ features missing. The utilities @full:?@@ @full:?attr @full:?cond @full:?if
+ are not taken into account, and @racket[unsyntax] completely escapes the
+ ellipses
+
+ Another limitation is that subscripted identifiers are not searched for
+ within unquoted parts of the template.
+
+ Note that the syntax pattern variables must be matched with one of the
+ patched forms from @racketmodname[stxparse-info/parse] or
+ @racketmodname[stxparse-info/case], instead of the syntax pattern-matching
+ forms from @racketmodname[syntax/parse] or @racketmodname[racket/base],
+ respectively. }
diff --git a/scribblings/subtemplate.scrbl b/scribblings/subtemplate.scrbl
@@ -1,13 +1,170 @@
#lang scribble/manual
-@require[scriblib/footnote
- @for-label[subtemplate/private/template-subscripts
- syntax/parse/experimental/template
- racket/base]]
+@require[racket/require
+ scriblib/footnote
+ scribble-math
+ @for-label[subtemplate
+ (only-in syntax/parse/experimental/template)
+ (subtract-in racket/base subtemplate)]]
-@title{Subtemplate}
+@(begin
+ (module m racket/base
+ (require scribble/manual
+ (for-template syntax/parse
+ syntax/parse/experimental/template
+ racket/syntax)
+ (for-syntax racket/base
+ racket/syntax))
+ (define-syntax (mk stx)
+ (syntax-case stx ()
+ [(_ id)
+ (with-syntax ([orig: (format-id #'id "orig:~a" #'id)])
+ #'(begin
+ (define orig: @racket[id])
+ (provide orig:)))]))
+ (define-syntax-rule (mk* id ...) (begin (mk id) ...))
+
+ (mk* syntax-parse syntax-case with-syntax template quasitemplate syntax
+ unsyntax quasisyntax ?? ?@ template/loc quasitemplate/loc #%app
+ #%top begin let))
+ (require 'm))
+
+@title[#:style (with-html5 manual-doc-style)]{Subtemplate}
@author[@author+email["Georges Dupéron" "georges.duperon@gmail.com"]]
-@defmodule[subtemplate/private/template-subscripts]
+This library should be considered experimental. Although most of the syntax
+should work in the same way in future versions, the behaviour of some corner
+cases may change, as I try to find the best semantics.
+
+Also, this library requires some patched versions of @racket[syntax-parse] and
+@orig:syntax-case, as these do not offer the hooks needed to implement
+@racket[subtemplate]. Unfortunately, as the official implementations of
+@racket[syntax-parse] and @racket[syntax-case] evolve, compatibility issues
+may arise.
+
+If you desire to use this library, please drop me an e-mail (my address is
+below the title), so that I can keep you informed of upcoming changes, see if
+these are likely to cause problems in your code.
+
+Finally, If the maintenance burden is too high, I might drop the compatibility
+with @racketmodname[syntax/parse] and @|orig:syntax-case|.
+
+@section{The main @racketmodname[subtemplate] module}
+
+@defmodule[subtemplate]{
+ The @racketmodname[subtemplate] module provides @racket[subtemplate], an
+ alternative to @racketmodname[syntax/parse]'s @orig:template, which
+ supports several convenience features:
+
+ @itemlist[
+ @item{When an identifier @racket[yᵢ] is encountered in a template, it is
+ automatically defined as a pattern variable containing temporary identifiers.
+
+ This avoids the need to manually call @racket[generate-temporaries].
+
+ The generated temporary identifiers will be based on a @racket[xᵢ] pattern
+ variable with the same subscript as @racket[yᵢ], and @racket[yᵢ] will be
+ nested at the same ellipsis depth as @racket[xᵢ].
+
+ The identifiers @racket[xᵢ] and @racket[yᵢ] must end wit the same subscript,
+ which must be a sequence of unicode subscript characters picked among
+ @tt{ₐ ₑ ₕ ᵢ ⱼ ₖ ₗ ₘ ₙ ₒ ₚ ᵣ ₛ ₜ ᵤ ᵥ ₓ ᵦ ᵧ ᵨ ᵩ ᵪ}. Alternatively, the
+ subscript may be specified with an underscore followed by any characters
+ other than an underscore. The two notations are equivalent, in the sense that
+ @racket[yᵦ] and @racket[y_β] are interpreted in the same way, and if both
+ appear within a template, they will use the same sequence of temporary
+ identifiers generated from an @racket[xᵦ] or @racket[x_β].}
+ @item{The value of pattern variables is automatically extracted when the
+ variable does not appear in a syntax template. Note that since the syntax
+ object is transformed into a plain datum, source locations and lexical
+ contexts are lost. This is a trade-off between better error messages, which
+ make sure that source locations and lexical context are not lost by accident
+ (no automatic @racket[syntax-e]), and more concise code (with automatic
+ @racket[syntax-e]). It is possible that a future version may require explicit
+ syntax-e, possibly via a concise shorthand like @racket[unquote] (@tt{,}) or
+ @racket[unsyntax] (@tt{#,}), if this feature turns out to be too dangerous in
+ practice.}
+ @item{Ellipses work outside of syntax templates, and can be used after
+ definitions and expressions.
+ @itemlist[
+ @item{The result of an expression under @${n} ellipses is a
+ @${\text{nested}^n} list, where the expression is evaluated for
+ each value of the pattern variables located within. In other words,
+ @racket[(x ...)] should produce a value similar to
+ @racket[(syntax->datum #'(x ...))]. However, it is possible to actually
+ manipulate the value, e.g. by writing @racket[(+ x 1) ...]. It is possible
+ to write @${m} ellipses in a row (which has the effect of flattening
+ @${m - 1} levels in the result list). It is also possible to nest the use
+ of these ellipses, e.g. with @racket[(x ...) ...], which keeps the
+ structure of the nested lists in the result.}
+ @item{When a definition form (@racket[define] or @racket[define/with-syntax]
+ for now) is followed by @${n} ellipses, then the defined identifier is a
+ @${\text{nested}^n} list, or a syntax pattern variable with an ellipsis
+ depth of @${n}. The expression is evaluated for each value of the template
+ variables it contains. Note that the structure of the nested lists is not
+ flattened, despite the fact that the ellipses are written one after
+ another. This is because it is usually the desired outcome, and nesting
+ parentheses around the definition form would produce rather unreadable
+ code.}
+ @item{These ellipses can also be used ``inline'' within function calls
+ (@racketmodname[subtemplate] overrides @racket[#%app] to achieve this). For
+ example: @racket[(/ (+ x ...) (length x))] would compute the average of
+ @racket[(syntax->datum #'(x ...))]}
+ @item{Subscripted identifiers should also work in expressions
+ (@racketmodname[subtemplate] overrides @racket[#%top] to achieve this),
+ although this seems less useful, as the temporary identifiers loose their
+ lexical context information in that way.}
+ @item{The splicing forms @racket[?@] and @racket[?@@], as well as
+ @racket[??], @racket[?if] and @racket[?cond] work within expressions, and
+ can be used to splice values into the argument list when calling a
+ function. For example, @racket[(+ 1 (?@ l) 3)] would splice the values of
+ the list @racket[l] into the argument list, effectively calling
+ @racket[(+ 1 97 98 99 3)] if @racket[l] was equal to
+ @racket[#'(97 98 99)]. Additionnally, it is possible to append a list
+ at the end of the argument list, with the syntax @racket[(+ 1 2 . l)].}
+ @item{It is possible to create a syntax object based on one of the iterated
+ pattern variables within the expression-ellipses, for example using
+ @racket[(#'x ...)].}]}
+ @item{Within a @racket[subtemplate] and a @racket[quasisubtemplate], it is
+ possible to use @racket[unsyntax] and @racket[unsyntax-splicing] to escape
+ from the template. Within the escaped expression, the ellipsis depth of the
+ template is conserved, making it possible to write
+ @RACKET[(subtemplate (#,(+ x 1) ...))], for example.
+
+ The usual behaviour, which resets the ellipsis count to 0, can be obtained
+ with @RACKET[#,,expr] (that is, @RACKET[(unsyntax (unquote expr))]) for an
+ @racket[unsyntax]-like escape. An @racket[unsyntax-splicing]-style escape
+ can be obtained with @RACKET[#,,@expr] or @RACKET[#,@,expr] (that is,
+ @RACKET[(unsyntax (unquote-splicing expr))] or
+ @RACKET[(unsyntax-splicing (unquote expr))]).}
+ @item{Several utilities in the spirit of @racket[??] and @racket[?@] are
+ provided, namely @racket[?@@], @racket[?attr] @racket[?cond] and
+ @racket[?if].}
+ @item{All features (subscripted identifiers, dotted expressions and
+ definitions, and the ellipsis-preserving @racket[unsyntax]) should work well
+ with omitted elements in attributes, as created by
+ @racket[~optional] or @racket[~or] in @racket[syntax-parse].}]}
+
+@subsection{Modules re-provided by @racketmodname[subtemplate]}
+
+The @racketmodname[subtemplate] library needs some cooperation from
+@racket[syntax-case], @racket[syntax-parse] and similar forms. For this
+reason, some patched versions are defined in the @racketmodname[stxparse-info]
+library. @racket[subtemplate] cannot work properly if the right modules are
+loaded. To make it easier to use @racketmodname[subtemplate], it re-provides
+the modules that need to be loaded for it to function properly.
+
+The @racketmodname[subtemplate] module re-provides
+@racketmodname[stxparse-info/parse], @racketmodname[stxparse-info/case] and
+the parts of @racketmodname[racket/syntax] which are not overridden by
+@racketmodname[stxparse-info/case].
+
+The @racketmodname[subtemplate/private/override] module also re-provides
+@racketmodname[stxparse-info/parse/experimental/template], but without
+@orig:template, @orig:quasitemplate, @orig:?? and @orig:?@, which are remapped
+to their equivalents from this library, and without @orig:template/loc] and
+@orig:quasitemplate/loc, which do not have an equivalent yet.
+
+@subsection{New and overridden bindings provided by @racketmodname[subtemplate]}
@defform*[{(subtemplate template)
(subtemplate template #:properties (prop ...))}
@@ -15,12 +172,7 @@
([prop identifier?])]{
Like @racket[template], but automatically derives identifiers for any
@racket[yᵢ …] which is not bound as a syntax pattern variable, based on a
- corresponding @racket[xᵢ …] which is bound as a syntax pattern variable.
-
- Note that the syntax pattern variables must be matched with one of the
- patched forms from @racket[stxparse-info/parse] or
- @racket[stxparse-info/case], instead of the syntax pattern-matching forms from
- @racket[syntax/parse] or @racket[racket/base], respectively.}
+ corresponding @racket[xᵢ …] which is bound as a syntax pattern variable.}
@defform*[{(quasisubtemplate template)
(quasisubtemplate template #:properties (prop ...))}
@@ -29,31 +181,134 @@
Like @racket[quasitemplate], but automatically derives identifiers for any
@racket[yᵢ …] which is not bound as a syntax pattern variable, based on a
corresponding @racket[xᵢ …] which is bound as a syntax pattern variable, in
- the same way as @racket[subtemplate].
+ the same way as @racket[subtemplate].}
- Note that the syntax pattern variables must be matched with one of the
- patched forms from @racket[stxparse-info/parse] or
- @racket[stxparse-info/case], instead of the syntax pattern-matching forms from
- @racket[syntax/parse] or @racket[racket/base], respectively.
-}
+@defform*[{(template template)
+ (template template #:properties (prop ...))}
+ #:contracts
+ ([prop identifier?])]{
+
+ Like @racket[subtemplate], but does not automatically generate pattern
+ variables based on their subscript. The other features still work
+ (ellipsis-preserving escapes with @racket[unsyntax], support for @racket[?@@],
+ @racket[?attr], @racket[?cond] and @racket[?if]).}
-@section{Overriding the default @racket[#'…] and @racket[#`…]}
+@defform*[{(quasitemplate template)
+ (quasitemplate template #:properties (prop ...))}
+ #:contracts
+ ([prop identifier?])]{
+
+ Like @racket[quasisubtemplate], but does not automatically generate pattern
+ variables based on their subscript. The other features still work
+ (ellipsis-preserving escapes with @racket[unsyntax], support for @racket[?@@],
+ @racket[?attr], @racket[?cond] and @racket[?if]).}
-@defmodule[subtemplate/private/override]
+@defform[#:kind "procedure"
+ (?@ . expr)]{Splices the @racket[expr] into the surrounding form
+ (which must be a function application). If the surrounding form is a
+ @racket[begin], @racket[let], or @racket[#%intdef-begin], then the the
+ splicing lists are not processed, but may be processed later by using the
+ splicing-list value as an argument to a function.
-The @racketmodname[subtemplate/private/override] module re-provides
-@racket[subtemplate] as @racket[syntax], and @racket[quasisubtemplate] as
-@racket[quasisyntax]. This allows @racketmodname[subtemplate] to be used via
-the reader shorthands @racket[#'…] and @racket[#`…].
+ Also works in @racket[template], @racket[subtemplate] and their derivatives.}
+@defform[#:kind "procedure"
+ (?@@ . expr)]{Appends all the lists contained within @racket[expr],
+ and splices the resulting list into the surrounding form. If the
+ surrounding form is a @racket[begin], @racket[let], or
+ @racket[#%intdef-begin], then the splicing lists are not processed, but may be
+ processed later by using the splicing-list value as an argument to a
+ function.
-The @racketmodname[subtemplate/private/override] module also re-provides
-@racketmodname[stxparse-info/parse] and @racketmodname[stxparse-info/case].
+ Also works in @racket[template], @racket[subtemplate] and their derivatives.}
+@defform*[[(?? alt)
+ (?? alt else)]]{
+ Executes @racket[alt], if none of the template variables within is omitted
+ (i.e. bound to @racket[#false] for the current ellipsis iteration). Otherwise,
+ executes @racket[else]. If @racket[else] is omitted, it defaults to
+ @racket[(?@)], i.e. the empty splice.
-The @racketmodname[subtemplate/private/override] module also re-provides
-@racketmodname[stxparse-info/parse/experimental/template], but without
-@racket[template] and @racket[quasitemplate], which are remapped to their
-@racket[sub] equivalents, and without @racket[template/loc] and
-@racket[quasitemplate/loc], which do not have a @racket[sub] equivalent yet.
+ Also works in @racket[template], @racket[subtemplate] and their derivatives.}
+
+@defform*[[(?if condition alt)
+ (?if condition alt else)]]{
+ Generalisation of @racket[??]. If none of the template variables within
+ @racket[condition] is omitted (i.e. bound to @racket[#false] for the current
+ ellipsis iteration), then @racket[alt] is executed. Otherwise, @racket[else]
+ is executed.
+
+ Also works in @racket[template], @racket[subtemplate] and their derivatives.}
+
+@defform[(?attr condition)]{Shorthand for @racket[?if condition #t #f]
+
+ Also works in @racket[template], @racket[subtemplate] and their derivatives.}
+
+@defform*[#:literals (else)
+ [(?cond [condition alt] …)
+ (?cond [condition alt] … [else alt])]]{
+ Equivalent to nested uses of @racket[?if]. If no @racket[else] clause is
+ supplied, then @racket[(?@)], i.e. the empty splice, is used instead.}
+
+@defform[(begin body ...)]{
+ Overridden version of @|orig:begin|. Supports ellipses after definitions
+ (using @racket[define] and @racket[define-syntax]). Supports ellipses after
+ expressions, in which case the results are grouped into a splicing list, which
+ makes it possible to write @racket[(+ (begin x ...))] and obtain the same
+ result as with @racket[(+ x ...)].}
+
+@defform*[[(let ([var val] …) . body)
+ (let name ([var val] …) . body)]]{
+ Overridden version of @|orig:let|. Supports ellipses in the @racket[body]
+ after definitions (using @racket[define] and @racket[define-syntax]). Supports
+ ellipses after expressions both in the @racket[body] and in the @racket[val].
+ In both cases, the results are grouped into a splicing list, which makes it
+ possible to write @racket[(let ([vs x ...]) (+ vs))] and obtain the same
+ result as with @racket[(+ x ...)].}
+
+@defform[(#%intdef-begin . body)]{
+ Equivalent to @racket[begin] from @racketmodname[subtemplate], but assumes
+ that it appears directly within the body of a @racket[let] or similar form.
+
+ Third-party macros can cooperate with @racketmodname[subtemplate], allowing
+ its features to be used where a sequence of statements is expected. To achieve
+ that, the macro would need to detect with @racket[identifier-binding] and
+ @racket[syntax-local-introduce] whether @racket[#%intdef-begin] is bound at the
+ macro's use-site. If this is the case, then the macro could use
+ @racket[#%intdef-begin] instead of @racket[begin].}
+
+@defform*[[(#%app f arg ... . rest)
+ (#%app val ooo ...+ expression ...+ . rest)]]{
+ Overridden version of @|orig:#%app|, which supports ellipses in the argument
+ list. When one of the arguments contains a splicing list, the list's values
+ are spliced into the argument list.
+
+ If the first argument is an ellipsis, the @racket[list] function is
+ implicitly used, and the first element following @racket[#%app] is interpreted
+ as an argument under ellipses.
+
+ A variable appearing in tail position after a dot is appended to the argument
+ list, and splicing-lists within are handled.}
+
+@defform[(#%top . var)]{Overridden version of @|orig:#%top|, which is used to
+ automatically derive temporary identifiers in expressions. When an unbound
+ variable @racket[yᵢ] is used and a matching pattern variable @racket[xᵢ] with
+ the same subscript is bound. Note that if a variable @racket[yᵢ] is already
+ bound to some value, no attempt will be made to derive temporary identifiers
+ for that variable. In contrast, if the identifier @racket[yᵢ] appears, quoted
+ by a @racket[subtemplate], then subtemplate will attempt to derive it even if
+ it is bound (unless it is bound as a pattern variable).}
+
+@defidform[…]{Alias for @racket[...]}
+@defidform[…+]{Alias for @racket[...+]}
+
+
+@section{Overriding the default @racket[#'…] and @racket[#`…]}
+
+@defmodule[subtemplate/override]{
+ The @racketmodname[subtemplate/override] module provides the same bindings as
+ @racketmodname[subtemplate], but also re-provides @racket[subtemplate] as
+ @racket[syntax], and @racket[quasisubtemplate] as @racket[quasisyntax]. This
+ allows @racketmodname[subtemplate] to be used via the reader shorthands
+ @racket[#'…] and @racket[#`…].}
@section{Limitations}
@@ -61,8 +316,7 @@ The derived subscripted identifiers have to be syntactically present within
the template. In particular, if a template metafunction generates a part of a
template containing @racket[yᵢ], it will work only if @racket[yᵢ] is also
present in the "main" part of the template (possibly as an argument to the
-template metafunction, or elsewhere). Subscripted identifiers are not searched
-for within unquoted parts of the template.
+template metafunction, or elsewhere).
Currently, template metafunctions defined with
@racketmodname[stxparse-info/parse/experimental/template] are not compatible
@@ -77,12 +331,18 @@ from the fact that @racket[subtemplate] cannot derive @racket[yᵢ] from
@racketmodname[syntax/parse]), please report them to
@url{https://github.com/jsmaniac/subtemplate/issues}.
-The code generated by @racket[subtemplate] is not very optimised, so
-compile-time and run-time performance will not be as good as with
-@racket[syntax] or @racket[template].
+The code generated by @racket[subtemplate] is not optimised, so compile-time
+and run-time performance will not be as good as with @racket[syntax] or
+@racket[template].
-Despite the rather extensive test suite, there are probably a few bugs lurking,
-please report them to @url{https://github.com/jsmaniac/subtemplate/issues}.
+The expression splicing-lists are not recognised by templates, and it is not
+possible for a template to produce a ``splicing syntax object'' (instead, an
+error is raised if a @racket[?@] causes a template to return more than one
+syntax object).
+
+Despite the rather extensive test suite, there are likely still some bugs
+lurking, please report them to
+@url{https://github.com/jsmaniac/subtemplate/issues}.
@subsection{Omitted elements in attributes (via @racket[~optional])}
@@ -105,4 +365,6 @@ If new pattern variables with the same subscript are introduced after a
generated variable was used, they should have the same structure (i.e. missing
sublists in the same positions). Otherwise, the derived variable generated by
@racket[subtemplate] would not contain the same elements before and after that
-new pattern variable was introduced.
-\ No newline at end of file
+new pattern variable was introduced.
+
+@include-section{light.scrbl}
diff --git a/test/test-ddd-forms.rkt b/test/test-ddd-forms.rkt
@@ -169,45 +169,53 @@
'(1 2 3 4 5 6))
;; Implicit (list _), could also be changed to an implicit (values).
-(check-equal? (syntax-parse #'(([1 2 3] [4 5 6]) [a])
- [(([x …] …) [y …])
- x … …])
+(check-equal? (list ;; unwrap the splice
+ (syntax-parse #'(([1 2 3] [4 5 6]) [a])
+ [(([x …] …) [y …])
+ x … …]))
'(1 2 3 4 5 6))
;; TODO: expr … inside begin and let
-(check-equal? (syntax-case #'((1 2 3) (4 5)) ()
- [((x …) …)
- (let ()
- (list (length (syntax->list #'(x …)))
- (+ (syntax-e #'x) 3) …)
- …)])
+(check-equal? (list ;; unwrap the splice
+ (syntax-case #'((1 2 3) (4 5)) ()
+ [((x …) …)
+ (let ()
+ (list (length (syntax->list #'(x …)))
+ (+ (syntax-e #'x) 3) …)
+ …)]))
'([3 4 5 6]
[2 7 8]))
-(check-equal? (syntax-parse #'([1 2 3] [4 5 6])
- [([x …] …)
- x … …])
+(check-equal? (list ;; unwrap the splice
+ (syntax-parse #'([1 2 3] [4 5 6])
+ [([x …] …)
+ x … …]))
'(1 2 3 4 5 6))
-(check-equal? (syntax-parse #'([1 2 3] [4 5 6])
- [([x …] …)
- (x …) …])
+(check-equal? (list ;; unwrap the splice
+ (syntax-parse #'([1 2 3] [4 5 6])
+ [([x …] …)
+ (x …) …]))
'((1 2 3) (4 5 6)))
-(check-equal? (syntax-parse #'([1 2 3] [4 5 6])
- [([x …] …)
- ((list x) …) …])
+(check-equal? (list ;; unwrap the splice
+ (syntax-parse #'([1 2 3] [4 5 6])
+ [([x …] …)
+ ((list x) …) …]))
'(((1) (2) (3)) ((4) (5) (6))))
-(check-equal? (syntax-parse #'([1 2 3] [4 5 6])
- [([x …] …)
- ((+ x 10) …) …])
+(check-equal? (list ;; unwrap the splice
+ (syntax-parse #'([1 2 3] [4 5 6])
+ [([x …] …)
+ ((+ x 10) …) …]))
'((11 12 13) (14 15 16)))
-(check-equal? (syntax-parse #'([1 2 3] [4 5 6])
- [([x …] …)
- (begin ((+ x 10) …) …)])
+(check-equal? (list ;; unwrap the splice
+ (syntax-parse #'([1 2 3] [4 5 6])
+ [([x …] …)
+ (begin ((+ x 10) …) …)]))
'((11 12 13) (14 15 16)))
-(check-equal? (syntax-parse #'([1 2 3] [4 5 6])
- [([x …] …)
- (define/with-syntax y (+ x 10)) … …
- y … …])
+(check-equal? (list ;; unwrap the splice
+ (syntax-parse #'([1 2 3] [4 5 6])
+ [([x …] …)
+ (define/with-syntax y (+ x 10)) … …
+ y … …]))
'(11 12 13 14 15 16))
;; Implicit apply with (+ y … …)
diff --git a/test/test-optional.rkt b/test/test-optional.rkt
@@ -6,9 +6,10 @@
syntax/macro-testing
phc-toolkit/untyped)
-(check-equal? (syntax-parse #'(1 #:kw 3)
- [({~and {~or x:nat #:kw}} …)
- (?? x 'missing) …])
+(check-equal? (list ;; unwrap the splice
+ (syntax-parse #'(1 #:kw 3)
+ [({~and {~or x:nat #:kw}} …)
+ (?? x 'missing) …]))
'(1 missing 3))
(check-equal? (syntax-parse #'(1 #:kw 3)