├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── btmap.rs ├── goal.rs ├── lib.rs └── state.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.o 3 | *.so 4 | *.rlib 5 | *.dll 6 | 7 | # Executables 8 | *.exe 9 | 10 | # Generated by Cargo 11 | /target/ 12 | 13 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 14 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 15 | Cargo.lock 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rslogic" 3 | version = "0.1.0" 4 | authors = ["Gordon Tisher "] 5 | description = "rslogic is a logic programming framework for Rust inspired by µKanren." 6 | documentation = "https://kulibali.github.io/rslogic" 7 | homepage = "https://github.com/kulibali/rslogic" 8 | repository = "https://github.com/kulibali/rslogic" 9 | readme = "README.md" 10 | license = "MIT" 11 | 12 | [dependencies] 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Gordon Tisher 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rslogic ![Build Status](https://travis-ci.org/kulibali/rslogic.svg?branch=master) 2 | 3 | **[rslogic](https://github.com/kulibali/rslogic)** is a logic programming framework 4 | for Rust inspired by [µKanren](https://github.com/jasonhemann/microKanren). 5 | 6 | [API Documentation](https://kulibali.github.io/rslogic) 7 | 8 | A logical statement is built from **variables**, **states**, and **goals**. 9 | Create an initial state, then obtain some variables (and resulting state) from it. 10 | Construct a goal consisting of variable bindings, logical operations (AND, OR), or 11 | predicates. Then evaluate the goal using the state resulting from making the variables. 12 | Evaluating a goal returns all possible solutions to the statement, in the form of 13 | a number of states containing variable bindings. 14 | 15 | ``` 16 | use rslogic::state; 17 | use rslogic::state::{Unif, State, PossibleStates}; 18 | use rslogic::goal; 19 | use rslogic::goal::{Goal, fail, unify_val, unify_vars, conj, disj, pred}; 20 | 21 | let s = state::State::::empty(); 22 | let (v1, s) = s.make_var(); 23 | let (v2, s) = s.make_var(); 24 | 25 | let n = 123; 26 | let g = goal::conj(goal::unify_vars(&v1, &v2), goal::unify_val(&v2, n)); 27 | 28 | let results = g.eval(&s); 29 | assert_eq!(results.len(), 1); 30 | let bound_value = results[0].get(&v1).unwrap(); 31 | assert_eq!(bound_value, &n); 32 | ``` 33 | 34 | This example creates two variables, `v1` and `v2`, and then assembles a logical expression 35 | equivalent to `(v1 = v2) && (v2 = 123)`. When evaluated, the resulting state binds `123` to 36 | both `v1` and `v2`. 37 | -------------------------------------------------------------------------------- /src/btmap.rs: -------------------------------------------------------------------------------- 1 | //! # Binary Tree Map 2 | //! 3 | //! An immutable map implemented with a binary tree. 4 | 5 | use std::borrow::Borrow; 6 | use std::cmp::Ord; 7 | use std::cmp::Ordering; 8 | use std::rc::Rc; 9 | 10 | struct Node where K: Ord { 11 | key: K, 12 | val: V, 13 | left: Option>>, 14 | right: Option>>, 15 | } 16 | 17 | impl Node where K: Ord { 18 | pub fn get(&self, key: &Q) -> Option<&V> 19 | where K: Ord + Borrow, Q: Ord 20 | { 21 | match key.cmp(self.key.borrow()) { 22 | Ordering::Equal => return Some(&self.val), 23 | Ordering::Less => if let Some(ref child) = self.left { return child.get(key) }, 24 | Ordering::Greater => if let Some(ref child) = self.right { return child.get(key) }, 25 | } 26 | None 27 | } 28 | } 29 | 30 | /// An immutable map implemented with a binary tree. 31 | pub struct BtMap where K : Ord { 32 | size: usize, 33 | root: Option>> 34 | } 35 | 36 | impl BtMap where K : Ord { 37 | /// Creates an empty map. 38 | pub fn empty() -> BtMap { 39 | BtMap { size: 0, root: None } 40 | } 41 | 42 | /// Returns the number of items in the map. 43 | pub fn _len(&self) -> usize { 44 | self.size 45 | } 46 | 47 | /// Returns a reference to the item in the map corresponding to the key, 48 | /// or `None` if there is no item corresponding the the key. 49 | pub fn get(&self, key: &Q) -> Option<&V> 50 | where K: Ord + Borrow, Q: Ord 51 | { 52 | match self.root { 53 | Some(ref node) => node.get(key), 54 | None => None 55 | } 56 | } 57 | 58 | /// Returns `true` if the map contains an item corresponding to the key. 59 | pub fn contains_key(&self, key: &Q) -> bool 60 | where K: Ord + Borrow, Q: Ord 61 | { 62 | self.get(key).is_some() 63 | } 64 | 65 | /// Returns a new map containing all the items in the original map, 66 | /// as well as the new item. Returns an error if there is already 67 | /// an item corresponding to the key. 68 | pub fn insert(&self, key: K, item: V) -> Result, ()> { 69 | match self.root { 70 | Some(ref node) => { 71 | match key.cmp(&node.key) { 72 | Ordering::Equal => Err(()), 73 | Ordering::Less => Ok(BtMap { 74 | size: self.size + 1, 75 | root: Some(Rc::new(Node { 76 | key: key, 77 | val: item, 78 | left: None, 79 | right: Some(node.clone()), 80 | })) 81 | }), 82 | Ordering::Greater => Ok(BtMap { 83 | size: self.size + 1, 84 | root: Some(Rc::new(Node { 85 | key: key, 86 | val: item, 87 | left: Some(node.clone()), 88 | right: None, 89 | })) 90 | }) 91 | } 92 | }, 93 | None => { 94 | Ok(BtMap { 95 | size: 1, 96 | root: Some(Rc::new(Node { 97 | key: key, 98 | val: item, 99 | left: None, 100 | right: None, 101 | })) 102 | }) 103 | } 104 | } 105 | } 106 | } 107 | 108 | use std::clone::Clone; 109 | 110 | impl Clone for BtMap where K: Ord { 111 | fn clone(&self) -> BtMap { 112 | BtMap { 113 | size: self.size, 114 | root: match self.root { 115 | Some(ref root) => Some(root.clone()), 116 | None => None 117 | } 118 | } 119 | } 120 | 121 | fn clone_from(&mut self, source: &BtMap) { 122 | self.size = source.size; 123 | self.root = match source.root { 124 | Some(ref root) => Some(root.clone()), 125 | None => None, 126 | } 127 | } 128 | } 129 | 130 | use std::ops::Index; 131 | 132 | impl<'a, K, V, Q: ?Sized> Index<&'a Q> for BtMap 133 | where K: Ord + Borrow, Q: Ord 134 | { 135 | type Output = V; 136 | fn index(&self, index: &Q) -> &V { 137 | self.get(index).expect("no entry found for key") 138 | } 139 | } 140 | 141 | #[cfg(test)] 142 | mod tests { 143 | use super::BtMap; 144 | 145 | #[test] 146 | fn test_empty() { 147 | let m : BtMap = BtMap::empty(); 148 | assert!(m._len() == 0); 149 | assert!(m.get(&34).is_none()); 150 | assert!(!m.contains_key(&34)); 151 | } 152 | 153 | #[test] 154 | fn test_nonempty() { 155 | let m : BtMap = BtMap::empty(); 156 | let m = m.insert(1234, "OneTwoThreeFour".to_string()).unwrap(); 157 | let m = m.insert(5543, "FiveFiveFourThree".to_string()).unwrap(); 158 | let m = m.insert(8876, "EightEightSevenSix".to_string()).unwrap(); 159 | let m = m.insert(22, "TwentyTwo".to_string()).unwrap(); 160 | 161 | assert!(m._len() == 4); 162 | assert!(m.get(&22).unwrap() == "TwentyTwo"); 163 | assert!(m.get(&5543).unwrap() == "FiveFiveFourThree"); 164 | assert!(m.get(&3332).is_none()); 165 | assert!(m.contains_key(&22)); 166 | assert!(!m.contains_key(&111)); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/goal.rs: -------------------------------------------------------------------------------- 1 | //! # Logical Goals 2 | //! 3 | //! Goals are used to specify logical statements. 4 | 5 | use state::{Unif, Var, State, PossibleStates}; 6 | use std::marker::PhantomData; 7 | 8 | /// Evaluate a `Goal` to produce zero or more `State`s, or 9 | /// collections of variable bindings. 10 | pub trait Goal where T: PartialEq + Unif { 11 | fn eval(&self, state: &State) -> PossibleStates; 12 | } 13 | 14 | 15 | /// Evaluating a `Fail` goal always results in zero states. 16 | pub struct Fail where T: PartialEq + Unif { 17 | _m: PhantomData, 18 | } 19 | 20 | impl Goal for Fail where T: PartialEq + Unif { 21 | fn eval(&self, _: &State) -> PossibleStates { 22 | Vec::with_capacity(0) 23 | } 24 | } 25 | 26 | /// Creates a `Fail` goal. 27 | pub fn fail() -> Fail where T: PartialEq + Unif { 28 | Fail { _m: PhantomData } 29 | } 30 | 31 | 32 | /// Evaluating a `UnifyVal` goal attempts to unify a variable and a value. 33 | pub struct UnifyVal where T: PartialEq + Unif { 34 | var: Var, 35 | val: T, 36 | } 37 | 38 | impl Goal for UnifyVal where T: Clone + Eq + Unif { 39 | fn eval(&self, state: &State) -> PossibleStates { 40 | state.unify_val(&self.var, self.val.clone()) 41 | } 42 | } 43 | 44 | /// Creates a `UnifyVal` goal that attempts to unify the variable and the value. 45 | pub fn unify_val(var: &Var, val: T) -> UnifyVal where T: PartialEq + Unif { 46 | UnifyVal { var: *var, val: val } 47 | } 48 | 49 | 50 | /// Evaluating a `UnifyVar` goal attempts to unify the variables. 51 | pub struct UnifyVar where T: PartialEq + Unif { 52 | v1: Var, 53 | v2: Var, 54 | _m: PhantomData, 55 | } 56 | 57 | impl Goal for UnifyVar where T: PartialEq + Unif { 58 | fn eval(&self, state: &State) -> PossibleStates { 59 | state.unify_var(&self.v1, &self.v2) 60 | } 61 | } 62 | 63 | /// Creates a `UnifyVar` goal that attempts to unify the variables. 64 | pub fn unify_vars(v1: &Var, v2: &Var) -> UnifyVar where T: PartialEq + Unif { 65 | UnifyVar { v1: *v1, v2: *v2, _m: PhantomData } 66 | } 67 | 68 | 69 | /// A `Conjunction` goal evaluates its sub-goal `a` using a given state, 70 | /// then evaluates sub-goal `b` using the results. 71 | pub struct Conjunction where T: PartialEq + Unif, A: Goal, B: Goal { 72 | a: A, 73 | b: B, 74 | _m: PhantomData, 75 | } 76 | 77 | impl Goal for Conjunction where T: PartialEq + Unif, A: Goal, B: Goal { 78 | fn eval(&self, state: &State) -> PossibleStates { 79 | let ra = self.a.eval(state); 80 | let mut result : Vec> = Vec::with_capacity(0); 81 | for s in ra { 82 | let mut rb = self.b.eval(&s); 83 | result.append(&mut rb); 84 | } 85 | result 86 | } 87 | } 88 | 89 | /// Creates a `Conjunction` goal which returns the conjunction (logical AND) of evaluating the two sub-goals. 90 | pub fn conj(a: A, b: B) -> Conjunction where T: PartialEq + Unif, A: Goal, B: Goal { 91 | Conjunction { a: a, b: b, _m: PhantomData } 92 | } 93 | 94 | 95 | /// Evaluating a `Disjunction` goal returns all the possible states of evaluating `a` and `b`. 96 | pub struct Disjunction where T: PartialEq + Unif, A: Goal, B: Goal { 97 | a: A, 98 | b: B, 99 | _m: PhantomData, 100 | } 101 | 102 | impl Goal for Disjunction where T: PartialEq + Unif, A: Goal, B: Goal { 103 | fn eval(&self, state: &State) -> PossibleStates { 104 | let mut da = self.a.eval(state).into_iter(); 105 | let mut db = self.b.eval(state).into_iter(); 106 | let mut result: Vec> = Vec::with_capacity(0); 107 | loop { 108 | let sa = da.next(); 109 | let sb = db.next(); 110 | 111 | let mut found = false; 112 | if let Some(state) = sa { result.push(state); found = true; } 113 | if let Some(state) = sb { result.push(state); found = true; } 114 | 115 | if !found { break; } 116 | } 117 | result 118 | } 119 | } 120 | 121 | /// Creates a `Disjunction` goal which returns the disjunction (logical OR) of evaluating the two sub-goals. 122 | pub fn disj(a: A, b: B) -> Disjunction where T: PartialEq + Unif, A: Goal, B: Goal { 123 | Disjunction { a: a, b: b, _m: PhantomData } 124 | } 125 | 126 | 127 | /// Evaluating a `Predicate` goal returns the given state only if the function returns `true`. 128 | pub struct Predicate<'a, T, F> where T: PartialEq + Unif, F: Fn(&State) -> bool + 'a { 129 | f: &'a F, 130 | _m: PhantomData, 131 | } 132 | 133 | impl<'a, T, F> Goal for Predicate<'a, T, F> where T: PartialEq + Unif, F: Fn(&State) -> bool { 134 | fn eval(&self, state: &State) -> PossibleStates { 135 | let f = self.f; 136 | if f(state) { 137 | vec![state.clone()] 138 | } else { 139 | Vec::with_capacity(0) 140 | } 141 | } 142 | } 143 | 144 | /// Creates a `Predicate` goal that filters a set of possible states with the given function. 145 | pub fn pred<'a, T, F>(f: &'a F) -> Predicate<'a, T, F> where T: PartialEq + Unif, F: Fn(&State) -> bool { 146 | Predicate { f: f, _m: PhantomData } 147 | } 148 | 149 | 150 | macro_rules! unif_prim { 151 | ( $t:ty ) => { 152 | impl Unif<$t> for $t { 153 | fn unify(&self, other: &$t, prev: &State<$t>) -> PossibleStates<$t> { 154 | if self.eq(other) { vec![prev.clone()] } else { PossibleStates::new() } 155 | } 156 | } 157 | } 158 | } 159 | 160 | unif_prim!(bool); 161 | unif_prim!(char); 162 | unif_prim!(f32); 163 | unif_prim!(f64); 164 | unif_prim!(i16); 165 | unif_prim!(i32); 166 | unif_prim!(i64); 167 | unif_prim!(i8); 168 | unif_prim!(isize); 169 | unif_prim!(u16); 170 | unif_prim!(u32); 171 | unif_prim!(u64); 172 | unif_prim!(u8); 173 | unif_prim!(usize); 174 | unif_prim!(String); 175 | 176 | 177 | #[cfg(test)] 178 | mod tests { 179 | use state::{State}; 180 | use super::{Goal, fail, unify_val, unify_vars, conj, disj, pred}; 181 | 182 | #[test] 183 | fn test_bind_val() { 184 | let s = State::::empty(); 185 | let (v, s) = s.make_var(); 186 | 187 | let n: i32 = 34; 188 | let g = unify_val(&v, n); 189 | 190 | let results = g.eval(&s); 191 | assert_eq!(results.len(), 1); 192 | 193 | let val = results[0].get(&v).unwrap(); 194 | assert_eq!(val, &n); 195 | } 196 | 197 | #[test] 198 | fn test_bind_var() { 199 | let s = State::::empty(); 200 | let (a, s) = s.make_var(); 201 | let (b, s) = s.make_var(); 202 | 203 | let n: i32 = 12; 204 | let g1 = unify_vars(&a, &b); 205 | let g2 = unify_val(&b, n); 206 | let g = conj(g1, g2); 207 | 208 | let results = g.eval(&s); 209 | assert_eq!(results.len(), 1); 210 | 211 | let val = results[0].get(&a).unwrap(); 212 | assert_eq!(val, &n); 213 | } 214 | 215 | #[test] 216 | fn test_conj_fail() { 217 | let s = State::::empty(); 218 | let (v, s) = s.make_var(); 219 | let g1 = unify_val(&v, 56); 220 | let g2 = fail::(); 221 | let g = conj(g1, g2); 222 | 223 | let results = g.eval(&s); 224 | assert_eq!(results.len(), 0); 225 | } 226 | 227 | #[test] 228 | fn test_disj_fail() { 229 | let s = State::::empty(); 230 | let (v, s) = s.make_var(); 231 | let g1 = fail::(); 232 | let g2 = unify_val(&v, 43); 233 | let g = disj(g1, g2); 234 | 235 | let results = g.eval(&s); 236 | assert_eq!(results.len(), 1); 237 | 238 | let val = results[0].get(&v).unwrap(); 239 | assert_eq!(val, &43); 240 | } 241 | 242 | #[test] 243 | fn test_disj() { 244 | let s = State::::empty(); 245 | let (a, s) = s.make_var(); 246 | 247 | let g1 = unify_val(&a, 123); 248 | let g2 = unify_val(&a, 456); 249 | let g = disj(g1, g2); 250 | 251 | let results = g.eval(&s); 252 | assert_eq!(results.len(), 2); 253 | 254 | let val = results[0].get(&a).unwrap(); 255 | assert_eq!(val, &123); 256 | 257 | let val = results[1].get(&a).unwrap(); 258 | assert_eq!(val, &456); 259 | } 260 | 261 | #[test] 262 | fn test_pred() { 263 | let s = State::::empty(); 264 | let (a, s) = s.make_var(); 265 | 266 | let d = disj(unify_val(&a, 123), unify_val(&a, 987)); 267 | let f = |s: &State| match s.get(&a) { Some(n) => *n == 987, None => false }; 268 | let p = pred(&f); 269 | let g = conj(d, p); 270 | 271 | let results = g.eval(&s); 272 | assert_eq!(results.len(), 1); 273 | 274 | let val = results[0].get(&a).unwrap(); 275 | assert_eq!(val, &987); 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Rust Logic 2 | //! 3 | //! **[rslogic](https://github.com/kulibali/rslogic)** is a logic programming framework 4 | //! for Rust inspired by [µKanren](https://github.com/jasonhemann/microKanren). 5 | //! 6 | //! A logical statement is built from **variable**s, **state**s, and **goal**s. 7 | //! Create an initial state, then obtain some variables (and resulting state) from it. 8 | //! Construct a goal consisting of variable bindings, logical operations (AND, OR), or 9 | //! predicates. Then evaluate the goal using the state resulting from making the variables. 10 | //! Evaluating a goal returns all possible solutions to the statement, in the form of 11 | //! a number of states containing variable bindings. 12 | //! 13 | //! ``` 14 | //! use rslogic::state; 15 | //! use rslogic::state::{Unif, State, PossibleStates}; 16 | //! use rslogic::goal; 17 | //! use rslogic::goal::{Goal, fail, unify_val, unify_vars, conj, disj, pred}; 18 | //! 19 | //! let s = state::State::::empty(); 20 | //! let (v1, s) = s.make_var(); 21 | //! let (v2, s) = s.make_var(); 22 | //! 23 | //! let n = 123; 24 | //! let g = goal::conj(goal::unify_vars(&v1, &v2), goal::unify_val(&v2, n)); 25 | //! 26 | //! let results = g.eval(&s); 27 | //! assert_eq!(results.len(), 1); 28 | //! let bound_value = results[0].get(&v1).unwrap(); 29 | //! assert_eq!(bound_value, &n); 30 | //! ``` 31 | //! 32 | //! This example creates two variables, `v1` and `v2`, and then assembles a logical expression 33 | //! equivalent to `(v1 = v2) && (v2 = 123)`. When evaluated, the resulting state binds `123` to 34 | //! both `v1` and `v2`. 35 | //! 36 | 37 | mod btmap; 38 | pub mod goal; 39 | pub mod state; 40 | -------------------------------------------------------------------------------- /src/state.rs: -------------------------------------------------------------------------------- 1 | //! # Logical States 2 | //! 3 | //! A logical state is a collection of variable bindings. 4 | //! 5 | 6 | use std::clone::Clone; 7 | 8 | /// A collection of possible states. 9 | pub type PossibleStates = Vec>; 10 | 11 | /// Values used in a state must be unifiable. Unifying two values produces 12 | /// zero or more possible states, where variables that may be contained in the 13 | /// values may be bound in various combinations. 14 | pub trait Unif where T : PartialEq + Unif { 15 | fn unify(&self, other: &T, prev: &State) -> PossibleStates; 16 | } 17 | 18 | /// Represents a logical variable. A variable must be created by calling 19 | /// `State::make_var()` before a goal is evaluated (by passing the 20 | /// resulting state to a goal). 21 | #[derive(Clone, Copy)] 22 | pub struct Var { 23 | index: usize, 24 | } 25 | 26 | use btmap::BtMap; 27 | 28 | /// A logical state, containing a collection of variable bindings. 29 | /// 30 | /// Variables are stored with one level of indirection, to indicate 31 | /// variables that have been unified before being bound. 32 | pub struct State where T : PartialEq + Unif { 33 | bindings: BtMap, // var index -> slot 34 | slots: BtMap, // slot -> value 35 | next_index: usize, 36 | } 37 | 38 | impl State where T : PartialEq + Unif { 39 | /// Creates an empty state. 40 | pub fn empty() -> State { 41 | State { 42 | bindings: BtMap::empty(), 43 | slots: BtMap::empty(), 44 | next_index: 0 45 | } 46 | } 47 | 48 | /// Returns `true` if the variable is bound in the state. 49 | pub fn binds_var(&self, var: &Var) -> bool { 50 | match self.bindings.get(&var.index) { 51 | Some(ref slot) => self.slots.contains_key(slot), 52 | None => false 53 | } 54 | } 55 | 56 | /// Returns a reference to the value bound to the variable in the state, 57 | /// or None if the var4iable is not bound. 58 | pub fn get<'a>(&'a self, var: &Var) -> Option<&'a T> { 59 | match self.bindings.get(&var.index) { 60 | Some(ref slot) => self.slots.get(slot), 61 | None => None, 62 | } 63 | } 64 | 65 | /// Attempts to unify a variable with a value. If the variable is not bound, 66 | /// returns a new state containing a binding to the value. If the variable is 67 | /// already bound, returns the unification of the two values. 68 | pub fn unify_val(&self, var: &Var, val: T) -> PossibleStates { 69 | match self.bindings.get(&var.index) { 70 | Some(slot) => { 71 | // if the variable has a slot (could be bound or unified with another variable) 72 | // see if it has a value. if so, unify with the value, otherwise bind it to the value 73 | match self.slots.get(slot) { 74 | Some(existing) => { 75 | existing.unify(&val, self) 76 | }, 77 | None => { 78 | vec![State { 79 | bindings: self.bindings.clone(), 80 | slots: self.slots.insert(*slot, val).unwrap(), 81 | .. *self 82 | }] 83 | } 84 | } 85 | }, 86 | None => { 87 | // if this variable is not bound, make a new slot and binding for it 88 | let index = &var.index; 89 | vec![State { 90 | bindings: self.bindings.insert(*index, *index).unwrap(), 91 | slots: self.slots.insert(*index, val).unwrap(), 92 | .. *self 93 | }] 94 | } 95 | } 96 | } 97 | 98 | /// Attempts to unify two variables. 99 | pub fn unify_var(&self, v1: &Var, v2: &Var) -> PossibleStates { 100 | let b1 = self.bindings.get(&v1.index); 101 | let b2 = self.bindings.get(&v2.index); 102 | 103 | match b1 { 104 | Some(s1) => { // v1 has a slot 105 | match b2 { 106 | Some(s2) => { // both variables have slots 107 | let value1 = self.slots.get(s1); 108 | let value2 = self.slots.get(s2); 109 | 110 | match value1 { 111 | Some(vv1) => { 112 | match value2 { 113 | Some(ref vv2) => vv1.unify(vv2, self), // both v1 and v2 are bound, unify values 114 | None => PossibleStates::new() // v2 is not bound, this is an error 115 | } 116 | }, 117 | None => { 118 | match value2 { 119 | Some(_vv2) => PossibleStates::new(), // v1 is not bound, this is an error 120 | None => if s1.eq(s2) { vec![self.clone()] } 121 | else { PossibleStates::new() } // neither slot is bound; slots must then be equal 122 | } 123 | } 124 | } 125 | }, 126 | None => { // v1 has a slot, v2 does not 127 | vec![State { 128 | bindings: self.bindings.insert(v2.index, *s1).unwrap(), 129 | slots: self.slots.clone(), 130 | .. *self 131 | }] 132 | } 133 | } 134 | }, 135 | None => { // v1 does not have a slot 136 | match b2 { 137 | Some(s2) => { // v1 does not have a slot, v2 does 138 | vec![State { 139 | bindings: self.bindings.insert(v1.index, *s2).unwrap(), 140 | slots: self.slots.clone(), 141 | .. *self 142 | }] 143 | }, 144 | None => { // neither variable has a slot 145 | let slot = &v1.index; 146 | vec![State { 147 | bindings: self.bindings 148 | .insert(v1.index, *slot).unwrap() 149 | .insert(v2.index, *slot).unwrap(), 150 | slots: self.slots.clone(), 151 | .. *self 152 | }] 153 | } 154 | } 155 | } 156 | } 157 | } 158 | 159 | /// Creates a new variable and a new state with which it is usable. 160 | pub fn make_var(&self) -> (Var, State) { 161 | let var = Var { index: self.next_index }; 162 | let state = State { 163 | bindings: self.bindings.clone(), 164 | slots: self.slots.clone(), 165 | next_index: self.next_index + 1 166 | }; 167 | (var, state) 168 | } 169 | } 170 | 171 | impl Clone for State where T : PartialEq + Unif { 172 | fn clone(&self) -> State { 173 | State { 174 | bindings: self.bindings.clone(), 175 | slots: self.slots.clone(), 176 | next_index: self.next_index 177 | } 178 | } 179 | 180 | fn clone_from(&mut self, source: &State) { 181 | self.bindings = source.bindings.clone(); 182 | self.slots = source.slots.clone(); 183 | self.next_index = source.next_index; 184 | } 185 | } 186 | --------------------------------------------------------------------------------