8.2.5 More keyword arguments

► |  | Syntax: Meta-Programming Helpers

1 | Syntax Object Helpers
2 | Module-Processing Helpers
3 | Macro Transformer Helpers
4 | Reader Helpers
5 | Non-Module Compilation And Expansion
6 | Trusting Standard Recertifying Transformers
7 | Attaching Documentation to Exports
8 | Parsing and specifying syntax
| Index

► | 8 | Parsing and specifying syntax

8.1 | Introduction
8.2 | Examples
8.3 | Parsing syntax
8.4 | Specifying syntax with syntax classes
8.5 | Syntax patterns
8.6 | Literal sets and Conventions
8.7 | Library syntax classes and literal sets
8.8 | Debugging and inspection tools
8.9 | Experimental

▼ | 8.2 | Examples

8.2.1 | Modules and reusable syntax classes
8.2.2 | Optional keyword arguments
8.2.3 | Variants with uniform meanings
8.2.4 | Variants with varied meanings
8.2.5 | More keyword arguments
8.2.6 | Experimental: Contracts on macro sub-expressions

top← prev up next →

##### 8.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 contraints is with a side condition using #:fail-when.

top← prev up next →