sim-nest 0.1.5

The SIM constellation umbrella facade: one crate that re-exports the runtime kernel, codecs, number domains, and libraries behind features.
Documentation
use std::sync::{
    Arc,
    atomic::{AtomicUsize, Ordering},
};

use sim_kernel::{
    Args, Cx, DefaultFactory, Demand, EagerPolicy, Expr, HybridPolicy, LazyPolicy, NeedPolicy,
    QuoteMode, StrictByShapePolicy, Symbol,
};

use crate::runtime::install_core_runtime;

use super::support::{
    TickCallable, call_expr, force_first_arg_twice_impl, ignore_arg_impl, one_arg_function,
    return_first_arg_impl, table_value, two_arg_function,
};

#[test]
fn demand_never_does_not_evaluate_exploding_argument() {
    let mut cx = Cx::new(Arc::new(HybridPolicy), Arc::new(DefaultFactory));
    install_core_runtime(&mut cx);
    let function = Symbol::qualified("test", "ignore");
    one_arg_function(&mut cx, function.clone(), Demand::Never, ignore_arg_impl);
    let result = cx
        .eval_expr(call_expr(
            function,
            vec![Expr::Call {
                operator: Box::new(Expr::Bool(true)),
                args: Vec::new(),
            }],
        ))
        .unwrap();
    assert_eq!(result.object().as_expr(&mut cx).unwrap(), Expr::Bool(true));
}

#[test]
fn demand_expr_receives_original_expression() {
    let mut cx = Cx::new(Arc::new(HybridPolicy), Arc::new(DefaultFactory));
    install_core_runtime(&mut cx);
    let function = Symbol::qualified("test", "expr");
    one_arg_function(
        &mut cx,
        function.clone(),
        Demand::Expr,
        return_first_arg_impl,
    );
    let original = Expr::Call {
        operator: Box::new(Expr::Symbol(Symbol::qualified("maybe", "later"))),
        args: vec![Expr::Bool(true)],
    };
    let result = cx
        .eval_expr(call_expr(function, vec![original.clone()]))
        .unwrap();
    assert_eq!(result.object().as_expr(&mut cx).unwrap(), original);
}

#[test]
fn lazy_policy_delays_unused_args_and_recomputes_forces() {
    let mut cx = Cx::new(Arc::new(LazyPolicy), Arc::new(DefaultFactory));
    install_core_runtime(&mut cx);
    let ignore_symbol = Symbol::qualified("test", "ignore");
    one_arg_function(
        &mut cx,
        ignore_symbol.clone(),
        Demand::Never,
        ignore_arg_impl,
    );
    let result = cx
        .eval_expr(call_expr(
            ignore_symbol,
            vec![Expr::Call {
                operator: Box::new(Expr::Bool(true)),
                args: Vec::new(),
            }],
        ))
        .unwrap();
    assert_eq!(result.object().as_expr(&mut cx).unwrap(), Expr::Bool(true));

    let counter = Arc::new(AtomicUsize::new(0));
    let tick = cx
        .factory()
        .opaque(Arc::new(TickCallable {
            counter: counter.clone(),
        }))
        .unwrap();
    let tick_symbol = Symbol::qualified("test", "tick");
    cx.env_mut().define(tick_symbol.clone(), tick);
    let force_symbol = Symbol::qualified("test", "force-twice");
    one_arg_function(
        &mut cx,
        force_symbol.clone(),
        Demand::Never,
        force_first_arg_twice_impl,
    );
    let result = cx
        .eval_expr(call_expr(
            force_symbol,
            vec![call_expr(tick_symbol, Vec::new())],
        ))
        .unwrap();
    assert_eq!(result.object().as_expr(&mut cx).unwrap(), Expr::Bool(true));
    assert_eq!(counter.load(Ordering::SeqCst), 2);
}

#[test]
fn lazy_by_need_memoizes_argument_evaluation_once() {
    let mut cx = Cx::new(Arc::new(NeedPolicy), Arc::new(DefaultFactory));
    install_core_runtime(&mut cx);
    let counter = Arc::new(AtomicUsize::new(0));
    let tick = cx
        .factory()
        .opaque(Arc::new(TickCallable {
            counter: counter.clone(),
        }))
        .unwrap();
    let tick_symbol = Symbol::qualified("test", "tick");
    cx.env_mut().define(tick_symbol.clone(), tick);
    let memo_symbol = Symbol::qualified("test", "memo");
    one_arg_function(
        &mut cx,
        memo_symbol.clone(),
        Demand::Never,
        force_first_arg_twice_impl,
    );
    let result = cx
        .eval_expr(call_expr(
            memo_symbol,
            vec![call_expr(tick_symbol, Vec::new())],
        ))
        .unwrap();
    assert_eq!(result.object().as_expr(&mut cx).unwrap(), Expr::Bool(true));
    assert_eq!(counter.load(Ordering::SeqCst), 1);
}

#[test]
fn strict_by_shape_evaluates_only_required_positions() {
    let mut cx = Cx::new(Arc::new(StrictByShapePolicy), Arc::new(DefaultFactory));
    install_core_runtime(&mut cx);
    let counter = Arc::new(AtomicUsize::new(0));
    let tick = cx
        .factory()
        .opaque(Arc::new(TickCallable {
            counter: counter.clone(),
        }))
        .unwrap();
    let tick_symbol = Symbol::qualified("test", "tick");
    cx.env_mut().define(tick_symbol.clone(), tick);
    let first_symbol = Symbol::qualified("test", "first");
    two_arg_function(
        &mut cx,
        first_symbol.clone(),
        [Demand::Value, Demand::Never],
        return_first_arg_impl,
    );
    let result = cx
        .eval_expr(call_expr(
            first_symbol,
            vec![
                call_expr(tick_symbol, Vec::new()),
                Expr::Call {
                    operator: Box::new(Expr::Bool(true)),
                    args: Vec::new(),
                },
            ],
        ))
        .unwrap();
    assert_eq!(result.object().as_expr(&mut cx).unwrap(), Expr::Bool(true));
    assert_eq!(counter.load(Ordering::SeqCst), 1);
}

#[test]
fn eager_lazy_and_hybrid_can_run_the_same_simple_program() {
    for eval_policy in [
        Arc::new(EagerPolicy) as sim_kernel::EvalPolicyRef,
        Arc::new(LazyPolicy) as sim_kernel::EvalPolicyRef,
        Arc::new(NeedPolicy) as sim_kernel::EvalPolicyRef,
        Arc::new(StrictByShapePolicy) as sim_kernel::EvalPolicyRef,
        Arc::new(HybridPolicy) as sim_kernel::EvalPolicyRef,
    ] {
        let mut cx = Cx::new(eval_policy, Arc::new(DefaultFactory));
        install_core_runtime(&mut cx);
        let identity = Symbol::qualified("test", "identity");
        one_arg_function(
            &mut cx,
            identity.clone(),
            Demand::Value,
            return_first_arg_impl,
        );
        let result = cx
            .eval_expr(call_expr(
                identity.clone(),
                vec![call_expr(identity, vec![Expr::Bool(true)])],
            ))
            .unwrap();
        assert_eq!(result.object().as_expr(&mut cx).unwrap(), Expr::Bool(true));
    }
}

#[test]
fn with_eval_policy_switches_for_body_and_restores_policy() {
    let mut cx = Cx::new(Arc::new(EagerPolicy), Arc::new(DefaultFactory));
    install_core_runtime(&mut cx);
    let ignore_symbol = Symbol::qualified("test", "ignore");
    one_arg_function(
        &mut cx,
        ignore_symbol.clone(),
        Demand::Never,
        ignore_arg_impl,
    );
    let result = cx
        .eval_expr(call_expr(
            Symbol::qualified("core", "with-eval-policy"),
            vec![
                Expr::Quote {
                    mode: QuoteMode::Quote,
                    expr: Box::new(Expr::Symbol(Symbol::new("lazy"))),
                },
                call_expr(
                    ignore_symbol,
                    vec![Expr::Call {
                        operator: Box::new(Expr::Bool(true)),
                        args: Vec::new(),
                    }],
                ),
            ],
        ))
        .unwrap();
    assert_eq!(result.object().as_expr(&mut cx).unwrap(), Expr::Bool(true));
    assert_eq!(cx.eval_policy_name(), "eager");
}

#[test]
fn eval_policies_reports_typed_identity_for_active_policy() {
    let mut cx = Cx::new(Arc::new(NeedPolicy), Arc::new(DefaultFactory));
    install_core_runtime(&mut cx);
    let policies = cx
        .call_function(
            &Symbol::qualified("core", "eval-policies"),
            Args::new(Vec::new()),
        )
        .unwrap();
    let policies_expr = policies.object().as_expr(&mut cx).unwrap();
    let Expr::List(entries) = policies_expr else {
        panic!("expected eval policy browse list");
    };
    for policy in ["eager", "lazy", "lazy-by-need", "strict-by-shape", "hybrid"] {
        assert!(entries.iter().any(|entry| {
            table_value(entry, &Symbol::new("id"))
                == Some(&Expr::Symbol(Symbol::qualified("core", policy)))
        }));
    }
    let current = entries
        .iter()
        .find(|entry| table_value(entry, &Symbol::new("current")) == Some(&Expr::Bool(true)))
        .unwrap();
    assert_eq!(
        table_value(current, &Symbol::new("id")),
        Some(&Expr::Symbol(Symbol::qualified("core", "lazy-by-need")))
    );
}