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, Value};

use crate::runtime::install_core_runtime;

fn cx() -> sim_kernel::Cx {
    let mut cx = sim_kernel::Cx::new(Arc::new(EagerPolicy), Arc::new(DefaultFactory));
    install_core_runtime(&mut cx);
    cx
}

fn number_expr(text: &str) -> Expr {
    Expr::Number(NumberLiteral {
        domain: Symbol::qualified("numbers", "f64"),
        canonical: text.to_owned(),
    })
}

fn expr_kind_shape(cx: &mut sim_kernel::Cx, kind: &str) -> Value {
    let kind = cx.factory().symbol(Symbol::new(kind)).unwrap();
    cx.call_class(
        &Symbol::qualified("core", "ExprKindShape"),
        Args::new(vec![kind]),
    )
    .unwrap()
}

fn exact_expr_shape(cx: &mut sim_kernel::Cx, expr: Expr) -> Value {
    let value = cx.factory().expr(expr).unwrap();
    cx.call_class(
        &Symbol::qualified("core", "ExactExprShape"),
        Args::new(vec![value]),
    )
    .unwrap()
}

fn table_entries(cx: &mut sim_kernel::Cx, value: &Value) -> Vec<(Symbol, Value)> {
    value.object().as_table_impl().unwrap().entries(cx).unwrap()
}

fn entry(entries: &[(Symbol, Value)], key: &str) -> Value {
    entries
        .iter()
        .find_map(|(candidate, value)| (candidate == &Symbol::new(key)).then_some(value.clone()))
        .unwrap()
}

fn expr_of(cx: &mut sim_kernel::Cx, value: Value) -> Expr {
    value.object().as_expr(cx).unwrap()
}

#[test]
fn runtime_shape_compare_returns_stable_table_keys() {
    let mut cx = cx();
    let exact = exact_expr_shape(&mut cx, number_expr("1"));
    let number = expr_kind_shape(&mut cx, "number");

    let relation = cx
        .call_function(
            &Symbol::qualified("shape", "compare"),
            Args::new(vec![exact, number]),
        )
        .unwrap();
    let entries = table_entries(&mut cx, &relation);
    let keys = entries
        .iter()
        .map(|(key, _)| key.clone())
        .collect::<Vec<_>>();

    assert_eq!(
        keys,
        vec![
            Symbol::new("kind"),
            Symbol::new("proven"),
            Symbol::new("left"),
            Symbol::new("right"),
            Symbol::new("witness-count"),
            Symbol::new("witnesses"),
            Symbol::new("diagnostics"),
        ]
    );
    assert!(matches!(
        expr_of(&mut cx, entry(&entries, "kind")),
        Expr::Symbol(symbol) if symbol == Symbol::qualified("shape", "left-subshape")
    ));
    assert!(matches!(
        expr_of(&mut cx, entry(&entries, "proven")),
        Expr::Bool(true)
    ));
}

#[test]
fn runtime_shape_compare_with_parses_expr_probes() {
    let mut cx = cx();
    let number = expr_kind_shape(&mut cx, "number");
    let string = expr_kind_shape(&mut cx, "string");
    let kind = cx.factory().symbol(Symbol::new("expr")).unwrap();
    let label = cx.factory().string("bool".to_owned()).unwrap();
    let target = cx.factory().bool(true).unwrap();
    let probe = cx.factory().list(vec![kind, label, target]).unwrap();
    let probes = cx.factory().list(vec![probe]).unwrap();

    let relation = cx
        .call_function(
            &Symbol::qualified("shape", "compare-with"),
            Args::new(vec![number, string, probes]),
        )
        .unwrap();
    let entries = table_entries(&mut cx, &relation);

    assert!(matches!(
        expr_of(&mut cx, entry(&entries, "kind")),
        Expr::Symbol(symbol) if symbol == Symbol::qualified("shape", "unknown")
    ));
    assert!(expr_of(&mut cx, entry(&entries, "witness-count")).canonical_eq(&number_expr("1")));
}

#[test]
fn runtime_venn_helpers_are_registered_and_build_shapes() {
    let mut cx = cx();
    let number = expr_kind_shape(&mut cx, "number");
    let string = expr_kind_shape(&mut cx, "string");
    let number_member = cx
        .factory()
        .list(vec![
            cx.factory().symbol(Symbol::new("number")).unwrap(),
            number.clone(),
        ])
        .unwrap();
    let string_member = cx
        .factory()
        .list(vec![
            cx.factory().symbol(Symbol::new("string")).unwrap(),
            string.clone(),
        ])
        .unwrap();
    let members = cx
        .factory()
        .list(vec![number_member, string_member])
        .unwrap();
    let venn = cx
        .call_function(
            &Symbol::qualified("shape", "venn"),
            Args::new(vec![members]),
        )
        .unwrap();

    let union = cx
        .call_function(
            &Symbol::qualified("shape", "venn-union"),
            Args::new(vec![venn.clone()]),
        )
        .unwrap();
    let intersection = cx
        .call_function(
            &Symbol::qualified("shape", "venn-intersection"),
            Args::new(vec![venn.clone()]),
        )
        .unwrap();
    let only_name = cx.factory().symbol(Symbol::new("number")).unwrap();
    let only = cx
        .call_function(
            &Symbol::qualified("shape", "venn-only"),
            Args::new(vec![venn.clone(), only_name]),
        )
        .unwrap();
    let outside = cx
        .call_function(
            &Symbol::qualified("shape", "venn-outside"),
            Args::new(vec![venn.clone()]),
        )
        .unwrap();
    let exact_name = cx.factory().symbol(Symbol::new("number")).unwrap();
    let names = cx.factory().list(vec![exact_name]).unwrap();
    let exactly = cx
        .call_function(
            &Symbol::qualified("shape", "venn-exactly"),
            Args::new(vec![venn, names]),
        )
        .unwrap();

    assert!(union.object().as_shape().is_some());
    assert!(intersection.object().as_shape().is_some());
    assert!(only.object().as_shape().is_some());
    assert!(outside.object().as_shape().is_some());
    assert!(exactly.object().as_shape().is_some());
    assert!(
        union
            .object()
            .as_shape()
            .unwrap()
            .check_expr(&mut cx, &Expr::String("ok".to_owned()))
            .unwrap()
            .accepted
    );
    assert!(
        !only
            .object()
            .as_shape()
            .unwrap()
            .check_expr(&mut cx, &Expr::String("ok".to_owned()))
            .unwrap()
            .accepted
    );
}