1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
//! The memory module contains structures for storing information about the execution
//! of the rules. We have Memory structures that represent the information about a field,
//! and Work Memory Entities that store information about a matching fact.

use std::cmp::Ordering;

use crate::condition::{ConditionField, ConditionBody};

/// A memory stores the fact that we have matched a field, or the field didn't need matching.
///
/// Exact matching in alphamemories using hash functions is done against all permutations of
/// either the bound or unbound value. In case it is unbound, it can by Any value. For bound
/// values, we require a fixed Value.
#[derive(Hash, Eq, PartialEq, Debug, Clone)]
pub enum Memory {
    /// A constant value, must match exactly
    Value(String),
    /// Anything will match
    Any,
}

impl Memory {
    pub fn new(c: &ConditionField) -> Memory {
        match c {
            ConditionField::Const(s) => Memory::Value(s.clone()),
            _ => Memory::Any,
        }
    }
}

impl From<&ConditionField> for Memory {
    fn from(field: &ConditionField) -> Self {
        match field {
            ConditionField::Const(s) => Memory::Value(s.clone()),
            _ => Memory::Any,
        }
    }
}

/// Since we use Memory for hashing, we need partial ordering.
impl PartialOrd for Memory {
    /// Compare Value against Value, ignoring Any types
    ///
    fn partial_cmp(&self, other: &Memory) -> Option<Ordering> {
        match self {
            Memory::Value(value) =>
                if let Memory::Value(other_value) = &other {
                    value.partial_cmp(other_value)
                } else {
                    None
                },
            _ => None
        }
    }
}

/// A WmeMatch represents a combination of bound and unbound fields that matches a Wme for a rule.
///
/// We use WmeMatches as hash-keys to find the AlphaMemories for which the constant-conditionss
/// match the WME we add to the network.
#[derive(Hash, Eq, PartialEq, Debug, Clone, PartialOrd)]
pub(crate) struct WmeMatch {
    pub(crate) f1: Memory,
    pub(crate) f2: Memory,
    pub(crate) f3: Memory,
}

impl WmeMatch {
    pub(crate) fn new(f1: Memory, f2: Memory, f3: Memory) -> WmeMatch {
        WmeMatch { f1, f2, f3 }
    }
}

impl From<&ConditionBody> for WmeMatch {
    fn from(body: &ConditionBody) -> Self {
        WmeMatch{
            f1: Memory::from(&body.f1 ),
            f2: Memory::from(&body.f2 ),
            f3: Memory::from(&body.f3 ),
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_memory() {
        let v1 = Memory::Value("1".to_string());
        let v2 = Memory::Value("2".to_string());
        let a = Memory::Any;

        assert!(v1 < v2);
        assert!(!(v2 < v1));
        assert_eq!(v1, v1);
        assert_ne!(v1, v2);

        assert!(!(v1 < a));
        assert!(!(a < v1));

        assert_ne!(v1, a);
        assert_ne!(v2, a);
    }
}