use std::sync::{Arc, Mutex};
use sim_kernel::{
Cx, DefaultFactory, Demand, EagerPolicy, Error, EvalPolicy, Expr, PreparedArgs, RawArgs,
Result, ShapeMatch, Symbol, Value,
};
use crate::{LogicConfig, LogicDb, query::query_all};
struct ProbeLogicPolicy {
db: Arc<LogicDb>,
config: LogicConfig,
answers: Arc<Mutex<Vec<ShapeMatch>>>,
}
impl ProbeLogicPolicy {
fn new(db: LogicDb, answers: Arc<Mutex<Vec<ShapeMatch>>>) -> Self {
Self {
db: Arc::new(db),
config: LogicConfig::default(),
answers,
}
}
}
impl EvalPolicy for ProbeLogicPolicy {
fn name(&self) -> &'static str {
"probe-logic"
}
fn prepare_call_args(
&self,
cx: &mut Cx,
raw: RawArgs,
demands: &[Demand],
) -> Result<PreparedArgs> {
EagerPolicy.prepare_call_args(cx, raw, demands)
}
fn force(&self, cx: &mut Cx, value: Value, demand: Demand) -> Result<Value> {
EagerPolicy.force(cx, value, demand)
}
fn eval_expr(&self, cx: &mut Cx, expr: Expr) -> Result<Value> {
let answers = query_all(cx, &self.db, &self.config, expr, Some(1))?;
*self
.answers
.lock()
.map_err(|_| Error::PoisonedLock("probe logic answers"))? = answers;
cx.factory().nil()
}
}
fn parent_fact() -> Expr {
Expr::List(vec![
Expr::Symbol(Symbol::new("fact")),
Expr::List(vec![
Expr::Symbol(Symbol::new("parent")),
Expr::Symbol(Symbol::new("tom")),
Expr::Symbol(Symbol::new("bob")),
]),
])
}
fn parent_query() -> Expr {
Expr::List(vec![
Expr::Symbol(Symbol::new("parent")),
Expr::Symbol(Symbol::new("tom")),
Expr::Local(Symbol::new("X")),
])
}
#[test]
fn eval_policy_can_route_one_fact_query_to_logic_resolver() {
let answers = Arc::new(Mutex::new(Vec::new()));
let mut db = LogicDb::new();
db.assert_clause_expr(parent_fact()).unwrap();
let policy = Arc::new(ProbeLogicPolicy::new(db, Arc::clone(&answers)));
let mut cx = Cx::new(policy, Arc::new(DefaultFactory));
cx.eval_expr(parent_query()).unwrap();
let answers = answers.lock().unwrap();
assert_eq!(answers.len(), 1);
assert!(answers[0].accepted);
assert_eq!(
answers[0].captures.exprs(),
&[(Symbol::new("X"), Expr::Symbol(Symbol::new("bob")))]
);
}
#[test]
fn logic_policy_eval_returns_first_answer_bindings() {
let mut db = LogicDb::new();
db.assert_clause_expr(parent_fact()).unwrap();
let policy = Arc::new(crate::LogicPolicy::new(db, LogicConfig::default()));
assert_eq!(
policy
.db()
.lock()
.expect("logic policy db lock should be available")
.clauses()
.len(),
1
);
let mut cx = Cx::new(policy, Arc::new(DefaultFactory));
let result = cx.eval_expr(parent_query()).unwrap();
let table = result
.object()
.as_table_impl()
.expect("logic answer must be a table");
let x = table.get(&mut cx, Symbol::new("X")).unwrap();
assert_eq!(
x.object().as_expr(&mut cx).unwrap(),
Expr::Symbol(Symbol::new("bob"))
);
}