8.1 Quick Start
This section provides a rapid introduction to the syntax/parse library for the macro programmer.
To use syntax-parse to write a macro transformer, import it for-syntax:
(require (for-syntax syntax/parse))
For example, here is is a module that defines mylet, a macro that has the same behavior as the standard let form (including “named let”):
(module example racket/base |
(require (for-syntax scheme/base syntax/parse)) |
(define-syntax (mylet stx) |
(syntax-parse stx |
[(_ loop:id ((x:id e:expr) ...) . body) |
#'(letrec ([loop (lambda (x ...) . body)]) |
(loop e ...))] |
[(_ ((x:id e:expr) ...) . body) |
#'((lambda (x ...) . body) e ...)]))) |
The macro is defined as a procedure that takes one argument, stx. The syntax-parse form is similar to syntax-case, except that there is no literals list between the syntax argument and the sequence of clauses.
Note: Remember not to put a syntax-case style literals list between the syntax argument and the clauses!
The patterns contain identifiers consisting of two parts separated by a colon character, such as loop:id or e:expr. These are pattern variables annotated with syntax classes. For example, loop:id is a pattern variable named loop with the syntax class id (identifier). Note that only the pattern variable part is used in the syntax template.
Syntax classes restrict what a pattern variable can match. Above, loop only matches an identifier, so the first clause only matches the “named-let” syntax. Syntax classes replace some uses of syntax-case’s “fenders” or guard expressions. They also enable syntax-parse to automatically give specific error messages.
The syntax/parse library provides several built-in syntax classes (see Library syntax classes and literal sets for a list). Programmers can also define their own using define-syntax-class:
(module example-syntax racket/base |
(require syntax/parse) |
(provide binding) |
(define-syntax-class binding |
#:attributes (x e) |
(pattern (x:id e:expr)))) |
(module example racket/base |
(require (for-syntax racket/base |
syntax/parse |
'example-syntax)) |
(define-syntax (mylet stx) |
(syntax-parse stx |
[(_ loop:id (b:binding ...) . body) |
#'(letrec ([loop (lambda (b.x ...) . body)]) |
(loop b.e ...))] |
[(_ (b:binding ...) . body) |
#'((lambda (b.x ...) . body) b.e ...)]))) |
Note: Syntax classes must be defined in the same phase as the syntax-parse expression they’re used in. The right-hand side of a macro is at phase 1, so syntax classes it uses must be defined in a separate module and required for-syntax. Since the auxiliary module uses define-syntax-class at phase 0, it has (require syntax/parse), with no for-syntax.
Alternatively, the syntax class could be made a local definition, thus:
(module example racket/base |
(require (for-syntax scheme/base |
syntax/parse)) |
(define-syntax (mylet stx) |
(define-syntax-class binding |
#:attributes (x e) |
(pattern (x:id e:expr))) |
(syntax-parse stx |
[(_ loop:id (b:binding ...) . body) |
#'(letrec ([loop (lambda (b.x ...) . body)]) |
(loop b.e ...))] |
[(_ (b:binding ...) . body) |
#'((lambda (b.x ...) . body) b.e ...)]))) |
A syntax class is an abstraction of a syntax pattern. The syntax class binding gives a name to the repeated pattern fragment (x:id e:expr). The components of the fragment, x and e, become attributes of the syntax class. When b:binding matches, b gets bound to the whole binding pair, and b.x and b.e get bound to the variable name and expression, respectively. Actually, all of them are bound to sequences, because of the ellipses.
Syntax classes can have multiple alternative patterns. Suppose we wanted to extend mylet to allow a simple identifier as a binding, in which case it would get the value #f:
(mylet ([a 1] b [c 'foo]) ....)
Here’s how the syntax class would change:
The (require (for-template scheme/base)) is needed for the quote expression. If the syntax class definition were a local definition in the same module, the for-template would be unnecessary.
(module example-syntax scheme/base |
(require syntax/parse) |
(require (for-template scheme/base)) |
(provide binding) |
(define-syntax-class binding |
#:attributes (x e) |
(pattern (x:id e:expr)) |
(pattern x:id |
#:with e #''#f))) |
The second pattern matches unparenthesized identifiers. The e attribute is bound using a #:with clause, which matches the pattern e against the syntax from evaluating #'#f.
Optional keyword arguments are supported via head patterns. Unlike normal patterns, which match one term, head patterns can match a variable number of subterms in a list.
Suppose mylet accepted an optional #:check keyword with one argument, a procedure that would be applied to every variable’s value. Here’s one way to write it (dropping the named-let variant for simplicity):
(define-syntax (mylet stx) |
(syntax-parse stx |
[(_ (~optional (~seq #:check pred)) (b:binding ...) . body) |
#`((lambda (b.x ...) |
#,(if (attribute pred) |
#'(unless (and (pred b.x) ...) (error 'check)) |
#'(void)) |
. body) |
b.e ...)])) |
An optional subpattern might not match, so attributes within an ~optional form might not be bound to syntax. Such non-syntax-valued attributes may not be used within syntax templates. The attribute special form is used to get the value of an attribute; if the attribute didn’t get matched, the value is #f.
Here’s another way write it, using #:defaults to give the pred attribute a default value:
(define-syntax (mylet stx) |
(syntax-parse stx |
[(_ (~optional (~seq #:check pred) |
#:defaults ([pred #'(lambda (x) #t)])) |
(b:binding ...) . body) |
#`((lambda (b.x ...) |
(unless (and (pred b.x) ...) (error 'check)) |
. body) |
b.e ...)])) |
Programmers can also create abstractions over head patterns, using define-splicing-syntax-class. Here it is, rewritten to use multiple alternatives instead of ~optional:
(define-splicing-syntax-class optional-check |
#:attributes (pred) |
(pattern (~seq #:check pred)) |
(pattern (~seq) |
#:with pred #'(lambda (x) #t))) |
Note: When defining a splicing syntax class, remember to include ~seq in the pattern!
Here is the corresponding macro:
(define-syntax (mylet stx) |
(syntax-parse stx |
[(_ c:optional-check (b:binding ...) . body) |
#'((lambda (b.x ...) |
(unless (and (c.pred b.x) ...) (error 'check)) |
. body) |
b.e ...)])) |
The documentation in the following sections contains additional examples of syntax/parse features.