1use super::infix::*;
6use super::parse_goals::*;
7use super::s_linked_list::*;
8use super::logic_var::*;
9use super::s_complex::*;
10use super::unifiable::{*, Unifiable::*};
11use super::built_in_functions::*;
12
13use crate::atom;
14use crate::sfunction;
15use crate::str_to_chars;
16use crate::chars_to_string;
17
18pub fn parse_arguments(to_parse: &str) -> Result<Vec<Unifiable>, String> {
38
39 let s = to_parse.trim();
40 let chrs = str_to_chars!(s);
41 let length_chrs = chrs.len();
42
43 if length_chrs == 0 {
44 let err = "parse_arguments() - Empty argument list: ";
45 return Err(err.to_string());
46 }
47
48 if chrs[0] == ',' {
49 let err = "parse_arguments() - Missing first argument: ";
50 return Err(err.to_string());
51 }
52
53 if chrs[length_chrs - 1] == ',' {
56 let prev = chrs[length_chrs - 2];
59 if prev != '\\' { let err = "parse_arguments() - Missing last argument: ";
61 return Err(err.to_string());
62 }
63 }
64
65 let mut has_digit = false;
66 let mut has_non_digit = false;
67 let mut has_period = false;
68 let mut open_quote = false;
69
70 let mut num_quotes = 0;
71 let mut round_depth = 0; let mut square_depth = 0; let mut argument = "".to_string();
75 let mut term_list = Vec::<Unifiable>::new();
76
77 let mut start = 0;
78
79 let mut i = start;
80 while i < length_chrs {
81
82 let ch = chrs[i];
83
84 if open_quote {
87 argument.push(ch);
88 if ch == '"' {
89 open_quote = false;
90 num_quotes += 1;
91 }
92 }
93 else {
94 if ch == '[' {
95 argument.push(ch);
96 square_depth += 1;
97 }
98 else if ch == ']' {
99 argument.push(ch);
100 square_depth -= 1;
101 }
102 else if ch == '(' {
103 argument.push(ch);
104 round_depth += 1;
105 }
106 else if ch == ')' {
107 argument.push(ch);
108 round_depth -= 1
109 }
110 else if round_depth == 0 && square_depth == 0 {
111
112 if ch == ',' {
113
114 let s2 = argument.trim();
115 match check_quotes(s2, num_quotes) {
116 Some(err) => { return Err(err); },
117 None => {},
118 }
119 num_quotes = 0;
120
121 let term = make_term(s2, has_digit, has_non_digit, has_period)?;
122 term_list.push(term);
123 argument = "".to_string();
124 has_digit = false;
125 has_non_digit = false;
126 has_period = false;
127 start = i + 1; }
129 else if ch >= '0' && ch <= '9' {
130 argument.push(ch);
131 has_digit = true
132 }
133 else if ch == '+' || ch == '-' {
134 argument.push(ch);
135 let mut next_ch = 'x';
138 if i < length_chrs - 1 { next_ch = chrs[i + 1]; }
139 let mut prev_ch = ' ';
140 if i > 0 { prev_ch = chrs[i]; }
141 if prev_ch == ' ' && (next_ch < '0' || next_ch > '9') {
142 has_non_digit = true;
143 }
144 }
145 else if ch == '.' {
146 argument.push(ch);
147 has_period = true
148 }
149 else if ch == '\\' { if i < length_chrs {
151 i += 1;
152 argument.push(chrs[i]);
153 }
154 else { argument.push(ch);
156 }
157 }
158 else if ch == '"' {
159 argument.push(ch);
160 open_quote = true; num_quotes += 1;
162 }
163 else {
164 argument.push(ch);
165 if ch > ' ' { has_non_digit = true; }
166 }
167 }
168 else {
169 argument.push(ch);
171 }
172 } i += 1;
175
176 } if start < length_chrs {
179
180 let s2 = argument.trim();
181 match check_quotes(s2, num_quotes) {
182 Some(err) => {
183 return Err(err);
184 },
185 None => {},
186 }
187
188 let term = make_term(s2, has_digit, has_non_digit, has_period)?;
189 term_list.push(term);
190 }
191
192 if round_depth != 0 {
193 let err = "parse_arguments() - Unmatched parentheses: ";
194 return Err(err.to_string());
195 }
196
197 if square_depth != 0 {
198 let err = "parse_arguments() - Unmatched brackets: ";
199 return Err(err.to_string());
200 }
201
202 return Ok(term_list);
203
204} fn make_term(to_parse: &str,
218 has_digit: bool,
219 has_non_digit: bool,
220 has_period: bool) -> Result<Unifiable, String> {
221
222 let s = to_parse.trim();
223
224 let term_chars = str_to_chars!(s);
225 let length_term = term_chars.len();
226
227 if length_term == 0 {
228 let err = mt_error("Length of term is 0", s);
229 return Err(err);
230 }
231
232 let first: char = term_chars[0];
233 if first == '$' {
234
235 if s == "$_" { return Ok(Anonymous); }
237
238 match make_logic_var(s.to_string()) {
241 Ok(var) => { return Ok(var); },
242 Err(_) => { return Ok(atom!(s)); },
243 }
244 }
245
246 if length_term >= 2 {
249 let last = term_chars[length_term - 1];
250 if first == '"' {
251 if last == '"' {
252 let chars2: Vec<char> = term_chars[1..length_term - 1].to_vec();
253 if chars2.len() == 0 {
254 let err = mt_error("Invalid term. Length is 0", s);
255 return Err(err);
256 }
257 let s2 = chars_to_string!(chars2);
258 return Ok(Atom(s2.to_string()));
259 } else {
260 let err = mt_error("Invalid term. Unmatched quote mark", s);
261 return Err(err)
262 }
263 } else if first == '[' && last == ']' {
264 return parse_linked_list(s);
265 }
266 else if first != '(' && last == ')' {
268 if s.starts_with("join(") { return parse_function(s); }
270 if s.starts_with("add(") { return parse_function(s); }
271 if s.starts_with("subtract(") { return parse_function(s); }
272 if s.starts_with("multiply(") { return parse_function(s); }
273 if s.starts_with("divide(") { return parse_function(s); }
274 return parse_complex(s);
275 }
276 } if has_digit && !has_non_digit { if has_period {
280 match s.parse::<f64>() {
281 Ok(fl) => { return Ok(SFloat(fl)); },
282 Err(_) => { return Err("Invalid float.".to_string()) },
283 }
284 } else {
285 match s.parse::<i64>() {
286 Ok(i) => { return Ok(SInteger(i)); },
287 Err(_) => { return Err("Invalid integer".to_string()); },
288 }
289 }
290 }
291 return Ok(atom!(s));
292
293} pub fn check_quotes(to_check: &str, count: usize) -> Option<String> {
316 if count == 0 { return None; }
317 if count != 2 {
318 return Some(cq_error("Unmatched quotes", to_check));
319 }
320 let chrs = str_to_chars!(to_check);
321 let first = chrs[0];
322 if first != '"' {
323 return Some(cq_error("Text before opening quote", to_check));
324 }
325 let last = chrs[chrs.len() - 1];
326 if last != '"' {
327 return Some(cq_error("Text after closing quote", to_check));
328 }
329 None
330} pub fn parse_term(to_parse: &str) -> Result<Unifiable, String> {
361
362 let mut s = to_parse.trim();
363
364 let mut has_digit = false;
365 let mut has_non_digit = false;
366 let mut has_period = false;
367
368 let chrs = str_to_chars!(&s);
369
370 let (infix, index) = check_arithmetic_infix(&chrs);
373
374 if infix == Infix::Plus || infix == Infix::Minus ||
375 infix == Infix::Multiply || infix == Infix::Divide {
376
377 let (left, right) = get_left_and_right(chrs, index, 1)?;
378
379 let sfunc = match infix {
380 Infix::Plus => { sfunction!("add", left, right) },
381 Infix::Minus => { sfunction!("subtract", left, right) },
382 Infix::Multiply => { sfunction!("multiply", left, right) },
383 Infix::Divide => { sfunction!("divide", left, right) },
384 _ => {
385 let s = format!("parse_term() - Invalid infix {}", infix);
386 return Err(s);
387 },
388 };
389 return Ok(sfunc);
390 }
391
392 for ch in &chrs {
393 if *ch >= '0' && *ch <= '9' {
394 has_digit = true;
395 } else if *ch == '.' {
396 has_period = true;
397 } else {
398 has_non_digit = true;
399 }
400 }
401
402 if chrs.len() == 2 && chrs[0] == '\\' { s = &s[1..]; }
404
405 return make_term(s, has_digit, has_non_digit, has_period);
406
407} fn mt_error(err: &str, bad: &str) -> String {
416 format!("make_term() - {}: {}", err, bad)
417}
418
419fn cq_error(err: &str, bad: &str) -> String {
426 format!("check_quotes() - {}: {}", err, bad)
427}
428
429#[cfg(test)]
430mod test {
431
432 use super::*;
433 use crate::unifiable::Unifiable;
434
435 #[test]
436 fn test_check_quotes() {
437
438 if let Some(error_message) = check_quotes("\"\"Hello\"", 3) {
440 assert_eq!(error_message,
441 "check_quotes() - Unmatched quotes: \"\"Hello\"");
442 }
443 if let Some(error_message) = check_quotes("x\"Hello\"", 2) {
445 assert_eq!(error_message,
446 "check_quotes() - Text before opening quote: x\"Hello\"");
447 }
448 if let Some(error_message) = check_quotes("\"Hello\"x", 2) {
450 assert_eq!(error_message,
451 "check_quotes() - Text after closing quote: \"Hello\"x");
452 }
453 if let Some(error_message) = check_quotes("\"Hello\"", 2) {
455 panic!("The string should be OK: {}", error_message);
456 }
457 if let Some(error_message) = check_quotes("Hello", 0) {
459 panic!("The string should be OK: {}", error_message);
460 }
461 } #[test]
464 fn test_parse_term() {
465 match parse_term(" $_ ") {
466 Ok(term) => {
467 if matches!(term, Unifiable::Anonymous) {
468 assert_eq!("$_", term.to_string());
469 } else {
470 panic!("Should create an Anonymous variable: {}", term)
471 }
472 },
473 Err(msg) => { panic!("{}", msg); },
474 }
475 match parse_term(" $X ") {
476 Ok(term) => {
477 if let Unifiable::LogicVar{id, name} = term {
478 assert_eq!(0, id);
479 assert_eq!("$X", name);
480 } else {
481 panic!("Should create a LogicVar: {}", term)
482 }
483 },
484 Err(msg) => { panic!("{}", msg); },
485 }
486 match parse_term(" $10 ") {
487 Ok(term) => {
488 if let Unifiable::Atom(s) = term {
489 assert_eq!("$10", s);
490 } else {
491 panic!("Should create an Atom: {}", term)
492 }
493 },
494 Err(msg) => { panic!("{}", msg); },
495 }
496 match parse_term(" verb ") {
497 Ok(term) => {
498 if matches!(term, Unifiable::Atom(_)) {
499 assert_eq!("verb", term.to_string());
500 } else {
501 panic!("Should create an Atom: {}", term)
502 }
503 },
504 Err(msg) => { panic!("{}", msg); },
505 }
506 match parse_term(" 1.7 ") {
507 Ok(term) => {
508 if matches!(term, Unifiable::SFloat(_)) {
509 assert_eq!("1.7", term.to_string());
510 } else {
511 panic!("Should create an SFloat: {}", term)
512 }
513 },
514 Err(msg) => { panic!("{}", msg); },
515 }
516 match parse_term(" 46 ") {
517 Ok(term) => {
518 if matches!(term, Unifiable::SInteger(_)) {
519 assert_eq!("46", term.to_string());
520 } else {
521 panic!("Should create an SInteger: {}", term)
522 }
523 },
524 Err(msg) => { panic!("{}", msg); },
525 }
526 match parse_term(" animal(horse, mammal) ") {
527 Ok(term) => {
528 if let Unifiable::SComplex(terms) = term {
529 assert_eq!("mammal", terms[2].to_string());
530 } else {
531 panic!("Should create an SComplex: {}", term)
532 }
533 },
534 Err(msg) => { panic!("{}", msg); },
535 }
536 } #[test]
539 fn test_parse_arguments() {
540
541 let terms_str = "8, 5.9, symptom, [], [a, b | $T], city(Toronto, 2.79)";
542
543 match parse_arguments(terms_str) {
544 Ok(terms) => {
545 match terms[0] {
546 SInteger(i) => { assert_eq!(i, 8, "Incorrect integer."); },
547 _ => { panic!("Should create SInteger: {}", terms[0]); },
548 };
549 match terms[1] {
550 SFloat(f) => { assert_eq!(f, 5.9, "Incorrect float."); },
551 _ => { panic!("Should create SFloat: {}", terms[1]); },
552 };
553 match &terms[2] {
554 Atom(a) => { assert_eq!(a, "symptom", "Incorrect atom"); },
555 _ => { panic!("Should create an Atom: {}", terms[2]); },
556 };
557 match &terms[3] {
558 SLinkedList{term: _, next: _, count: c, tail_var: _} => {
559 let size: usize = 0;
560 assert_eq!(*c, size, "Should be empty list.");
561 },
562 _ => { panic!("Should create an empty list: {}", terms[3]); },
563 };
564 match &terms[4] {
565 SLinkedList{term: _, next: _, count: c, tail_var: _} => {
566 let size: usize = 3;
567 assert_eq!(*c, size, "Incorrect list size.");
568 let s = terms[4].to_string();
569 assert_eq!(s, "[a, b | $T]", "Incorrect list.");
570 },
571 _ => { panic!("Should create a list: {}", terms[4]); },
572 };
573 match &terms[5] {
574 SComplex(args) => {
575 let arity = args.len() - 1; assert_eq!(arity, 2, "Complex term, incorrect arity.");
577 let s = terms[5].to_string();
578 assert_eq!(s, "city(Toronto, 2.79)", "Incorrect complex term.");
579 },
580 _ => { panic!("Should create a complex term: {}", terms[5]); },
581 };
582 },
583 Err(err) => { panic!("{}", err); },
584 }
585
586 } }