scheme4r 0.2.3

Scheme interpreter for rust
Documentation
use scheme4r::{eval, Value};

#[test]
fn define_syntax_when_expands_and_runs() {
    let value = eval(
        "\
        (define x 0)
        (define-syntax when
          (syntax-rules ()
            ((_ test body ...)
             (if test (begin body ...)))))
        (when #t
          (set! x 41)
          (set! x (+ x 1)))
        x
        ",
    )
    .unwrap();

    assert!(matches!(value, Value::Number(42)));
}

#[test]
fn syntax_rules_support_recursive_variadic_macros() {
    let value = eval(
        "\
        (define-syntax and
          (syntax-rules ()
            ((_ ) #t)
            ((_ x) x)
            ((_ x y ...) (if x (and y ...) x))))
        (and #t 1 2 3)
        ",
    )
    .unwrap();

    assert!(matches!(value, Value::Number(3)));
}

#[test]
fn syntax_rules_support_literal_identifiers() {
    let value = eval(
        "\
        (define-syntax my-cond
          (syntax-rules (else)
            ((_ (else expr)) expr)
            ((_ (test expr) rest ...)
             (if test expr (my-cond rest ...)))))
        (my-cond ((> 1 2) 11) (else 99))
        ",
    )
    .unwrap();

    assert!(matches!(value, Value::Number(99)));
}

#[test]
fn macros_can_expand_into_bindings_and_lists() {
    let value = eval(
        "\
        (define-syntax push-front
          (syntax-rules ()
            ((_ item lst)
             (set! lst (cons item lst)))))
        (define values '(2 3))
        (push-front 1 values)
        values
        ",
    )
    .unwrap();

    assert_eq!(format!("{value}"), "(1 2 3)");
}

#[test]
fn let_syntax_creates_local_macro_scope() {
    let value = eval(
        "\
        (let-syntax
          ((swap
             (syntax-rules ()
               ((_ a b)
                (let ((tmp a))
                  (set! a b)
                  (set! b tmp))))))
          (let ((x 1) (y 2))
            (swap x y)
            (list x y)))
        ",
    )
    .unwrap();

    assert_eq!(format!("{value}"), "(2 1)");
    assert!(eval("(swap 1 2)").is_err());
}

#[test]
fn letrec_syntax_supports_local_recursive_macros() {
    let value = eval(
        "\
        (letrec-syntax
          ((my-or
             (syntax-rules ()
               ((_ ) #f)
               ((_ x) x)
               ((_ x y ...) (let ((tmp x)) (if tmp tmp (my-or y ...)))))))
          (my-or #f #f 7 #f))
        ",
    )
    .unwrap();

    assert!(matches!(value, Value::Number(7)));
}