suiron/
operator.rs

1//! A base type for And and Or logic operators, etc.
2//!
3//! Methods for accessing and displaying operands
4//! (which are [Goals](../goal/index.html)).
5//!
6// Cleve Lendon 2023
7
8use std::fmt;
9
10use super::goal::*;
11use super::unifiable::*;
12use super::logic_var::*;
13
14/// Defines logical And, Or, etc. An operator holds a vector of goals.
15#[derive(Debug, Clone, PartialEq)]
16pub enum Operator {
17    /// Logical And operator.
18    And(Vec<Goal>),
19    /// Logical Or operator.
20    Or(Vec<Goal>),
21    /// Time - Measures elapsed time.
22    // Note: time() only takes one goal, but indirection is needed.
23    // Therefore, it is necessary to enclose the goal in a vector.
24    Time(Vec<Goal>),
25    /// Not - Succeeds if goal argument cannot be proven true.
26    Not(Vec<Goal>),
27}
28
29impl Operator {
30
31    /// Splits the operands into head and tail.
32    ///
33    /// The head is the first [Goal](../goal/index.html).
34    /// The tail is an Operator (same variant) which holds
35    /// the remaining Goals.
36    ///
37    /// # Arguments
38    /// * `self`
39    /// # Return
40    /// * `(head, tail)` - [Goal](../goal/index.html), Operator
41    /// # Panics
42    /// * If operator is not And or Or.
43    /// * If there are no operands.
44    /// # Usage
45    /// ```
46    /// use suiron::*;
47    ///
48    /// let m = parse_subgoal("mother($X, $Y)").unwrap();
49    /// let f = parse_subgoal("father($X, $Y)").unwrap();
50    /// let s = parse_subgoal("sibling($X, $Y)").unwrap();
51    /// let mfs = Operator::Or(vec![m, f, s]); // mother or father or sibling
52    ///
53    /// let (head, tail) = mfs.split_head_tail();
54    /// // Head is a Goal: mother
55    /// // Tail is an Operator: father or sibling
56    /// ```
57    pub fn split_head_tail(&self) -> (Goal, Operator) {
58
59        match &self {
60            Operator::And(op) => {
61                if op.len() == 0 { panic!("split_head_tail() - No operands."); }
62                let mut operands = op.clone();
63                let head = operands.remove(0);
64                let tail = Operator::And(operands);
65                return (head, tail);
66            },
67            Operator::Or(op) => {
68                if op.len() == 0 { panic!("split_head_tail() - No operands."); }
69                let mut operands = op.clone();
70                let head = operands.remove(0);
71                let tail = Operator::Or(operands);
72                return (head, tail);
73            },
74            _ => { panic!("split_head_tail() - Valid for And and Or operators only."); },
75        }
76    } // split_head_tail()
77
78
79    /// Give logic variables unique IDs.
80    ///
81    /// Logic variables in the knowledge base have an ID of 0, but
82    /// when a rule is fetched from the knowledge base, the logic
83    /// variables must be given unique IDs.
84    ///
85    /// # Arguments
86    /// * `self`
87    /// * `vars` - set of previously recreated variable IDs
88    /// # Return
89    /// * `Operator`
90    /// # Usage
91    /// ```
92    /// use suiron::*;
93    ///
94    /// // Make an And operator: parent($X, $Y), female($X)
95    /// let parent = parse_subgoal("parent($X, $Y)").unwrap();
96    /// let female = parse_subgoal("female($X)").unwrap();
97    /// let op = and_goal!(parent, female);
98    ///
99    /// let mut var_map = VarMap::new();
100    /// let op2 = op.recreate_variables(&mut var_map);
101    /// println!("{}", op2); // Prints: parent($X_1, $Y_2), female($X_1)
102    /// ```
103    pub fn recreate_variables(self, vars: &mut VarMap) -> Operator {
104        match self {
105            Operator::And(goals) => {
106                Operator::And(recreate_vars_goals(goals, vars))
107            },
108            Operator::Or(goals) => {
109                Operator::Or(recreate_vars_goals(goals, vars))
110            },
111            Operator::Time(goals) => {
112                Operator::Time(recreate_vars_goals(goals, vars))
113            },
114            Operator::Not(goals) => {
115                Operator::Not(recreate_vars_goals(goals, vars))
116            },
117        }
118    }
119
120    /// Counts the number of subgoals in the operator.
121    ///
122    /// # Arguments
123    /// * `self`
124    /// # Return
125    /// * number of subgoals
126    pub fn len(&self) -> usize {
127        match self {
128            Operator::And(goals) |
129            Operator::Or(goals) |
130            Operator::Time(goals) |
131            Operator::Not(goals) => { return goals.len(); },
132        }
133    }
134
135    /// Get the indexed subgoal from the operator.
136    ///
137    /// The operator must be And or Or.
138    ///
139    /// # Arguments
140    /// * `self`
141    /// * `index`
142    /// # Return
143    /// * [Goal](../goal/index.html)
144    /// # Usage
145    /// ```
146    /// use suiron::*;
147    ///
148    /// // Make an And operator: parent($X, $Y), female($X)
149    /// let parent = parse_subgoal("parent($X, $Y)").unwrap();
150    /// let female = parse_subgoal("female($X)").unwrap();
151    /// let op = Operator::And(vec![parent, female]);
152    ///
153    /// let goal = op.get_subgoal(1); // Get second subgoal.
154    /// println!("{}", goal);  // Prints: female($X)
155    /// ```
156    pub fn get_subgoal(&self, index: usize) -> Goal {
157        match self {
158            Operator::And(goals) |
159            Operator::Or(goals) |
160            Operator::Time(goals)=> { return goals[index].clone(); },
161            Operator::Not(goals)=> { return goals[index].clone(); },
162        }
163    } // get_subgoal()
164
165} // impl Operator
166
167/// Formats a list for the Display trait.
168///
169/// For example, assuming 'goals' contains [a, b, c],
170/// format_list(goals, ", ") will produce: "a, b, c"
171///
172/// # Arguments
173/// * operands - a vector of Goals or Unifiable terms
174/// * separator - Eg. "," ";" " = "
175/// # Return
176/// * string representation of list
177fn format_list<T>(operands: &Vec<T>, separator: &str)
178                  -> String where T: std::fmt::Display {
179    let mut out = "".to_string();
180    let mut first = true;
181    for op in operands {
182        if first {
183            out += &op.to_string();
184            first = false;
185        }
186        else {
187            out += separator;
188            out += &op.to_string();
189        }
190    }
191    return out;
192} // format_list()
193
194// Display trait, to display operators.
195impl fmt::Display for Operator {
196    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
197        match &self {
198            Operator::And(goals) => {
199                write!(f, "{}", format_list(goals, ", "))
200            },
201            Operator::Or(goals) => {
202                write!(f, "{}", format_list(goals, "; "))
203            },
204            Operator::Time(goals) => {
205                write!(f, "time({})", goals[0])
206            },
207            Operator::Not(goals) => {
208                write!(f, "not({})", goals[0])
209            },
210        } // match
211    } // fmt
212} // fmt::Display
213
214
215#[cfg(test)]
216mod test {
217
218    use crate::*;
219
220    // Create logic vars for testing.
221    fn x() -> Unifiable { logic_var!("$X") }
222    fn y() -> Unifiable { logic_var!("$Y") }
223
224    // Make complex terms for testing.
225    fn make_parent() -> Unifiable {
226        scomplex!(atom!("parent"), x(), y())  // parent($X, $Y)
227    }
228
229    fn make_male() -> Unifiable {
230        scomplex!(atom!("male"), x())  // male($X)
231    }
232
233    fn make_mother() -> Unifiable {
234        scomplex!(atom!("mother"), x(), y())  // mother($X, $Y)
235    }
236
237    fn make_father() -> Unifiable {
238        scomplex!(atom!("father"), x(), y())  // father($X, $Y)
239    }
240
241    // Functions which make operators.
242    fn make_and() -> operator::Operator {
243        let goal1 = Goal::ComplexGoal(make_parent());
244        let goal2 = Goal::ComplexGoal(make_male());
245        Operator::And(vec![goal1, goal2])
246    }
247
248    fn make_or() -> operator::Operator {
249        let goal1 = Goal::ComplexGoal(make_mother());
250        let goal2 = Goal::ComplexGoal(make_father());
251        Operator::Or(vec![goal1, goal2])
252    }
253
254    // Test creation and display of operators.
255    // AND:   parent($X, $Y), male($X).
256    // OR:    mother($X, $Y); father($X, $Y)
257    // UNIFY: $Z = 7
258    #[test]
259    fn test_creation_of_operators() {
260
261        let op1 = make_and();
262        let op2 = make_or();
263
264        let s = format!("{}", op1);
265        assert_eq!("parent($X, $Y), male($X)", s);
266
267        let s = format!("{}", op2);
268        assert_eq!("mother($X, $Y); father($X, $Y)", s);
269    }
270
271    #[test]
272    fn test_split_head_tail() {
273
274        let and_op = make_and();  // parent($X, $Y), male($X)
275        let (head, tail) = and_op.split_head_tail();
276        assert_eq!(tail.len(), 1);
277        let male = Goal::ComplexGoal(make_male());
278        assert_eq!(tail.get_subgoal(0), male);
279        let parent = Goal::ComplexGoal(make_parent());
280        assert_eq!(head, parent);
281
282        let (_, tail) = tail.split_head_tail();
283        assert_eq!(tail.len(), 0);
284    }
285
286    // The method split_head_tail() should panic
287    // if there are no operands. (Length == 0)
288    #[test]
289    #[should_panic]
290    fn test_split_head_tail_panic2() {
291        let and_op = Operator::And(vec![]);
292        and_op.split_head_tail();
293    }
294
295} // test