Generic Syntax Expanders
1 Expanders And Transformers
expander
expander-of-type?
expand-syntax-tree-with-expanders-of-type
2 Expander Types
expander-type?
make-expander-type
make-union-expander-type
expander-type-includes?
3 Defining Generic Syntax Expanders
define-expander-type
4 Lens Scoped Syntax Transformers
with-scoped-pre-transformer
with-scoped-pre-transformers
5 Lens Scoped Syntax Transformers - Definition Form
define-syntax-with-scoped-pre-transformers
6 require and provide transformers
expander-in
expander-out
7.1

Generic Syntax Expanders

Jack Firth <[email protected]>

This library provides forms to define generic syntax expanders. These are essentially macros that have no meaning on their own, but other macros can be told to expand all generic syntax expanders of some type in some portion of their body before themselves expanding. This is similar to how Racket’s built in match form has match expanders, which allows the grammar of the match form to be extended with custom match expanders using define-match-expander. This library generalizes the concept, making complex macros more composable and extensible.

Source code is available at https://github.com/jackfirth/generic-syntax-expanders

1 Expanders And Transformers

Generic expanders are implemented as values of the expander? struct bound with define-syntax, that store both a type and a transformer procedure. Future versions of this library may support storing an additional transformer for use outside expander-contexts in normal syntax parsing. This could be used for better error messages, or for an expander meant to have meaning in both a particularly typed expansion context and a normal expression expansion context.

struct

(struct expander (type transformer))

  type : expander-type?
  transformer : (-> syntax? syntax?)
A structure type for generic syntax expanders. A generic syntax expander has an associated type and transformer. The transformer can be any arbitrary function that accepts a syntax object in the same manner a transformer given to define-syntax would behave.

procedure

(expander-of-type? type expander)  boolean?

  type : expander-type?
  expander : expander?
Returns #t if the expander has type type, according to the semantics of expander-type-includes?, and returns #f otherwise.

Examples:
> (define A (make-expander-type))
> (define exp (expander A (λ (stx) stx)))
> (expander-of-type? A exp)

#t

procedure

(expand-syntax-tree-with-expanders-of-type type    
  syntax)  syntax?
  type : expander-type?
  syntax : syntax?
Recursively searches through syntax for identifiers bound to generic syntax expanders of the given type. When an expander is found, its transformer is called with the given syntax value of its location in the tree. The returned syntax object with have all expanders of the given type fully expanded, but nothing else will be expanded. Due to how expanders are bound to identifiers, this procedure can only be called in a transformer environment.

2 Expander Types

Under the hood, each generic expander defined with this library has an associated expander type. Syntax transformers built with this library examine this type to determine whether or not they should expand them.

A predicate for values produced by make-expander-type and variants.

Examples:

Creates a unique expander-type? for use in defining a new kind of generic expander.

Example:
> (make-expander-type)

'#s(expander-type g6240)

procedure

(make-union-expander-type type ...+)  expander-type?

  type : expander-type?
Creates a union expander-type?. This union type includes all of the given types, as well as any union type of a subset of the given types.

Example:
> (make-union-expander-type (make-expander-type) (make-expander-type))

'#s(expander-type (g6369 g6370))

procedure

(expander-type-includes? type-1 type-2)  boolean?

  type-1 : expander-type?
  type-2 : expander-type?
Returns #t if the two types are either identical, or if either type is a union type that contains the other, or if both types are union types and contain a nonempty intersection. Returns #f otherwise.

3 Defining Generic Syntax Expanders

This module provides a high-level API for creating generic expanders for use with other macros.

Creates an expander type and binds several derived values for working with the expander type:
  • id-expander-type - a new unique expander-type? bound at phase level 1

  • make-id-expander - a procedure bound at phase level 1 that accepts a transformer procedure and returns an expander? with id-expander-type

  • id-expander? - a predicate bound at phase level 1 recognizing expanders produced by make-id-expander

  • define-id-expander - a syntactic form at phase level 0 that takes an identifier and a transformer procedure and binds the identifier as a id-expander? for use in a transformer environment

  • expand-all-id-expanders - a procedure bound at phase level 1 that’s equivalent to expand-syntax-tree-with-expanders-of-type with the id-expander-type as the type argument

4 Lens Scoped Syntax Transformers

This module uses the lens package to create syntax transformers that affect only some small subpiece of a syntax object and compose them with other transformers. This allows for the creation of a macro that cedes control to other macros to pre-expand parts of its body before the macro expands. Combined with the syntax transformer produced by define-expander-type, this makes it easy to define a macro that expands all instances of a generic expander type in a specific subpiece of its body, turning it into an extensible macro.

procedure

((with-scoped-pre-transformer transformer    
  stx-lens    
  pre-transformer)    
  stx)  syntax?
  transformer : (-> syntax? syntax?)
  stx-lens : lens?
  pre-transformer : (-> syntax? syntax?)
  stx : syntax?
Transformers stx in two passes. First, the piece of stx that stx-lens views is transformed with pre-transformer. Then, the entire resulting syntax object is transformed with transformer.

procedure

((with-scoped-pre-transformers transformer 
  pre-transformer-lens-pairs) 
  stx) 
  syntax?
  transformer : (-> syntax? syntax?)
  pre-transformer-lens-pairs : 
(listof (list/c lens?
                (-> syntax? syntax?)))
  stx : syntax?
Similar to with-scoped-pre-transformer. Given pre-transformer-lens-pairs, a list of pairs of lenses and transformers, transformer is wrapped with with-scoped-pre-transformer with the pair’s pre-transformer and lens. The last pair in pre-transformer-lens-pairs is applied to stx first.

5 Lens Scoped Syntax Transformers - Definition Form

Syntax definition forms built on with-scoped-pre-transformer and friends.

syntax

(define-syntax-with-scoped-pre-transformers id
  ([stx-lens pre-transformer] ...)
  transformer-expr)
Binds id as a syntax transformer that is equivalent to (with-scoped-pre-transformers transformer-expr ([stx-lens pre-transformer] ...)).

6 require and provide transformers

require transformer

(expander-in require-spec id)

This require transformer imports from require-spec the identifiers defined by (define-expander-type id):
  • id-expander-type

  • make-id-expander

  • id-expander?

  • define-id-expander

  • expand-all-id-expanders

provide transformer

(expander-out id)

This provide transformer exports the identifiers defined by (define-expander-type id):
  • id-expander-type

  • make-id-expander

  • id-expander?

  • define-id-expander

  • expand-all-id-expanders