suiron/built_in_predicates.rs
1//! Functions to support built-in predicates.
2//!
3//! Built-in predicates, such as append() and print(), are a kind of
4//! [goal](../goal/enum.Goal.html).
5//!
6// Cleve Lendon 2023
7
8use std::fmt;
9use std::rc::Rc;
10use std::cell::RefCell;
11
12use super::logic_var::*;
13use super::unifiable::*;
14use super::solution_node::*;
15use super::built_in_print::*;
16use super::built_in_count::*;
17use super::built_in_append::*;
18use super::built_in_filter::*;
19use super::built_in_functor::*;
20use super::built_in_comparison::*;
21use super::built_in_print_list::*;
22use super::substitution_set::*;
23
24/// Defines built-in predicates, such as print(), append(), etc.
25///
26/// In Suiron source code, built-in predicates have the form:
27/// <blockquote>
28/// functor(term1, term2, ...)
29/// </blockquote>
30#[derive(Debug, Clone, PartialEq)]
31pub struct BuiltInPredicate {
32 pub functor: String,
33 pub terms: Option<Vec<Unifiable>>,
34}
35
36impl BuiltInPredicate {
37
38 /// Creates a new BuiltInPredicate struct.
39 ///
40 /// # Usage
41 /// ```
42 /// use suiron::*;
43 ///
44 /// // To make: append(a, b, c)
45 /// let app = "append".to_string();
46 /// let terms = vec![atom!("a"), atom!("b"), atom!("c")];
47 /// let pred = BuiltInPredicate::new(app, Some(terms));
48 ///
49 /// // To make a 'fail' predicate:
50 /// let pred = BuiltInPredicate::new("fail".to_string(), None);
51 /// ```
52 #[inline]
53 pub fn new(functor: String, terms: Option<Vec<Unifiable>>) -> Self {
54 BuiltInPredicate { functor, terms }
55 }
56
57 /// Recreates logic variables to give them unique IDs.
58 ///
59 /// Logic variables in the knowledge base have an ID of 0, but
60 /// when a rule is fetched from the knowledge base, the logic
61 /// variables must be given unique IDs.
62 ///
63 /// # Arguments
64 /// * self
65 /// * map of previously recreated variable IDs
66 /// # Return
67 /// * new BuiltInPredicate
68 ///
69 /// # Usage
70 /// ```
71 /// use suiron::*;
72 ///
73 /// // Make a built-in predicate: print($X, $Y)
74 /// let print_predicate = parse_subgoal("print($X, $Y)").unwrap();
75 ///
76 /// let mut var_map = VarMap::new();
77 /// let new_print = print_predicate.recreate_variables(&mut var_map);
78 /// println!("{}", new_print); // Prints: print($X_1, $Y_2)
79 /// ```
80 pub fn recreate_variables(self, vars: &mut VarMap) -> BuiltInPredicate {
81
82 if let Some(terms) = self.terms {
83 let new_terms = recreate_vars_terms(terms, vars);
84 return BuiltInPredicate::new(self.functor, Some(new_terms));
85 }
86 return BuiltInPredicate::new(self.functor, None);
87
88 } // recreate_variables()
89
90} // BuiltInPredicate
91
92/// Finds solutions for built-in predicates.
93///
94/// See also [next_solution()](../solution_node/fn.next_solution.html)
95/// in solution_node.rs.
96///
97/// # Arguments
98/// * reference to [SolutionNode](../solution_node/struct.SolutionNode.html)
99/// * [BuiltInPredicate](../built_in_predicates/struct.BuiltInPredicate.html)
100/// # Return
101/// * [SubstitutionSet](../substitution_set/type.SubstitutionSet.html) or None
102pub fn next_solution_bip<'a>(sn: Rc<RefCell<SolutionNode<'a>>>,
103 bip: BuiltInPredicate)
104 -> Option<Rc<SubstitutionSet<'a>>> {
105
106 let mut sn_ref = sn.borrow_mut(); // Get a mutable reference.
107
108 if !sn_ref.more_solutions { return None; };
109 sn_ref.more_solutions = false;
110
111 match bip.functor.as_str() {
112 "print" => {
113 next_solution_print(bip, &sn_ref.ss);
114 let ss = Rc::clone(&sn_ref.ss);
115 return Some(ss);
116 },
117 "append" => {
118 // next_solution_append writes to ss.
119 return next_solution_append(bip, &sn_ref.ss);
120 },
121 "functor" => {
122 return next_solution_functor(bip, &sn_ref.ss);
123 },
124 "include" => { // filters a list
125 return bip_include(bip, &sn_ref.ss);
126 },
127 "exclude" => {
128 return bip_exclude(bip, &sn_ref.ss);
129 },
130 "print_list" => {
131 next_solution_print_list(bip, &sn_ref.ss);
132 let ss = Rc::clone(&sn_ref.ss);
133 return Some(ss);
134 },
135 "unify" => {
136 if let Some(terms) = &bip.terms {
137 let left = &terms[0];
138 let right = &terms[1];
139 return left.unify(right, &sn_ref.ss);
140 }
141 return None;
142 },
143 "equal" => {
144 return bip_equal(bip, &sn_ref.ss);
145 },
146 "less_than" => {
147 return bip_less_than(bip, &sn_ref.ss);
148 },
149 "less_than_or_equal" => {
150 return bip_less_than_or_equal(bip, &sn_ref.ss);
151 },
152 "greater_than" => {
153 return bip_greater_than(bip, &sn_ref.ss);
154 },
155 "greater_than_or_equal" => {
156 return bip_greater_than_or_equal(bip, &sn_ref.ss);
157 },
158 "nl" => { // New Line. This cannot fail.
159 print!("\n");
160 return Some(Rc::clone(&sn_ref.ss));
161 },
162 "!" => { // !
163 sn_ref.set_no_backtracking();
164 return Some(Rc::clone(&sn_ref.ss));
165 },
166 "count" => { // count terms in list
167 return bip_count(bip, &sn_ref.ss);
168 },
169 "fail" => { return None; }, // always fails
170 _ => { panic!("next_solution_bip() - Not implemented yet: {}",
171 bip.functor.as_str()); },
172 }
173} // next_solution_bip()
174
175/// Formats a built-in predicate (or function) for Display.
176///
177/// Built-in predicates and functions have the format:
178/// <blockquote>
179/// name(term1, term2, term3...)
180/// </blockquote>
181///
182/// # Arguments
183/// * name (string)
184/// * vector of [Unifiable](../unifiable/enum.Unifiable.html) terms
185/// # Return
186/// * formatted string
187/// # Usage
188/// ```
189/// use suiron::*;
190///
191/// let terms = vec![logic_var!("$X"), logic_var!("$Y")];
192/// let s = format_built_in("compare", &terms);
193/// println!("{}", s); // Prints: compare($X, $Y)
194/// ```
195pub fn format_built_in(name: &str, terms: &Vec<Unifiable>) -> String {
196 let mut out = format!("{}(", name);
197 let mut comma = false;
198 for term in terms {
199 if comma { out += ", "; }
200 else { comma = true; }
201 out += &term.to_string();
202 }
203 out += ")";
204 out
205} // format_built_in
206
207// Display trait, to display built-in predicates.
208impl fmt::Display for BuiltInPredicate {
209 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
210 let func = self.functor.as_str();
211 match &self.terms {
212 Some(terms) => {
213 match func {
214 "unify" => {
215 let (left, right) = (&terms[0], &terms[1]);
216 return write!(f, "{} = {}", left, right);
217 },
218 _ => {
219 let out = format_built_in(func, terms);
220 return write!(f, "{}", out);
221 },
222 }
223 },
224 None => { return write!(f, "{}", func); }
225 }
226 }
227} // Display
228
229
230#[cfg(test)]
231mod test {
232
233 use super::*;
234 use crate::*;
235
236 // Create logic vars for testing.
237 fn x() -> Unifiable { logic_var!("$X") }
238
239 fn two_terms() -> Vec<Unifiable> {
240 let my_name = atom!("Klivo");
241 vec![x(), my_name]
242 }
243
244 // Test formatting of (some) built-in predicates.
245 #[test]
246 fn test_format_built_in() {
247 let s = format_built_in("pred_name", &two_terms());
248 assert_eq!("pred_name($X, Klivo)", s);
249 }
250
251 // Test Display trait for built-in predicates.
252 #[test]
253 fn test_display() {
254
255 let functor = "print".to_string();
256 let print_pred = BuiltInPredicate::new(functor, Some(two_terms()));
257 assert_eq!("print($X, Klivo)", format!("{}", print_pred));
258
259 let functor = "unify".to_string();
260 let unify_pred = BuiltInPredicate::new(functor, Some(two_terms()));
261 assert_eq!("$X = Klivo", format!("{}", unify_pred));
262 }
263
264} // test