diff options
Diffstat (limited to 'crates/elfmind/src/knowledge.rs')
| -rw-r--r-- | crates/elfmind/src/knowledge.rs | 187 |
1 files changed, 92 insertions, 95 deletions
diff --git a/crates/elfmind/src/knowledge.rs b/crates/elfmind/src/knowledge.rs index a610328..97ff118 100644 --- a/crates/elfmind/src/knowledge.rs +++ b/crates/elfmind/src/knowledge.rs @@ -1,86 +1,45 @@ -pub mod relation; -pub use relation::*; +pub mod traits; pub use traits::*; +pub mod relation; pub use relation::*; -use crate::time::*; -use crate::item::*; -use crate::character::*; +pub use crate::noun::*; use std::collections::{HashMap, HashSet}; +use std::fmt::Display; use itertools::Itertools; +use strum::IntoDiscriminant; use enum_map::EnumMap; -use strum::IntoDiscriminant; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Var { index: u64 } - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Value { - Var(Var), - Item(Item), - Character(Character), - Time(Time), -} -impl From<Var> for Value { fn from(x: Var) -> Self {Value::Var(x)} } -impl From<Item> for Value { fn from(x: Item) -> Self {Value::Item(x)} } -impl From<Character> for Value { fn from(x: Character) -> Self {Value::Character(x)} } -impl From<Time> for Value { fn from(x: Time) -> Self {Value::Time(x)} } -impl TryFrom<Value> for Var { - type Error = (); - fn try_from(v: Value) -> Result<Self, Self::Error> { - if let Value::Var(x) = v { Ok(x) } else { Err(()) } - } -} -impl TryFrom<Value> for Item { - type Error = (); - fn try_from(v: Value) -> Result<Self, Self::Error> { - if let Value::Item(x) = v { Ok(x) } else { Err(()) } - } -} -impl TryFrom<Value> for Character { - type Error = (); - fn try_from(v: Value) -> Result<Self, Self::Error> { - if let Value::Character(x) = v { Ok(x) } else { Err(()) } - } -} -impl TryFrom<Value> for Time { - type Error = (); - fn try_from(v: Value) -> Result<Self, Self::Error> { - if let Value::Time(x) = v { Ok(x) } else { Err(()) } +pub struct Var(pub u64); +impl Display for Var { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "v{}", self.0) } } #[derive(Debug, Default)] pub struct Unification { - assignment: HashMap<Var, Value>, + assignment: HashMap<Var, Noun>, } impl Unification { - pub fn lookup(&self, v: &Var) -> Option<Value> { + pub fn lookup(&self, v: &Var) -> Option<Noun> { self.assignment.get(v).cloned() } - pub fn add(&mut self, v: Var, x: impl Into<Value>) -> bool { + pub fn add(&mut self, v: Var, x: impl Into<Noun>) -> bool { let y = x.into(); if let Some(old) = self.assignment.get(&v) { - if let Value::Var(v2) = y { - self.add(v2, *old) - } else if let Value::Var(v2) = old { - self.add(*v2, y) - } else { - *old == y - } + *old == y } else { self.assignment.insert(v, y); true } } - pub fn unify_hole<A, B>(&mut self, h1: &Hole<A>, h2: &Hole<B>) -> bool - where Value: From<A>, Value: From<B>, A: Copy, B: Copy { - match (h1, h2) { - (Hole::Var(v1), Hole::Var(v2)) => - self.add(*v1, *v2) && self.add(*v2, *v1), - (Hole::Val(x1), Hole::Val(x2)) => Value::from(*x1) == Value::from(*x2), - (Hole::Var(v1), Hole::Val(x2)) => self.add(*v1, *x2), - (Hole::Val(x1), Hole::Var(v2)) => self.add(*v2, *x1), + pub fn unify_hole<A, B>(&mut self, h1: &Hole<A>, x2: &B) -> bool + where Noun: From<A>, Noun: From<B>, A: Copy, B: Copy { + match h1 { + Hole::Val(x1) => Noun::from(*x1) == Noun::from(*x2), + Hole::Var(v1) => self.add(*v1, *x2), } } } @@ -90,7 +49,20 @@ pub enum Hole<T> { Var(Var), Val(T), } -impl<T: Clone + TryFrom<Value>> Hole<T> { +impl<T: std::fmt::Debug> Display for Hole<T> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Var(v) => v.fmt(f), + Self::Val(x) => write!(f, "{:?}", x), + } + } +} +impl<A: std::fmt::Debug> DisplayNoun for Hole<A> { + fn display_noun(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self) + } +} +impl<T: Clone + TryFrom<Noun>> Hole<T> { pub fn substitute(&self, u: &Unification) -> Self { match self { x@Self::Var(v) => if let Some(y) = u.lookup(v) && let Ok(z) = T::try_from(y) { @@ -105,8 +77,22 @@ impl<T: Clone + TryFrom<Value>> Hole<T> { #[derive(Debug, Clone)] pub struct Rule { - pub head: Relation, - pub body: Vec<Relation>, + pub head: RelationHole, + pub body: Vec<RelationHole>, +} +impl Display for Rule { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} :- ", self.head)?; + #[allow(unstable_name_collisions)] + for mb in self.body.iter().map(Some).intersperse(None) { + if let Some(b) = mb { + write!(f, "{}", b)?; + } else { + write!(f, ", ")?; + } + } + std::fmt::Result::Ok(()) + } } impl Rule { pub fn unify_body(&self, facts: &[&Relation]) -> Option<Unification> { @@ -117,12 +103,40 @@ impl Rule { } Some(u) } + pub fn canonize(&mut self, mut next: u64) -> u64 { + let mut remap = HashMap::new(); + let mut f = |v| { + if let Some(new) = remap.get(&v) { + *new + } else { + let new = Var(next); + next += 1; + remap.insert(v, new); + new + } + }; + self.head = self.head.map_variables(&mut f); + for b in self.body.iter_mut() { + *b = b.map_variables(&mut f); + } + next + } } #[derive(Debug, Default, Clone)] pub struct Facts { facts: EnumMap<RelationDiscriminants, HashSet<Relation>>, } +impl Display for Facts { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for (_, s) in self.facts.iter() { + for r in s.iter() { + write!(f, "{}.\n", r)?; + } + } + std::fmt::Result::Ok(()) + } +} impl Facts { pub fn add(&mut self, f: Relation) -> bool { self.facts[f.discriminant()].insert(f) @@ -148,6 +162,15 @@ pub struct Database { facts: Facts, rules: Vec<Rule>, } +impl Display for Database { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.facts)?; + for r in self.rules.iter() { + write!(f, "{}.\n", r)?; + } + std::fmt::Result::Ok(()) + } +} impl Database { pub fn is_known(&self, r: &Relation) -> bool { self.facts.contains(r) } pub fn query<'a>(&'a mut self) -> Query<'a> { @@ -176,16 +199,6 @@ impl<'a> Query<'a> { fn apply_rule(&self, r: &Rule, new_facts: &mut Facts, required_facts: QueryFactsToConsider) -> bool { // we don't need to consider every possible fact against each body term (naive evaluation) // instead, we can limit ourselves to the subset where at least one of the facts is new (semi-naive) - // let found: Vec<Unification> = r.body.iter() - // .map(|r| { - // let d = r.discriminant(); - // new_facts.with_sort(d).iter() - // .chain(self.facts.with_sort(d).iter()) - // .chain(self.db.facts.with_sort(d).iter()) - // }) - // .multi_cartesian_product() - // .filter_map(|fs| r.unify_body(&fs)) - // .collect(); let mut found = Vec::new(); // identify which slots of this rule body possibly have new facts to consider let changed_slots = r.body.iter() @@ -216,8 +229,7 @@ impl<'a> Query<'a> { QueryFactsToConsider::Query if i == cs => self.facts.with_sort(d).iter().chain(empty.iter()), // otherwise, consider all facts - a new fact might be used alongside an unchanged fact! - _ => - self.facts.with_sort(d).iter().chain(self.db.facts.with_sort(d).iter()), + _ => self.facts.with_sort(d).iter().chain(self.db.facts.with_sort(d).iter()), } }) .multi_cartesian_product(); @@ -229,8 +241,10 @@ impl<'a> Query<'a> { } let mut changed = false; for u in found { - let new = r.head.substitute(&u); - if new.is_concrete() && !self.facts.contains(&new) && !self.db.facts.contains(&new) { + if let Some(new) = r.head.substitute(&u).concretize() + && !self.facts.contains(&new) + && !self.db.facts.contains(&new) + { if new_facts.add(new) { changed = true; } } } @@ -259,7 +273,8 @@ impl<'a> Query<'a> { } self } - pub fn rule(mut self, r: Rule) -> Self { + pub fn rule(mut self, mut r: Rule) -> Self { + r.canonize(0); self.rules.push(r); self } @@ -286,21 +301,3 @@ impl<'a> Query<'a> { self.db.rules.extend_from_slice(&mut self.rules); } } - -#[derive(Debug, Default)] -pub struct Context { - next_index: u64, - var_names: HashMap<Var, String>, -} -impl Context { - pub fn fresh(&mut self) -> Var { - let ret = Var { index: self.next_index }; - self.next_index += 1; - ret - } - pub fn var(&mut self, nm: &str) -> Var { - let ret = self.fresh(); - self.var_names.insert(ret, nm.to_owned()); - ret - } -} |
