syntax-case and Procedural Macros

Key procedures: syntax->datum, datum->syntax, identifier?, free-identifier=?, bound-identifier=?, generate-temporaries, with-syntax, quasisyntax (#), unsyntax (#,`).

Racket's syntax-parse adds declarative syntax classes and excellent error messages.

Phases: procedural macro code runs at expansion time. Helpers must be available at that phase (R6RS (import (for (lib) expand)), Racket (require (for-syntax ...))).

;; R6RS / Chez / Guile / Racket: macros as procedures over syntax objects
(define-syntax my-if
  (lambda (stx)
    (syntax-case stx ()
      ((_ c t e) #'(cond (c t) (else e))))))   ; #'template = (syntax template)

;; Fenders (guards on patterns)
(define-syntax my-let
  (lambda (stx)
    (syntax-case stx ()
      ((_ ((name val) ...) body1 body2 ...)
       (for-all identifier? #'(name ...))      ; fender: all names must be identifiers (R6RS for-all)
       #'((lambda (name ...) body1 body2 ...) val ...)))))

;; Computing at expansion time: generate accessor names
(define-syntax define-getter
  (lambda (stx)
    (syntax-case stx ()
      ((_ record field)
       (with-syntax ((getter (datum->syntax #'record
                                            (string->symbol
                                              (string-append (symbol->string (syntax->datum #'record))
                                                             "-"
                                                             (symbol->string (syntax->datum #'field)))))))
         #'(define (getter obj) (cdr (assq 'field obj))))))))
(define-getter person name)            ; defines person-name
(person-name '((name . "
…