syntax-rules
Hygiene has two guarantees: (1) identifiers introduced by the macro (like tmp) don't capture user variables; (2) free identifiers in the template (like let, if) refer to their bindings where the macro was defined, even if the user rebinds them locally.
;; (define-syntax name (syntax-rules (literals ...) (pattern template) ...))
(define-syntax my-unless
(syntax-rules ()
((_ test body ...) ; _ matches the macro keyword; `...` matches zero or more
(if test #f (begin body ...)))))
(my-unless (> 1 2) (display "1 is not greater than 2"))
(define-syntax swap!
(syntax-rules ()
((_ a b) (let ((tmp a)) (set! a b) (set! b tmp)))))
(define tmp 1) (define other 2)
(swap! tmp other) ; works: hygiene keeps the macro's tmp separate
(list tmp other) ; (2 1)
;; Multiple patterns, recursion
(define-syntax my-or
(syntax-rules ()
((_) #f)
((_ e) e)
((_ e rest ...) (let ((t e)) (if t t (my-or rest ...))))))
(my-or #f #f 3) ; 3
(define-syntax my-let*
(syntax-rules ()
((_ () body ...) (let () body ...))
((_ ((name val) rest ...) body ...)
(let ((name val)) (my-let* (rest ...) body ...)))))
(my-let* ((x 1) (y (+ x 1))) (* x y)) ; 2
;; Literals: keywords that must appear verbatim
(define-syntax for
(syntax-rules (in from to)
((_ x in lst body ...) (for-each (lambda (x) body ...) lst))
((_ x from start to end body ...)
…