1.2.5 More Keyword Arguments

► |  | 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 | Source Locations
9 | Preserving Source Locations
10 | Non-Module Compilation And Expansion
11 | Trusting Standard Recertifying Transformers
12 | Attaching Documentation to Exports
| 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 | Debugging and Inspection Tools
1.10 | Experimental

▼ | 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 ← prev up next →

##### 1.2.5`` More Keyword Arguments

This section shows how to express the syntax of struct’s optional keyword arguments using syntax-parse patterns.

The part of struct’s syntax that is difficult to specify is the sequence of struct options. Let’s get the easy part out of the way first.

> > (define-splicing-syntax-class maybe-super
> (pattern (~seq super:id))
> (pattern (~seq)))
> > (define-syntax-class field-option
> (pattern #:mutable)
> (pattern #:auto))
> > (define-syntax-class field
> (pattern field:id
> #:with (option ...) '())
> (pattern [field:id option:field-option ...]))

Given those auxiliary syntax classes, here is a first approximation of the main pattern, including the struct options:

> (struct name:id super:maybe-super (field:field ...)
> (~or (~seq #:mutable)
> (~seq #:super super-expr:expr)
> (~seq #:inspector inspector:expr)
> (~seq #:auto-value auto:expr)
> (~seq #:guard guard:expr)
> (~seq #:property prop:expr prop-val:expr)
> (~seq #:transparent)
> (~seq #:prefab)
> (~seq #:constructor-name constructor-name:id)
> (~seq #:extra-constructor-name extra-constructor-name:id)
> (~seq #:omit-define-syntaxes)
> (~seq #:omit-define-values))
> ...)

The fact that expr does not match keywords helps in the case where the programmer omits a keyword’s argument; instead of accepting the next keyword as the argument expression, syntax-parse reports that an expression was expected.

There are two main problems with the pattern above:

- There’s no way to tell whether a zero-argument keyword like #:mutable was seen.

- Some options, like #:mutable, should appear at most once.

The first problem can be remedied using ~and patterns to bind a pattern variable to the keyword itself, as in this sub-pattern:

> (~seq (~and #:mutable mutable-kw))

The second problem can be solved using repetition constraints:

> (struct name:id super:maybe-super (field:field ...)
> (~or (~optional (~seq (~and #:mutable) mutable-kw))
> (~optional (~seq #:super super-expr:expr))
> (~optional (~seq #:inspector inspector:expr))
> (~optional (~seq #:auto-value auto:expr))
> (~optional (~seq #:guard guard:expr))
> (~seq #:property prop:expr prop-val:expr)
> (~optional (~seq (~and #:transparent transparent-kw)))
> (~optional (~seq (~and #:prefab prefab-kw)))
> (~optional (~seq #:constructor-name constructor-name:id))
> (~optional
> (~seq #:extra-constructor-name extra-constructor-name:id))
> (~optional
> (~seq (~and #:omit-define-syntaxes omit-def-stxs-kw)))
> (~optional (~seq (~and #:omit-define-values omit-def-vals-kw))))
> ...)

The ~optional repetition constraint indicates that an alternative can appear at most once. (There is a ~once form that means it must appear exactly once.) In struct’s keyword options, only #:property may occur any number of times.

There are still some problems, though. Without additional help, ~optional does not report particularly good errors. We must give it the language to use, just as we had to give descriptions to sub-patterns via syntax classes. Also, some related options are mutually exclusive, such as #:inspector, #:transparent, and #:prefab.

> (struct name:id super:maybe-super (field:field ...)
> (~or (~optional
> (~or (~seq #:inspector inspector:expr)
> (~seq (~and #:transparent transparent-kw))
> (~seq (~and #:prefab prefab-kw)))
> #:name "#:inspector, #:transparent, or #:prefab option")
> (~optional (~seq (~and #:mutable) mutable-kw)
> #:name "#:mutable option")
> (~optional (~seq #:super super-expr:expr)
> #:name "#:super option")
> (~optional (~seq #:auto-value auto:expr)
> #:name "#:auto-value option")
> (~optional (~seq #:guard guard:expr)
> #:name "#:guard option")
> (~seq #:property prop:expr prop-val:expr)
> (~optional (~seq #:constructor-name constructor-name:id)
> #:name "#:constructor-name option")
> (~optional
> (~seq #:extra-constructor-name extra-constructor-name:id)
> #:name "#:extra-constructor-name option")
> (~optional (~seq (~and #:omit-define-syntaxes omit-def-stxs-kw))
> #:name "#:omit-define-syntaxes option")
> (~optional (~seq (~and #:omit-define-values omit-def-vals-kw))
> #:name "#:omit-define-values option"))
> ...)

Here we have grouped the three incompatible options together under a single ~optional constraint. That means that at most one of any of those options is allowed. We have given names to the optional clauses. See ~optional for other customization options.

Note that there are other constraints that we have not represented in the pattern. For example, #:prefab is also incompatible with both #:guard and #:property. Repetition constraints cannot express arbitrary incompatibility relations. The best way to handle such constraints is with a side condition using #:fail-when.

top ← prev up next →