sim-lib-logic 0.1.0

SIM workspace package for sim lib logic.
Documentation
use std::sync::Arc;

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

use crate::{LogicConfig, LogicDb, builtins::BuiltinTable, query::query_all};

fn cx_with_number_tower() -> Cx {
    let mut cx = Cx::new(Arc::new(EagerPolicy), Arc::new(DefaultFactory));
    cx.load_lib(&sim_lib_numbers_arith::NumbersArithmeticLib::new())
        .unwrap();
    cx.load_lib(&sim_lib_numbers_i64::I64NumbersLib::new())
        .unwrap();
    cx.load_lib(&sim_lib_numbers_f64::F64NumbersLib::new())
        .unwrap();
    cx
}

fn number(domain: &str, canonical: impl Into<String>) -> Expr {
    Expr::Number(NumberLiteral {
        domain: Symbol::qualified("numbers", domain),
        canonical: canonical.into(),
    })
}

fn query_compare(op: &str, left: Expr, right: Expr) -> Vec<ShapeMatch> {
    let mut cx = cx_with_number_tower();
    query_all(
        &mut cx,
        &LogicDb::new(),
        &LogicConfig::default(),
        Expr::List(vec![Expr::Symbol(Symbol::new(op)), left, right]),
        Some(2),
    )
    .unwrap()
}

#[test]
fn comparison_family_is_registered_as_number_arith() {
    let table = BuiltinTable::standard();

    for key in ["=:=", "=\\=", "<", "=<", ">", ">="] {
        assert_eq!(
            table.organ_of(&Symbol::new(key)),
            Some(&Symbol::qualified("numbers", "arith"))
        );
    }
}

#[test]
fn cross_domain_equality_promotes_through_number_tower() {
    let answers = query_compare("=:=", number("i64", "2"), number("f64", "2.0"));

    assert_eq!(answers.len(), 1);
}

#[test]
fn arithmetic_comparison_family_obeys_cmp_result() {
    let true_cases = [
        ("=\\=", number("i64", "2"), number("i64", "3")),
        ("<", number("i64", "1"), number("i64", "2")),
        ("=<", number("i64", "2"), number("i64", "2")),
        (">", number("i64", "3"), number("i64", "2")),
        (">=", number("i64", "2"), number("i64", "2")),
    ];
    for (op, left, right) in true_cases {
        assert_eq!(query_compare(op, left, right).len(), 1, "{op} should hold");
    }

    assert!(query_compare("<", number("i64", "3"), number("i64", "2")).is_empty());
}