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