suiron/built_in_functions.rs
1//! Functions to support Suiron's built-in functions (SFunction).
2//!
3
4use std::rc::Rc;
5use super::s_complex::*;
6use super::parse_terms::*;
7use super::parse_goals::*;
8use super::substitution_set::*;
9use super::unifiable::Unifiable;
10use super::built_in_join::*;
11use super::built_in_arithmetic::*;
12
13use crate::str_to_chars;
14use crate::chars_to_string;
15
16/// Unify an SFunction with another Unifiable term.
17///
18/// When the function [unify()](../unifiable/enum.Unifiable.html#method.unify)
19/// tries to unify an
20/// [SFunction](../unifiable/enum.Unifiable.html#variant.SFunction)
21/// with another term, it calls `unify_sfunction()`.
22/// This function evaluates its arguments (`terms`), and unifies the result
23/// with the `other` [Unifiable](../unifiable/enum.Unifiable.html) term.
24///
25/// # Arguments
26/// * name of function
27/// * terms - vector of Unifiable terms
28/// * other Unifiable term
29/// * [SubstitutionSet](../substitution_set/index.html)
30/// # Returns
31/// * [SubstitutionSet](../substitution_set/index.html) or None.
32/// # Usage
33/// ```
34/// use std::rc::Rc;
35/// use suiron::*;
36///
37/// // Try: $X = add(1, 2, 3)
38/// let x = logic_var!(next_id(), "$X");
39/// let ss = empty_ss!();
40/// let terms = vec![SInteger(1), SInteger(2), SInteger(3)];
41///
42/// match unify_sfunction("add", &terms, &x, &ss) {
43/// Some(ss) => {
44/// let res = get_ground_term(&x, &ss).unwrap();
45/// println!("{}", res);
46/// },
47/// None => { println!("No solution."); },
48/// }
49/// // Should print: 6
50/// ```
51///
52pub fn unify_sfunction<'a>(name: &str, terms: &'a Vec<Unifiable>,
53 other: &'a Unifiable, ss: &'a Rc<SubstitutionSet<'a>>)
54 -> Option<Rc<SubstitutionSet<'a>>> {
55
56 let name = name.to_string();
57
58 if name.eq("join") {
59 let result = evaluate_join(terms, ss);
60 return result.unify(other, ss);
61 }
62 else if name.eq("add") {
63 let result = evaluate_add(terms, ss);
64 return result.unify(other, ss);
65 }
66 else if name.eq("subtract") {
67 let result = evaluate_subtract(terms, ss);
68 return result.unify(other, ss);
69 }
70 else if name.eq("multiply") {
71 let result = evaluate_multiply(terms, ss);
72 return result.unify(other, ss);
73 }
74 else if name.eq("divide") {
75 let result = evaluate_divide(terms, ss);
76 return result.unify(other, ss);
77 }
78
79 return None;
80
81} // unify_sfunction()
82
83/// Parses a string to produce a built-in function (SFunction).
84///
85/// # Note
86/// * parse_function() is very similar to
87/// [parse_complex()](../s_complex/fn.parse_complex.html).
88/// # Arguments
89/// * string to parse
90/// # Returns
91/// * [SFunction](../unifiable/enum.Unifiable.html#variant.SFunction)
92/// or error message
93/// # Usage
94/// ```
95/// use suiron::*;
96///
97/// match parse_function("add(7, 9, 4)") {
98/// Ok(term) => { println!("{}", term); },
99/// Err(msg) => { println!("{}", msg); },
100/// }
101/// ```
102/// Should print: add(7, 9, 4)
103pub fn parse_function(to_parse: &str) -> Result<Unifiable, String> {
104
105 let s = to_parse.trim();
106 let chrs = str_to_chars!(s);
107
108 // Built-in functions have the same form as complex terms.
109 // That is: name(term1, term2...)
110 match validate_complex(s, &chrs) {
111 Some(error_message) => { return Err(error_message); },
112 None => {},
113 }
114
115 // Get indices.
116 match indices_of_parentheses(&chrs) {
117 Ok(indices) => {
118 match indices {
119 Some((left, right)) => {
120
121 let name = chars_to_string!(chrs[0..left]);
122 let terms_str = chars_to_string!(chrs[left + 1..right]);
123 if terms_str.len() == 0 {
124 let err = format!("parse_function - No arguments: {}", s);
125 return Err(err);
126 }
127
128 let mut new_terms: Vec<Unifiable> = vec![];
129 match parse_arguments(&terms_str) {
130 Ok(terms) => {
131 // If everything parsed correctly, return SFunction.
132 for term in terms { new_terms.push(term); }
133 return Ok(Unifiable::SFunction{name, terms: new_terms});
134 },
135 Err(err) => {
136 let err = format!("{}{}", err, s);
137 return Err(err);
138 },
139 }
140 },
141 None => {
142 let err = format!("parse_function() - Invalid function: {}", s);
143 return Err(err);
144 },
145 } // match
146 },
147 Err(err) => { return Err(err); }
148 } // match
149
150} // parse_function()
151
152
153#[cfg(test)]
154mod test {
155
156 use crate::*;
157 use super::*;
158
159 #[test]
160 fn test_parse_function() {
161 match parse_function("add(5, 6, 7)") {
162 Ok(sf) => {
163 if matches!(sf, Unifiable::SFunction{name: _, terms: _}) {
164 assert_eq!("add(5, 6, 7)", sf.to_string());
165 } else {
166 panic!("parse_function() - \
167 Should create a function: {}", sf);
168 }
169 },
170 Err(err) => { panic!("{}", err); },
171 }
172 match parse_function("add") {
173 Ok(c) => {
174 panic!("parse_function() - Should generate error: {}", c)
175 },
176 Err(err) => {
177 assert_eq!(err, "parse_function() - Invalid function: add");
178 },
179 }
180 match parse_function("add(, 6, 7)") {
181 Ok(c) => {
182 panic!("parse_function() - Should generate error: {}", c)
183 },
184 Err(err) => {
185 assert_eq!(err, "parse_arguments() - \
186 Missing first argument: add(, 6, 7)");
187 },
188 }
189 match parse_function("add(5, 6, )") {
190 Ok(c) => {
191 panic!("parse_function() - Should generate error: {}", c)
192 },
193 Err(err) => {
194 assert_eq!(err, "parse_arguments() - \
195 Missing last argument: add(5, 6, )");
196 },
197 }
198 match parse_function("add()") {
199 Ok(c) => {
200 panic!("parse_function() - Should generate error: {}", c)
201 },
202 Err(err) => {
203 assert_eq!(err, "parse_function - No arguments: add()");
204 },
205 }
206 } // test_parse_function()
207
208} // test