1use crate::{
2 RiddleError,
3 core::Core,
4 scope::{Class, Predicate, Scope, Type},
5};
6use core::fmt;
7use std::{
8 any::Any,
9 cell::RefCell,
10 collections::HashMap,
11 ops::Deref,
12 rc::{Rc, Weak},
13};
14
15#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
16pub struct ObjectId(pub(super) usize);
17
18impl From<usize> for ObjectId {
19 fn from(val: usize) -> Self {
20 ObjectId(val)
21 }
22}
23
24impl Deref for ObjectId {
25 type Target = usize;
26
27 fn deref(&self) -> &Self::Target {
28 &self.0
29 }
30}
31
32impl fmt::Display for ObjectId {
33 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34 write!(f, "obj-{}", self.0)
35 }
36}
37
38#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
39pub struct AtomId(pub(super) usize);
40
41impl Deref for AtomId {
42 type Target = usize;
43
44 fn deref(&self) -> &Self::Target {
45 &self.0
46 }
47}
48
49impl From<usize> for AtomId {
50 fn from(val: usize) -> Self {
51 AtomId(val)
52 }
53}
54
55impl fmt::Display for AtomId {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 write!(f, "atm-{}", self.0)
58 }
59}
60
61#[derive(Clone)]
62pub enum Slot {
63 Primitive(Rc<dyn Var>),
64 ObjectRef(ObjectId),
65 AtomRef(AtomId),
66}
67
68impl fmt::Display for Slot {
69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70 match self {
71 Slot::Primitive(var) => write!(f, "{}", var.var_type().name()),
72 Slot::ObjectRef(obj_id) => write!(f, "Object({})", *obj_id),
73 Slot::AtomRef(atom_id) => write!(f, "Atom({})", *atom_id),
74 }
75 }
76}
77
78pub trait Var {
79 fn var_type(&self) -> Rc<dyn Type>;
81 fn as_any(self: Rc<Self>) -> Rc<dyn Any>;
83 fn as_env(self: Rc<Self>) -> Option<Rc<dyn Env>> {
85 None
86 }
87}
88
89pub trait Env {
90 fn parent(&self) -> Option<Rc<dyn Env>>;
92 fn get_slots(&self) -> HashMap<String, Slot>;
94 fn get(&self, name: &str) -> Option<Slot>;
96 fn set(&self, name: String, value: Slot);
98}
99
100pub struct CommonEnv {
101 parent: Option<Rc<dyn Env>>,
102 variables: RefCell<HashMap<String, Slot>>,
103}
104
105impl CommonEnv {
106 pub fn new(parent: Option<Rc<dyn Env>>) -> Self {
107 Self { parent, variables: RefCell::new(HashMap::new()) }
108 }
109}
110
111impl Env for CommonEnv {
112 fn parent(&self) -> Option<Rc<dyn Env>> {
113 self.parent.clone()
114 }
115
116 fn get_slots(&self) -> HashMap<String, Slot> {
117 self.variables.borrow().clone()
118 }
119
120 fn get(&self, name: &str) -> Option<Slot> {
121 self.variables.borrow().get(name).cloned().or_else(|| self.parent.as_ref()?.get(name))
122 }
123
124 fn set(&self, name: String, value: Slot) {
125 self.variables.borrow_mut().insert(name, value);
126 }
127}
128
129pub enum BoolExpr {
130 Term { var_type: Weak<dyn Type>, term: Slot },
131 Not { var_type: Weak<dyn Type>, term: Rc<BoolExpr> },
132 Eq { var_type: Weak<dyn Type>, left: Slot, right: Slot },
133 Lt { var_type: Weak<dyn Type>, left: Slot, right: Slot },
134 Leq { var_type: Weak<dyn Type>, left: Slot, right: Slot },
135 Or { var_type: Weak<dyn Type>, terms: Vec<Rc<BoolExpr>> },
136 And { var_type: Weak<dyn Type>, terms: Vec<Rc<BoolExpr>> },
137}
138
139impl Var for BoolExpr {
140 fn var_type(&self) -> Rc<dyn Type> {
141 match self {
142 BoolExpr::Term { var_type: var_tp, .. } | BoolExpr::Not { var_type: var_tp, .. } | BoolExpr::Eq { var_type: var_tp, .. } | BoolExpr::Lt { var_type: var_tp, .. } | BoolExpr::Leq { var_type: var_tp, .. } | BoolExpr::Or { var_type: var_tp, .. } | BoolExpr::And { var_type: var_tp, .. } => var_tp.upgrade().unwrap(),
143 }
144 }
145
146 fn as_any(self: Rc<Self>) -> Rc<dyn Any> {
147 self
148 }
149}
150
151pub struct Object {
152 id: ObjectId,
153 class: Weak<dyn Class>,
154 env: CommonEnv,
155}
156
157impl Object {
158 pub(super) fn new(id: ObjectId, class: Rc<dyn Class>) -> Self {
159 Self { id, class: Rc::downgrade(&class), env: CommonEnv::new(Some(class.core())) }
160 }
161
162 pub fn id(&self) -> ObjectId {
163 self.id
164 }
165
166 pub fn class(&self) -> Rc<dyn Class> {
167 self.class.upgrade().unwrap()
168 }
169}
170
171impl Var for Object {
172 fn var_type(&self) -> Rc<dyn Type> {
173 self.class.upgrade().unwrap()
174 }
175
176 fn as_any(self: Rc<Self>) -> Rc<dyn Any> {
177 self
178 }
179
180 fn as_env(self: Rc<Self>) -> Option<Rc<dyn Env>> {
181 Some(self.clone())
182 }
183}
184
185impl Env for Object {
186 fn parent(&self) -> Option<Rc<dyn Env>> {
187 self.env.parent.clone()
188 }
189
190 fn get_slots(&self) -> HashMap<String, Slot> {
191 self.env.get_slots()
192 }
193
194 fn get(&self, name: &str) -> Option<Slot> {
195 self.env.get(name)
196 }
197
198 fn set(&self, name: String, value: Slot) {
199 self.env.set(name, value);
200 }
201}
202
203pub struct Atom {
204 id: AtomId,
205 predicate: Weak<Predicate>,
206 fact: bool,
207 env: CommonEnv,
208}
209
210impl Atom {
211 pub fn new(id: AtomId, predicate: Rc<Predicate>, fact: bool, args: HashMap<String, Slot>) -> Self {
212 let env = match args.get("tau") {
214 Some(tau) => match tau {
215 Slot::Primitive(var) => var.clone().as_env().expect("Tau variable does not have an environment").clone(),
216 Slot::ObjectRef(obj_id) => predicate.clone().core().get_object(*obj_id).expect("Object ID in tau does not exist").as_env().expect("Object in tau does not have an environment").clone(),
217 Slot::AtomRef(atom_id) => predicate.clone().core().get_atom(*atom_id).expect("Atom ID in tau does not exist").as_env().expect("Atom in tau does not have an environment").clone(),
218 },
219 None => predicate.core(),
220 };
221 let env = CommonEnv::new(Some(env));
223 for (name, value) in args {
224 env.set(name, value);
225 }
226 Self { id, predicate: Rc::downgrade(&predicate), fact, env }
227 }
228
229 pub fn id(&self) -> AtomId {
230 self.id
231 }
232
233 pub fn predicate(&self) -> Rc<Predicate> {
234 self.predicate.upgrade().unwrap()
235 }
236
237 pub fn is_fact(&self) -> bool {
238 self.fact
239 }
240}
241
242impl Var for Atom {
243 fn var_type(&self) -> Rc<dyn Type> {
244 self.predicate.upgrade().unwrap()
245 }
246
247 fn as_any(self: Rc<Self>) -> Rc<dyn Any> {
248 self
249 }
250
251 fn as_env(self: Rc<Self>) -> Option<Rc<dyn Env>> {
252 Some(self.clone())
253 }
254}
255
256impl Env for Atom {
257 fn parent(&self) -> Option<Rc<dyn Env>> {
258 self.env.parent.clone()
259 }
260
261 fn get_slots(&self) -> HashMap<String, Slot> {
262 self.env.get_slots()
263 }
264
265 fn get(&self, name: &str) -> Option<Slot> {
266 self.env.get(name)
267 }
268
269 fn set(&self, name: String, value: Slot) {
270 self.env.set(name, value);
271 }
272}
273
274fn push_negations(expr: Rc<BoolExpr>) -> Rc<BoolExpr> {
275 match expr.as_ref() {
276 BoolExpr::Not { term, .. } => push_inverted(term.clone()),
277 BoolExpr::And { var_type, terms } => Rc::new(BoolExpr::And {
278 var_type: var_type.clone(),
279 terms: terms.iter().map(|t| push_negations(t.clone())).collect(),
280 }),
281 BoolExpr::Or { var_type, terms } => Rc::new(BoolExpr::Or {
282 var_type: var_type.clone(),
283 terms: terms.iter().map(|t| push_negations(t.clone())).collect(),
284 }),
285 _ => expr,
286 }
287}
288
289fn push_inverted(expr: Rc<BoolExpr>) -> Rc<BoolExpr> {
291 match expr.as_ref() {
292 BoolExpr::Not { term, .. } => push_negations(term.clone()),
294
295 BoolExpr::And { var_type, terms } => Rc::new(BoolExpr::Or {
297 var_type: var_type.clone(),
298 terms: terms.iter().map(|t| push_inverted(t.clone())).collect(),
299 }),
300
301 BoolExpr::Or { var_type, terms } => Rc::new(BoolExpr::And {
303 var_type: var_type.clone(),
304 terms: terms.iter().map(|t| push_inverted(t.clone())).collect(),
305 }),
306
307 BoolExpr::Leq { var_type, left, right } => Rc::new(BoolExpr::Lt { var_type: var_type.clone(), left: right.clone(), right: left.clone() }),
308 BoolExpr::Lt { var_type, left, right } => Rc::new(BoolExpr::Leq { var_type: var_type.clone(), left: right.clone(), right: left.clone() }),
309
310 BoolExpr::Term { var_type: var_tp, .. } | BoolExpr::Eq { var_type: var_tp, .. } => Rc::new(BoolExpr::Not { var_type: var_tp.clone(), term: expr }),
311 }
312}
313
314fn distribute(expr: Rc<BoolExpr>) -> Rc<BoolExpr> {
315 match expr.as_ref() {
316 BoolExpr::Or { var_type, terms } => {
317 let mut distributed_terms = Vec::new();
319 for t in terms {
320 let dist = distribute(t.clone());
321 if let BoolExpr::Or { terms: inner_terms, .. } = dist.as_ref() {
322 distributed_terms.extend(inner_terms.clone());
323 } else {
324 distributed_terms.push(dist);
325 }
326 }
327
328 let mut result_ands: Vec<Vec<Rc<BoolExpr>>> = vec![vec![]];
331
332 for term in distributed_terms {
333 if let BoolExpr::And { terms: and_terms, .. } = term.as_ref() {
334 let mut next_ands = Vec::new();
336 for existing_and in &result_ands {
337 for and_term in and_terms {
338 let mut combo = existing_and.clone();
339 combo.push(and_term.clone());
340 next_ands.push(combo);
341 }
342 }
343 result_ands = next_ands;
344 } else {
345 for existing_and in &mut result_ands {
347 existing_and.push(term.clone());
348 }
349 }
350 }
351
352 let cnf_or_nodes: Vec<Rc<BoolExpr>> = result_ands.into_iter().map(|sub_terms| Rc::new(BoolExpr::Or { var_type: var_type.clone(), terms: sub_terms })).collect();
354
355 if cnf_or_nodes.len() == 1 { cnf_or_nodes[0].clone() } else { Rc::new(BoolExpr::And { var_type: var_type.clone(), terms: cnf_or_nodes }) }
357 }
358
359 BoolExpr::And { var_type, terms } => {
360 let mut distributed_terms = Vec::new();
362 for t in terms {
363 let dist = distribute(t.clone());
364 if let BoolExpr::And { terms: inner_terms, .. } = dist.as_ref() {
365 distributed_terms.extend(inner_terms.clone());
366 } else {
367 distributed_terms.push(dist);
368 }
369 }
370 Rc::new(BoolExpr::And { var_type: var_type.clone(), terms: distributed_terms })
371 }
372
373 _ => expr,
374 }
375}
376
377pub fn to_cnf(expr: Rc<BoolExpr>) -> Rc<BoolExpr> {
378 distribute(push_negations(expr))
379}
380
381pub fn get_var_by_path(core: &dyn Core, env: &dyn Env, path: &[String]) -> Result<Slot, RiddleError> {
389 let (first, rest) = path.split_first().ok_or_else(|| RiddleError::RuntimeError("Empty variable path".into()))?;
390 rest.iter().try_fold(env.get(first).ok_or_else(|| RiddleError::NotFound(first.to_string()))?, |acc, id| match acc {
391 Slot::Primitive(var) => var.clone().as_env().ok_or_else(|| RiddleError::NotAnEnvironment(format!("Variable '{}' in path does not have an environment", first)))?.get(id).ok_or_else(|| RiddleError::NotFound(format!("Variable '{}' in path not found in variable '{}'", id, first))),
392 Slot::ObjectRef(obj_id) => {
393 let obj = core.get_object(obj_id).ok_or_else(|| RiddleError::NotFound(format!("Object {} not found", *obj_id)))?;
394 obj.as_env().ok_or_else(|| RiddleError::NotAnEnvironment(format!("Object {} does not have an environment", *obj_id)))?.get(id).ok_or_else(|| RiddleError::NotFound(format!("Variable '{}' in path not found in object {}", id, *obj_id)))
395 }
396 Slot::AtomRef(atom_id) => {
397 let atom = core.get_atom(atom_id).ok_or_else(|| RiddleError::NotFound(format!("Atom {} not found", *atom_id)))?;
398 atom.as_env().ok_or_else(|| RiddleError::NotAnEnvironment(format!("Atom {} does not have an environment", *atom_id)))?.get(id).ok_or_else(|| RiddleError::NotFound(format!("Variable '{}' in path not found in atom {}", id, *atom_id)))
399 }
400 })
401}