#lang scribble/doc
@(require "common.rkt" (for-label syntax/template))

@title[#:tag "template"]{Controlling Syntax Templates}

@defmodule[syntax/template]

@defproc[(transform-template [template-stx syntax?]
                             [#:save save-proc (syntax? . -> . any/c)]
                             [#:restore-stx restore-proc-stx syntax?]
                             [#:leaf-save leaf-save-proc (syntax? . -> . any/c) save-proc]
                             [#:leaf-restore-stx leaf-restore-proc-stx syntax? #'(lambda (data stx) stx)]
                             [#:leaf-datum-stx leaf-datum-proc-stx syntax? #'(lambda (v) v)]
                             [#:pvar-save pvar-save-proc (identifier? . -> . any/c) (lambda (x) #f)]
                             [#:pvar-restore-stx pvar-restore-stx syntax? #'(lambda (d stx) stx)]
                             [#:cons-stx cons-proc-stx syntax? cons]
                             [#:ellipses-end-stx ellipses-end-stx syntax? #'values]
                             [#:constant-as-leaf? constant-as-leaf? boolean? #f])
         syntax?]{

Produces an representation of an expression similar to
@RACKET[#`((UNSYNTAX @racket[syntax]) #,template-stx)], but functions like
@racket[save-proc] can collect information that might otherwise be
lost by @racket[syntax] (such as properties when the syntax object is
marshaled within bytecode), and run-time functions like the one
specified by @racket[restore-proc-stx] can use the saved information or
otherwise process the syntax object that is generated by the template.

The @racket[save-proc] is applied to each syntax object in the
representation of the original template (i.e., in
@racket[template-stx]). If @racket[constant-as-leaf?] is @racket[#t],
then @racket[save-proc] is applied only to syntax objects that contain
at least one pattern variable in a sub-form. The result of
@racket[save-proc] is provided back as the first argument to
@racket[restore-proc-stx], which indicates a function with a contract
@racket[(any/c syntax any/c . -> . any/c)]; the second argument to
@racket[restore-proc-stx] is the syntax object that @racket[syntax]
generates, and the last argument is a datum that have been processed
recursively (by functions such as @racket[restore-proc-stx]) and that
normally would be converted back to a syntax object using the second
argument's context, source, and properties. Note that
@racket[save-proc] works at expansion time (with respect to the
template form), while @racket[restore-proc-stx] indicates a function
that is called at run time (for the template form), and the data that
flows from @racket[save-proc] to @racket[restore-proc-stx] crosses
phases via @racket[quote].

The @racket[leaf-save-proc] and @racket[leaf-restore-proc-stx] procedures
are analogous to @racket[save-proc] and
@racket[restore-proc-stx], but they are applied to leaves, so
there is no third argument for recursively processed sub-forms. The
function indicated by @racket[leaf-restore-proc-stx] should have the
contract @racket[(any/c syntax? . -> . any/c)].

The @racket[leaf-datum-proc-stx] procedure is applied to leaves that
are not syntax objects, which can happen because pairs and the empty
list are not always individually wrapped as syntax objects. The
function should have the contract @racket[(any/c . -> . any/c)]. When
@racket[constant-as-leaf?] is @racket[#f], the only possible argument
to the procedure is @racket[null].

The @racket[pvar-save] and @racket[pvar-restore-stx] procedures are
analogous to @racket[save-proc] and @racket[restore-proc-stx],
but they are applied to pattern variables. The
@racket[pvar-restore-stx] procedure should have the contract
@racket[(any/c syntax? . -> . any/c)], where the second argument
corresponds to the substitution of the pattern variable.

The @racket[cons-proc-stx] procedure is used to build intermediate
pairs, including pairs passed to @racket[restore-proc-stx] and pairs
that do not correspond to syntax objects.

The @racket[ellipses-end-stx] procedure is an extra filter on the
syntax object that follows a sequence of @racket[...] ellipses in the
template. The procedure should have the contract @racket[(any/c . ->
. any/c)].

The following example illustrates a use of @racket[transform-template]
to implement a @racket[syntax/shape] form that preserves the
@racket['paren-shape] property from the original template, even if the
template code is marshaled within bytecode.

@racketblock[
(define-for-syntax (get-shape-prop stx)
  (syntax-property stx 'paren-shape))

(define (add-shape-prop v stx datum)
  (syntax-property (datum->syntax stx datum stx stx stx)
                   'paren-shape
                   v))

(define-syntax (syntax/shape stx)
  (syntax-case stx ()
    [(_ tmpl)
     (transform-template #'tmpl
                         #:save get-shape-prop
                         #:restore-stx #'add-shape-prop)]))
]}
