updated syntax/parse docs
This commit is contained in:
parent
bf5248e3b5
commit
aac8be59ac
|
@ -33,28 +33,19 @@ Here is the specification of @scheme[mylet]'s syntax:
|
|||
For simplicity, we handle only the first case for now. We return to
|
||||
the second case later in the introduction.
|
||||
|
||||
First, we import @scheme[syntax-parse] into the @tech[#:doc '(lib
|
||||
"scribblings/reference/reference.scrbl")]{transformer environment},
|
||||
since we will use it to implement a macro transformer.
|
||||
|
||||
@myinteraction[(require (for-syntax syntax/parse))]
|
||||
|
||||
We get the first version of @scheme[mylet] by essentially
|
||||
transliterating the syntax specification above. The result is similar
|
||||
to what one would write using @scheme[syntax-rules] or perhaps
|
||||
@scheme[syntax-case].
|
||||
The macro can be implemented very simply using
|
||||
@racket[define-syntax-rule]:
|
||||
|
||||
@myinteraction[
|
||||
(define-syntax (mylet stx)
|
||||
(syntax-parse stx
|
||||
[(_ ([var-id rhs-expr] ...) body ...+)
|
||||
#'((lambda (var-id ...) body ...) rhs-expr ...)]))
|
||||
(define-syntax-rule (mylet ([var rhs] ...) body ...)
|
||||
((lambda (var ...) body ...) rhs ...))
|
||||
]
|
||||
|
||||
Note the use of @scheme[...] and @scheme[...+] in the pattern;
|
||||
@scheme[...] means match zero or more repetitions of the preceding
|
||||
pattern; @scheme[...+] means match one or more. Only @scheme[...] may
|
||||
be used in the template, however.
|
||||
When used correctly, the macro works, but it behaves very badly in the
|
||||
presence of errors. In some cases, the macro merely fails with an
|
||||
uninformative error message; in others, it blithely accepts illegal
|
||||
syntax and passes it along to @scheme[lambda], with strange
|
||||
consequences:
|
||||
|
||||
@myinteraction[
|
||||
(mylet ([a 1] [b 2]) (+ a b))
|
||||
|
@ -63,14 +54,9 @@ be used in the template, however.
|
|||
(mylet ([#:x 1] [y 2]) (* x y))
|
||||
]
|
||||
|
||||
When used correctly, the macro works, but it behaves very badly in the
|
||||
presence of errors. In some cases, @scheme[mylet] blithely accepts
|
||||
illegal syntax and passes it along to @scheme[lambda], with strange
|
||||
consequences.
|
||||
|
||||
These examples of illegal syntax are not to suggest that a typical
|
||||
programmer would make such mistakes attempting to use
|
||||
@scheme[mylet]. At least, not often. After an initial learning
|
||||
@scheme[mylet]. At least, not often, not after an initial learning
|
||||
curve. But macros are also used by inexpert programmers and as targets
|
||||
of other macros (or code generators), and many macros are far more
|
||||
complex than @scheme[mylet]. Macros must validate their syntax and
|
||||
|
@ -78,6 +64,30 @@ report appropriate errors. Furthermore, the macro writer benefits from
|
|||
the @emph{machine-checked} specification of syntax in the form of more
|
||||
readable, maintainable code.
|
||||
|
||||
We can improve the error behavior of the macro by using
|
||||
@racket[syntax-parse]. First, we import @scheme[syntax-parse] into the
|
||||
@tech[#:doc '(lib
|
||||
"scribblings/reference/reference.scrbl")]{transformer environment},
|
||||
since we will use it to implement a macro transformer.
|
||||
|
||||
@myinteraction[(require (for-syntax syntax/parse))]
|
||||
|
||||
The following is the syntax specification above transliterated into a
|
||||
@racket[syntax-parse] macro definition. It behaves no better than the
|
||||
version using @racket[define-syntax-rule] above.
|
||||
|
||||
@myinteraction[
|
||||
(define-syntax (mylet stx)
|
||||
(syntax-parse stx
|
||||
[(_ ([var-id rhs-expr] ...) body ...+)
|
||||
#'((lambda (var-id ...) body ...) rhs-expr ...)]))
|
||||
]
|
||||
|
||||
One minor difference is the use of @scheme[...+] in the pattern;
|
||||
@scheme[...] means match zero or more repetitions of the preceding
|
||||
pattern; @scheme[...+] means match one or more. Only @scheme[...] may
|
||||
be used in the template, however.
|
||||
|
||||
The first step toward validation and high-quality error reporting is
|
||||
annotating each of the macro's pattern variables with the @tech{syntax
|
||||
class} that describes its acceptable syntax. In @scheme[mylet], each
|
||||
|
|
|
@ -679,7 +679,10 @@ terms instead.
|
|||
@specsubform/subs[(@#,def[~optional h] H-pattern maybe-optional-option)
|
||||
([maybe-optional-option
|
||||
(code:line)
|
||||
(code:line #:defaults ([attr-id expr] ...))])]{
|
||||
(code:line #:defaults ([attr-arity-decl expr] ...))]
|
||||
[attr-arity-decl
|
||||
attr-id
|
||||
(attr-id depth)])]{
|
||||
|
||||
Matches either the given head subpattern or an empty sequence of
|
||||
terms. If the @scheme[#:defaults] option is given, the subsequent
|
||||
|
@ -696,8 +699,11 @@ default attributes must be a subset of the subpattern's attributes.
|
|||
(syntax-parse #'(m a b c)
|
||||
[(_ (~optional (~seq #:foo x)) y:id ...)
|
||||
(attribute x)])
|
||||
(syntax-parse #'(m #:syms a b c)
|
||||
[(_ (~optional (~seq #:nums n:nat ...) #:defaults ([(n 1) null]))
|
||||
(~optional (~seq #:syms s:id ...) #:defaults ([(s 1) null])))
|
||||
#'((n ...) (s ...))])
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
@specsubform[(@#,def[~describe h] expr H-pattern)]{
|
||||
|
|
Loading…
Reference in New Issue
Block a user