summaryrefslogtreecommitdiff
path: root/crates/elfmind/src/knowledge.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/elfmind/src/knowledge.rs')
-rw-r--r--crates/elfmind/src/knowledge.rs187
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
- }
-}