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
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?)
procedure
(generate-expand-context [liberal-definitions?]) → list?
liberal-definitions? : boolean? = #f
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
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?
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?
> (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?
> (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?
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
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)
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)
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))
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
v : any/c
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
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?
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
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]) → (and/c set!-transformer? (-> syntax? syntax?)) reference-stx : (or/c syntax? (-> identifier? syntax?)) setter-stx : (or/c syntax? (-> syntax? syntax?) #f) = #f
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 (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.
procedure
(make-expression-transformer transformer)
→ (-> syntax? syntax?) transformer : (-> syntax? syntax?)
Added in version 7.7.0.9 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?) = '()
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.