scheme4r 0.2.3

Scheme interpreter for rust
Documentation
use std::collections::HashMap;

use scheme4r::{eval::Engine, BuiltinFn, Environment, ErrorKind, Scheme, SchemeError, Value};

fn host_double(_: &Engine, args: &[Value]) -> Result<Value, SchemeError> {
    if args.len() != 1 {
        return Err(SchemeError::new(
            ErrorKind::Arity,
            "'double' expects exactly 1 argument",
            None,
        ));
    }

    match &args[0] {
        Value::Number(number) => Ok(Value::Number(number * 2)),
        other => Err(SchemeError::new(
            ErrorKind::Type,
            format!("'double' expected a number, got {other}"),
            None,
        )),
    }
}

#[test]
fn scheme_object_reuses_state_across_eval_calls() {
    let scheme = Scheme::with_env(Environment::standard());

    scheme.eval("(define counter 40)").unwrap();

    let value = scheme
        .eval("(begin (set! counter (+ counter 2)) counter)")
        .unwrap();
    assert!(matches!(value, Value::Number(42)));

    let value = scheme.eval("counter").unwrap();
    assert!(matches!(value, Value::Number(42)));
}

#[test]
fn default_scheme_uses_standard_environment() {
    let scheme = Scheme::default();
    let value = scheme.eval("(+ 1 2 3)").unwrap();
    assert!(matches!(value, Value::Number(6)));
}

#[test]
fn scheme_new_accepts_host_builtin_registry() {
    let mut builtins = HashMap::new();
    builtins.insert("double".to_string(), host_double as BuiltinFn);

    let scheme = Scheme::new(builtins);
    let value = scheme.eval("(double 21)").unwrap();
    assert!(matches!(value, Value::Number(42)));
}