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;

use sim_kernel::{Args, DefaultFactory, EagerPolicy, Expr, NumberLiteral, Symbol};

use crate::runtime::install_core_runtime;

#[test]
fn shape_runtime_helpers_use_kernel_shape_protocol() {
    let mut cx = sim_kernel::Cx::new(Arc::new(EagerPolicy), Arc::new(DefaultFactory));
    install_core_runtime(&mut cx);
    let any = cx.resolve_shape(&Symbol::qualified("core", "Any")).unwrap();
    assert!(any.object().as_shape().is_some());
    assert!(any.object().as_callable().is_some());

    let checked = cx
        .call_value(
            any,
            Args::new(vec![cx.factory().string("ok".to_owned()).unwrap()]),
        )
        .unwrap();
    let accepted = cx
        .call_function(
            &Symbol::qualified("shape", "accepted?"),
            Args::new(vec![checked]),
        )
        .unwrap();
    assert_eq!(
        accepted.object().as_expr(&mut cx).unwrap(),
        Expr::Bool(true)
    );

    let number = cx
        .resolve_shape(&Symbol::qualified("core", "Number"))
        .unwrap();
    let checked = cx
        .call_function(
            &Symbol::qualified("shape", "check"),
            Args::new(vec![
                number,
                cx.factory().string("not-number".to_owned()).unwrap(),
            ]),
        )
        .unwrap();
    let accepted = cx
        .call_function(
            &Symbol::qualified("shape", "accepted?"),
            Args::new(vec![checked]),
        )
        .unwrap();
    assert_eq!(
        accepted.object().as_expr(&mut cx).unwrap(),
        Expr::Bool(false)
    );
}

#[test]
fn shape_subshape_helpers_cover_any_exact_one_of_and_class_ancestry() {
    let mut cx = sim_kernel::Cx::new(Arc::new(EagerPolicy), Arc::new(DefaultFactory));
    install_core_runtime(&mut cx);
    let any = cx.resolve_shape(&Symbol::qualified("core", "Any")).unwrap();
    let number = cx
        .resolve_shape(&Symbol::qualified("core", "Number"))
        .unwrap();
    let number_kind = cx
        .call_class(
            &Symbol::qualified("core", "ExprKindShape"),
            Args::new(vec![cx.factory().symbol(Symbol::new("number")).unwrap()]),
        )
        .unwrap();
    let exact_one = exact_number_shape(&mut cx, "1");
    let exact_two = exact_number_shape(&mut cx, "2");
    let exacts = cx.factory().list(vec![exact_one, exact_two]).unwrap();
    let one_of_numbers = cx
        .call_class(
            &Symbol::qualified("core", "OneOfShape"),
            Args::new(vec![exacts]),
        )
        .unwrap();
    let shape_class = cx
        .resolve_class(&Symbol::qualified("core", "Shape"))
        .unwrap();
    let any_shape_class = cx
        .resolve_class(&Symbol::qualified("core", "AnyShape"))
        .unwrap();
    let class_parent = cx
        .call_class(
            &Symbol::qualified("core", "ClassShape"),
            Args::new(vec![shape_class]),
        )
        .unwrap();
    let class_child = cx
        .call_class(
            &Symbol::qualified("core", "ClassShape"),
            Args::new(vec![any_shape_class.clone()]),
        )
        .unwrap();

    for (child, parent) in [
        (number, any),
        (one_of_numbers, number_kind),
        (class_child, class_parent),
    ] {
        let result = cx
            .call_function(
                &Symbol::qualified("shape", "subshape?"),
                Args::new(vec![child, parent]),
            )
            .unwrap();
        assert_eq!(result.object().as_expr(&mut cx).unwrap(), Expr::Bool(true));
    }

    let class_parent_shape = cx
        .call_class(
            &Symbol::qualified("core", "ClassShape"),
            Args::new(vec![
                cx.resolve_class(&Symbol::qualified("core", "Shape"))
                    .unwrap(),
            ]),
        )
        .unwrap();
    let matched = class_parent_shape
        .object()
        .as_shape()
        .unwrap()
        .check_value(&mut cx, any_shape_class)
        .unwrap();
    assert!(matched.accepted);
}

#[test]
fn shape_constructor_values_encode_without_opaque_display_text() {
    let mut cx = sim_kernel::Cx::new(Arc::new(EagerPolicy), Arc::new(DefaultFactory));
    install_core_runtime(&mut cx);
    let shape = cx
        .call_class(
            &Symbol::qualified("core", "ExprKindShape"),
            Args::new(vec![cx.factory().symbol(Symbol::new("number")).unwrap()]),
        )
        .unwrap();
    let expr = shape.object().as_expr(&mut cx).unwrap();

    assert_eq!(
        expr,
        Expr::Call {
            operator: Box::new(Expr::Symbol(Symbol::qualified("core", "ExprKindShape"))),
            args: vec![Expr::Symbol(Symbol::new("number"))],
        }
    );
    assert!(shape.object().as_shape().is_some());
    assert!(shape.object().as_callable().is_some());
}

#[cfg(feature = "codec-lisp")]
#[test]
fn shape_read_construct_decodes_to_callable_shape_value() {
    use sim_codec::{Input, decode_with_codec};
    use sim_codec_lisp::LispCodecLib;
    use sim_kernel::{CapabilitySet, ReadPolicy, TrustLevel, read_construct_capability};

    let mut cx = sim_kernel::Cx::new(Arc::new(EagerPolicy), Arc::new(DefaultFactory));
    install_core_runtime(&mut cx);
    cx.grant(read_construct_capability());
    let codec_id = cx.registry_mut().fresh_codec_id();
    cx.load_lib(&LispCodecLib::new(codec_id).unwrap()).unwrap();
    let decoded = decode_with_codec(
        &mut cx,
        &Symbol::qualified("codec", "lisp"),
        Input::Text("#(core/ExprKindShape number)".to_owned()),
        ReadPolicy {
            trust: TrustLevel::TrustedSource,
            capabilities: CapabilitySet::new().grant(read_construct_capability()),
        },
    )
    .unwrap();
    let value = cx.eval_expr(decoded).unwrap();

    assert!(value.object().as_shape().is_some());
    assert!(value.object().as_callable().is_some());
}

fn exact_number_shape(cx: &mut sim_kernel::Cx, canonical: &str) -> sim_kernel::Value {
    cx.call_class(
        &Symbol::qualified("core", "ExactExprShape"),
        Args::new(vec![
            cx.factory()
                .expr(Expr::Number(NumberLiteral {
                    domain: Symbol::qualified("numbers", "f64"),
                    canonical: canonical.to_owned(),
                }))
                .unwrap(),
        ]),
    )
    .unwrap()
}