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

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

fn test_cx() -> Cx {
    Cx::new(Arc::new(EagerPolicy), Arc::new(DefaultFactory))
}

fn call(name: &str, args: Vec<Expr>) -> Expr {
    let mut items = Vec::with_capacity(args.len() + 1);
    items.push(Expr::Symbol(Symbol::new(name)));
    items.extend(args);
    Expr::List(items)
}

fn fact(goal: Expr) -> Expr {
    Expr::List(vec![Expr::Symbol(Symbol::new("fact")), goal])
}

fn naf(goal: Expr) -> Expr {
    call("not", vec![goal])
}

fn symbol_expr(name: &str) -> Expr {
    Expr::Symbol(Symbol::new(name))
}

fn local(name: &str) -> Expr {
    Expr::Local(Symbol::new(name))
}

#[test]
fn naf_succeeds_when_goal_fails() {
    let mut cx = test_cx();
    let db = LogicDb::new();
    let answers = query_all(
        &mut cx,
        &db,
        &LogicConfig::default(),
        naf(call(
            "contains",
            vec![symbol_expr("d"), symbol_expr("missing")],
        )),
        Some(1),
    )
    .unwrap();
    assert_eq!(answers.len(), 1);
}

#[test]
fn naf_fails_when_goal_succeeds() {
    let mut cx = test_cx();
    let mut db = LogicDb::new();
    db.assert_clause_expr(fact(call(
        "contains",
        vec![symbol_expr("a"), symbol_expr("present")],
    )))
    .unwrap();
    let answers = query_all(
        &mut cx,
        &db,
        &LogicConfig::default(),
        naf(call(
            "contains",
            vec![symbol_expr("a"), symbol_expr("present")],
        )),
        Some(1),
    )
    .unwrap();
    assert!(answers.is_empty());
}

#[test]
fn naf_flounders_on_unbound_variable() {
    let mut cx = test_cx();
    let db = LogicDb::new();
    let err = query_all(
        &mut cx,
        &db,
        &LogicConfig::default(),
        naf(call("foo", vec![local("x")])),
        Some(1),
    )
    .unwrap_err();
    assert!(format!("{err}").contains("flounders"));
}