5 Macro Transformer Helpers

▼ |  | Syntax: Meta-Programming Helpers

1 | Parsing and Specifying Syntax
2 | Syntax Object Helpers
3 | Datum Pattern Matching
4 | Module-Processing Helpers
5 | Macro Transformer Helpers
6 | Reader Helpers
7 | Parsing for Bodies
8 | Unsafe for Clause Transforms
9 | Source Locations
10 | Preserving Source Locations
11 | Non-Module Compilation And Expansion
12 | Trusting Standard Recertifying Transformers
13 | Attaching Documentation to Exports
14 | Contracts for Macro Subexpressions
15 | Macro Testing
16 | Internal-Definition Context Helpers
| Index

► | 5 | Macro Transformer Helpers

5.1 | Extracting Inferred Names
5.2 | Support for local-expand
5.3 | Parsing define-like Forms
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

On this page:

5.1`` Extracting Inferred Names
syntax-local-infer-name
5.2`` Support for local-expand
build-expand-context
generate-expand-context
5.3`` Parsing define-like Forms
normalize-definition
normalize-definition/ mk-rhs
5.4`` Flattening begin Forms
flatten-begin
flatten-all-begins
5.5`` Expanding define-struct-like Forms
parse-define-struct
build-struct-names
build-struct-generation
build-struct-generation*
build-struct-expand-info
struct-declaration-info?
generate-struct-declaration
5.6`` Resolving include-like Paths
resolve-path-spec
5.7`` Controlling Syntax Templates
transform-template
5.8`` Creating Macro Transformers
make-variable-like-transformer
5.9`` Applying Macro Transformers
local-apply-transformer

top ← prev up next →

### 5`` Macro Transformer Helpers

#### 5.1`` Extracting Inferred Names

(require syntax/name) | package: base

> procedure (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

> procedure (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.

> procedure (generate-expand-context) → list?

Calls build-expand-context with a generated symbol.

#### 5.3`` Parsing define-like Forms

(require syntax/define) | package: base

> > procedure
> > (normalize-definition |  | defn-stx |  |  |  |
> > |  | lambda-id-stx |  |  |  |
> > | [ | check-context? |  |  |  |
> > |  | opt+kws?]) |  | → |  |
> > 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.

> > procedure
> > (normalize-definition/mk-rhs |  | defn-stx |
> > |  | lambda-id-stx |
> > |  | check-context? |
> > |  | opt+kws? |
> > |  | err-no-body?) |
> | → |  |
> 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 6.1.1.8 of package base.

#### 5.4`` Flattening begin Forms

(require syntax/flatten-begin) | package: base

> procedure (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.

Examples:

> > (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>)

> procedure (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.

Examples:

> > (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 6.1.0.3 of package base.

#### 5.5`` Expanding define-struct-like Forms

(require syntax/struct) | package: base

> > procedure
> > (parse-define-struct stx orig-stx) |  | → |  |
> > identifier?
> > (or/c identifier? false/c)
> > (listof identifier?)
> > syntax?
> 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.

> > procedure
> > (build-struct-names |  | name-id |
> > |  | field-ids |
> > | [ | #:constructor-name ctr-name] |
> > |  | omit-sel? |
> > |  | omit-set? |
> > | [ | src-stx]) |
> → (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? false/c) = #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:

- struct: name-id

- ctr-name, or make-name-id if ctr-name is #f

- name-id?

- name-id-field, for each field in field-ids.

- set-name-id-field! (getter and setter names alternate).

- ....

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.

> > procedure
> > (build-struct-generation |  | name-id |
> > |  | field-ids |
> > | [ | #:constructor-name ctr-name] |
> > |  | omit-sel? |
> > |  | omit-set? |
> > | [ | super-type |
> > |  | prop-value-list |
> > |  | immutable-k-list]) |
> → (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.

> > procedure
> > (build-struct-generation* |  | all-name-ids |
> > |  | name-id |
> > |  | field-ids |
> > | [ | #:constructor-name ctr-name] |
> > |  | omit-sel? |
> > |  | omit-set? |
> > | [ | super-type |
> > |  | prop-value-list |
> > |  | immutable-k-list]) |
> → (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.

> > procedure
> > (build-struct-expand-info |  | name-id |  |  |  |
> > |  | field-ids |  |  |  |
> > | [ | #:omit-constructor? no-ctr? |  |  |  |
> > |  | #:constructor-name ctr-name |  |  |  |
> > |  | #:omit-struct-type? no-type?] |  |  |  |
> > |  | omit-sel? |  |  |  |
> > |  | omit-set? |  |  |  |
> > |  | base-name |  |  |  |
> > |  | base-getters |  |  |  |
> > |  | 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? false/c))
> base-setters : (listof (or/c identifier? false/c))

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.

> procedure (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.

> > procedure
> > (generate-struct-declaration |  | orig-stx |  |  |  |
> > |  | name-id |  |  |  |
> > |  | super-id-or-false |  |  |  |
> > |  | field-id-list |  |  |  |
> > |  | current-context |  |  |  |
> > |  | make-make-struct-type |  |  |  |
> > | [ | omit-sel? |  |  |  |
> > |  | omit-set?]) |  | → |  | syntax?
> orig-stx : syntax?
> name-id : identifier?
> super-id-or-false : (or/c identifier? false/c)
> 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

> > procedure
> > (resolve-path-spec |  | path-spec-stx |  |  |  |
> > |  | source-stx |  |  |  |
> > |  | expr-stx |  |  |  |
> > |  | build-path-stx) |  | → |  | complete-path?
> path-spec-stx : syntax?
> source-stx : syntax?
> expr-stx : syntax?
> build-path-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. The build-path-stx is usually #'build-path; it provides an identifier to compare to parts of path-spec-stx to recognize the build-path keyword.

#### 5.7`` Controlling Syntax Templates

(require syntax/template) | package: base

> > procedure
> > (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?]) |
> → syntax?
> 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)
> 'paren-shape
> v))
> (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

> > procedure
> > (make-variable-like-transformer |  | reference-stx |
> > | [ | setter-stx]) |
> → set!-transformer?
> reference-stx : syntax?
> setter-stx : (or/c 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 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.

Examples:

> > (define the-box (box add1))
> > (define-syntax op
> (make-variable-like-transformer
> #'(unbox the-box)
> #'(lambda (v) (set-box! the-box v))))
> > (op 5)
> 6
> > (set! op 0)
> > op
> 0

Added in version 6.3 of package base.

#### 5.9`` Applying Macro Transformers

(require syntax/apply-transformer) | package: base

> > procedure
> > (local-apply-transformer |  | transformer |  |  |  |
> > |  | stx |  |  |  |
> > |  | context |  |  |  |
> > | [ | intdef-ctxs]) |  | → |  | syntax?
> transformer : (or/c (-> syntax? syntax?) set!-transformer?)
> stx : syntax?
> context : (or/c 'expression 'top-level 'module 'module-begin list?)
> intdef-ctxs : (listof internal-definition-context?) = '()

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-ctxs arguments are treated the same way as the corresponding arguments to local-expand.

Added in version 6.90.0.29 of package base.

top ← prev up next →