suiron/
rule.rs

1//! Defines a fact or rule.
2//!
3//! In Suiron source code, rules have the form:<br>
4//! <blockquote>
5//! head :- body.
6//! </blockquote>
7//! Facts are defined as rules without a body:
8//! <blockquote>
9//! grandfather($X, $Y) :- father($X, $Z), father($Z, $Y).
10//! &nbsp; &nbsp; % This is a rule.<br>
11//! father(John, Kaitlyn). &nbsp; &nbsp; % This is a fact.
12//! </blockquote>
13//!
14// Cleve Lendon  2023
15
16use std::fmt;
17
18use crate::str_to_chars;
19use crate::chars_to_string;
20
21use super::goal::Goal;
22use super::parse_goals::*;
23use super::s_complex::*;
24use super::logic_var::*;
25use super::unifiable::Unifiable;
26use super::tokenizer::*;
27
28/// Defines a fact or rule.
29///
30/// A rule consists of a head and a body.
31/// The head must be a
32/// [complex](../unifiable/enum.Unifiable.html#variant.SComplex) term,
33/// and the body is a [goal](../goal/enum.Goal.html).<br>
34/// For facts, the body is set to [Nil](../goal/enum.Goal.html#variant.Nil).
35#[derive(Debug, Clone)]
36pub struct Rule {
37    pub head: Unifiable, // Must be a Unifiable::SComplex term.
38    pub body: Goal,      // For facts, body is Goal::Nil
39}
40
41/// Finds the index of the neck operator (:-) in a vector of characters.
42/// # Arguments
43/// * `characters`
44/// # Return
45/// * `Option` - Some(index) or None
46fn index_of_neck(chrs: &[char]) -> Option<usize> {
47    let mut previous_colon = false;
48    for (i, ch) in chrs.iter().enumerate() {
49       if *ch == '-' {
50           if previous_colon == true { return Some(i - 1); }
51       }
52       if *ch == ':' { previous_colon = true; }
53       else { previous_colon = false; }
54    }
55    return None;
56} // index_of_neck()
57
58
59/// Create a fact or rule from a string representation.
60///
61/// # Arguments
62/// * `to_parse` - &str
63/// # Return
64/// * `Result` - Ok([Rule](../rule/struct.Rule.html)) or Err(message)
65/// # Usage
66/// ```
67/// use suiron::*;
68///
69/// match parse_rule("male(Harold).") {
70///     Ok(fact) => { println!("{}", fact); },
71///     Err(msg) => { println!("{}", msg); },
72/// }
73/// // Prints: male(Harold).
74///
75/// match parse_rule("father($X, $Y) :- parent($X, $Y), male($X).") {
76///     Ok(rule) => { println!("{}", rule); },
77///     Err(msg) => { println!("{}", msg); },
78/// }
79/// // Prints: father($X, $Y) :- parent($X, $Y), male($X).
80/// ```
81pub fn parse_rule(to_parse: &str) -> Result<Rule, String> {
82
83    let s = to_parse.trim();
84
85    // Create vector of characters.
86    let mut chrs = str_to_chars!(s);
87
88    let mut length = chrs.len();
89    if length < 4 {
90        let err = pr_error("Invalid string.", s);
91        return Err(err);
92    }
93
94    // Remove final period.
95    let ch = chrs[length - 1];
96    if ch == '.' {
97        chrs = chrs[0..length - 1].to_vec();
98        length = length - 1;
99    }
100
101    match index_of_neck(&chrs[..]) {
102
103        Some(index) => {
104
105            let head_chrs = &chrs[0..index];
106            let body_chrs = &chrs[index + 2..length];
107
108            // Make sure there is not a second ':-'.
109            if let Some(_) = index_of_neck(&body_chrs) {
110                let err = pr_error("Invalid rule.", s);
111                return Err(err);
112            }
113
114            let head: Unifiable;
115            match parse_subgoal(&chars_to_string!(head_chrs)) {
116                Ok(sg) => {
117                    match sg {
118                        Goal::ComplexGoal(h) => { head = h; },
119                        _ => { panic!("parse_rule() - \
120                               Head of rule must be complex term."); },
121                    }
122                },
123                Err(err) => { return Err(err); },
124            }
125
126            match generate_goal(&chars_to_string!(body_chrs)) {
127                Ok(body) => { return Ok( Rule{head, body}); },
128                Err(err) => { return Err(err); },
129            }
130        },
131        None => {  // Must be a fact, no body.
132            let fact: Unifiable;
133            let s = chars_to_string!(chrs);
134            match parse_complex(&s) {
135                Ok(f) => { fact = f; },
136                Err(err) => { return Err(err); },
137            }
138            return Ok(Rule{head: fact, body: Goal::Nil});
139        },
140
141    } // match index_of_neck(chrs)...
142
143} // parse_rule
144
145impl Rule {
146
147    /// Creates a key (predicate name) for indexing into the
148    /// [knowledge base](../knowledge_base/index.html).
149    ///
150    /// The name of a predicate consists of its functor and its arity,
151    /// separated by a slash. For example, for the fact
152    /// `loves(Chandler, Monica)`, the functor is `loves` and the arity
153    /// is 2, therefore the name of the predicate is `loves/2`.
154    ///
155    /// # Arguments
156    /// * `self`
157    /// # Return
158    /// * `key` - String
159    /// # Usage
160    /// ```
161    /// use suiron::*;
162    ///
163    /// let query = parse_query("parse($In, $Out, $ErrIn, $ErrOut)");
164    /// match query {
165    ///     Ok(q) => { println!("{}", q.key()); },
166    ///     Err(msg) => { println!("{}", msg); },
167    /// }
168    /// // Prints: parse/4
169    /// ```
170    pub fn key(&self) -> String { return self.head.key(); }
171
172    /// Returns the head of this rule.
173    ///
174    /// # Arguments
175    /// * `self`
176    /// # Return
177    /// * `head term` -
178    /// ([SComplex](../unifiable/enum.Unifiable.html#variant.SComplex))
179    /// ```
180    /// use suiron::*;
181    ///
182    /// clear_id();
183    /// let kb = test_kb();
184    /// // Get grandfather rule.
185    /// let rule = get_rule(&kb, "grandfather/2", 0);
186    /// let head = rule.get_head();
187    /// println!("{}", head); // Prints: grandfather($X_1, $Y_2)
188    /// ```
189    pub fn get_head(&self) -> Unifiable { return self.head.clone(); }
190
191    /// Returns the body of this rule, which is a goal.
192    ///
193    /// # Arguments
194    /// * `self`
195    /// # Return
196    /// * `body` - ([Goal](../goal/enum.Goal.html))
197    /// # Usage
198    /// ```
199    /// use suiron::*;
200    ///
201    /// clear_id();
202    /// let kb = test_kb();
203    /// // Get grandfather rule.
204    /// let rule = get_rule(&kb, "grandfather/2", 0);
205    /// let body = rule.get_body();
206    /// println!("{}", body);  // father($X_1, $Z_3), father($Z_3, $Y_2)
207    /// ```
208    pub fn get_body(&self) -> Goal { return self.body.clone(); }
209
210    /// The scope of a logic variable is the rule or goal in which it is defined.
211    ///
212    /// When the inference algorithm tries to solve a goal, it calls this method
213    /// to ensure that the variables are unique.
214    ///
215    /// # Argument
216    /// * `self`
217    /// * `recreated_vars` - logic variables already recreated
218    /// # Return
219    /// * `Rule`
220    /// # Usage
221    /// ```
222    /// use suiron::*;
223    ///
224    /// clear_id();
225    /// match parse_rule("parent($X, $Y) :- mother($X, $Y).") {
226    ///     Ok(rule) => {
227    ///         let mut var_map = VarMap::new();
228    ///         let rule = rule.recreate_variables(&mut var_map);
229    ///         println!("{}", rule);
230    ///     },
231    ///     Err(msg) => { println!("{}", msg); },
232    /// }
233    /// // Prints: parent($X_1, $Y_2) :- mother($X_1, $Y_2).
234    /// ```
235    pub fn recreate_variables(self, recreated_vars: &mut VarMap) -> Rule {
236        let new_head = self.head.recreate_variables(recreated_vars);
237        let new_body: Goal;
238        match self.body {
239            Goal::OperatorGoal(op) => {
240                new_body = Goal::OperatorGoal(op.recreate_variables(recreated_vars));
241            },
242            Goal::ComplexGoal(comp) => {
243                new_body = Goal::ComplexGoal(comp.recreate_variables(recreated_vars));
244            },
245            Goal::BuiltInGoal(bip) => {
246                new_body = Goal::BuiltInGoal(bip.recreate_variables(recreated_vars));
247            },
248            Goal::Nil => { new_body = Goal::Nil; },
249        }
250        return Rule{ head: new_head, body: new_body };
251    } // recreate_variables
252
253}
254
255// Creates an error message for parse_rule() function.
256// Arguments:
257//    err - error description
258//    bad - string which caused the error
259// Return:
260//    error message (String)
261fn pr_error(err: &str, bad: &str) -> String {
262    format!("parse_rule() - {}: >{}<", err, bad)
263}
264
265// Display trait, to display facts and rules.
266impl fmt::Display for Rule {
267
268    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
269        if self.body == Goal::Nil {
270            // Display fact.
271            write!(f, "{}.", self.head)
272        }
273        else {
274            // Display rule.
275            write!(f, "{} :- {}.", self.head, self.body)
276        }
277    } // fmt
278
279} // fmt::Display