Skip to main content

sim_lib_logic/
policy.rs

1//! Eval-policy adapter for running logic queries through a [`sim_kernel::Cx`].
2
3use std::sync::{Arc, Mutex};
4
5use sim_kernel::{
6    Cx, Demand, EagerPolicy, Error, EvalPolicy, Expr, PreparedArgs, RawArgs, Result, ShapeMatch,
7    Value,
8};
9
10use crate::{LogicConfig, LogicDb, query::query_all};
11
12/// Eval policy that treats each evaluated expression as a logic query goal.
13///
14/// The policy delegates resolution to the crate's existing query resolver and
15/// returns the first answer as a symbol-keyed table of captured bindings.
16pub struct LogicPolicy {
17    db: Arc<Mutex<LogicDb>>,
18    config: LogicConfig,
19}
20
21impl LogicPolicy {
22    /// Creates a policy over `db` with the supplied query configuration.
23    pub fn new(db: LogicDb, config: LogicConfig) -> Self {
24        Self {
25            db: Arc::new(Mutex::new(db)),
26            config,
27        }
28    }
29
30    /// Creates a policy over an existing shared database handle.
31    pub fn from_shared(db: Arc<Mutex<LogicDb>>, config: LogicConfig) -> Self {
32        Self { db, config }
33    }
34
35    /// Returns the shared database handle used by this policy.
36    pub fn db(&self) -> Arc<Mutex<LogicDb>> {
37        Arc::clone(&self.db)
38    }
39}
40
41impl EvalPolicy for LogicPolicy {
42    fn name(&self) -> &'static str {
43        "logic"
44    }
45
46    fn prepare_call_args(
47        &self,
48        cx: &mut Cx,
49        raw: RawArgs,
50        demands: &[Demand],
51    ) -> Result<PreparedArgs> {
52        EagerPolicy.prepare_call_args(cx, raw, demands)
53    }
54
55    fn force(&self, cx: &mut Cx, value: Value, demand: Demand) -> Result<Value> {
56        EagerPolicy.force(cx, value, demand)
57    }
58
59    fn eval_expr(&self, cx: &mut Cx, expr: Expr) -> Result<Value> {
60        let answers = {
61            let db = self
62                .db
63                .lock()
64                .map_err(|_| Error::PoisonedLock("logic policy db"))?;
65            query_all(cx, &db, &self.config, expr, Some(1))?
66        };
67        match answers.into_iter().next() {
68            Some(answer) => answer_to_value(cx, answer),
69            None => cx.factory().nil(),
70        }
71    }
72}
73
74fn answer_to_value(cx: &mut Cx, answer: ShapeMatch) -> Result<Value> {
75    let mut entries = Vec::new();
76    for (name, value) in answer.captures.values() {
77        entries.push((name.clone(), value.clone()));
78    }
79    for (name, expr) in answer.captures.exprs() {
80        entries.push((name.clone(), cx.factory().expr(expr.clone())?));
81    }
82    cx.factory().table(entries)
83}