Next: baselib tail call, Previous: baselib syntax binding, Up: baselib [Index]
Each ?literal must be an identifier. Each ?syntax-rule must have the following form:
(?srpattern ?template)
An ?srpattern is a restricted form of ?pattern, namely, a nonempty ?pattern in one of four parenthesized forms below whose first subform is an identifier or an underscore ‘_’. A ?pattern is an identifier, constant, or one of the following.
(?pattern …) (?pattern ?pattern … . ?pattern) (?pattern … ?pattern ?ellipsis ?pattern …) (?pattern … ?pattern ?ellipsis ?pattern … . ?pattern) #(?pattern …) #(?pattern … ?pattern ?ellipsis ?pattern …)
An ?ellipsis is the identifier ‘...’ (three periods).
A ?template is a pattern variable, an identifier that is not a pattern variable, a pattern datum, or one of the following.
(?subtemplate …) (?subtemplate … . ?template) #(?subtemplate …)
A ?subtemplate is a ?template followed by zero or more ellipses.
An instance of syntax-rules
evaluates, at macro–expansion time,
to a new macro transformer by specifying a sequence of hygienic rewrite
rules. A use of a macro whose keyword is associated with a transformer
specified by syntax-rules
is matched against the patterns
contained in the ?syntax-rules, beginning with the leftmost
?syntax-rule. When a match is found, the macro use is transcribed
hygienically according to the template. It is a syntax violation when
no match is found.
An identifier appearing within a ?pattern may be an underscore
‘_’, an ellipsis ‘...’ or a literal identifier listed in the
list of literals (?literal …)
. All other identifiers
appearing within a ?pattern are pattern variables. It is a
syntax violation if an ellipsis or underscore appears in
(?literal …)
.
While the first subform of ?srpattern may be an identifier, the identifier is not involved in the matching and is not considered a pattern variable or literal identifier.
Pattern variables match arbitrary input subforms and are used to refer to elements of the input. It is a syntax violation if the same pattern variable appears more than once in a ?pattern.
Underscores also match arbitrary input subforms but are not pattern variables and so cannot be used to refer to those elements. Multiple underscores may appear in a ?pattern.
A ?literal identifier matches an input subform if and only if the input subform is an identifier and either both its occurrence in the input expression and its occurrence in the list of literals have the same lexical binding, or the two identifiers have the same name and both have no lexical binding.
A subpattern followed by an ellipsis can match zero or more elements of the input.
More formally, an input form F matches a pattern P if and only if one of the following holds:
(P_1 … P_n)
and F is a list of n elements that match P_1 through P_n.
(P_1 … P_n . P_x)
and F is a list or improper list of n or more elements whose first n elements match P_1 through P_n and whose n-th cdr matches P_x.
(P_1 … P_k P_e ?ellipsis P_(m+1) … P_n)
where ?ellipsis is the identifier ‘...’ and F is a list of n elements whose first k elements match P_1 through P_k, whose next m-k elements each match P_e, and whose remaining n-m elements match P_(m+1) through P_n.
(P_1 … P_k P_e ?ellipsis P_(m+1) … P_n . P_x)
where ?ellipsis is the identifier ‘...’ and F is a list or improper list of n elements whose first k elements match P_1 through P_k, whose next m-k elements each match P_e, whose next n-m elements match P_(m+1) through P_n, and whose nth and final cdr matches P_x.
#(P_1 … P_n)
and F is a vector of n elements that match P_1 through P_n.
#(P_1 … P_k P_e ?ellipsis P_(m+1) … P_n)
where ?ellipsis is the identifier ‘...’ and F is a vector of n or more elements whose first k elements match P_1 through P_k, whose next m-k elements each match P_e, and whose remaining n-m elements match P_(m+1) through P_n.
equal?
procedure.
When a macro use is transcribed according to the template of the matching ?syntax-rule, pattern variables that occur in the template are replaced by the subforms they match in the input.
Pattern data and identifiers that are not pattern variables or ellipses are copied into the output. A subtemplate followed by an ellipsis expands into zero or more occurrences of the subtemplate. Pattern variables that occur in subpatterns followed by one or more ellipses may occur only in subtemplates that are followed by (at least) as many ellipses. These pattern variables are replaced in the output by the input subforms to which they are bound, distributed as specified. If a pattern variable is followed by more ellipses in the subtemplate than in the associated subpattern, the input form is replicated as necessary. The subtemplate must contain at least one pattern variable from a subpattern followed by an ellipsis, and for at least one such pattern variable, the subtemplate must be followed by exactly as many ellipses as the subpattern in which the pattern variable appears. (Otherwise, the expander would not be able to determine how many times the subform should be repeated in the output.) It is a syntax violation if the constraints of this paragraph are not met.
A template of the form (?ellipsis ?template)
is
identical to ?template, except that ellipses within the template
have no special meaning. That is, any ellipses contained within
?template are treated as ordinary identifiers. In particular, the
template (... ...)
produces a single ellipsis, ‘...’. This
allows syntactic abstractions to expand into forms containing ellipses.
(define-syntax be-like-begin (syntax-rules () ((be-like-begin name) (define-syntax name (syntax-rules () ((name expr (... ...)) (begin expr (... ...)))))))) (be-like-begin sequence) (sequence 1 2 3 4) ⇒ 4
As an example for hygienic use of auxiliary identifier, if let
and cond
are defined as in let
and cond then they are hygienic (as required) and the
following is not an error.
(let ((=> #f)) (cond (#t => 'ok))) ⇒ ok
The macro transformer for cond
recognizes ‘=>’ as a local
variable, and hence an expression, and not as the identifier ‘=>’,
which the macro transformer treats as a syntactic keyword. Thus the
example expands into:
(let ((=> #f)) (if #t (begin => 'ok)))
instead of:
(let ((=> #f)) (let ((temp #t)) (if temp ('ok temp))))
which would result in an assertion violation.
The ?ids must be identifiers. The ?templates must be as for
syntax-rules
.
When a keyword is bound to a transformer produced by the first form of
identifier-syntax
, references to the keyword within the scope of
the binding are replaced by ?template.
(define p (cons 4 5)) (define-syntax p.car (identifier-syntax (car p))) p.car ⇒ 4 (set! p.car 15) error→ exception &syntax
The second, more general, form of identifier-syntax
permits the
transformer to determine what happens when ‘set!’ is used. In this
case, uses of the identifier by itself are replaced by
?template1, and uses of ‘set!’ with the identifier are
replaced by ?template2.
(define p (cons 4 5)) (define-syntax p.car (identifier-syntax (_ (car p)) ((set! _ e) (set-car! p e)))) (set! p.car 15) p.car ⇒ 15 p ⇒ (15 . 5)
Here we give a little explanation on the (?literal …)
arguments of syntax-rules
. Scheme defines the if
syntax
as:
(if ?test ?consequent ?alternate)
where the only “language keyword” is if
itself. Many other
languages use a version with then
and else
, which are also
“reserved keywords” for those languages; Scheme has no reserved
keywords.
A Scheme if
with then
and else
, let’s call it
if*
, would look like:
(if* ?test (then ?consequent) (else ?alternate))
we can define it with the following simple syntax-rules
:
#!r6rs (import (rnrs)) (define-syntax if* (syntax-rules () ((_ ?test (?then ?consequent) (?else ?alternate)) (if ?test ?consequent ?alternate)))) (let ((a 2)) (if* (< 1 a) (then (display "yeah")) (else (display "nay")))) -| yeah
but notice that in this definition the pattern is:
(_ ?test (?then ?consequent) (?else ?alternate))
and the identifiers ?THEN
and ?ELSE
become pattern
variables, with no validation of their value; so the following macro use
also works with no error:
(let ((a 2)) (if* (< 1 a) (123 (display "yeah")) (456 (display "nay"))))
that is in the template of the first arm of syntax-rules
:
?then
is a pattern variable bound to a syntax
object holding the number 123.
?else
is a pattern variable bound to a syntax
object holding the number 456.
we can verify it with the following modified program:
#!r6rs (import (rnrs)) (define-syntax if* (syntax-rules () ((_ ?test (?then ?consequent) (?else ?alternate)) (display ?then)))) (let ((a 2)) (if* (< 1 a) (123 (display "yeah")) (456 (display "nay")))) -| 123
This is not what we want: we would like the macro to verify that the
components of the input form falling in the positions of ?then
and ?else
in the pattern, are identifiers having as names the
symbol then
and the symbol else
.
Specifically we would like, in those positions, identifiers being
free-identifier=?
to “auxiliary keywords” then
and
else
that we have defined. We could do such a test using
syntax-case
:
#!r6rs (import (except (rnrs) else)) ;;Define auxiliary keywords, they are just identifiers ;;bound to something. (define-syntax then (syntax-rules ())) (define-syntax else (syntax-rules ())) (define-syntax if* (lambda (stx) (syntax-case stx () ((_ ?test (?then ?consequent) (?else ?alternate)) (begin (if (and (identifier? #'?then) (free-identifier=? #'?then #'then)) (display 'good) (display 'bad)) #f))))) (let ((a 2)) (if* (< 1 a) (then (display "yeah")) (else (display "nay")))) -| good (let ((a 2)) (if* (< 1 a) (123 (display "yeah")) (456 (display "nay")))) -| bad
We do not need to use syntax-case
for such a simple validation,
because syntax-rules
offers the ?literal arguments exactly
for this purpose. So the full version of ‘IF... THEN... ELSE...’
is:
#!r6rs (import (except (rnrs) else)) (define-syntax if* (syntax-rules (then else) ((_ ?test (then ?consequent) (else ?alternate)) (if ?test ?consequent ?alternate)))) (define-syntax then (syntax-rules ())) (define-syntax else (syntax-rules ())) (let ((a 2)) (if* (< 1 a) (then (display "yeah")) (else (display "nay")))) -| yeah (let ((a 2)) (if* (< 1 a) (123 (display "yeah")) (456 (display "nay")))) error→ syntax error
The language (rnrs)
already defines an auxiliary syntax
else
and it is all right to use it for if*
so we can just
do:
#!r6rs (import (rnrs)) (define-syntax if* (syntax-rules (then else) ((_ ?test (then ?consequent) (else ?alternate)) (if ?test ?consequent ?alternate)))) (define-syntax then (syntax-rules ()))
Notice that with these definitions we have created actual bindings for
then
and else
in the lexical context of the definition for
if*
, and if*
will recognise only those. So when writing a
library that exports if*
we have to export then
and
else
along with it:
#!r6rs (library (if-star) (export if* then else) (import (rnrs)) (define-syntax if* (syntax-rules (then else) ((_ ?test (then ?consequent) (else ?alternate)) (if ?test ?consequent ?alternate)))) (define-syntax then (syntax-rules ())))
and use it as:
#!r6rs (import (rnrs) (if-star)) (let ((a 2)) (if* (< 1 a) (then (display "yeah")) (else (display "nay")))) -| yeah (let ((a 2)) (if* (< 1 a) (123 (display "yeah")) (456 (display "nay")))) error→ syntax error
Next: baselib tail call, Previous: baselib syntax binding, Up: baselib [Index]