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 . "
…