;; lib/scheme/tests/macros.sx — define-syntax + syntax-rules. (define scm-mac-pass 0) (define scm-mac-fail 0) (define scm-mac-fails (list)) (define scm-mac-test (fn (name actual expected) (if (= actual expected) (set! scm-mac-pass (+ scm-mac-pass 1)) (begin (set! scm-mac-fail (+ scm-mac-fail 1)) (append! scm-mac-fails {:name name :actual actual :expected expected}))))) (define scm-mac (fn (src) (scheme-eval-program (scheme-parse-all src) (scheme-standard-env)))) ;; ── Basic syntax-rules ────────────────────────────────────────── (scm-mac-test "my-if true" (scm-mac "(define-syntax my-if (syntax-rules () ((_ c t e) (cond (c t) (else e)))))\n (my-if #t 'yes 'no)") "yes") (scm-mac-test "my-if false" (scm-mac "(define-syntax my-if (syntax-rules () ((_ c t e) (cond (c t) (else e)))))\n (my-if #f 'yes 'no)") "no") (scm-mac-test "double" (scm-mac "(define-syntax double (syntax-rules () ((_ x) (+ x x))))\n (double 21)") 42) (scm-mac-test "nested macro use" (scm-mac "(define-syntax double (syntax-rules () ((_ x) (+ x x))))\n (double (double 5))") 20) ;; ── Macro with multiple rules ─────────────────────────────────── (scm-mac-test "multi-rule: matches first" (scm-mac "(define-syntax twin (syntax-rules () ((_ a) a) ((_ a b) (+ a b))))\n (twin 7)") 7) (scm-mac-test "multi-rule: matches second" (scm-mac "(define-syntax twin (syntax-rules () ((_ a) a) ((_ a b) (+ a b))))\n (twin 3 4)") 7) ;; ── Macros wrapping control flow ──────────────────────────────── (scm-mac-test "swap idiom" (scm-mac "(define-syntax swap! (syntax-rules () ((_ a b) (let ((tmp a)) (set! a b) (set! b tmp)))))\n (define x 1) (define y 2)\n (swap! x y)\n (list x y)") (list 2 1)) ;; ── Macros that expand to expressions, not values ────────────── (scm-mac-test "my-unless: true → empty" (scm-mac "(define-syntax my-unless (syntax-rules () ((_ c body) (if c 'skipped body))))\n (my-unless #t 99)") "skipped") (scm-mac-test "my-unless: false → body" (scm-mac "(define-syntax my-unless (syntax-rules () ((_ c body) (if c 'skipped body))))\n (my-unless #f 99)") 99) ;; ── Macro with literal keyword ───────────────────────────────── (scm-mac-test "literal: => recognised" (scm-mac "(define-syntax tag-arrow (syntax-rules (=>) ((_ a => b) (list 'arrow a b))))\n (tag-arrow 1 => 2)") (list "arrow" 1 2)) ;; ── Macro keyword passed through unevaluated ──────────────────── (scm-mac-test "list expansion preserves arg order" (scm-mac "(define-syntax tuple (syntax-rules () ((_ a b c) (list a b c))))\n (tuple 1 2 3)") (list 1 2 3)) ;; ── Macros + lambdas ──────────────────────────────────────────── (scm-mac-test "macro inside lambda" (scm-mac "(define-syntax sq (syntax-rules () ((_ x) (* x x))))\n (define (f n) (+ (sq n) 1))\n (f 5)") 26) ;; ── Ellipsis patterns (Phase 6b — tail-rest single-variable) ──── (scm-mac-test "ellipsis: empty rest" (scm-mac "(define-syntax my-list (syntax-rules () ((_ xs ...) (list xs ...)))) (my-list)") (list)) (scm-mac-test "ellipsis: list of values" (scm-mac "(define-syntax my-list (syntax-rules () ((_ xs ...) (list xs ...)))) (my-list 1 2 3 4)") (list 1 2 3 4)) (scm-mac-test "ellipsis: my-when truthy" (scm-mac "(define-syntax my-when (syntax-rules () ((_ c body ...) (if c (begin body ...))))) (my-when #t 1 2 3)") 3) (scm-mac-test "ellipsis: my-when falsy returns nil" (scm-mac "(define-syntax my-when (syntax-rules () ((_ c body ...) (if c (begin body ...))))) (my-when #f 1 2 3)") nil) (scm-mac-test "ellipsis: begin-rebuild" (scm-mac "(define-syntax my-begin (syntax-rules () ((_ body ...) (let () body ...)))) (my-begin (define x 5) (define y 10) (+ x y))") 15) (scm-mac-test "ellipsis: variadic sum-em via fold" (scm-mac "(define-syntax sum-em (syntax-rules () ((_ xs ...) (fold-left + 0 (list xs ...))))) (sum-em 1 2 3 4 5)") 15) (scm-mac-test "ellipsis: recursive my-and" (scm-mac "(define-syntax my-and (syntax-rules () ((_) #t) ((_ x) x) ((_ x xs ...) (if x (my-and xs ...) #f)))) (my-and 1 2 3)") 3) (scm-mac-test "ellipsis: my-and short-circuits" (scm-mac "(define-syntax my-and (syntax-rules () ((_) #t) ((_ x) x) ((_ x xs ...) (if x (my-and xs ...) #f)))) (my-and 1 #f 3)") false) (define scm-mac-tests-run! (fn () {:total (+ scm-mac-pass scm-mac-fail) :passed scm-mac-pass :failed scm-mac-fail :fails scm-mac-fails}))