1.2.1 Phases and Reusable Syntax Classes

► |  | 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

► | 1 | Parsing and Specifying Syntax

1.1 | Introduction
1.2 | Examples
1.3 | Parsing Syntax
1.4 | Specifying Syntax with Syntax Classes
1.5 | Syntax Patterns
1.6 | Defining Simple Macros
1.7 | Literal Sets and Conventions
1.8 | Library Syntax Classes and Literal Sets
1.9 | Unwindable State
1.10 | Configuring Error Reporting
1.11 | Debugging and Inspection Tools
1.12 | Experimental
1.13 | Minimal Library

▼ | 1.2 | Examples

1.2.1 | Phases and Reusable Syntax Classes
1.2.2 | Optional Keyword Arguments
1.2.3 | Variants with Uniform Meanings
1.2.4 | Variants with Varied Meanings
1.2.5 | More Keyword Arguments
1.2.6 | Contracts on Macro Sub-expressions

top contents ← prev up next →

##### 1.2.1`` Phases and Reusable Syntax Classes🔗ℹ

As demonstrated in the Introduction, the simplest place to define a syntax class is within the macro definition that uses it. But that limits the scope of the syntax class to the one client macro, and it makes for very large macro definitions. Creating reusable syntax classes requires some awareness of the Racket phase level separation. A syntax class defined immediately within a module cannot be used by macros in the same module; it is defined at the wrong phase.

> > (module phase-mismatch-mod racket
> (require syntax/parse (for-syntax syntax/parse))
> (define-syntax-class foo
> (pattern (a b c)))
> (define-syntax (macro stx)
> (syntax-parse stx
> [(_ f:foo) #'(+ f.a f.b f.c)])))
> syntax-parse: not defined as syntax class
> at: foo
> in: (syntax-parse stx ((_ f:foo) (syntax (+ f.a f.b
> f.c))))

In the module above, the syntax class foo is defined at phase level 0. The reference to foo within macro, however, is at phase level 1, being the implementation of a macro transformer. (Needing to require syntax/parse twice, once normally and once for-syntax is a common warning sign of phase level incompatibility.)

The phase level mismatch is easily remedied by putting the syntax class definition within a begin-for-syntax block:

> > (module phase-ok-mod racket
> (require (for-syntax syntax/parse))
> (begin-for-syntax
> (define-syntax-class foo
> (pattern (a b c))))
> (define-syntax (macro stx)
> (syntax-parse stx
> [(_ f:foo) #'(+ f.a f.b f.c)])))

In the revised module above, foo is defined at phase 1, so it can be used in the implementation of the macro.

An alternative to begin-for-syntax is to define the syntax class in a separate module and require that module for-syntax.

> > (module stxclass-mod racket
> (require syntax/parse)
> (define-syntax-class foo
> (pattern (a b c)))
> (provide foo))
> > (module macro-mod racket
> (require (for-syntax syntax/parse
> 'stxclass-mod))
> (define-syntax (macro stx)
> (syntax-parse stx
> [(_ f:foo) #'(+ f.a f.b f.c)]))
> (provide macro))
> > (require 'macro-mod)
> > (macro (1 2 3))
> 6

If a syntax class refers to literal identifiers, or if it computes expressions via syntax templates, then the module containing the syntax class must generally require for-template the bindings referred to in the patterns and templates.

> > (module arith-keywords-mod racket
> (define-syntax plus (syntax-rules ()))
> (define-syntax times (syntax-rules ()))
> (provide plus times))
> > (module arith-stxclass-mod racket
> (require syntax/parse
> (for-template 'arith-keywords-mod
> racket))
> (define-syntax-class arith
> #:literals (plus times)
> (pattern n:nat
> #:with expr #'n)
> (pattern (plus a:arith b:arith)
> #:with expr #'(+ a.expr b.expr))
> (pattern (times a:arith b:arith)
> #:with expr #'(* a.expr b.expr)))
> (provide arith))
> > (module arith-macro-mod racket
> (require (for-syntax syntax/parse
> 'arith-stxclass-mod)
> 'arith-keywords-mod)
> (define-syntax (arith-macro stx)
> (syntax-parse stx
> [(_ a:arith)
> #'(values 'a.expr a.expr)]))
> (provide arith-macro
> (all-from-out 'arith-keywords-mod)))
> > (require 'arith-macro-mod)
> > (arith-macro (plus 1 (times 2 3)))
> '(+ 1 (* 2 3))
> 7

In 'arith-stxclass-mod, the module 'arith-keywords-mod must be required for-template because the keywords are used in phase-0 expressions. Likewise, the module racket must be required for-template because the syntax class contains syntax templates involving + and * (and, in fact, the implicit #%app syntax). All of these identifiers (the keywords plus and times; the procedures + and *; and the implicit syntax #%app) must be bound at “absolute” phase level 0. Since the module 'arith-stxclass-mod is required with a phase level offset of 1 (that is, for-syntax), it must compensate with a phase level offset of -1, or for-template.

top contents ← prev up next →