├── src ├── runtime │ ├── mod.rs │ └── memory.rs ├── shared │ ├── runtimes │ │ ├── mod.rs │ │ └── array │ │ │ ├── mod.rs │ │ │ ├── runtime.rs │ │ │ └── builder.rs │ ├── nodes │ │ ├── mod.rs │ │ ├── tests.rs │ │ └── alpha.rs │ ├── mod.rs │ ├── compiler │ │ ├── mod.rs │ │ ├── id_generator.rs │ │ ├── builder.rs │ │ ├── as_vec.rs │ │ ├── as_ref.rs │ │ └── prelude.rs │ ├── context.rs │ └── fact.rs ├── builders │ ├── mod.rs │ ├── ids.rs │ └── statement.rs ├── network │ ├── mod.rs │ ├── ids.rs │ └── tests.rs ├── macros.rs ├── serial.rs ├── lib.rs ├── errors.rs ├── iter.rs ├── traits.rs ├── bin │ └── sandbox.rs ├── base.rs └── builder.rs ├── .gitignore ├── Cargo.toml ├── LICENSE-MIT ├── README.md └── LICENSE-APACHE /src/runtime/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod memory; -------------------------------------------------------------------------------- /src/shared/runtimes/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod array; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | **/*.rs.bk 3 | Cargo.lock -------------------------------------------------------------------------------- /src/builders/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod ids; 2 | pub mod statement; -------------------------------------------------------------------------------- /src/network/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod ids; 2 | pub mod tests; 3 | -------------------------------------------------------------------------------- /src/shared/runtimes/array/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod builder; 2 | pub mod runtime; -------------------------------------------------------------------------------- /src/shared/nodes/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod alpha; 2 | pub mod beta; 3 | pub mod tests; 4 | -------------------------------------------------------------------------------- /src/shared/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod fact; 2 | pub mod compiler; 3 | pub mod context; 4 | pub mod nodes; 5 | pub mod runtimes; -------------------------------------------------------------------------------- /src/shared/compiler/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod prelude; 2 | pub mod as_ref; 3 | pub mod as_vec; 4 | pub mod builder; 5 | pub mod id_generator; -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | macro_rules! index_id { 2 | ( $( $t:ident),+) => { 3 | $( 4 | impl $t { 5 | pub fn index(&self) -> usize {self.index} 6 | } 7 | 8 | impl Debug for $t { 9 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 10 | write!(f, "{:?}", self.index) 11 | } 12 | } 13 | 14 | impl Into<$t> for usize { 15 | fn into(self) -> $t { 16 | $t{index: self} 17 | } 18 | } 19 | 20 | )* 21 | }; 22 | } 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/network/ids.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::fmt::Debug; 3 | 4 | use serial::SerialGen; 5 | 6 | #[derive(Copy, Clone, Eq, Hash, Ord, PartialOrd, PartialEq)] 7 | pub struct HashEqId{index: usize} 8 | 9 | #[derive(Copy, Clone, Eq, Hash, Ord, PartialOrd, PartialEq)] 10 | pub struct AlphaId{index: usize} 11 | 12 | #[derive(Copy, Clone, Eq, Hash, Ord, PartialOrd, PartialEq)] 13 | pub struct BetaId {index: usize} 14 | 15 | #[derive(Copy, Clone, Eq, Hash, Ord, PartialOrd, PartialEq)] 16 | pub struct InterId {index: usize} 17 | 18 | index_id!(HashEqId, AlphaId, BetaId, InterId); 19 | 20 | pub type HashEqIdGen = SerialGen; 21 | pub type AlphaIdGen = SerialGen; 22 | pub type BetaIdGen = SerialGen; 23 | pub type InterIdGen = SerialGen; 24 | -------------------------------------------------------------------------------- /src/serial.rs: -------------------------------------------------------------------------------- 1 | use num::{One, Zero, Unsigned}; 2 | use std::marker::PhantomData; 3 | 4 | #[derive(Debug)] 5 | pub struct SerialGen, R> { 6 | r: PhantomData, 7 | serial: T 8 | } 9 | 10 | impl, R> SerialGen{ 11 | pub fn new(serial: T) -> SerialGen { 12 | SerialGen{r: PhantomData, serial} 13 | } 14 | 15 | pub fn next(&mut self) -> R { 16 | let prev = self.serial; 17 | self.serial = prev + T::one(); 18 | prev.into() 19 | } 20 | 21 | pub fn check(&self) -> T { 22 | self.serial 23 | } 24 | } 25 | 26 | 27 | impl, R> Default for SerialGen { 28 | fn default() -> Self { 29 | SerialGen::new(T::zero()) 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate failure_derive; 3 | 4 | #[macro_use] 5 | extern crate failure; 6 | 7 | extern crate num; 8 | 9 | #[macro_use] 10 | extern crate itertools; 11 | 12 | extern crate string_interner; 13 | 14 | extern crate ordered_float; 15 | 16 | extern crate float_cmp; 17 | 18 | #[macro_use] 19 | extern crate decimal; 20 | extern crate chrono; 21 | extern crate ord_subset; 22 | 23 | extern crate enum_index; 24 | #[macro_use] 25 | extern crate enum_index_derive; 26 | 27 | #[macro_use] 28 | extern crate anymap; 29 | 30 | extern crate bimap; 31 | 32 | #[macro_use] 33 | mod macros; 34 | 35 | pub mod base; 36 | pub mod traits; 37 | pub mod network; 38 | pub mod runtime; 39 | pub mod serial; 40 | pub mod builder; 41 | pub mod builders; 42 | pub mod iter; 43 | pub mod errors; 44 | pub mod shared; 45 | 46 | pub use failure::Error; 47 | 48 | type Result = std::result::Result; 49 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "expert" 3 | version = "0.1.0" 4 | authors = ["Adam Baxter "] 5 | license = "MIT/Apache-2.0" 6 | readme = "README.md" 7 | repository = "https://github.com/ambaxter/expert-rs.git" 8 | homepage = "https://github.com/ambaxter/expert-rs" 9 | description = "A cache-friendly, typesafe expert/business rules system" 10 | categories = ["algorithms", "data-structures"] 11 | keywords = ["rete", "expert", "business", "rules", "brms", "decision", "manager"] 12 | 13 | [dependencies] 14 | num = "0.2" 15 | itertools = "0.7" 16 | string-interner = "0.7" 17 | ordered-float = "0.5" 18 | float-cmp = "0.4" 19 | parking_lot = {version = "0.6", features = ["nightly"]} 20 | failure = "0.1" 21 | failure_derive = "0.1" 22 | decimal = "^2" 23 | chrono = "0.4" 24 | ord_subset = "^3" 25 | enum_index = "0.2.0" 26 | enum_index_derive = "0.2.0" 27 | anymap = "0.12.1" 28 | bimap = "0.1.5" -------------------------------------------------------------------------------- /src/errors.rs: -------------------------------------------------------------------------------- 1 | use shared::compiler::id_generator::StatementId; 2 | 3 | #[derive(Debug, Fail)] 4 | pub enum CompileError { 5 | #[fail(display = "Invalid getter {}. Expected a {}, found {}", getter, to, from)] 6 | IncorrectGetter { 7 | getter: String, 8 | to: String, 9 | from: String 10 | }, 11 | #[fail(display = "No getter {}", getter)] 12 | MissingGetter { 13 | getter: String 14 | }, 15 | #[fail(display = "No setter {}", setter)] 16 | MissingSetter { 17 | setter: String 18 | }, 19 | #[fail(display = "Rule {} declares duplicate variable on statement {:?}", rule, statement_id)] 20 | MultipleVariables { 21 | rule: String, 22 | statement_id: StatementId, 23 | }, 24 | #[fail(display = "Rule {} declares duplicate field {}", rule, field)] 25 | DuplicateFields { 26 | rule: String, 27 | field: String 28 | } 29 | } -------------------------------------------------------------------------------- /src/shared/runtimes/array/runtime.rs: -------------------------------------------------------------------------------- 1 | use shared::fact::Fact; 2 | use shared::nodes::alpha::AlphaNode; 3 | use shared::nodes::beta::BetaNode; 4 | 5 | #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] 6 | pub struct AlphaNodeData { 7 | parent: usize, 8 | children_index: usize, 9 | children_len: usize, 10 | is_memory: bool, 11 | } 12 | 13 | #[derive(Debug)] 14 | pub struct AlphaTree { 15 | tests: Vec>, 16 | data: Vec, 17 | } 18 | 19 | #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] 20 | pub enum BetaGroupType { 21 | ALL, 22 | NOTALL, 23 | ANY, 24 | NOTANY 25 | } 26 | 27 | #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] 28 | pub struct BetaNodeData { 29 | parent: usize, 30 | child_tests_index: usize, 31 | child_test_len: usize, 32 | child_data_index: usize, 33 | child_data_len: usize, 34 | } 35 | 36 | #[derive(Debug)] 37 | pub struct BetaTree { 38 | data: Vec, 39 | tests: Vec>, 40 | } -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Adam Baxter 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/shared/context.rs: -------------------------------------------------------------------------------- 1 | use ::runtime::memory::SymbolId; 2 | use ord_subset::OrdVar; 3 | use decimal::d128; 4 | use chrono::{NaiveTime, Date, DateTime, Duration, Utc}; 5 | use ordered_float::NotNaN; 6 | use runtime::memory::StringCache; 7 | 8 | pub trait AlphaContext { 9 | fn get_string_cache(&self) -> &StringCache; 10 | } 11 | 12 | pub trait BetaContext { 13 | fn get_bool(&self, sym: SymbolId) -> &bool; 14 | fn get_i8(&self, sym: SymbolId) -> i8; 15 | fn get_i16(&self, sym: SymbolId) -> i16; 16 | fn get_i32(&self, sym: SymbolId) -> i32; 17 | fn get_i64(&self, sym: SymbolId) -> i64; 18 | fn get_i128(&self, sym: SymbolId) -> i128; 19 | fn get_u8(&self, sym: SymbolId) -> u8; 20 | fn get_u16(&self, sym: SymbolId) -> u16; 21 | fn get_u32(&self, sym: SymbolId) -> u32; 22 | fn get_u64(&self, sym: SymbolId) -> u64; 23 | fn get_u128(&self, sym: SymbolId) -> u128; 24 | fn get_f32(&self, sym: SymbolId) -> NotNaN; 25 | fn get_f64(&self, sym: SymbolId) -> NotNaN; 26 | fn get_d128(&self, sym: SymbolId) -> OrdVar; 27 | fn get_str(&self, sym: SymbolId) -> &str; 28 | fn get_time(&self, sym: SymbolId) -> &NaiveTime; 29 | fn get_date(&self, sym: SymbolId) -> &Date; 30 | fn get_datetime(&self, sym: SymbolId) -> &DateTime; 31 | fn get_string_cache(&self) -> &StringCache; 32 | } -------------------------------------------------------------------------------- /src/builders/ids.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::fmt::Debug; 3 | use serial::SerialGen; 4 | 5 | #[derive(Copy, Clone, Eq, Hash, Ord, PartialOrd, PartialEq)] 6 | pub struct RuleId{index: usize} 7 | 8 | #[derive(Copy, Clone, Eq, Hash, Ord, PartialOrd, PartialEq)] 9 | pub struct StatementId{index: usize} 10 | 11 | #[derive(Copy, Clone, Eq, Hash, Ord, PartialOrd, PartialEq)] 12 | pub struct ConditionId{index: usize} 13 | 14 | index_id!(RuleId, StatementId, ConditionId); 15 | 16 | pub type RuleIdGen = SerialGen; 17 | pub type StatementIdGen = SerialGen; 18 | pub type ConditionIdGen = SerialGen; 19 | 20 | pub struct BuilderIdGen { 21 | rule_ids: RuleIdGen, 22 | statement_ids: StatementIdGen, 23 | condition_ids: ConditionIdGen 24 | } 25 | 26 | impl BuilderIdGen { 27 | pub fn new() -> BuilderIdGen { 28 | BuilderIdGen{ 29 | rule_ids: Default::default(), 30 | statement_ids: Default::default(), 31 | condition_ids: Default::default() 32 | } 33 | } 34 | 35 | pub fn next_rule_id(&mut self) -> RuleId { 36 | self.rule_ids.next() 37 | } 38 | 39 | pub fn next_statement_id(&mut self) -> StatementId { 40 | self.statement_ids.next() 41 | } 42 | 43 | pub fn next_condition_id(&mut self) -> ConditionId { 44 | self.condition_ids.next() 45 | } 46 | } 47 | 48 | impl Default for BuilderIdGen { 49 | fn default() -> Self { 50 | BuilderIdGen::new() 51 | } 52 | } -------------------------------------------------------------------------------- /src/runtime/memory.rs: -------------------------------------------------------------------------------- 1 | use string_interner::{StringInterner, Symbol}; 2 | use std::fmt; 3 | use std::fmt::Debug; 4 | use std::any::TypeId; 5 | 6 | use network::ids::*; 7 | 8 | #[derive(Copy, Clone, Eq, Hash, Ord, PartialOrd, PartialEq)] 9 | pub struct SymbolId { 10 | id: usize, 11 | } 12 | 13 | impl Symbol for SymbolId { 14 | fn from_usize(val: usize) -> Self { 15 | SymbolId{id: val} 16 | } 17 | 18 | fn to_usize(self) -> usize { 19 | self.id 20 | } 21 | } 22 | 23 | impl Debug for SymbolId { 24 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 25 | write!(f, "{:?}", self.id) 26 | } 27 | } 28 | 29 | pub type StringCache = StringInterner; 30 | 31 | pub trait AlphaMemoryId {} 32 | 33 | #[derive(Debug, Copy, Clone, Eq, Hash, Ord, PartialOrd, PartialEq)] 34 | pub enum MemoryId { 35 | HashEq(HashEqId), 36 | Alpha(AlphaId), 37 | Beta(BetaId) 38 | } 39 | 40 | impl Into for HashEqId { 41 | fn into(self) -> MemoryId { 42 | MemoryId::HashEq(self) 43 | } 44 | } 45 | 46 | impl AlphaMemoryId for HashEqId {} 47 | 48 | impl Into for AlphaId { 49 | fn into(self) -> MemoryId { 50 | MemoryId::Alpha(self) 51 | } 52 | } 53 | 54 | impl AlphaMemoryId for AlphaId {} 55 | 56 | impl Into for BetaId { 57 | fn into(self) -> MemoryId { 58 | MemoryId::Beta(self) 59 | } 60 | } 61 | 62 | #[derive(Debug, Copy, Clone, Eq, Hash, Ord, PartialOrd, PartialEq)] 63 | pub enum InterMemoryId { 64 | HashEq(TypeId, HashEqId), 65 | Alpha(TypeId, AlphaId), 66 | Beta(TypeId, BetaId), 67 | Inter(InterId) 68 | } -------------------------------------------------------------------------------- /src/iter.rs: -------------------------------------------------------------------------------- 1 | #[derive(Copy, Clone)] 2 | pub struct OptionIter { 3 | is_some: bool, 4 | is_none: bool, 5 | t: Option 6 | } 7 | 8 | impl OptionIter { 9 | pub fn new(t: Option) -> Self { 10 | let is_some = t.is_some(); 11 | let is_none = true; 12 | OptionIter{is_some, is_none, t} 13 | } 14 | 15 | pub fn some(t: T) -> Self { 16 | Self::new(Some(t)) 17 | } 18 | 19 | pub fn none() -> Self { 20 | Self::new(None) 21 | } 22 | } 23 | 24 | impl Iterator for OptionIter { 25 | type Item = Option; 26 | 27 | fn next(&mut self) -> Option { 28 | if self.is_some { 29 | self.is_some = false; 30 | return Some(self.t); 31 | } else if self.is_none { 32 | self.is_none = false; 33 | return Some(None); 34 | } 35 | None 36 | } 37 | } 38 | 39 | /* Thanks bluss! - owe you alcohol of choice 40 | 41 | pub fn product(mut iterators: Vec, mut cb: F) 42 | where I: Iterator + Clone, 43 | F: FnMut(&[I::Item]) 44 | { 45 | inner(&mut Vec::with_capacity(iterators.len()), 46 | &iterators.clone(), &mut iterators, &mut cb) 47 | } 48 | 49 | fn inner(cur: &mut Vec, 50 | orig: &[I], iters: &mut [I], cb: &mut F) 51 | where I: Iterator + Clone, 52 | F: FnMut(&[I::Item]) 53 | { 54 | if let Some((front, rest)) = iters.split_first_mut() { 55 | for elt in &mut *front { 56 | cur.push(elt); 57 | inner(cur, &orig[1..], rest, cb); 58 | cur.pop(); 59 | } 60 | if !cur.is_empty() { 61 | *front = orig[0].clone(); 62 | } 63 | } else { 64 | cb(cur.as_slice()) 65 | } 66 | } 67 | 68 | 69 | fn main() { 70 | let iter = vec![0..3, 1..2, 0..5, 0..3]; 71 | 72 | product(iter, |elems| println!("{:?}", elems)); 73 | } 74 | */ -------------------------------------------------------------------------------- /src/shared/compiler/id_generator.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use serial::SerialGen; 3 | 4 | macro_rules! id_generator { 5 | ( $( $t:ident => $g:ident => $f:ident),+) => { 6 | $( 7 | #[derive(Copy, Clone, Eq, Hash, Ord, PartialOrd, PartialEq)] 8 | pub struct $t{index: usize} 9 | 10 | impl $t { 11 | pub fn index(&self) -> usize {self.index} 12 | } 13 | 14 | impl fmt::Debug for $t { 15 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 16 | write!(f, "{:?}", self.index) 17 | } 18 | } 19 | 20 | impl From for $t { 21 | fn from( index: usize) -> $t { 22 | $t{index} 23 | } 24 | } 25 | 26 | pub type $g = SerialGen; 27 | 28 | )* 29 | 30 | #[derive(Debug)] 31 | pub struct IdGenerator { 32 | $( 33 | pub $f : $g, 34 | )* 35 | } 36 | 37 | impl IdGenerator { 38 | pub fn new() -> IdGenerator { 39 | IdGenerator { 40 | $( 41 | $f: Default::default(), 42 | )* 43 | } 44 | } 45 | } 46 | 47 | impl Default for IdGenerator { 48 | fn default() -> Self { 49 | IdGenerator::new() 50 | } 51 | } 52 | }; 53 | } 54 | 55 | id_generator!( 56 | RuleId => RuleIdGen => rule_ids, 57 | StatementId => StatementIdGen => statement_ids, 58 | StatementGroupId => StatementGroupIdGen => statement_group_ids, 59 | ConditionId => ConditionIdGen => condition_ids, 60 | ConditionGroupId => ConditionGroupIdGen => condition_group_ids, 61 | HashEqId => HashEqIdGen => hasheq_ids, 62 | AlphaId => AlphaIdGen => alpha_ids, 63 | BetaId => BetaIdGen => beta_ids 64 | ); 65 | 66 | -------------------------------------------------------------------------------- /src/shared/compiler/builder.rs: -------------------------------------------------------------------------------- 1 | use super::prelude::Stage1Compile; 2 | use errors::CompileError; 3 | use shared::compiler::prelude::{DrainWhere, ProvidesNode}; 4 | use std; 5 | use shared::fact::Fact; 6 | 7 | // Have a single KnowledgeBuilder trait 8 | // Have a single RuleBuilder trait 9 | // Have a single Struct that implements both traits 10 | // it switches between the two traits via impl trait on the entrance and exit functions 11 | // stores current rule_id (0 by default) 12 | // fetches each rule during modification (set name, salience, other options) 13 | // 14 | //pub struct KnowledgeBaseBuilder { 15 | // 16 | //} 17 | // 18 | //pub struct RuleBuilder { 19 | // cache: StringCache, 20 | //} 21 | // 22 | //impl RuleBuilder { 23 | // 24 | // pub fn when>(mut self, nodes: &[N]) -> RuleBuilder { 25 | // Stage1Node::All(N::stage1_compile_slice(nodes, &mut self.cache).unwrap()).clean(); 26 | // self 27 | // } 28 | // 29 | //} 30 | 31 | pub trait KnowledgeBase { 32 | 33 | } 34 | 35 | pub trait BaseBuilder { 36 | type RB: RuleBuilder; 37 | type KB: KnowledgeBase; 38 | 39 | fn rule>(self, name: S) -> Self::RB; 40 | fn rule_with_agenda, A: AsRef>(mut self, name: S, agenda_group: A) -> Self::RB; 41 | fn end(self) -> Self::KB; 42 | } 43 | 44 | pub trait RuleBuilder { 45 | type CB: ConsequenceBuilder; 46 | 47 | fn salience(self, salience: i32) -> Self; 48 | fn no_loop(self, no_loop: bool) -> Self; 49 | fn when>(self, nodes: &[N]) -> Result 50 | where Self: std::marker::Sized; 51 | fn provides_when, N: Stage1Compile>(self, provides: &[ProvidesNode], nodes: &[N]) -> Result 52 | where Self: std::marker::Sized; 53 | fn when_exists>(self, nodes: &[N]) -> Result 54 | where Self: std::marker::Sized; 55 | fn when_absent>(self, nodes: &[N]) -> Result 56 | where Self: std::marker::Sized; 57 | fn when_for_all>(self, node: &[N]) -> Result 58 | where Self: std::marker::Sized; 59 | fn provides_when_for_all, N: Stage1Compile>(self, provides: &[ProvidesNode], nodes: &[N]) -> Result 60 | where Self: std::marker::Sized; 61 | fn all_group(self) -> Self; 62 | fn any_group(self) -> Self; 63 | fn end_group(self) -> Result where Self: std::marker::Sized; 64 | fn then(self) -> Result; 65 | } 66 | 67 | pub trait ConsequenceBuilder { 68 | type BB: BaseBuilder; 69 | fn end(self) -> Result; 70 | } 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # expert-rs 2 | 3 | A cache friendly and type safe expert/business rules system written in Rust. 4 | 5 | # Usage 6 | 7 | Don't. It's not even Alpha quality, yet. 8 | 9 | Eventually you'll be able to add a dependency ala 10 | 11 | ```toml 12 | [dependencies] 13 | expert = "0.10" 14 | ``` 15 | 16 | And then write code similar to 17 | 18 | ```rust 19 | #[macro_use] 20 | extern crate expert; 21 | 22 | 23 | // A fact type 24 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Fact)] 25 | pub struct Fact1 { 26 | id: usize, 27 | len: usize, 28 | } 29 | 30 | 31 | // Another fact type 32 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Fact)] 33 | pub struct Fact2 { 34 | id: usize, 35 | height: usize 36 | } 37 | 38 | mod example { 39 | // Creates a new expert system specifically for Fact1 and Fact2 40 | expert!(Example; [Fact1, Fact2]); 41 | } 42 | 43 | pub fn main() { 44 | use example::*; 45 | 46 | // Create a custom rule builder 47 | let mut builder = ExampleBuilder::new(); 48 | builder. 49 | // Add a new rule named "rule" 50 | rule("rule") 51 | // When a Fact1 is inserted with self.id == 2 and self.len < 32 52 | .when().eq("id", 2usize).lt("len", 32usize) 53 | // And when a Fact2 is inserted with sef.id ==3 and self.height >= 64 54 | .when().eq("id", 17usize).gte("height", 64usize) 55 | .then() 56 | // Add a new Fact1{id: 3, len: 20} to be available for return 57 | .ret().field("id", 3usize).field("len", 20usize) 58 | .end(); 59 | 60 | // Create a knowledge base to hold the read-only, in-memory representation of all of the rules 61 | let example_base = builder.build(); 62 | 63 | // Create a knowledge session/rule runtime 64 | let mut example_session = example_base.new_session(); 65 | 66 | let a_fact = Fact1{id: 2usize, len: 31usize}; 67 | let another_fact = Fact2{id: 3usize, height: 60usize}; 68 | 69 | // Insert the facts 70 | example_session.insert(a_fact); 71 | example_session.insert(another_fact); 72 | 73 | // Resolve the rule consequences 74 | example_session.resolve(); 75 | 76 | let fact1_returns: &[Fact1] = ::get_returns(&example_session); 77 | assert(fact1_returns.len() == 1); 78 | let fact1_ret = fact1_returns.get(0).unwrap(); 79 | assert(fact1_ret.id == 3); 80 | assert(fact1_ret.len == 20); 81 | } 82 | 83 | ``` 84 | 85 | # Features 86 | 87 | * The code compiles! 88 | * Alpha (fact analysis) nodes! 89 | * Statement Builder! 90 | 91 | # In-Progress 92 | 93 | * Rule Builder! 94 | * KnowledgeBase Builder! 95 | * Alpha, Beta node compilation and layout! 96 | * Networks represented as a set of arrays! 97 | * Alpha network ordered from most depended on nodes to least! 98 | * Beta network ordered from least depended to most! 99 | * Chaining! 100 | * Forwards! 101 | * Backwards! (w/ Iterators) 102 | * Returns, Insert within rule resolution 103 | * All of those sweet, sweet macros 104 | 105 | # Not In-Progress (everything else!) 106 | 107 | * Variables 108 | * Fact 109 | * Field 110 | * Complex logic ((this AND this AND NOT that) OR other) 111 | * DSL 112 | * Fast rule compilation times 113 | * Rule optimizations 114 | * Rule sanity checks 115 | * In-place fact modification 116 | 117 | # Why? 118 | 119 | * Expert/Business Rules Systems are still in use in a variety of industries 120 | * I wanted to understand how the algorithms worked 121 | * I wanted to push my software development expertise 122 | * I wanted to see if I could write it without pointers, fat or otherwise 123 | * It's hard 124 | 125 | 126 | 127 | 128 | # License 129 | 130 | This project is licensed under either of 131 | 132 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or 133 | http://www.apache.org/licenses/LICENSE-2.0) 134 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or 135 | http://opensource.org/licenses/MIT) 136 | 137 | at your option. 138 | 139 | ### Contribution 140 | 141 | Unless you explicitly state otherwise, any contribution intentionally submitted 142 | for inclusion in Metis by you, as defined in the Apache-2.0 license, shall be 143 | dual licensed as above, without any additional terms or conditions. -------------------------------------------------------------------------------- /src/traits.rs: -------------------------------------------------------------------------------- 1 | use std; 2 | use std::any::TypeId; 3 | use std::hash::Hash; 4 | use builders::statement::{ConditionDesc, StatementConditions}; 5 | use network::tests::AlphaTest; 6 | use ::builder::StatementCondition; 7 | use runtime::memory::{SymbolId, StringCache}; 8 | use std::collections::{HashMap, HashSet}; 9 | use std::fmt::Debug; 10 | use std::fmt; 11 | use ordered_float::NotNaN; 12 | use builders::ids::*; 13 | 14 | pub trait Introspect { 15 | fn static_type_id() -> TypeId; 16 | } 17 | 18 | #[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialOrd, PartialEq)] 19 | pub enum FieldValue { 20 | BOOL(SymbolId, bool), 21 | I8(SymbolId, i8), 22 | I16(SymbolId, i16), 23 | I32(SymbolId, i32), 24 | I64(SymbolId, i64), 25 | U8(SymbolId, u8), 26 | U16(SymbolId, u16), 27 | U32(SymbolId, u32), 28 | U64(SymbolId, u64), 29 | ISIZE(SymbolId, isize), 30 | USIZE(SymbolId, usize), 31 | F32(SymbolId, NotNaN), 32 | F64(SymbolId, NotNaN), 33 | STR(SymbolId, SymbolId), 34 | } 35 | 36 | impl FieldValue { 37 | pub fn field(&self) -> SymbolId { 38 | use self::FieldValue::*; 39 | match self { 40 | &BOOL(field, _) => field, 41 | &I8(field, _) => field, 42 | &I16(field, _) => field, 43 | &I32(field, _) => field, 44 | &I64(field, _) => field, 45 | &U8(field, _) => field, 46 | &U16(field, _) => field, 47 | &U32(field, _) => field, 48 | &U64(field, _) => field, 49 | &ISIZE(field, _) => field, 50 | &USIZE(field, _) => field, 51 | &F32(field, _) => field, 52 | &F64(field, _) => field, 53 | &STR(field, _) => field, 54 | } 55 | } 56 | } 57 | 58 | #[derive(Copy, Clone)] 59 | pub enum Getters { 60 | BOOL(fn(&I) -> &bool), 61 | I8(fn(&I) -> &i8), 62 | I16(fn(&I) -> &i16), 63 | I32(fn(&I) -> &i32), 64 | I64(fn(&I) -> &i64), 65 | U8(fn(&I) -> &u8), 66 | U16(fn(&I) -> &u16), 67 | U32(fn(&I) -> &u32), 68 | U64(fn(&I) -> &u64), 69 | ISIZE(fn(&I) -> &isize), 70 | USIZE(fn(&I) -> &usize), 71 | F32(fn(&I) -> &f32), 72 | F64(fn(&I) -> &f64), 73 | STR(fn(&I) -> &str), 74 | } 75 | 76 | impl Debug for Getters { 77 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 78 | use self::Getters::*; 79 | write!(f, "Getters(")?; 80 | match self { 81 | &BOOL(accessor) => write!(f, "BOOL({:#x})", accessor as usize)?, 82 | &I8(accessor) => write!(f, "I8({:#x})", accessor as usize)?, 83 | &I16(accessor) => write!(f, "I16({:#x})", accessor as usize)?, 84 | &I32(accessor) => write!(f, "I32({:#x})", accessor as usize)?, 85 | &I64(accessor) => write!(f, "I64({:#x})", accessor as usize)?, 86 | &U8(accessor) => write!(f, "U8({:#x})", accessor as usize)?, 87 | &U16(accessor) => write!(f, "U16({:#x})", accessor as usize)?, 88 | &U32(accessor) => write!(f, "U32({:#x})", accessor as usize)?, 89 | &U64(accessor) => write!(f, "U64({:#x})", accessor as usize)?, 90 | &ISIZE(accessor) => write!(f, "ISIZE({:#x})", accessor as usize)?, 91 | &USIZE(accessor) => write!(f, "USIZE({:#x})", accessor as usize)?, 92 | &F32(accessor) => write!(f, "F32({:#x})", accessor as usize)?, 93 | &F64(accessor) => write!(f, "F64({:#x})", accessor as usize)?, 94 | &STR(accessor) => write!(f, "STR({:#x})", accessor as usize)?, 95 | _ => {} 96 | } 97 | write!(f, ")") 98 | } 99 | } 100 | 101 | pub trait Fact: Introspect + Eq + Hash 102 | where Self: std::marker::Sized { 103 | type HashEq: Hash + Eq + Clone + Debug; 104 | fn create_hash_eq(conditions: &Vec, cache: &StringCache) -> Self::HashEq; 105 | fn new_from_fields(fields: &[FieldValue], cache: &StringCache) -> Self; 106 | fn getter(field: &str) -> Option>; 107 | fn exhaustive_hash(&self) -> Box>; 108 | } 109 | 110 | pub trait NetworkBuilder { 111 | fn get_id_generator(&mut self) -> &mut BuilderIdGen; 112 | fn get_conditions(&mut self) -> &mut HashMap, ConditionDesc>>; 113 | fn get_string_cache(&mut self) -> &mut StringCache; 114 | 115 | } 116 | 117 | pub trait RuleBuilder { 118 | fn get_for_condition_collapse(&mut self, hash_eq: I::HashEq) -> (&mut StringCache, &mut BuilderIdGen, &mut HashMap, ConditionDesc>); 119 | fn get_id_generator(&mut self) -> &mut BuilderIdGen; 120 | fn get_conditions(&mut self) -> &mut HashMap, ConditionDesc>>; 121 | fn get_statement_ids(&mut self) -> &mut HashSet; 122 | fn get_string_cache(&mut self) -> &mut StringCache; 123 | } 124 | 125 | 126 | pub trait ReteIntrospection : Eq + Hash { 127 | type HashEq: Hash + Eq + Clone + Debug; 128 | 129 | fn static_type_id() -> TypeId; 130 | fn create_hash_eq(conditions: &Vec, string_interner: &StringCache) -> Self::HashEq; 131 | fn getter(field: &str) -> Option &u64>; 132 | fn type_id(&self) -> TypeId; 133 | } 134 | 135 | -------------------------------------------------------------------------------- /src/shared/compiler/as_vec.rs: -------------------------------------------------------------------------------- 1 | use super::prelude::*; 2 | pub use super::prelude::dyn; 3 | use super::super::nodes::tests::{ApplyNot, EqTest, OrdTest, BetweenTest, StrArrayTest}; 4 | use super::super::nodes::beta::TestRepr; 5 | use runtime::memory::StringCache; 6 | use shared::fact::Fact; 7 | use errors::CompileError; 8 | 9 | #[derive(Clone, Hash, Eq, PartialEq, Debug)] 10 | pub enum VecNodes> { 11 | Test(TestRepr), 12 | Any(Vec>), 13 | NotAny(Vec>), 14 | All(Vec>), 15 | NotAll(Vec>), 16 | } 17 | 18 | pub fn eq, T: IntoEqTest>(field: S, to: T) -> VecNodes { 19 | VecNodes::Test(to.into_eq_test(field, EqTest::Eq)) 20 | } 21 | 22 | pub fn ne, T: IntoEqTest>(field: S, to: T) -> VecNodes { 23 | VecNodes::Test(to.into_eq_test(field, EqTest::Ne)) 24 | } 25 | 26 | pub fn lt, T: IntoOrdTest>(field: S, to: T) -> VecNodes { 27 | VecNodes::Test(to.into_ord_test(field, OrdTest::Lt)) 28 | } 29 | 30 | pub fn le, T: IntoOrdTest>(field: S, to: T) -> VecNodes { 31 | VecNodes::Test(to.into_ord_test(field, OrdTest::Le)) 32 | } 33 | 34 | pub fn gt, T: IntoOrdTest>(field: S, to: T) -> VecNodes { 35 | VecNodes::Test(to.into_ord_test(field, OrdTest::Gt)) 36 | } 37 | 38 | pub fn ge, T: IntoOrdTest>(field: S, to: T) -> VecNodes { 39 | VecNodes::Test(to.into_ord_test(field, OrdTest::Ge)) 40 | } 41 | 42 | pub fn gtlt, T>(field: S, from: T, to: T) -> VecNodes 43 | where (T, T): IntoBtwnTest{ 44 | VecNodes::Test((from, to).into_btwn_test(field, BetweenTest::GtLt)) 45 | } 46 | 47 | pub fn gelt, T>(field: S, from: T, to: T) -> VecNodes 48 | where (T, T): IntoBtwnTest{ 49 | VecNodes::Test((from, to).into_btwn_test(field, BetweenTest::GeLt)) 50 | } 51 | 52 | pub fn gtle, T>(field: S, from: T, to: T) -> VecNodes 53 | where (T, T): IntoBtwnTest{ 54 | VecNodes::Test((from, to).into_btwn_test(field, BetweenTest::GtLe)) 55 | } 56 | 57 | pub fn gele, T>(field: S, from: T, to: T) -> VecNodes 58 | where (T, T): IntoBtwnTest{ 59 | VecNodes::Test((from, to).into_btwn_test(field, BetweenTest::GeLe)) 60 | } 61 | 62 | pub fn contains, T: IntoStrTest>(field: S, val: T) -> VecNodes { 63 | VecNodes::Test(val.into_str_test(field, StrArrayTest::Contains)) 64 | } 65 | 66 | pub fn starts_with, T: IntoStrTest>(field: S, val: T) -> VecNodes { 67 | VecNodes::Test(val.into_str_test(field, StrArrayTest::StartsWith)) 68 | } 69 | 70 | pub fn ends_with, T: IntoStrTest>(field: S, val: T) -> VecNodes { 71 | VecNodes::Test(val.into_str_test(field, StrArrayTest::EndsWith)) 72 | } 73 | 74 | pub fn not>(node: VecNodes) -> VecNodes { 75 | use self::VecNodes::*; 76 | match node { 77 | Test(mut t) => { 78 | t.apply_not(); 79 | Test(t) 80 | }, 81 | Any(t) => NotAny(t), 82 | NotAny(t) => Any(t), 83 | All(t) => NotAll(t), 84 | NotAll(t) => All(t) 85 | } 86 | } 87 | 88 | pub fn any>(nodes: Vec>) -> VecNodes { 89 | VecNodes::Any(nodes) 90 | } 91 | 92 | pub fn all>(nodes: Vec>) -> VecNodes { 93 | VecNodes::All(nodes) 94 | } 95 | 96 | 97 | impl, T: Fact> Stage1Compile for VecNodes { 98 | fn stage1_compile(&self, cache: &mut StringCache) -> Result, CompileError> { 99 | use self::VecNodes::*; 100 | match *self { 101 | Test(ref t) => Ok(Stage1Node::Test(t.compile(cache)?)), 102 | Any(ref v) => Ok(Stage1Node::Any(Stage1Compile::stage1_compile_slice(v, cache)?)), 103 | NotAny(ref v) => Ok(Stage1Node::NotAny(Stage1Compile::stage1_compile_slice(v, cache)?)), 104 | All(ref v) => Ok(Stage1Node::All(Stage1Compile::stage1_compile_slice(v, cache)?)), 105 | NotAll(ref v) => Ok(Stage1Node::NotAny(Stage1Compile::stage1_compile_slice(v, cache)?)), 106 | } 107 | } 108 | } 109 | 110 | 111 | 112 | #[cfg(test)] 113 | mod tests { 114 | use shared::fact::{Fact, Getter}; 115 | use super::*; 116 | use shared::nodes::alpha::HashEqField; 117 | 118 | #[derive(Clone, Hash, Eq, PartialEq, Debug)] 119 | struct Dummy { 120 | d: u64 121 | } 122 | 123 | impl Dummy { 124 | fn get_d(&self) -> &u64 { 125 | &self.d 126 | } 127 | } 128 | 129 | impl Fact for Dummy { 130 | type HashEq = (); 131 | 132 | fn getter(field: &str) -> Option> { 133 | match field { 134 | "d" => Some(Getter::U64(Dummy::get_d)), 135 | _ => unimplemented!() 136 | } 137 | } 138 | 139 | fn exhaustive_hash(&self) -> Box::HashEq>> { 140 | unimplemented!() 141 | } 142 | 143 | fn create_hash_eq(conditions: &Vec, cache: &StringCache) -> Self::HashEq { 144 | unimplemented!() 145 | } 146 | } 147 | 148 | #[test] 149 | pub fn as_vec_test() { 150 | let mut cache = StringCache::new(); 151 | let nodes: Stage1Node = all( 152 | vec![not(any(vec![eq("d", 6u64), any(vec![gt("d", 1024u64)])])), all(vec![le("d", 64u64), le("d", dyn("ab"))])] 153 | ).stage1_compile(&mut cache).unwrap() 154 | .clean(); 155 | println!("{:?}", nodes); 156 | } 157 | } -------------------------------------------------------------------------------- /src/shared/compiler/as_ref.rs: -------------------------------------------------------------------------------- 1 | use super::prelude::*; 2 | pub use super::prelude::dyn; 3 | use super::super::nodes::tests::{ApplyNot, EqTest, OrdTest, BetweenTest, StrArrayTest}; 4 | use super::super::nodes::beta::TestRepr; 5 | use runtime::memory::StringCache; 6 | use errors::CompileError; 7 | use shared::fact::Fact; 8 | 9 | #[derive(Clone, Hash, Eq, PartialEq, Debug)] 10 | pub enum RefNodes<'a, S: 'a + AsRef> { 11 | Test(TestRepr), 12 | Any(&'a [RefNodes<'a, S>]), 13 | NotAny(&'a [RefNodes<'a, S>]), 14 | All(&'a [RefNodes<'a, S>]), 15 | NotAll(&'a [RefNodes<'a, S>]) 16 | } 17 | 18 | pub fn eq<'a, S: AsRef, T: IntoEqTest>(field: S, to: T) -> RefNodes<'a, S> { 19 | RefNodes::Test(to.into_eq_test(field, EqTest::Eq)) 20 | } 21 | 22 | pub fn ne<'a, S: AsRef, T: IntoEqTest>(field: S, to: T) -> RefNodes<'a, S> { 23 | RefNodes::Test(to.into_eq_test(field, EqTest::Ne)) 24 | } 25 | 26 | pub fn lt<'a, S: AsRef, T: IntoOrdTest>(field: S, to: T) -> RefNodes<'a, S> { 27 | RefNodes::Test(to.into_ord_test(field, OrdTest::Lt)) 28 | } 29 | 30 | pub fn le<'a, S: AsRef, T: IntoOrdTest>(field: S, to: T) -> RefNodes<'a, S> { 31 | RefNodes::Test(to.into_ord_test(field, OrdTest::Le)) 32 | } 33 | 34 | pub fn gt<'a, S: AsRef, T: IntoOrdTest>(field: S, to: T) -> RefNodes<'a, S> { 35 | RefNodes::Test(to.into_ord_test(field, OrdTest::Gt)) 36 | } 37 | 38 | pub fn ge<'a, S: AsRef, T: IntoOrdTest>(field: S, to: T) -> RefNodes<'a, S> { 39 | RefNodes::Test(to.into_ord_test(field, OrdTest::Ge)) 40 | } 41 | 42 | pub fn gtlt<'a, S: AsRef, T>(field: S, from: T, to: T) -> RefNodes<'a, S> 43 | where (T, T): IntoBtwnTest{ 44 | RefNodes::Test((from, to).into_btwn_test(field, BetweenTest::GtLt)) 45 | } 46 | 47 | pub fn gelt<'a, S: AsRef, T>(field: S, from: T, to: T) -> RefNodes<'a, S> 48 | where (T, T): IntoBtwnTest{ 49 | RefNodes::Test((from, to).into_btwn_test(field, BetweenTest::GeLt)) 50 | } 51 | 52 | pub fn gtle<'a, S: AsRef, T>(field: S, from: T, to: T) -> RefNodes<'a, S> 53 | where (T, T): IntoBtwnTest{ 54 | RefNodes::Test((from, to).into_btwn_test(field, BetweenTest::GtLe)) 55 | } 56 | 57 | pub fn gele<'a, S: AsRef, T>(field: S, from: T, to: T) -> RefNodes<'a, S> 58 | where (T, T): IntoBtwnTest{ 59 | RefNodes::Test((from, to).into_btwn_test(field, BetweenTest::GeLe)) 60 | } 61 | 62 | pub fn contains<'a, S: AsRef, T: IntoStrTest>(field: S, val: T) -> RefNodes<'a, S> { 63 | RefNodes::Test(val.into_str_test(field, StrArrayTest::Contains)) 64 | } 65 | 66 | pub fn starts_with<'a, S: AsRef, T: IntoStrTest>(field: S, val: T) -> RefNodes<'a, S> { 67 | RefNodes::Test(val.into_str_test(field, StrArrayTest::StartsWith)) 68 | } 69 | 70 | pub fn ends_with<'a, S: AsRef, T: IntoStrTest>(field: S, val: T) -> RefNodes<'a, S> { 71 | RefNodes::Test(val.into_str_test(field, StrArrayTest::EndsWith)) 72 | } 73 | 74 | pub fn not>(node: RefNodes) -> RefNodes { 75 | use self::RefNodes::*; 76 | match node { 77 | Test(mut t) => { 78 | t.apply_not(); 79 | Test(t) 80 | }, 81 | Any(t) => NotAny(t), 82 | NotAny(t) => Any(t), 83 | All(t) => NotAll(t), 84 | NotAll(t) => All(t) 85 | } 86 | } 87 | 88 | pub fn any<'a, S: AsRef>(nodes: &'a[RefNodes<'a, S>]) -> RefNodes<'a, S> { 89 | RefNodes::Any(nodes) 90 | } 91 | 92 | pub fn all<'a, S: AsRef>(nodes: &'a[RefNodes<'a, S>]) -> RefNodes<'a, S> { 93 | RefNodes::All(nodes) 94 | } 95 | 96 | impl<'a, S: AsRef, T: Fact> Stage1Compile for RefNodes<'a, S> { 97 | fn stage1_compile(&self, cache: &mut StringCache) -> Result, CompileError> { 98 | use self::RefNodes::*; 99 | match *self { 100 | Test(ref t) => Ok(Stage1Node::Test(t.compile(cache)?)), 101 | Any(ref v) => Ok(Stage1Node::Any(Stage1Compile::stage1_compile_slice(v, cache)?)), 102 | NotAny(ref v) => Ok(Stage1Node::NotAny(Stage1Compile::stage1_compile_slice(v, cache)?)), 103 | All(ref v) => Ok(Stage1Node::All(Stage1Compile::stage1_compile_slice(v, cache)?)), 104 | NotAll(ref v) => Ok(Stage1Node::NotAny(Stage1Compile::stage1_compile_slice(v, cache)?)), 105 | } 106 | } 107 | } 108 | 109 | 110 | 111 | 112 | #[cfg(test)] 113 | mod tests { 114 | use shared::fact::{Fact, Getter}; 115 | use super::*; 116 | use shared::nodes::alpha::HashEqField; 117 | 118 | #[derive(Clone, Hash, Eq, PartialEq, Debug)] 119 | struct Dummy { 120 | d: u64 121 | } 122 | 123 | impl Dummy { 124 | fn get_d(&self) -> &u64 { 125 | &self.d 126 | } 127 | } 128 | 129 | impl Fact for Dummy { 130 | type HashEq = (); 131 | 132 | fn getter(field: &str) -> Option> { 133 | match field { 134 | "d" => Some(Getter::U64(Dummy::get_d)), 135 | _ => unimplemented!() 136 | } 137 | } 138 | 139 | fn exhaustive_hash(&self) -> Box::HashEq>> { 140 | unimplemented!() 141 | } 142 | 143 | fn create_hash_eq(conditions: &Vec, cache: &StringCache) -> Self::HashEq { 144 | unimplemented!() 145 | } 146 | } 147 | 148 | #[test] 149 | pub fn as_ref_test() { 150 | let mut cache = StringCache::new(); 151 | let nodes: Stage1Node = all( 152 | &[not(any(&[eq("d", 6u64)])), all(&[le("d", 64u64), le("d", dyn("ab"))])] 153 | ).stage1_compile(&mut cache).unwrap() 154 | .clean(); 155 | println!("{:?}", nodes); 156 | } 157 | } -------------------------------------------------------------------------------- /src/bin/sandbox.rs: -------------------------------------------------------------------------------- 1 | extern crate num; 2 | 3 | #[macro_use] 4 | extern crate itertools; 5 | 6 | extern crate string_interner; 7 | 8 | extern crate ordered_float; 9 | 10 | extern crate expert; 11 | 12 | extern crate parking_lot; 13 | 14 | use parking_lot::Mutex; 15 | 16 | use std::any::TypeId; 17 | use expert::builder::StatementCondition; 18 | use expert::builders::statement::{ValueHolder, StatementValues, StatementConditions}; 19 | use expert::runtime::memory::StringCache; 20 | use expert::iter::OptionIter; 21 | use expert::traits::ReteIntrospection; 22 | use expert::traits::{Introspect, Fact, Getters, FieldValue}; 23 | use expert::shared::nodes::alpha::HashEqField; 24 | use expert::shared::nodes::alpha::AlphaNode; 25 | 26 | #[derive(Debug, Copy, Clone, Eq, Hash, Ord, PartialOrd, PartialEq)] 27 | struct Aspect { 28 | id: u64, 29 | aspect_type: u64, 30 | impact: u64 31 | } 32 | 33 | impl Aspect { 34 | fn get_id(&self) -> &u64 { 35 | &self.id 36 | } 37 | 38 | fn get_aspect_type(&self) -> &u64 { 39 | &self.aspect_type 40 | } 41 | 42 | fn get_impact(&self) -> &u64 { 43 | &self.impact 44 | } 45 | 46 | fn exhaustive_hash<'a>(&'a self) -> impl Iterator, Option, Option)> +'a { 47 | iproduct!(OptionIter::new(Some(self.id)), OptionIter::new(Some(self.aspect_type)), OptionIter::new(Some(self.impact))) 48 | } 49 | } 50 | 51 | impl Introspect for Aspect { 52 | fn static_type_id() -> TypeId { 53 | TypeId::of::() 54 | } 55 | } 56 | 57 | impl Fact for Aspect { 58 | type HashEq = (Option, Option, Option); 59 | 60 | fn getter(field: &str) -> Option> { 61 | use Getters::*; 62 | match field { 63 | "id" => Some(U64(Self::get_id)), 64 | "aspect_type" => Some(U64(Self::get_aspect_type)), 65 | "impact" => Some(U64(Self::get_impact)), 66 | _ => None 67 | } 68 | } 69 | 70 | fn create_hash_eq(conditions: &Vec, cache: &StringCache) -> Self::HashEq { 71 | use StatementConditions::*; 72 | use StatementValues::*; 73 | use ValueHolder::*; 74 | let mut o_id = None; 75 | let mut o_aspect_type = None; 76 | let mut o_impact = None; 77 | 78 | for c in conditions.iter() 79 | .filter(|c| c.is_hash_eq()) { 80 | let field = c.field(); 81 | match (cache.resolve(field), c) { 82 | (Some("id"), &Eq(_, U64(S(to)))) => o_id = Some(to), 83 | (Some("aspect_type"), &Eq(_, U64(S(to)))) => o_aspect_type = Some(to), 84 | (Some("impact"), &Eq(_, U64(S(to)))) => o_impact = Some(to), 85 | _ => continue 86 | } 87 | } 88 | (o_id, o_aspect_type, o_impact) 89 | } 90 | fn exhaustive_hash(&self) -> Box> { 91 | Box::new(iproduct!(OptionIter::some(self.id), OptionIter::some(self.aspect_type), OptionIter::some(self.impact))) 92 | } 93 | fn new_from_fields(fields: &[FieldValue], cache: &StringCache) -> Self { 94 | use self::FieldValue::*; 95 | let mut o_id = None; 96 | let mut o_aspect_type = None; 97 | let mut o_impact = None; 98 | 99 | for f in fields.iter() { 100 | let field = f.field(); 101 | match (cache.resolve(field), f) { 102 | (Some("id"), &U64(_, val)) => o_id = Some(val), 103 | (Some("aspect_type"), &U64(_, val)) => o_aspect_type = Some(val), 104 | (Some("impact"), &U64(_, val)) => o_impact = Some(val), 105 | _ => {} 106 | } 107 | } 108 | 109 | Aspect { 110 | id: o_id.unwrap_or(0), 111 | aspect_type: o_aspect_type.unwrap_or(1), 112 | impact: o_impact.unwrap_or(2) 113 | } 114 | } 115 | } 116 | 117 | impl ReteIntrospection for Aspect { 118 | type HashEq = [Option; 3]; 119 | fn static_type_id() -> TypeId { 120 | TypeId::of::() 121 | } 122 | 123 | fn getter(field: &str) -> Option &u64> { 124 | match field { 125 | "id" => Some(Self::get_id), 126 | "aspect_type" => Some(Self::get_aspect_type), 127 | "impact" => Some(Self::get_impact), 128 | _ => None 129 | } 130 | } 131 | 132 | fn type_id(&self) -> TypeId { 133 | TypeId::of::() 134 | } 135 | 136 | 137 | fn create_hash_eq(conditions: &Vec, string_interner: &StringCache) -> [Option; 3] { 138 | let mut o_id = None; 139 | let mut o_aspect_type = None; 140 | let mut o_impact = None; 141 | 142 | for c in conditions { 143 | match c { 144 | &StatementCondition::Exists => return [None, None, None], 145 | &StatementCondition::Eq{field_sym, to} => { 146 | match string_interner.resolve(field_sym) { 147 | Some("id") => o_id = Some(to), 148 | Some("aspect_type") => o_aspect_type = Some(to), 149 | Some("impact") => o_impact = Some(to), 150 | _ => {} 151 | } 152 | }, 153 | _ => continue 154 | } 155 | } 156 | [o_id, o_aspect_type, o_impact] 157 | } 158 | } 159 | 160 | 161 | #[derive(Debug, Clone, Eq, Hash, Ord, PartialOrd, PartialEq)] 162 | pub struct ATestAspect { 163 | test: String 164 | } 165 | 166 | impl expert::shared::fact::Fact for ATestAspect { 167 | type HashEq = (); 168 | 169 | fn getter(_field: &str) -> Option> { 170 | unimplemented!() 171 | } 172 | 173 | fn exhaustive_hash(&self) -> Box> { 174 | unimplemented!() 175 | } 176 | 177 | fn create_hash_eq(conditions: &[AlphaNode]) -> Self::HashEq { 178 | unimplemented!() 179 | } 180 | } 181 | 182 | fn main() { 183 | use expert::builder::KnowledgeBuilder; 184 | 185 | println!("New AlphaNode: {:?}", std::mem::size_of::>()); 186 | println!("New AlphaGroup: {:?}", std::mem::size_of::()); 187 | 188 | println!("New BetaNode: {:?}", std::mem::size_of::>()); 189 | println!("New BetaGroup: {:?}", std::mem::size_of::()); 190 | 191 | 192 | 193 | let default = vec![false, true, false]; 194 | let mut test_into = Vec::with_capacity(default.len()); 195 | for c in default.iter().cloned().map(|c| Mutex::new(c)) { 196 | test_into.push(c); 197 | } 198 | 199 | let builder: KnowledgeBuilder = KnowledgeBuilder::new() 200 | .rule("test1") 201 | .when() 202 | .eq("id", 6).eq("aspect_type", 12).btwn("impact", 8, false, 400, true) 203 | .then() 204 | .when() 205 | .eq("id", 6).eq("aspect_type", 12).btwn("impact", 8, false, 400, true) 206 | .then() 207 | .end() 208 | .rule("test2") 209 | .when() 210 | .eq("id", 9).eq("aspect_type", 1).btwn("impact", 8, false, 400, true) 211 | .then() 212 | .when() 213 | .eq("id", 9).eq("aspect_type", 1)//.btwn("impact", 8, false, 400, true).btwn("impact", 9, false, 400, true) 214 | .then() 215 | .when() 216 | .gt("impact", 5, true) 217 | .then() 218 | .end(); 219 | 220 | println!("builder: {:?}", &builder); 221 | 222 | // let base = builder.compile(); 223 | 224 | let a = Aspect{id: 485, aspect_type: 3840, impact: 9}; 225 | for i in a.exhaustive_hash() { 226 | println!("{:?}", i); 227 | } 228 | 229 | } 230 | -------------------------------------------------------------------------------- /src/shared/nodes/tests.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | Base test implementations and traits 3 | */ 4 | 5 | use ordered_float::NotNaN; 6 | use float_cmp::ApproxEqUlps; 7 | 8 | /// Updates a test's configuration to apply a not 9 | pub trait ApplyNot { 10 | fn apply_not(&mut self); 11 | } 12 | 13 | // Don't try to make this Truth again. This ends up making the Repl -> Node function massive 14 | #[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Debug)] 15 | pub enum Truth { 16 | Not, 17 | Is 18 | } 19 | 20 | impl Truth { 21 | pub fn is_not(self) -> bool { 22 | use self::Truth::*; 23 | match self { 24 | Not => true, 25 | Is => false 26 | } 27 | } 28 | } 29 | 30 | impl ApplyNot for Truth { 31 | fn apply_not(&mut self) { 32 | use self::Truth::*; 33 | *self = match *self { 34 | Not => Is, 35 | Is => Not 36 | }; 37 | } 38 | } 39 | 40 | /// Compare a value against a single parameter 41 | pub trait STest{ 42 | fn test(&self, val: &T, to: &T) -> bool; 43 | } 44 | 45 | impl<'a, F, T: ? Sized> STest for (Truth, &'a F) 46 | where F: STest { 47 | fn test(&self, val: &T, to: &T) -> bool { 48 | self.0.is_not() ^ self.1.test(val, to) 49 | } 50 | } 51 | 52 | /// Compare a value against two parameters 53 | pub trait DTest{ 54 | fn test(&self, val: &T, from: &T, to: &T) -> bool; 55 | } 56 | 57 | impl<'a, F, T: ? Sized> DTest for (Truth, &'a F) 58 | where F: DTest { 59 | fn test(&self, val: &T, from: &T, to: &T) -> bool { 60 | self.0.is_not() ^ self.1.test(val, from, to) 61 | } 62 | } 63 | 64 | 65 | /// Single value ordinal test 66 | #[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Debug)] 67 | pub enum OrdTest { 68 | /// val < to 69 | Lt, 70 | /// val <= to 71 | Le, 72 | /// val > to 73 | Gt, 74 | /// 75 | /// val >= to 76 | Ge, 77 | } 78 | 79 | impl STest for OrdTest 80 | where T: Ord + ?Sized { 81 | fn test(&self, val: &T, to: &T) -> bool { 82 | use self::OrdTest::*; 83 | match *self { 84 | Lt => val < to, 85 | Le => val <= to, 86 | Gt => val > to, 87 | Ge => val >= to, 88 | } 89 | } 90 | } 91 | 92 | /// Multiple value ordinal test 93 | #[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Debug)] 94 | pub enum BetweenTest { 95 | /// val > from && val < to 96 | GtLt, 97 | /// val >= from && val < to 98 | GeLt, 99 | /// val > from && val <= to 100 | GtLe, 101 | /// val >= from && val <= to 102 | GeLe, 103 | } 104 | 105 | impl DTest for BetweenTest 106 | where T: Ord + ?Sized{ 107 | fn test(&self, val: &T, from: &T, to: &T) -> bool { 108 | use self::BetweenTest::*; 109 | match *self { 110 | GtLt => val > from && val < to, 111 | GeLt => val >= from && val < to, 112 | GtLe => val > from && val <= to, 113 | GeLe => val >= from && val <= to, 114 | } 115 | } 116 | } 117 | 118 | /// Single value equivalence test 119 | #[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Debug)] 120 | pub enum EqTest { 121 | /// val == to 122 | Eq, 123 | /// val != to 124 | Ne 125 | } 126 | 127 | impl STest for EqTest 128 | where T: Eq + ?Sized { 129 | fn test(&self, val: &T, to: &T) -> bool { 130 | use self::EqTest::*; 131 | match *self { 132 | Eq => val == to, 133 | Ne => val != to, 134 | } 135 | } 136 | } 137 | 138 | impl From for ApproxEqTest { 139 | fn from(eq: EqTest) -> ApproxEqTest { 140 | match eq { 141 | EqTest::Eq => ApproxEqTest::Eq, 142 | EqTest::Ne => ApproxEqTest::Ne 143 | } 144 | } 145 | } 146 | 147 | /// Single value approximate equivalence test for floats (default to 2 ULPs) 148 | #[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Debug)] 149 | pub enum ApproxEqTest { 150 | /// val ~= to 151 | Eq, 152 | /// val !~= to 153 | Ne 154 | } 155 | 156 | // TODO: I wish I could make this more generic. Revisit once impl specialization lands? 157 | impl STest> for ApproxEqTest { 158 | fn test(&self, val: &NotNaN, to: &NotNaN) -> bool { 159 | use self::ApproxEqTest::*; 160 | match *self { 161 | Eq => val.as_ref().approx_eq_ulps(to.as_ref(), 2), 162 | Ne => val.as_ref().approx_ne_ulps(to.as_ref(), 2), 163 | } 164 | } 165 | } 166 | 167 | impl STest> for ApproxEqTest { 168 | fn test(&self, val: &NotNaN, to: &NotNaN) -> bool { 169 | use self::ApproxEqTest::*; 170 | match *self { 171 | Eq => val.as_ref().approx_eq_ulps(to.as_ref(), 2), 172 | Ne => val.as_ref().approx_ne_ulps(to.as_ref(), 2), 173 | } 174 | } 175 | } 176 | 177 | /// &str tests 178 | #[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Debug)] 179 | pub enum StrArrayTest { 180 | /// val.contains(to) 181 | Contains, 182 | /// val.starts_with(to) 183 | StartsWith, 184 | /// val.ends_with(to) 185 | EndsWith, 186 | /// to.contains(val) 187 | ContainedBy, 188 | /// to.starts_with(val) 189 | StartedBy, 190 | /// to.ends_with(val) 191 | EndedBy, 192 | } 193 | 194 | impl STest for StrArrayTest 195 | where T: AsRef + ?Sized { 196 | fn test(&self, val: &T, to: &T) -> bool { 197 | use self::StrArrayTest::*; 198 | match *self { 199 | Contains => val.as_ref().contains(to.as_ref()), 200 | StartsWith => val.as_ref().starts_with(to.as_ref()), 201 | EndsWith => val.as_ref().ends_with(to.as_ref()), 202 | ContainedBy => to.as_ref().contains(val.as_ref()), 203 | StartedBy => to.as_ref().starts_with(val.as_ref()), 204 | EndedBy => to.as_ref().ends_with(val.as_ref()), 205 | } 206 | } 207 | } 208 | 209 | 210 | 211 | #[cfg(test)] 212 | mod tests { 213 | 214 | use ordered_float::NotNaN; 215 | use shared::nodes::tests::*; 216 | 217 | #[test] 218 | fn eq_tests() { 219 | assert_eq!(true, EqTest::Eq.test(&5, &5)); 220 | assert_eq!(false, EqTest::Eq.test(&4, &5)); 221 | 222 | assert_eq!(false, EqTest::Ne.test(&5, &5)); 223 | assert_eq!(true, EqTest::Ne.test(&4, &5)); 224 | } 225 | 226 | #[test] 227 | fn truth_tests() { 228 | assert_eq!(true, (Truth::Is, &EqTest::Eq).test(&5, &5)); 229 | assert_eq!(false, (Truth::Not, &EqTest::Eq).test(&5, &5)); 230 | assert_eq!(true, (Truth::Not, &EqTest::Eq).test(&4, &5)); 231 | assert_eq!(false, (Truth::Is, &EqTest::Eq).test(&4, &5)); 232 | } 233 | 234 | #[test] 235 | fn ord_tests() { 236 | assert_eq!(false, OrdTest::Lt.test(&5, &5)); 237 | assert_eq!(true, OrdTest::Lt.test(&4, &5)); 238 | 239 | assert_eq!(false, OrdTest::Le.test(&6, &5)); 240 | assert_eq!(true, OrdTest::Le.test(&5, &5)); 241 | assert_eq!(true, OrdTest::Le.test(&4, &5)); 242 | 243 | assert_eq!(false, OrdTest::Gt.test(&5, &5)); 244 | assert_eq!(true, OrdTest::Gt.test(&5, &4)); 245 | 246 | assert_eq!(false, OrdTest::Ge.test(&5, &6)); 247 | assert_eq!(true, OrdTest::Ge.test(&5, &5)); 248 | assert_eq!(true, OrdTest::Ge.test(&5, &4)); 249 | 250 | } 251 | 252 | #[test] 253 | fn approx_eq_tests() { 254 | let five = NotNaN::new(5.0f32).unwrap(); 255 | let four = NotNaN::new(4.0f32).unwrap(); 256 | 257 | assert_eq!(true, ApproxEqTest::Eq.test(&five, &five)); 258 | assert_eq!(false, ApproxEqTest::Eq.test(&four, &five)); 259 | 260 | assert_eq!(false, ApproxEqTest::Ne.test(&five, &five)); 261 | assert_eq!(true, ApproxEqTest::Ne.test(&four, &five)); 262 | } 263 | 264 | #[test] 265 | fn str_array_tests() { 266 | assert_eq!(true, StrArrayTest::Contains.test("abcd", "bc")); 267 | assert_eq!(true, StrArrayTest::StartsWith.test("abcd", "ab")); 268 | assert_eq!(true, StrArrayTest::EndsWith.test("abcd", "cd")); 269 | 270 | assert_eq!(true, StrArrayTest::ContainedBy.test("bc" , "abcd")); 271 | assert_eq!(true, StrArrayTest::StartedBy.test( "ab", "abcd")); 272 | assert_eq!(true, StrArrayTest::EndedBy.test("cd", "abcd")); 273 | } 274 | 275 | } -------------------------------------------------------------------------------- /src/shared/fact.rs: -------------------------------------------------------------------------------- 1 | use std::hash::Hash; 2 | use std; 3 | use std::fmt; 4 | use std::fmt::Debug; 5 | use ord_subset::OrdVar; 6 | use decimal::d128; 7 | use chrono::{NaiveTime, Date, DateTime, Duration, Utc}; 8 | use ::runtime::memory::SymbolId; 9 | use ordered_float::NotNaN; 10 | use super::context::BetaContext; 11 | use runtime::memory::StringCache; 12 | use shared::nodes::alpha::HashEqField; 13 | use shared::nodes::alpha::AlphaNode; 14 | use std::hash::Hasher; 15 | use std::cmp::Ordering; 16 | use enum_index; 17 | use enum_index::EnumIndex; 18 | use std::any::Any; 19 | 20 | #[derive(Copy, EnumIndex)] 21 | pub enum Getter { 22 | BOOL(fn(&T) -> &bool), 23 | I8(fn(&T) -> &i8), 24 | I16(fn(&T) -> &i16), 25 | I32(fn(&T) -> &i32), 26 | I64(fn(&T) -> &i64), 27 | I128(fn(&T) -> &i128), 28 | U8(fn(&T) -> &u8), 29 | U16(fn(&T) -> &u16), 30 | U32(fn(&T) -> &u32), 31 | U64(fn(&T) -> &u64), 32 | U128(fn(&T) -> &u128), 33 | F32(fn(&T) -> &NotNaN), 34 | F64(fn(&T) -> &NotNaN), 35 | D128(fn(&T) -> &OrdVar), 36 | STR(fn(&T) -> &str), 37 | TIME(fn(&T) -> &NaiveTime), 38 | DATE(fn(&T) -> &Date), 39 | DATETIME(fn(&T) -> &DateTime), 40 | } 41 | 42 | impl Getter { 43 | fn hash_self(ord: usize, getter: usize, state: &mut H) { 44 | ord.hash(state); 45 | getter.hash(state); 46 | } 47 | } 48 | 49 | impl Debug for Getter { 50 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 51 | use self::Getter::*; 52 | write!(f, "Getter(")?; 53 | match *self { 54 | BOOL(accessor) => write!(f, "BOOL({:#x})", accessor as usize)?, 55 | I8(accessor) => write!(f, "I8({:#x})", accessor as usize)?, 56 | I16(accessor) => write!(f, "I16({:#x})", accessor as usize)?, 57 | I32(accessor) => write!(f, "I32({:#x})", accessor as usize)?, 58 | I64(accessor) => write!(f, "I64({:#x})", accessor as usize)?, 59 | I128(accessor) => write!(f, "I128({:#x})", accessor as usize)?, 60 | U8(accessor) => write!(f, "U8({:#x})", accessor as usize)?, 61 | U16(accessor) => write!(f, "U16({:#x})", accessor as usize)?, 62 | U32(accessor) => write!(f, "U32({:#x})", accessor as usize)?, 63 | U64(accessor) => write!(f, "U64({:#x})", accessor as usize)?, 64 | U128(accessor) => write!(f, "U128({:#x})", accessor as usize)?, 65 | F32(accessor) => write!(f, "F32({:#x})", accessor as usize)?, 66 | F64(accessor) => write!(f, "F64({:#x})", accessor as usize)?, 67 | D128(accessor) => write!(f, "D128({:#x})", accessor as usize)?, 68 | STR(accessor) => write!(f, "STR({:#x})", accessor as usize)?, 69 | TIME(accessor) => write!(f, "TIME({:#x})", accessor as usize)?, 70 | DATE(accessor) => write!(f, "DATE({:#x})", accessor as usize)?, 71 | DATETIME(accessor) => write!(f, "DATETIME({:#x})", accessor as usize)?, 72 | } 73 | write!(f, ")") 74 | } 75 | } 76 | 77 | macro_rules! getter_derive { 78 | ($($t:ident),+ ) => { 79 | 80 | impl Clone for Getter { 81 | fn clone(&self) -> Self { 82 | use self::Getter::*; 83 | match *self { 84 | $( 85 | $t(getter) => $t(getter), 86 | )* 87 | } 88 | } 89 | } 90 | 91 | impl Hash for Getter { 92 | fn hash < H: Hasher > ( & self, state: & mut H) { 93 | use self::Getter::*; 94 | match *self { 95 | $ ( $ t(getter) => Self::hash_self(self.enum_index(), getter as usize, state), 96 | )* 97 | } 98 | } 99 | } 100 | 101 | impl PartialEq for Getter { 102 | fn eq(&self, other: &Self) -> bool { 103 | use self::Getter::*; 104 | match (self, other) { 105 | $( (&$t(getter1), &$t(getter2)) => { 106 | (getter1 as usize) == (getter2 as usize) 107 | },)* 108 | _ => false 109 | } 110 | } 111 | } 112 | 113 | impl Eq for Getter {} 114 | 115 | impl Ord for Getter { 116 | fn cmp(&self, other: &Self) -> Ordering { 117 | use self::Getter::*; 118 | match(self, other) { 119 | $( (&$t(getter1), &$t(getter2)) => { 120 | (getter1 as usize).cmp(&(getter2 as usize)) 121 | },)* 122 | _ => self.enum_index().cmp(&other.enum_index()) 123 | } 124 | } 125 | } 126 | 127 | impl PartialOrd for Getter { 128 | fn partial_cmp(&self, other: &Self) -> Option { 129 | Some(self.cmp(other)) 130 | } 131 | } 132 | 133 | #[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Debug)] 134 | pub enum FactFieldType { 135 | $( 136 | $t, 137 | )* 138 | } 139 | 140 | pub trait GetFieldType { 141 | fn get_field_type(&self) -> FactFieldType; 142 | } 143 | 144 | impl GetFieldType for Getter { 145 | fn get_field_type(&self) -> FactFieldType { 146 | use self::Getter::*; 147 | match self { 148 | $( 149 | $t(_) => FactFieldType::$t, 150 | )* 151 | } 152 | } 153 | } 154 | }; 155 | } 156 | 157 | getter_derive!( 158 | BOOL, 159 | I8, I16, I32, I64, I128, 160 | U8, U16, U32, U64, U128, 161 | F32, F64, D128, 162 | STR , 163 | TIME, DATE, DATETIME 164 | ); 165 | 166 | 167 | impl FactFieldType { 168 | 169 | pub fn is_compatible(self, other: Self) -> bool { 170 | self.is_number_compatible(other) 171 | } 172 | 173 | pub fn is_number_compatible(self, other: Self) -> bool { 174 | match (self.is_number(), other.is_number()) { 175 | (true, true) => true, 176 | _ => false 177 | } 178 | } 179 | 180 | pub fn is_number(self) -> bool { 181 | use self::FactFieldType::*; 182 | match self { 183 | I8 | I16 | I32 | I64 | I128 184 | | U8 | U16 | U32 | U64 | U128 185 | | F32 | F64 | D128 => true, 186 | _ => false 187 | } 188 | } 189 | } 190 | 191 | pub trait Fact: 'static + Eq + Hash + Any 192 | where Self: std::marker::Sized { 193 | 194 | type HashEq: Hash + Eq + Clone + Debug; 195 | 196 | fn getter(field: &str) -> Option>; 197 | fn exhaustive_hash(&self) -> Box>; 198 | fn create_hash_eq(conditions: &[AlphaNode]) -> Self::HashEq; 199 | } 200 | 201 | pub trait FactField { 202 | fn get_field_type() -> FactFieldType; 203 | } 204 | 205 | pub trait RefField : FactField { 206 | fn resolve(context: &C, sym: SymbolId) -> &Self; 207 | } 208 | 209 | pub trait CastField : FactField { 210 | fn resolve(context: &C, sym: SymbolId) -> Self; 211 | } 212 | 213 | macro_rules! impl_ref_field { 214 | ($($id:ty => $getter:ident => $field_type:ident),+) => { 215 | $( 216 | impl FactField for $id { 217 | fn get_field_type() -> FactFieldType { 218 | FactFieldType::$field_type 219 | } 220 | } 221 | 222 | impl RefField for $id { 223 | #[inline] 224 | fn resolve(context: &C, sym: SymbolId) -> &Self { 225 | context.$getter(sym) 226 | } 227 | } 228 | )* 229 | }; 230 | } 231 | 232 | macro_rules! impl_cast_field { 233 | ($($id:ty => $getter:ident => $field_type:ident),+) => { 234 | $( 235 | impl FactField for $id { 236 | fn get_field_type() -> FactFieldType { 237 | FactFieldType::$field_type 238 | } 239 | } 240 | 241 | impl CastField for $id { 242 | #[inline] 243 | fn resolve(context: &C, sym: SymbolId) -> Self { 244 | context.$getter(sym) 245 | } 246 | } 247 | )* 248 | }; 249 | } 250 | 251 | impl_ref_field!( 252 | bool => get_bool => BOOL, 253 | str => get_str => STR, 254 | NaiveTime => get_time => TIME, 255 | Date => get_date => DATE, 256 | DateTime => get_datetime => DATETIME 257 | ); 258 | 259 | impl_cast_field!( 260 | i8 => get_i8 => I8, 261 | i16 => get_i16 => I16, 262 | i32 => get_i32 => I32, 263 | i64 => get_i64 => I64, 264 | i128 => get_i128 => I128, 265 | u8 => get_u8 => U8, 266 | u16 => get_u16 => U16, 267 | u32 => get_u32 => U32, 268 | u64 => get_u64 => U64, 269 | u128 => get_u128 => U128, 270 | NotNaN => get_f32 => F32, 271 | NotNaN => get_f64 => F64, 272 | OrdVar => get_d128 => D128 273 | ); 274 | 275 | impl FactField for SymbolId { 276 | fn get_field_type() -> FactFieldType { 277 | FactFieldType::STR 278 | } 279 | } -------------------------------------------------------------------------------- /src/network/tests.rs: -------------------------------------------------------------------------------- 1 | use std::hash::{Hash, Hasher}; 2 | use std::fmt; 3 | use std::fmt::Debug; 4 | use traits::Fact; 5 | use ordered_float::NotNaN; 6 | use runtime::memory::SymbolId; 7 | use num::Float; 8 | 9 | #[derive(Clone, Hash, Eq, PartialEq)] 10 | pub enum CLimits { 11 | S(T), 12 | D(T, T) 13 | } 14 | 15 | #[derive(Clone)] 16 | pub enum OrdData{ 17 | I8(fn(&T) -> &i8, CLimits), 18 | I16(fn(&T) -> &i16, CLimits), 19 | I32(fn(&T) -> &i32, CLimits), 20 | I64(fn(&T) -> &i64, CLimits), 21 | U8(fn(&T) -> &u8, CLimits), 22 | U16(fn(&T) -> &u16, CLimits), 23 | U32(fn(&T) -> &u32, CLimits), 24 | U64(fn(&T) -> &u64, CLimits), 25 | ISIZE(fn(&T) -> &isize, CLimits), 26 | USIZE(fn(&T) -> &usize, CLimits), 27 | } 28 | 29 | impl OrdData { 30 | fn hash_self(ord: usize, accessor: usize, limits: &L, state: &mut H) { 31 | ord.hash(state); 32 | accessor.hash(state); 33 | limits.hash(state); 34 | } 35 | } 36 | 37 | impl Hash for OrdData { 38 | fn hash(&self, state: &mut H) { 39 | use self::OrdData::*; 40 | match self { 41 | &I8(accessor, ref limits) => { 42 | Self::hash_self(0, accessor as usize, limits, state); 43 | }, 44 | &I16(accessor, ref limits) => { 45 | Self::hash_self(1, accessor as usize, limits, state); 46 | }, 47 | &I32(accessor, ref limits) => { 48 | Self::hash_self(2, accessor as usize, limits, state); 49 | }, 50 | &I64(accessor, ref limits) => { 51 | Self::hash_self(3, accessor as usize, limits, state); 52 | }, 53 | &U8(accessor, ref limits) => { 54 | Self::hash_self(4, accessor as usize, limits, state); 55 | }, 56 | &U16(accessor, ref limits) => { 57 | Self::hash_self(5, accessor as usize, limits, state); 58 | }, 59 | &U32(accessor, ref limits) => { 60 | Self::hash_self(6, accessor as usize, limits, state); 61 | }, 62 | &U64(accessor, ref limits) => { 63 | Self::hash_self(7, accessor as usize, limits, state); 64 | }, 65 | &ISIZE(accessor, ref limits) => { 66 | Self::hash_self(8, accessor as usize, limits, state); 67 | }, 68 | &USIZE(accessor, ref limits) => { 69 | Self::hash_self(9, accessor as usize, limits, state); 70 | } 71 | } 72 | } 73 | } 74 | 75 | impl PartialEq for OrdData { 76 | fn eq(&self, other: &Self) -> bool { 77 | use self::OrdData::*; 78 | match (self, other) { 79 | (&I8(accessor1, ref limits1), &I8(accessor2, ref limits2)) => { 80 | (accessor1 as usize) == (accessor2 as usize) && limits1 == limits2 81 | }, 82 | (&I16(accessor1, ref limits1), &I16(accessor2, ref limits2)) => { 83 | (accessor1 as usize) == (accessor2 as usize) && limits1 == limits2 84 | }, 85 | (&I32(accessor1, ref limits1), &I32(accessor2, ref limits2)) => { 86 | (accessor1 as usize) == (accessor2 as usize) && limits1 == limits2 87 | }, 88 | (&I64(accessor1, ref limits1), &I64(accessor2, ref limits2)) => { 89 | (accessor1 as usize) == (accessor2 as usize) && limits1 == limits2 90 | }, 91 | (&U8(accessor1, ref limits1), &U8(accessor2, ref limits2)) => { 92 | (accessor1 as usize) == (accessor2 as usize) && limits1 == limits2 93 | }, 94 | (&U16(accessor1, ref limits1), &U16(accessor2, ref limits2)) => { 95 | (accessor1 as usize) == (accessor2 as usize) && limits1 == limits2 96 | }, 97 | (&U32(accessor1, ref limits1), &U32(accessor2, ref limits2)) => { 98 | (accessor1 as usize) == (accessor2 as usize) && limits1 == limits2 99 | }, 100 | (&U64(accessor1, ref limits1), &U64(accessor2, ref limits2)) => { 101 | (accessor1 as usize) == (accessor2 as usize) && limits1 == limits2 102 | }, 103 | (&ISIZE(accessor1, ref limits1), &ISIZE(accessor2, ref limits2)) => { 104 | (accessor1 as usize) == (accessor2 as usize) && limits1 == limits2 105 | }, 106 | (&USIZE(accessor1, ref limits1), &USIZE(accessor2, ref limits2)) => { 107 | (accessor1 as usize) == (accessor2 as usize) && limits1 == limits2 108 | }, 109 | _ => false 110 | } 111 | } 112 | } 113 | 114 | impl Eq for OrdData {} 115 | 116 | #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)] 117 | pub enum OrdTest { 118 | Ne, 119 | Lt, 120 | Le, 121 | Gt, 122 | Ge, 123 | GtLt, 124 | GeLt, 125 | GtLe, 126 | GeLe 127 | } 128 | 129 | #[derive(Copy, Clone, Eq, PartialEq)] 130 | pub enum FLimits { 131 | S(NotNaN), 132 | D(NotNaN, NotNaN) 133 | } 134 | 135 | // TODO: Why do we need to do this? Seems silly as NotNaN already provides hash 136 | impl Hash for FLimits { 137 | fn hash(&self, state: &mut H) { 138 | use self::FLimits::*; 139 | match self { 140 | &S(ref to) => to.hash(state), 141 | &D(ref from, ref to) => { 142 | from.hash(state); 143 | to.hash(state); 144 | }, 145 | 146 | } 147 | } 148 | } 149 | 150 | #[derive(Clone)] 151 | pub enum FlData{ 152 | F32(fn(&T) -> &f32, FLimits), 153 | F64(fn(&T) -> &f64, FLimits), 154 | } 155 | 156 | impl FlData { 157 | fn hash_self(ord: usize, accessor: usize, limits: &L, state: &mut H) { 158 | ord.hash(state); 159 | accessor.hash(state); 160 | limits.hash(state); 161 | } 162 | } 163 | 164 | impl Hash for FlData { 165 | fn hash(&self, state: &mut H) { 166 | use self::FlData::*; 167 | match self { 168 | &F32(accessor, ref limits) => { 169 | Self::hash_self(0, accessor as usize, limits, state); 170 | }, 171 | &F64(accessor, ref limits) => { 172 | Self::hash_self(1, accessor as usize, limits, state); 173 | }, 174 | } 175 | } 176 | } 177 | 178 | impl PartialEq for FlData { 179 | fn eq(&self, other: &Self) -> bool { 180 | use self::FlData::*; 181 | match (self, other) { 182 | (&F32(accessor1, ref limits1), &F32(accessor2, ref limits2)) => { 183 | (accessor1 as usize) == (accessor2 as usize) && limits1 == limits2 184 | }, 185 | (&F64(accessor1, ref limits1), &F64(accessor2, ref limits2)) => { 186 | (accessor1 as usize) == (accessor2 as usize) && limits1 == limits2 187 | }, 188 | _ => false 189 | 190 | } 191 | } 192 | } 193 | 194 | impl Eq for FlData {} 195 | 196 | #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)] 197 | pub enum FlTest { 198 | ApproxEq, 199 | ApproxNe, 200 | Lt, 201 | Le, 202 | Gt, 203 | Ge, 204 | GtLt, 205 | GeLt, 206 | GtLe, 207 | GeLe 208 | } 209 | 210 | #[derive(Clone)] 211 | pub enum StrData { 212 | REF(fn(&T) -> &str, CLimits), 213 | } 214 | 215 | impl StrData { 216 | fn hash_self(ord: usize, accessor: usize, limits: &L, state: &mut H) { 217 | ord.hash(state); 218 | accessor.hash(state); 219 | limits.hash(state); 220 | } 221 | } 222 | 223 | impl Hash for StrData { 224 | fn hash(&self, state: &mut H) { 225 | use self::StrData::*; 226 | match self { 227 | &REF(accessor, ref limits) => { 228 | Self::hash_self(0, accessor as usize, limits, state); 229 | }, 230 | } 231 | } 232 | } 233 | 234 | impl PartialEq for StrData { 235 | fn eq(&self, other: &Self) -> bool { 236 | use self::StrData::*; 237 | match (self, other) { 238 | (&REF(accessor1, ref limits1), &REF(accessor2, ref limits2)) => { 239 | (accessor1 as usize) == (accessor2 as usize) && limits1 == limits2 240 | }, 241 | _ => false 242 | } 243 | } 244 | } 245 | 246 | 247 | impl Eq for StrData {} 248 | 249 | #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)] 250 | pub enum StrTest { 251 | Ne, 252 | Lt, 253 | Le, 254 | Gt, 255 | Ge, 256 | GtLt, 257 | GeLt, 258 | GtLe, 259 | GeLe, 260 | Contains, 261 | StartsWith, 262 | EndsWith 263 | } 264 | 265 | #[derive(Hash, Eq, PartialEq)] 266 | pub enum AlphaTest { 267 | HashEq, 268 | Ord(OrdData, OrdTest), 269 | Fl(FlData, FlTest), 270 | Str(StrData, StrTest), 271 | } 272 | 273 | impl AlphaTest { 274 | pub fn is_hash_eq(&self) -> bool { 275 | use self::AlphaTest::*; 276 | match self { 277 | &HashEq => true, 278 | _ => false 279 | } 280 | } 281 | } 282 | 283 | impl Debug for AlphaTest { 284 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 285 | use self::AlphaTest::*; 286 | write!(f, "Test{{")?; 287 | match self { 288 | &HashEq => { 289 | write!(f, "HashEq")? 290 | }, 291 | &Ord(ref data, ref test) => { 292 | write!(f, "Ord")? 293 | }, 294 | &Fl(ref data, ref test) => { 295 | write!(f, "Fl")? 296 | }, 297 | &Str(ref data, ref test) => { 298 | write!(f, "Str")? 299 | } 300 | } 301 | write!(f, "}}") 302 | } 303 | } 304 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /src/base.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::rc::Rc; 3 | use itertools::Itertools; 4 | use std::collections::{HashMap, HashSet}; 5 | use ::serial::SerialGen; 6 | use ::traits::ReteIntrospection; 7 | use ::builder::{AlphaTest, ConditionInfo, KnowledgeBuilder}; 8 | use ::network::ids::*; 9 | use ::builders::ids::{StatementId, RuleId}; 10 | use runtime::memory::{AlphaMemoryId, MemoryId}; 11 | 12 | 13 | pub struct LayoutIdGenerator { 14 | hash_eq_ids: HashEqIdGen, 15 | alpha_ids: AlphaIdGen, 16 | beta_ids: BetaIdGen 17 | } 18 | 19 | impl LayoutIdGenerator { 20 | pub fn new() -> LayoutIdGenerator { 21 | LayoutIdGenerator{ 22 | hash_eq_ids: Default::default(), 23 | alpha_ids: Default::default(), 24 | beta_ids: Default::default() 25 | } 26 | } 27 | 28 | pub fn next_hash_eq_id(&mut self) -> HashEqId { 29 | self.hash_eq_ids.next() 30 | } 31 | 32 | pub fn next_alpha_id(&mut self) -> AlphaId { 33 | self.alpha_ids.next() 34 | } 35 | 36 | pub fn next_beta_id(&mut self) -> BetaId { 37 | self.beta_ids.next() 38 | } 39 | } 40 | 41 | impl Default for LayoutIdGenerator { 42 | fn default() -> Self { 43 | LayoutIdGenerator::new() 44 | } 45 | } 46 | 47 | pub struct KnowledgeBase { 48 | t: PhantomData 49 | } 50 | 51 | impl KnowledgeBase { 52 | 53 | pub fn compile(builder: KnowledgeBuilder) -> KnowledgeBase { 54 | let (string_repo, rules, condition_map) = builder.explode(); 55 | 56 | 57 | let (hash_eq_nodes, alpha_network, statement_memories) = Self::compile_alpha_network(condition_map); 58 | 59 | let mut statement_rule_map = HashMap::new(); 60 | for (rule_id, rule) in rules { 61 | for statement_id in &rule.statement_ids { 62 | statement_rule_map.insert(*statement_id, rule_id); 63 | } 64 | } 65 | 66 | KnowledgeBase{t: PhantomData} 67 | } 68 | 69 | fn compile_alpha_network(condition_map: HashMap, ConditionInfo>>) 70 | -> (HashMap, Vec>, HashMap) { 71 | let mut conditions: Vec<_> = condition_map.into_iter().collect(); 72 | // Order conditions ascending by dependent statement count, then test count. 73 | conditions.sort_by(|&(_, ref tests1), &(_, ref tests2)| { 74 | if let (Some(ref hash1), Some(ref hash2)) = (tests1.get(&AlphaTest::HashEq), tests2.get(&AlphaTest::HashEq)) { 75 | hash1.dependents.len().cmp(&hash2.dependents.len()).then(tests1.len().cmp(&tests2.len())) 76 | } else { 77 | unreachable!("Unexpected comparison. HashEq must be set"); 78 | } 79 | }); 80 | 81 | let mut node_id_gen = LayoutIdGenerator::new(); 82 | 83 | let mut hash_eq_nodes = HashMap::new(); 84 | 85 | let mut statement_memories: HashMap = HashMap::new(); 86 | 87 | let mut alpha_network = Vec::new(); 88 | 89 | // Pop off the most shared & complex tests first and lay them out at the front of the network. 90 | // That way they're more likely to be right next to each other 91 | while let Some((hash_val, mut test_map)) = conditions.pop() { 92 | 93 | let mut layout_map = HashMap::new(); 94 | 95 | // Take the HashEq node (our entry point) and exhaustively assign destination nodes until no more statements are shared. 96 | let mut hash_eq_info = test_map.remove(&AlphaTest::HashEq).unwrap(); 97 | let hash_eq_id = node_id_gen.next_hash_eq_id(); 98 | let mut hash_eq_destinations: Vec = Vec::new(); 99 | 100 | // Lay down the node for the most shared nodes before the others 101 | while let Some((max_info, max_intersection)) = test_map.iter() 102 | .map(|(_, info)| info) 103 | .map(|info| (info, &hash_eq_info.dependents & &info.dependents)) 104 | .filter(|&(_, ref intersection)| !intersection.is_empty()) 105 | .max_by_key(|&(_, ref intersection)| intersection.len()) { 106 | 107 | let destination_id = layout_map.entry(max_info.id) 108 | .or_insert_with(|| NodeLayout{node_id: node_id_gen.next_alpha_id(), destinations: Default::default()}) 109 | .node_id; 110 | 111 | hash_eq_info.dependents.retain(|x| !max_intersection.contains(&x)); 112 | hash_eq_destinations.push(destination_id.into()); 113 | } 114 | 115 | // Add the HashEq node to the map && store any remaining statements for the beta network 116 | hash_eq_nodes.insert(hash_eq_id, (hash_val, HashEqNode{id: hash_eq_id, store: !hash_eq_info.dependents.is_empty(), destinations: hash_eq_destinations})); 117 | 118 | for statment_id in hash_eq_info.dependents { 119 | statement_memories.insert(statment_id, hash_eq_id.into()); 120 | } 121 | 122 | let mut tests: Vec<_> = test_map.into_iter().collect(); 123 | 124 | loop { 125 | // Sort the remaining tests by layed-out vs not. 126 | // TODO: sort by dependents.size, too. put that at the front 127 | tests.sort_by_key(|&(_, ref info)| !layout_map.contains_key(&info.id)); 128 | println!("Layout: {:?}", layout_map); 129 | println!("Sorted: {:?}", tests); 130 | 131 | // Again, in order of most shared to least, lay down nodes 132 | // TODO: when closure is cloneable, fix this to use cartisian product 133 | let output = tests.iter().enumerate().tuple_combinations() 134 | .filter(|&((_, &(_, ref info1)), (_, &(_, ref info2)))| !info1.dependents.is_empty() && layout_map.contains_key(&info1.id) && !layout_map.contains_key(&info2.id)) 135 | .map(|((pos1, &(_, ref info1)), (_, &(_, ref info2)))| (pos1, info1.id, info2.id, &info1.dependents & &info2.dependents)) 136 | .filter(|&(_, _, _, ref shared)| !shared.is_empty()) 137 | .max_by_key(|&(_, _, _, ref shared)| shared.len()); 138 | 139 | if let Some((pos1, id1, id2, shared)) = output { 140 | let alpha2_id = layout_map.entry(id2) 141 | .or_insert_with(|| NodeLayout{node_id: node_id_gen.next_alpha_id(), destinations: Default::default()}) 142 | .node_id; 143 | layout_map.get_mut(&id1).unwrap().destinations.push(alpha2_id.into()); 144 | tests.get_mut(pos1).unwrap().1.dependents.retain(|x| !shared.contains(&x)); 145 | } else { 146 | break; 147 | } 148 | } 149 | println!("Final layout: {:?}", &layout_map); 150 | // TODO: Assert layout numbers are correct 151 | // Do the actual layout into the alpha network 152 | tests.sort_by_key(|&(_, ref info)| layout_map.get(&info.id).unwrap().node_id); 153 | for (test, info) in tests.into_iter() { 154 | let alpha_layout = layout_map.remove(&info.id).unwrap(); 155 | let id = alpha_layout.node_id; 156 | let dest = alpha_layout.destinations; 157 | let store = !info.dependents.is_empty(); 158 | assert_eq!(alpha_network.len(), alpha_layout.node_id.index()); 159 | alpha_network.push(AlphaNode{id, test, store, dest}); 160 | 161 | for statment_id in info.dependents { 162 | statement_memories.insert(statment_id, id.into()); 163 | } 164 | } 165 | 166 | } 167 | println!("Conditions: {:?}", &conditions); 168 | println!("HashEqNode: {:?}", &hash_eq_nodes); 169 | println!("Memory map: {:?}", &statement_memories); 170 | println!("Alpha Network: size {:?}", alpha_network.len()); 171 | (hash_eq_nodes, alpha_network, statement_memories) 172 | } 173 | 174 | fn compile_beta_network(statement_memories: &HashMap, 175 | statement_rule_map: &HashMap, 176 | mut hash_eq_nodes: HashMap, 177 | mut alpha_network: Vec>) { 178 | let mut beta_ids: SerialGen = Default::default(); 179 | 180 | let mut memory_rule_map: HashMap> = HashMap::new(); 181 | 182 | for (statement_id, memory_id) in statement_memories { 183 | let rule_id = *statement_rule_map.get(statement_id).unwrap(); 184 | memory_rule_map 185 | .entry(*memory_id) 186 | .or_insert_with(|| Default::default()).insert(rule_id); 187 | } 188 | /* 189 | 190 | let mut beta_network= Vec::new(); 191 | 192 | let mut beta_stack = Vec::new(); 193 | */ 194 | 195 | // 1. Select (and remove from the map) the memory (m1) with the most rules 196 | // 2. Select the next memory (m2) with the most shared rules 197 | // 3a. Create a new AND beta node (b1) (in NodeLayout) 198 | // 3b. Remove shared rules from m1 & m2. If either have no more rules, remove from map. 199 | // 3c. Add b1's destination id to m1 and m2's destinations 200 | // 3d. Add b1 to beta stack. 201 | // 4. If an m2 can be found, go to 3a. Otherwise add rule to destination. pop b1 off beta stack 202 | // 5. If stack empty, select next m2 for m1. if no m2, add rule ids as destination nodes. if no more m1 rules, remove from map 203 | 204 | 205 | let mut alpha_mem_dependents: Vec<(MemoryId, HashSet)> = memory_rule_map.into_iter().collect(); 206 | alpha_mem_dependents.sort_by_key(|&(_, ref rule_set)| rule_set.len()); 207 | 208 | while let Some((most_dep_id, mut most_dep)) = alpha_mem_dependents.pop() { 209 | // early exit in case we've reached the front with no dependencies 210 | if most_dep.is_empty() { 211 | break; 212 | } 213 | while let Some((intersect_pos, intersect)) = alpha_mem_dependents.iter().enumerate().rev() 214 | .filter(|&(_, &(_, ref rule_set))| !rule_set.is_empty()) 215 | .map(|(pos, &(_, ref rule_set))| (pos, &most_dep & rule_set)) 216 | .filter(|&(pos, ref intersect)| !intersect.is_empty()) 217 | .max_by_key(|&(pos, ref intersect)| !intersect.len()) { 218 | 219 | // Join alpha nodes with beta 220 | let beta_id = beta_ids.next(); 221 | 222 | most_dep.retain(|x| !intersect.contains(x)); 223 | Self::add_alpha_destination(&mut hash_eq_nodes, &mut alpha_network, most_dep_id, beta_id.into()); 224 | { 225 | let &mut (intersect_id, ref mut intersect_dep) = alpha_mem_dependents.get_mut(intersect_pos).unwrap(); 226 | intersect_dep.retain(|x| !intersect.contains(x)); 227 | Self::add_alpha_destination(&mut hash_eq_nodes, &mut alpha_network, intersect_id, beta_id.into()); 228 | } 229 | // TODO: Left off at creating new beta node 230 | 231 | } 232 | 233 | 234 | alpha_mem_dependents.sort_by_key(|&(_, ref rule_set)| rule_set.len()); 235 | } 236 | } 237 | 238 | fn add_alpha_destination(hash_eq_nodes: &mut HashMap, 239 | alpha_network: &mut Vec>, 240 | memory: MemoryId, 241 | destination: DestinationNode) { 242 | use ::base::MemoryId::*; 243 | match memory { 244 | HashEq(ref id) => {hash_eq_nodes.get_mut(id).unwrap().1.destinations.push(destination)}, 245 | Alpha(alpha_id) => {alpha_network.get_mut(alpha_id.index()).unwrap().dest.push(destination)}, 246 | _ => unreachable!("We shouldn't be adding an beta memory destination with this function") 247 | } 248 | } 249 | 250 | } 251 | #[derive(Debug, Eq, Hash, Ord, PartialOrd, PartialEq)] 252 | struct NodeLayout { 253 | node_id: T, 254 | destinations: Vec 255 | } 256 | 257 | #[derive(Debug, Copy, Clone, Eq, Hash, Ord, PartialOrd, PartialEq)] 258 | pub enum DestinationNode { 259 | Alpha(AlphaId), 260 | Beta(BetaId), 261 | Rule(RuleId) 262 | } 263 | 264 | impl Into for AlphaId { 265 | fn into(self) -> DestinationNode { 266 | DestinationNode::Alpha(self) 267 | } 268 | } 269 | 270 | impl Into for BetaId { 271 | fn into(self) -> DestinationNode { 272 | DestinationNode::Beta(self) 273 | } 274 | } 275 | 276 | impl Into for RuleId { 277 | fn into(self) -> DestinationNode { 278 | DestinationNode::Rule(self) 279 | } 280 | 281 | } 282 | 283 | #[derive(Debug)] 284 | pub struct HashEqNode { 285 | id: HashEqId, 286 | store: bool, 287 | destinations: Vec 288 | } 289 | 290 | pub struct AlphaNode { 291 | id: AlphaId, 292 | test: AlphaTest, 293 | store: bool, 294 | dest: Vec 295 | } 296 | 297 | pub struct AlphaMemory { 298 | mem: HashMap>>, 299 | } 300 | 301 | impl AlphaMemory { 302 | pub fn insert + AlphaMemoryId>(&mut self, id: I, val: Rc) { 303 | let mem_id = id.into(); 304 | self.mem.entry(mem_id) 305 | .or_insert_with(Default::default) 306 | .insert(val); 307 | } 308 | } 309 | 310 | pub struct AlphaNetwork { 311 | hash_eq_node: HashMap, 312 | alpha_network: Vec> 313 | } 314 | 315 | pub struct FactStore { 316 | store: HashSet> 317 | } 318 | 319 | impl FactStore { 320 | pub fn insert(&mut self, val: T) -> Rc { 321 | let rc = Rc::new(val); 322 | if !self.store.insert(rc.clone()) { 323 | self.store.get(&rc).unwrap().clone() 324 | } else { 325 | rc 326 | } 327 | } 328 | } 329 | 330 | pub enum BetaNodeType { 331 | And(MemoryId, MemoryId) 332 | } 333 | 334 | pub struct BetaNode { 335 | id: BetaId, 336 | b_type: BetaNodeType, 337 | destinations: Vec 338 | } 339 | 340 | pub struct BetaNetwork { 341 | b_network: Vec 342 | } 343 | 344 | pub struct BetaMemory { 345 | tripwire: Vec, 346 | } 347 | -------------------------------------------------------------------------------- /src/shared/nodes/alpha.rs: -------------------------------------------------------------------------------- 1 | use ord_subset::OrdVar; 2 | use decimal::d128; 3 | use ordered_float::NotNaN; 4 | use std::hash::{Hash, Hasher}; 5 | use super::tests::*; 6 | use runtime::memory::SymbolId; 7 | use chrono::NaiveTime; 8 | use chrono::Date; 9 | use chrono::Utc; 10 | use chrono::DateTime; 11 | use shared::fact::Fact; 12 | use shared::fact::FactField; 13 | use shared::context::AlphaContext; 14 | use std::fmt::Debug; 15 | use std::fmt; 16 | use shared::nodes::beta::{BetaNode, IsAlpha}; 17 | use enum_index; 18 | use enum_index::EnumIndex; 19 | 20 | pub trait IsHashEq { 21 | fn is_hash_eq(&self) -> bool; 22 | } 23 | 24 | pub trait AlphaTestField { 25 | fn alpha_test_field(&self, value: &T, context: &C) -> bool; 26 | } 27 | 28 | 29 | #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] 30 | pub enum BoolTest { 31 | Eq(Truth, EqTest, bool) 32 | } 33 | 34 | impl IsHashEq for BoolTest { 35 | fn is_hash_eq(&self) -> bool { 36 | use self::BoolTest::*; 37 | match self { 38 | Eq(..) => true 39 | } 40 | } 41 | } 42 | 43 | impl AlphaTestField for BoolTest { 44 | fn alpha_test_field(&self, value: &bool, _: &C) -> bool { 45 | use self::BoolTest::*; 46 | match *self { 47 | Eq(truth, ref test, ref to) => (truth, test).test(value, to) 48 | } 49 | } 50 | } 51 | 52 | macro_rules! alpha_number_test { 53 | ($($id:ty => $test:ident),+) => { 54 | $( 55 | #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] 56 | pub enum $test { 57 | Ord(Truth, OrdTest, $id), 58 | Btwn(Truth, BetweenTest, $id, $id), 59 | Eq(Truth, EqTest, $id) 60 | } 61 | 62 | impl IsHashEq for $test { 63 | fn is_hash_eq(&self) -> bool { 64 | use self::$test::*; 65 | match self { 66 | Eq(Truth::Is, EqTest::Eq,_) => true, 67 | Eq(Truth::Not, EqTest::Ne,_) => true, 68 | _ => false 69 | } 70 | } 71 | } 72 | 73 | impl AlphaTestField<$id> for $test { 74 | fn alpha_test_field(&self, value: &$id, _: &C) -> bool { 75 | use self::$test::*; 76 | match *self { 77 | Ord(truth, ref test, ref to) => (truth, test).test(value, to), 78 | Btwn(truth, ref test, ref from, ref to) => (truth, test).test(value, from, to), 79 | Eq(truth, ref test, ref to) => (truth, test).test(value, to) 80 | } 81 | } 82 | } 83 | )* 84 | }; 85 | } 86 | 87 | macro_rules! alpha_float_test { 88 | ($($id:ty => $test:ident),+) => { 89 | $( 90 | #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] 91 | pub enum $test { 92 | Ord(Truth, OrdTest, $id), 93 | Btwn(Truth, BetweenTest, $id, $id), 94 | ApproxEq(Truth, ApproxEqTest, $id) 95 | } 96 | 97 | impl IsHashEq for $test { 98 | fn is_hash_eq(&self) -> bool { 99 | false 100 | } 101 | } 102 | 103 | impl AlphaTestField<$id> for $test { 104 | fn alpha_test_field(&self, value: &$id, _: &C) -> bool { 105 | use self::$test::*; 106 | match *self { 107 | Ord(truth, ref test, ref to) => (truth, test).test(value, to), 108 | Btwn(truth, ref test, ref from, ref to) => (truth, test).test(value, from, to), 109 | ApproxEq(truth, ref test, ref to) => (truth, test).test(value, to) 110 | } 111 | } 112 | } 113 | )* 114 | }; 115 | } 116 | 117 | alpha_number_test!( 118 | i8 => I8Test, 119 | i16 => I16Test, 120 | i32 => I32Test, 121 | i64 => I64Test, 122 | i128 => I128Test, 123 | u8 => U8Test, 124 | u16 => U16Test, 125 | u32 => U32Test, 126 | u64 => U64Test, 127 | u128 => U128Test, 128 | OrdVar => D128Test 129 | ); 130 | 131 | alpha_float_test!( 132 | NotNaN => F32Test, 133 | NotNaN => F64Test 134 | ); 135 | 136 | #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] 137 | pub enum StrTest { 138 | Ord(Truth, OrdTest, SymbolId), 139 | Btwn(Truth, BetweenTest, SymbolId, SymbolId), 140 | Eq(Truth, EqTest, SymbolId), 141 | Str(Truth, StrArrayTest, SymbolId) 142 | } 143 | 144 | impl IsHashEq for StrTest { 145 | fn is_hash_eq(&self) -> bool { 146 | use self::StrTest::*; 147 | match self { 148 | Eq(Truth::Is, EqTest::Eq, _) => true, 149 | Eq(Truth::Not, EqTest::Ne, _) => true, 150 | _ => false 151 | } 152 | } 153 | } 154 | 155 | impl AlphaTestField for StrTest { 156 | fn alpha_test_field(&self, value: &str, context: &C) -> bool { 157 | use self::StrTest::*; 158 | let string_cache = context.get_string_cache(); 159 | match *self { 160 | Ord(truth, ref test, ref to) => (truth, test).test(value, string_cache.resolve(*to).unwrap()), 161 | Btwn(truth, ref test, ref from, ref to) => (truth, test).test(value, string_cache.resolve(*from).unwrap(), string_cache.resolve(*to).unwrap()), 162 | Eq(truth, ref test, ref to) => (truth, test).test(value, string_cache.resolve(*to).unwrap()), 163 | Str(truth, ref test, ref to) => (truth, test).test(value, string_cache.resolve(*to).unwrap()), 164 | 165 | } 166 | } 167 | } 168 | 169 | 170 | #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] 171 | pub enum TimeTest { 172 | Ord(Truth, OrdTest, NaiveTime), 173 | Btwn(Truth, BetweenTest, NaiveTime, NaiveTime), 174 | Eq(Truth, EqTest, NaiveTime) 175 | } 176 | 177 | impl IsHashEq for TimeTest { 178 | fn is_hash_eq(&self) -> bool { 179 | use self::TimeTest::*; 180 | match self { 181 | Eq(Truth::Is, EqTest::Eq, _) => true, 182 | Eq(Truth::Not, EqTest::Ne, _) => true, 183 | _ => false 184 | } 185 | } 186 | } 187 | 188 | impl AlphaTestField for TimeTest { 189 | fn alpha_test_field(&self, value: &NaiveTime, _: &C) -> bool { 190 | use self::TimeTest::*; 191 | match *self { 192 | Ord(truth, ref test, ref to) => (truth, test).test(value, to), 193 | Btwn(truth, ref test, ref from, ref to) => (truth, test).test(value, from, to), 194 | Eq(truth, ref test, ref to) => (truth, test).test(value, to) 195 | } 196 | } 197 | } 198 | 199 | #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] 200 | pub enum DateTest { 201 | Ord(Truth, OrdTest, Date), 202 | Btwn(Truth, BetweenTest, Date, Date), 203 | Eq(Truth, EqTest, Date) 204 | } 205 | 206 | impl IsHashEq for DateTest { 207 | fn is_hash_eq(&self) -> bool { 208 | use self::DateTest::*; 209 | match self { 210 | Eq(Truth::Is, EqTest::Eq, _) => true, 211 | Eq(Truth::Not, EqTest::Ne, _) => true, 212 | _ => false 213 | } 214 | } 215 | } 216 | 217 | impl AlphaTestField> for DateTest { 218 | fn alpha_test_field(&self, value: &Date, _: &C) -> bool { 219 | use self::DateTest::*; 220 | match *self { 221 | Ord(truth, ref test, ref to) => (truth, test).test(value, to), 222 | Btwn(truth, ref test, ref from, ref to) => (truth, test).test(value, from, to), 223 | Eq(truth, ref test, ref to) => (truth, test).test(value, to) 224 | } 225 | } 226 | } 227 | 228 | 229 | #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] 230 | pub enum DateTimeTest { 231 | Ord(Truth, OrdTest, DateTime), 232 | Btwn(Truth, BetweenTest, DateTime, DateTime), 233 | Eq(Truth, EqTest, DateTime) 234 | } 235 | 236 | impl IsHashEq for DateTimeTest { 237 | fn is_hash_eq(&self) -> bool { 238 | use self::DateTimeTest::*; 239 | match self { 240 | Eq(Truth::Is, EqTest::Eq, _) => true, 241 | Eq(Truth::Not, EqTest::Ne, _) => true, 242 | _ => false 243 | } 244 | } 245 | } 246 | 247 | impl AlphaTestField> for DateTimeTest { 248 | fn alpha_test_field(&self, value: &DateTime, _: &C) -> bool { 249 | use self::DateTimeTest::*; 250 | match *self { 251 | Ord(truth, ref test, ref to) => (truth, test).test(value, to), 252 | Btwn(truth, ref test, ref from, ref to) => (truth, test).test(value, from, to), 253 | Eq(truth, ref test, ref to) => (truth, test).test(value, to) 254 | } 255 | } 256 | } 257 | 258 | #[derive(Copy, EnumIndex)] 259 | pub enum AlphaNode { 260 | HASHEQ, 261 | BOOL(fn(&T) -> &bool, BoolTest), 262 | I8(fn(&T) -> &i8, I8Test), 263 | I16(fn(&T) -> &i16, I16Test), 264 | I32(fn(&T) -> &i32, I32Test), 265 | I64(fn(&T) -> &i64, I64Test), 266 | I128(fn(&T) -> &i128, I128Test), 267 | U8(fn(&T) -> &u8, U8Test), 268 | U16(fn(&T) -> &u16, U16Test), 269 | U32(fn(&T) -> &u32, U32Test), 270 | U64(fn(&T) -> &u64, U64Test), 271 | U128(fn(&T) -> &u128, U128Test), 272 | F32(fn(&T) -> &NotNaN, F32Test), 273 | F64(fn(&T) -> &NotNaN, F64Test), 274 | D128(fn(&T) -> &OrdVar, D128Test), 275 | STR(fn(&T) -> &str, StrTest), 276 | TIME(fn(&T) -> &NaiveTime, TimeTest), 277 | DATE(fn(&T) -> &Date, DateTest), 278 | DATETIME(fn(&T) -> &DateTime, DateTimeTest), 279 | } 280 | 281 | macro_rules! alpha_derive { 282 | ($($t:ident),+ ) => { 283 | impl Clone for AlphaNode { 284 | fn clone(&self) -> Self { 285 | use self::AlphaNode::*; 286 | match self { 287 | HASHEQ => HASHEQ, 288 | $( 289 | $t(getter, test) => $t(*getter, test.clone()), 290 | )* 291 | } 292 | } 293 | } 294 | 295 | impl Hash for AlphaNode { 296 | fn hash < H: Hasher > ( &self, state: & mut H) { 297 | use self::AlphaNode::*; 298 | match self { 299 | HASHEQ => self.enum_index().hash(state), 300 | $( 301 | $t(getter, test) => Self::hash_self(self.enum_index(), (*getter) as usize, test, state), 302 | )* 303 | } 304 | } 305 | } 306 | 307 | impl PartialEq for AlphaNode { 308 | fn eq(&self, other: &Self) -> bool { 309 | use self::AlphaNode::*; 310 | match (self, other) { 311 | (HASHEQ, HASHEQ) => true, 312 | $( ($t(getter1, test1), $t(getter2, test2)) => { 313 | (*getter1 as usize) == (*getter2 as usize) && test1 == test2 314 | },)* 315 | _ => false 316 | } 317 | } 318 | } 319 | 320 | impl Eq for AlphaNode {} 321 | 322 | impl IsHashEq for AlphaNode { 323 | fn is_hash_eq(&self) -> bool { 324 | use self::AlphaNode::*; 325 | match self { 326 | $( 327 | $t(_, test) => test.is_hash_eq(), 328 | )* 329 | _ => unreachable!(), 330 | } 331 | } 332 | } 333 | 334 | impl From> for AlphaNode { 335 | fn from(node: BetaNode) -> AlphaNode { 336 | use self::BetaNode::*; 337 | match node { 338 | $( 339 | $t(getter, test) if test.is_alpha() => AlphaNode::$t(getter, test.into()), 340 | )* 341 | _ => unreachable!("Into AlphaNode with unsupported config") 342 | } 343 | } 344 | } 345 | }; 346 | } 347 | 348 | alpha_derive!( 349 | BOOL, 350 | I8, I16, I32, I64, I128, 351 | U8, U16, U32, U64, U128, 352 | F32, F64, D128, 353 | STR, 354 | TIME, DATE, DATETIME 355 | ); 356 | 357 | impl Debug for AlphaNode { 358 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 359 | use self::AlphaNode::*; 360 | write!(f, "Getter(")?; 361 | match self { 362 | HASHEQ => write!(f, "HASHEQ")?, 363 | BOOL(getter, test) => write!(f, "BOOL({:#x}) - {:?}", (*getter) as usize, test)?, 364 | I8(getter, test) => write!(f, "I8({:#x}) - {:?}", (*getter) as usize, test)?, 365 | I16(getter, test) => write!(f, "I16({:#x}) - {:?}", (*getter) as usize, test)?, 366 | I32(getter, test) => write!(f, "I32({:#x}) - {:?}", (*getter) as usize, test)?, 367 | I64(getter, test) => write!(f, "I64({:#x}) - {:?}", (*getter) as usize, test)?, 368 | I128(getter, test) => write!(f, "I128({:#x}) - {:?}", (*getter) as usize, test)?, 369 | U8(getter, test) => write!(f, "U8({:#x}) - {:?}", (*getter) as usize, test)?, 370 | U16(getter, test) => write!(f, "U16({:#x}) - {:?}", (*getter) as usize, test)?, 371 | U32(getter, test) => write!(f, "U32({:#x}) - {:?}", (*getter) as usize, test)?, 372 | U64(getter, test) => write!(f, "U64({:#x}) - {:?}", (*getter) as usize, test)?, 373 | U128(getter, test) => write!(f, "U128({:#x}) - {:?}", (*getter) as usize, test)?, 374 | F32(getter, test) => write!(f, "F32({:#x}) - {:?}", (*getter) as usize, test)?, 375 | F64(getter, test) => write!(f, "F64({:#x}) - {:?}", (*getter) as usize, test)?, 376 | D128(getter, test) => write!(f, "D128({:#x}) - {:?}", (*getter) as usize, test)?, 377 | STR(getter, test) => write!(f, "STR({:#x}) - {:?}", (*getter) as usize, test)?, 378 | TIME(getter, test) => write!(f, "TIME({:#x}) - {:?}", (*getter) as usize, test)?, 379 | DATE(getter, test) => write!(f, "DATE({:#x}) - {:?}", (*getter) as usize, test)?, 380 | DATETIME(getter, test) => write!(f, "DATETIME({:#x}) - {:?}", (*getter) as usize, test)?, 381 | } 382 | write!(f, ")") 383 | } 384 | } 385 | 386 | impl AlphaNode { 387 | fn hash_self(ord: usize, getter: usize, test: &K, state: &mut H) { 388 | ord.hash(state); 389 | getter.hash(state); 390 | test.hash(state); 391 | } 392 | } 393 | 394 | pub enum HashEqField { 395 | BOOL(usize, bool), 396 | I8(usize, i8), 397 | I16(usize, i16), 398 | I32(usize, i32), 399 | I64(usize, i64), 400 | U8(usize, u8), 401 | U16(usize, u16), 402 | U32(usize, u32), 403 | U64(usize, u64), 404 | F32(usize, NotNaN), 405 | F64(usize, NotNaN), 406 | D128(usize, OrdVar), 407 | STR(usize, SymbolId), 408 | TIME(usize, NaiveTime), 409 | DATE(usize, Date), 410 | DATETIME(usize, DateTime), 411 | } 412 | 413 | macro_rules! from_alphanode_to_hasheq { 414 | ($($id:tt => $test:tt),+) => { 415 | impl From> for HashEqField { 416 | fn from(node: AlphaNode) -> Self { 417 | use self::AlphaNode::*; 418 | match node { 419 | BOOL(getter, BoolTest::Eq(truth, EqTest::Eq, to)) => HashEqField::BOOL(getter as usize, truth.is_not() ^ to), 420 | BOOL(getter, BoolTest::Eq(truth, EqTest::Ne, to)) => HashEqField::BOOL(getter as usize, truth.is_not() ^ ! to), 421 | $( 422 | $id(getter, $test::Eq(Truth::Is, EqTest::Eq, to)) => HashEqField::$id(getter as usize, to), 423 | $id(getter, $test::Eq(Truth::Not, EqTest::Ne, to)) => HashEqField::$id(getter as usize, to), 424 | )* 425 | _ => unreachable!("Into HashEqField With Unsupported Config") 426 | } 427 | } 428 | } 429 | }; 430 | } 431 | 432 | from_alphanode_to_hasheq!( 433 | I8 => I8Test, 434 | I16 => I16Test, 435 | I32 => I32Test, 436 | I64 => I64Test, 437 | U8 => U8Test, 438 | U16 => U16Test, 439 | U32 => U32Test, 440 | U64 => U64Test, 441 | D128 => D128Test, 442 | STR => StrTest, 443 | TIME => TimeTest, 444 | DATE => DateTest, 445 | DATETIME => DateTimeTest 446 | ); 447 | -------------------------------------------------------------------------------- /src/builder.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use ::traits::ReteIntrospection; 3 | use ::base::KnowledgeBase; 4 | use runtime::memory::{SymbolId, StringCache}; 5 | use ::builders::ids::*; 6 | use std::hash::Hash; 7 | use std::hash::Hasher; 8 | use std::collections::{HashMap, HashSet}; 9 | use std::fmt; 10 | use std::fmt::Debug; 11 | 12 | pub struct ReteIdGenerator { 13 | rule_ids: RuleIdGen, 14 | statement_ids: StatementIdGen, 15 | condition_ids: ConditionIdGen 16 | } 17 | 18 | impl ReteIdGenerator { 19 | pub fn new() -> ReteIdGenerator { 20 | ReteIdGenerator{ 21 | rule_ids: Default::default(), 22 | statement_ids: Default::default(), 23 | condition_ids: Default::default() 24 | } 25 | } 26 | 27 | pub fn next_rule_id(&mut self) -> RuleId { 28 | self.rule_ids.next() 29 | } 30 | 31 | pub fn next_statement_id(&mut self) -> StatementId { 32 | self.statement_ids.next() 33 | } 34 | 35 | pub fn next_condition_id(&mut self) -> ConditionId { 36 | self.condition_ids.next() 37 | } 38 | } 39 | 40 | impl Default for ReteIdGenerator { 41 | fn default() -> Self { 42 | ReteIdGenerator::new() 43 | } 44 | } 45 | 46 | struct BuilderShared { 47 | string_repo: StringCache, 48 | id_generator: ReteIdGenerator, 49 | condition_map: HashMap, ConditionInfo>> 50 | } 51 | 52 | impl BuilderShared { 53 | fn new() -> BuilderShared { 54 | BuilderShared{ 55 | string_repo: StringCache::new(), 56 | id_generator: Default::default(), 57 | condition_map: Default::default() 58 | } 59 | } 60 | } 61 | 62 | pub struct KnowledgeBuilder { 63 | dummy: PhantomData, 64 | build_shared: BuilderShared, 65 | rules: HashMap 66 | } 67 | 68 | impl Debug for KnowledgeBuilder 69 | where T::HashEq: Debug { 70 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 71 | writeln!(f, "KBuilder {{")?; 72 | writeln!(f, " Rules: [")?; 73 | for rule in self.rules.values() { 74 | writeln!(f, " {:?} - sids: {:?}", self.build_shared.string_repo.resolve(rule.name_sym).unwrap(), rule.statement_ids)?; 75 | } 76 | writeln!(f, " ],")?; 77 | writeln!(f, " Alpha Nodes: [")?; 78 | for (root_hash, dependents) in &self.build_shared.condition_map { 79 | let root_info = dependents.get(&AlphaTest::HashEq).unwrap(); 80 | writeln!(f, " -{:?} - sids: {:?}", root_hash, root_info.dependents)?; 81 | for (test, info) in dependents.iter().filter(|&(t, _)| !t.is_hash_eq()) { 82 | writeln!(f, " +{:?}({:?}) - {:?} - sids: {:?}", 83 | info.field_sym.and_then(|s| self.build_shared.string_repo.resolve(s)), 84 | info.id, 85 | test, 86 | info.dependents 87 | )?; 88 | } 89 | } 90 | writeln!(f, " ],")?; 91 | write!(f, "}}") 92 | } 93 | } 94 | 95 | impl KnowledgeBuilder { 96 | pub fn new() -> KnowledgeBuilder { 97 | KnowledgeBuilder {dummy: PhantomData, build_shared: BuilderShared::new(), rules: Default::default()} 98 | } 99 | 100 | pub fn rule + AsRef>(self, name: S) -> RuleBuilder { 101 | RuleBuilder::new(self, name) 102 | } 103 | 104 | pub fn compile(self) -> KnowledgeBase { 105 | KnowledgeBase::compile(self) 106 | } 107 | 108 | pub(crate) fn explode(self) -> (StringCache, HashMap, HashMap, ConditionInfo>>) { 109 | (self.build_shared.string_repo, self.rules, self.build_shared.condition_map) 110 | } 111 | 112 | 113 | fn get_shared(&mut self) -> &mut BuilderShared { 114 | &mut self.build_shared 115 | } 116 | } 117 | 118 | pub struct Rule { 119 | rule_id: RuleId, 120 | name_sym: SymbolId, 121 | pub(crate) statement_ids: HashSet 122 | } 123 | 124 | pub struct RuleBuilder { 125 | kbuilder: KnowledgeBuilder, 126 | rule_id: RuleId, 127 | name_sym: SymbolId, 128 | statement_ids: HashSet 129 | } 130 | 131 | impl RuleBuilder { 132 | fn new + AsRef>(mut kbuilder: KnowledgeBuilder, name: S) -> RuleBuilder { 133 | let (rule_id, name_sym) = { 134 | (kbuilder.get_shared().id_generator.next_rule_id(), kbuilder.get_shared().string_repo.get_or_intern(name)) 135 | }; 136 | RuleBuilder{kbuilder, rule_id, name_sym, statement_ids: Default::default()} 137 | } 138 | 139 | pub fn when(self) -> StatementBuilder { 140 | StatementBuilder::new(self) 141 | } 142 | 143 | pub fn end(self) -> KnowledgeBuilder { 144 | // nop in terms of output for now 145 | let rule = Rule{rule_id: self.rule_id, name_sym: self.name_sym, statement_ids: self.statement_ids}; 146 | let mut kbuilder = self.kbuilder; 147 | kbuilder.rules.insert(rule.rule_id, rule); 148 | kbuilder 149 | } 150 | 151 | fn get_shared(&mut self) -> &mut BuilderShared { 152 | self.kbuilder.get_shared() 153 | } 154 | } 155 | 156 | pub struct StatementBuilder { 157 | rule_builder: RuleBuilder, 158 | conditions: Vec 159 | } 160 | 161 | impl StatementBuilder { 162 | fn new(mut rule_builder: RuleBuilder) -> StatementBuilder { 163 | StatementBuilder {rule_builder, conditions: Default::default()} 164 | } 165 | 166 | pub fn exists(mut self) -> RuleBuilder { 167 | self.conditions.push(StatementCondition::Exists); 168 | self.rule_builder 169 | } 170 | 171 | pub fn eq + AsRef>(mut self, field: S, to: u64) -> StatementBuilder { 172 | let field_sym = self.rule_builder.get_shared().string_repo.get_or_intern(field); 173 | self.conditions.push(StatementCondition::Eq{field_sym, to}); 174 | self 175 | } 176 | 177 | pub fn lt + AsRef>(mut self, field: S, to: u64, closed: bool) -> StatementBuilder { 178 | let field_sym = self.rule_builder.get_shared().string_repo.get_or_intern(field); 179 | self.conditions.push(StatementCondition::Lt{field_sym, to, closed}); 180 | self 181 | } 182 | 183 | pub fn gt + AsRef>(mut self, field: S, from: u64, closed: bool) -> StatementBuilder { 184 | let field_sym = self.rule_builder.get_shared().string_repo.get_or_intern(field); 185 | self.conditions.push(StatementCondition::Gt{field_sym, from, closed}); 186 | self 187 | } 188 | 189 | pub fn btwn + AsRef>(mut self, field: S, from: u64, from_closed: bool, to: u64, to_closed: bool) -> StatementBuilder { 190 | let field_sym = self.rule_builder.get_shared().string_repo.get_or_intern(field); 191 | self.conditions.push(StatementCondition::Btwn{field_sym, from, from_closed, to, to_closed}); 192 | self 193 | } 194 | 195 | pub fn then(self) -> RuleBuilder { 196 | self.collapse_builder() 197 | } 198 | 199 | fn collapse_builder(mut self) -> RuleBuilder { 200 | let hash_eq = T::create_hash_eq(&self.conditions, &self.rule_builder.get_shared().string_repo); 201 | let (conditions, mut rule_builder) = (self.conditions, self.rule_builder); 202 | 203 | if !conditions.is_empty() { 204 | let statement_id = { 205 | //let rule_id = rule_builder.rule_id; 206 | let shared = rule_builder.get_shared(); 207 | let (id_generator, string_repo, condition_map) = (&mut shared.id_generator, &shared.string_repo, &mut shared.condition_map); 208 | 209 | let statement_id = id_generator.next_statement_id(); 210 | 211 | let entry_point = condition_map 212 | .entry(hash_eq).or_insert_with(Default::default); 213 | 214 | for condition in conditions.into_iter().filter(|c| !c.is_hash_eq()) { 215 | let field_sym = condition.field_sym(); 216 | let test = condition.convert(string_repo); 217 | entry_point.entry(test).or_insert_with(|| ConditionInfo::new(id_generator.next_condition_id(), field_sym)) 218 | .dependents.insert(statement_id); 219 | } 220 | 221 | entry_point.entry(AlphaTest::HashEq).or_insert_with(|| ConditionInfo::new(id_generator.next_condition_id(), None)) 222 | .dependents.insert(statement_id); 223 | 224 | statement_id 225 | }; 226 | rule_builder.statement_ids.insert(statement_id); 227 | } 228 | rule_builder 229 | } 230 | } 231 | 232 | #[derive(Debug, Copy, Clone)] 233 | pub enum StatementCondition { 234 | Exists, 235 | Eq{field_sym: SymbolId, to: u64}, 236 | Lt{field_sym: SymbolId, to: u64, closed: bool}, 237 | Gt{field_sym: SymbolId, from: u64, closed: bool}, 238 | Btwn{field_sym: SymbolId, from:u64, from_closed: bool, to: u64, to_closed: bool} 239 | } 240 | 241 | impl StatementCondition { 242 | fn is_hash_eq(&self) -> bool { 243 | use self::StatementCondition::*; 244 | 245 | match self { 246 | &Exists | &Eq{..} => true, 247 | _ => false 248 | } 249 | } 250 | 251 | fn convert(self, string_repo: &StringCache) -> AlphaTest { 252 | use self::StatementCondition::*; 253 | use self::CLimits::*; 254 | match self { 255 | Lt{field_sym, to, closed} => { 256 | let accessor= string_repo.resolve(field_sym) 257 | .and_then(|s| T::getter(s)).unwrap(); 258 | let test = if closed { 259 | OrdTest::Le 260 | } else { 261 | OrdTest::Lt 262 | }; 263 | AlphaTest::Ord{data: CData::U64(accessor, S(to)), test} 264 | } 265 | Gt{field_sym, from, closed} => { 266 | let accessor= string_repo.resolve(field_sym) 267 | .and_then(|s| T::getter(s)).unwrap(); 268 | let test = if closed { 269 | OrdTest::Ge 270 | } else { 271 | OrdTest::Gt 272 | }; 273 | AlphaTest::Ord{data: CData::U64(accessor, S(from)), test} 274 | } 275 | Btwn{field_sym, from, from_closed, to, to_closed} => { 276 | let accessor= string_repo.resolve(field_sym) 277 | .and_then(|s| T::getter(s)).unwrap(); 278 | let test = match (from_closed, to_closed) { 279 | (false, false) => OrdTest::GtLt, 280 | (false, true) => OrdTest::GtLe, 281 | (true, false) => OrdTest::GeLt, 282 | (true, true) => OrdTest::GeLe, 283 | 284 | }; 285 | AlphaTest::Ord{data: CData::U64(accessor, D(from, to)), test} 286 | }, 287 | _ => AlphaTest::HashEq 288 | } 289 | } 290 | 291 | fn field_sym(&self) -> Option { 292 | use self::StatementCondition::*; 293 | match self { 294 | &Lt{field_sym, ..} => Some(field_sym), 295 | &Gt{field_sym, ..} => Some(field_sym), 296 | &Btwn{field_sym, ..} => Some(field_sym), 297 | _ => None 298 | } 299 | } 300 | } 301 | 302 | #[derive(Debug, Clone)] 303 | pub struct ConditionInfo { 304 | pub(crate) id: ConditionId, 305 | pub(crate) field_sym: Option, 306 | pub(crate) dependents: HashSet 307 | } 308 | 309 | impl ConditionInfo { 310 | fn new(id: ConditionId, field_sym: Option) -> ConditionInfo { 311 | ConditionInfo{id, field_sym, dependents: Default::default()} 312 | } 313 | } 314 | 315 | #[derive(Hash, Eq, PartialEq)] 316 | pub enum AlphaTest { 317 | HashEq, 318 | Ord{data: CData, test: OrdTest } 319 | } 320 | 321 | impl AlphaTest { 322 | pub fn is_hash_eq(&self) -> bool { 323 | use self::AlphaTest::*; 324 | match self { 325 | &HashEq => true, 326 | _ => false 327 | } 328 | } 329 | } 330 | 331 | impl Debug for AlphaTest { 332 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 333 | use self::AlphaTest::*; 334 | write!(f, "Test{{")?; 335 | match self { 336 | &HashEq => { 337 | write!(f, "HashEq")?; 338 | }, 339 | &Ord{ref data, test} => { 340 | write!(f, "Ord")?; 341 | } 342 | } 343 | write!(f, "}}") 344 | } 345 | } 346 | 347 | #[derive(Clone, Hash, Eq, PartialEq)] 348 | pub enum CLimits { 349 | S(T), 350 | D(T, T) 351 | } 352 | 353 | #[derive(Clone)] 354 | pub enum CData{ 355 | I8(fn(&T) -> &i8, CLimits), 356 | I16(fn(&T) -> &i16, CLimits), 357 | I32(fn(&T) -> &i32, CLimits), 358 | I64(fn(&T) -> &i64, CLimits), 359 | U8(fn(&T) -> &u8, CLimits), 360 | U16(fn(&T) -> &u16, CLimits), 361 | U32(fn(&T) -> &u32, CLimits), 362 | U64(fn(&T) -> &u64, CLimits), 363 | ISIZE(fn(&T) -> &isize, CLimits), 364 | USIZE(fn(&T) -> &usize, CLimits), 365 | //F32(fn(&T) -> &f32, ConditionLimits>), 366 | //F64(fn(&T) -> &f64, ConditionLimits>), 367 | //STR(fn(&T) -> &str, ConditionLimits), 368 | } 369 | 370 | impl CData { 371 | fn hash_self(ord: usize, accessor: usize, limits: &L, state: &mut H) { 372 | ord.hash(state); 373 | accessor.hash(state); 374 | limits.hash(state); 375 | } 376 | } 377 | 378 | impl Hash for CData { 379 | fn hash(&self, state: &mut H) { 380 | use self::CData::*; 381 | match self { 382 | &I8(accessor, ref limits) => { 383 | Self::hash_self(0, accessor as usize, limits, state); 384 | }, 385 | &I16(accessor, ref limits) => { 386 | Self::hash_self(1, accessor as usize, limits, state); 387 | }, 388 | &I32(accessor, ref limits) => { 389 | Self::hash_self(2, accessor as usize, limits, state); 390 | 391 | }, 392 | &I64(accessor, ref limits) => { 393 | Self::hash_self(3, accessor as usize, limits, state); 394 | }, 395 | &U8(accessor, ref limits) => { 396 | Self::hash_self(4, accessor as usize, limits, state); 397 | }, 398 | &U16(accessor, ref limits) => { 399 | Self::hash_self(5, accessor as usize, limits, state); 400 | }, 401 | &U32(accessor, ref limits) => { 402 | Self::hash_self(6, accessor as usize, limits, state); 403 | }, 404 | &U64(accessor, ref limits) => { 405 | Self::hash_self(7, accessor as usize, limits, state); 406 | }, 407 | &ISIZE(accessor, ref limits) => { 408 | Self::hash_self(8, accessor as usize, limits, state); 409 | }, 410 | &USIZE(accessor, ref limits) => { 411 | Self::hash_self(9, accessor as usize, limits, state); 412 | }, 413 | /* &F32(accessor, ref limits) => { 414 | Self::hash_self(10, accessor as usize, limits, state); 415 | }, 416 | &F64(accessor, ref limits) => { 417 | Self::hash_self(11, accessor as usize, limits, state); 418 | },*/ 419 | _ => {} 420 | } 421 | } 422 | } 423 | 424 | impl PartialEq for CData { 425 | fn eq(&self, other: &Self) -> bool { 426 | use self::CData::*; 427 | match (self, other) { 428 | (&I8(accessor1, ref limits1), &I8(accessor2, ref limits2)) => { 429 | (accessor1 as usize) == (accessor2 as usize) && limits1 == limits2 430 | }, 431 | (&I16(accessor1, ref limits1), &I16(accessor2, ref limits2)) => { 432 | (accessor1 as usize) == (accessor2 as usize) && limits1 == limits2 433 | }, 434 | (&I32(accessor1, ref limits1), &I32(accessor2, ref limits2)) => { 435 | (accessor1 as usize) == (accessor2 as usize) && limits1 == limits2 436 | }, 437 | (&I64(accessor1, ref limits1), &I64(accessor2, ref limits2)) => { 438 | (accessor1 as usize) == (accessor2 as usize) && limits1 == limits2 439 | }, 440 | (&U8(accessor1, ref limits1), &U8(accessor2, ref limits2)) => { 441 | (accessor1 as usize) == (accessor2 as usize) && limits1 == limits2 442 | }, 443 | (&U16(accessor1, ref limits1), &U16(accessor2, ref limits2)) => { 444 | (accessor1 as usize) == (accessor2 as usize) && limits1 == limits2 445 | }, 446 | (&U32(accessor1, ref limits1), &U32(accessor2, ref limits2)) => { 447 | (accessor1 as usize) == (accessor2 as usize) && limits1 == limits2 448 | }, 449 | (&U64(accessor1, ref limits1), &U64(accessor2, ref limits2)) => { 450 | (accessor1 as usize) == (accessor2 as usize) && limits1 == limits2 451 | }, 452 | (&ISIZE(accessor1, ref limits1), &ISIZE(accessor2, ref limits2)) => { 453 | (accessor1 as usize) == (accessor2 as usize) && limits1 == limits2 454 | }, 455 | (&USIZE(accessor1, ref limits1), &USIZE(accessor2, ref limits2)) => { 456 | (accessor1 as usize) == (accessor2 as usize) && limits1 == limits2 457 | }, 458 | /* (&F32(accessor1, ref limits1), &F32(accessor2, ref limits2)) => { 459 | (accessor1 as usize) == (accessor2 as usize) && limits1 == limits2 460 | }, 461 | (&F64(accessor1, ref limits1), &F64(accessor2, ref limits2)) => { 462 | (accessor1 as usize) == (accessor2 as usize) && limits1 == limits2 463 | },*/ 464 | _ => false 465 | } 466 | } 467 | } 468 | 469 | impl Eq for CData {} 470 | 471 | #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)] 472 | pub enum OrdTest { 473 | Lt, 474 | Le, 475 | Gt, 476 | Ge, 477 | GtLt, 478 | GeLt, 479 | GtLe, 480 | GeLe 481 | } 482 | 483 | 484 | -------------------------------------------------------------------------------- /src/shared/compiler/prelude.rs: -------------------------------------------------------------------------------- 1 | use ::shared::nodes::beta::{ 2 | TestRepr, SLimit, SDynLimit, DLimit, DDynLimit, 3 | BoolTest, 4 | I8Test, I16Test, I32Test, I64Test, I128Test, 5 | U8Test, U16Test, U32Test, U64Test, U128Test, 6 | F32Test, F64Test, D128Test, 7 | StrTest, 8 | TimeTest, DateTest, DateTimeTest, 9 | SDynTests, DDynTests 10 | }; 11 | use std::marker; 12 | use ord_subset::OrdVar; 13 | use ordered_float::NotNaN; 14 | use decimal::d128; 15 | use chrono::{Utc, NaiveTime, Date, DateTime}; 16 | use std::borrow::Cow; 17 | use shared::nodes::tests::{Truth, EqTest, OrdTest, BetweenTest, StrArrayTest}; 18 | use shared::fact::{Fact, FactFieldType}; 19 | use runtime::memory::StringCache; 20 | use shared::nodes::beta::{IsAlpha, BetaNode}; 21 | use errors::CompileError; 22 | use enum_index; 23 | use enum_index::EnumIndex; 24 | use std::cmp::Ordering; 25 | use shared::nodes::tests::ApplyNot; 26 | use std::mem; 27 | use runtime::memory::SymbolId; 28 | use shared::fact::Getter; 29 | use shared::nodes::alpha::AlphaNode; 30 | use shared::nodes::beta::CollectRequired; 31 | use std::collections::HashSet; 32 | use std::collections::HashMap; 33 | 34 | pub fn dyn>(limit: S) -> SDynLimit { 35 | SDynLimit{limit} 36 | } 37 | 38 | pub trait AString: AsRef {} 39 | 40 | impl<'a> AString for &'a str {} 41 | impl AString for String {} 42 | impl<'a> AString for Cow<'a, str> {} 43 | 44 | pub trait IntoEqTest> { 45 | fn into_eq_test(self, field: S, test: EqTest) -> TestRepr; 46 | } 47 | 48 | pub trait IntoOrdTest> { 49 | fn into_ord_test(self, field: S, test: OrdTest) -> TestRepr; 50 | } 51 | 52 | pub trait IntoBtwnTest> { 53 | fn into_btwn_test(self, field: S, test: BetweenTest) -> TestRepr; 54 | } 55 | 56 | pub trait IntoStrTest> { 57 | fn into_str_test(self, field: S, test: StrArrayTest) -> TestRepr; 58 | } 59 | 60 | // Single values 61 | 62 | // Eq testing 63 | macro_rules! into_eq_tests { 64 | ($($id:ty => [$sub:ident, $test:ident]),+) => { 65 | $( 66 | impl> IntoEqTest for $id { 67 | fn into_eq_test(self, field: S, test: EqTest) -> TestRepr { 68 | TestRepr::$sub(field, $test::Eq(Truth::Is, test, SLimit::St(self))) 69 | } 70 | } 71 | )* 72 | }; 73 | } 74 | 75 | into_eq_tests!( 76 | bool => [BOOL, BoolTest], 77 | i8 => [I8, I8Test], 78 | i16 => [I16, I16Test], 79 | i32 => [I32, I32Test], 80 | i64 => [I64, I64Test], 81 | i128 => [I128, I128Test], 82 | u8 => [U8, U8Test], 83 | u16 => [U16, U16Test], 84 | u32 => [U32, U32Test], 85 | u64 => [U64, U64Test], 86 | u128 => [U128, U128Test], 87 | OrdVar => [D128, D128Test], 88 | NaiveTime => [TIME, TimeTest], 89 | Date => [DATE, DateTest], 90 | DateTime => [DATETIME, DateTimeTest] 91 | ); 92 | 93 | macro_rules! float_into_approx_eq_tests { 94 | ($($id:ty => [$sub:ident, $test:ident]),+) => { 95 | $( 96 | impl> IntoEqTest for $id { 97 | fn into_eq_test(self, field: S, test: EqTest) -> TestRepr { 98 | TestRepr::$sub(field, $test::ApproxEq(Truth::Is, test.into(), SLimit::St(self.into()))) 99 | } 100 | } 101 | )* 102 | }; 103 | } 104 | 105 | float_into_approx_eq_tests!( 106 | f32 => [F32, F32Test], 107 | f64 => [F64, F64Test] 108 | ); 109 | 110 | macro_rules! nn_float_into_approx_eq_tests { 111 | ($($id:ty => [$sub:ident, $test:ident]),+) => { 112 | $( 113 | impl> IntoEqTest for $id { 114 | fn into_eq_test(self, field: S, test: EqTest) -> TestRepr { 115 | TestRepr::$sub(field, $test::ApproxEq(Truth::Is, test.into(), SLimit::St(self))) 116 | } 117 | } 118 | )* 119 | }; 120 | } 121 | 122 | nn_float_into_approx_eq_tests!( 123 | NotNaN => [F32, F32Test], 124 | NotNaN => [F64, F64Test] 125 | ); 126 | 127 | 128 | impl IntoEqTest for S { 129 | fn into_eq_test(self, field: S, test: EqTest) -> TestRepr { 130 | TestRepr::STR(field, StrTest::Eq(Truth::Is, test, SLimit::St(self))) 131 | } 132 | } 133 | 134 | impl> IntoEqTest for d128 { 135 | fn into_eq_test(self, field: S, test: EqTest) -> TestRepr { 136 | TestRepr::D128(field, D128Test::Eq(Truth::Is, test, SLimit::St(self.into()))) 137 | } 138 | } 139 | 140 | impl> IntoEqTest for SDynLimit { 141 | fn into_eq_test(self, field: S, test: EqTest) -> TestRepr { 142 | TestRepr::SDYN(field, Truth::Is, SDynTests::Eq(test), self) 143 | } 144 | } 145 | 146 | // Ord testing 147 | 148 | macro_rules! into_ord_tests { 149 | ($($id:ty => [$sub:ident, $test:ident]),+) => { 150 | $( 151 | impl> IntoOrdTest for $id { 152 | fn into_ord_test(self, field: S, test: OrdTest) -> TestRepr { 153 | TestRepr::$sub(field, $test::Ord(Truth::Is,test, SLimit::St(self))) 154 | } 155 | } 156 | )* 157 | }; 158 | } 159 | 160 | into_ord_tests!( 161 | i8 => [I8, I8Test], 162 | i16 => [I16, I16Test], 163 | i32 => [I32, I32Test], 164 | i64 => [I64, I64Test], 165 | i128 => [I128, I128Test], 166 | u8 => [U8, U8Test], 167 | u16 => [U16, U16Test], 168 | u32 => [U32, U32Test], 169 | u64 => [U64, U64Test], 170 | u128 => [U128, U128Test], 171 | NotNaN => [F32, F32Test], 172 | NotNaN => [F64, F64Test], 173 | OrdVar => [D128, D128Test], 174 | NaiveTime => [TIME, TimeTest], 175 | Date => [DATE, DateTest], 176 | DateTime => [DATETIME, DateTimeTest] 177 | ); 178 | 179 | macro_rules! float_into_ord_tests { 180 | ($($id:ty => [$sub:ident, $test:ident]),+) => { 181 | $( 182 | impl> IntoOrdTest for $id { 183 | fn into_ord_test(self, field: S, test: OrdTest) -> TestRepr { 184 | TestRepr::$sub(field, $test::Ord(Truth::Is, test, SLimit::St(self.into()))) 185 | } 186 | } 187 | )* 188 | }; 189 | } 190 | 191 | float_into_ord_tests!( 192 | f32 => [F32, F32Test], 193 | f64 => [F64, F64Test], 194 | d128 => [D128, D128Test] 195 | ); 196 | 197 | impl IntoOrdTest for S { 198 | fn into_ord_test(self, field: S, test: OrdTest) -> TestRepr { 199 | TestRepr::STR(field, StrTest::Ord(Truth::Is, test, SLimit::St(self))) 200 | } 201 | } 202 | 203 | impl> IntoOrdTest for SDynLimit { 204 | fn into_ord_test(self, field: S, test: OrdTest) -> TestRepr { 205 | TestRepr::SDYN(field, Truth::Is,SDynTests::Ord(test), self) 206 | } 207 | } 208 | 209 | // Double values 210 | 211 | // Between testing 212 | 213 | macro_rules! into_btwn_tests { 214 | ($($id:ty => [$sub:ident, $test:ident]),+) => { 215 | $( 216 | impl> IntoBtwnTest for ($id, $id) { 217 | fn into_btwn_test(self, field: S, test: BetweenTest) -> TestRepr { 218 | TestRepr::$sub(field, $test::Btwn(Truth::Is, test, DLimit::St(self.0, self.1))) 219 | } 220 | } 221 | 222 | impl> IntoBtwnTest for (SDynLimit, $id) { 223 | fn into_btwn_test(self, field: S, test: BetweenTest) -> TestRepr { 224 | TestRepr::$sub(field, $test::Btwn(Truth::Is, test, DLimit::DynSt(self.0.limit, self.1))) 225 | } 226 | } 227 | 228 | impl> IntoBtwnTest for ($id, SDynLimit) { 229 | fn into_btwn_test(self, field: S, test: BetweenTest) -> TestRepr { 230 | TestRepr::$sub(field, $test::Btwn(Truth::Is, test, DLimit::StDyn(self.0, self.1.limit))) 231 | } 232 | } 233 | )* 234 | }; 235 | } 236 | 237 | into_btwn_tests!( 238 | i8 => [I8, I8Test], 239 | i16 => [I16, I16Test], 240 | i32 => [I32, I32Test], 241 | i64 => [I64, I64Test], 242 | i128 => [I128, I128Test], 243 | u8 => [U8, U8Test], 244 | u16 => [U16, U16Test], 245 | u32 => [U32, U32Test], 246 | u64 => [U64, U64Test], 247 | u128 => [U128, U128Test], 248 | NotNaN => [F32, F32Test], 249 | NotNaN => [F64, F64Test], 250 | OrdVar => [D128, D128Test], 251 | NaiveTime => [TIME, TimeTest], 252 | Date => [DATE, DateTest], 253 | DateTime => [DATETIME, DateTimeTest] 254 | ); 255 | 256 | macro_rules! float_into_btwn_tests { 257 | ($($id:ty => [$sub:ident, $test:ident]),+) => { 258 | $( 259 | impl> IntoBtwnTest for ($id, $id) { 260 | fn into_btwn_test(self, field: S, test: BetweenTest) -> TestRepr { 261 | TestRepr::$sub(field, $test::Btwn(Truth::Is, test, DLimit::St(self.0.into(), self.1.into()))) 262 | } 263 | } 264 | 265 | impl> IntoBtwnTest for (SDynLimit, $id) { 266 | fn into_btwn_test(self, field: S, test: BetweenTest) -> TestRepr { 267 | TestRepr::$sub(field, $test::Btwn(Truth::Is, test, DLimit::DynSt(self.0.limit, self.1.into()))) 268 | } 269 | } 270 | 271 | impl> IntoBtwnTest for ($id, SDynLimit) { 272 | fn into_btwn_test(self, field: S, test: BetweenTest) -> TestRepr { 273 | TestRepr::$sub(field, $test::Btwn(Truth::Is, test, DLimit::StDyn(self.0.into(), self.1.limit))) 274 | } 275 | } 276 | )* 277 | }; 278 | } 279 | 280 | float_into_btwn_tests!( 281 | f32 => [F32, F32Test], 282 | f64 => [F64, F64Test], 283 | d128 => [D128, D128Test] 284 | ); 285 | 286 | impl IntoBtwnTest for (S, S) { 287 | fn into_btwn_test(self, field: S, test: BetweenTest) -> TestRepr { 288 | TestRepr::STR(field, StrTest::Btwn(Truth::Is,test, DLimit::St(self.0, self.1))) 289 | } 290 | } 291 | 292 | impl IntoBtwnTest for (SDynLimit, S) { 293 | fn into_btwn_test(self, field: S, test: BetweenTest) -> TestRepr { 294 | TestRepr::STR(field, StrTest::Btwn(Truth::Is,test, DLimit::DynSt(self.0.limit, self.1))) 295 | } 296 | } 297 | 298 | impl IntoBtwnTest for (S, SDynLimit) { 299 | fn into_btwn_test(self, field: S, test: BetweenTest) -> TestRepr { 300 | TestRepr::STR(field, StrTest::Btwn(Truth::Is, test, DLimit::StDyn(self.0, self.1.limit))) 301 | } 302 | } 303 | 304 | impl> IntoBtwnTest for (SDynLimit, SDynLimit) { 305 | fn into_btwn_test(self, field: S, test: BetweenTest) -> TestRepr { 306 | let limit = DDynLimit{l: self.0.limit, r: self.1.limit}; 307 | TestRepr::DDYN(field, Truth::Is, DDynTests::Btwn(test), limit) 308 | } 309 | } 310 | 311 | impl IntoStrTest for S { 312 | fn into_str_test(self, field: S, test: StrArrayTest) -> TestRepr { 313 | TestRepr::STR(field, StrTest::Str(Truth::Is,test, SLimit::St(self))) 314 | } 315 | } 316 | 317 | pub trait DrainWhere 318 | where F: FnMut(&T) -> bool { 319 | fn drain_where(&mut self, f: F) -> Self; 320 | } 321 | 322 | impl DrainWhere for Vec 323 | where F: FnMut(&T) -> bool { 324 | fn drain_where(&mut self, mut f: F) -> Self { 325 | let mut i = 0; 326 | let mut v = Vec::new(); 327 | while i != self.len() { 328 | if f(&mut self[i]) { 329 | v.push(self.remove(i)); 330 | } else { 331 | i += 1; 332 | } 333 | } 334 | v 335 | } 336 | } 337 | 338 | #[derive(Clone, Hash, Eq, PartialEq, Debug, EnumIndex)] 339 | pub enum Stage1Node { 340 | Test(BetaNode), 341 | Any(Vec>), 342 | NotAny(Vec>), 343 | All(Vec>), 344 | NotAll(Vec>) 345 | } 346 | 347 | // https://stackoverflow.com/questions/36557412/change-enum-variant-while-moving-the-field-to-the-new-variant 348 | impl ApplyNot for Stage1Node { 349 | fn apply_not(&mut self) { 350 | use self::Stage1Node::*; 351 | let interim = unsafe { mem::zeroed() }; 352 | let prev = mem::replace(self, interim); 353 | let next = match prev { 354 | Test(mut node) => { 355 | node.apply_not(); 356 | Test(node) 357 | }, 358 | Any(nodes) => NotAny(nodes), 359 | NotAny(nodes) => Any(nodes), 360 | All(nodes) => NotAll(nodes), 361 | NotAll(nodes) => All(nodes), 362 | }; 363 | let interim = mem::replace(self, next); 364 | mem::forget(interim); // Important! interim was never initialized 365 | } 366 | } 367 | 368 | impl Ord for Stage1Node { 369 | fn cmp(&self, other: &Self) -> Ordering { 370 | use self::Stage1Node::*; 371 | match (self, other) { 372 | (Test(ref n1), Test(ref n2)) => n1.cmp(n2), 373 | (Any(ref v1), Any(ref v2)) => v1.cmp(v2), 374 | (NotAny(ref v1), NotAny(ref v2)) => v1.cmp(v2), 375 | (All(ref v1), All(ref v2)) => v1.cmp(v2), 376 | (NotAll(ref v1), NotAll(ref v2)) => v1.cmp(v2), 377 | _ => self.enum_index().cmp(&other.enum_index()) 378 | } 379 | } 380 | } 381 | 382 | impl PartialOrd for Stage1Node { 383 | fn partial_cmp(&self, other: &Self) -> Option { 384 | Some(self.cmp(other)) 385 | } 386 | } 387 | 388 | impl Stage1Node { 389 | 390 | pub fn is_test(&self) -> bool { 391 | use self::Stage1Node::*; 392 | match *self { 393 | Test(_) => true, 394 | _ => false 395 | } 396 | } 397 | 398 | pub fn is_alpha(&self) -> bool { 399 | use self::Stage1Node::*; 400 | match *self { 401 | Test(ref test) => test.is_alpha(), 402 | _ => false 403 | } 404 | } 405 | 406 | pub fn get_alpha(self) -> AlphaNode { 407 | use self::Stage1Node::*; 408 | match self { 409 | Test(test) => test.into(), 410 | _ => unreachable!("get_alpha on non alpha node") 411 | } 412 | } 413 | 414 | pub fn is_any(&self) -> bool { 415 | use self::Stage1Node::*; 416 | match *self { 417 | Any(_) => true, 418 | _ => false 419 | } 420 | } 421 | 422 | pub fn is_all(&self) -> bool { 423 | use self::Stage1Node::*; 424 | match *self { 425 | All(_) => true, 426 | _ => false 427 | } 428 | } 429 | 430 | fn is_singleton(&self) -> bool { 431 | use self::Stage1Node::*; 432 | match *self { 433 | Any(ref nodes) => nodes.len() == 1, 434 | NotAny(ref nodes) => nodes.len() == 1, 435 | All(ref nodes) => nodes.len() == 1, 436 | NotAll(ref nodes) => nodes.len() == 1, 437 | _ => false 438 | } 439 | } 440 | 441 | pub fn is_empty(&self) -> bool { 442 | use self::Stage1Node::*; 443 | match *self { 444 | Any(ref nodes) => nodes.is_empty(), 445 | NotAny(ref nodes) => nodes.is_empty(), 446 | All(ref nodes) => nodes.is_empty(), 447 | NotAll(ref nodes) => nodes.is_empty(), 448 | _ => false 449 | } 450 | } 451 | 452 | fn extract_singleton(self) -> Self { 453 | use self::Stage1Node::*; 454 | debug_assert!(self.is_singleton()); 455 | match self { 456 | Any(mut nodes) => nodes.pop().unwrap(), 457 | NotAny(mut nodes) => { 458 | let mut node = nodes.pop().unwrap(); 459 | node.apply_not(); 460 | node 461 | }, 462 | All(mut nodes) => nodes.pop().unwrap(), 463 | NotAll(mut nodes) => { 464 | let mut node = nodes.pop().unwrap(); 465 | node.apply_not(); 466 | node 467 | }, 468 | _ => unreachable!("extract_singleton on a BetaNode"), 469 | } 470 | } 471 | 472 | fn simplify(&mut self) { 473 | use self::Stage1Node::*; 474 | match *self { 475 | Any(ref mut nodes) => while Self::simplify_any(nodes) {}, 476 | NotAny(ref mut nodes) => while Self::simplify_any(nodes) {}, 477 | All(ref mut nodes) => while Self::simplify_all(nodes) {}, 478 | NotAll(ref mut nodes) => while Self::simplify_all(nodes) {}, 479 | _ => {} 480 | } 481 | } 482 | 483 | /* 484 | * A while ago I was having a bad day and this quote made me laugh. 485 | * Let it be immortalized here forever with these merge functions as it 486 | * was posted around the same time I wrote the initial version of this algorithm. 487 | * They are inextricably bound in my mind 488 | * 489 | * This tank ain't big enough for both of us. 490 | * It's just the perfect size for a fish of our both's combined volume! And that fish is me! 491 | * - /u/GregTheMad - https://www.reddit.com/r/WTF/comments/82o8jv/this_tank_aint_big_enough_for_both_of_us/dvboaqw/ 492 | */ 493 | fn give(&mut self, to: &mut Vec) { 494 | use self::Stage1Node::*; 495 | match *self { 496 | Any(ref mut from) => to.append(from), 497 | All(ref mut from) => to.append(from), 498 | _ => unreachable!("give from invalid node") 499 | } 500 | } 501 | 502 | fn merge(&mut self, from_node: Self) { 503 | use self::Stage1Node::*; 504 | match(self, from_node) { 505 | (&mut Any(ref mut to), Any(ref mut from)) => to.append(from), 506 | _ => unreachable!("merge on invalid node combination") 507 | } 508 | } 509 | 510 | fn simplify_any(any: &mut Vec) -> bool { 511 | for node in any.iter_mut() { 512 | node.simplify(); 513 | } 514 | let mut continue_simplify = false; 515 | // Extract singletons 516 | if any.iter().filter(|n| n.is_singleton()).count() > 0 { 517 | continue_simplify = true; 518 | for mut o in any.drain_where(|n| n.is_singleton()) { 519 | any.push(o.extract_singleton()); 520 | } 521 | } 522 | // Merge any nodes 523 | if any.iter().filter(|n| n.is_any()).count() > 0 { 524 | continue_simplify = true; 525 | for mut o in any.drain_where(|n| n.is_any()) { 526 | o.give(any); 527 | } 528 | 529 | } 530 | continue_simplify 531 | } 532 | 533 | fn simplify_all(all: &mut Vec) -> bool { 534 | for node in all.iter_mut() { 535 | node.simplify(); 536 | } 537 | let mut continue_simplify = false; 538 | // Extract singletons 539 | if all.iter().filter(|n| n.is_singleton()).count() > 0 { 540 | continue_simplify = true; 541 | for mut o in all.drain_where(|n| n.is_singleton()) { 542 | all.push(o.extract_singleton()); 543 | } 544 | } 545 | // Merge all nodes 546 | if all.iter().filter(|n| n.is_all()).count() > 0 { 547 | continue_simplify = true; 548 | for mut o in all.drain_where(|n| n.is_all()) { 549 | o.give(all); 550 | } 551 | } 552 | // If there are multiple any nodes, merge into 1 553 | if all.iter().filter(|n| n.is_any()).count() > 1 { 554 | let mut cum_any = all.drain_where(|n| n.is_any()); 555 | let mut parent = cum_any.pop().unwrap(); 556 | for mut o in cum_any { 557 | parent.merge(o); 558 | } 559 | parent.simplify(); 560 | all.push(parent); 561 | } 562 | continue_simplify 563 | } 564 | 565 | fn dedup(&mut self) { 566 | use self::Stage1Node::*; 567 | match *self { 568 | Any(ref mut nodes) => Self::dedup_vec(nodes), 569 | NotAny(ref mut nodes) => Self::dedup_vec(nodes), 570 | All(ref mut nodes) => Self::dedup_vec(nodes), 571 | NotAll(ref mut nodes) => Self::dedup_vec(nodes), 572 | _ => {} 573 | } 574 | } 575 | 576 | fn dedup_vec(nodes: &mut Vec) { 577 | for node in nodes.iter_mut() { 578 | node.dedup(); 579 | } 580 | nodes.sort(); 581 | nodes.dedup(); 582 | } 583 | 584 | pub fn clean(mut self) -> Self { 585 | self.simplify(); 586 | self.dedup(); 587 | 588 | // TODO - add a walker to determine if there are any singletons, then do this until there are none 589 | self.simplify(); 590 | self.dedup(); 591 | 592 | let mut node = self; 593 | while node.is_singleton() { 594 | node = node.extract_singleton(); 595 | } 596 | 597 | node 598 | } 599 | 600 | pub fn collect_alpha(&mut self) -> Vec> { 601 | use self::Stage1Node::*; 602 | match *self { 603 | All(ref mut v) => { 604 | v.drain_where(|n| n.is_alpha()) 605 | .into_iter() 606 | .map(|n| n.get_alpha()).collect() 607 | }, 608 | _ => unreachable!("collect_alpha on non-all node") 609 | } 610 | } 611 | } 612 | 613 | impl CollectRequired for Stage1Node { 614 | fn collect_required(&self, symbols: &mut HashMap>) { 615 | use self::Stage1Node::*; 616 | match *self { 617 | Test(ref node) => node.collect_required(symbols), 618 | Any(ref nodes) => nodes.iter().for_each(|n| n.collect_required(symbols)), 619 | NotAny(ref nodes) => nodes.iter().for_each(|n| n.collect_required(symbols)), 620 | All(ref nodes) => nodes.iter().for_each(|n| n.collect_required(symbols)), 621 | NotAll(ref nodes) => nodes.iter().for_each(|n| n.collect_required(symbols)), 622 | } 623 | } 624 | } 625 | 626 | pub trait Stage1Compile { 627 | 628 | fn stage1_compile(&self, cache: &mut StringCache) -> Result, CompileError>; 629 | 630 | fn stage1_compile_slice(t: &[Self], cache: &mut StringCache) -> Result>, CompileError> 631 | where Self: marker::Sized { 632 | t.iter().map(|c| c.stage1_compile(cache)).collect() 633 | } 634 | } 635 | 636 | 637 | #[derive(Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Debug, EnumIndex)] 638 | pub enum ProvidesNode { 639 | Var(S), 640 | Field(S, G) 641 | } 642 | 643 | impl ProvidesNode { 644 | pub fn is_variable(&self) -> bool { 645 | use self::ProvidesNode::*; 646 | match *self { 647 | Var(_) => true, 648 | _ => false, 649 | } 650 | } 651 | 652 | pub fn is_field(&self) -> bool { 653 | use self::ProvidesNode::*; 654 | match *self { 655 | Field(..) => true, 656 | _ => false, 657 | } 658 | } 659 | } 660 | 661 | impl ProvidesNode where S: AsRef { 662 | pub fn compile(&self, cache: &mut StringCache) -> Result>, CompileError> { 663 | use self::ProvidesNode::*; 664 | match *self { 665 | Var(ref s) => Ok(Var(cache.get_or_intern(s.as_ref()))), 666 | Field(ref s, ref g) => Ok(Field( 667 | cache.get_or_intern(s.as_ref()), 668 | T::getter(g.as_ref()).ok_or_else(|| CompileError::MissingGetter { getter: g.as_ref().to_owned() })? 669 | )), 670 | } 671 | } 672 | } 673 | 674 | pub fn var>(s: S) -> ProvidesNode { 675 | ProvidesNode::Var(s) 676 | } 677 | 678 | pub fn field>(s: S, g: S) -> ProvidesNode { 679 | ProvidesNode::Field(s, g) 680 | } -------------------------------------------------------------------------------- /src/shared/runtimes/array/builder.rs: -------------------------------------------------------------------------------- 1 | use shared::fact::{Fact, Getter, FactFieldType, GetFieldType}; 2 | use shared::nodes::alpha::{IsHashEq, AlphaNode}; 3 | use shared::nodes::beta::{CollectRequired, BetaNode}; 4 | use shared::compiler::prelude::Stage1Node; 5 | use runtime::memory::StringCache; 6 | use shared::compiler::prelude::{DrainWhere, ProvidesNode}; 7 | use shared::compiler::id_generator::{IdGenerator, StatementGroupId, StatementId, ConditionId, ConditionGroupId, RuleId}; 8 | use runtime::memory::SymbolId; 9 | use std::collections::HashSet; 10 | use std::collections::HashMap; 11 | use errors::CompileError; 12 | use std; 13 | use std::collections::BTreeMap; 14 | use shared::nodes::alpha::HashEqField; 15 | use anymap::any::{IntoBox, Any, UncheckedAnyExt}; 16 | use anymap::Map; 17 | use shared::compiler::builder::BaseBuilder; 18 | use shared::compiler::builder::RuleBuilder; 19 | use shared::compiler::prelude::Stage1Compile; 20 | use shared::compiler::builder::ConsequenceBuilder; 21 | use shared::compiler::builder::KnowledgeBase; 22 | use bimap::BiMap; 23 | use std::any::TypeId; 24 | use std::fmt::Debug; 25 | 26 | // TODO Beta compile 27 | /* 28 | * Store 2 different Map> to represent the relationships between parent and children 29 | * one for the relationship between parent node and children 30 | * one for relationship between child and parents 31 | * Store a Set> and Map>> te track parent types 32 | * Store Map, ArrayId> to prevent duplicate parents 33 | * take the most shared child 34 | * iterate parents' children to determine the set of most shared children 35 | * create a new intermediate node between the parent and the children. Update their information as necessary 36 | * continue until no more nodes are shared (up to cd twhat point?) 37 | 38 | * Rules will remember their entry point 39 | * Arrays will remember their rules 40 | */ 41 | 42 | 43 | pub struct ArrayKnowledgeBase { 44 | 45 | } 46 | 47 | impl KnowledgeBase for ArrayKnowledgeBase {} 48 | 49 | 50 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug)] 51 | enum StatementGroupEntry { 52 | Statement(StatementId), 53 | Exists(StatementId), 54 | Absent(StatementId), 55 | Child(StatementGroupId), 56 | } 57 | 58 | #[derive(Clone, Eq, PartialEq, Debug)] 59 | enum StatementGroup { 60 | All(StatementGroupId, Vec), 61 | Any(StatementGroupId, Vec), 62 | ForAll(StatementGroupId, StatementId, Vec), 63 | } 64 | 65 | impl StatementGroup { 66 | fn all(parent: StatementGroupId) -> StatementGroup { 67 | StatementGroup::All(parent, Vec::new()) 68 | } 69 | 70 | fn any(parent: StatementGroupId) -> StatementGroup { 71 | StatementGroup::Any(parent, Vec::new()) 72 | } 73 | 74 | fn for_all(parent: StatementGroupId, statement: StatementId) -> StatementGroup { 75 | StatementGroup::ForAll(parent, statement, Vec::new()) 76 | } 77 | 78 | fn parent(&self) -> StatementGroupId { 79 | match *self { 80 | StatementGroup::All(parent, _) => parent, 81 | StatementGroup::Any(parent, _) => parent, 82 | StatementGroup::ForAll(parent, ..) => parent, 83 | } 84 | } 85 | 86 | fn push(&mut self, entry: StatementGroupEntry) { 87 | match *self { 88 | StatementGroup::All(_, ref mut entries) => entries.push(entry), 89 | StatementGroup::Any(_, ref mut entries) => entries.push(entry), 90 | StatementGroup::ForAll(_, _, ref mut entries) => entries.push(entry), 91 | } 92 | } 93 | 94 | fn should_merge(&self, other: &Self) -> bool { 95 | use self::StatementGroup::*; 96 | match (self, other) { 97 | (All(..), All(..)) => true, 98 | (ForAll(..), All(..)) => true, 99 | (Any(..), Any(..)) => true, 100 | _ => false 101 | } 102 | } 103 | 104 | fn can_single_entry_optimize(&self) -> bool { 105 | use self::StatementGroup::*; 106 | match self { 107 | StatementGroup::All(_, entries) => entries.len() == 1, 108 | StatementGroup::Any(_, entries) => entries.len() == 1, 109 | _ => false 110 | } 111 | } 112 | 113 | fn extract_single_entry(self) -> StatementGroupEntry { 114 | use self::StatementGroup::*; 115 | match self { 116 | StatementGroup::All(_, entries) => entries[0], 117 | StatementGroup::Any(_, entries) => entries[0], 118 | _ => unreachable!("extract single entry from non optimizable group {:?}", self) 119 | } 120 | } 121 | } 122 | 123 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] 124 | pub enum ConditionGroupType { 125 | Any, 126 | NotAny, 127 | All, 128 | NotAll 129 | } 130 | 131 | pub trait StatementDetails { 132 | fn provides_var(&self) -> Option; 133 | fn provides_fields<'a>(&'a self) -> Box + 'a>; 134 | fn requires_fields(&self) -> &HashMap>; 135 | } 136 | 137 | #[derive(Clone, Eq, PartialEq, Debug)] 138 | struct StatementProvides { 139 | var: Option, 140 | fields: HashMap> 141 | } 142 | 143 | impl Default for StatementProvides { 144 | fn default() -> Self { 145 | StatementProvides{var: None, fields: Default::default()} 146 | } 147 | } 148 | 149 | #[derive(Clone, Eq, PartialEq, Debug)] 150 | struct StatementData { 151 | statement_provides: StatementProvides, 152 | statement_requires: HashMap>, 153 | condition_groups: HashMap 154 | } 155 | 156 | impl StatementDetails for StatementData { 157 | fn provides_var(&self) -> Option { 158 | self.statement_provides.var 159 | } 160 | 161 | fn provides_fields<'a>(&'a self) -> Box + 'a> { 162 | Box::new( 163 | self.statement_provides.fields.iter() 164 | .map(|(key, val)| (*key, val.get_field_type())) 165 | ) 166 | } 167 | 168 | fn requires_fields(&self) -> &HashMap> { 169 | &self.statement_requires 170 | } 171 | } 172 | 173 | // TODO: After we build up the groupings & requirements, cascade down the groupings to ensure that we're not screwing anything up 174 | 175 | struct ArrayRuleData { 176 | id: RuleId, 177 | name: SymbolId, 178 | salience: i32, 179 | no_loop: bool, 180 | agenda_group: SymbolId, 181 | current_group: StatementGroupId, 182 | statement_groups: BTreeMap, 183 | statement_data: BTreeMap>, 184 | } 185 | 186 | 187 | struct AlphaConditionInfo { 188 | condition_id: ConditionId, 189 | dependents: HashSet, 190 | } 191 | 192 | impl AlphaConditionInfo { 193 | fn new(condition_id: ConditionId) -> AlphaConditionInfo { 194 | AlphaConditionInfo{ condition_id, dependents: HashSet::new() } 195 | } 196 | } 197 | 198 | pub trait NetworkBuilder: Any { 199 | 200 | } 201 | 202 | //impl NetworkBuilder for T {} 203 | 204 | impl UncheckedAnyExt for NetworkBuilder { 205 | #[inline] 206 | unsafe fn downcast_ref_unchecked(&self) -> &T { 207 | &*(self as *const Self as *const T) 208 | } 209 | 210 | #[inline] 211 | unsafe fn downcast_mut_unchecked(&mut self) -> &mut T { 212 | &mut *(self as *mut Self as *mut T) 213 | } 214 | 215 | #[inline] 216 | unsafe fn downcast_unchecked(self: Box) -> Box { 217 | Box::from_raw(Box::into_raw(self) as *mut T) 218 | } 219 | } 220 | 221 | #[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Debug)] 222 | enum ConditionGroupChild { 223 | Condition(ConditionId), 224 | Group(ConditionGroupId), 225 | } 226 | 227 | struct BetaGraph { 228 | rule_rel: HashMap>, 229 | statement_root: HashMap, 230 | parent_child_rel: BiMap>, 231 | child_group_rel: HashMap, 232 | test_nodes: BiMap> 233 | } 234 | 235 | impl Default for BetaGraph { 236 | fn default() -> Self { 237 | BetaGraph { 238 | rule_rel: Default::default(), 239 | statement_root: Default::default(), 240 | parent_child_rel: Default::default(), 241 | child_group_rel: Default::default(), 242 | test_nodes: Default::default() 243 | } 244 | } 245 | } 246 | 247 | pub struct ArrayNetworkBuilder { 248 | alpha_graph: HashMap, AlphaConditionInfo>>, 249 | beta_graph_map: HashMap> 250 | } 251 | 252 | impl IntoBox for ArrayNetworkBuilder { 253 | fn into_box(self) -> Box { 254 | Box::new(self) 255 | } 256 | } 257 | 258 | 259 | impl ArrayNetworkBuilder { 260 | fn new() -> ArrayNetworkBuilder { 261 | ArrayNetworkBuilder{ alpha_graph: Default::default(), beta_graph_map: Default::default()} 262 | } 263 | } 264 | 265 | impl Default for ArrayNetworkBuilder { 266 | fn default() -> Self { 267 | ArrayNetworkBuilder::new() 268 | } 269 | } 270 | 271 | impl NetworkBuilder for ArrayNetworkBuilder { 272 | 273 | } 274 | 275 | pub struct ArrayBaseBuilder { 276 | id_generator: IdGenerator, 277 | cache: StringCache, 278 | network_builders: Map 279 | } 280 | 281 | impl ArrayBaseBuilder { 282 | fn insert_alpha(&mut self, statement_id: StatementId, nodes: Vec>) { 283 | let hash_eq = T::create_hash_eq(&nodes); 284 | let (alpha_graph, id_generator) = 285 | ( 286 | self.network_builders.entry::>().or_insert_with( Default::default) 287 | .alpha_graph.entry(hash_eq).or_insert_with( Default::default), 288 | &mut self.id_generator 289 | ); 290 | for node in nodes.into_iter().filter(|n| !n.is_hash_eq()) { 291 | alpha_graph.entry(node) 292 | .or_insert_with(|| AlphaConditionInfo::new(id_generator.condition_ids.next())) 293 | .dependents.insert(statement_id); 294 | } 295 | } 296 | 297 | fn insert_beta(&mut self, agenda_group: SymbolId, rule_id: RuleId, statement_id: StatementId, beta_node: &Stage1Node) -> HashMap { 298 | let mut condition_groups = Default::default(); 299 | if !beta_node.is_empty() { 300 | let(beta_graph, id_generator) = 301 | ( 302 | self.network_builders.entry::>().or_insert_with(Default::default) 303 | .beta_graph_map.entry(agenda_group).or_insert_with(Default::default), 304 | &mut self.id_generator 305 | ); 306 | // Thank you @moxian in the Rust Discord for figuring out my monumental mistake! 307 | use self::Stage1Node::*; 308 | let statement_root = match beta_node { 309 | Any(ref beta_nodes) => Self::insert_beta_group(beta_graph, id_generator, rule_id, statement_id, ConditionGroupType::Any, beta_nodes, &mut condition_groups), 310 | NotAny(ref beta_nodes) => Self::insert_beta_group(beta_graph, id_generator, rule_id, statement_id, ConditionGroupType::NotAny, beta_nodes, &mut condition_groups), 311 | All(ref beta_nodes) => Self::insert_beta_group(beta_graph, id_generator, rule_id, statement_id, ConditionGroupType::All, beta_nodes, &mut condition_groups), 312 | NotAll(ref beta_nodes) => Self::insert_beta_group(beta_graph, id_generator, rule_id, statement_id, ConditionGroupType::NotAll, beta_nodes, &mut condition_groups), 313 | _ => unreachable!("Should not find a test at the topmost level") 314 | }; 315 | beta_graph.statement_root.insert(statement_id, statement_root); 316 | } 317 | condition_groups 318 | } 319 | 320 | fn insert_beta_group(beta_graph: &mut BetaGraph, 321 | id_generator: &mut IdGenerator, 322 | rule_id: RuleId, 323 | statement_id: StatementId, 324 | condition_group_type: ConditionGroupType, 325 | beta_nodes: &[Stage1Node], 326 | condition_groups: &mut HashMap) -> ConditionGroupChild { 327 | use self::Stage1Node::*; 328 | let mut children: Vec = beta_nodes.iter() 329 | .map(|beta_node| Self::insert_beta_child(beta_graph, id_generator, rule_id, statement_id, beta_node, condition_groups)) 330 | .collect(); 331 | children.sort(); 332 | let parent_group_id = { 333 | if !beta_graph.parent_child_rel.contains_right(&children) { 334 | let new_group_id = id_generator.condition_group_ids.next(); 335 | beta_graph.parent_child_rel.insert(new_group_id, children.clone()); 336 | new_group_id 337 | } else { 338 | *beta_graph.parent_child_rel.get_by_right(&children).unwrap() 339 | } 340 | }; 341 | for child in children { 342 | beta_graph.child_group_rel.insert(child, parent_group_id); 343 | } 344 | condition_groups.insert(parent_group_id, condition_group_type); 345 | let child_id = ConditionGroupChild::Group(parent_group_id); 346 | beta_graph.rule_rel.entry(child_id).or_insert_with(Default::default).push(rule_id); 347 | child_id 348 | } 349 | 350 | fn insert_beta_child(beta_graph: &mut BetaGraph, 351 | id_generator: &mut IdGenerator, 352 | rule_id: RuleId, 353 | statement_id: StatementId, 354 | beta_node: &Stage1Node, 355 | condition_groups: &mut HashMap) -> ConditionGroupChild { 356 | use self::Stage1Node::*; 357 | match beta_node { 358 | Any(ref beta_nodes) => Self::insert_beta_group(beta_graph, id_generator, rule_id, statement_id, ConditionGroupType::Any, beta_nodes, condition_groups), 359 | NotAny(ref beta_nodes) => Self::insert_beta_group(beta_graph, id_generator, rule_id, statement_id, ConditionGroupType::NotAny, beta_nodes, condition_groups), 360 | All(ref beta_nodes) => Self::insert_beta_group(beta_graph, id_generator, rule_id, statement_id, ConditionGroupType::All, beta_nodes, condition_groups), 361 | NotAll(ref beta_nodes) => Self::insert_beta_group(beta_graph, id_generator, rule_id, statement_id, ConditionGroupType::NotAll, beta_nodes, condition_groups), 362 | Test(ref node) => { 363 | if !beta_graph.test_nodes.contains_right(node) { 364 | let new_condition_id = id_generator.condition_ids.next(); 365 | beta_graph.test_nodes.insert(new_condition_id, node.clone()); 366 | ConditionGroupChild::Condition(new_condition_id) 367 | } else { 368 | ConditionGroupChild::Condition(*beta_graph.test_nodes.get_by_right(node).unwrap()) 369 | } 370 | } 371 | } 372 | } 373 | } 374 | 375 | impl BaseBuilder for ArrayBaseBuilder { 376 | type RB = ArrayRuleBuilder; 377 | type KB = ArrayKnowledgeBase; 378 | 379 | fn rule>(mut self, name: S) -> Self::RB { 380 | self.rule_with_agenda(name, "MAIN") 381 | } 382 | 383 | fn rule_with_agenda, A: AsRef>(mut self, name: S, agenda_group: A) -> Self::RB { 384 | let id = self.id_generator.rule_ids.next(); 385 | let name_symbol = self.cache.get_or_intern(name.as_ref()); 386 | let agenda_symbol = self.cache.get_or_intern(agenda_group.as_ref()); 387 | let root_group_id = self.id_generator.statement_group_ids.next(); 388 | let root_group = StatementGroup::all(root_group_id); 389 | 390 | let mut statement_groups: BTreeMap = Default::default(); 391 | statement_groups.insert(root_group_id, root_group); 392 | 393 | ArrayRuleBuilder { 394 | rule_data : ArrayRuleData { 395 | id, 396 | name: name_symbol, 397 | salience: 0, 398 | no_loop: false, 399 | agenda_group: agenda_symbol, 400 | current_group: root_group_id, 401 | statement_groups, 402 | statement_data: Default::default(), 403 | }, 404 | base_builder: self 405 | } 406 | } 407 | 408 | fn end(self) -> Self::KB { 409 | unimplemented!() 410 | } 411 | } 412 | 413 | 414 | pub struct ArrayRuleBuilder { 415 | rule_data: ArrayRuleData, 416 | base_builder: ArrayBaseBuilder, 417 | } 418 | 419 | impl ArrayRuleBuilder { 420 | fn add_new_statement, N: Stage1Compile>(&mut self, provides: &[ProvidesNode], nodes: &[N]) -> Result<(StatementId, Box), CompileError> { 421 | let rule_id = self.rule_data.id; 422 | let statement_id = self.base_builder.id_generator.statement_ids.next(); 423 | 424 | // Retrieve the upfront declarations 425 | // TODO - is there a way to do this in one line? 426 | let provides_result: Result>>, CompileError> 427 | = provides.iter() 428 | .map(|d| d.compile(&mut self.base_builder.cache)) 429 | .collect(); 430 | let provided = provides_result?; 431 | 432 | // TODO: Move into TryFrom once stable 433 | if provided.iter() 434 | .filter(|p| p.is_variable()) 435 | .count() > 1 { 436 | let rule = self.base_builder.cache.resolve(self.rule_data.name).unwrap(); 437 | return Err(CompileError::MultipleVariables {rule: rule.to_owned(), statement_id}); 438 | } 439 | 440 | let mut statement_provides: StatementProvides = Default::default(); 441 | 442 | for node in provided { 443 | match node { 444 | ProvidesNode::Var(s) => {statement_provides.var = Some(s);}, 445 | ProvidesNode::Field(s, g) => {statement_provides.fields.insert(s, g);}, 446 | } 447 | } 448 | 449 | let mut beta_nodes = Stage1Node::All(Stage1Compile::stage1_compile_slice(nodes, &mut self.base_builder.cache)?).clean(); 450 | let alpha_nodes = beta_nodes.collect_alpha(); 451 | self.base_builder.insert_alpha(statement_id, alpha_nodes); 452 | 453 | // For speed purposes, requires & condition_groups should be done in the same step? 454 | let mut statement_requires = Default::default(); 455 | beta_nodes.collect_required(&mut statement_requires); 456 | 457 | let condition_groups = 458 | self.base_builder.insert_beta(self.rule_data.agenda_group, rule_id, statement_id, &beta_nodes); 459 | 460 | let statement_details = Box::new(StatementData { 461 | statement_provides, 462 | statement_requires, 463 | condition_groups, 464 | }); 465 | 466 | Ok((statement_id, statement_details)) 467 | } 468 | 469 | fn add_new_group(&mut self, new_group: StatementGroup) { 470 | 471 | let parent_id = new_group.parent(); 472 | let mut has_group_id = None; 473 | { 474 | let parent_group = self.rule_data.statement_groups.get_mut(&parent_id).unwrap(); 475 | if !parent_group.should_merge(&new_group) { 476 | let new_group_id = self.base_builder.id_generator.statement_group_ids.next(); 477 | parent_group.push(StatementGroupEntry::Child(new_group_id)); 478 | has_group_id = Some(new_group_id); 479 | } 480 | } 481 | if let Some(new_group_id) = has_group_id { 482 | self.rule_data.statement_groups.insert(new_group_id, new_group); 483 | self.rule_data.current_group = new_group_id; 484 | }; 485 | 486 | } 487 | 488 | // ALL - there may only be a single source of a particular ID 489 | // ANY - all must supply the same IDs 490 | fn test(&mut self) { 491 | 492 | } 493 | } 494 | 495 | impl RuleBuilder for ArrayRuleBuilder { 496 | type CB = ArrayConsequenceBuilder; 497 | 498 | fn salience(mut self, salience: i32) -> Self { 499 | self.rule_data.salience = salience; 500 | self 501 | } 502 | 503 | fn no_loop(mut self, no_loop: bool) -> Self { 504 | self.rule_data.no_loop = no_loop; 505 | self 506 | } 507 | 508 | fn when>(self, nodes: &[N]) -> Result { 509 | self.provides_when::(&[], nodes) 510 | } 511 | 512 | fn provides_when, N: Stage1Compile>(mut self, provides: &[ProvidesNode], nodes: &[N]) -> Result { 513 | let (statement_id, statement_details) = self.add_new_statement(provides, nodes)?; 514 | let statement_group = self.rule_data.current_group; 515 | self.rule_data.statement_groups.get_mut(&statement_group) 516 | .unwrap() 517 | .push(StatementGroupEntry::Statement(statement_id)); 518 | 519 | self.rule_data.statement_data.insert(statement_id, statement_details); 520 | Ok(self) 521 | } 522 | 523 | fn when_exists>(mut self, nodes: &[N]) -> Result { 524 | let (statement_id, statement_details) = self.add_new_statement::(&[], nodes)?; 525 | let statement_group = self.rule_data.current_group; 526 | self.rule_data.statement_groups.get_mut(&statement_group) 527 | .unwrap() 528 | .push(StatementGroupEntry::Exists(statement_id)); 529 | 530 | self.rule_data.statement_data.insert(statement_id, statement_details); 531 | Ok(self) 532 | } 533 | 534 | fn when_absent>(mut self, nodes: &[N]) -> Result { 535 | let (statement_id, statement_details) = self.add_new_statement::(&[], nodes)?; 536 | let statement_group = self.rule_data.current_group; 537 | self.rule_data.statement_groups.get_mut(&statement_group) 538 | .unwrap() 539 | .push(StatementGroupEntry::Absent(statement_id)); 540 | 541 | self.rule_data.statement_data.insert(statement_id, statement_details); 542 | Ok(self) 543 | } 544 | 545 | fn when_for_all>(self, nodes: &[N]) -> Result { 546 | self.provides_when_for_all::(&[], nodes) 547 | } 548 | 549 | fn provides_when_for_all, N: Stage1Compile>(mut self, provides: &[ProvidesNode], nodes: &[N]) -> Result { 550 | let (statement_id, statement_details) = self.add_new_statement(provides, nodes)?; 551 | self.rule_data.statement_data.insert(statement_id, statement_details); 552 | let parent_group = self.rule_data.current_group; 553 | self.add_new_group(StatementGroup::for_all(parent_group, statement_id)); 554 | Ok(self) 555 | } 556 | 557 | fn all_group(mut self) -> Self { 558 | let parent_group = self.rule_data.current_group; 559 | self.add_new_group(StatementGroup::all(parent_group)); 560 | self 561 | } 562 | 563 | fn any_group(mut self) -> Self { 564 | let parent_group = self.rule_data.current_group; 565 | self.add_new_group(StatementGroup::any(parent_group)); 566 | self 567 | } 568 | 569 | fn end_group(mut self) -> Result { 570 | let current_group_id = self.rule_data.current_group; 571 | let parent_id = self.rule_data.statement_groups[¤t_group_id].parent(); 572 | if current_group_id != parent_id { 573 | // TODO - optimize this somehow 574 | let current_group = self.rule_data.statement_groups.remove(¤t_group_id).unwrap(); 575 | if current_group.can_single_entry_optimize() { 576 | let entry = current_group.extract_single_entry(); 577 | self.rule_data.statement_groups.get_mut(&parent_id).unwrap().push(entry); 578 | } else { 579 | self.rule_data.statement_groups.insert(current_group_id, current_group); 580 | } 581 | self.rule_data.current_group = parent_id; 582 | } 583 | Ok(self) 584 | } 585 | 586 | fn then(mut self) -> Result { 587 | // TODO: Validate statement groups & requirements 588 | // TODO: How do we want to handle consequences? 589 | 590 | // The grouping algorithm allows some pretty bad combinations at this stage 591 | // AND(NOT(AND(NOT( 592 | // A state machine may make more sense, but I've spent too much time trying to logic my way out of this 593 | // https://youtu.be/x4E5hzC8Xvs?list=PL6EC7B047181AD013&t=536 594 | loop { 595 | let mut current_group_id = self.rule_data.current_group; 596 | let mut parent_id = self.rule_data.statement_groups[¤t_group_id].parent(); 597 | if current_group_id == parent_id { 598 | break; 599 | } 600 | self = self.end_group()?; 601 | } 602 | Ok(ArrayConsequenceBuilder{ 603 | rule_data: self.rule_data, 604 | consequence_data: ArrayConsequenceData {}, 605 | base_builder: self.base_builder 606 | }) 607 | } 608 | } 609 | 610 | 611 | 612 | struct ArrayConsequenceData { 613 | 614 | } 615 | 616 | 617 | pub struct ArrayConsequenceBuilder { 618 | rule_data: ArrayRuleData, 619 | consequence_data: ArrayConsequenceData, 620 | base_builder: ArrayBaseBuilder, 621 | } 622 | 623 | impl ConsequenceBuilder for ArrayConsequenceBuilder { 624 | type BB = ArrayBaseBuilder; 625 | 626 | fn end(self) -> Result { 627 | Ok(self.base_builder) 628 | } 629 | } 630 | -------------------------------------------------------------------------------- /src/builders/statement.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use traits::{Fact, RuleBuilder, Getters}; 3 | use ordered_float::NotNaN; 4 | use runtime::memory::{StringCache, SymbolId}; 5 | use builders::ids::{StatementId, ConditionId}; 6 | use network::tests::{CLimits, OrdTest, OrdData, FLimits, FlData, FlTest, StrData, StrTest, AlphaTest}; 7 | use std::collections::HashSet; 8 | use ::Result; 9 | use failure::ResultExt; 10 | 11 | #[derive(Copy, Clone, Eq, Hash, Ord, PartialOrd, PartialEq, Debug)] 12 | pub enum ValueHolder { 13 | S(T), 14 | D(T, T) 15 | } 16 | 17 | #[derive(Copy, Clone, Eq, Hash, Ord, PartialOrd, PartialEq, Debug)] 18 | pub enum StatementValues { 19 | BOOL(ValueHolder), 20 | I8(ValueHolder), 21 | I16(ValueHolder), 22 | I32(ValueHolder), 23 | I64(ValueHolder), 24 | U8(ValueHolder), 25 | U16(ValueHolder), 26 | U32(ValueHolder), 27 | U64(ValueHolder), 28 | ISIZE(ValueHolder), 29 | USIZE(ValueHolder), 30 | F32(ValueHolder>), 31 | F64(ValueHolder>), 32 | STR(ValueHolder) 33 | } 34 | 35 | impl StatementValues { 36 | pub fn is_hash_eq(&self) -> bool { 37 | use self::StatementValues::*; 38 | match self { 39 | &F32(_) | &F64(_) => false, 40 | _ => true 41 | } 42 | } 43 | } 44 | 45 | pub trait IntoIntern { 46 | fn into_intern(self, cache: &mut StringCache) -> T; 47 | } 48 | 49 | pub trait TryInto { 50 | fn try_into(self) -> Result; 51 | } 52 | 53 | 54 | macro_rules! into_values { 55 | ($($id:ty => $sub:ident),+) => { 56 | $( 57 | impl IntoIntern for $id { 58 | fn into_intern(self, _: &mut StringCache) -> StatementValues { 59 | StatementValues::$sub(ValueHolder::S(self)) 60 | } 61 | } 62 | 63 | impl IntoIntern for ($id, $id) { 64 | fn into_intern(self, _: &mut StringCache) -> StatementValues { 65 | StatementValues::$sub(ValueHolder::D(self.0, self.1)) 66 | } 67 | } 68 | )* 69 | }; 70 | } 71 | 72 | macro_rules! into_float_values { 73 | ($($id:ty => $sub:ident),+) => { 74 | $( 75 | impl IntoIntern for $id { 76 | fn into_intern(self, _: &mut StringCache) -> StatementValues { 77 | StatementValues::$sub(ValueHolder::S(NotNaN::from(self))) 78 | } 79 | } 80 | 81 | impl IntoIntern for ($id, $id) { 82 | fn into_intern(self, _: &mut StringCache) -> StatementValues { 83 | StatementValues::$sub(ValueHolder::D(NotNaN::from(self.0), NotNaN::from(self.1))) 84 | } 85 | } 86 | )* 87 | }; 88 | } 89 | 90 | impl IntoIntern for bool { 91 | fn into_intern(self, _: &mut StringCache) -> StatementValues { 92 | StatementValues::BOOL(ValueHolder::S(self)) 93 | } 94 | } 95 | 96 | into_values!( 97 | i8 => I8, i16 => I16, i32 => I32, i64 => I64, 98 | u8 => U8, u16 => U16, u32 => U32, u64 => U64, 99 | isize => ISIZE, usize => USIZE, 100 | SymbolId => STR 101 | ); 102 | 103 | into_float_values!( 104 | f32 => F32, f64 => F64 105 | ); 106 | 107 | impl<'a> IntoIntern for &'a str { 108 | fn into_intern(self, cache: &mut StringCache) -> StatementValues { 109 | StatementValues::STR(ValueHolder::S(cache.get_or_intern(self))) 110 | } 111 | } 112 | 113 | impl<'a> IntoIntern for (&'a str, &'a str) { 114 | fn into_intern(self, cache: &mut StringCache) -> StatementValues { 115 | StatementValues::STR(ValueHolder::D(cache.get_or_intern(self.0), cache.get_or_intern(self.1))) 116 | } 117 | } 118 | 119 | #[derive(Copy, Clone, Eq, Hash, Ord, PartialOrd, PartialEq, Debug)] 120 | pub enum StatementConditions { 121 | Eq(SymbolId, StatementValues), 122 | Ne(SymbolId, StatementValues), 123 | Lt(SymbolId, StatementValues), 124 | Le(SymbolId, StatementValues), 125 | Gt(SymbolId, StatementValues), 126 | Ge(SymbolId, StatementValues), 127 | GtLt(SymbolId, StatementValues), 128 | GeLt(SymbolId, StatementValues), 129 | GtLe(SymbolId, StatementValues), 130 | GeLe(SymbolId, StatementValues), 131 | Contains(SymbolId, StatementValues), 132 | StartsWith(SymbolId, StatementValues), 133 | EndsWith(SymbolId, StatementValues) 134 | } 135 | 136 | impl StatementConditions { 137 | pub fn is_hash_eq(&self) -> bool { 138 | use self::StatementConditions::*; 139 | match self { 140 | &Eq(_, values) if values.is_hash_eq() => true, 141 | _ => false 142 | } 143 | } 144 | 145 | pub fn field(&self) -> SymbolId { 146 | use self::StatementConditions::*; 147 | match self { 148 | &Eq(field, _) => field, 149 | &Ne(field, _) => field, 150 | &Lt(field, _) => field, 151 | &Le(field, _) => field, 152 | &Gt(field, _) => field, 153 | &Ge(field, _) => field, 154 | &GtLt(field, _) => field, 155 | &GeLt(field, _) => field, 156 | &GtLe(field, _) => field, 157 | &GeLe(field, _) => field, 158 | &Contains(field, _) => field, 159 | &StartsWith(field, _) => field, 160 | &EndsWith(field, _) => field, 161 | } 162 | } 163 | } 164 | 165 | pub struct StatementBuilder { 166 | statement_type: PhantomData, 167 | rule_builder: R, 168 | conditions: Vec 169 | } 170 | 171 | impl StatementBuilder { 172 | 173 | pub fn eq + AsRef, T: IntoIntern>(&mut self, field: S, to: T) { 174 | let field_symbol = self.rule_builder.get_string_cache().get_or_intern(field); 175 | self.conditions.push( 176 | StatementConditions::Eq(field_symbol, to.into_intern(self.rule_builder.get_string_cache())) 177 | ) 178 | } 179 | 180 | pub fn ne + AsRef, T: IntoIntern>(&mut self, field: S, to: T) { 181 | let field_symbol = self.rule_builder.get_string_cache().get_or_intern(field); 182 | self.conditions.push( 183 | StatementConditions::Ne(field_symbol, to.into_intern(self.rule_builder.get_string_cache())) 184 | ) 185 | } 186 | 187 | pub fn lt + AsRef, T: IntoIntern>(&mut self, field: S, to: T) { 188 | let field_symbol = self.rule_builder.get_string_cache().get_or_intern(field); 189 | self.conditions.push( 190 | StatementConditions::Lt(field_symbol, to.into_intern(self.rule_builder.get_string_cache())) 191 | ) 192 | } 193 | 194 | pub fn le + AsRef, T: IntoIntern>(&mut self, field: S, to: T) { 195 | let field_symbol = self.rule_builder.get_string_cache().get_or_intern(field); 196 | self.conditions.push( 197 | StatementConditions::Le(field_symbol, to.into_intern(self.rule_builder.get_string_cache())) 198 | ) 199 | } 200 | 201 | pub fn gt + AsRef, T: IntoIntern>(&mut self, field: S, to: T) { 202 | let field_symbol = self.rule_builder.get_string_cache().get_or_intern(field); 203 | self.conditions.push( 204 | StatementConditions::Gt(field_symbol, to.into_intern(self.rule_builder.get_string_cache())) 205 | ) 206 | } 207 | 208 | pub fn ge + AsRef, T: IntoIntern>(&mut self, field: S, to: T) { 209 | let field_symbol = self.rule_builder.get_string_cache().get_or_intern(field); 210 | self.conditions.push( 211 | StatementConditions::Ge(field_symbol, to.into_intern(self.rule_builder.get_string_cache())) 212 | ) 213 | } 214 | 215 | pub fn gtlt + AsRef, T: IntoIntern>(&mut self, field: S, from: T, to: T) 216 | where (T,T) : IntoIntern { 217 | let field_symbol = self.rule_builder.get_string_cache().get_or_intern(field); 218 | self.conditions.push( 219 | StatementConditions::GtLt(field_symbol, (from, to).into_intern(self.rule_builder.get_string_cache())) 220 | ) 221 | } 222 | 223 | pub fn gelt + AsRef, T: IntoIntern>(&mut self, field: S, from: T, to: T) 224 | where (T,T) : IntoIntern { 225 | let field_symbol = self.rule_builder.get_string_cache().get_or_intern(field); 226 | self.conditions.push( 227 | StatementConditions::GeLt(field_symbol, (from, to).into_intern(self.rule_builder.get_string_cache())) 228 | ) 229 | } 230 | 231 | pub fn gtle + AsRef, T: IntoIntern>(&mut self, field: S, from: T, to: T) 232 | where (T,T) : IntoIntern { 233 | let field_symbol = self.rule_builder.get_string_cache().get_or_intern(field); 234 | self.conditions.push( 235 | StatementConditions::GtLe(field_symbol, (from, to).into_intern(self.rule_builder.get_string_cache())) 236 | ) 237 | } 238 | 239 | pub fn gele + AsRef, T: IntoIntern>(&mut self, field: S, from: T, to: T) 240 | where (T,T) : IntoIntern { 241 | let field_symbol = self.rule_builder.get_string_cache().get_or_intern(field); 242 | self.conditions.push( 243 | StatementConditions::GeLe(field_symbol, (from, to).into_intern(self.rule_builder.get_string_cache())) 244 | ) 245 | } 246 | 247 | pub fn contains + AsRef>(&mut self, field: S, to: &str) { 248 | let field_symbol = self.rule_builder.get_string_cache().get_or_intern(field); 249 | self.conditions.push( 250 | StatementConditions::Contains(field_symbol, to.into_intern(self.rule_builder.get_string_cache())) 251 | ) 252 | } 253 | 254 | pub fn starts_with + AsRef>(&mut self, field: S, to: &str) { 255 | let field_symbol = self.rule_builder.get_string_cache().get_or_intern(field); 256 | self.conditions.push( 257 | StatementConditions::StartsWith(field_symbol, to.into_intern(self.rule_builder.get_string_cache())) 258 | ) 259 | } 260 | 261 | pub fn ends_with + AsRef>(&mut self, field: S, to: &str) { 262 | let field_symbol = self.rule_builder.get_string_cache().get_or_intern(field); 263 | self.conditions.push( 264 | StatementConditions::EndsWith(field_symbol, to.into_intern(self.rule_builder.get_string_cache())) 265 | ) 266 | } 267 | 268 | 269 | fn collapse(mut self) -> Result { 270 | let hash_eq = I::create_hash_eq(&self.conditions, self.rule_builder.get_string_cache()); 271 | 272 | { 273 | let statement_id = self.rule_builder.get_id_generator().next_statement_id(); 274 | self.rule_builder.get_statement_ids().insert(statement_id); 275 | let (cache, id_gen, entry_point) = self.rule_builder.get_for_condition_collapse::(hash_eq); 276 | 277 | entry_point.entry(AlphaTest::HashEq).or_insert_with(|| ConditionDesc::new(id_gen.next_condition_id(), None)) 278 | .dependents.insert(statement_id); 279 | 280 | for c in self.conditions 281 | .into_iter() 282 | .filter(|c| !c.is_hash_eq()) { 283 | 284 | let field_sym = c.field(); 285 | let getter = cache.resolve(field_sym) 286 | .and_then(|s| I ::getter(s)) 287 | .ok_or(format_err!("Type has no getter {:?}", cache.resolve(field_sym)))?; 288 | let test = (getter, c).try_into() 289 | .context(format_err!("Field {:?}", cache.resolve(field_sym)))?; 290 | entry_point.entry(test).or_insert_with(|| ConditionDesc::new(id_gen.next_condition_id(), Some(field_sym))) 291 | .dependents.insert(statement_id); 292 | } 293 | } 294 | Ok(self.rule_builder) 295 | } 296 | } 297 | 298 | 299 | // TODO: Certainly there's a way to do this in a macro - https://users.rust-lang.org/t/cartesian-product-using-macros/10763/24 300 | // I will figure this out eventually :/ 301 | impl TryInto> for (Getters, StatementConditions) { 302 | fn try_into(self) -> Result> { 303 | match self { 304 | //I8 305 | (Getters::I8(accessor), StatementConditions::Ne(_, StatementValues::I8(ValueHolder::S(to)))) => 306 | Ok(AlphaTest::Ord(OrdData::I8(accessor, CLimits::S(to)), OrdTest::Ne)), 307 | (Getters::I8(accessor), StatementConditions::Lt(_, StatementValues::I8(ValueHolder::S(to)))) => 308 | Ok(AlphaTest::Ord(OrdData::I8(accessor, CLimits::S(to)), OrdTest::Lt)), 309 | (Getters::I8(accessor), StatementConditions::Le(_, StatementValues::I8(ValueHolder::S(to)))) => 310 | Ok(AlphaTest::Ord(OrdData::I8(accessor, CLimits::S(to)), OrdTest::Le)), 311 | (Getters::I8(accessor), StatementConditions::Gt(_, StatementValues::I8(ValueHolder::S(to)))) => 312 | Ok(AlphaTest::Ord(OrdData::I8(accessor, CLimits::S(to)), OrdTest::Gt)), 313 | (Getters::I8(accessor), StatementConditions::Ge(_, StatementValues::I8(ValueHolder::S(to)))) => 314 | Ok(AlphaTest::Ord(OrdData::I8(accessor, CLimits::S(to)), OrdTest::Ge)), 315 | (Getters::I8(accessor), StatementConditions::GtLt(_, StatementValues::I8(ValueHolder::D(from, to)))) => 316 | Ok(AlphaTest::Ord(OrdData::I8(accessor, CLimits::D(from, to)), OrdTest::GtLt)), 317 | (Getters::I8(accessor), StatementConditions::GeLt(_, StatementValues::I8(ValueHolder::D(from, to)))) => 318 | Ok(AlphaTest::Ord(OrdData::I8(accessor, CLimits::D(from, to)), OrdTest::GeLt)), 319 | (Getters::I8(accessor), StatementConditions::GtLe(_, StatementValues::I8(ValueHolder::D(from, to)))) => 320 | Ok(AlphaTest::Ord(OrdData::I8(accessor, CLimits::D(from, to)), OrdTest::GtLe)), 321 | (Getters::I8(accessor), StatementConditions::GeLe(_, StatementValues::I8(ValueHolder::D(from, to)))) => 322 | Ok(AlphaTest::Ord(OrdData::I8(accessor, CLimits::D(from, to)), OrdTest::GeLe)), 323 | //I16 324 | (Getters::I16(accessor), StatementConditions::Ne(_, StatementValues::I16(ValueHolder::S(to)))) => 325 | Ok(AlphaTest::Ord(OrdData::I16(accessor, CLimits::S(to)), OrdTest::Ne)), 326 | (Getters::I16(accessor), StatementConditions::Lt(_, StatementValues::I16(ValueHolder::S(to)))) => 327 | Ok(AlphaTest::Ord(OrdData::I16(accessor, CLimits::S(to)), OrdTest::Lt)), 328 | (Getters::I16(accessor), StatementConditions::Le(_, StatementValues::I16(ValueHolder::S(to)))) => 329 | Ok(AlphaTest::Ord(OrdData::I16(accessor, CLimits::S(to)), OrdTest::Le)), 330 | (Getters::I16(accessor), StatementConditions::Gt(_, StatementValues::I16(ValueHolder::S(to)))) => 331 | Ok(AlphaTest::Ord(OrdData::I16(accessor, CLimits::S(to)), OrdTest::Gt)), 332 | (Getters::I16(accessor), StatementConditions::Ge(_, StatementValues::I16(ValueHolder::S(to)))) => 333 | Ok(AlphaTest::Ord(OrdData::I16(accessor, CLimits::S(to)), OrdTest::Ge)), 334 | (Getters::I16(accessor), StatementConditions::GtLt(_, StatementValues::I16(ValueHolder::D(from, to)))) => 335 | Ok(AlphaTest::Ord(OrdData::I16(accessor, CLimits::D(from, to)), OrdTest::GtLt)), 336 | (Getters::I16(accessor), StatementConditions::GeLt(_, StatementValues::I16(ValueHolder::D(from, to)))) => 337 | Ok(AlphaTest::Ord(OrdData::I16(accessor, CLimits::D(from, to)), OrdTest::GeLt)), 338 | (Getters::I16(accessor), StatementConditions::GtLe(_, StatementValues::I16(ValueHolder::D(from, to)))) => 339 | Ok(AlphaTest::Ord(OrdData::I16(accessor, CLimits::D(from, to)), OrdTest::GtLe)), 340 | (Getters::I16(accessor), StatementConditions::GeLe(_, StatementValues::I16(ValueHolder::D(from, to)))) => 341 | Ok(AlphaTest::Ord(OrdData::I16(accessor, CLimits::D(from, to)), OrdTest::GeLe)), 342 | //I32 343 | (Getters::I32(accessor), StatementConditions::Ne(_, StatementValues::I32(ValueHolder::S(to)))) => 344 | Ok(AlphaTest::Ord(OrdData::I32(accessor, CLimits::S(to)), OrdTest::Ne)), 345 | (Getters::I32(accessor), StatementConditions::Lt(_, StatementValues::I32(ValueHolder::S(to)))) => 346 | Ok(AlphaTest::Ord(OrdData::I32(accessor, CLimits::S(to)), OrdTest::Lt)), 347 | (Getters::I32(accessor), StatementConditions::Le(_, StatementValues::I32(ValueHolder::S(to)))) => 348 | Ok(AlphaTest::Ord(OrdData::I32(accessor, CLimits::S(to)), OrdTest::Le)), 349 | (Getters::I32(accessor), StatementConditions::Gt(_, StatementValues::I32(ValueHolder::S(to)))) => 350 | Ok(AlphaTest::Ord(OrdData::I32(accessor, CLimits::S(to)), OrdTest::Gt)), 351 | (Getters::I32(accessor), StatementConditions::Ge(_, StatementValues::I32(ValueHolder::S(to)))) => 352 | Ok(AlphaTest::Ord(OrdData::I32(accessor, CLimits::S(to)), OrdTest::Ge)), 353 | (Getters::I32(accessor), StatementConditions::GtLt(_, StatementValues::I32(ValueHolder::D(from, to)))) => 354 | Ok(AlphaTest::Ord(OrdData::I32(accessor, CLimits::D(from, to)), OrdTest::GtLt)), 355 | (Getters::I32(accessor), StatementConditions::GeLt(_, StatementValues::I32(ValueHolder::D(from, to)))) => 356 | Ok(AlphaTest::Ord(OrdData::I32(accessor, CLimits::D(from, to)), OrdTest::GeLt)), 357 | (Getters::I32(accessor), StatementConditions::GtLe(_, StatementValues::I32(ValueHolder::D(from, to)))) => 358 | Ok(AlphaTest::Ord(OrdData::I32(accessor, CLimits::D(from, to)), OrdTest::GtLe)), 359 | (Getters::I32(accessor), StatementConditions::GeLe(_, StatementValues::I32(ValueHolder::D(from, to)))) => 360 | Ok(AlphaTest::Ord(OrdData::I32(accessor, CLimits::D(from, to)), OrdTest::GeLe)), 361 | //I64 362 | (Getters::I64(accessor), StatementConditions::Ne(_, StatementValues::I64(ValueHolder::S(to)))) => 363 | Ok(AlphaTest::Ord(OrdData::I64(accessor, CLimits::S(to)), OrdTest::Ne)), 364 | (Getters::I64(accessor), StatementConditions::Lt(_, StatementValues::I64(ValueHolder::S(to)))) => 365 | Ok(AlphaTest::Ord(OrdData::I64(accessor, CLimits::S(to)), OrdTest::Lt)), 366 | (Getters::I64(accessor), StatementConditions::Le(_, StatementValues::I64(ValueHolder::S(to)))) => 367 | Ok(AlphaTest::Ord(OrdData::I64(accessor, CLimits::S(to)), OrdTest::Le)), 368 | (Getters::I64(accessor), StatementConditions::Gt(_, StatementValues::I64(ValueHolder::S(to)))) => 369 | Ok(AlphaTest::Ord(OrdData::I64(accessor, CLimits::S(to)), OrdTest::Gt)), 370 | (Getters::I64(accessor), StatementConditions::Ge(_, StatementValues::I64(ValueHolder::S(to)))) => 371 | Ok(AlphaTest::Ord(OrdData::I64(accessor, CLimits::S(to)), OrdTest::Ge)), 372 | (Getters::I64(accessor), StatementConditions::GtLt(_, StatementValues::I64(ValueHolder::D(from, to)))) => 373 | Ok(AlphaTest::Ord(OrdData::I64(accessor, CLimits::D(from, to)), OrdTest::GtLt)), 374 | (Getters::I64(accessor), StatementConditions::GeLt(_, StatementValues::I64(ValueHolder::D(from, to)))) => 375 | Ok(AlphaTest::Ord(OrdData::I64(accessor, CLimits::D(from, to)), OrdTest::GeLt)), 376 | (Getters::I64(accessor), StatementConditions::GtLe(_, StatementValues::I64(ValueHolder::D(from, to)))) => 377 | Ok(AlphaTest::Ord(OrdData::I64(accessor, CLimits::D(from, to)), OrdTest::GtLe)), 378 | (Getters::I64(accessor), StatementConditions::GeLe(_, StatementValues::I64(ValueHolder::D(from, to)))) => 379 | Ok(AlphaTest::Ord(OrdData::I64(accessor, CLimits::D(from, to)), OrdTest::GeLe)), 380 | //U8 381 | (Getters::U8(accessor), StatementConditions::Ne(_, StatementValues::U8(ValueHolder::S(to)))) => 382 | Ok(AlphaTest::Ord(OrdData::U8(accessor, CLimits::S(to)), OrdTest::Ne)), 383 | (Getters::U8(accessor), StatementConditions::Lt(_, StatementValues::U8(ValueHolder::S(to)))) => 384 | Ok(AlphaTest::Ord(OrdData::U8(accessor, CLimits::S(to)), OrdTest::Lt)), 385 | (Getters::U8(accessor), StatementConditions::Le(_, StatementValues::U8(ValueHolder::S(to)))) => 386 | Ok(AlphaTest::Ord(OrdData::U8(accessor, CLimits::S(to)), OrdTest::Le)), 387 | (Getters::U8(accessor), StatementConditions::Gt(_, StatementValues::U8(ValueHolder::S(to)))) => 388 | Ok(AlphaTest::Ord(OrdData::U8(accessor, CLimits::S(to)), OrdTest::Gt)), 389 | (Getters::U8(accessor), StatementConditions::Ge(_, StatementValues::U8(ValueHolder::S(to)))) => 390 | Ok(AlphaTest::Ord(OrdData::U8(accessor, CLimits::S(to)), OrdTest::Ge)), 391 | (Getters::U8(accessor), StatementConditions::GtLt(_, StatementValues::U8(ValueHolder::D(from, to)))) => 392 | Ok(AlphaTest::Ord(OrdData::U8(accessor, CLimits::D(from, to)), OrdTest::GtLt)), 393 | (Getters::U8(accessor), StatementConditions::GeLt(_, StatementValues::U8(ValueHolder::D(from, to)))) => 394 | Ok(AlphaTest::Ord(OrdData::U8(accessor, CLimits::D(from, to)), OrdTest::GeLt)), 395 | (Getters::U8(accessor), StatementConditions::GtLe(_, StatementValues::U8(ValueHolder::D(from, to)))) => 396 | Ok(AlphaTest::Ord(OrdData::U8(accessor, CLimits::D(from, to)), OrdTest::GtLe)), 397 | (Getters::U8(accessor), StatementConditions::GeLe(_, StatementValues::U8(ValueHolder::D(from, to)))) => 398 | Ok(AlphaTest::Ord(OrdData::U8(accessor, CLimits::D(from, to)), OrdTest::GeLe)), 399 | //U16 400 | (Getters::U16(accessor), StatementConditions::Ne(_, StatementValues::U16(ValueHolder::S(to)))) => 401 | Ok(AlphaTest::Ord(OrdData::U16(accessor, CLimits::S(to)), OrdTest::Ne)), 402 | (Getters::U16(accessor), StatementConditions::Lt(_, StatementValues::U16(ValueHolder::S(to)))) => 403 | Ok(AlphaTest::Ord(OrdData::U16(accessor, CLimits::S(to)), OrdTest::Lt)), 404 | (Getters::U16(accessor), StatementConditions::Le(_, StatementValues::U16(ValueHolder::S(to)))) => 405 | Ok(AlphaTest::Ord(OrdData::U16(accessor, CLimits::S(to)), OrdTest::Le)), 406 | (Getters::U16(accessor), StatementConditions::Gt(_, StatementValues::U16(ValueHolder::S(to)))) => 407 | Ok(AlphaTest::Ord(OrdData::U16(accessor, CLimits::S(to)), OrdTest::Gt)), 408 | (Getters::U16(accessor), StatementConditions::Ge(_, StatementValues::U16(ValueHolder::S(to)))) => 409 | Ok(AlphaTest::Ord(OrdData::U16(accessor, CLimits::S(to)), OrdTest::Ge)), 410 | (Getters::U16(accessor), StatementConditions::GtLt(_, StatementValues::U16(ValueHolder::D(from, to)))) => 411 | Ok(AlphaTest::Ord(OrdData::U16(accessor, CLimits::D(from, to)), OrdTest::GtLt)), 412 | (Getters::U16(accessor), StatementConditions::GeLt(_, StatementValues::U16(ValueHolder::D(from, to)))) => 413 | Ok(AlphaTest::Ord(OrdData::U16(accessor, CLimits::D(from, to)), OrdTest::GeLt)), 414 | (Getters::U16(accessor), StatementConditions::GtLe(_, StatementValues::U16(ValueHolder::D(from, to)))) => 415 | Ok(AlphaTest::Ord(OrdData::U16(accessor, CLimits::D(from, to)), OrdTest::GtLe)), 416 | (Getters::U16(accessor), StatementConditions::GeLe(_, StatementValues::U16(ValueHolder::D(from, to)))) => 417 | Ok(AlphaTest::Ord(OrdData::U16(accessor, CLimits::D(from, to)), OrdTest::GeLe)), 418 | //U32 419 | (Getters::U32(accessor), StatementConditions::Ne(_, StatementValues::U32(ValueHolder::S(to)))) => 420 | Ok(AlphaTest::Ord(OrdData::U32(accessor, CLimits::S(to)), OrdTest::Ne)), 421 | (Getters::U32(accessor), StatementConditions::Lt(_, StatementValues::U32(ValueHolder::S(to)))) => 422 | Ok(AlphaTest::Ord(OrdData::U32(accessor, CLimits::S(to)), OrdTest::Lt)), 423 | (Getters::U32(accessor), StatementConditions::Le(_, StatementValues::U32(ValueHolder::S(to)))) => 424 | Ok(AlphaTest::Ord(OrdData::U32(accessor, CLimits::S(to)), OrdTest::Le)), 425 | (Getters::U32(accessor), StatementConditions::Gt(_, StatementValues::U32(ValueHolder::S(to)))) => 426 | Ok(AlphaTest::Ord(OrdData::U32(accessor, CLimits::S(to)), OrdTest::Gt)), 427 | (Getters::U32(accessor), StatementConditions::Ge(_, StatementValues::U32(ValueHolder::S(to)))) => 428 | Ok(AlphaTest::Ord(OrdData::U32(accessor, CLimits::S(to)), OrdTest::Ge)), 429 | (Getters::U32(accessor), StatementConditions::GtLt(_, StatementValues::U32(ValueHolder::D(from, to)))) => 430 | Ok(AlphaTest::Ord(OrdData::U32(accessor, CLimits::D(from, to)), OrdTest::GtLt)), 431 | (Getters::U32(accessor), StatementConditions::GeLt(_, StatementValues::U32(ValueHolder::D(from, to)))) => 432 | Ok(AlphaTest::Ord(OrdData::U32(accessor, CLimits::D(from, to)), OrdTest::GeLt)), 433 | (Getters::U32(accessor), StatementConditions::GtLe(_, StatementValues::U32(ValueHolder::D(from, to)))) => 434 | Ok(AlphaTest::Ord(OrdData::U32(accessor, CLimits::D(from, to)), OrdTest::GtLe)), 435 | (Getters::U32(accessor), StatementConditions::GeLe(_, StatementValues::U32(ValueHolder::D(from, to)))) => 436 | Ok(AlphaTest::Ord(OrdData::U32(accessor, CLimits::D(from, to)), OrdTest::GeLe)), 437 | //U64 438 | (Getters::U64(accessor), StatementConditions::Ne(_, StatementValues::U64(ValueHolder::S(to)))) => 439 | Ok(AlphaTest::Ord(OrdData::U64(accessor, CLimits::S(to)), OrdTest::Ne)), 440 | (Getters::U64(accessor), StatementConditions::Lt(_, StatementValues::U64(ValueHolder::S(to)))) => 441 | Ok(AlphaTest::Ord(OrdData::U64(accessor, CLimits::S(to)), OrdTest::Lt)), 442 | (Getters::U64(accessor), StatementConditions::Le(_, StatementValues::U64(ValueHolder::S(to)))) => 443 | Ok(AlphaTest::Ord(OrdData::U64(accessor, CLimits::S(to)), OrdTest::Le)), 444 | (Getters::U64(accessor), StatementConditions::Gt(_, StatementValues::U64(ValueHolder::S(to)))) => 445 | Ok(AlphaTest::Ord(OrdData::U64(accessor, CLimits::S(to)), OrdTest::Gt)), 446 | (Getters::U64(accessor), StatementConditions::Ge(_, StatementValues::U64(ValueHolder::S(to)))) => 447 | Ok(AlphaTest::Ord(OrdData::U64(accessor, CLimits::S(to)), OrdTest::Ge)), 448 | (Getters::U64(accessor), StatementConditions::GtLt(_, StatementValues::U64(ValueHolder::D(from, to)))) => 449 | Ok(AlphaTest::Ord(OrdData::U64(accessor, CLimits::D(from, to)), OrdTest::GtLt)), 450 | (Getters::U64(accessor), StatementConditions::GeLt(_, StatementValues::U64(ValueHolder::D(from, to)))) => 451 | Ok(AlphaTest::Ord(OrdData::U64(accessor, CLimits::D(from, to)), OrdTest::GeLt)), 452 | (Getters::U64(accessor), StatementConditions::GtLe(_, StatementValues::U64(ValueHolder::D(from, to)))) => 453 | Ok(AlphaTest::Ord(OrdData::U64(accessor, CLimits::D(from, to)), OrdTest::GtLe)), 454 | (Getters::U64(accessor), StatementConditions::GeLe(_, StatementValues::U64(ValueHolder::D(from, to)))) => 455 | Ok(AlphaTest::Ord(OrdData::U64(accessor, CLimits::D(from, to)), OrdTest::GeLe)), 456 | //ISIZE 457 | (Getters::ISIZE(accessor), StatementConditions::Ne(_, StatementValues::ISIZE(ValueHolder::S(to)))) => 458 | Ok(AlphaTest::Ord(OrdData::ISIZE(accessor, CLimits::S(to)), OrdTest::Ne)), 459 | (Getters::ISIZE(accessor), StatementConditions::Lt(_, StatementValues::ISIZE(ValueHolder::S(to)))) => 460 | Ok(AlphaTest::Ord(OrdData::ISIZE(accessor, CLimits::S(to)), OrdTest::Lt)), 461 | (Getters::ISIZE(accessor), StatementConditions::Le(_, StatementValues::ISIZE(ValueHolder::S(to)))) => 462 | Ok(AlphaTest::Ord(OrdData::ISIZE(accessor, CLimits::S(to)), OrdTest::Le)), 463 | (Getters::ISIZE(accessor), StatementConditions::Gt(_, StatementValues::ISIZE(ValueHolder::S(to)))) => 464 | Ok(AlphaTest::Ord(OrdData::ISIZE(accessor, CLimits::S(to)), OrdTest::Gt)), 465 | (Getters::ISIZE(accessor), StatementConditions::Ge(_, StatementValues::ISIZE(ValueHolder::S(to)))) => 466 | Ok(AlphaTest::Ord(OrdData::ISIZE(accessor, CLimits::S(to)), OrdTest::Ge)), 467 | (Getters::ISIZE(accessor), StatementConditions::GtLt(_, StatementValues::ISIZE(ValueHolder::D(from, to)))) => 468 | Ok(AlphaTest::Ord(OrdData::ISIZE(accessor, CLimits::D(from, to)), OrdTest::GtLt)), 469 | (Getters::ISIZE(accessor), StatementConditions::GeLt(_, StatementValues::ISIZE(ValueHolder::D(from, to)))) => 470 | Ok(AlphaTest::Ord(OrdData::ISIZE(accessor, CLimits::D(from, to)), OrdTest::GeLt)), 471 | (Getters::ISIZE(accessor), StatementConditions::GtLe(_, StatementValues::ISIZE(ValueHolder::D(from, to)))) => 472 | Ok(AlphaTest::Ord(OrdData::ISIZE(accessor, CLimits::D(from, to)), OrdTest::GtLe)), 473 | (Getters::ISIZE(accessor), StatementConditions::GeLe(_, StatementValues::ISIZE(ValueHolder::D(from, to)))) => 474 | Ok(AlphaTest::Ord(OrdData::ISIZE(accessor, CLimits::D(from, to)), OrdTest::GeLe)), 475 | //USIZE 476 | (Getters::USIZE(accessor), StatementConditions::Ne(_, StatementValues::USIZE(ValueHolder::S(to)))) => 477 | Ok(AlphaTest::Ord(OrdData::USIZE(accessor, CLimits::S(to)), OrdTest::Ne)), 478 | (Getters::USIZE(accessor), StatementConditions::Lt(_, StatementValues::USIZE(ValueHolder::S(to)))) => 479 | Ok(AlphaTest::Ord(OrdData::USIZE(accessor, CLimits::S(to)), OrdTest::Lt)), 480 | (Getters::USIZE(accessor), StatementConditions::Le(_, StatementValues::USIZE(ValueHolder::S(to)))) => 481 | Ok(AlphaTest::Ord(OrdData::USIZE(accessor, CLimits::S(to)), OrdTest::Le)), 482 | (Getters::USIZE(accessor), StatementConditions::Gt(_, StatementValues::USIZE(ValueHolder::S(to)))) => 483 | Ok(AlphaTest::Ord(OrdData::USIZE(accessor, CLimits::S(to)), OrdTest::Gt)), 484 | (Getters::USIZE(accessor), StatementConditions::Ge(_, StatementValues::USIZE(ValueHolder::S(to)))) => 485 | Ok(AlphaTest::Ord(OrdData::USIZE(accessor, CLimits::S(to)), OrdTest::Ge)), 486 | (Getters::USIZE(accessor), StatementConditions::GtLt(_, StatementValues::USIZE(ValueHolder::D(from, to)))) => 487 | Ok(AlphaTest::Ord(OrdData::USIZE(accessor, CLimits::D(from, to)), OrdTest::GtLt)), 488 | (Getters::USIZE(accessor), StatementConditions::GeLt(_, StatementValues::USIZE(ValueHolder::D(from, to)))) => 489 | Ok(AlphaTest::Ord(OrdData::USIZE(accessor, CLimits::D(from, to)), OrdTest::GeLt)), 490 | (Getters::USIZE(accessor), StatementConditions::GtLe(_, StatementValues::USIZE(ValueHolder::D(from, to)))) => 491 | Ok(AlphaTest::Ord(OrdData::USIZE(accessor, CLimits::D(from, to)), OrdTest::GtLe)), 492 | (Getters::USIZE(accessor), StatementConditions::GeLe(_, StatementValues::USIZE(ValueHolder::D(from, to)))) => 493 | Ok(AlphaTest::Ord(OrdData::USIZE(accessor, CLimits::D(from, to)), OrdTest::GeLe)), 494 | //F32 495 | (Getters::F32(accessor), StatementConditions::Eq(_, StatementValues::F32(ValueHolder::S(to)))) => 496 | Ok(AlphaTest::Fl(FlData::F32(accessor, FLimits::S(to)), FlTest::ApproxEq)), 497 | (Getters::F32(accessor), StatementConditions::Ne(_, StatementValues::F32(ValueHolder::S(to)))) => 498 | Ok(AlphaTest::Fl(FlData::F32(accessor, FLimits::S(to)), FlTest::ApproxNe)), 499 | (Getters::F32(accessor), StatementConditions::Lt(_, StatementValues::F32(ValueHolder::S(to)))) => 500 | Ok(AlphaTest::Fl(FlData::F32(accessor, FLimits::S(to)), FlTest::Lt)), 501 | (Getters::F32(accessor), StatementConditions::Le(_, StatementValues::F32(ValueHolder::S(to)))) => 502 | Ok(AlphaTest::Fl(FlData::F32(accessor, FLimits::S(to)), FlTest::Le)), 503 | (Getters::F32(accessor), StatementConditions::Gt(_, StatementValues::F32(ValueHolder::S(to)))) => 504 | Ok(AlphaTest::Fl(FlData::F32(accessor, FLimits::S(to)), FlTest::Gt)), 505 | (Getters::F32(accessor), StatementConditions::Ge(_, StatementValues::F32(ValueHolder::S(to)))) => 506 | Ok(AlphaTest::Fl(FlData::F32(accessor, FLimits::S(to)), FlTest::Ge)), 507 | (Getters::F32(accessor), StatementConditions::GtLt(_, StatementValues::F32(ValueHolder::D(from, to)))) => 508 | Ok(AlphaTest::Fl(FlData::F32(accessor, FLimits::D(from, to)), FlTest::GtLt)), 509 | (Getters::F32(accessor), StatementConditions::GeLt(_, StatementValues::F32(ValueHolder::D(from, to)))) => 510 | Ok(AlphaTest::Fl(FlData::F32(accessor, FLimits::D(from, to)), FlTest::GeLt)), 511 | (Getters::F32(accessor), StatementConditions::GtLe(_, StatementValues::F32(ValueHolder::D(from, to)))) => 512 | Ok(AlphaTest::Fl(FlData::F32(accessor, FLimits::D(from, to)), FlTest::GtLe)), 513 | (Getters::F32(accessor), StatementConditions::GeLe(_, StatementValues::F32(ValueHolder::D(from, to)))) => 514 | Ok(AlphaTest::Fl(FlData::F32(accessor, FLimits::D(from, to)), FlTest::GeLe)), 515 | //F64 516 | (Getters::F64(accessor), StatementConditions::Eq(_, StatementValues::F64(ValueHolder::S(to)))) => 517 | Ok(AlphaTest::Fl(FlData::F64(accessor, FLimits::S(to)), FlTest::ApproxEq)), 518 | (Getters::F64(accessor), StatementConditions::Ne(_, StatementValues::F64(ValueHolder::S(to)))) => 519 | Ok(AlphaTest::Fl(FlData::F64(accessor, FLimits::S(to)), FlTest::ApproxNe)), 520 | (Getters::F64(accessor), StatementConditions::Lt(_, StatementValues::F64(ValueHolder::S(to)))) => 521 | Ok(AlphaTest::Fl(FlData::F64(accessor, FLimits::S(to)), FlTest::Lt)), 522 | (Getters::F64(accessor), StatementConditions::Le(_, StatementValues::F64(ValueHolder::S(to)))) => 523 | Ok(AlphaTest::Fl(FlData::F64(accessor, FLimits::S(to)), FlTest::Le)), 524 | (Getters::F64(accessor), StatementConditions::Gt(_, StatementValues::F64(ValueHolder::S(to)))) => 525 | Ok(AlphaTest::Fl(FlData::F64(accessor, FLimits::S(to)), FlTest::Gt)), 526 | (Getters::F64(accessor), StatementConditions::Ge(_, StatementValues::F64(ValueHolder::S(to)))) => 527 | Ok(AlphaTest::Fl(FlData::F64(accessor, FLimits::S(to)), FlTest::Ge)), 528 | (Getters::F64(accessor), StatementConditions::GtLt(_, StatementValues::F64(ValueHolder::D(from, to)))) => 529 | Ok(AlphaTest::Fl(FlData::F64(accessor, FLimits::D(from, to)), FlTest::GtLt)), 530 | (Getters::F64(accessor), StatementConditions::GeLt(_, StatementValues::F64(ValueHolder::D(from, to)))) => 531 | Ok(AlphaTest::Fl(FlData::F64(accessor, FLimits::D(from, to)), FlTest::GeLt)), 532 | (Getters::F64(accessor), StatementConditions::GtLe(_, StatementValues::F64(ValueHolder::D(from, to)))) => 533 | Ok(AlphaTest::Fl(FlData::F64(accessor, FLimits::D(from, to)), FlTest::GtLe)), 534 | (Getters::F64(accessor), StatementConditions::GeLe(_, StatementValues::F64(ValueHolder::D(from, to)))) => 535 | Ok(AlphaTest::Fl(FlData::F64(accessor, FLimits::D(from, to)), FlTest::GeLe)), 536 | //STR::REF 537 | (Getters::STR(accessor), StatementConditions::Ne(_, StatementValues::STR(ValueHolder::S(to)))) => 538 | Ok(AlphaTest::Str(StrData::REF(accessor, CLimits::S(to)), StrTest::Ne)), 539 | (Getters::STR(accessor), StatementConditions::Lt(_, StatementValues::STR(ValueHolder::S(to)))) => 540 | Ok(AlphaTest::Str(StrData::REF(accessor, CLimits::S(to)), StrTest::Lt)), 541 | (Getters::STR(accessor), StatementConditions::Le(_, StatementValues::STR(ValueHolder::S(to)))) => 542 | Ok(AlphaTest::Str(StrData::REF(accessor, CLimits::S(to)), StrTest::Le)), 543 | (Getters::STR(accessor), StatementConditions::Gt(_, StatementValues::STR(ValueHolder::S(to)))) => 544 | Ok(AlphaTest::Str(StrData::REF(accessor, CLimits::S(to)), StrTest::Gt)), 545 | (Getters::STR(accessor), StatementConditions::Ge(_, StatementValues::STR(ValueHolder::S(to)))) => 546 | Ok(AlphaTest::Str(StrData::REF(accessor, CLimits::S(to)), StrTest::Ge)), 547 | (Getters::STR(accessor), StatementConditions::GtLt(_, StatementValues::STR(ValueHolder::D(from, to)))) => 548 | Ok(AlphaTest::Str(StrData::REF(accessor, CLimits::D(from, to)), StrTest::GtLt)), 549 | (Getters::STR(accessor), StatementConditions::GeLt(_, StatementValues::STR(ValueHolder::D(from, to)))) => 550 | Ok(AlphaTest::Str(StrData::REF(accessor, CLimits::D(from, to)), StrTest::GeLt)), 551 | (Getters::STR(accessor), StatementConditions::GtLe(_, StatementValues::STR(ValueHolder::D(from, to)))) => 552 | Ok(AlphaTest::Str(StrData::REF(accessor, CLimits::D(from, to)), StrTest::GtLe)), 553 | (Getters::STR(accessor), StatementConditions::GeLe(_, StatementValues::STR(ValueHolder::D(from, to)))) => 554 | Ok(AlphaTest::Str(StrData::REF(accessor, CLimits::D(from, to)), StrTest::GeLe)), 555 | (Getters::STR(accessor), StatementConditions::Contains(_, StatementValues::STR(ValueHolder::S(to)))) => 556 | Ok(AlphaTest::Str(StrData::REF(accessor, CLimits::S(to)), StrTest::Contains)), 557 | (Getters::STR(accessor), StatementConditions::StartsWith(_, StatementValues::STR(ValueHolder::S(to)))) => 558 | Ok(AlphaTest::Str(StrData::REF(accessor, CLimits::S(to)), StrTest::StartsWith)), 559 | (Getters::STR(accessor), StatementConditions::EndsWith(_, StatementValues::STR(ValueHolder::S(to)))) => 560 | Ok(AlphaTest::Str(StrData::REF(accessor, CLimits::S(to)), StrTest::EndsWith)), 561 | _ => Err(format_err!("Something went wrong during transformation! - Data - {:?}", self)) 562 | } 563 | } 564 | } 565 | 566 | #[derive(Debug, Clone)] 567 | pub struct ConditionDesc { 568 | pub(crate) id: ConditionId, 569 | pub(crate) field: Option, 570 | pub(crate) dependents: HashSet 571 | } 572 | 573 | impl ConditionDesc { 574 | pub fn new(id: ConditionId, field: Option) -> ConditionDesc { 575 | ConditionDesc{id, field, dependents: Default::default()} 576 | } 577 | } 578 | --------------------------------------------------------------------------------