16.2.3 Mixing Patterns and Expressions: syntax-case

► |  | The Racket Guide

1 | Welcome to Racket
2 | Racket Essentials
3 | Built-In Datatypes
4 | Expressions and Definitions
5 | Programmer-Defined Datatypes
6 | Modules
7 | Contracts
8 | Input and Output
9 | Regular Expressions
10 | Exceptions and Control
11 | Iterations and Comprehensions
12 | Pattern Matching
13 | Classes and Objects
14 | Units
15 | Reflection and Dynamic Evaluation
16 | Macros
17 | Creating Languages
18 | Concurrency and Synchronization
19 | Performance
20 | Parallelism
21 | Running and Creating Executables
22 | More Libraries
23 | Dialects of Racket and Scheme
24 | Command-Line Tools and Your Editor of Choice
| Bibliography
| Index

► | 16 | Macros

16.1 | Pattern-Based Macros
16.2 | General Macro Transformers
16.3 | Module Instantiations and Visits

▼ | 16.2 | General Macro Transformers

16.2.1 | Syntax Objects
16.2.2 | Macro Transformer Procedures
16.2.3 | Mixing Patterns and Expressions: syntax-case
16.2.4 | with-syntax and generate-temporaries
16.2.5 | Compile and Run-Time Phases
16.2.6 | General Phase Levels
16.2.7 | Tainted Syntax

top contents ← prev up next →

##### 16.2.3`` Mixing Patterns and Expressions: syntax-case🔗ℹ

The procedure generated by syntax-rules internally uses syntax-e to deconstruct the given syntax object, and it uses datum->syntax to construct the result. The syntax-rules form doesn’t provide a way to escape from pattern-matching and template-construction mode into an arbitrary Racket expression.

The syntax-case form lets you mix pattern matching, template construction, and arbitrary expressions:

> > (syntax-case stx-expr (literal-id ...)
> > [pattern expr]
> > ...)

Unlike syntax-rules, the syntax-case form does not produce a procedure. Instead, it starts with a stx-expr expression that determines the syntax object to match against the patterns. Also, each syntax-case clause has a pattern and expr, instead of a pattern and template. Within an expr, the syntax form—usually abbreviated with #'—shifts into template-construction mode; if the expr of a clause starts with #', then we have something like a syntax-rules form:

> > (syntax->datum
> (syntax-case #'(+ 1 2) ()
> [(op n1 n2) #'(- n1 n2)]))
> '(- 1 2)

We could write the swap macro using syntax-case instead of define-syntax-rule or syntax-rules:

> (define-syntax (swap stx)
> (syntax-case stx ()
> [(swap x y) #'(let ([tmp x])
> (set! x y)
> (set! y tmp))]))

One advantage of using syntax-case is that we can provide better error reporting for swap. For example, with the define-syntax-rule definition of swap, then (swap x 2) produces a syntax error in terms of set!, because 2 is not an identifier. We can refine our syntax-case implementation of swap to explicitly check the sub-forms:

> (define-syntax (swap stx)
> (syntax-case stx ()
> [(swap x y)
> (if (and (identifier? #'x)
> (identifier? #'y))
> #'(let ([tmp x])
> (set! x y)
> (set! y tmp))
> (raise-syntax-error #f
> "not an identifier"
> stx
> (if (identifier? #'x)
> #'y
> #'x)))]))

With this definition, (swap x 2) provides a syntax error originating from swap instead of set!.

In the above definition of swap, #'x and #'y are templates, even though they are not used as the result of the macro transformer. This example illustrates how templates can be used to access pieces of the input syntax, in this case for checking the form of the pieces. Also, the match for #'x or #'y is used in the call to raise-syntax-error, so that the syntax-error message can point directly to the source location of the non-identifier.

top contents ← prev up next →