1use crate::builtins::{
2 build_list, collect_list, exec_builtin, is_builtin, term_compare, BuiltinResult,
3};
4use crate::database::CompiledDatabase;
5use crate::term::{Clause, Term, VarId};
6use crate::unify::Substitution;
7use fnv::FnvHashMap;
8use serde::{Deserialize, Serialize};
9use std::collections::VecDeque;
10use std::io::Write as IoWrite;
11
12fn format_float(f: f64) -> String {
14 if f.is_nan() || f.is_infinite() {
15 return format!("{}", f);
16 }
17 let s = format!("{}", f);
18 if s.contains('.') || s.contains('e') || s.contains('E') {
19 s
20 } else {
21 format!("{}.0", s)
22 }
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct Solution {
28 pub bindings: Vec<(String, Term)>,
29}
30
31#[derive(Debug)]
33pub enum SolveResult {
34 Success(Solution),
36 Failure,
38 Error(String),
40}
41
42struct ChoicePoint {
44 goals: VecDeque<Term>,
46 untried: Vec<usize>,
48 trail_mark: usize,
50 var_counter: VarId,
52 cut_barrier: bool,
54 disjunction: bool,
56}
57
58pub struct Solver<'a> {
59 db: &'a CompiledDatabase,
60 subst: Substitution,
61 var_counter: VarId,
62 query_vars: FnvHashMap<String, VarId>,
63 choice_stack: Vec<ChoicePoint>,
64 limit: Option<usize>,
66 solutions_found: usize,
67 steps: usize,
69 max_depth: usize,
71 interner: crate::term::StringInterner,
73 cut_in_try_solve: bool,
75 steps_exceeded: bool,
77}
78
79fn max_var_in_term(term: &Term) -> Option<VarId> {
81 match term {
82 Term::Var(id) => Some(*id),
83 Term::Atom(_) | Term::Integer(_) | Term::Float(_) => None,
84 Term::Compound { args, .. } => args.iter().filter_map(max_var_in_term).max(),
85 Term::List { head, tail } => {
86 let h = max_var_in_term(head);
87 let t = max_var_in_term(tail);
88 h.max(t)
89 }
90 }
91}
92
93impl<'a> Solver<'a> {
94 pub fn new(
96 db: &'a CompiledDatabase,
97 goals: Vec<Term>,
98 query_vars: FnvHashMap<String, VarId>,
99 ) -> Self {
100 let max_from_vars = query_vars.values().copied().max().unwrap_or(0);
102 let max_from_goals = goals.iter().filter_map(max_var_in_term).max().unwrap_or(0);
103 let initial_var_counter = max_from_vars.max(max_from_goals) + 1;
104 let mut solver = Solver {
105 interner: db.interner.clone(),
106 db,
107 subst: Substitution::new(),
108 var_counter: initial_var_counter,
109 query_vars,
110 choice_stack: Vec::new(),
111 limit: None,
112 solutions_found: 0,
113 steps: 0,
114 max_depth: 10_000,
115 cut_in_try_solve: false,
116 steps_exceeded: false,
117 };
118 solver.choice_stack.push(ChoicePoint {
121 goals: VecDeque::from(goals),
122 untried: vec![],
123 trail_mark: 0,
124 var_counter: initial_var_counter,
125 cut_barrier: false,
126 disjunction: false,
127 });
128 solver
129 }
130
131 pub fn with_limit(mut self, limit: usize) -> Self {
132 self.limit = Some(limit);
133 self
134 }
135
136 pub fn with_max_depth(mut self, max_depth: usize) -> Self {
137 self.max_depth = max_depth;
138 self
139 }
140
141 pub fn interner(&self) -> &crate::term::StringInterner {
143 &self.interner
144 }
145
146 pub fn next(&mut self) -> SolveResult {
148 if let Some(limit) = self.limit {
149 if self.solutions_found >= limit {
150 return SolveResult::Failure;
151 }
152 }
153
154 if self.solutions_found == 0 && !self.choice_stack.is_empty() {
156 let initial = self.choice_stack.pop().unwrap();
157 self.var_counter = initial.var_counter;
158 return self.solve(initial.goals);
159 }
160
161 self.backtrack()
163 }
164
165 pub fn all_solutions(self) -> Result<Vec<Solution>, String> {
167 self.all_solutions_with_interner().map(|(sols, _)| sols)
168 }
169
170 pub fn all_solutions_with_interner(
172 mut self,
173 ) -> Result<(Vec<Solution>, crate::term::StringInterner), String> {
174 let mut solutions = Vec::new();
175 loop {
176 match self.next() {
177 SolveResult::Success(sol) => solutions.push(sol),
178 SolveResult::Failure => return Ok((solutions, self.interner)),
179 SolveResult::Error(e) => return Err(e),
180 }
181 }
182 }
183
184 fn solve(&mut self, mut goals: VecDeque<Term>) -> SolveResult {
186 loop {
187 if goals.is_empty() {
188 self.solutions_found += 1;
190 return SolveResult::Success(self.extract_solution());
191 }
192
193 self.steps += 1;
194 if self.steps > self.max_depth {
195 return SolveResult::Error(format!(
196 "Maximum step limit exceeded ({})",
197 self.max_depth
198 ));
199 }
200
201 let goal = goals.pop_front().unwrap();
202 let walked_goal = self.subst.walk(&goal);
203
204 if is_builtin(&walked_goal, &self.interner) {
205 match exec_builtin(&walked_goal, &mut self.subst, &self.interner) {
206 Ok(BuiltinResult::Success) => {
207 continue;
209 }
210 Ok(BuiltinResult::Failure) => {
211 return self.backtrack();
212 }
213 Ok(BuiltinResult::Cut) => {
214 while let Some(cp) = self.choice_stack.pop() {
216 if cp.cut_barrier {
217 break;
218 }
219 }
220 continue;
222 }
223 Ok(BuiltinResult::NegationAsFailure(inner_goal)) => {
224 let mark = self.subst.trail_mark();
226 let saved_counter = self.var_counter;
227 let saved_stack_len = self.choice_stack.len();
228
229 let inner_result = self.try_solve_once(vec![inner_goal]);
231
232 self.subst.undo_to(mark);
234 self.var_counter = saved_counter;
235 self.choice_stack.truncate(saved_stack_len);
236 self.cut_in_try_solve = false;
237
238 if self.steps_exceeded {
240 self.steps_exceeded = false;
241 return SolveResult::Error(format!(
242 "Maximum step limit exceeded ({}) inside negation",
243 self.max_depth
244 ));
245 }
246
247 if inner_result {
248 return self.backtrack();
250 } else {
251 continue;
253 }
254 }
255 Ok(BuiltinResult::Disjunction(left, right)) => {
256 let mark = self.subst.trail_mark();
258 let saved_counter = self.var_counter;
259
260 let mut alt_goals = VecDeque::from(vec![right]);
262 alt_goals.extend(goals.iter().cloned());
263
264 self.choice_stack.push(ChoicePoint {
266 goals: alt_goals,
267 untried: vec![],
268 trail_mark: mark,
269 var_counter: saved_counter,
270 cut_barrier: false,
271 disjunction: true,
272 });
273
274 goals.push_front(left);
276 continue;
277 }
278 Ok(BuiltinResult::IfThenElse(cond, then, else_branch)) => {
279 let mark = self.subst.trail_mark();
282 let saved_counter = self.var_counter;
283 let saved_stack_len = self.choice_stack.len();
284
285 let cond_result = self.try_solve_once(vec![cond]);
286 self.cut_in_try_solve = false;
287
288 if self.steps_exceeded {
289 self.steps_exceeded = false;
290 return SolveResult::Error(format!(
291 "Maximum step limit exceeded ({})",
292 self.max_depth
293 ));
294 }
295
296 if cond_result {
297 self.choice_stack.truncate(saved_stack_len);
299 goals.push_front(then);
300 continue;
301 } else {
302 self.subst.undo_to(mark);
304 self.var_counter = saved_counter;
305 self.choice_stack.truncate(saved_stack_len);
306
307 goals.push_front(else_branch);
308 continue;
309 }
310 }
311 Ok(BuiltinResult::IfThen(cond, then)) => {
312 let mark = self.subst.trail_mark();
314 let saved_counter = self.var_counter;
315 let saved_stack_len = self.choice_stack.len();
316
317 let cond_result = self.try_solve_once(vec![cond]);
318 self.cut_in_try_solve = false;
319
320 if self.steps_exceeded {
321 self.steps_exceeded = false;
322 return SolveResult::Error(format!(
323 "Maximum step limit exceeded ({})",
324 self.max_depth
325 ));
326 }
327
328 if cond_result {
329 self.choice_stack.truncate(saved_stack_len);
331 goals.push_front(then);
332 continue;
333 } else {
334 self.subst.undo_to(mark);
335 self.var_counter = saved_counter;
336 self.choice_stack.truncate(saved_stack_len);
337 return self.backtrack();
338 }
339 }
340 Ok(BuiltinResult::Conjunction(a, b)) => {
341 goals.push_front(b);
343 goals.push_front(a);
344 continue;
345 }
346 Ok(BuiltinResult::FindAll(template, goal, result_var)) => {
347 match self.exec_findall(template, goal) {
348 Ok(result_list) => {
349 if self.subst.unify(&result_var, &result_list) {
350 continue;
351 } else {
352 return self.backtrack();
353 }
354 }
355 Err(e) => return SolveResult::Error(e),
356 }
357 }
358 Ok(BuiltinResult::Once(inner_goal)) => {
359 let saved_stack_len = self.choice_stack.len();
362
363 let once_result = self.try_solve_once(vec![inner_goal]);
364 self.cut_in_try_solve = false;
365
366 if self.steps_exceeded {
367 self.steps_exceeded = false;
368 self.choice_stack.truncate(saved_stack_len);
369 return SolveResult::Error(format!(
370 "Maximum step limit exceeded ({})",
371 self.max_depth
372 ));
373 }
374
375 if once_result {
376 self.choice_stack.truncate(saved_stack_len);
377 continue;
378 } else {
379 self.choice_stack.truncate(saved_stack_len);
380 return self.backtrack();
381 }
382 }
383 Ok(BuiltinResult::Call(inner_goal)) => {
384 let walked = self.subst.walk(&inner_goal);
386 goals.push_front(walked);
387 continue;
388 }
389 Ok(BuiltinResult::AtomLength(atom_arg, len_arg)) => {
390 let walked = self.subst.walk(&atom_arg);
391 if let Term::Atom(id) = walked {
392 let name_str = self.interner.resolve(id);
393 let len = name_str.chars().count() as i64;
394 if self.subst.unify(&len_arg, &Term::Integer(len)) {
395 continue;
396 } else {
397 return self.backtrack();
398 }
399 } else {
400 return SolveResult::Error(
401 "atom_length/2: first argument must be an atom".to_string(),
402 );
403 }
404 }
405 Ok(BuiltinResult::AtomConcat(a_arg, b_arg, result_arg)) => {
406 let a = self.subst.walk(&a_arg);
407 let b = self.subst.walk(&b_arg);
408 if let (Term::Atom(id_a), Term::Atom(id_b)) = (&a, &b) {
409 let s = format!(
410 "{}{}",
411 self.interner.resolve(*id_a),
412 self.interner.resolve(*id_b)
413 );
414 let result_id = self.interner.intern(&s);
415 if self.subst.unify(&result_arg, &Term::Atom(result_id)) {
416 continue;
417 } else {
418 return self.backtrack();
419 }
420 } else {
421 return SolveResult::Error(
422 "atom_concat/3: first two arguments must be atoms".to_string(),
423 );
424 }
425 }
426 Ok(BuiltinResult::AtomChars(atom_arg, list_arg)) => {
427 let walked = self.subst.walk(&atom_arg);
428 if let Term::Atom(id) = walked {
429 let name_str = self.interner.resolve(id).to_string();
431 let nil_id = self.interner.lookup("[]").expect("[] must be interned");
432 let mut list = Term::Atom(nil_id);
433 for ch in name_str.chars().rev() {
434 let ch_id = self.interner.intern(&ch.to_string());
435 list = Term::List {
436 head: Box::new(Term::Atom(ch_id)),
437 tail: Box::new(list),
438 };
439 }
440 if self.subst.unify(&list_arg, &list) {
441 continue;
442 } else {
443 return self.backtrack();
444 }
445 } else if let Term::Var(_) = walked {
446 let wlist = self.subst.apply(&list_arg);
448 if let Some(elems) = collect_list(&wlist, &self.interner) {
449 let s: Option<String> = elems
450 .iter()
451 .map(|e| {
452 if let Term::Atom(id) = e {
453 let ch = self.interner.resolve(*id);
454 if ch.chars().count() == 1 {
455 Some(ch.to_string())
456 } else {
457 None
458 }
459 } else {
460 None
461 }
462 })
463 .collect();
464 if let Some(s) = s {
465 let atom_id = self.interner.intern(&s);
466 if self.subst.unify(&atom_arg, &Term::Atom(atom_id)) {
467 continue;
468 }
469 }
470 return self.backtrack();
471 }
472 return SolveResult::Error(
473 "atom_chars/2: second argument must be a character list"
474 .to_string(),
475 );
476 } else {
477 return SolveResult::Error(
478 "atom_chars/2: first argument must be an atom or variable"
479 .to_string(),
480 );
481 }
482 }
483 Ok(BuiltinResult::Write(term)) => {
484 let resolved = self.subst.apply(&term);
485 let s = term_to_string(&resolved, &self.interner);
486 print!("{}", s);
487 let _ = std::io::stdout().flush();
488 continue;
489 }
490 Ok(BuiltinResult::Writeln(term)) => {
491 let resolved = self.subst.apply(&term);
492 let s = term_to_string(&resolved, &self.interner);
493 println!("{}", s);
494 continue;
495 }
496 Ok(BuiltinResult::Nl) => {
497 println!();
498 continue;
499 }
500 Ok(BuiltinResult::Compare(order_arg, t1, t2)) => {
501 let w1 = self.subst.apply(&t1);
502 let w2 = self.subst.apply(&t2);
503 let cmp = term_compare(&w1, &w2, &self.interner);
504 let order_name = match cmp {
505 std::cmp::Ordering::Less => "<",
506 std::cmp::Ordering::Equal => "=",
507 std::cmp::Ordering::Greater => ">",
508 };
509 let order_id = self.interner.intern(order_name);
510 if self.subst.unify(&order_arg, &Term::Atom(order_id)) {
511 continue;
512 } else {
513 return self.backtrack();
514 }
515 }
516 Ok(BuiltinResult::Functor(term_arg, name_arg, arity_arg)) => {
517 let walked = self.subst.walk(&term_arg);
518 match &walked {
519 Term::Atom(id) => {
520 if self.subst.unify(&name_arg, &Term::Atom(*id))
522 && self.subst.unify(&arity_arg, &Term::Integer(0))
523 {
524 continue;
525 }
526 return self.backtrack();
527 }
528 Term::Integer(_) | Term::Float(_) => {
529 if self.subst.unify(&name_arg, &walked)
530 && self.subst.unify(&arity_arg, &Term::Integer(0))
531 {
532 continue;
533 }
534 return self.backtrack();
535 }
536 Term::Compound { functor, args } => {
537 if self.subst.unify(&name_arg, &Term::Atom(*functor))
538 && self
539 .subst
540 .unify(&arity_arg, &Term::Integer(args.len() as i64))
541 {
542 continue;
543 }
544 return self.backtrack();
545 }
546 Term::List { .. } => {
547 let dot_id = self.interner.intern(".");
549 if self.subst.unify(&name_arg, &Term::Atom(dot_id))
550 && self.subst.unify(&arity_arg, &Term::Integer(2))
551 {
552 continue;
553 }
554 return self.backtrack();
555 }
556 Term::Var(_) => {
557 let wname = self.subst.walk(&name_arg);
559 let warity = self.subst.walk(&arity_arg);
560 if let (Term::Atom(name_id), Term::Integer(0)) = (&wname, &warity) {
561 if self.subst.unify(&term_arg, &Term::Atom(*name_id)) {
562 continue;
563 }
564 return self.backtrack();
565 }
566 if let (Term::Integer(_), Term::Integer(0)) = (&wname, &warity) {
567 if self.subst.unify(&term_arg, &wname) {
568 continue;
569 }
570 return self.backtrack();
571 }
572 if let (Term::Float(_), Term::Integer(0)) = (&wname, &warity) {
573 if self.subst.unify(&term_arg, &wname) {
574 continue;
575 }
576 return self.backtrack();
577 }
578 if let (Term::Atom(name_id), Term::Integer(arity)) =
579 (&wname, &warity)
580 {
581 if *arity < 0 {
582 return SolveResult::Error(
583 "functor/3: arity must be non-negative".to_string(),
584 );
585 }
586 if *arity > 1024 {
587 return SolveResult::Error(
588 "functor/3: arity too large (max 1024)".to_string(),
589 );
590 }
591 if *arity > 0 {
592 let args: Vec<Term> = (0..*arity as u32)
593 .map(|_| {
594 let v = self.var_counter;
595 self.var_counter += 1;
596 Term::Var(v)
597 })
598 .collect();
599 let constructed = Term::Compound {
600 functor: *name_id,
601 args,
602 };
603 if self.subst.unify(&term_arg, &constructed) {
604 continue;
605 }
606 return self.backtrack();
607 }
608 }
609 return SolveResult::Error(
610 "functor/3: insufficient arguments".to_string(),
611 );
612 }
613 }
614 }
615 Ok(BuiltinResult::Arg(n_arg, term_arg, result_arg)) => {
616 let wn = self.subst.walk(&n_arg);
617 let wterm = self.subst.walk(&term_arg);
618 if let Term::Integer(n) = wn {
619 let args_list = match &wterm {
620 Term::Compound { args, .. } => Some(args.as_slice()),
621 Term::List { .. } => {
622 None
624 }
625 _ => {
626 return SolveResult::Error(
627 "arg/3: second argument must be compound".to_string(),
628 );
629 }
630 };
631 if let Some(args) = args_list {
632 if n >= 1 && (n as usize) <= args.len() {
633 let arg = args[(n - 1) as usize].clone();
634 if self.subst.unify(&result_arg, &arg) {
635 continue;
636 }
637 return self.backtrack();
638 }
639 return self.backtrack();
640 }
641 if let Term::List { head, tail } = &wterm {
643 match n {
644 1 => {
645 if self.subst.unify(&result_arg, head) {
646 continue;
647 }
648 return self.backtrack();
649 }
650 2 => {
651 if self.subst.unify(&result_arg, tail) {
652 continue;
653 }
654 return self.backtrack();
655 }
656 _ => return self.backtrack(),
657 }
658 }
659 return self.backtrack();
660 }
661 return SolveResult::Error(
662 "arg/3: first argument must be integer".to_string(),
663 );
664 }
665 Ok(BuiltinResult::Univ(term_arg, list_arg)) => {
666 let walked = self.subst.walk(&term_arg);
667 match &walked {
668 Term::Var(_) => {
669 let wlist = self.subst.apply(&list_arg);
671 if let Some(elems) = collect_list(&wlist, &self.interner) {
672 if elems.is_empty() {
673 return SolveResult::Error(
674 "=../2: list must not be empty".to_string(),
675 );
676 }
677 if let Term::Atom(functor_id) = &elems[0] {
678 if elems.len() == 1 {
679 if self.subst.unify(&term_arg, &Term::Atom(*functor_id))
680 {
681 continue;
682 }
683 } else {
684 let constructed = Term::Compound {
685 functor: *functor_id,
686 args: elems[1..].to_vec(),
687 };
688 if self.subst.unify(&term_arg, &constructed) {
689 continue;
690 }
691 }
692 } else if elems.len() == 1 {
693 if matches!(&elems[0], Term::Var(_)) {
695 return SolveResult::Error(
696 "=../2: instantiation error - element must be bound".to_string(),
697 );
698 }
699 if self.subst.unify(&term_arg, &elems[0]) {
700 continue;
701 }
702 } else {
703 return SolveResult::Error(
704 "=../2: functor must be an atom when arity > 0"
705 .to_string(),
706 );
707 }
708 return self.backtrack();
709 }
710 return SolveResult::Error(
711 "=../2: second argument must be a list".to_string(),
712 );
713 }
714 Term::Atom(id) => {
715 let elems = vec![Term::Atom(*id)];
716 let list = build_list(elems, &self.interner);
717 if self.subst.unify(&list_arg, &list) {
718 continue;
719 }
720 return self.backtrack();
721 }
722 Term::Integer(_) | Term::Float(_) => {
723 let elems = vec![walked.clone()];
724 let list = build_list(elems, &self.interner);
725 if self.subst.unify(&list_arg, &list) {
726 continue;
727 }
728 return self.backtrack();
729 }
730 Term::Compound { functor, args } => {
731 let mut elems = vec![Term::Atom(*functor)];
732 elems.extend(args.clone());
733 let list = build_list(elems, &self.interner);
734 if self.subst.unify(&list_arg, &list) {
735 continue;
736 }
737 return self.backtrack();
738 }
739 Term::List { head, tail } => {
740 let dot_id = self.interner.intern(".");
741 let elems = vec![Term::Atom(dot_id), *head.clone(), *tail.clone()];
742 let list = build_list(elems, &self.interner);
743 if self.subst.unify(&list_arg, &list) {
744 continue;
745 }
746 return self.backtrack();
747 }
748 }
749 }
750 Ok(BuiltinResult::Between(low_arg, high_arg, x_arg)) => {
751 let wlow = self.subst.walk(&low_arg);
752 let whigh = self.subst.walk(&high_arg);
753 if let (Term::Integer(low), Term::Integer(high)) = (&wlow, &whigh) {
754 if low > high {
755 return self.backtrack();
756 }
757 let wx = self.subst.walk(&x_arg);
759 if let Term::Integer(x_val) = &wx {
760 if *x_val >= *low && *x_val <= *high {
761 continue;
762 }
763 return self.backtrack();
764 }
765 let mark = self.subst.trail_mark();
766 let saved_counter = self.var_counter;
767 if *low < *high {
769 let new_low = Term::Integer(match low.checked_add(1) {
770 Some(v) => v,
771 None => {
772 return SolveResult::Error(
773 "between/3: integer overflow".to_string(),
774 )
775 }
776 });
777 let between_functor = self.interner.intern("between");
778 let alt_goal = Term::Compound {
779 functor: between_functor,
780 args: vec![new_low, whigh.clone(), x_arg.clone()],
781 };
782 let mut alt_goals = VecDeque::from(vec![alt_goal]);
783 alt_goals.extend(goals.iter().cloned());
784 self.choice_stack.push(ChoicePoint {
785 goals: alt_goals,
786 untried: vec![],
787 trail_mark: mark,
788 var_counter: saved_counter,
789 cut_barrier: false,
790 disjunction: true,
791 });
792 }
793 if self.subst.unify(&x_arg, &Term::Integer(*low)) {
794 continue;
795 }
796 return self.backtrack();
797 }
798 return SolveResult::Error(
799 "between/3: first two arguments must be integers".to_string(),
800 );
801 }
802 Ok(BuiltinResult::CopyTerm(original, copy)) => {
803 let walked = self.subst.walk(&original);
804 let copied = self.copy_term_fresh(&walked);
805 if self.subst.unify(©, &copied) {
806 continue;
807 }
808 return self.backtrack();
809 }
810 Ok(BuiltinResult::Succ(x_arg, s_arg)) => {
811 let wx = self.subst.walk(&x_arg);
812 let ws = self.subst.walk(&s_arg);
813 match (&wx, &ws) {
814 (Term::Integer(x), _) if *x >= 0 => match x.checked_add(1) {
815 Some(result) => {
816 if self.subst.unify(&s_arg, &Term::Integer(result)) {
817 continue;
818 }
819 return self.backtrack();
820 }
821 None => {
822 return SolveResult::Error(
823 "succ/2: integer overflow".to_string(),
824 )
825 }
826 },
827 (_, Term::Integer(s)) if *s > 0 => {
828 if self.subst.unify(&x_arg, &Term::Integer(s - 1)) {
829 continue;
830 }
831 return self.backtrack();
832 }
833 (_, Term::Integer(s)) if *s == 0 => {
834 return self.backtrack();
836 }
837 (Term::Integer(_), _) => {
838 return SolveResult::Error(
839 "succ/2: argument must be non-negative".to_string(),
840 );
841 }
842 (_, Term::Integer(_)) => {
843 return SolveResult::Error(
845 "succ/2: successor must be non-negative".to_string(),
846 );
847 }
848 _ => {
849 return SolveResult::Error(
850 "succ/2: at least one argument must be an integer".to_string(),
851 );
852 }
853 }
854 }
855 Ok(BuiltinResult::Plus(x_arg, y_arg, z_arg)) => {
856 let wx = self.subst.walk(&x_arg);
857 let wy = self.subst.walk(&y_arg);
858 let wz = self.subst.walk(&z_arg);
859 match (&wx, &wy, &wz) {
860 (Term::Integer(x), Term::Integer(y), _) => match x.checked_add(*y) {
861 Some(result) => {
862 if self.subst.unify(&z_arg, &Term::Integer(result)) {
863 continue;
864 }
865 return self.backtrack();
866 }
867 None => {
868 return SolveResult::Error(
869 "plus/3: integer overflow".to_string(),
870 )
871 }
872 },
873 (Term::Integer(x), _, Term::Integer(z)) => match z.checked_sub(*x) {
874 Some(result) => {
875 if self.subst.unify(&y_arg, &Term::Integer(result)) {
876 continue;
877 }
878 return self.backtrack();
879 }
880 None => {
881 return SolveResult::Error(
882 "plus/3: integer overflow".to_string(),
883 )
884 }
885 },
886 (_, Term::Integer(y), Term::Integer(z)) => match z.checked_sub(*y) {
887 Some(result) => {
888 if self.subst.unify(&x_arg, &Term::Integer(result)) {
889 continue;
890 }
891 return self.backtrack();
892 }
893 None => {
894 return SolveResult::Error(
895 "plus/3: integer overflow".to_string(),
896 )
897 }
898 },
899 _ => {
900 return SolveResult::Error(
901 "plus/3: at least two arguments must be integers".to_string(),
902 );
903 }
904 }
905 }
906 Ok(BuiltinResult::MSort(list_arg, sorted_arg)) => {
907 let wlist = self.subst.apply(&list_arg);
908 if let Some(mut elems) = collect_list(&wlist, &self.interner) {
909 elems.sort_by(|a, b| term_compare(a, b, &self.interner));
910 let sorted = build_list(elems, &self.interner);
911 if self.subst.unify(&sorted_arg, &sorted) {
912 continue;
913 }
914 return self.backtrack();
915 }
916 return SolveResult::Error(
917 "msort/2: first argument must be a list".to_string(),
918 );
919 }
920 Ok(BuiltinResult::Sort(list_arg, sorted_arg)) => {
921 let wlist = self.subst.apply(&list_arg);
922 if let Some(mut elems) = collect_list(&wlist, &self.interner) {
923 elems.sort_by(|a, b| term_compare(a, b, &self.interner));
924 elems.dedup_by(|a, b| {
925 term_compare(a, b, &self.interner) == std::cmp::Ordering::Equal
926 });
927 let sorted = build_list(elems, &self.interner);
928 if self.subst.unify(&sorted_arg, &sorted) {
929 continue;
930 }
931 return self.backtrack();
932 }
933 return SolveResult::Error(
934 "sort/2: first argument must be a list".to_string(),
935 );
936 }
937 Ok(BuiltinResult::NumberChars(num_arg, chars_arg)) => {
938 let wnum = self.subst.walk(&num_arg);
939 match &wnum {
940 Term::Integer(n) => {
941 let s = n.to_string();
942 let nil_id =
943 self.interner.lookup("[]").expect("[] must be interned");
944 let mut list = Term::Atom(nil_id);
945 for ch in s.chars().rev() {
946 let ch_id = self.interner.intern(&ch.to_string());
947 list = Term::List {
948 head: Box::new(Term::Atom(ch_id)),
949 tail: Box::new(list),
950 };
951 }
952 if self.subst.unify(&chars_arg, &list) {
953 continue;
954 }
955 return self.backtrack();
956 }
957 Term::Float(f) => {
958 let s = format_float(*f);
959 let nil_id =
960 self.interner.lookup("[]").expect("[] must be interned");
961 let mut list = Term::Atom(nil_id);
962 for ch in s.chars().rev() {
963 let ch_id = self.interner.intern(&ch.to_string());
964 list = Term::List {
965 head: Box::new(Term::Atom(ch_id)),
966 tail: Box::new(list),
967 };
968 }
969 if self.subst.unify(&chars_arg, &list) {
970 continue;
971 }
972 return self.backtrack();
973 }
974 Term::Var(_) => {
975 let wchars = self.subst.apply(&chars_arg);
977 if let Some(elems) = collect_list(&wchars, &self.interner) {
978 let s: Option<String> = elems
979 .iter()
980 .map(|e| match e {
981 Term::Atom(id) => {
982 let ch = self.interner.resolve(*id);
983 if ch.chars().count() == 1 {
984 Some(ch.to_string())
985 } else {
986 None
987 }
988 }
989 _ => None,
990 })
991 .collect();
992 match s {
993 Some(s) => {
994 if let Ok(n) = s.parse::<i64>() {
995 if self.subst.unify(&num_arg, &Term::Integer(n)) {
996 continue;
997 }
998 return self.backtrack();
999 } else if let Ok(f) = s.parse::<f64>() {
1000 if f.is_nan() || f.is_infinite() {
1001 return SolveResult::Error(
1002 "number_chars/2: invalid number syntax"
1003 .to_string(),
1004 );
1005 }
1006 if self.subst.unify(&num_arg, &Term::Float(f)) {
1007 continue;
1008 }
1009 return self.backtrack();
1010 }
1011 return SolveResult::Error(
1012 "number_chars/2: invalid number syntax".to_string(),
1013 );
1014 }
1015 None => {
1016 return SolveResult::Error(
1017 "number_chars/2: list elements must be single-character atoms".to_string(),
1018 );
1019 }
1020 }
1021 }
1022 return SolveResult::Error(
1023 "number_chars/2: at least one argument must be bound"
1024 .to_string(),
1025 );
1026 }
1027 _ => {
1028 return SolveResult::Error(
1029 "number_chars/2: first argument must be a number".to_string(),
1030 );
1031 }
1032 }
1033 }
1034 Ok(BuiltinResult::NumberCodes(num_arg, codes_arg)) => {
1035 let wnum = self.subst.walk(&num_arg);
1036 match &wnum {
1037 Term::Integer(n) => {
1038 let s = n.to_string();
1039 let nil_id =
1040 self.interner.lookup("[]").expect("[] must be interned");
1041 let mut list = Term::Atom(nil_id);
1042 for ch in s.chars().rev() {
1043 list = Term::List {
1044 head: Box::new(Term::Integer(ch as i64)),
1045 tail: Box::new(list),
1046 };
1047 }
1048 if self.subst.unify(&codes_arg, &list) {
1049 continue;
1050 }
1051 return self.backtrack();
1052 }
1053 Term::Float(f) => {
1054 let s = format_float(*f);
1055 let nil_id =
1056 self.interner.lookup("[]").expect("[] must be interned");
1057 let mut list = Term::Atom(nil_id);
1058 for ch in s.chars().rev() {
1059 list = Term::List {
1060 head: Box::new(Term::Integer(ch as i64)),
1061 tail: Box::new(list),
1062 };
1063 }
1064 if self.subst.unify(&codes_arg, &list) {
1065 continue;
1066 }
1067 return self.backtrack();
1068 }
1069 Term::Var(_) => {
1070 let wcodes = self.subst.apply(&codes_arg);
1072 if let Some(elems) = collect_list(&wcodes, &self.interner) {
1073 let s: Option<String> = elems
1074 .iter()
1075 .map(|e| {
1076 if let Term::Integer(code) = e {
1077 if *code >= 0 && *code <= 0x10FFFF {
1078 char::from_u32(*code as u32)
1079 .map(|c| c.to_string())
1080 } else {
1081 None
1082 }
1083 } else {
1084 None
1085 }
1086 })
1087 .collect();
1088 match s {
1089 Some(s) => {
1090 if let Ok(n) = s.parse::<i64>() {
1091 if self.subst.unify(&num_arg, &Term::Integer(n)) {
1092 continue;
1093 }
1094 return self.backtrack();
1095 } else if let Ok(f) = s.parse::<f64>() {
1096 if f.is_nan() || f.is_infinite() {
1097 return SolveResult::Error(
1098 "number_codes/2: invalid number syntax"
1099 .to_string(),
1100 );
1101 }
1102 if self.subst.unify(&num_arg, &Term::Float(f)) {
1103 continue;
1104 }
1105 return self.backtrack();
1106 }
1107 return SolveResult::Error(
1108 "number_codes/2: invalid number syntax".to_string(),
1109 );
1110 }
1111 None => {
1112 return SolveResult::Error(
1113 "number_codes/2: list elements must be valid character codes".to_string(),
1114 );
1115 }
1116 }
1117 }
1118 return SolveResult::Error(
1119 "number_codes/2: at least one argument must be bound"
1120 .to_string(),
1121 );
1122 }
1123 _ => {
1124 return SolveResult::Error(
1125 "number_codes/2: first argument must be a number".to_string(),
1126 );
1127 }
1128 }
1129 }
1130 Err(e) => return SolveResult::Error(e),
1131 }
1132 } else {
1133 let candidates = self.db.lookup(&walked_goal);
1135 if candidates.is_empty() {
1136 return self.backtrack();
1137 }
1138
1139 match self.try_clauses(walked_goal, goals, candidates) {
1140 Some(new_goals) => {
1141 goals = new_goals;
1142 continue;
1143 }
1144 None => return self.backtrack(),
1145 }
1146 }
1147 }
1148 }
1149
1150 fn try_clauses(
1152 &mut self,
1153 goal: Term,
1154 rest_goals: VecDeque<Term>,
1155 candidates: Vec<usize>,
1156 ) -> Option<VecDeque<Term>> {
1157 for (i, &clause_idx) in candidates.iter().enumerate() {
1158 let mark = self.subst.trail_mark();
1159 let saved_counter = self.var_counter;
1160
1161 let clause = &self.db.clauses[clause_idx];
1162 let renamed = self.rename_clause(clause);
1163
1164 if self.subst.unify(&goal, &renamed.head) {
1165 let mut new_goals: VecDeque<Term> = VecDeque::from(renamed.body);
1167 new_goals.extend(rest_goals.iter().cloned());
1168
1169 if i + 1 < candidates.len() {
1171 self.choice_stack.push(ChoicePoint {
1172 goals: {
1173 let mut g = VecDeque::from(vec![goal.clone()]);
1174 g.extend(rest_goals);
1175 g
1176 },
1177 untried: candidates[i + 1..].to_vec(),
1178 trail_mark: mark,
1179 var_counter: saved_counter,
1180 cut_barrier: true,
1181 disjunction: false,
1182 });
1183 }
1184
1185 return Some(new_goals);
1186 } else {
1187 self.subst.undo_to(mark);
1189 self.var_counter = saved_counter;
1190 }
1191 }
1192 None
1193 }
1194
1195 fn backtrack(&mut self) -> SolveResult {
1197 while let Some(cp) = self.choice_stack.pop() {
1198 self.subst.undo_to(cp.trail_mark);
1200 self.var_counter = cp.var_counter;
1201
1202 if cp.disjunction {
1203 return self.solve(cp.goals);
1205 }
1206
1207 if cp.untried.is_empty() {
1208 continue;
1210 }
1211
1212 let mut cp_goals = cp.goals;
1214 let goal = cp_goals.pop_front().unwrap();
1215 let rest_goals = cp_goals;
1216 let candidates = cp.untried;
1217
1218 match self.try_clauses(goal, rest_goals, candidates) {
1219 Some(new_goals) => {
1220 return self.solve(new_goals);
1221 }
1222 None => {
1223 continue;
1225 }
1226 }
1227 }
1228 SolveResult::Failure
1229 }
1230
1231 fn try_solve_once(&mut self, goals: Vec<Term>) -> bool {
1234 let mut goal_list = VecDeque::from(goals);
1235 loop {
1236 if goal_list.is_empty() {
1237 return true;
1238 }
1239
1240 self.steps += 1;
1241 if self.steps > self.max_depth {
1242 self.steps_exceeded = true;
1243 return false;
1244 }
1245
1246 let goal = goal_list.pop_front().unwrap();
1247 let walked_goal = self.subst.walk(&goal);
1248
1249 if is_builtin(&walked_goal, &self.interner) {
1250 match exec_builtin(&walked_goal, &mut self.subst, &self.interner) {
1251 Ok(BuiltinResult::Success) => continue,
1252 Ok(BuiltinResult::Failure) => return false,
1253 Ok(BuiltinResult::Cut) => {
1254 self.cut_in_try_solve = true;
1255 continue;
1256 }
1257 Ok(BuiltinResult::NegationAsFailure(inner)) => {
1258 let mark = self.subst.trail_mark();
1259 let saved_counter = self.var_counter;
1260 let inner_result = self.try_solve_once(vec![inner]);
1261 self.subst.undo_to(mark);
1262 self.var_counter = saved_counter;
1263 if self.steps_exceeded {
1265 return false;
1266 }
1267 if inner_result {
1268 return false;
1269 }
1270 continue;
1271 }
1272 Ok(BuiltinResult::Conjunction(a, b)) => {
1273 goal_list.push_front(b);
1274 goal_list.push_front(a);
1275 continue;
1276 }
1277 Ok(BuiltinResult::Disjunction(left, right)) => {
1278 let mark = self.subst.trail_mark();
1280 let saved_counter = self.var_counter;
1281 let mut left_goals = vec![left];
1282 left_goals.extend(goal_list.iter().cloned());
1283 if self.try_solve_once(left_goals) {
1284 return true;
1285 }
1286 self.subst.undo_to(mark);
1287 self.var_counter = saved_counter;
1288 goal_list.push_front(right);
1290 continue;
1291 }
1292 Ok(BuiltinResult::IfThenElse(cond, then, else_branch)) => {
1293 let mark = self.subst.trail_mark();
1294 let saved_counter = self.var_counter;
1295 if self.try_solve_once(vec![cond]) {
1296 goal_list.push_front(then);
1298 continue;
1299 } else {
1300 self.subst.undo_to(mark);
1301 self.var_counter = saved_counter;
1302 goal_list.push_front(else_branch);
1303 continue;
1304 }
1305 }
1306 Ok(BuiltinResult::IfThen(cond, then)) => {
1307 let mark = self.subst.trail_mark();
1308 let saved_counter = self.var_counter;
1309 if self.try_solve_once(vec![cond]) {
1310 goal_list.push_front(then);
1312 continue;
1313 } else {
1314 self.subst.undo_to(mark);
1315 self.var_counter = saved_counter;
1316 return false;
1317 }
1318 }
1319 Ok(BuiltinResult::FindAll(template, goal, result_var)) => {
1320 match self.exec_findall(template, goal) {
1321 Ok(result_list) => {
1322 if self.subst.unify(&result_var, &result_list) {
1323 continue;
1324 } else {
1325 return false;
1326 }
1327 }
1328 Err(_) => return false,
1329 }
1330 }
1331 Ok(BuiltinResult::Once(inner_goal)) => {
1332 let walked = self.subst.walk(&inner_goal);
1334 goal_list.push_front(walked);
1335 continue;
1336 }
1337 Ok(BuiltinResult::Call(inner_goal)) => {
1338 let walked = self.subst.walk(&inner_goal);
1339 goal_list.push_front(walked);
1340 continue;
1341 }
1342 Ok(BuiltinResult::AtomLength(atom_arg, len_arg)) => {
1343 let walked = self.subst.walk(&atom_arg);
1344 if let Term::Atom(id) = walked {
1345 let len = self.interner.resolve(id).chars().count() as i64;
1346 if self.subst.unify(&len_arg, &Term::Integer(len)) {
1347 continue;
1348 }
1349 }
1350 return false;
1351 }
1352 Ok(BuiltinResult::AtomConcat(a_arg, b_arg, result_arg)) => {
1353 let a = self.subst.walk(&a_arg);
1354 let b = self.subst.walk(&b_arg);
1355 if let (Term::Atom(id_a), Term::Atom(id_b)) = (&a, &b) {
1356 let s = format!(
1357 "{}{}",
1358 self.interner.resolve(*id_a),
1359 self.interner.resolve(*id_b)
1360 );
1361 let result_id = self.interner.intern(&s);
1362 if self.subst.unify(&result_arg, &Term::Atom(result_id)) {
1363 continue;
1364 }
1365 }
1366 return false;
1367 }
1368 Ok(BuiltinResult::AtomChars(atom_arg, list_arg)) => {
1369 let walked = self.subst.walk(&atom_arg);
1370 if let Term::Atom(id) = walked {
1371 let name_str = self.interner.resolve(id).to_string();
1372 let nil_id = self.interner.lookup("[]").expect("[] must be interned");
1373 let mut list = Term::Atom(nil_id);
1374 for ch in name_str.chars().rev() {
1375 let ch_id = self.interner.intern(&ch.to_string());
1376 list = Term::List {
1377 head: Box::new(Term::Atom(ch_id)),
1378 tail: Box::new(list),
1379 };
1380 }
1381 if self.subst.unify(&list_arg, &list) {
1382 continue;
1383 }
1384 } else if let Term::Var(_) = walked {
1385 let wlist = self.subst.apply(&list_arg);
1386 if let Some(elems) = collect_list(&wlist, &self.interner) {
1387 let s: Option<String> = elems
1388 .iter()
1389 .map(|e| {
1390 if let Term::Atom(id) = e {
1391 let ch = self.interner.resolve(*id);
1392 if ch.chars().count() == 1 {
1393 Some(ch.to_string())
1394 } else {
1395 None
1396 }
1397 } else {
1398 None
1399 }
1400 })
1401 .collect();
1402 if let Some(s) = s {
1403 let atom_id = self.interner.intern(&s);
1404 if self.subst.unify(&atom_arg, &Term::Atom(atom_id)) {
1405 continue;
1406 }
1407 }
1408 }
1409 }
1410 return false;
1411 }
1412 Ok(BuiltinResult::Between(low_arg, high_arg, x_arg)) => {
1413 let wlow = self.subst.walk(&low_arg);
1416 let whigh = self.subst.walk(&high_arg);
1417 if let (Term::Integer(low), Term::Integer(high)) = (&wlow, &whigh) {
1418 let wx = self.subst.walk(&x_arg);
1420 if let Term::Integer(x_val) = &wx {
1421 if *x_val >= *low && *x_val <= *high {
1422 let remaining: Vec<Term> = goal_list.iter().cloned().collect();
1423 return self.try_solve_once(remaining);
1424 }
1425 return false;
1426 }
1427 let remaining: Vec<Term> = goal_list.iter().cloned().collect();
1429 for val in *low..=*high {
1430 self.steps += 1;
1431 if self.steps > self.max_depth {
1432 self.steps_exceeded = true;
1433 return false;
1434 }
1435 let mark = self.subst.trail_mark();
1436 let saved_counter = self.var_counter;
1437 if self.subst.unify(&x_arg, &Term::Integer(val)) {
1438 if self.try_solve_once(remaining.clone()) {
1439 return true;
1440 }
1441 }
1442 self.subst.undo_to(mark);
1443 self.var_counter = saved_counter;
1444 }
1445 return false;
1446 }
1447 return false;
1448 }
1449 Ok(other) => {
1450 if let Some(success) = self.try_exec_misc(other, &mut goal_list) {
1452 if success {
1453 continue;
1454 } else {
1455 return false;
1456 }
1457 }
1458 return false;
1459 }
1460 Err(_) => return false,
1461 }
1462 }
1463
1464 let candidates = self.db.lookup(&walked_goal);
1465 for &clause_idx in &candidates {
1466 let mark = self.subst.trail_mark();
1467 let saved_counter = self.var_counter;
1468
1469 let clause = &self.db.clauses[clause_idx];
1470 let renamed = self.rename_clause(clause);
1471
1472 if self.subst.unify(&walked_goal, &renamed.head) {
1473 let mut new_goals = renamed.body;
1474 new_goals.extend(goal_list.clone());
1475 let saved_cut = self.cut_in_try_solve;
1476 self.cut_in_try_solve = false;
1477 if self.try_solve_once(new_goals) {
1478 self.cut_in_try_solve = saved_cut;
1479 return true;
1480 }
1481 if self.cut_in_try_solve {
1482 self.cut_in_try_solve = saved_cut;
1484 self.subst.undo_to(mark);
1485 self.var_counter = saved_counter;
1486 return false;
1487 }
1488 self.cut_in_try_solve = saved_cut;
1489 }
1490 self.subst.undo_to(mark);
1491 self.var_counter = saved_counter;
1492 }
1493 return false;
1494 }
1495 }
1496
1497 fn exec_findall(&mut self, template: Term, goal: Term) -> Result<Term, String> {
1499 let mark = self.subst.trail_mark();
1500 let saved_counter = self.var_counter;
1501 let saved_stack_len = self.choice_stack.len();
1502
1503 let mut collected = Vec::new();
1504 self.try_solve_collecting(VecDeque::from(vec![goal]), &template, &mut collected);
1505
1506 let truncated = self.steps > self.max_depth || self.steps_exceeded;
1508
1509 self.subst.undo_to(mark);
1511 self.var_counter = saved_counter;
1512 self.choice_stack.truncate(saved_stack_len);
1513
1514 if truncated {
1515 self.steps_exceeded = false;
1516 return Err(format!(
1517 "findall: step limit ({}) exceeded during collection",
1518 self.max_depth
1519 ));
1520 }
1521
1522 let nil_id = self.interner.lookup("[]").expect("[] must be interned");
1524 let mut result = Term::Atom(nil_id);
1525 for term in collected.into_iter().rev() {
1526 result = Term::List {
1527 head: Box::new(term),
1528 tail: Box::new(result),
1529 };
1530 }
1531
1532 Ok(result)
1533 }
1534
1535 fn try_solve_collecting(
1537 &mut self,
1538 goals: VecDeque<Term>,
1539 template: &Term,
1540 results: &mut Vec<Term>,
1541 ) -> bool {
1542 if goals.is_empty() {
1543 results.push(self.subst.apply(template));
1544 return true;
1545 }
1546
1547 self.steps += 1;
1548 if self.steps > self.max_depth {
1549 self.steps_exceeded = true;
1550 return false;
1551 }
1552
1553 let mut goal_list = goals;
1554 let goal = goal_list.pop_front().unwrap();
1555 let walked_goal = self.subst.walk(&goal);
1556
1557 if is_builtin(&walked_goal, &self.interner) {
1558 match exec_builtin(&walked_goal, &mut self.subst, &self.interner) {
1559 Ok(BuiltinResult::Success) => {
1560 return self.try_solve_collecting(goal_list, template, results);
1561 }
1562 Ok(BuiltinResult::Cut) => {
1563 self.cut_in_try_solve = true;
1564 return self.try_solve_collecting(goal_list, template, results);
1565 }
1566 Ok(BuiltinResult::Conjunction(a, b)) => {
1567 goal_list.push_front(b);
1568 goal_list.push_front(a);
1569 return self.try_solve_collecting(goal_list, template, results);
1570 }
1571 Ok(BuiltinResult::NegationAsFailure(inner)) => {
1572 let mark = self.subst.trail_mark();
1573 let saved_counter = self.var_counter;
1574 let inner_result = self.try_solve_once(vec![inner]);
1575 self.subst.undo_to(mark);
1576 self.var_counter = saved_counter;
1577 if self.steps_exceeded {
1579 return false;
1580 }
1581 if inner_result {
1582 return false;
1583 }
1584 return self.try_solve_collecting(goal_list, template, results);
1585 }
1586 Ok(BuiltinResult::Disjunction(left, right)) => {
1587 let mark = self.subst.trail_mark();
1589 let saved_counter = self.var_counter;
1590 let mut left_goals = VecDeque::from(vec![left]);
1591 left_goals.extend(goal_list.iter().cloned());
1592 let found_left = self.try_solve_collecting(left_goals, template, results);
1593 self.subst.undo_to(mark);
1594 self.var_counter = saved_counter;
1595 let mut right_goals = VecDeque::from(vec![right]);
1597 right_goals.extend(goal_list);
1598 let found_right = self.try_solve_collecting(right_goals, template, results);
1599 return found_left || found_right;
1600 }
1601 Ok(BuiltinResult::IfThenElse(cond, then, else_branch)) => {
1602 let mark = self.subst.trail_mark();
1603 let saved_counter = self.var_counter;
1604 if self.try_solve_once(vec![cond]) {
1605 goal_list.push_front(then);
1607 return self.try_solve_collecting(goal_list, template, results);
1608 } else {
1609 self.subst.undo_to(mark);
1610 self.var_counter = saved_counter;
1611 goal_list.push_front(else_branch);
1612 return self.try_solve_collecting(goal_list, template, results);
1613 }
1614 }
1615 Ok(BuiltinResult::IfThen(cond, then)) => {
1616 let mark = self.subst.trail_mark();
1617 let saved_counter = self.var_counter;
1618 if self.try_solve_once(vec![cond]) {
1619 goal_list.push_front(then);
1621 return self.try_solve_collecting(goal_list, template, results);
1622 } else {
1623 self.subst.undo_to(mark);
1624 self.var_counter = saved_counter;
1625 return false;
1626 }
1627 }
1628 Ok(BuiltinResult::FindAll(tmpl, inner_goal, result_var)) => {
1629 match self.exec_findall(tmpl, inner_goal) {
1630 Ok(result_list) => {
1631 if self.subst.unify(&result_var, &result_list) {
1632 return self.try_solve_collecting(goal_list, template, results);
1633 }
1634 return false;
1635 }
1636 Err(_) => return false,
1637 }
1638 }
1639 Ok(BuiltinResult::Once(inner_goal)) => {
1640 let walked = self.subst.walk(&inner_goal);
1641 if self.try_solve_once(vec![walked]) {
1642 return self.try_solve_collecting(goal_list, template, results);
1643 }
1644 return false;
1645 }
1646 Ok(BuiltinResult::Call(inner_goal)) => {
1647 let walked = self.subst.walk(&inner_goal);
1648 goal_list.push_front(walked);
1649 return self.try_solve_collecting(goal_list, template, results);
1650 }
1651 Ok(BuiltinResult::AtomLength(atom_arg, len_arg)) => {
1652 let walked = self.subst.walk(&atom_arg);
1653 if let Term::Atom(id) = walked {
1654 let len = self.interner.resolve(id).chars().count() as i64;
1655 if self.subst.unify(&len_arg, &Term::Integer(len)) {
1656 return self.try_solve_collecting(goal_list, template, results);
1657 }
1658 }
1659 return false;
1660 }
1661 Ok(BuiltinResult::AtomConcat(a_arg, b_arg, result_arg)) => {
1662 let a = self.subst.walk(&a_arg);
1663 let b = self.subst.walk(&b_arg);
1664 if let (Term::Atom(id_a), Term::Atom(id_b)) = (&a, &b) {
1665 let s = format!(
1666 "{}{}",
1667 self.interner.resolve(*id_a),
1668 self.interner.resolve(*id_b)
1669 );
1670 let result_id = self.interner.intern(&s);
1671 if self.subst.unify(&result_arg, &Term::Atom(result_id)) {
1672 return self.try_solve_collecting(goal_list, template, results);
1673 }
1674 }
1675 return false;
1676 }
1677 Ok(BuiltinResult::AtomChars(atom_arg, list_arg)) => {
1678 let walked = self.subst.walk(&atom_arg);
1679 if let Term::Atom(id) = walked {
1680 let name_str = self.interner.resolve(id).to_string();
1681 let nil_id = self.interner.lookup("[]").expect("[] must be interned");
1682 let mut list = Term::Atom(nil_id);
1683 for ch in name_str.chars().rev() {
1684 let ch_id = self.interner.intern(&ch.to_string());
1685 list = Term::List {
1686 head: Box::new(Term::Atom(ch_id)),
1687 tail: Box::new(list),
1688 };
1689 }
1690 if self.subst.unify(&list_arg, &list) {
1691 return self.try_solve_collecting(goal_list, template, results);
1692 }
1693 } else if let Term::Var(_) = walked {
1694 let wlist = self.subst.apply(&list_arg);
1695 if let Some(elems) = collect_list(&wlist, &self.interner) {
1696 let s: Option<String> = elems
1697 .iter()
1698 .map(|e| {
1699 if let Term::Atom(id) = e {
1700 let ch = self.interner.resolve(*id);
1701 if ch.chars().count() == 1 {
1702 Some(ch.to_string())
1703 } else {
1704 None
1705 }
1706 } else {
1707 None
1708 }
1709 })
1710 .collect();
1711 if let Some(s) = s {
1712 let atom_id = self.interner.intern(&s);
1713 if self.subst.unify(&atom_arg, &Term::Atom(atom_id)) {
1714 return self.try_solve_collecting(goal_list, template, results);
1715 }
1716 }
1717 }
1718 }
1719 return false;
1720 }
1721 Ok(BuiltinResult::Failure) => return false,
1722 Ok(BuiltinResult::Between(low_arg, high_arg, x_arg)) => {
1723 let wlow = self.subst.walk(&low_arg);
1725 let whigh = self.subst.walk(&high_arg);
1726 if let (Term::Integer(low), Term::Integer(high)) = (&wlow, &whigh) {
1727 let wx = self.subst.walk(&x_arg);
1729 if let Term::Integer(x_val) = &wx {
1730 if *x_val >= *low && *x_val <= *high {
1731 let remaining = goal_list.clone();
1732 return self.try_solve_collecting(remaining, template, results);
1733 }
1734 return false;
1735 }
1736 let mut found_any = false;
1738 let remaining = goal_list.clone();
1739 for val in *low..=*high {
1740 self.steps += 1;
1741 if self.steps > self.max_depth {
1742 self.steps_exceeded = true;
1743 return found_any;
1744 }
1745 let mark = self.subst.trail_mark();
1746 let saved_counter = self.var_counter;
1747 if self.subst.unify(&x_arg, &Term::Integer(val)) {
1748 if self.try_solve_collecting(remaining.clone(), template, results) {
1749 found_any = true;
1750 }
1751 }
1752 self.subst.undo_to(mark);
1753 self.var_counter = saved_counter;
1754 }
1755 return found_any;
1756 }
1757 return false;
1758 }
1759 Ok(other) => {
1760 if let Some(success) = self.try_exec_misc(other, &mut goal_list) {
1762 if success {
1763 return self.try_solve_collecting(goal_list, template, results);
1764 }
1765 }
1766 return false;
1767 }
1768 Err(_) => return false,
1769 }
1770 }
1771
1772 let candidates = self.db.lookup(&walked_goal);
1773 let mut found_any = false;
1774 for &clause_idx in &candidates {
1775 let mark = self.subst.trail_mark();
1776 let saved_counter = self.var_counter;
1777
1778 let clause = &self.db.clauses[clause_idx];
1779 let renamed = self.rename_clause(clause);
1780
1781 let saved_cut = self.cut_in_try_solve;
1782 self.cut_in_try_solve = false;
1783 if self.subst.unify(&walked_goal, &renamed.head) {
1784 let mut new_goals: VecDeque<Term> = VecDeque::from(renamed.body);
1785 new_goals.extend(goal_list.iter().cloned());
1786 if self.try_solve_collecting(new_goals, template, results) {
1787 found_any = true;
1788 }
1789 }
1790 if self.cut_in_try_solve {
1792 self.cut_in_try_solve = saved_cut;
1793 self.subst.undo_to(mark);
1794 self.var_counter = saved_counter;
1795 return found_any;
1796 }
1797 self.cut_in_try_solve = saved_cut;
1798 self.subst.undo_to(mark);
1799 self.var_counter = saved_counter;
1800 }
1801 found_any
1802 }
1803
1804 fn rename_clause(&mut self, clause: &Clause) -> Clause {
1806 let mut var_map: FnvHashMap<VarId, VarId> = FnvHashMap::default();
1807 Clause {
1808 head: self.rename_term(&clause.head, &mut var_map),
1809 body: clause
1810 .body
1811 .iter()
1812 .map(|t| self.rename_term(t, &mut var_map))
1813 .collect(),
1814 }
1815 }
1816
1817 fn rename_term(&mut self, term: &Term, var_map: &mut FnvHashMap<VarId, VarId>) -> Term {
1818 match term {
1819 Term::Var(id) => {
1820 let new_id = *var_map.entry(*id).or_insert_with(|| {
1821 let fresh = self.var_counter;
1822 self.var_counter += 1;
1823 fresh
1824 });
1825 Term::Var(new_id)
1826 }
1827 Term::Compound { functor, args } => Term::Compound {
1828 functor: *functor,
1829 args: args.iter().map(|a| self.rename_term(a, var_map)).collect(),
1830 },
1831 Term::List { head, tail } => Term::List {
1832 head: Box::new(self.rename_term(head, var_map)),
1833 tail: Box::new(self.rename_term(tail, var_map)),
1834 },
1835 _ => term.clone(),
1836 }
1837 }
1838
1839 fn try_exec_misc(
1842 &mut self,
1843 result: BuiltinResult,
1844 _goal_list: &mut VecDeque<Term>,
1845 ) -> Option<bool> {
1846 match result {
1847 BuiltinResult::Write(term) => {
1848 let resolved = self.subst.apply(&term);
1849 print!("{}", term_to_string(&resolved, &self.interner));
1850 let _ = std::io::stdout().flush();
1851 Some(true)
1852 }
1853 BuiltinResult::Writeln(term) => {
1854 let resolved = self.subst.apply(&term);
1855 println!("{}", term_to_string(&resolved, &self.interner));
1856 Some(true)
1857 }
1858 BuiltinResult::Nl => {
1859 println!();
1860 Some(true)
1861 }
1862 BuiltinResult::Compare(order_arg, t1, t2) => {
1863 let w1 = self.subst.apply(&t1);
1864 let w2 = self.subst.apply(&t2);
1865 let cmp = term_compare(&w1, &w2, &self.interner);
1866 let order_name = match cmp {
1867 std::cmp::Ordering::Less => "<",
1868 std::cmp::Ordering::Equal => "=",
1869 std::cmp::Ordering::Greater => ">",
1870 };
1871 let order_id = self.interner.intern(order_name);
1872 Some(self.subst.unify(&order_arg, &Term::Atom(order_id)))
1873 }
1874 BuiltinResult::Functor(term_arg, name_arg, arity_arg) => {
1875 let walked = self.subst.walk(&term_arg);
1876 match &walked {
1877 Term::Atom(id) => Some(
1878 self.subst.unify(&name_arg, &Term::Atom(*id))
1879 && self.subst.unify(&arity_arg, &Term::Integer(0)),
1880 ),
1881 Term::Integer(_) | Term::Float(_) => Some(
1882 self.subst.unify(&name_arg, &walked)
1883 && self.subst.unify(&arity_arg, &Term::Integer(0)),
1884 ),
1885 Term::Compound { functor, args } => Some(
1886 self.subst.unify(&name_arg, &Term::Atom(*functor))
1887 && self
1888 .subst
1889 .unify(&arity_arg, &Term::Integer(args.len() as i64)),
1890 ),
1891 Term::List { .. } => {
1892 let dot_id = self.interner.intern(".");
1893 Some(
1894 self.subst.unify(&name_arg, &Term::Atom(dot_id))
1895 && self.subst.unify(&arity_arg, &Term::Integer(2)),
1896 )
1897 }
1898 Term::Var(_) => {
1899 let wname = self.subst.walk(&name_arg);
1900 let warity = self.subst.walk(&arity_arg);
1901 match (&wname, &warity) {
1902 (Term::Atom(name_id), Term::Integer(0)) => {
1903 Some(self.subst.unify(&term_arg, &Term::Atom(*name_id)))
1904 }
1905 (Term::Integer(_) | Term::Float(_), Term::Integer(0)) => {
1906 Some(self.subst.unify(&term_arg, &wname))
1907 }
1908 (Term::Atom(name_id), Term::Integer(arity))
1909 if *arity > 0 && *arity <= 1024 =>
1910 {
1911 let args: Vec<Term> = (0..*arity as u32)
1912 .map(|_| {
1913 let v = self.var_counter;
1914 self.var_counter += 1;
1915 Term::Var(v)
1916 })
1917 .collect();
1918 let constructed = Term::Compound {
1919 functor: *name_id,
1920 args,
1921 };
1922 Some(self.subst.unify(&term_arg, &constructed))
1923 }
1924 _ => Some(false),
1925 }
1926 }
1927 }
1928 }
1929 BuiltinResult::Arg(n_arg, term_arg, result_arg) => {
1930 let wn = self.subst.walk(&n_arg);
1931 let wterm = self.subst.walk(&term_arg);
1932 if let Term::Integer(n) = wn {
1933 if let Term::Compound { args, .. } = &wterm {
1934 if n >= 1 && (n as usize) <= args.len() {
1935 return Some(self.subst.unify(&result_arg, &args[(n - 1) as usize]));
1936 }
1937 }
1938 if let Term::List { head, tail } = &wterm {
1939 return match n {
1940 1 => Some(self.subst.unify(&result_arg, head)),
1941 2 => Some(self.subst.unify(&result_arg, tail)),
1942 _ => Some(false),
1943 };
1944 }
1945 }
1946 Some(false)
1947 }
1948 BuiltinResult::Univ(term_arg, list_arg) => {
1949 let walked = self.subst.walk(&term_arg);
1950 match &walked {
1951 Term::Atom(id) => {
1952 let list = build_list(vec![Term::Atom(*id)], &self.interner);
1953 Some(self.subst.unify(&list_arg, &list))
1954 }
1955 Term::Integer(_) | Term::Float(_) => {
1956 let list = build_list(vec![walked.clone()], &self.interner);
1957 Some(self.subst.unify(&list_arg, &list))
1958 }
1959 Term::Compound { functor, args } => {
1960 let mut elems = vec![Term::Atom(*functor)];
1961 elems.extend(args.clone());
1962 let list = build_list(elems, &self.interner);
1963 Some(self.subst.unify(&list_arg, &list))
1964 }
1965 Term::List { head, tail } => {
1966 let dot_id = self.interner.intern(".");
1967 let elems = vec![Term::Atom(dot_id), *head.clone(), *tail.clone()];
1968 let list = build_list(elems, &self.interner);
1969 Some(self.subst.unify(&list_arg, &list))
1970 }
1971 Term::Var(_) => {
1972 let wlist = self.subst.apply(&list_arg);
1973 if let Some(elems) = collect_list(&wlist, &self.interner) {
1974 if !elems.is_empty() {
1975 if let Term::Atom(fid) = &elems[0] {
1976 if elems.len() == 1 {
1977 return Some(
1978 self.subst.unify(&term_arg, &Term::Atom(*fid)),
1979 );
1980 }
1981 let constructed = Term::Compound {
1982 functor: *fid,
1983 args: elems[1..].to_vec(),
1984 };
1985 return Some(self.subst.unify(&term_arg, &constructed));
1986 } else if elems.len() == 1 {
1987 if matches!(&elems[0], Term::Var(_)) {
1989 return Some(false);
1990 }
1991 return Some(self.subst.unify(&term_arg, &elems[0]));
1992 }
1993 }
1994 }
1995 Some(false)
1996 }
1997 }
1998 }
1999 BuiltinResult::CopyTerm(original, copy) => {
2000 let walked = self.subst.walk(&original);
2001 let copied = self.copy_term_fresh(&walked);
2002 Some(self.subst.unify(©, &copied))
2003 }
2004 BuiltinResult::Succ(x_arg, s_arg) => {
2005 let wx = self.subst.walk(&x_arg);
2006 let ws = self.subst.walk(&s_arg);
2007 match (&wx, &ws) {
2008 (Term::Integer(x), _) if *x >= 0 => match x.checked_add(1) {
2009 Some(result) => Some(self.subst.unify(&s_arg, &Term::Integer(result))),
2010 None => Some(false),
2011 },
2012 (_, Term::Integer(s)) if *s > 0 => {
2013 Some(self.subst.unify(&x_arg, &Term::Integer(s - 1)))
2014 }
2015 _ => Some(false),
2016 }
2017 }
2018 BuiltinResult::Plus(x_arg, y_arg, z_arg) => {
2019 let wx = self.subst.walk(&x_arg);
2020 let wy = self.subst.walk(&y_arg);
2021 let wz = self.subst.walk(&z_arg);
2022 match (&wx, &wy, &wz) {
2023 (Term::Integer(x), Term::Integer(y), _) => match x.checked_add(*y) {
2024 Some(result) => Some(self.subst.unify(&z_arg, &Term::Integer(result))),
2025 None => Some(false),
2026 },
2027 (Term::Integer(x), _, Term::Integer(z)) => match z.checked_sub(*x) {
2028 Some(result) => Some(self.subst.unify(&y_arg, &Term::Integer(result))),
2029 None => Some(false),
2030 },
2031 (_, Term::Integer(y), Term::Integer(z)) => match z.checked_sub(*y) {
2032 Some(result) => Some(self.subst.unify(&x_arg, &Term::Integer(result))),
2033 None => Some(false),
2034 },
2035 _ => Some(false),
2036 }
2037 }
2038 BuiltinResult::MSort(list_arg, sorted_arg) => {
2039 let wlist = self.subst.apply(&list_arg);
2040 if let Some(mut elems) = collect_list(&wlist, &self.interner) {
2041 elems.sort_by(|a, b| term_compare(a, b, &self.interner));
2042 let sorted = build_list(elems, &self.interner);
2043 return Some(self.subst.unify(&sorted_arg, &sorted));
2044 }
2045 Some(false)
2046 }
2047 BuiltinResult::Sort(list_arg, sorted_arg) => {
2048 let wlist = self.subst.apply(&list_arg);
2049 if let Some(mut elems) = collect_list(&wlist, &self.interner) {
2050 elems.sort_by(|a, b| term_compare(a, b, &self.interner));
2051 elems.dedup_by(|a, b| {
2052 term_compare(a, b, &self.interner) == std::cmp::Ordering::Equal
2053 });
2054 let sorted = build_list(elems, &self.interner);
2055 return Some(self.subst.unify(&sorted_arg, &sorted));
2056 }
2057 Some(false)
2058 }
2059 BuiltinResult::NumberChars(num_arg, chars_arg) => {
2060 let wnum = self.subst.walk(&num_arg);
2061 match &wnum {
2062 Term::Integer(n) => {
2063 let s = n.to_string();
2064 let nil_id = self.interner.lookup("[]").expect("[] must be interned");
2065 let mut list = Term::Atom(nil_id);
2066 for ch in s.chars().rev() {
2067 let ch_id = self.interner.intern(&ch.to_string());
2068 list = Term::List {
2069 head: Box::new(Term::Atom(ch_id)),
2070 tail: Box::new(list),
2071 };
2072 }
2073 Some(self.subst.unify(&chars_arg, &list))
2074 }
2075 Term::Float(f) => {
2076 let s = format_float(*f);
2077 let nil_id = self.interner.lookup("[]").expect("[] must be interned");
2078 let mut list = Term::Atom(nil_id);
2079 for ch in s.chars().rev() {
2080 let ch_id = self.interner.intern(&ch.to_string());
2081 list = Term::List {
2082 head: Box::new(Term::Atom(ch_id)),
2083 tail: Box::new(list),
2084 };
2085 }
2086 Some(self.subst.unify(&chars_arg, &list))
2087 }
2088 Term::Var(_) => {
2089 let wchars = self.subst.apply(&chars_arg);
2091 if let Some(elems) = collect_list(&wchars, &self.interner) {
2092 let s: Option<String> = elems
2093 .iter()
2094 .map(|e| match e {
2095 Term::Atom(id) => {
2096 let ch = self.interner.resolve(*id);
2097 if ch.chars().count() == 1 {
2098 Some(ch.to_string())
2099 } else {
2100 None
2101 }
2102 }
2103 _ => None,
2104 })
2105 .collect();
2106 if let Some(s) = s {
2107 if let Ok(n) = s.parse::<i64>() {
2108 return Some(self.subst.unify(&num_arg, &Term::Integer(n)));
2109 } else if let Ok(f) = s.parse::<f64>() {
2110 if !f.is_nan() && !f.is_infinite() {
2111 return Some(self.subst.unify(&num_arg, &Term::Float(f)));
2112 }
2113 }
2114 }
2115 }
2116 Some(false)
2117 }
2118 _ => Some(false),
2119 }
2120 }
2121 BuiltinResult::NumberCodes(num_arg, codes_arg) => {
2122 let wnum = self.subst.walk(&num_arg);
2123 match &wnum {
2124 Term::Integer(n) => {
2125 let s = n.to_string();
2126 let nil_id = self.interner.lookup("[]").expect("[] must be interned");
2127 let mut list = Term::Atom(nil_id);
2128 for ch in s.chars().rev() {
2129 list = Term::List {
2130 head: Box::new(Term::Integer(ch as i64)),
2131 tail: Box::new(list),
2132 };
2133 }
2134 Some(self.subst.unify(&codes_arg, &list))
2135 }
2136 Term::Float(f) => {
2137 let s = format_float(*f);
2138 let nil_id = self.interner.lookup("[]").expect("[] must be interned");
2139 let mut list = Term::Atom(nil_id);
2140 for ch in s.chars().rev() {
2141 list = Term::List {
2142 head: Box::new(Term::Integer(ch as i64)),
2143 tail: Box::new(list),
2144 };
2145 }
2146 Some(self.subst.unify(&codes_arg, &list))
2147 }
2148 Term::Var(_) => {
2149 let wcodes = self.subst.apply(&codes_arg);
2151 if let Some(elems) = collect_list(&wcodes, &self.interner) {
2152 let s: Option<String> = elems
2153 .iter()
2154 .map(|e| {
2155 if let Term::Integer(code) = e {
2156 if *code >= 0 && *code <= 0x10FFFF {
2157 char::from_u32(*code as u32).map(|c| c.to_string())
2158 } else {
2159 None
2160 }
2161 } else {
2162 None
2163 }
2164 })
2165 .collect();
2166 if let Some(s) = s {
2167 if let Ok(n) = s.parse::<i64>() {
2168 return Some(self.subst.unify(&num_arg, &Term::Integer(n)));
2169 } else if let Ok(f) = s.parse::<f64>() {
2170 if !f.is_nan() && !f.is_infinite() {
2171 return Some(self.subst.unify(&num_arg, &Term::Float(f)));
2172 }
2173 }
2174 }
2175 }
2176 Some(false)
2177 }
2178 _ => Some(false),
2179 }
2180 }
2181 _ => None, }
2183 }
2184
2185 fn copy_term_fresh(&mut self, term: &Term) -> Term {
2187 let mut var_map: FnvHashMap<VarId, VarId> = FnvHashMap::default();
2188 self.copy_term_impl(term, &mut var_map)
2189 }
2190
2191 fn copy_term_impl(&mut self, term: &Term, var_map: &mut FnvHashMap<VarId, VarId>) -> Term {
2192 let walked = self.subst.walk(term);
2193 match &walked {
2194 Term::Var(id) => {
2195 let new_id = *var_map.entry(*id).or_insert_with(|| {
2196 let fresh = self.var_counter;
2197 self.var_counter += 1;
2198 fresh
2199 });
2200 Term::Var(new_id)
2201 }
2202 Term::Atom(_) | Term::Integer(_) | Term::Float(_) => walked.clone(),
2203 Term::Compound { functor, args } => Term::Compound {
2204 functor: *functor,
2205 args: args
2206 .iter()
2207 .map(|a| self.copy_term_impl(a, var_map))
2208 .collect(),
2209 },
2210 Term::List { .. } => {
2211 let mut heads = Vec::new();
2214 let mut current_owned = walked;
2215 loop {
2216 match current_owned {
2217 Term::List { head, tail } => {
2218 heads.push(self.copy_term_impl(&head, var_map));
2219 let walked_tail = self.subst.walk(&tail);
2220 match walked_tail {
2221 Term::List { .. } => {
2222 current_owned = walked_tail;
2224 }
2225 _ => {
2226 let final_tail = self.copy_term_impl(&tail, var_map);
2228 let mut result = final_tail;
2229 for h in heads.into_iter().rev() {
2230 result = Term::List {
2231 head: Box::new(h),
2232 tail: Box::new(result),
2233 };
2234 }
2235 return result;
2236 }
2237 }
2238 }
2239 _ => {
2240 let final_tail = self.copy_term_impl(¤t_owned, var_map);
2242 let mut result = final_tail;
2243 for h in heads.into_iter().rev() {
2244 result = Term::List {
2245 head: Box::new(h),
2246 tail: Box::new(result),
2247 };
2248 }
2249 return result;
2250 }
2251 }
2252 }
2253 }
2254 }
2255 }
2256
2257 fn extract_solution(&self) -> Solution {
2259 let mut bindings = Vec::new();
2260 let mut vars: Vec<_> = self.query_vars.iter().collect();
2261 vars.sort_by_key(|(name, _)| name.to_string());
2262 for (name, &var_id) in vars {
2263 if name == "_" {
2264 continue;
2265 }
2266 let resolved = self.subst.apply(&Term::Var(var_id));
2267 bindings.push((name.clone(), resolved));
2268 }
2269 Solution { bindings }
2270 }
2271}
2272
2273pub fn term_to_string(term: &Term, interner: &crate::term::StringInterner) -> String {
2275 match term {
2276 Term::Atom(id) => interner.resolve(*id).to_string(),
2277 Term::Var(id) => format!("_{}", id),
2278 Term::Integer(n) => n.to_string(),
2279 Term::Float(f) => format!("{}", f),
2280 Term::Compound { functor, args } => {
2281 let name = interner.resolve(*functor);
2282 if args.len() == 2 {
2283 match name {
2285 "+" | "-" | "*" | "/" | "mod" | "is" | "=" | "\\=" | "<" | ">" | "=<"
2286 | ">=" | "=:=" | "=\\=" => {
2287 return format!(
2288 "{} {} {}",
2289 term_to_string(&args[0], interner),
2290 name,
2291 term_to_string(&args[1], interner)
2292 );
2293 }
2294 _ => {}
2295 }
2296 }
2297 format!(
2298 "{}({})",
2299 name,
2300 args.iter()
2301 .map(|a| term_to_string(a, interner))
2302 .collect::<Vec<_>>()
2303 .join(", ")
2304 )
2305 }
2306 Term::List { head, tail } => {
2307 let mut elements = vec![term_to_string(head, interner)];
2308 let mut current = tail.as_ref();
2309 loop {
2310 match current {
2311 Term::List { head, tail } => {
2312 elements.push(term_to_string(head, interner));
2313 current = tail;
2314 }
2315 Term::Atom(id) if interner.resolve(*id) == "[]" => {
2316 return format!("[{}]", elements.join(", "));
2317 }
2318 _ => {
2319 return format!(
2320 "[{}|{}]",
2321 elements.join(", "),
2322 term_to_string(current, interner)
2323 );
2324 }
2325 }
2326 }
2327 }
2328 }
2329}
2330
2331#[cfg(test)]
2332mod tests {
2333 use super::*;
2334 use crate::database::CompiledDatabase;
2335 use crate::parser::Parser;
2336
2337 fn query(source: &str, query_str: &str) -> Vec<Solution> {
2338 let mut interner = crate::term::StringInterner::new();
2339 let clauses = Parser::parse_program(source, &mut interner).unwrap();
2340 let (goals, vars) = Parser::parse_query_with_vars(query_str, &mut interner).unwrap();
2341 let db = CompiledDatabase::new(interner, clauses);
2342 let solver = Solver::new(&db, goals, vars);
2343 solver.all_solutions().unwrap()
2344 }
2345
2346 fn query_first_binding(source: &str, query_str: &str, var_name: &str) -> Option<String> {
2347 let mut interner = crate::term::StringInterner::new();
2348 let clauses = Parser::parse_program(source, &mut interner).unwrap();
2349 let (goals, vars) = Parser::parse_query_with_vars(query_str, &mut interner).unwrap();
2350 let db = CompiledDatabase::new(interner, clauses);
2351 let solver = Solver::new(&db, goals, vars);
2352 let (solutions, solver_interner) = solver.all_solutions_with_interner().unwrap();
2353 solutions.first().and_then(|sol| {
2354 sol.bindings
2355 .iter()
2356 .find(|(name, _)| name == var_name)
2357 .map(|(_, term)| term_to_string(term, &solver_interner))
2358 })
2359 }
2360
2361 #[test]
2362 fn test_simple_fact() {
2363 let solutions = query("likes(mary, food).", "likes(mary, food)");
2364 assert_eq!(solutions.len(), 1);
2365 assert!(solutions[0].bindings.is_empty()); }
2367
2368 #[test]
2369 fn test_fact_negative() {
2370 let solutions = query("likes(mary, food).", "likes(mary, beer)");
2371 assert_eq!(solutions.len(), 0);
2372 }
2373
2374 #[test]
2375 fn test_variable_binding() {
2376 let result = query_first_binding("likes(mary, food).", "likes(mary, X)", "X");
2377 assert_eq!(result, Some("food".to_string()));
2378 }
2379
2380 #[test]
2381 fn test_simple_rule() {
2382 let source = "likes(mary, food). happy(X) :- likes(X, food).";
2383 let solutions = query(source, "happy(mary)");
2384 assert_eq!(solutions.len(), 1);
2385 }
2386
2387 #[test]
2388 fn test_multiple_solutions() {
2389 let source = "parent(tom, mary). parent(tom, james). parent(tom, ann).";
2390 let solutions = query(source, "parent(tom, X)");
2391 assert_eq!(solutions.len(), 3);
2392 }
2393
2394 #[test]
2395 fn test_recursive_rule() {
2396 let source = r#"
2397 parent(tom, mary).
2398 parent(mary, ann).
2399 ancestor(X, Y) :- parent(X, Y).
2400 ancestor(X, Y) :- parent(X, Z), ancestor(Z, Y).
2401 "#;
2402 let solutions = query(source, "ancestor(tom, ann)");
2403 assert!(solutions.len() >= 1);
2404 }
2405
2406 #[test]
2407 fn test_grandparent() {
2408 let source = r#"
2409 parent(tom, mary).
2410 parent(mary, ann).
2411 grandparent(X, Z) :- parent(X, Y), parent(Y, Z).
2412 "#;
2413 let result = query_first_binding(source, "grandparent(tom, X)", "X");
2414 assert_eq!(result, Some("ann".to_string()));
2415 }
2416
2417 #[test]
2418 fn test_arithmetic_is() {
2419 let source = "add(X, Y, Z) :- Z is X + Y.";
2420 let result = query_first_binding(source, "add(3, 4, X)", "X");
2421 assert_eq!(result, Some("7".to_string()));
2422 }
2423
2424 #[test]
2425 fn test_arithmetic_multiply() {
2426 let source = "double(X, Y) :- Y is X * 2.";
2427 let result = query_first_binding(source, "double(5, X)", "X");
2428 assert_eq!(result, Some("10".to_string()));
2429 }
2430
2431 #[test]
2432 fn test_comparison_positive() {
2433 let source = "big(X) :- X > 100.";
2434 let solutions = query(source, "big(200)");
2435 assert_eq!(solutions.len(), 1);
2436 }
2437
2438 #[test]
2439 fn test_comparison_negative() {
2440 let source = "big(X) :- X > 100.";
2441 let solutions = query(source, "big(50)");
2442 assert_eq!(solutions.len(), 0);
2443 }
2444
2445 #[test]
2446 fn test_operator_precedence() {
2447 let source = "result(X) :- X is 2 + 3 * 4.";
2448 let result = query_first_binding(source, "result(X)", "X");
2449 assert_eq!(result, Some("14".to_string()));
2450 }
2451
2452 #[test]
2453 fn test_mod_operator() {
2454 let source = "remainder(X, Y, Z) :- Z is X mod Y.";
2455 let result = query_first_binding(source, "remainder(10, 3, X)", "X");
2456 assert_eq!(result, Some("1".to_string()));
2457 }
2458
2459 #[test]
2460 fn test_nested_compound() {
2461 let source = "outer(inner(deep(hello))).";
2462 let solutions = query(source, "outer(inner(deep(hello)))");
2463 assert_eq!(solutions.len(), 1);
2464 }
2465
2466 #[test]
2467 fn test_nested_compound_with_var() {
2468 let source = "outer(inner(deep(hello))).";
2469 let result = query_first_binding(source, "outer(inner(deep(X)))", "X");
2470 assert_eq!(result, Some("hello".to_string()));
2471 }
2472
2473 #[test]
2474 fn test_negation_as_failure() {
2475 let source = r#"
2476 likes(mary, food).
2477 likes(mary, wine).
2478 dislikes(X, Y) :- \+ likes(X, Y).
2479 "#;
2480 let solutions = query(source, "dislikes(mary, beer)");
2481 assert_eq!(solutions.len(), 1);
2482
2483 let solutions = query(source, "dislikes(mary, food)");
2484 assert_eq!(solutions.len(), 0);
2485 }
2486
2487 #[test]
2488 fn test_cut() {
2489 let source = r#"
2490 max(X, Y, X) :- X >= Y, !.
2491 max(X, Y, Y).
2492 "#;
2493 let result = query_first_binding(source, "max(3, 5, Z)", "Z");
2495 assert_eq!(result, Some("5".to_string()));
2496
2497 let result = query_first_binding(source, "max(5, 3, Z)", "Z");
2499 assert_eq!(result, Some("5".to_string()));
2500 }
2501
2502 #[test]
2503 fn test_index_multi_predicate() {
2504 let source = "color(red). color(blue). color(green). shape(circle). shape(square).";
2505 let solutions = query(source, "shape(circle)");
2506 assert_eq!(solutions.len(), 1);
2507
2508 let solutions = query(source, "color(circle)");
2509 assert_eq!(solutions.len(), 0);
2510 }
2511
2512 #[test]
2513 fn test_index_first_arg() {
2514 let source = r#"
2515 component(engine, piston).
2516 component(engine, crankshaft).
2517 component(engine, valve).
2518 component(brake, pad).
2519 component(brake, rotor).
2520 component(wheel, tire).
2521 component(wheel, rim).
2522 "#;
2523 let result = query_first_binding(source, "component(brake, X)", "X");
2524 assert_eq!(result, Some("pad".to_string()));
2525
2526 let solutions = query(source, "component(engine, X)");
2527 assert_eq!(solutions.len(), 3);
2528
2529 let solutions = query(source, "component(transmission, X)");
2530 assert_eq!(solutions.len(), 0);
2531 }
2532
2533 #[test]
2534 fn test_index_variable_fallback() {
2535 let source = r#"
2536 component(engine, piston).
2537 component(brake, pad).
2538 component(wheel, tire).
2539 "#;
2540 let solutions = query(source, "component(X, tire)");
2541 assert!(solutions.len() >= 1);
2542 let result = query_first_binding(source, "component(X, tire)", "X");
2543 assert_eq!(result, Some("wheel".to_string()));
2544 }
2545
2546 #[test]
2547 fn test_mixed_ground_var_clauses() {
2548 let source = r#"
2549 lookup(a, 1).
2550 lookup(b, 2).
2551 lookup(c, 3).
2552 lookup(X, 0) :- X = default.
2553 "#;
2554 let result = query_first_binding(source, "lookup(b, X)", "X");
2555 assert_eq!(result, Some("2".to_string()));
2556
2557 let result = query_first_binding(source, "lookup(default, X)", "X");
2558 assert_eq!(result, Some("0".to_string()));
2559 }
2560
2561 #[test]
2562 fn test_disjunction() {
2563 let source = "color(red). color(blue).";
2564 let solutions = query(source, "( color(red) ; color(green) )");
2565 assert_eq!(solutions.len(), 1);
2566 }
2567
2568 #[test]
2569 fn test_disjunction_both_branches() {
2570 let source = "color(red). color(blue).";
2571 let solutions = query(source, "( color(red) ; color(blue) )");
2572 assert_eq!(solutions.len(), 2);
2573 }
2574
2575 #[test]
2576 fn test_if_then_else_true() {
2577 let source = "";
2579 let result = query_first_binding(source, "(1 < 2 -> X = yes ; X = no)", "X");
2580 assert_eq!(result, Some("yes".to_string()));
2581 }
2582
2583 #[test]
2584 fn test_if_then_else_false() {
2585 let source = "";
2586 let result = query_first_binding(source, "(2 < 1 -> X = yes ; X = no)", "X");
2587 assert_eq!(result, Some("no".to_string()));
2588 }
2589
2590 #[test]
2591 fn test_findall_basic() {
2592 let source = "color(red). color(green). color(blue).";
2593 let result = query_first_binding(source, "findall(X, color(X), L)", "L");
2594 assert_eq!(result, Some("[red, green, blue]".to_string()));
2595 }
2596
2597 #[test]
2598 fn test_findall_empty() {
2599 let source = "color(red).";
2600 let result = query_first_binding(source, "findall(X, shape(X), L)", "L");
2601 assert_eq!(result, Some("[]".to_string()));
2602 }
2603
2604 #[test]
2605 fn test_findall_with_rule() {
2606 let source = r#"
2607 parent(tom, mary). parent(tom, james). parent(tom, ann).
2608 "#;
2609 let result = query_first_binding(source, "findall(C, parent(tom, C), Kids)", "Kids");
2610 assert_eq!(result, Some("[mary, james, ann]".to_string()));
2611 }
2612
2613 #[test]
2614 fn test_if_then_no_else() {
2615 let source = "";
2616 let solutions = query(source, "(1 < 2 -> true)");
2617 assert_eq!(solutions.len(), 1);
2618
2619 let solutions = query(source, "(2 < 1 -> true)");
2620 assert_eq!(solutions.len(), 0);
2621 }
2622
2623 #[test]
2624 fn test_solution_limit() {
2625 let source = "n(1). n(2). n(3). n(4). n(5).";
2626 let mut interner = crate::term::StringInterner::new();
2627 let clauses = Parser::parse_program(source, &mut interner).unwrap();
2628 let (goals, vars) = Parser::parse_query_with_vars("n(X)", &mut interner).unwrap();
2629 let db = CompiledDatabase::new(interner, clauses);
2630 let solver = Solver::new(&db, goals, vars).with_limit(3);
2631 let solutions = solver.all_solutions().unwrap();
2632 assert_eq!(solutions.len(), 3);
2633 }
2634
2635 #[test]
2636 fn test_depth_limit() {
2637 let source = "loop :- loop.";
2639 let mut interner = crate::term::StringInterner::new();
2640 let clauses = Parser::parse_program(source, &mut interner).unwrap();
2641 let (goals, vars) = Parser::parse_query_with_vars("loop", &mut interner).unwrap();
2642 let db = CompiledDatabase::new(interner, clauses);
2643 let mut solver = Solver::new(&db, goals, vars).with_max_depth(100);
2644 let result = solver.next();
2645 assert!(matches!(result, SolveResult::Error(ref e) if e.contains("step limit")));
2646 }
2647
2648 #[test]
2651 fn test_once_basic() {
2652 let source = "color(red). color(green). color(blue).";
2653 let solutions = query(source, "once(color(X))");
2655 assert_eq!(solutions.len(), 1);
2656 }
2657
2658 #[test]
2659 fn test_once_prevents_backtracking() {
2660 let source = "n(1). n(2). n(3).";
2661 let result = query_first_binding(source, "once(n(X))", "X");
2662 assert_eq!(result, Some("1".to_string()));
2663 }
2664
2665 #[test]
2666 fn test_once_fails_if_goal_fails() {
2667 let source = "color(red).";
2668 let solutions = query(source, "once(shape(X))");
2669 assert_eq!(solutions.len(), 0);
2670 }
2671
2672 #[test]
2673 fn test_call_basic() {
2674 let source = "color(red). color(blue).";
2675 let solutions = query(source, "call(color(red))");
2676 assert_eq!(solutions.len(), 1);
2677 }
2678
2679 #[test]
2680 fn test_call_with_variable() {
2681 let source = "color(red). color(blue). color(green).";
2682 let solutions = query(source, "call(color(X))");
2683 assert_eq!(solutions.len(), 3);
2684 }
2685
2686 #[test]
2687 fn test_call_fails() {
2688 let source = "color(red).";
2689 let solutions = query(source, "call(shape(X))");
2690 assert_eq!(solutions.len(), 0);
2691 }
2692
2693 #[test]
2694 fn test_atom_length() {
2695 let source = "";
2696 let result = query_first_binding(source, "atom_length(hello, N)", "N");
2697 assert_eq!(result, Some("5".to_string()));
2698 }
2699
2700 #[test]
2701 fn test_atom_length_empty() {
2702 let source = "";
2703 let result = query_first_binding(source, "atom_length('', N)", "N");
2705 assert_eq!(result, Some("0".to_string()));
2706 }
2707
2708 #[test]
2709 fn test_atom_concat() {
2710 let source = "";
2711 let result = query_first_binding(source, "atom_concat(hello, world, X)", "X");
2712 assert_eq!(result, Some("helloworld".to_string()));
2713 }
2714
2715 #[test]
2716 fn test_atom_chars() {
2717 let source = "";
2718 let result = query_first_binding(source, "atom_chars(abc, X)", "X");
2719 assert_eq!(result, Some("[a, b, c]".to_string()));
2720 }
2721
2722 #[test]
2723 fn test_atom_chars_single() {
2724 let source = "";
2725 let result = query_first_binding(source, "atom_chars(x, X)", "X");
2726 assert_eq!(result, Some("[x]".to_string()));
2727 }
2728
2729 #[test]
2730 fn test_arith_abs_in_rule() {
2731 let source = "dist(X, Y, D) :- D is abs(X - Y).";
2732 let result = query_first_binding(source, "dist(3, 7, D)", "D");
2733 assert_eq!(result, Some("4".to_string()));
2734 }
2735
2736 #[test]
2737 fn test_arith_max_in_rule() {
2738 let source = "bigger(X, Y, Z) :- Z is max(X, Y).";
2739 let result = query_first_binding(source, "bigger(3, 7, Z)", "Z");
2740 assert_eq!(result, Some("7".to_string()));
2741 }
2742
2743 #[test]
2744 fn test_arith_min_in_rule() {
2745 let source = "smaller(X, Y, Z) :- Z is min(X, Y).";
2746 let result = query_first_binding(source, "smaller(3, 7, Z)", "Z");
2747 assert_eq!(result, Some("3".to_string()));
2748 }
2749
2750 #[test]
2751 fn test_arith_sign_in_rule() {
2752 let source = "direction(X, S) :- S is sign(X).";
2753 let result = query_first_binding(source, "direction(-42, S)", "S");
2754 assert_eq!(result, Some("-1".to_string()));
2755 }
2756}