1use std::collections::BTreeMap;
8
9use sim_kernel::{Expr, Result, Symbol};
10
11use crate::clause::{
12 Clause, ClauseId, goal_arity, goal_first_arg, normalize_goal_expr, parse_clause_expr,
13 predicate_symbol,
14};
15
16#[derive(Clone, Debug, Default)]
21pub struct LogicDb {
22 clauses: Vec<Clause>,
23 by_predicate: BTreeMap<Symbol, Vec<ClauseId>>,
24 by_first_arg: BTreeMap<(Symbol, usize, String), Vec<ClauseId>>,
25}
26
27impl LogicDb {
28 pub fn new() -> Self {
50 Self::default()
51 }
52
53 pub fn clauses(&self) -> &[Clause] {
55 &self.clauses
56 }
57
58 pub fn predicate_exists(&self, predicate: &Symbol) -> bool {
60 self.by_predicate
61 .get(predicate)
62 .is_some_and(|clauses| !clauses.is_empty())
63 }
64
65 pub fn assert_clause_expr(&mut self, expr: Expr) -> Result<ClauseId> {
67 let id = ClauseId(self.clauses.len() + 1);
68 let clause = parse_clause_expr(id, expr)?;
69 self.assert_clause(clause)
70 }
71
72 pub fn assert_clause(&mut self, clause: Clause) -> Result<ClauseId> {
74 let id = clause.id;
75 let predicate = clause.predicate()?;
76 let arity = clause.arity()?;
77 let first_key = goal_first_arg(&clause.head)
78 .filter(|expr| !matches!(expr, Expr::Local(_)))
79 .map(canonical_goal_key);
80 self.by_predicate
81 .entry(predicate.clone())
82 .or_default()
83 .push(id);
84 if let Some(first_key) = first_key {
85 self.by_first_arg
86 .entry((predicate, arity, first_key))
87 .or_default()
88 .push(id);
89 }
90 self.clauses.push(clause);
91 Ok(id)
92 }
93
94 pub fn retract_clause_expr(&mut self, expr: &Expr) -> Result<bool> {
98 let target = parse_clause_expr(ClauseId(0), expr.clone())?;
99 if let Some(index) = self
100 .clauses
101 .iter()
102 .position(|clause| clause.fact_expr().canonical_eq(&target.fact_expr()))
103 {
104 self.clauses.remove(index);
105 self.rebuild_indexes()?;
106 return Ok(true);
107 }
108 Ok(false)
109 }
110
111 pub fn facts(&self, predicate: &Symbol) -> Vec<Expr> {
113 self.clauses
114 .iter()
115 .filter(|clause| clause.body.is_empty())
116 .filter_map(|clause| match clause.predicate() {
117 Ok(symbol) if &symbol == predicate => Some(clause.fact_expr()),
118 _ => None,
119 })
120 .collect()
121 }
122
123 pub fn clause_by_id(&self, id: ClauseId) -> Option<&Clause> {
125 self.clauses.iter().find(|clause| clause.id == id)
126 }
127
128 pub fn clauses_for_goal(&self, goal: &Expr, indexing: bool) -> Result<Vec<&Clause>> {
133 let predicate = predicate_symbol(goal)?;
134 let arity = goal_arity(goal)?;
135 let ids = if indexing {
136 let first_key = goal_first_arg(goal)
137 .filter(|expr| !matches!(expr, Expr::Local(_)))
138 .map(canonical_goal_key);
139 if let Some(first_key) = first_key {
140 self.by_first_arg
141 .get(&(predicate.clone(), arity, first_key))
142 .cloned()
143 .or_else(|| self.by_predicate.get(&predicate).cloned())
144 .unwrap_or_default()
145 } else {
146 self.by_predicate
147 .get(&predicate)
148 .cloned()
149 .unwrap_or_default()
150 }
151 } else {
152 self.by_predicate
153 .get(&predicate)
154 .cloned()
155 .unwrap_or_default()
156 };
157 Ok(ids
158 .into_iter()
159 .filter_map(|id| self.clause_by_id(id))
160 .collect())
161 }
162
163 fn rebuild_indexes(&mut self) -> Result<()> {
164 self.by_predicate.clear();
165 self.by_first_arg.clear();
166 let clauses = std::mem::take(&mut self.clauses);
167 for clause in clauses {
168 self.assert_clause(clause)?;
169 }
170 Ok(())
171 }
172}
173
174fn canonical_goal_key(expr: &Expr) -> String {
175 format!("{:?}", normalize_goal_expr(expr).canonical_key())
176}