Next: , Previous: , Up: stdlib syntax-case   [Index]


5.12.5 Parsing input and producing output

Transformers can destructure their input with syntax-case and rebuild their output with syntax.

Syntax: syntax-case ?expression (?literal …) ?syntax-case-clause
Auxiliary Syntax: _
Auxiliary Syntax: ...

Each ?literal must be an identifier. Each ?syntax-case-clause must take one of the following two forms.

(?pattern ?output-expression)
(?pattern ?fender ?output-expression)

?fender and ?output-expression must be ?expressions.

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).

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 …)’.

_’ and ‘...’ are the same as in the (rnrs base (6)) library.

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:

A syntax-case expression first evaluates ?expression. It then attempts to match the ?pattern from the first ?syntax-case-clause against the resulting value, which is unwrapped as necessary to perform the match. If the pattern matches the value and no ?fender is present, ?output-expression is evaluated and its value returned as the value of the syntax-case expression. If the pattern does not match the value, syntax-case tries the second ?syntax-case-clause, then the third, and so on. It is a syntax violation if the value does not match any of the patterns.

If the optional ?fender is present, it serves as an additional constraint on acceptance of a clause. If the ?pattern of a given ?syntax-case-clause matches the input value, the corresponding ?fender is evaluated. If ?fender evaluates to a true value, the clause is accepted; otherwise, the clause is rejected as if the pattern had failed to match the value. Fenders are logically a part of the matching process, i.e., they specify additional matching constraints beyond the basic structure of the input.

Pattern variables contained within a clause’s ?pattern are bound to the corresponding pieces of the input value within the clause’s ?fender (if present) and ?output-expression. Pattern variables can be referenced only within syntax expressions (see below). Pattern variables occupy the same name space as program variables and keywords.

If the syntax-case form is in tail context, the ?output-expressions are also in tail position.

Syntax: syntax ?template

A syntax expression is similar to a quote expression except that:

  1. The values of pattern variables appearing within ?template are inserted into ?template.
  2. Contextual information associated both with the input and with the template is retained in the output to support lexical scoping.
  3. The value of a syntax expression is a syntax object.

The following sharp–quote expression:

#'?template

is equivalent to:

(syntax ?template)

scheme lex datum abbreviations for the other abbreviations.

A ?template can be one among: 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.

The value of a syntax form is a copy of ?template in which the pattern variables appearing within the template are replaced with the input subforms to which they are bound:

It is a syntax violation if the above constraints 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 macro uses to expand into forms containing ellipses.

The output produced by syntax is wrapped or unwrapped according to the following rules:

The input subforms inserted in place of the pattern variables are wrapped if and only if the corresponding input subforms are wrapped.

The following definitions of or illustrate syntax-case and syntax; the second is equivalent to the first but uses the sharp–quote prefix instead of the full syntax form:

(define-syntax or
  (lambda (x)
    (syntax-case x ()
      ((_)
       (syntax #f))
      ((_ e)
       (syntax e))
      ((_ e1 e2 e3 ...)
       (syntax (let ((t e1))
                 (if t t (or e2 e3 ...))))))))

(define-syntax or
  (lambda (x)
    (syntax-case x ()
      ((_)
       #'#f)
      ((_ e)
       #'e)
      ((_ e1 e2 e3 ...)
       #'(let ((t e1))
           (if t t (or e2 e3 ...)))))))

The examples below define identifier macros, macro uses supporting keyword references that do not necessarily appear in the first position of a list–structured form; the second example uses make-variable-transformer to handle the case where the keyword appears on the left-hand side of a set! expression:

(define p (cons 4 5))
(define-syntax p.car
  (lambda (x)
    (syntax-case x ()

      ((_ . rest)
       #'((car p) . rest))

      (_
       #'(car p)))))

p.car                   ⇒ 4
(set! p.car 15)         error→ exception &syntax

(define p (cons 4 5))
(define-syntax p.car
  (make-variable-transformer
    (lambda (x)
      (syntax-case x (set!)

        ((set! _ e)
         #'(set-car! p e))

        ((_ . rest)
         #'((car p) . rest))

        (_
         #'(car p))))))

(set! p.car 15)

p.car                   ⇒ 15
p                       ⇒ (15 . 5)

Any syntax-rules form can be expressed with syntax-case by making the lambda expression and syntax expressions explicit, and syntax-rules may be defined in terms of syntax-case as follows.

(define-syntax syntax-rules
  (lambda (x)
    (syntax-case x ()
      ((_ (lit ...) ((k . p) t) ...)
       (for-all identifier? #'(lit ... k ...))
       #'(lambda (x)
           (syntax-case x (lit ...)
             ((_ . p) #'t) ...))))))

The identifier-syntax form of the base library (see baselib transformers) may be defined in terms of syntax-case, syntax, and make-variable-transformer as follows.

(define-syntax identifier-syntax
  (syntax-rules (set!)
    ((_ e)
     (lambda (x)
       (syntax-case x ()
         (id (identifier? #'id) #'e)
         ((_ x (... ...)) #'(e x (... ...))))))
    ((_ (id exp1) ((set! var val) exp2))
     (and (identifier? #'id) (identifier? #'var))
     (make-variable-transformer
       (lambda (x)
         (syntax-case x (set!)
           ((set! var val) #'exp2)
           ((id x (... ...)) #'(exp1 x (... ...)))
           (id (identifier? #'id) #'exp1)))))))

Next: , Previous: , Up: stdlib syntax-case   [Index]