On this page:
5.1 Extracting Inferred Names
5.2 Support for local-expand
5.3 Parsing define-like Forms
normalize-definition/  mk-rhs
5.4 Flattening begin Forms
5.5 Expanding define-struct-like Forms
5.6 Resolving include-like Paths
5.7 Controlling Syntax Templates
5.8 Creating Macro Transformers
5.9 Applying Macro Transformers

5 Macro Transformer Helpers🔗

5.1 Extracting Inferred Names🔗

 (require syntax/name) package: base


(syntax-local-infer-name stx [use-local?])  any/c

  stx : syntax?
  use-local? : any/c = #t
Similar to syntax-local-name, except that stx is checked for an 'inferred-name property (which overrides any inferred name). If neither syntax-local-name nor 'inferred-name produce a name, or if the 'inferred-name property value is #<void>, then a name is constructed from the source-location information in stx, if any. If no name can be constructed, the result is #f.

To support the propagation and merging of consistent properties during expansions, the value of the 'inferred-name property can be a tree formed with cons where all of the leaves are the same. For example, (cons 'name 'name) is equivalent to 'name, and (cons (void) (void)) is equivalent to #<void>.

If use-local? is #f, then syntax-local-name is not used. Provide use-local? as #f to construct a name for a syntax object that is not an expression currently being expanded.

5.2 Support for local-expand🔗

 (require syntax/context) package: base


(build-expand-context v)  list?

  v : (or/c symbol? list?)
Returns a list suitable for use as a context argument to local-expand for an internal-definition context. The v argument represents the immediate context for expansion. The context list builds on (syntax-local-context) if it is a list.


(generate-expand-context [liberal-definitions?])  list?

  liberal-definitions? : boolean? = #f
Calls build-expand-context with a generated unique value. When liberal-definitions? is true, the value is an instance of a structure type with a true value for the prop:liberal-define-context property.

5.3 Parsing define-like Forms🔗

 (require syntax/define) package: base


(normalize-definition defn-stx    
identifier? syntax?
  defn-stx : syntax?
  lambda-id-stx : identifier?
  check-context? : boolean? = #t
  opt+kws? : boolean? = #f
Takes a definition form whose shape is like define (though possibly with a different name) and returns two values: the defined identifier and the right-hand side expression.

To generate the right-hand side, this function may need to insert uses of lambda. The lambda-id-stx argument provides a suitable lambda identifier.

If the definition is ill-formed, a syntax error is raised. If check-context? is true, then a syntax error is raised if (syntax-local-context) indicates that the current context is an expression context. The default value of check-context? is #t.

If opt+kws? is #t, then arguments of the form [id expr], keyword id, and keyword [id expr] are allowed, and they are preserved in the expansion.


(normalize-definition/mk-rhs defn-stx 
identifier? (-> syntax? syntax?) syntax?
  defn-stx : syntax?
  lambda-id-stx : identifier?
  check-context? : boolean?
  opt+kws? : boolean?
  err-no-body? : boolean?
The helper for normalize-definition that produces three values: the defined identifier, a function that takes the syntax of the body and produces syntax that has the expected binding structure, and finally the right-hand side expression that normalize-definition gives to the previous function.

If err-no-body? is true, then there must be a right-hand side expression or else it is a syntax error. The err-no-body? argument is true for uses of normalize-definition.

Added in version of package base.

5.4 Flattening begin Forms🔗

 (require syntax/flatten-begin) package: base


(flatten-begin stx)  (listof syntax?)

  stx : syntax?
Extracts the sub-expressions from a begin-like form, reporting an error if stx does not have the right shape (i.e., a syntax list). The resulting syntax objects have annotations transferred from stx using syntax-track-origin.

> (flatten-begin #'(begin 1 2 3))

'(#<syntax:eval:2:0 1> #<syntax:eval:2:0 2> #<syntax:eval:2:0 3>)

> (flatten-begin #'(begin (begin 1 2) 3))

'(#<syntax:eval:3:0 (begin 1 2)> #<syntax:eval:3:0 3>)

> (flatten-begin #'(+ (- 1 2) 3))

'(#<syntax:eval:4:0 (- 1 2)> #<syntax:eval:4:0 3>)


(flatten-all-begins stx)  (listof syntax?)

  stx : syntax?
Extracts the sub-expressions from a begin form and recursively flattens begin forms nested in the original one. An error will be reported if stx is not a begin form. The resulting syntax objects have annotations transferred from stx using syntax-track-origin.

> (flatten-all-begins #'(begin 1 2 3))

'(#<syntax:eval:5:0 1> #<syntax:eval:5:0 2> #<syntax:eval:5:0 3>)

> (flatten-all-begins #'(begin (begin 1 2) 3))

'(#<syntax:eval:6:0 1> #<syntax:eval:6:0 2> #<syntax:eval:6:0 3>)

Added in version of package base.

5.5 Expanding define-struct-like Forms🔗

 (require syntax/struct) package: base


(parse-define-struct stx orig-stx)  
(or/c identifier? #f)
(listof identifier?)
  stx : syntax?
  orig-stx : syntax?
Parses stx as a define-struct form, but uses orig-stx to report syntax errors (under the assumption that orig-stx is the same as stx, or that they at least share sub-forms). The result is four values: an identifier for the struct type name, a identifier or #f for the super-name, a list of identifiers for fields, and a syntax object for the inspector expression.


(build-struct-names name-id 
  [#:constructor-name ctr-name] 
  (listof identifier?)
  name-id : identifier?
  field-ids : (listof identifier?)
  ctr-name : (or/c identifier? #f) = #f
  omit-sel? : boolean?
  omit-set? : boolean?
  src-stx : (or/c syntax? #f) = #f
Generates the names bound by define-struct given an identifier for the struct type name and a list of identifiers for the field names. The result is a list of identifiers:

If omit-sel? is true, then the selector names are omitted from the result list. If omit-set? is true, then the setter names are omitted from the result list.

The default src-stx is #f; it is used to provide a source location to the generated identifiers.


(build-struct-generation name-id 
  [#:constructor-name ctr-name] 
  (listof identifier?)
  name-id : identifier?
  field-ids : (listof identifier?)
  ctr-name : (or/c identifier? #f) = #f
  omit-sel? : boolean?
  omit-set? : boolean?
  super-type : any/c = #f
  prop-value-list : list? = '(list)
  immutable-k-list : list? = '(list)
Takes the same arguments as build-struct-names and generates an S-expression for code using make-struct-type to generate the structure type and return values for the identifiers created by build-struct-names. The optional super-type, prop-value-list, and immutable-k-list parameters take S-expressions that are used as the corresponding argument expressions to make-struct-type.


(build-struct-generation* all-name-ids 
  [#:constructor-name ctr-name] 
  (listof identifier?)
  all-name-ids : (listof identifier?)
  name-id : identifier?
  field-ids : (listof identifier?)
  ctr-name : (or/c identifier? #f) = #f
  omit-sel? : boolean?
  omit-set? : boolean?
  super-type : any/c = #f
  prop-value-list : list? = '(list)
  immutable-k-list : list? = '(list)
Like build-struct-generation, but given the names produced by build-struct-names, instead of re-generating them.


(build-struct-expand-info name-id    
  [#:omit-constructor? no-ctr?    
  #:constructor-name ctr-name    
  #:omit-struct-type? no-type?]    
  base-setters)  any
  name-id : identifier?
  field-ids : (listof identifier?)
  no-ctr? : any/c = #f
  ctr-name : (or/c identifier? #f) = #f
  no-type? : any/c = #f
  omit-sel? : boolean?
  omit-set? : boolean?
  base-name : (or/c identifier? boolean?)
  base-getters : (listof (or/c identifier? #f))
  base-setters : (listof (or/c identifier? #f))
Takes mostly the same arguments as build-struct-names, plus a parent identifier/#t/#f and a list of accessor and mutator identifiers (possibly ending in #f) for a parent type, and generates an S-expression for expansion-time code to be used in the binding for the structure name.

If no-ctr? is true, then the constructor name is omitted from the expansion-time information. Similarly, if no-type? is true, then the structure-type name is omitted.

A #t for the base-name means no super-type, #f means that the super-type (if any) is unknown, and an identifier indicates the super-type identifier.


(struct-declaration-info? v)  boolean?

  v : any/c
Returns #t if x has the shape of expansion-time information for structure type declarations, #f otherwise. See Structure Type Transformer Binding.


(generate-struct-declaration orig-stx    
  omit-set?])  syntax?
  orig-stx : syntax?
  name-id : identifier?
  super-id-or-false : (or/c identifier? #f)
  field-id-list : (listof identifier?)
  current-context : any/c
  make-make-struct-type : procedure?
  omit-sel? : boolean? = #f
  omit-set? : boolean? = #f
This procedure implements the core of a define-struct expansion.

The generate-struct-declaration procedure is called by a macro expander to generate the expansion, where the name-id, super-id-or-false, and field-id-list arguments provide the main parameters. The current-context argument is normally the result of syntax-local-context. The orig-stx argument is used for syntax errors. The optional omit-sel? and omit-set? arguments default to #f; a #t value suppresses definitions of field selectors or mutators, respectively.

The make-struct-type procedure is called to generate the expression to actually create the struct type. Its arguments are orig-stx, name-id-stx, defined-name-stxes, and super-info. The first two are as provided originally to generate-struct-declaration, the third is the set of names generated by build-struct-names, and the last is super-struct info obtained by resolving super-id-or-false when it is not #f, #f otherwise.

The result should be an expression whose values are the same as the result of make-struct-type. Thus, the following is a basic make-make-struct-type:

(lambda (orig-stx name-stx defined-name-stxes super-info)
  #`(make-struct-type '#,name-stx
                       #,(and super-info (list-ref super-info 0))
                       #,(/ (- (length defined-name-stxes) 3) 2)
                       0 #f))

but an actual make-make-struct-type will likely do more.

5.6 Resolving include-like Paths🔗

 (require syntax/path-spec) package: base


(resolve-path-spec path-spec-stx    
  expr-stx)  complete-path?
  path-spec-stx : syntax?
  source-stx : syntax?
  expr-stx : syntax?
Resolves the syntactic path specification path-spec-stx as for include.

The source-stx specifies a syntax object whose source-location information determines relative-path resolution. The expr-stx is used for reporting syntax errors.

5.7 Controlling Syntax Templates🔗

 (require syntax/template) package: base


(transform-template template-stx 
  #:save save-proc 
  #:restore-stx restore-proc-stx 
  [#:leaf-save leaf-save-proc 
  #:leaf-restore-stx leaf-restore-proc-stx 
  #:leaf-datum-stx leaf-datum-proc-stx 
  #:pvar-save pvar-save-proc 
  #:pvar-restore-stx pvar-restore-stx 
  #:cons-stx cons-proc-stx 
  #:ellipses-end-stx ellipses-end-stx 
  #:constant-as-leaf? constant-as-leaf?]) 
  template-stx : syntax?
  save-proc : (syntax? . -> . any/c)
  restore-proc-stx : syntax?
  leaf-save-proc : (syntax? . -> . any/c) = save-proc
  leaf-restore-proc-stx : syntax? = #'(lambda (data stx) stx)
  leaf-datum-proc-stx : syntax? = #'(lambda (v) v)
  pvar-save-proc : (identifier? . -> . any/c) = (lambda (x) #f)
  pvar-restore-stx : syntax? = #'(lambda (d stx) stx)
  cons-proc-stx : syntax? = #'cons
  ellipses-end-stx : syntax? = #'values
  constant-as-leaf? : boolean? = #f
Produces an representation of an expression similar to #`(syntax #,template-stx), but functions like save-proc can collect information that might otherwise be lost by syntax (such as properties when the syntax object is marshaled within bytecode), and run-time functions like the one specified by restore-proc-stx can use the saved information or otherwise process the syntax object that is generated by the template.

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

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

The 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 (-> any/c any/c). When constant-as-leaf? is #f, the only possible argument to the procedure is null.

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

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

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

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

(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)
(define-syntax (syntax/shape stx)
  (syntax-case stx ()
    [(_ tmpl)
     (transform-template #'tmpl
                         #:save get-shape-prop
                         #:restore-stx #'add-shape-prop)]))

5.8 Creating Macro Transformers🔗

 (require syntax/transformer) package: base


(make-variable-like-transformer reference-stx 
  (and/c set!-transformer? (-> syntax? syntax?))
  reference-stx : (or/c syntax? (-> identifier? syntax?))
  setter-stx : (or/c syntax? (-> syntax? syntax?) #f) = #f
Creates a transformer that replaces references to the macro identifier with reference-stx. Uses of the macro in operator position are interpreted as an application with reference-stx as the function and the arguments as given. If the reference-stx is a procedure, it is applied to the macro identifier.

If the macro identifier is used as the target of a set! form, then the set! form expands into the application of setter-stx to the set! expression’s right-hand side, if setter-stx is syntax; otherwise, the identifier is considered immutable and a syntax error is raised. If setter-stx is a procedure, it is applied to the entire set! expression.

> (define the-box (box add1))
> (define-syntax op
     #'(unbox the-box)
     #'(lambda (v) (set-box! the-box v))))
> (op 5)


> (set! op 0)
> op


Added in version 6.3 of package base.


(make-expression-transformer transformer)

  (-> syntax? syntax?)
  transformer : (-> syntax? syntax?)
Creates a transformer derived from transformer that ensures it expands in an expression context. When invoked in an expression context, it calls transformer. When invoked in any other context, the new transformer wraps the argument syntax with #%expression.

Added in version of package base.

5.9 Applying Macro Transformers🔗

 (require syntax/apply-transformer) package: base


(local-apply-transformer transformer    
  intdef-ctx)  syntax?
  transformer : (or/c (-> syntax? syntax?) set!-transformer?)
  stx : syntax?
  context : (or/c 'expression 'top-level 'module 'module-begin list?)
  intdef-ctx : 
(or/c internal-definition-context?
       (listof internal-definition-context?))
For backwards compatibility only; syntax-local-apply-transformer is preferred.

Applies transformer as a syntax transformer to stx in the current expansion context. The result is similar to expanding a use of an identifier bound as a syntax transformer bound to transformer with local-expand, except that expansion is guaranteed to stop after applying a single macro transformation (assuming transformer does not explicitly force further recursive expansion).

Unlike simply applying transformer to stx directly, using local-apply-transformer introduces the appropriate use-site scope and macro-introduction scope that would be added by the expander.

The context and intdef-ctx arguments are treated the same way as the corresponding arguments to local-expand.

Added in version of package base.