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