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//! % This is a rule.<br>
11//! father(John, Kaitlyn). % 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