1use std::cell::RefCell;
2use std::collections::BTreeMap;
3use std::collections::{HashMap, HashSet};
4use std::fmt::Write;
5use std::rc::Rc;
6use std::string::ToString;
7use std::sync::{Arc, RwLock, RwLockReadGuard};
8
9#[cfg(target_arch = "wasm32")]
10use wasm_bindgen::prelude::*;
11
12use crate::bindings::{
13 Binding, BindingManager, BindingStack, Bindings, Bsp, FollowerId, VariableState,
14};
15use crate::counter::Counter;
16use crate::data_filtering::partition_equivs;
17use crate::debugger::{get_binding_for_var, DebugEvent, Debugger};
18use crate::error::{invalid_state, unsupported, PolarError, PolarResult, RuntimeError};
19use crate::events::*;
20use crate::folder::Folder;
21use crate::inverter::Inverter;
22use crate::kb::*;
23use crate::messages::*;
24use crate::numerics::*;
25use crate::partial::{simplify_bindings_opt, simplify_partial, sub_this, IsaConstraintCheck};
26use crate::rewrites::Renamer;
27use crate::rules::*;
28use crate::runnable::Runnable;
29use crate::sources::Context;
30use crate::terms::*;
31use crate::traces::*;
32use crate::visitor::{walk_term, Visitor};
33
34pub const MAX_STACK_SIZE: usize = 10_000;
35pub const DEFAULT_TIMEOUT_MS: u64 = 30_000;
36
37#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Eq)]
38pub enum LogLevel {
39 Trace,
40 Debug,
41 Info,
42}
43
44impl LogLevel {
45 fn should_print_on_level(&self, level: LogLevel) -> bool {
46 *self <= level
47 }
48}
49
50#[derive(Debug, Clone)]
51#[must_use = "ignored goals are never accomplished"]
52#[allow(clippy::large_enum_variant)]
53pub enum Goal {
54 Backtrack,
55 Cut {
56 choice_index: usize, },
58 Debug {
59 message: String,
60 },
61 Error {
62 error: PolarError,
63 },
64 Halt,
65 Isa {
66 left: Term,
67 right: Term,
68 },
69 IsMoreSpecific {
70 left: Arc<Rule>,
71 right: Arc<Rule>,
72 args: TermList,
73 },
74 IsSubspecializer {
75 answer: Symbol,
76 left: Term,
77 right: Term,
78 arg: Term,
79 },
80 Lookup {
81 dict: Dictionary,
82 field: Term,
83 value: Term,
84 },
85 LookupExternal {
86 call_id: u64,
87 instance: Term,
88 field: Term,
89 },
90 IsaExternal {
91 instance: Term,
92 literal: InstanceLiteral,
93 },
94 MakeExternal {
95 constructor: Term,
96 instance_id: u64,
97 },
98 NextExternal {
99 call_id: u64,
100 iterable: Term,
101 },
102 CheckError,
103 Noop,
104 Query {
105 term: Term,
106 },
107 PopQuery {
108 term: Term,
109 },
110 FilterRules {
111 args: TermList,
112 applicable_rules: Rules,
113 unfiltered_rules: Rules,
114 },
115 SortRules {
116 args: TermList,
117 rules: Rules,
118 outer: usize,
119 inner: usize,
120 },
121 TraceRule {
122 trace: Rc<Trace>,
123 },
124 TraceStackPush,
125 TraceStackPop,
126 Unify {
127 left: Term,
128 right: Term,
129 },
130
131 Run {
133 runnable: Box<dyn Runnable>,
134 },
135
136 AddConstraint {
138 term: Term,
139 },
140
141 AddConstraintsBatch {
144 add_constraints: Rc<RefCell<Bindings>>,
145 },
146}
147
148#[derive(Clone, Debug)]
149pub struct Choice {
150 pub alternatives: Vec<GoalStack>,
151 bsp: Bsp, pub goals: GoalStack, queries: Queries, trace: Vec<Rc<Trace>>, trace_stack: TraceStack,
156}
157
158pub type Choices = Vec<Choice>;
159pub type Goals = Vec<Goal>;
161pub type TraceStack = Vec<Rc<Vec<Rc<Trace>>>>;
162
163#[derive(Clone, Debug, Default)]
164pub struct GoalStack(Vec<Rc<Goal>>);
165
166impl GoalStack {
167 fn new_reversed(goals: Goals) -> Self {
168 Self(goals.into_iter().rev().map(Rc::new).collect())
169 }
170}
171
172impl std::ops::Deref for GoalStack {
173 type Target = Vec<Rc<Goal>>;
174
175 fn deref(&self) -> &Self::Target {
176 &self.0
177 }
178}
179
180impl std::ops::DerefMut for GoalStack {
181 fn deref_mut(&mut self) -> &mut Self::Target {
182 &mut self.0
183 }
184}
185
186pub type Queries = TermList;
187
188pub fn compare(
189 op: Operator,
190 left: &Term,
191 right: &Term,
192 context: Option<&Term>,
193) -> PolarResult<bool> {
194 use {Operator::*, Value::*};
195 fn to_int(x: bool) -> Numeric {
198 Numeric::Integer(i64::from(x))
199 }
200
201 fn compare<T: PartialOrd>(op: Operator, left: T, right: T) -> PolarResult<bool> {
202 match op {
203 Lt => Ok(left < right),
204 Leq => Ok(left <= right),
205 Gt => Ok(left > right),
206 Geq => Ok(left >= right),
207 Eq => Ok(left == right),
208 Neq => Ok(left != right),
209 _ => invalid_state(format!("`{}` is not a comparison operator", op)),
210 }
211 }
212
213 match (left.value(), right.value()) {
214 (Boolean(l), Boolean(r)) => compare(op, &to_int(*l), &to_int(*r)),
215 (Boolean(l), Number(r)) => compare(op, &to_int(*l), r),
216 (Number(l), Boolean(r)) => compare(op, l, &to_int(*r)),
217 (Number(l), Number(r)) => compare(op, l, r),
218 (String(l), String(r)) => compare(op, l, r),
219 _ => {
220 let context = context.expect("should only be None in Grounder, where we unwrap anyway");
221 unsupported(context.to_string(), context)
222 }
223 }
224}
225
226#[derive(Clone)]
227pub struct PolarVirtualMachine {
228 pub goals: GoalStack,
230 binding_manager: BindingManager,
231 choices: Choices,
232 pub queries: Queries,
233
234 pub tracing: bool,
235 pub trace_stack: TraceStack, pub trace: Vec<Rc<Trace>>, pub external_error: Option<String>,
240
241 #[cfg(not(target_arch = "wasm32"))]
242 query_start_time: Option<std::time::Instant>,
243 #[cfg(target_arch = "wasm32")]
244 query_start_time: Option<f64>,
245 query_timeout_ms: u64,
246
247 stack_limit: usize,
249
250 csp: Bsp,
252
253 pub debugger: Debugger,
255
256 pub kb: Arc<RwLock<KnowledgeBase>>,
258
259 call_id_symbols: HashMap<u64, Symbol>,
261
262 log_level: Option<LogLevel>,
264
265 polar_log_stderr: bool,
266 polar_trace_mute: bool,
267
268 pub query_contains_partial: bool,
270 pub inverting: bool,
271
272 pub messages: MessageQueue,
274}
275
276impl Default for PolarVirtualMachine {
277 fn default() -> Self {
278 PolarVirtualMachine::new(
279 Arc::new(RwLock::new(KnowledgeBase::default())),
280 false,
281 vec![],
282 MessageQueue::new(),
284 )
285 }
286}
287
288#[cfg(target_arch = "wasm32")]
289#[wasm_bindgen]
290extern "C" {
291 #[wasm_bindgen(js_namespace = console, js_name = error)]
292 fn console_error(a: &str);
293}
294
295impl PolarVirtualMachine {
297 pub fn new(
300 kb: Arc<RwLock<KnowledgeBase>>,
301 tracing: bool,
302 goals: Goals,
303 messages: MessageQueue,
304 ) -> Self {
305 let query_timeout_ms = std::env::var("POLAR_TIMEOUT_MS")
306 .ok()
307 .and_then(|timeout_str| timeout_str.parse::<u64>().ok())
308 .unwrap_or(DEFAULT_TIMEOUT_MS);
309 let constants = kb
310 .read()
311 .expect("cannot acquire KB read lock")
312 .get_registered_constants()
313 .clone();
314
315 let mut vm = Self {
316 goals: GoalStack::new_reversed(goals),
317 binding_manager: BindingManager::new(),
318 query_start_time: None,
319 query_timeout_ms,
320 stack_limit: MAX_STACK_SIZE,
321 csp: Bsp::default(),
322 choices: vec![],
323 queries: vec![],
324 tracing,
325 trace_stack: vec![],
326 trace: vec![],
327 external_error: None,
328 debugger: Debugger::default(),
329 kb,
330 call_id_symbols: HashMap::new(),
331 log_level: None,
333 polar_log_stderr: false,
335 polar_trace_mute: false,
336 query_contains_partial: false,
337 inverting: false,
338 messages,
339 };
340 vm.bind_constants(constants);
341 vm.query_contains_partial();
342
343 let polar_log = std::env::var("POLAR_LOG");
344 vm.set_logging_options(None, polar_log.ok());
345
346 vm
347 }
348
349 pub fn set_logging_options(&mut self, rust_log: Option<String>, polar_log: Option<String>) {
350 let polar_log = polar_log.unwrap_or_default();
351 let polar_log_vars: HashSet<String> = polar_log
352 .split(',')
353 .filter(|v| !v.is_empty())
354 .map(|s| s.to_lowercase())
355 .collect();
356
357 self.polar_log_stderr = polar_log_vars.contains(&"now".to_string());
358
359 self.log_level = if rust_log.is_some() {
361 Some(LogLevel::Trace)
362 } else {
363 None
364 };
365
366 if !polar_log_vars.is_empty()
369 && polar_log_vars.is_disjoint(&HashSet::from(["off".to_string(), "0".to_string()]))
370 {
371 self.log_level = if polar_log_vars.contains(&LogLevel::Trace.to_string()) {
372 Some(LogLevel::Trace)
373 } else if polar_log_vars.contains(&LogLevel::Debug.to_string()) {
374 Some(LogLevel::Debug)
375 } else {
376 Some(LogLevel::Info)
377 }
378 }
379 }
380
381 fn query_contains_partial(&mut self) {
382 struct VarVisitor<'vm> {
383 has_partial: bool,
384 vm: &'vm PolarVirtualMachine,
385 }
386
387 impl<'vm> Visitor for VarVisitor<'vm> {
388 fn visit_variable(&mut self, v: &Symbol) {
389 if matches!(self.vm.variable_state(v), VariableState::Partial) {
390 self.has_partial = true;
391 }
392 }
393 }
394
395 let mut visitor = VarVisitor {
396 has_partial: false,
397 vm: self,
398 };
399 self.query_contains_partial = self.goals.iter().any(|goal| {
400 if let Goal::Query { term } = goal.as_ref() {
401 walk_term(&mut visitor, term);
402 visitor.has_partial
403 } else {
404 false
405 }
406 });
407 }
408
409 #[cfg(test)]
410 pub fn new_test(kb: Arc<RwLock<KnowledgeBase>>, tracing: bool, goals: Goals) -> Self {
411 PolarVirtualMachine::new(kb, tracing, goals, MessageQueue::new())
412 }
413
414 pub fn clone_with_goals(&self, goals: Goals) -> Self {
416 let mut vm = Self::new(self.kb.clone(), self.tracing, goals, self.messages.clone());
417 vm.binding_manager.clone_from(&self.binding_manager);
418 vm.query_contains_partial = self.query_contains_partial;
419 vm.debugger = self.debugger.clone();
420 vm
421 }
422
423 #[cfg(test)]
424 fn set_stack_limit(&mut self, limit: usize) {
425 self.stack_limit = limit;
426 }
427
428 fn kb(&self) -> RwLockReadGuard<KnowledgeBase> {
429 self.kb.read().unwrap()
430 }
431
432 fn new_id(&self) -> u64 {
433 self.kb().new_id()
434 }
435
436 pub fn id_counter(&self) -> Counter {
437 self.kb().id_counter()
438 }
439
440 fn new_call_id(&mut self, symbol: &Symbol) -> u64 {
441 let call_id = self.new_id();
442 self.call_id_symbols.insert(call_id, symbol.clone());
443 call_id
444 }
445
446 fn new_call_var(&mut self, var_prefix: &str, initial_value: Value) -> (u64, Term) {
447 let sym = self.kb().gensym(var_prefix);
448 self.bind(&sym, Term::from(initial_value)).unwrap();
449 let call_id = self.new_call_id(&sym);
450 (call_id, Term::from(sym))
451 }
452
453 fn get_call_sym(&self, call_id: u64) -> &Symbol {
454 self.call_id_symbols
455 .get(&call_id)
456 .expect("unregistered external call ID")
457 }
458
459 fn next(&mut self, goal: Rc<Goal>) -> PolarResult<QueryEvent> {
462 self.log(LogLevel::Trace, || goal.to_string(), &[]);
463
464 self.check_timeout()?;
465
466 match goal.as_ref() {
467 Goal::Backtrack => self.backtrack()?,
468 Goal::Cut { choice_index } => self.cut(*choice_index),
469 Goal::Debug { message } => return Ok(self.debug(message)),
470 Goal::Halt => return Ok(self.halt()),
471 Goal::Error { error } => return Err(error.clone()),
472 Goal::Isa { left, right } => self.isa(left, right)?,
473 Goal::IsMoreSpecific { left, right, args } => {
474 self.is_more_specific(left, right, args)?
475 }
476 Goal::IsSubspecializer {
477 answer,
478 left,
479 right,
480 arg,
481 } => return self.is_subspecializer(answer, left, right, arg),
482 Goal::Lookup { dict, field, value } => self.lookup(dict, field, value)?,
483 Goal::LookupExternal {
484 call_id,
485 instance,
486 field,
487 } => return self.lookup_external(*call_id, instance, field),
488 Goal::IsaExternal { instance, literal } => return self.isa_external(instance, literal),
489 Goal::MakeExternal {
490 constructor,
491 instance_id,
492 } => return Ok(self.make_external(constructor, *instance_id)),
493 Goal::NextExternal { call_id, iterable } => {
494 return self.next_external(*call_id, iterable)
495 }
496 Goal::CheckError => return self.check_error(),
497 Goal::Noop => {}
498 Goal::Query { term } => {
499 let result = self.query(term);
500 self.maybe_break(DebugEvent::Query)?;
501 return result;
502 }
503 Goal::PopQuery { .. } => self.pop_query(),
504 Goal::FilterRules {
505 applicable_rules,
506 unfiltered_rules,
507 args,
508 } => self.filter_rules(applicable_rules, unfiltered_rules, args)?,
509 Goal::SortRules {
510 rules,
511 outer,
512 inner,
513 args,
514 } => self.sort_rules(rules, args, *outer, *inner)?,
515 Goal::TraceStackPush => {
516 self.trace_stack.push(Rc::new(self.trace.clone()));
517 self.trace = vec![];
518 }
519 Goal::TraceStackPop => {
520 let mut children = self.trace.clone();
521 self.trace = self.trace_stack.pop().unwrap().as_ref().clone();
522 let mut trace = self.trace.pop().unwrap();
523 let trace = Rc::make_mut(&mut trace);
524 trace.children.append(&mut children);
525 self.trace.push(Rc::new(trace.clone()));
526 self.maybe_break(DebugEvent::Pop)?;
527 }
528 Goal::TraceRule { trace } => {
529 if let Node::Rule(rule) = &trace.node {
530 self.log(LogLevel::Info, || format!("RULE: {}", rule), &[]);
531 }
532 self.trace.push(trace.clone());
533 self.maybe_break(DebugEvent::Rule)?;
534 }
535 Goal::Unify { left, right } => self.unify(left, right)?,
536 Goal::AddConstraint { term } => self.add_constraint(term)?,
537 Goal::AddConstraintsBatch { add_constraints } => {
538 add_constraints
539 .borrow_mut()
540 .drain()
541 .try_for_each(|(_, constraint)| self.add_constraint(&constraint))?
542 }
543 Goal::Run { runnable } => return self.run_runnable(runnable.clone_runnable()),
544 }
545 Ok(QueryEvent::None)
546 }
547
548 pub fn push_goal(&mut self, goal: Goal) -> PolarResult<()> {
550 use {Goal::*, VariableState::Unbound};
551 if self.goals.len() >= self.stack_limit {
552 let msg = format!("Goal stack overflow! MAX_GOALS = {}", self.stack_limit);
553 Err(RuntimeError::StackOverflow { msg }.into())
554 } else if matches!(goal, LookupExternal { call_id, ..} | NextExternal { call_id, .. } if self.variable_state(self.get_call_sym(call_id)) != Unbound)
555 {
556 invalid_state("The call_id result variables for LookupExternal and NextExternal goals must be unbound.")
557 } else {
558 self.goals.push(Rc::new(goal));
559 Ok(())
560 }
561 }
562
563 fn push_choice<I>(&mut self, alternatives: I) -> PolarResult<()>
574 where
575 I: IntoIterator<Item = Goals>,
576 I::IntoIter: std::iter::DoubleEndedIterator,
577 {
578 let alternatives = alternatives
580 .into_iter()
581 .rev()
582 .map(GoalStack::new_reversed)
583 .collect();
584 if self.choices.len() >= self.stack_limit {
585 let msg = "Too many choices.".to_owned();
586 Err(RuntimeError::StackOverflow { msg }.into())
587 } else {
588 self.choices.push(Choice {
589 alternatives,
590 bsp: self.bsp(),
591 goals: self.goals.clone(),
592 queries: self.queries.clone(),
593 trace: self.trace.clone(),
594 trace_stack: self.trace_stack.clone(),
595 });
596 Ok(())
597 }
598 }
599
600 fn choose<I>(&mut self, alternatives: I) -> PolarResult<()>
608 where
609 I: IntoIterator<Item = Goals>,
610 I::IntoIter: std::iter::DoubleEndedIterator,
611 {
612 let mut alternatives_iter = alternatives.into_iter();
613 if let Some(alternative) = alternatives_iter.next() {
614 self.push_choice(alternatives_iter)?;
615 self.append_goals(alternative)
616 } else {
617 self.backtrack()
618 }
619 }
620
621 fn choose_conditional(
626 &mut self,
627 mut conditional: Goals,
628 consequent: Goals,
629 mut alternative: Goals,
630 ) -> PolarResult<()> {
631 let cut_consequent = Goal::Cut {
633 choice_index: self.choices.len(),
634 };
635 alternative.insert(0, cut_consequent);
636
637 self.push_choice(vec![consequent])?;
639 let cut_alternative = Goal::Cut {
640 choice_index: self.choices.len(),
641 };
642 conditional.push(cut_alternative);
643 conditional.push(Goal::Backtrack);
644
645 self.choose(vec![conditional, alternative])
646 }
647
648 fn append_goals<I>(&mut self, goals: I) -> PolarResult<()>
650 where
651 I: IntoIterator<Item = Goal>,
652 I::IntoIter: std::iter::DoubleEndedIterator,
653 {
654 goals.into_iter().rev().try_for_each(|g| self.push_goal(g))
655 }
656
657 fn rebind_external_answer(&mut self, var: &Symbol, val: Term) {
661 self.binding_manager.unsafe_rebind(var, val);
662 }
663
664 pub fn bind(&mut self, var: &Symbol, val: Term) -> PolarResult<()> {
666 self.log(
667 LogLevel::Trace,
668 || format!("⇒ bind: {} ← {}", var, val),
669 &[],
670 );
671 if let Some(goal) = self.binding_manager.bind(var, val)? {
672 self.push_goal(goal)
673 } else {
674 Ok(())
675 }
676 }
677
678 pub fn add_binding_follower(&mut self) -> FollowerId {
679 self.binding_manager.add_follower(BindingManager::new())
680 }
681
682 pub fn remove_binding_follower(&mut self, follower_id: &FollowerId) -> Option<BindingManager> {
683 self.binding_manager.remove_follower(follower_id)
684 }
685
686 fn add_constraint(&mut self, term: &Term) -> PolarResult<()> {
690 self.log(
691 LogLevel::Trace,
692 || format!("⇒ add_constraint: {}", term),
693 &[],
694 );
695 self.binding_manager.add_constraint(term)
696 }
697
698 fn bind_constants(&mut self, bindings: Bindings) {
701 assert_eq!(self.bsp(), self.csp);
702 for (var, value) in bindings.iter() {
703 self.bind(var, value.clone()).unwrap();
704 }
705 self.csp = self.bsp();
706 }
707
708 pub fn bindings(&self, include_temps: bool) -> Bindings {
710 self.binding_manager
711 .bindings_after(include_temps, &self.csp)
712 }
713
714 pub fn bindings_debug(&self) -> &BindingStack {
716 self.binding_manager.bindings_debug()
717 }
718
719 pub fn relevant_bindings(&self, terms: &[&Term]) -> Bindings {
721 let mut variables = HashSet::new();
722 for t in terms {
723 t.variables(&mut variables);
724 }
725 self.binding_manager.variable_bindings(&variables)
726 }
727
728 fn bsp(&self) -> Bsp {
730 self.binding_manager.bsp()
731 }
732
733 pub fn variable_state_at_point(&self, variable: &Symbol, bsp: &Bsp) -> VariableState {
735 self.binding_manager.variable_state_at_point(variable, bsp)
736 }
737
738 fn variable_state(&self, variable: &Symbol) -> VariableState {
740 self.binding_manager.variable_state(variable)
741 }
742
743 fn deref(&self, term: &Term) -> Term {
745 self.binding_manager.deep_deref(term)
746 }
747
748 fn rename_rule_vars(&self, rule: &Rule) -> Rule {
750 let kb = &*self.kb.read().unwrap();
751 let mut renamer = Renamer::new(kb);
752 renamer.fold_rule(rule.clone())
753 }
754
755 #[cfg(not(target_arch = "wasm32"))]
757 fn print<S: Into<String>>(&self, message: S) {
758 let message = message.into();
759 if self.polar_log_stderr {
760 eprintln!("{}", message);
761 } else {
762 self.messages.push(MessageKind::Print, message);
763 }
764 }
765
766 #[cfg(target_arch = "wasm32")]
768 fn print<S: Into<String>>(&self, message: S) {
769 let message = message.into();
770 if self.polar_log_stderr {
771 console_error(&message);
772 } else {
773 self.messages.push(MessageKind::Print, message);
774 }
775 }
776
777 fn log<F, R>(&self, level: LogLevel, message_fn: F, terms: &[&Term])
778 where
779 F: FnOnce() -> R,
780 R: AsRef<str>,
781 {
782 if let Some(configured_log_level) = self.log_level {
783 if level == LogLevel::Trace && self.polar_trace_mute {
786 return;
787 }
788 if configured_log_level.should_print_on_level(level) {
789 let mut indent = String::new();
790 for _ in 0..=self.queries.len() {
791 indent.push_str(" ");
792 }
793 let message = message_fn();
794 let lines = message.as_ref().split('\n').collect::<Vec<&str>>();
795 if let Some(line) = lines.first() {
796 let prefix = format!("[oso][{}] {}", level, &indent);
797 let mut msg = format!("{}{}", prefix, line);
798
799 if !terms.is_empty() && configured_log_level == LogLevel::Trace {
801 let relevant_bindings = self.relevant_bindings(terms);
802 write!(
803 msg,
804 ", BINDINGS: {{{}}}",
805 relevant_bindings
806 .iter()
807 .map(|(var, val)| format!("{} => {}", var.0, val))
808 .collect::<Vec<String>>()
809 .join(", ")
810 )
811 .unwrap();
812 }
813 self.print(msg);
814 for line in &lines[1..] {
815 self.print(format!("{}{}", prefix, line));
816 }
817 }
818 }
819 }
820 }
821
822 pub(crate) fn stack_trace(&self) -> String {
824 let mut trace_stack = self.trace_stack.clone();
825 let mut trace = self.trace.clone();
826
827 let mut stack = vec![];
830 while let Some(t) = trace.last() {
831 stack.push(t.clone());
832 trace = trace_stack
833 .pop()
834 .map(|ts| ts.as_ref().clone())
835 .unwrap_or_default();
836 }
837
838 stack.reverse();
839
840 let mut i = stack.iter().filter_map(|t| t.term()).count();
843
844 let mut st = "trace (most recent evaluation last):\n".to_owned();
845 let mut rule = None;
846 for t in stack {
847 match &t.node {
848 Node::Rule(r) => {
849 rule = Some(r.clone());
850 }
851 Node::Term(t) => {
852 if matches!(t.value(), Value::Expression(Operation { operator: Operator::And, args}) if args.len() == 1)
853 {
854 continue;
855 }
856
857 i -= 1;
858 let _ = writeln!(st, " {:03}: {}", i, self.term_source(t, false));
859
860 if let Some(context) = t.parsed_context() {
861 if let Some(rule) = &rule {
862 let _ = write!(st, " in rule {}", rule.name);
863 } else {
864 let _ = write!(st, " in query");
865 }
866 let _ = writeln!(st, "{}", context.source_position());
867 };
868 }
869 }
870 }
871 st
872 }
873
874 #[cfg(not(target_arch = "wasm32"))]
875 fn query_duration(&self) -> u64 {
876 let now = std::time::Instant::now();
877 let start = self.query_start_time.expect("Query start not recorded");
878 (now - start).as_millis() as u64
879 }
880
881 #[cfg(target_arch = "wasm32")]
882 fn query_duration(&self) -> u64 {
883 let now: f64 = js_sys::Date::now();
884 let start = self.query_start_time.expect("Query start not recorded");
885 (now - start) as u64
886 }
887
888 fn is_query_timeout_disabled(&self) -> bool {
889 self.query_timeout_ms == 0
890 }
891
892 fn check_timeout(&self) -> PolarResult<()> {
893 if self.is_query_timeout_disabled() {
894 return Ok(());
896 }
897
898 let elapsed = self.query_duration();
899 let timeout = self.query_timeout_ms;
900 if elapsed > timeout {
901 return Err(RuntimeError::QueryTimeout { elapsed, timeout }.into());
902 }
903 Ok(())
904 }
905}
906
907impl PolarVirtualMachine {
909 fn backtrack(&mut self) -> PolarResult<()> {
912 self.log(LogLevel::Trace, || "BACKTRACK", &[]);
913
914 loop {
915 match self.choices.pop() {
916 None => return self.push_goal(Goal::Halt),
917 Some(Choice {
918 mut alternatives,
919 bsp,
920 goals,
921 queries,
922 trace,
923 trace_stack,
924 }) => {
925 self.binding_manager.backtrack(&bsp);
926 if let Some(mut alternative) = alternatives.pop() {
927 if alternatives.is_empty() {
928 self.goals = goals;
929 self.queries = queries;
930 self.trace = trace;
931 self.trace_stack = trace_stack;
932 } else {
933 self.goals.clone_from(&goals);
934 self.queries.clone_from(&queries);
935 self.trace.clone_from(&trace);
936 self.trace_stack.clone_from(&trace_stack);
937 self.choices.push(Choice {
938 alternatives,
939 bsp,
940 goals,
941 queries,
942 trace,
943 trace_stack,
944 })
945 }
946 self.goals.append(&mut alternative);
947 break;
948 }
949 }
950 }
951 }
952 Ok(())
953 }
954
955 fn cut(&mut self, index: usize) {
957 self.choices.truncate(index);
958 }
959
960 fn pop_query(&mut self) {
962 self.queries.pop();
963 }
964
965 fn debug(&mut self, message: &str) -> QueryEvent {
967 self.query_start_time.take();
969
970 QueryEvent::Debug {
971 message: message.to_string(),
972 }
973 }
974
975 fn halt(&mut self) -> QueryEvent {
977 self.log(LogLevel::Trace, || "HALT", &[]);
978 self.goals.clear();
979 self.choices.clear();
980 QueryEvent::Done { result: true }
981 }
982
983 #[allow(clippy::many_single_char_names)]
985 fn isa(&mut self, left: &Term, right: &Term) -> PolarResult<()> {
986 self.log(
987 LogLevel::Trace,
988 || format!("MATCHES: {} matches {}", left, right),
989 &[left, right],
990 );
991
992 match (left.value(), right.value()) {
993 (_, Value::Dictionary(_)) => todo!("make this case unreachable"),
994 (Value::Expression(_), _) | (_, Value::Expression(_)) => {
995 unreachable!("encountered bare expression")
996 }
997
998 _ if self.kb.read().unwrap().is_union(left) => {
999 let unions_match = (left.is_actor_union() && right.is_actor_union())
1005 || (left.is_resource_union() && right.is_resource_union());
1006 if !unions_match {
1007 return self.push_goal(Goal::Backtrack);
1008 }
1009 }
1010 _ if self.kb.read().unwrap().is_union(right) => self.isa_union(left, right)?,
1011
1012 (Value::Variable(l), Value::Variable(r))
1014 | (Value::Variable(l), Value::RestVariable(r))
1015 | (Value::RestVariable(l), Value::Variable(r))
1016 | (Value::RestVariable(l), Value::RestVariable(r)) => {
1017 match (self.variable_state(l), self.variable_state(r)) {
1019 (VariableState::Bound(x), _) => self.push_goal(Goal::Isa {
1020 left: x,
1021 right: right.clone(),
1022 })?,
1023 (_, VariableState::Bound(y)) => self.push_goal(Goal::Isa {
1024 left: left.clone(),
1025 right: y,
1026 })?,
1027 (_, _) => self.add_constraint(&term!(op!(Isa, left.clone(), right.clone())))?,
1028 }
1029 }
1030 (Value::Variable(l), _) | (Value::RestVariable(l), _) => match self.variable_state(l) {
1031 VariableState::Bound(x) => self.push_goal(Goal::Isa {
1032 left: x,
1033 right: right.clone(),
1034 })?,
1035 _ => self.isa_expr(left, right)?,
1036 },
1037 (_, Value::Variable(r)) | (_, Value::RestVariable(r)) => match self.variable_state(r) {
1038 VariableState::Bound(y) => self.push_goal(Goal::Isa {
1039 left: left.clone(),
1040 right: y,
1041 })?,
1042 _ => self.push_goal(Goal::Unify {
1043 left: left.clone(),
1044 right: right.clone(),
1045 })?,
1046 },
1047
1048 (Value::List(left), Value::List(right)) => {
1049 self.unify_lists(left, right, |(left, right)| Goal::Isa {
1050 left: left.clone(),
1051 right: right.clone(),
1052 })?;
1053 }
1054
1055 (Value::Dictionary(left), Value::Pattern(Pattern::Dictionary(right))) => {
1056 let left_fields: HashSet<&Symbol> = left.fields.keys().collect();
1058 let right_fields: HashSet<&Symbol> = right.fields.keys().collect();
1059 if !right_fields.is_subset(&left_fields) {
1060 return self.push_goal(Goal::Backtrack);
1061 }
1062
1063 for (k, v) in right.fields.iter() {
1066 let left = left
1067 .fields
1068 .get(k)
1069 .expect("left fields should be a superset of right fields")
1070 .clone();
1071 self.push_goal(Goal::Isa {
1072 left,
1073 right: v.clone(),
1074 })?;
1075 }
1076 }
1077
1078 (_, Value::Pattern(Pattern::Dictionary(right))) => {
1079 for (field, right_value) in right.fields.iter() {
1082 let answer = self.kb.read().unwrap().gensym("isa_value");
1085 let call_id = self.new_call_id(&answer);
1086
1087 let lookup = Goal::LookupExternal {
1088 instance: left.clone(),
1089 call_id,
1090 field: right_value.clone_with_value(Value::String(field.0.clone())),
1091 };
1092 let isa = Goal::Isa {
1093 left: Term::from(answer),
1094 right: right_value.clone(),
1095 };
1096 self.append_goals(vec![lookup, isa])?;
1097 }
1098 }
1099
1100 (_, Value::Pattern(Pattern::Instance(right_literal))) => {
1101 self.push_goal(Goal::Isa {
1103 left: left.clone(),
1104 right: right.clone_with_value(Value::Pattern(Pattern::Dictionary(
1105 right_literal.fields.clone(),
1106 ))),
1107 })?;
1108
1109 if let Value::ExternalInstance(ExternalInstance {
1112 class_id: Some(class_id),
1113 ..
1114 }) = *left.value()
1115 {
1116 let isa = {
1117 let kb = self.kb.read().unwrap();
1118 let right_id = kb
1119 .get_class_id_for_symbol(&right_literal.tag)
1120 .expect("no class ID for symbol");
1121 let left_symbol = kb
1122 .get_symbol_for_class_id(&class_id)
1123 .expect("no symbol for class ID");
1124 if let Some(mro) = kb.mro.get(left_symbol) {
1125 mro.contains(right_id)
1126 } else {
1127 false
1128 }
1129 };
1130 if !isa {
1131 self.push_goal(Goal::Backtrack)?;
1132 }
1133 } else {
1135 self.push_goal(Goal::IsaExternal {
1137 instance: left.clone(),
1138 literal: right_literal.clone(),
1139 })?;
1140 }
1141 }
1142
1143 _ => self.push_goal(Goal::Unify {
1145 left: left.clone(),
1146 right: right.clone(),
1147 })?,
1148 }
1149 Ok(())
1150 }
1151
1152 fn get_names(&self, s: &Symbol) -> HashSet<Symbol> {
1153 let cycles = self
1154 .binding_manager
1155 .get_constraints(s)
1156 .constraints()
1157 .into_iter()
1158 .filter_map(|con| match con.operator {
1159 Operator::Unify | Operator::Eq => {
1160 if let (Ok(l), Ok(r)) = (con.args[0].as_symbol(), con.args[1].as_symbol()) {
1161 Some((l.clone(), r.clone()))
1162 } else {
1163 None
1164 }
1165 }
1166 _ => None,
1167 });
1168
1169 partition_equivs(cycles)
1170 .into_iter()
1171 .find(|c| c.contains(s))
1172 .unwrap_or_else(|| {
1173 let mut hs = HashSet::with_capacity(1);
1174 hs.insert(s.clone());
1175 hs
1176 })
1177 }
1178
1179 fn isa_expr(&mut self, left: &Term, right: &Term) -> PolarResult<()> {
1180 match right.value() {
1181 Value::Pattern(Pattern::Dictionary(fields)) => {
1182 let to_unify = |(field, value): (&Symbol, &Term)| -> Term {
1184 let value = self.deref(value);
1185 let field = right.clone_with_value(value!(field.0.as_ref()));
1186 let left = left.clone_with_value(value!(op!(Dot, left.clone(), field)));
1187 term!(op!(Unify, left, value))
1188 };
1189
1190 let constraints = fields.fields.iter().rev().map(to_unify).collect::<Vec<_>>();
1191 for op in constraints {
1192 self.add_constraint(&op)?;
1193 }
1194 }
1195 Value::Pattern(Pattern::Instance(InstanceLiteral { fields, tag })) => {
1196 let var = left.as_symbol()?;
1200
1201 let partial = self.binding_manager.get_constraints(var);
1203
1204 let names = self.get_names(var);
1205 let output = names.clone();
1206
1207 let partial = partial.into();
1208 let (simplified, _) = simplify_partial(var, partial, output, false);
1209
1210 let simplified = simplified.as_expression()?;
1211
1212 let lhss_of_matches = simplified
1215 .constraints()
1216 .into_iter()
1217 .filter_map(|c| {
1218 if c.operator != Operator::Unify && c.operator != Operator::In {
1223 None
1224 } else if matches!(c.args[0].as_symbol(), Ok(s) if names.contains(s)) &&
1225 matches!(c.args[1].as_expression(), Ok(o) if o.operator == Operator::Dot) {
1226 Some(c.args[1].clone())
1227 } else if c.operator == Operator::Unify && matches!(c.args[1].as_symbol(), Ok(s) if names.contains(s)) &&
1228 matches!(c.args[0].as_expression(), Ok(o) if o.operator == Operator::Dot) {
1230 Some(c.args[0].clone())
1231 } else {
1232 None
1233 }
1234 })
1235 .chain(std::iter::once(left.clone()));
1236
1237 let tag_pattern = right.clone_with_value(value!(pattern!(instance!(tag.clone()))));
1239 let type_constraint = op!(Isa, left.clone(), tag_pattern);
1240
1241 let new_matcheses =
1242 lhss_of_matches.map(|lhs_of_matches| op!(Isa, lhs_of_matches, right.clone()));
1243
1244 let runnables = new_matcheses
1245 .map(|new_matches| {
1246 let runnable = Box::new(IsaConstraintCheck::new(
1247 simplified.constraints(),
1248 new_matches,
1249 names.clone(),
1250 ));
1251 Goal::Run { runnable }
1252 })
1253 .collect();
1254
1255 let field_constraints = fields.fields.iter().rev().map(|(f, v)| {
1257 let v = self.deref(v);
1258 let field = right.clone_with_value(value!(f.0.as_ref()));
1259 let left = left.clone_with_value(value!(op!(Dot, left.clone(), field)));
1260 op!(Unify, left, v)
1261 });
1262
1263 let mut add_constraints = vec![type_constraint];
1264 add_constraints.extend(field_constraints);
1265
1266 self.choose_conditional(
1268 runnables,
1269 add_constraints
1270 .into_iter()
1271 .map(|op| Goal::AddConstraint { term: op.into() })
1272 .collect(),
1273 vec![Goal::CheckError, Goal::Backtrack],
1274 )?;
1275 }
1276 _ => self.push_goal(Goal::Unify {
1280 left: left.clone(),
1281 right: right.clone(),
1282 })?,
1283 }
1284 Ok(())
1285 }
1286
1287 fn isa_union(&mut self, left: &Term, union: &Term) -> PolarResult<()> {
1290 let member_isas = {
1291 let kb = self.kb.read().unwrap();
1292 let members = kb.get_union_members(union).iter();
1293 members
1294 .map(|member| {
1295 let tag = member.as_symbol().unwrap().0.as_str();
1296 member.clone_with_value(value!(pattern!(instance!(tag))))
1297 })
1298 .map(|pattern| {
1299 vec![Goal::Isa {
1300 left: left.clone(),
1301 right: pattern,
1302 }]
1303 })
1304 .collect::<Vec<Goals>>()
1305 };
1306 self.choose(member_isas)
1307 }
1308
1309 fn lookup(&mut self, dict: &Dictionary, field: &Term, value: &Term) -> PolarResult<()> {
1310 let field = self.deref(field);
1311 match field.value() {
1312 Value::Variable(_) => {
1313 let mut alternatives = vec![];
1314 for (k, v) in &dict.fields {
1315 let mut goals: Goals = vec![];
1316 goals.push(Goal::Unify {
1320 left: field.clone_with_value(Value::String(k.clone().0)),
1321 right: field.clone(),
1322 });
1323 goals.push(Goal::Unify {
1325 left: v.clone(),
1326 right: value.clone(),
1327 });
1328 alternatives.push(goals);
1329 }
1330 self.choose(alternatives)
1331 }
1332 Value::String(field) => {
1333 if let Some(retrieved) = dict.fields.get(&Symbol(field.clone())) {
1334 self.push_goal(Goal::Unify {
1335 left: retrieved.clone(),
1336 right: value.clone(),
1337 })
1338 } else {
1339 self.push_goal(Goal::Backtrack)
1340 }
1341 }
1342 v => self.type_error(
1343 &field,
1344 format!("cannot look up field {:?} on a dictionary", v),
1345 ),
1346 }
1347 }
1348
1349 fn lookup_external(
1353 &mut self,
1354 call_id: u64,
1355 instance: &Term,
1356 field: &Term,
1357 ) -> PolarResult<QueryEvent> {
1358 let (field_name, args, kwargs): (
1359 Symbol,
1360 Option<Vec<Term>>,
1361 Option<BTreeMap<Symbol, Term>>,
1362 ) = match self.deref(field).value() {
1363 Value::Call(Call { name, args, kwargs }) => (
1364 name.clone(),
1365 Some(args.iter().map(|arg| self.deref(arg)).collect()),
1366 kwargs.as_ref().map(|unwrapped| {
1367 unwrapped
1368 .iter()
1369 .map(|(k, v)| (k.to_owned(), self.deref(v)))
1370 .collect()
1371 }),
1372 ),
1373 Value::String(field) => (Symbol(field.clone()), None, None),
1374 v => {
1375 return self.type_error(
1376 field,
1377 format!("cannot look up field {:?} on an external instance", v),
1378 )
1379 }
1380 };
1381
1382 self.push_choice(vec![])?;
1385
1386 self.log(
1387 LogLevel::Trace,
1388 || {
1389 let mut msg = format!("LOOKUP: {}.{}", instance, field_name);
1390 msg.push('(');
1391 let args = args
1392 .clone()
1393 .unwrap_or_default()
1394 .into_iter()
1395 .map(|a| a.to_string());
1396 let kwargs = kwargs
1397 .clone()
1398 .unwrap_or_default()
1399 .into_iter()
1400 .map(|(k, v)| format!("{}: {}", k, v));
1401 msg.push_str(&args.chain(kwargs).collect::<Vec<_>>().join(", "));
1402 msg.push(')');
1403 msg
1404 },
1405 &[],
1406 );
1407
1408 Ok(QueryEvent::ExternalCall {
1409 call_id,
1410 instance: self.deref(instance),
1411 attribute: field_name,
1412 args,
1413 kwargs,
1414 })
1415 }
1416
1417 fn isa_external(
1418 &mut self,
1419 instance: &Term,
1420 literal: &InstanceLiteral,
1421 ) -> PolarResult<QueryEvent> {
1422 let (call_id, answer) = self.new_call_var("isa", false.into());
1423 self.push_goal(Goal::Unify {
1424 left: answer,
1425 right: Term::from(true),
1426 })?;
1427
1428 Ok(QueryEvent::ExternalIsa {
1429 call_id,
1430 instance: self.deref(instance),
1431 class_tag: literal.tag.clone(),
1432 })
1433 }
1434
1435 fn next_external(&mut self, call_id: u64, iterable: &Term) -> PolarResult<QueryEvent> {
1436 self.push_choice(vec![vec![Goal::NextExternal {
1438 call_id,
1439 iterable: iterable.clone(),
1440 }]])?;
1441
1442 Ok(QueryEvent::NextExternal {
1443 call_id,
1444 iterable: iterable.clone(),
1445 })
1446 }
1447
1448 fn make_external(&self, constructor: &Term, instance_id: u64) -> QueryEvent {
1449 QueryEvent::MakeExternal {
1450 instance_id,
1451 constructor: self.deref(constructor),
1452 }
1453 }
1454
1455 fn check_error(&mut self) -> PolarResult<QueryEvent> {
1456 if let Some(msg) = self.external_error.take() {
1457 let term = match self.trace.last().map(|t| t.node.clone()) {
1458 Some(Node::Term(t)) => Some(t),
1459 _ => None,
1460 };
1461 let stack_trace = self.stack_trace();
1462 Err(RuntimeError::Application {
1463 msg,
1464 stack_trace,
1465 term,
1466 }
1467 .into())
1468 } else {
1469 Ok(QueryEvent::None)
1470 }
1471 }
1472
1473 fn query(&mut self, term: &Term) -> PolarResult<QueryEvent> {
1480 match &term.value() {
1485 Value::Call(predicate) => {
1486 self.log(
1487 LogLevel::Info,
1488 || format!("QUERY RULE: {}", predicate),
1489 &[term],
1490 );
1491 }
1492 Value::Expression(Operation {
1493 operator: Operator::And,
1494 args,
1495 }) if args.len() < 2 => (),
1496 _ => {
1497 self.log(LogLevel::Trace, || format!("QUERY: {}", term), &[term]);
1498 }
1499 };
1500
1501 self.queries.push(term.clone());
1502 self.push_goal(Goal::PopQuery { term: term.clone() })?;
1503 self.trace.push(Rc::new(Trace {
1504 node: Node::Term(term.clone()),
1505 children: vec![],
1506 }));
1507
1508 match &term.value() {
1509 Value::Call(predicate) => {
1510 self.query_for_predicate(predicate.clone())?;
1511 }
1512 Value::Expression(_) => {
1513 return self.query_for_operation(term);
1514 }
1515 Value::Variable(sym) => {
1516 self.push_goal(
1517 if let VariableState::Bound(val) = self.variable_state(sym) {
1518 Goal::Query { term: val }
1519 } else {
1520 Goal::Unify {
1523 left: term.clone(),
1524 right: term!(true),
1525 }
1526 },
1527 )?
1528 }
1529 Value::Boolean(value) => {
1530 if !value {
1531 self.push_goal(Goal::Backtrack)?;
1533 }
1534
1535 return Ok(QueryEvent::None);
1536 }
1537 _ => {
1538 return self.type_error(
1540 term,
1541 format!(
1542 "{} isn't something that is true or false so can't be a condition",
1543 term
1544 ),
1545 );
1546 }
1547 }
1548 Ok(QueryEvent::None)
1549 }
1550
1551 fn query_for_predicate(&mut self, predicate: Call) -> PolarResult<()> {
1555 if predicate.kwargs.is_some() {
1556 return invalid_state(format!(
1557 "query_for_predicate: unexpected kwargs: {}",
1558 predicate
1559 ));
1560 }
1561 let goals = match self.kb.read().unwrap().get_generic_rule(&predicate.name) {
1562 None => {
1563 return Err(RuntimeError::QueryForUndefinedRule {
1564 name: predicate.name.0.clone(),
1565 }
1566 .into())
1567 }
1568 Some(generic_rule) => {
1569 if generic_rule.name != predicate.name {
1570 return invalid_state(format!(
1571 "query_for_predicate: different rule names: {} != {}",
1572 generic_rule.name, predicate.name
1573 ));
1574 }
1575
1576 let args = predicate.args.iter().map(|t| self.deref(t)).collect();
1578 let pre_filter = generic_rule.get_applicable_rules(&args);
1579
1580 self.polar_trace_mute = true;
1581
1582 vec![
1584 Goal::TraceStackPush,
1585 Goal::FilterRules {
1586 applicable_rules: vec![],
1587 unfiltered_rules: pre_filter,
1588 args: predicate.args,
1589 },
1590 Goal::TraceStackPop,
1591 ]
1592 }
1593 };
1594 self.append_goals(goals)
1595 }
1596
1597 fn query_for_operation(&mut self, term: &Term) -> PolarResult<QueryEvent> {
1598 let operation = term.as_expression().unwrap();
1599 let mut args = operation.args.clone();
1600 let wrong_arity = || invalid_state(format!("query_for_operation: wrong arity: {}", term));
1601 match operation.operator {
1602 Operator::And => {
1603 self.push_goal(Goal::TraceStackPop)?;
1605 self.append_goals(args.into_iter().map(|term| Goal::Query { term }))?;
1606 self.push_goal(Goal::TraceStackPush)?;
1607 }
1608 Operator::Or => {
1609 self.choose(args.into_iter().map(|term| vec![Goal::Query { term }]))?;
1611 }
1612 Operator::Not => {
1613 if args.len() != 1 {
1615 return wrong_arity();
1616 }
1617
1618 let term = args.pop().unwrap();
1619 let add_constraints = Rc::new(RefCell::new(Bindings::new()));
1620 let inverter = Box::new(Inverter::new(
1621 self,
1622 vec![Goal::Query { term }],
1623 add_constraints.clone(),
1624 self.bsp(),
1625 ));
1626 self.choose_conditional(
1627 vec![Goal::Run { runnable: inverter }],
1628 vec![Goal::AddConstraintsBatch { add_constraints }],
1629 vec![Goal::Backtrack],
1630 )?;
1631 }
1632 Operator::Assign => {
1633 if args.len() != 2 {
1634 return wrong_arity();
1635 }
1636 let right = args.pop().unwrap();
1637 let left = args.pop().unwrap();
1638 match (left.value(), right.value()) {
1639 (Value::Variable(var), _) => match self.variable_state(var) {
1640 VariableState::Unbound => {
1641 self.push_goal(Goal::Unify { left, right })?;
1642 }
1643 _ => {
1644 return self.type_error(
1645 &left,
1646 format!(
1647 "Can only assign to unbound variables, {} is not unbound.",
1648 var
1649 ),
1650 );
1651 }
1652 },
1653 _ => return self.type_error(&left, format!("Cannot assign to type {}.", left)),
1654 }
1655 }
1656
1657 Operator::Unify => {
1658 if args.len() != 2 {
1660 return wrong_arity();
1661 }
1662 let right = args.pop().unwrap();
1663 let left = args.pop().unwrap();
1664 self.push_goal(Goal::Unify { left, right })?
1665 }
1666 Operator::Dot => {
1667 return self.query_op_helper(term, Self::dot_op_helper, false, false);
1668 }
1669
1670 Operator::Lt
1671 | Operator::Gt
1672 | Operator::Leq
1673 | Operator::Geq
1674 | Operator::Eq
1675 | Operator::Neq => {
1676 return self.query_op_helper(term, Self::comparison_op_helper, true, true);
1677 }
1678
1679 Operator::Add
1680 | Operator::Sub
1681 | Operator::Mul
1682 | Operator::Div
1683 | Operator::Mod
1684 | Operator::Rem => {
1685 return self.query_op_helper(term, Self::arithmetic_op_helper, true, true);
1686 }
1687
1688 Operator::In => {
1689 return self.query_op_helper(term, Self::in_op_helper, false, true);
1690 }
1691
1692 Operator::Debug => {
1693 let message = self.debugger.break_msg(self).unwrap_or_else(|| {
1694 format!(
1695 "debug({})",
1696 args.iter()
1697 .map(|arg| self.deref(arg).to_string())
1698 .collect::<Vec<_>>()
1699 .join(", ")
1700 )
1701 });
1702 self.push_goal(Goal::Debug { message })?;
1703 }
1704 Operator::Print => {
1705 self.print(
1706 args.iter()
1707 .map(|arg| self.deref(arg).to_string())
1708 .collect::<Vec<_>>()
1709 .join(", "),
1710 );
1711 }
1712 Operator::New => {
1713 if args.len() != 2 {
1714 return wrong_arity();
1715 }
1716 let result = args.pop().unwrap();
1717 result.as_symbol()?; let constructor = args.pop().unwrap();
1719
1720 let instance_id = self.new_id();
1721
1722 let class = &constructor.as_call()?.name;
1723 let class_repr = if self.kb().is_constant(class) {
1724 Some(class.0.clone())
1725 } else {
1726 None
1727 };
1728 let instance =
1729 constructor.clone_with_value(Value::ExternalInstance(ExternalInstance {
1730 instance_id,
1731 constructor: Some(constructor.clone()),
1732 repr: Some(constructor.to_string()),
1733 class_repr,
1734 class_id: None,
1735 }));
1736
1737 self.append_goals(vec![
1740 Goal::Unify {
1741 left: result,
1742 right: instance,
1743 },
1744 Goal::MakeExternal {
1745 instance_id,
1746 constructor,
1747 },
1748 ])?;
1749 }
1750 Operator::Cut => {
1751 if self.query_contains_partial {
1752 return unsupported("cannot use cut with partial evaluation", term);
1753 }
1754
1755 let mut choice_index = self.choices.len();
1758 for choice in self.choices.iter().rev() {
1759 let prefix = &self.queries[..(self.queries.len() - 2)];
1761 if choice.queries.starts_with(prefix) {
1762 choice_index -= 1;
1765 } else {
1766 break;
1767 }
1768 }
1769
1770 self.push_goal(Goal::Cut { choice_index })?;
1771 }
1772 Operator::Isa => {
1773 if args.len() != 2 {
1775 return wrong_arity();
1776 }
1777 let right = args.pop().unwrap();
1778 let left = args.pop().unwrap();
1779 self.push_goal(Goal::Isa { left, right })?
1780 }
1781 Operator::ForAll => {
1782 if args.len() != 2 {
1783 return wrong_arity();
1784 }
1785 let action = args.pop().unwrap();
1786 let condition = args.pop().unwrap();
1787 let op = Operation {
1789 operator: Operator::Not,
1790 args: vec![term.clone_with_value(Value::Expression(Operation {
1791 operator: Operator::And,
1792 args: vec![
1793 condition,
1794 term.clone_with_value(Value::Expression(Operation {
1795 operator: Operator::Not,
1796 args: vec![action],
1797 })),
1798 ],
1799 }))],
1800 };
1801 let double_negation = term.clone_with_value(Value::Expression(op));
1802 self.push_goal(Goal::Query {
1803 term: double_negation,
1804 })?;
1805 }
1806 }
1807 Ok(QueryEvent::None)
1808 }
1809
1810 #[allow(clippy::many_single_char_names)]
1821 fn query_op_helper<F>(
1822 &mut self,
1823 term: &Term,
1824 eval: F,
1825 handle_unbound_left_var: bool,
1826 handle_unbound_right_var: bool,
1827 ) -> PolarResult<QueryEvent>
1828 where
1829 F: Fn(&mut Self, &Term) -> PolarResult<QueryEvent>,
1830 {
1831 let Operation { operator: op, args } = term.as_expression().unwrap();
1832
1833 let mut args = args.clone();
1834 if args.len() < 2 {
1835 return invalid_state(format!("query_op_helper: wrong arity: {}", term));
1836 }
1837 let left = &args[0];
1838 let right = &args[1];
1839
1840 match (left.value(), right.value()) {
1841 (
1850 Value::Expression(Operation {
1851 operator: Operator::Dot,
1852 args,
1853 }),
1854 _,
1855 ) if args.len() == 2 => {
1856 let var = term!(self.kb().gensym("rwdot"));
1857 let val = Value::Expression(Operation {
1858 operator: *op,
1859 args: vec![var.clone(), right.clone()],
1860 });
1861 let term = term.clone_with_value(val);
1862 self.push_goal(Goal::Query { term })?;
1863 self.push_goal(Goal::Unify {
1864 left: left.clone(),
1865 right: var,
1866 })?;
1867 return Ok(QueryEvent::None);
1868 }
1869
1870 (
1872 _,
1873 Value::Expression(Operation {
1874 operator: Operator::Dot,
1875 args,
1876 }),
1877 ) if args.len() == 2 => {
1878 let var = term!(self.kb().gensym("rwdot"));
1879 let val = Value::Expression(Operation {
1880 operator: *op,
1881 args: vec![left.clone(), var.clone()],
1882 });
1883 let term = term.clone_with_value(val);
1884 self.push_goal(Goal::Query { term })?;
1885 self.push_goal(Goal::Unify {
1886 left: var,
1887 right: right.clone(),
1888 })?;
1889 return Ok(QueryEvent::None);
1890 }
1891
1892 (Value::Expression(_), _)
1894 | (_, Value::Expression(_))
1895 | (Value::RestVariable(_), _)
1896 | (_, Value::RestVariable(_)) => {
1897 return invalid_state(format!("invalid query: {}", term));
1898 }
1899 _ => {}
1900 };
1901
1902 if let Value::Variable(r) = right.value() {
1903 if let VariableState::Bound(x) = self.variable_state(r) {
1904 args[1] = x;
1905 self.push_goal(Goal::Query {
1906 term: term.clone_with_value(Value::Expression(Operation {
1907 operator: *op,
1908 args,
1909 })),
1910 })?;
1911 return Ok(QueryEvent::None);
1912 } else if !handle_unbound_right_var && left.as_symbol().is_err() {
1913 return eval(self, term);
1914 }
1915 }
1916
1917 if let Value::Variable(l) = left.value() {
1918 if let VariableState::Bound(x) = self.variable_state(l) {
1919 args[0] = x;
1920 self.push_goal(Goal::Query {
1921 term: term.clone_with_value(Value::Expression(Operation {
1922 operator: *op,
1923 args,
1924 })),
1925 })?;
1926 return Ok(QueryEvent::None);
1927 } else if !handle_unbound_left_var && right.as_symbol().is_err() {
1928 return eval(self, term);
1929 }
1930 }
1931
1932 if left.as_symbol().is_ok() || right.as_symbol().is_ok() {
1933 self.add_constraint(term)?;
1934 return Ok(QueryEvent::None);
1935 }
1936
1937 eval(self, term)
1938 }
1939
1940 fn comparison_op_helper(&mut self, term: &Term) -> PolarResult<QueryEvent> {
1942 let Operation { operator: op, args } = term.as_expression().unwrap();
1943
1944 if args.len() != 2 {
1945 return invalid_state(format!("comparison_op_helper: wrong arity: {}", term));
1946 }
1947 let left = &args[0];
1948 let right = &args[1];
1949
1950 match (left.value(), right.value()) {
1951 (Value::ExternalInstance(_), _) | (_, Value::ExternalInstance(_)) => {
1952 let (call_id, answer) = self.new_call_var("external_op_result", false.into());
1954
1955 self.push_goal(Goal::Unify {
1957 left: answer,
1958 right: Term::from(true),
1959 })?;
1960
1961 Ok(QueryEvent::ExternalOp {
1963 call_id,
1964 operator: *op,
1965 args: vec![left.clone(), right.clone()],
1966 })
1967 }
1968 _ => {
1969 if !compare(*op, left, right, Some(term))? {
1970 self.push_goal(Goal::Backtrack)?;
1971 }
1972 Ok(QueryEvent::None)
1973 }
1974 }
1975 }
1976
1977 fn arithmetic_op_helper(&mut self, term: &Term) -> PolarResult<QueryEvent> {
1981 let Operation { operator: op, args } = term.as_expression().unwrap();
1982
1983 if args.len() != 3 {
1984 return invalid_state(format!("arithmetic_op_helper: wrong arity: {}", term));
1985 }
1986 let left = &args[0];
1987 let right = &args[1];
1988 let result = &args[2];
1989 result.as_symbol()?; match (left.value(), right.value()) {
1992 (Value::Number(left), Value::Number(right)) => {
1993 if let Some(answer) = match op {
1994 Operator::Add => *left + *right,
1995 Operator::Sub => *left - *right,
1996 Operator::Mul => *left * *right,
1997 Operator::Div => *left / *right,
1998 Operator::Mod => (*left).modulo(*right),
1999 Operator::Rem => *left % *right,
2000 _ => return unsupported(format!("numeric operation {}", op), term),
2001 } {
2002 self.push_goal(Goal::Unify {
2003 left: term.clone_with_value(Value::Number(answer)),
2004 right: result.clone(),
2005 })?;
2006 Ok(QueryEvent::None)
2007 } else {
2008 Err(RuntimeError::ArithmeticError { term: term.clone() }.into())
2009 }
2010 }
2011 (_, _) => unsupported(format!("unsupported arithmetic operands: {}", term), term),
2012 }
2013 }
2014
2015 fn dot_op_helper(&mut self, term: &Term) -> PolarResult<QueryEvent> {
2017 let Operation { args, .. } = term.as_expression().unwrap();
2018
2019 if args.len() != 3 {
2020 return invalid_state(format!("dot_op_helper: wrong arity: {}", term));
2021 }
2022 let mut args = args.clone();
2023 let object = &args[0];
2024 let field = &args[1];
2025 let value = &args[2];
2026
2027 match object.value() {
2028 Value::Dictionary(dict)
2030 if matches!(field.value(), Value::String(_) | Value::Variable(_)) =>
2031 {
2032 self.push_goal(Goal::Lookup {
2033 dict: dict.clone(),
2034 field: field.clone(),
2035 value: args.remove(2),
2036 })?
2037 }
2038 Value::Dictionary(_)
2040 | Value::ExternalInstance(_)
2041 | Value::List(_)
2042 | Value::Number(_)
2043 | Value::String(_) => {
2044 let answer = self.kb.read().unwrap().gensym("lookup_value");
2045 let call_id = self.new_call_id(&answer);
2046 self.append_goals(vec![
2047 Goal::LookupExternal {
2048 call_id,
2049 field: field.clone(),
2050 instance: object.clone(),
2051 },
2052 Goal::CheckError,
2053 Goal::Unify {
2054 left: value.clone(),
2055 right: Term::from(answer),
2056 },
2057 ])?;
2058 }
2059 Value::Variable(v) => {
2060 if matches!(field.value(), Value::Call(_)) {
2061 return unsupported(
2062 format!("cannot call method on unbound variable {}", v),
2063 object,
2064 );
2065 }
2066
2067 let dot2 = op!(Dot, object.clone(), field.clone());
2069 let value = self.deref(value);
2070 let term = Term::from(op!(Unify, value, dot2.into()));
2071 self.add_constraint(&term)?;
2072 }
2073 _ => {
2074 return self.type_error(
2075 object,
2076 format!(
2077 "can only perform lookups on dicts and instances, this is {}",
2078 object
2079 ),
2080 )
2081 }
2082 }
2083 Ok(QueryEvent::None)
2084 }
2085
2086 fn in_op_helper(&mut self, term: &Term) -> PolarResult<QueryEvent> {
2087 let Operation { args, .. } = term.as_expression().unwrap();
2088
2089 if args.len() != 2 {
2090 return invalid_state(format!("in_op_helper: wrong arity: {}", term));
2091 }
2092 let item = &args[0];
2093 let iterable = &args[1];
2094 let item_is_ground = item.is_ground();
2095
2096 match iterable.value() {
2097 Value::List(terms) => self.choose(
2099 terms
2100 .iter()
2101 .filter(|term| {
2102 !item_is_ground || !term.is_ground() || term.value() == item.value()
2103 })
2104 .map(|term| match term.value() {
2105 Value::RestVariable(v) => {
2106 let term = op!(In, item.clone(), Term::from(v.clone())).into();
2107 vec![Goal::Query { term }]
2108 }
2109 _ => vec![Goal::Unify {
2110 left: item.clone(),
2111 right: term.clone(),
2112 }],
2113 })
2114 .collect::<Vec<Goals>>(),
2115 )?,
2116 Value::Dictionary(dict) => self.choose(
2118 dict.fields
2119 .iter()
2120 .map(|(k, v)| {
2121 iterable.clone_with_value(Value::List(vec![
2122 v.clone_with_value(Value::String(k.0.clone())),
2123 v.clone(),
2124 ]))
2125 })
2126 .filter(|term| {
2127 !item_is_ground || !term.is_ground() || term.value() == item.value()
2128 })
2129 .map(|term| {
2130 vec![Goal::Unify {
2131 left: item.clone(),
2132 right: term,
2133 }]
2134 })
2135 .collect::<Vec<Goals>>(),
2136 )?,
2137 Value::String(s) => self.choose(
2140 s.chars()
2141 .map(|c| c.to_string())
2142 .map(Value::String)
2143 .filter(|c| !item_is_ground || c == item.value())
2144 .map(|c| {
2145 vec![Goal::Unify {
2146 left: item.clone(),
2147 right: iterable.clone_with_value(c),
2148 }]
2149 })
2150 .collect::<Vec<Goals>>(),
2151 )?,
2152 Value::ExternalInstance(_) => {
2154 let next_sym = self.kb.read().unwrap().gensym("next_value");
2159 let call_id = self.new_call_id(&next_sym);
2160
2161 self.append_goals(vec![
2164 Goal::NextExternal {
2165 call_id,
2166 iterable: self.deref(iterable),
2167 },
2168 Goal::Unify {
2169 left: item.clone(),
2170 right: Term::from(next_sym),
2171 },
2172 ])?;
2173 }
2174 _ => {
2175 return self.type_error(
2176 iterable,
2177 format!(
2178 "can only use `in` on an iterable value, this is {:?}",
2179 iterable.value()
2180 ),
2181 );
2182 }
2183 }
2184 Ok(QueryEvent::None)
2185 }
2186
2187 fn unify(&mut self, left: &Term, right: &Term) -> PolarResult<()> {
2194 match (left.value(), right.value()) {
2195 (Value::Expression(op), other) | (other, Value::Expression(op)) => {
2196 match op {
2197 Operation {
2201 operator: Operator::Dot,
2202 args,
2203 } if args.len() == 2 => {
2204 let term = Term::from(op!(
2205 Dot,
2206 args[0].clone(),
2207 args[1].clone(),
2208 Term::from(other.clone())
2209 ));
2210 self.push_goal(Goal::Query { term })?
2211 }
2212 _ => {
2214 return self.type_error(
2215 left,
2216 format!("cannot unify expressions directly `{}` = `{}`", left, right),
2217 )
2218 }
2219 }
2220 }
2221 (Value::Pattern(_), _) | (_, Value::Pattern(_)) => {
2222 return self.type_error(
2223 left,
2224 format!("cannot unify patterns directly `{}` = `{}`", left, right),
2225 );
2226 }
2227
2228 (Value::Variable(l), Value::Variable(r))
2231 | (Value::Variable(l), Value::RestVariable(r))
2232 | (Value::RestVariable(l), Value::Variable(r))
2233 | (Value::RestVariable(l), Value::RestVariable(r)) => {
2234 if l != r {
2241 match (self.variable_state(l), self.variable_state(r)) {
2242 (VariableState::Bound(x), VariableState::Bound(y)) => {
2243 self.push_goal(Goal::Unify { left: x, right: y })?;
2245 }
2246 _ => {
2247 if self.bind(l, right.clone()).is_err() {
2249 self.push_goal(Goal::Backtrack)?;
2250 }
2251 }
2252 }
2253 }
2254 }
2255
2256 (Value::Variable(var), _) | (Value::RestVariable(var), _) => {
2264 let right = right.clone();
2265 match self.variable_state(var) {
2266 VariableState::Bound(value) => {
2267 self.push_goal(Goal::Unify { left: value, right })?;
2268 }
2269 _ => {
2270 if self.bind(var, right).is_err() {
2271 self.push_goal(Goal::Backtrack)?;
2272 }
2273 }
2274 }
2275 }
2276
2277 (_, Value::Variable(var)) | (_, Value::RestVariable(var)) => {
2279 let left = left.clone();
2280 match self.variable_state(var) {
2281 VariableState::Bound(value) => {
2282 self.push_goal(Goal::Unify { left, right: value })?;
2283 }
2284 _ => {
2285 if self.bind(var, left).is_err() {
2286 self.push_goal(Goal::Backtrack)?;
2287 }
2288 }
2289 }
2290 }
2291
2292 (Value::Call(left), Value::Call(right)) => {
2294 if left.kwargs.is_some() || right.kwargs.is_some() {
2295 return invalid_state("unify: unexpected kwargs");
2297 }
2298 if left.name == right.name && left.args.len() == right.args.len() {
2299 self.append_goals(left.args.iter().zip(right.args.iter()).map(
2300 |(left, right)| Goal::Unify {
2301 left: left.clone(),
2302 right: right.clone(),
2303 },
2304 ))?;
2305 } else {
2306 self.push_goal(Goal::Backtrack)?
2307 }
2308 }
2309
2310 (Value::List(l), Value::List(r)) => self.unify_lists(l, r, |(l, r)| Goal::Unify {
2312 left: l.clone(),
2313 right: r.clone(),
2314 })?,
2315
2316 (Value::Dictionary(left), Value::Dictionary(right)) => {
2317 let left_fields: HashSet<&Symbol> = left.fields.keys().collect();
2319 let right_fields: HashSet<&Symbol> = right.fields.keys().collect();
2320 if left_fields != right_fields {
2321 self.push_goal(Goal::Backtrack)?;
2322 return Ok(());
2323 }
2324
2325 for (k, v) in left.fields.iter() {
2327 let right = right.fields.get(k).expect("fields should be equal").clone();
2328 self.push_goal(Goal::Unify {
2329 left: v.clone(),
2330 right,
2331 })?
2332 }
2333 }
2334
2335 (Value::Number(left), Value::Number(right)) => {
2337 if left != right {
2338 self.push_goal(Goal::Backtrack)?;
2339 }
2340 }
2341
2342 (Value::String(left), Value::String(right)) => {
2343 if left != right {
2344 self.push_goal(Goal::Backtrack)?;
2345 }
2346 }
2347
2348 (Value::Boolean(left), Value::Boolean(right)) => {
2350 if left != right {
2351 self.push_goal(Goal::Backtrack)?;
2352 }
2353 }
2354
2355 (
2356 Value::ExternalInstance(ExternalInstance {
2357 instance_id: left, ..
2358 }),
2359 Value::ExternalInstance(ExternalInstance {
2360 instance_id: right, ..
2361 }),
2362 ) if left == right => (),
2363
2364 (Value::ExternalInstance(_), _) | (_, Value::ExternalInstance(_)) => {
2368 self.push_goal(Goal::Query {
2369 term: Term::from(Operation {
2370 operator: Operator::Eq,
2371 args: vec![left.clone(), right.clone()],
2372 }),
2373 })?
2374 }
2375
2376 (_, _) => self.push_goal(Goal::Backtrack)?,
2378 }
2379
2380 Ok(())
2381 }
2382
2383 #[allow(clippy::ptr_arg)]
2387 fn unify_lists<F>(&mut self, left: &TermList, right: &TermList, unify: F) -> PolarResult<()>
2388 where
2389 F: FnMut((&Term, &Term)) -> Goal,
2390 {
2391 if has_rest_var(left) && has_rest_var(right) {
2392 self.unify_two_lists_with_rest(left, right, unify)
2393 } else if has_rest_var(left) {
2394 self.unify_rest_list_with_list(left, right, unify)
2395 } else if has_rest_var(right) {
2396 self.unify_rest_list_with_list(right, left, unify)
2397 } else if left.len() == right.len() {
2398 self.append_goals(left.iter().zip(right).map(unify))
2400 } else {
2401 self.push_goal(Goal::Backtrack)
2402 }
2403 }
2404
2405 #[allow(clippy::ptr_arg)]
2408 fn unify_two_lists_with_rest<F>(
2409 &mut self,
2410 rest_list_a: &TermList,
2411 rest_list_b: &TermList,
2412 mut unify: F,
2413 ) -> PolarResult<()>
2414 where
2415 F: FnMut((&Term, &Term)) -> Goal,
2416 {
2417 if rest_list_a.len() == rest_list_b.len() {
2418 let n = rest_list_b.len() - 1;
2419 let rest = unify((&rest_list_b[n].clone(), &rest_list_a[n].clone()));
2420 self.append_goals(
2421 rest_list_b
2422 .iter()
2423 .take(n)
2424 .zip(rest_list_a)
2425 .map(unify)
2426 .chain(vec![rest]),
2427 )
2428 } else {
2429 let (shorter, longer) = {
2430 if rest_list_a.len() < rest_list_b.len() {
2431 (rest_list_a, rest_list_b)
2432 } else {
2433 (rest_list_b, rest_list_a)
2434 }
2435 };
2436 let n = shorter.len() - 1;
2437 let rest = unify((&shorter[n].clone(), &Term::from(longer[n..].to_vec())));
2438 self.append_goals(
2439 shorter
2440 .iter()
2441 .take(n)
2442 .zip(longer)
2443 .map(unify)
2444 .chain(vec![rest]),
2445 )
2446 }
2447 }
2448
2449 #[allow(clippy::ptr_arg)]
2452 fn unify_rest_list_with_list<F>(
2453 &mut self,
2454 rest_list: &TermList,
2455 list: &TermList,
2456 mut unify: F,
2457 ) -> PolarResult<()>
2458 where
2459 F: FnMut((&Term, &Term)) -> Goal,
2460 {
2461 let n = rest_list.len() - 1;
2462 if list.len() >= n {
2463 let rest = unify((&rest_list[n].clone(), &Term::from(list[n..].to_vec())));
2464 self.append_goals(
2465 rest_list
2466 .iter()
2467 .take(n)
2468 .zip(list)
2469 .map(unify)
2470 .chain(vec![rest]),
2471 )
2472 } else {
2473 self.push_goal(Goal::Backtrack)
2474 }
2475 }
2476
2477 #[allow(clippy::ptr_arg)]
2480 fn filter_rules(
2481 &mut self,
2482 applicable_rules: &Rules,
2483 unfiltered_rules: &Rules,
2484 args: &TermList,
2485 ) -> PolarResult<()> {
2486 if unfiltered_rules.is_empty() {
2487 if applicable_rules.is_empty() {
2490 self.log(LogLevel::Info, || "No matching rules found", &[]);
2491 }
2492
2493 self.push_goal(Goal::SortRules {
2494 rules: applicable_rules.iter().rev().cloned().collect(),
2495 args: args.clone(),
2496 outer: 1,
2497 inner: 1,
2498 })
2499 } else {
2500 let mut unfiltered_rules = unfiltered_rules.clone();
2502 let rule = unfiltered_rules.pop().unwrap();
2503
2504 let inapplicable = Goal::FilterRules {
2505 args: args.clone(),
2506 applicable_rules: applicable_rules.clone(),
2507 unfiltered_rules: unfiltered_rules.clone(),
2508 };
2509 if rule.params.len() != args.len() {
2510 return self.push_goal(inapplicable); }
2512
2513 let mut applicable_rules = applicable_rules.clone();
2514 applicable_rules.push(rule.clone());
2515 let applicable = Goal::FilterRules {
2516 args: args.clone(),
2517 applicable_rules,
2518 unfiltered_rules,
2519 };
2520
2521 if rule.is_ground() {
2523 return self.push_goal(applicable);
2524 }
2525
2526 let Rule { params, .. } = self.rename_rule_vars(&rule);
2529 let mut check_applicability = vec![];
2530 for (arg, param) in args.iter().zip(params.iter()) {
2531 check_applicability.push(Goal::Unify {
2532 left: arg.clone(),
2533 right: param.parameter.clone(),
2534 });
2535 if let Some(specializer) = ¶m.specializer {
2536 check_applicability.push(Goal::Isa {
2537 left: arg.clone(),
2538 right: specializer.clone(),
2539 });
2540 }
2541 }
2542 self.choose_conditional(check_applicability, vec![applicable], vec![inapplicable])?;
2543 Ok(())
2544 }
2545 }
2546
2547 #[allow(clippy::ptr_arg)]
2556 fn sort_rules(
2557 &mut self,
2558 rules: &Rules,
2559 args: &TermList,
2560 outer: usize,
2561 inner: usize,
2562 ) -> PolarResult<()> {
2563 if rules.is_empty() {
2564 return self.push_goal(Goal::Backtrack);
2565 } else if outer > rules.len() {
2566 return invalid_state("bad outer index");
2567 } else if inner > rules.len() {
2568 return invalid_state("bad inner index");
2569 } else if inner > outer {
2570 return invalid_state("bad insertion sort state");
2571 }
2572
2573 let next_outer = Goal::SortRules {
2574 rules: rules.clone(),
2575 args: args.clone(),
2576 outer: outer + 1,
2577 inner: outer + 1,
2578 };
2579 if outer < rules.len() {
2582 if inner > 0 {
2583 let compare = Goal::IsMoreSpecific {
2584 left: rules[inner].clone(),
2585 right: rules[inner - 1].clone(),
2586 args: args.clone(),
2587 };
2588
2589 let mut rules = rules.clone();
2590 rules.swap(inner - 1, inner);
2591 let next_inner = Goal::SortRules {
2592 rules,
2593 outer,
2594 inner: inner - 1,
2595 args: args.clone(),
2596 };
2597
2598 self.choose_conditional(vec![compare], vec![next_inner], vec![next_outer])?;
2601 } else {
2602 if inner != 0 {
2603 return invalid_state("inner == 0");
2604 }
2605 self.push_goal(next_outer)?;
2606 }
2607 } else {
2608 self.polar_trace_mute = false;
2612 self.log(
2613 LogLevel::Info,
2614 || {
2615 let mut rule_strs = "APPLICABLE_RULES:".to_owned();
2616 for rule in rules {
2617 let context = rule
2618 .parsed_context()
2619 .map_or_else(|| "".into(), Context::source_position);
2620
2621 write!(rule_strs, "\n {}{}", rule.head_as_string(), context).unwrap();
2622 }
2623 rule_strs
2624 },
2625 &[],
2626 );
2627
2628 let mut alternatives = Vec::with_capacity(rules.len());
2629 for rule in rules.iter() {
2630 let mut goals = Vec::with_capacity(2 * args.len() + 4);
2631 goals.push(Goal::TraceRule {
2632 trace: Rc::new(Trace {
2633 node: Node::Rule(rule.clone()),
2634 children: vec![],
2635 }),
2636 });
2637 goals.push(Goal::TraceStackPush);
2638 let Rule { body, params, .. } = self.rename_rule_vars(rule);
2639
2640 for (arg, param) in args.iter().zip(params.iter()) {
2642 goals.push(Goal::Unify {
2643 left: arg.clone(),
2644 right: param.parameter.clone(),
2645 });
2646 if let Some(specializer) = ¶m.specializer {
2647 goals.push(Goal::Isa {
2648 left: param.parameter.clone(),
2649 right: specializer.clone(),
2650 });
2651 }
2652 }
2653
2654 goals.push(Goal::Query { term: body.clone() });
2656 goals.push(Goal::TraceStackPop);
2657
2658 alternatives.push(goals)
2659 }
2660
2661 self.choose(alternatives)?;
2663 }
2664 Ok(())
2665 }
2666
2667 #[allow(clippy::ptr_arg, clippy::wrong_self_convention)]
2669 fn is_more_specific(&mut self, left: &Rule, right: &Rule, args: &TermList) -> PolarResult<()> {
2670 let zipped = left.params.iter().zip(right.params.iter()).zip(args.iter());
2671 for ((left_param, right_param), arg) in zipped {
2672 match (&left_param.specializer, &right_param.specializer) {
2673 (Some(left_spec), Some(right_spec))
2680 if self.kb.read().unwrap().is_union(left_spec)
2681 && self.kb.read().unwrap().is_union(right_spec) => {}
2682 (Some(left_spec), Some(_)) if self.kb.read().unwrap().is_union(left_spec) => {
2685 return self.push_goal(Goal::Backtrack)
2686 }
2687 (Some(_), Some(right_spec)) if self.kb.read().unwrap().is_union(right_spec) => {
2689 return Ok(())
2690 }
2691
2692 (Some(left_spec), Some(right_spec)) => {
2693 if left_spec != right_spec {
2698 let answer = self.kb.read().unwrap().gensym("is_subspecializer");
2699 self.bind(&answer, Term::from(false)).unwrap();
2705
2706 return self.append_goals(vec![
2707 Goal::IsSubspecializer {
2708 answer: answer.clone(),
2709 left: left_spec.clone(),
2710 right: right_spec.clone(),
2711 arg: arg.clone(),
2712 },
2713 Goal::Unify {
2714 left: Term::from(answer),
2715 right: Term::from(true),
2716 },
2717 ]);
2718 }
2719 }
2720 (None, Some(_)) => return self.push_goal(Goal::Backtrack),
2723 (Some(_), None) => return Ok(()),
2726 (None, None) => (),
2728 }
2729 }
2730 self.push_goal(Goal::Backtrack)
2732 }
2733
2734 #[allow(clippy::wrong_self_convention)]
2736 fn is_subspecializer(
2737 &mut self,
2738 answer: &Symbol,
2739 left: &Term,
2740 right: &Term,
2741 arg: &Term,
2742 ) -> PolarResult<QueryEvent> {
2743 let arg = self.deref(arg);
2744 match (arg.value(), left.value(), right.value()) {
2745 (
2746 Value::ExternalInstance(instance),
2747 Value::Pattern(Pattern::Instance(left_lit)),
2748 Value::Pattern(Pattern::Instance(right_lit)),
2749 ) => {
2750 let call_id = self.new_call_id(answer);
2751 let instance_id = instance.instance_id;
2752 if left_lit.tag == right_lit.tag
2753 && !(left_lit.fields.fields.is_empty() && right_lit.fields.fields.is_empty())
2754 {
2755 self.push_goal(Goal::IsSubspecializer {
2756 answer: answer.clone(),
2757 left: left.clone_with_value(Value::Pattern(Pattern::Dictionary(
2758 left_lit.fields.clone(),
2759 ))),
2760 right: right.clone_with_value(Value::Pattern(Pattern::Dictionary(
2761 right_lit.fields.clone(),
2762 ))),
2763 arg,
2764 })?;
2765 }
2766 Ok(QueryEvent::ExternalIsSubSpecializer {
2768 call_id,
2769 instance_id,
2770 left_class_tag: left_lit.tag.clone(),
2771 right_class_tag: right_lit.tag.clone(),
2772 })
2773 }
2774 (
2775 _,
2776 Value::Pattern(Pattern::Dictionary(left)),
2777 Value::Pattern(Pattern::Dictionary(right)),
2778 ) => {
2779 let left_fields: HashSet<&Symbol> = left.fields.keys().collect();
2780 let right_fields: HashSet<&Symbol> = right.fields.keys().collect();
2781
2782 if left_fields.len() != right_fields.len() {
2786 self.rebind_external_answer(
2787 answer,
2788 Term::from(right_fields.len() < left.fields.len()),
2789 );
2790 }
2791 Ok(QueryEvent::None)
2792 }
2793 (_, Value::Pattern(Pattern::Instance(_)), Value::Pattern(Pattern::Dictionary(_))) => {
2794 self.rebind_external_answer(answer, Term::from(true));
2795 Ok(QueryEvent::None)
2796 }
2797 _ => {
2798 self.rebind_external_answer(answer, Term::from(false));
2799 Ok(QueryEvent::None)
2800 }
2801 }
2802 }
2803
2804 pub fn term_source(&self, term: &Term, include_info: bool) -> String {
2805 let source_info = term.parsed_context();
2806
2807 let mut source_string = if let Some(context) = source_info {
2808 let chars = context.source.src.chars();
2809 chars.take(context.right).skip(context.left).collect()
2810 } else {
2811 term.to_string()
2812 };
2813
2814 if include_info {
2815 if let Some(context) = source_info {
2816 source_string += &context.source_position();
2817 }
2818 }
2819
2820 source_string
2821 }
2822
2823 fn type_error<T>(&self, term: &Term, msg: String) -> PolarResult<T> {
2824 Err(RuntimeError::TypeError {
2825 msg,
2826 stack_trace: self.stack_trace(),
2827 term: term.clone(),
2828 }
2829 .into())
2830 }
2831
2832 fn run_runnable(&mut self, runnable: Box<dyn Runnable>) -> PolarResult<QueryEvent> {
2833 let (call_id, answer) = self.new_call_var("runnable_result", Value::Boolean(false));
2834 self.push_goal(Goal::Unify {
2835 left: answer,
2836 right: Term::from(true),
2837 })?;
2838
2839 Ok(QueryEvent::Run { runnable, call_id })
2840 }
2841
2842 pub fn external_error(&mut self, message: String) -> PolarResult<()> {
2844 self.external_error = Some(message);
2845 Ok(())
2846 }
2847}
2848
2849impl Runnable for PolarVirtualMachine {
2850 fn run(&mut self, _: Option<&mut Counter>) -> PolarResult<QueryEvent> {
2855 if self.query_start_time.is_none() {
2856 #[cfg(not(target_arch = "wasm32"))]
2857 let query_start_time = Some(std::time::Instant::now());
2858 #[cfg(target_arch = "wasm32")]
2859 let query_start_time = Some(js_sys::Date::now());
2860 self.query_start_time = query_start_time;
2861 }
2862
2863 if self.goals.is_empty() {
2864 if self.choices.is_empty() {
2865 return Ok(QueryEvent::Done { result: true });
2866 } else {
2867 self.backtrack()?;
2868 }
2869 }
2870
2871 while let Some(goal) = self.goals.pop() {
2872 match self.next(goal.clone())? {
2873 QueryEvent::None => (),
2874 event => {
2875 self.external_error = None;
2876 return Ok(event);
2877 }
2878 }
2879 self.maybe_break(DebugEvent::Goal(goal.clone()))?;
2880 }
2881
2882 if self.tracing {
2883 for t in &self.trace {
2884 self.log(LogLevel::Trace, || format!("trace\n{}", t.draw(self)), &[]);
2885 }
2886 }
2887
2888 let trace = if self.tracing {
2889 let trace = self.trace.first().cloned();
2890 trace.map(|trace| TraceResult {
2891 formatted: trace.draw(self),
2892 trace,
2893 })
2894 } else {
2895 None
2896 };
2897
2898 let mut bindings = self.bindings(true);
2899 if !self.inverting {
2900 match simplify_bindings_opt(bindings, false) {
2901 Ok(Some(bs)) => {
2902 bindings = bs;
2904 }
2905 Ok(None) => {
2906 return Ok(QueryEvent::None);
2909 }
2910
2911 Err(RuntimeError::UnhandledPartial { term, ref var }) => {
2912 let Binding(original_var_name, simplified) = get_binding_for_var(&var.0, self);
2914
2915 fn try_to_add_context(t: &Term, simplified: Term) -> Term {
2924 struct GetSource {
2926 term: Option<Term>,
2927 }
2928
2929 impl Visitor for GetSource {
2930 fn visit_term(&mut self, t: &Term) {
2931 if self.term.is_none() {
2932 if t.parsed_context().is_none() {
2933 walk_term(self, t)
2934 } else {
2935 self.term = Some(t.clone())
2936 }
2937 }
2938 }
2939 }
2940
2941 let mut source_getter = GetSource { term: None };
2942 source_getter.visit_term(t);
2943 if let Some(term_with_context) = source_getter.term {
2944 term_with_context.clone_with_value(simplified.value().clone())
2945 } else {
2946 simplified
2947 }
2948 }
2949
2950 return Err(RuntimeError::UnhandledPartial {
2954 term: try_to_add_context(&term, simplified),
2955 var: original_var_name,
2956 }
2957 .into());
2958 }
2959 Err(e) => unreachable!("unexpected error: {}", e.to_string()),
2960 }
2961
2962 bindings = bindings
2963 .clone()
2964 .into_iter()
2965 .filter(|(var, _)| !var.is_temporary_var())
2966 .map(|(var, value)| (var.clone(), sub_this(var, value)))
2967 .collect();
2968 }
2969
2970 self.log(
2971 LogLevel::Info,
2972 || {
2973 if bindings.is_empty() {
2974 "RESULT: SUCCESS".to_string()
2975 } else {
2976 let mut out = "RESULT: {\n".to_string(); for (key, value) in &bindings {
2978 writeln!(out, " {}: {}", key, value).unwrap(); }
2980 out.push('}'); out
2982 }
2983 },
2984 &[],
2985 );
2986
2987 Ok(QueryEvent::Result { bindings, trace })
2988 }
2989
2990 fn handle_error(&mut self, error: PolarError) -> PolarResult<QueryEvent> {
2991 if self.maybe_break(DebugEvent::Error(error.clone()))? {
2993 let g = self.goals.pop().unwrap();
2994 self.push_goal(Goal::Error { error })?;
2995 self.goals.push(g);
2996 Ok(QueryEvent::None)
2997 } else {
2998 Err(error)
2999 }
3000 }
3001
3002 fn external_question_result(&mut self, call_id: u64, answer: bool) -> PolarResult<()> {
3004 let var = self.call_id_symbols.remove(&call_id).expect("bad call id");
3005 self.rebind_external_answer(&var, Term::from(answer));
3006 Ok(())
3007 }
3008
3009 fn external_call_result(&mut self, call_id: u64, term: Option<Term>) -> PolarResult<()> {
3016 if let Some(value) = term {
3020 self.log(LogLevel::Trace, || format!("=> {}", value), &[]);
3021
3022 let sym = self.get_call_sym(call_id).to_owned();
3024
3025 self.push_goal(Goal::Unify {
3026 left: Term::from(sym),
3027 right: value,
3028 })?;
3029 } else {
3030 self.log(LogLevel::Trace, || "=> No more results.", &[]);
3031
3032 self.call_id_symbols.remove(&call_id).expect("bad call ID");
3035
3036 let check_error = if let Some(goal) = self.goals.last() {
3037 matches!(*(*goal), Goal::CheckError)
3038 } else {
3039 false
3040 };
3041
3042 self.push_goal(Goal::Backtrack)?;
3043 self.push_goal(Goal::Cut {
3044 choice_index: self.choices.len() - 1,
3045 })?;
3046
3047 if check_error {
3048 self.push_goal(Goal::CheckError)?;
3049 }
3050 }
3051 Ok(())
3052 }
3053
3054 fn debug_command(&mut self, command: &str) -> PolarResult<()> {
3056 let mut debugger = self.debugger.clone();
3057 let maybe_goal = debugger.debug_command(command, self);
3058 if let Some(goal) = maybe_goal {
3059 self.push_goal(goal)?;
3060 }
3061 self.debugger = debugger;
3062 Ok(())
3063 }
3064
3065 fn clone_runnable(&self) -> Box<dyn Runnable> {
3066 Box::new(self.clone())
3067 }
3068}
3069
3070#[cfg(test)]
3071mod tests {
3072
3073 use permutohedron::Heap;
3074
3075 use super::*;
3076 use crate::error::ErrorKind;
3077 use crate::rewrites::unwrap_and;
3078
3079 impl PolarVirtualMachine {
3080 fn is_halted(&self) -> bool {
3082 self.goals.is_empty() && self.choices.is_empty()
3083 }
3084 }
3085
3086 macro_rules! query {
3097 ($term:expr) => {
3098 Goal::Query {
3099 term: term!($term)
3100 }
3101 };
3102 ($($term:expr),+) => {
3103 Goal::Query {
3104 term: term!(op!(And, $($term),+))
3105 }
3106 };
3107 }
3108
3109 macro_rules! assert_query_events {
3122 ($vm:ident, []) => {
3123 assert!($vm.is_halted());
3124 };
3125 ($vm:ident, [QueryEvent::Result{$result:expr}]) => {
3126 assert!(matches!($vm.run(None).unwrap(), QueryEvent::Result{bindings, ..} if bindings == $result));
3127 assert_query_events!($vm, []);
3128 };
3129 ($vm:ident, [QueryEvent::Result{$result:expr}, $($tail:tt)*]) => {
3130 assert!(matches!($vm.run(None).unwrap(), QueryEvent::Result{bindings, ..} if bindings == $result));
3131 assert_query_events!($vm, [$($tail)*]);
3132 };
3133 ($vm:ident, [$( $pattern:pat_param )|+ $( if $guard: expr )?]) => {
3134 assert!(matches!($vm.run(None).unwrap(), $($pattern)|+ $(if $guard)?));
3135 assert_query_events!($vm, []);
3136 };
3137 ($vm:ident, [$( $pattern:pat_param )|+ $( if $guard: expr )?, $($tail:tt)*]) => {
3138 assert!(matches!($vm.run(None).unwrap(), $($pattern)|+ $(if $guard)?));
3139 assert_query_events!($vm, [$($tail)*]);
3140 };
3141 }
3143
3144 #[test]
3145 #[allow(clippy::cognitive_complexity)]
3146 fn and_expression() {
3147 let f1 = rule!("f", [1]);
3148 let f2 = rule!("f", [2]);
3149
3150 let rule = GenericRule::new(sym!("f"), vec![Arc::new(f1), Arc::new(f2)]);
3151
3152 let mut kb = KnowledgeBase::new();
3153 kb.add_generic_rule(rule);
3154
3155 let goal = query!(op!(And));
3156
3157 let mut vm = PolarVirtualMachine::new_test(Arc::new(RwLock::new(kb)), false, vec![goal]);
3158 assert_query_events!(vm, [
3159 QueryEvent::Result{hashmap!()},
3160 QueryEvent::Done { result: true }
3161 ]);
3162
3163 assert!(vm.is_halted());
3164
3165 let f1 = term!(call!("f", [1]));
3166 let f2 = term!(call!("f", [2]));
3167 let f3 = term!(call!("f", [3]));
3168
3169 vm.push_goal(query!(op!(And, f1.clone()))).unwrap();
3171
3172 assert_query_events!(vm, [
3173 QueryEvent::Result{hashmap!{}},
3174 QueryEvent::Done { result: true }
3175 ]);
3176
3177 vm.push_goal(query!(f1.clone(), f2.clone())).unwrap();
3179 assert_query_events!(vm, [
3180 QueryEvent::Result{hashmap!{}},
3181 QueryEvent::Done { result: true }
3182 ]);
3183
3184 vm.push_goal(query!(op!(And, f3.clone()))).unwrap();
3186 assert_query_events!(vm, [QueryEvent::Done { result: true }]);
3187
3188 let mut parts = vec![f1, f2, f3];
3190 for permutation in Heap::new(&mut parts) {
3191 vm.push_goal(Goal::Query {
3192 term: Term::new_from_test(Value::Expression(Operation {
3193 operator: Operator::And,
3194 args: permutation,
3195 })),
3196 })
3197 .unwrap();
3198 assert_query_events!(vm, [QueryEvent::Done { result: true }]);
3199 }
3200 }
3201
3202 #[test]
3203 fn unify_expression() {
3204 let mut vm = PolarVirtualMachine::default();
3205 vm.push_goal(query!(op!(Unify, term!(1), term!(1))))
3206 .unwrap();
3207
3208 assert_query_events!(vm, [
3209 QueryEvent::Result{hashmap!{}},
3210 QueryEvent::Done { result: true }
3211 ]);
3212
3213 let q = op!(Unify, term!(1), term!(2));
3214 vm.push_goal(query!(q)).unwrap();
3215
3216 assert_query_events!(vm, [QueryEvent::Done { result: true }]);
3217 }
3218
3219 #[test]
3220 #[allow(clippy::cognitive_complexity)]
3221 fn isa_on_lists() {
3222 let mut vm = PolarVirtualMachine::default();
3223 let one = term!(1);
3224 let one_list = term!([1]);
3225 let one_two_list = term!([1, 2]);
3226 let two_one_list = term!([2, 1]);
3227 let empty_list = term!([]);
3228
3229 vm.push_goal(Goal::Isa {
3231 left: empty_list.clone(),
3232 right: empty_list.clone(),
3233 })
3234 .unwrap();
3235 assert!(
3236 matches!(vm.run(None).unwrap(), QueryEvent::Result{bindings, ..} if bindings.is_empty())
3237 );
3238 assert!(matches!(
3239 vm.run(None).unwrap(),
3240 QueryEvent::Done { result: true }
3241 ));
3242 assert!(vm.is_halted());
3243
3244 vm.push_goal(Goal::Isa {
3246 left: one_two_list.clone(),
3247 right: one_two_list.clone(),
3248 })
3249 .unwrap();
3250 assert!(
3251 matches!(vm.run(None).unwrap(), QueryEvent::Result{bindings, ..} if bindings.is_empty())
3252 );
3253 assert!(matches!(
3254 vm.run(None).unwrap(),
3255 QueryEvent::Done { result: true }
3256 ));
3257 assert!(vm.is_halted());
3258
3259 vm.push_goal(Goal::Isa {
3261 left: one_two_list.clone(),
3262 right: two_one_list,
3263 })
3264 .unwrap();
3265 assert!(matches!(
3266 vm.run(None).unwrap(),
3267 QueryEvent::Done { result: true }
3268 ));
3269 assert!(vm.is_halted());
3270
3271 vm.push_goal(Goal::Isa {
3273 left: one_list.clone(),
3274 right: one_two_list.clone(),
3275 })
3276 .unwrap();
3277 assert!(matches!(
3278 vm.run(None).unwrap(),
3279 QueryEvent::Done { result: true }
3280 ));
3281 assert!(vm.is_halted());
3282
3283 vm.push_goal(Goal::Isa {
3285 left: one_two_list.clone(),
3286 right: one_list.clone(),
3287 })
3288 .unwrap();
3289 assert!(matches!(
3290 vm.run(None).unwrap(),
3291 QueryEvent::Done { result: true }
3292 ));
3293 assert!(vm.is_halted());
3294
3295 vm.push_goal(Goal::Isa {
3297 left: one_list.clone(),
3298 right: empty_list.clone(),
3299 })
3300 .unwrap();
3301 assert!(matches!(
3302 vm.run(None).unwrap(),
3303 QueryEvent::Done { result: true }
3304 ));
3305 assert!(vm.is_halted());
3306
3307 vm.push_goal(Goal::Isa {
3309 left: empty_list,
3310 right: one_list.clone(),
3311 })
3312 .unwrap();
3313 assert!(matches!(
3314 vm.run(None).unwrap(),
3315 QueryEvent::Done { result: true }
3316 ));
3317 assert!(vm.is_halted());
3318
3319 vm.push_goal(Goal::Isa {
3321 left: one_list.clone(),
3322 right: one.clone(),
3323 })
3324 .unwrap();
3325 assert!(matches!(
3326 vm.run(None).unwrap(),
3327 QueryEvent::Done { result: true }
3328 ));
3329 assert!(vm.is_halted());
3330
3331 vm.push_goal(Goal::Isa {
3333 left: one,
3334 right: one_list,
3335 })
3336 .unwrap();
3337 assert!(matches!(
3338 vm.run(None).unwrap(),
3339 QueryEvent::Done { result: true }
3340 ));
3341 assert!(vm.is_halted());
3342
3343 vm.push_goal(Goal::Isa {
3345 left: one_two_list,
3346 right: term!([1, Value::RestVariable(sym!("rest"))]),
3347 })
3348 .unwrap();
3349 assert_query_events!(vm, [
3350 QueryEvent::Result{hashmap!{sym!("rest") => term!([2])}},
3351 QueryEvent::Done { result: true }
3352 ]);
3353 }
3354
3355 #[test]
3356 #[allow(clippy::cognitive_complexity)]
3357 fn isa_on_dicts() {
3358 let mut vm = PolarVirtualMachine::default();
3359 let dict = term!(btreemap! {
3360 sym!("x") => term!(1),
3361 sym!("y") => term!(2),
3362 });
3363 let dict_pattern = term!(pattern!(btreemap! {
3364 sym!("x") => term!(1),
3365 sym!("y") => term!(2),
3366 }));
3367 vm.push_goal(Goal::Isa {
3368 left: dict.clone(),
3369 right: dict_pattern.clone(),
3370 })
3371 .unwrap();
3372 assert_query_events!(vm, [QueryEvent::Result { hashmap!() }, QueryEvent::Done { result: true }]);
3373
3374 let different_dict_pattern = term!(pattern!(btreemap! {
3376 sym!("x") => term!(2),
3377 sym!("y") => term!(1),
3378 }));
3379 vm.push_goal(Goal::Isa {
3380 left: dict.clone(),
3381 right: different_dict_pattern,
3382 })
3383 .unwrap();
3384 assert_query_events!(vm, [QueryEvent::Done { result: true }]);
3385
3386 let empty_dict = term!(btreemap! {});
3387 let empty_dict_pattern = term!(pattern!(btreemap! {}));
3388 vm.push_goal(Goal::Isa {
3390 left: empty_dict.clone(),
3391 right: empty_dict_pattern.clone(),
3392 })
3393 .unwrap();
3394 assert_query_events!(vm, [QueryEvent::Result { hashmap!() }, QueryEvent::Done { result: true }]);
3395
3396 vm.push_goal(Goal::Isa {
3398 left: dict.clone(),
3399 right: empty_dict_pattern,
3400 })
3401 .unwrap();
3402 assert_query_events!(vm, [QueryEvent::Result { hashmap!() }, QueryEvent::Done { result: true }]);
3403
3404 vm.push_goal(Goal::Isa {
3406 left: empty_dict,
3407 right: dict_pattern.clone(),
3408 })
3409 .unwrap();
3410 assert_query_events!(vm, [QueryEvent::Done { result: true }]);
3411
3412 let subset_dict_pattern = term!(pattern!(btreemap! {sym!("x") => term!(1)}));
3413 vm.push_goal(Goal::Isa {
3415 left: dict,
3416 right: subset_dict_pattern,
3417 })
3418 .unwrap();
3419 assert_query_events!(vm, [QueryEvent::Result { hashmap!() }, QueryEvent::Done { result: true }]);
3420
3421 let subset_dict = term!(btreemap! {sym!("x") => term!(1)});
3423 vm.push_goal(Goal::Isa {
3424 left: subset_dict,
3425 right: dict_pattern,
3426 })
3427 .unwrap();
3428 assert_query_events!(vm, [QueryEvent::Done { result: true }]);
3429 }
3430
3431 #[test]
3432 fn unify_dicts() {
3433 let mut vm = PolarVirtualMachine::default();
3434 let left = term!(btreemap! {
3436 sym!("x") => term!(1),
3437 sym!("y") => term!(2),
3438 });
3439 let right = term!(btreemap! {
3440 sym!("x") => term!(1),
3441 sym!("y") => term!(2),
3442 });
3443 vm.push_goal(Goal::Unify {
3444 left: left.clone(),
3445 right,
3446 })
3447 .unwrap();
3448 assert_query_events!(vm, [QueryEvent::Result { hashmap!() }, QueryEvent::Done { result: true }]);
3449
3450 let right = term!(btreemap! {
3452 sym!("x") => term!(2),
3453 sym!("y") => term!(1),
3454 });
3455 vm.push_goal(Goal::Unify {
3456 left: left.clone(),
3457 right,
3458 })
3459 .unwrap();
3460 assert_query_events!(vm, [QueryEvent::Done { result: true }]);
3461
3462 vm.push_goal(Goal::Unify {
3464 left: term!(btreemap! {}),
3465 right: term!(btreemap! {}),
3466 })
3467 .unwrap();
3468 assert_query_events!(vm, [QueryEvent::Result { hashmap!() }, QueryEvent::Done { result: true }]);
3469
3470 vm.push_goal(Goal::Unify {
3472 left: left.clone(),
3473 right: term!(btreemap! {}),
3474 })
3475 .unwrap();
3476 assert_query_events!(vm, [QueryEvent::Done { result: true }]);
3477
3478 let right = term!(btreemap! {
3480 sym!("x") => term!(1),
3481 });
3482 vm.push_goal(Goal::Unify { left, right }).unwrap();
3483 assert_query_events!(vm, [QueryEvent::Done { result: true }]);
3484 }
3485
3486 #[test]
3487 fn unify_nested_dicts() {
3488 let mut vm = PolarVirtualMachine::default();
3489
3490 let left = term!(btreemap! {
3491 sym!("x") => term!(btreemap!{
3492 sym!("y") => term!(1)
3493 })
3494 });
3495 let right = term!(btreemap! {
3496 sym!("x") => term!(btreemap!{
3497 sym!("y") => term!(sym!("result"))
3498 })
3499 });
3500 vm.push_goal(Goal::Unify { left, right }).unwrap();
3501 assert_query_events!(vm, [QueryEvent::Result { hashmap!{sym!("result") => term!(1)} }, QueryEvent::Done { result: true }]);
3502 }
3503
3504 #[test]
3505 fn lookup() {
3506 let mut vm = PolarVirtualMachine::default();
3507
3508 let fields = btreemap! {
3509 sym!("x") => term!(1),
3510 };
3511 let dict = Dictionary { fields };
3512 vm.push_goal(Goal::Lookup {
3513 dict: dict.clone(),
3514 field: term!(string!("x")),
3515 value: term!(1),
3516 })
3517 .unwrap();
3518
3519 assert_query_events!(vm, [
3520 QueryEvent::Result{hashmap!{}}
3521 ]);
3522
3523 vm.push_goal(Goal::Lookup {
3525 dict: dict.clone(),
3526 field: term!(string!("x")),
3527 value: term!(2),
3528 })
3529 .unwrap();
3530
3531 assert_query_events!(vm, [QueryEvent::Done { result: true }]);
3532
3533 vm.push_goal(Goal::Lookup {
3535 dict,
3536 field: term!(string!("x")),
3537 value: term!(sym!("y")),
3538 })
3539 .unwrap();
3540 assert_query_events!(vm, [
3541 QueryEvent::Result{hashmap!{sym!("y") => term!(1)}}
3542 ]);
3543 }
3544
3545 #[test]
3546 fn debug() {
3547 let mut vm = PolarVirtualMachine::new_test(
3548 Arc::new(RwLock::new(KnowledgeBase::new())),
3549 false,
3550 vec![Goal::Debug {
3551 message: "Hello".to_string(),
3552 }],
3553 );
3554 assert!(matches!(
3555 vm.run(None).unwrap(),
3556 QueryEvent::Debug { message } if &message[..] == "Hello"
3557 ));
3558 }
3559
3560 #[test]
3561 fn halt() {
3562 let mut vm = PolarVirtualMachine::new_test(
3563 Arc::new(RwLock::new(KnowledgeBase::new())),
3564 false,
3565 vec![Goal::Halt],
3566 );
3567 let _ = vm.run(None).unwrap();
3568 assert_eq!(vm.goals.len(), 0);
3569 assert_eq!(vm.bindings(true).len(), 0);
3570 }
3571
3572 #[test]
3573 fn unify() {
3574 let x = sym!("x");
3575 let y = sym!("y");
3576 let vars = term!([x.clone(), y.clone()]);
3577 let zero = value!(0);
3578 let one = value!(1);
3579 let vals = term!([zero.clone(), one.clone()]);
3580 let mut vm = PolarVirtualMachine::new_test(
3581 Arc::new(RwLock::new(KnowledgeBase::new())),
3582 false,
3583 vec![Goal::Unify {
3584 left: vars,
3585 right: vals,
3586 }],
3587 );
3588 let _ = vm.run(None).unwrap();
3589 assert_eq!(vm.variable_state(&x), VariableState::Bound(term!(zero)));
3590 assert_eq!(vm.variable_state(&y), VariableState::Bound(term!(one)));
3591 }
3592
3593 #[test]
3594 fn unify_var() {
3595 let x = sym!("x");
3596 let y = sym!("y");
3597 let z = sym!("z");
3598 let one = term!(1);
3599 let two = term!(2);
3600
3601 let mut vm = PolarVirtualMachine::default();
3602
3603 vm.bind(&y, one.clone()).unwrap();
3605 vm.append_goals(vec![Goal::Unify {
3606 left: term!(x.clone()),
3607 right: term!(y),
3608 }])
3609 .unwrap();
3610 let _ = vm.run(None).unwrap();
3611 assert_eq!(vm.deref(&term!(x)), one);
3612 vm.backtrack().unwrap();
3613
3614 vm.bind(&z, one.clone()).unwrap();
3616 vm.append_goals(vec![Goal::Unify {
3617 left: term!(z.clone()),
3618 right: one.clone(),
3619 }])
3620 .unwrap();
3621 let _ = vm.run(None).unwrap();
3622 assert_eq!(vm.deref(&term!(z.clone())), one);
3623
3624 vm.append_goals(vec![Goal::Unify {
3626 left: term!(z.clone()),
3627 right: two,
3628 }])
3629 .unwrap();
3630 let _ = vm.run(None).unwrap();
3631 assert_eq!(vm.deref(&term!(z)), one);
3632 }
3633
3634 #[test]
3635 fn test_gen_var() {
3636 let vm = PolarVirtualMachine::default();
3637
3638 let rule = Rule::new_from_test(
3639 Symbol::new("foo"),
3640 vec![],
3641 Term::new_from_test(Value::Expression(Operation {
3642 operator: Operator::And,
3643 args: vec![
3644 term!(1),
3645 Term::new_from_test(Value::Variable(Symbol("x".to_string()))),
3646 Term::new_from_test(Value::Variable(Symbol("x".to_string()))),
3647 Term::new_from_test(Value::List(vec![Term::new_from_test(Value::Variable(
3648 Symbol("y".to_string()),
3649 ))])),
3650 ],
3651 })),
3652 );
3653
3654 let renamed_rule = vm.rename_rule_vars(&rule);
3655 let renamed_terms = unwrap_and(&renamed_rule.body);
3656 assert_eq!(renamed_terms[1].value(), renamed_terms[2].value());
3657 let x_value = match &renamed_terms[1].value() {
3658 Value::Variable(sym) => Some(sym.0.clone()),
3659 _ => None,
3660 };
3661 assert_eq!(x_value.unwrap(), "_x_1");
3662
3663 let y_value = match &renamed_terms[3].value() {
3664 Value::List(terms) => match &terms[0].value() {
3665 Value::Variable(sym) => Some(sym.0.clone()),
3666 _ => None,
3667 },
3668 _ => None,
3669 };
3670 assert_eq!(y_value.unwrap(), "_y_2");
3671 }
3672
3673 #[test]
3674 fn test_filter_rules() {
3675 let rule_a = Arc::new(rule!("bar", ["_"; instance!("a")]));
3676 let rule_b = Arc::new(rule!("bar", ["_"; instance!("b")]));
3677 let rule_1a = Arc::new(rule!("bar", [value!(1)]));
3678 let rule_1b = Arc::new(rule!("bar", ["_"; value!(1)]));
3679
3680 let gen_rule = GenericRule::new(sym!("bar"), vec![rule_a, rule_b, rule_1a, rule_1b]);
3681 let mut kb = KnowledgeBase::new();
3682 kb.add_generic_rule(gen_rule);
3683
3684 let kb = Arc::new(RwLock::new(kb));
3685
3686 let external_instance = Value::ExternalInstance(ExternalInstance {
3687 instance_id: 1,
3688 constructor: None,
3689 repr: None,
3690 class_repr: None,
3691 class_id: None,
3692 });
3693 let query = query!(call!("bar", [sym!("x")]));
3694 let mut vm = PolarVirtualMachine::new_test(kb.clone(), false, vec![query]);
3695 vm.bind(&sym!("x"), Term::new_from_test(external_instance))
3696 .unwrap();
3697
3698 let mut external_isas = vec![];
3699
3700 loop {
3701 match vm.run(None).unwrap() {
3702 QueryEvent::Done { .. } => break,
3703 QueryEvent::ExternalIsa {
3704 call_id, class_tag, ..
3705 } => {
3706 external_isas.push(class_tag.clone());
3707 vm.external_question_result(call_id, class_tag.0 == "a")
3709 .unwrap()
3710 }
3711 QueryEvent::ExternalOp { .. }
3712 | QueryEvent::ExternalIsSubSpecializer { .. }
3713 | QueryEvent::Result { .. } => (),
3714 e => panic!("Unexpected event: {:?}", e),
3715 }
3716 }
3717
3718 let expected = vec![sym!("b"), sym!("a"), sym!("a")];
3719 assert_eq!(external_isas, expected);
3720
3721 let query = query!(call!("bar", [sym!("x")]));
3722 let mut vm = PolarVirtualMachine::new_test(kb, false, vec![query]);
3723 vm.bind(&sym!("x"), Term::new_from_test(value!(1))).unwrap();
3724
3725 let mut results = vec![];
3726 loop {
3727 match vm.run(None).unwrap() {
3728 QueryEvent::Done { .. } => break,
3729 QueryEvent::ExternalIsa { .. } => (),
3730 QueryEvent::Result { bindings, .. } => results.push(bindings),
3731 _ => panic!("Unexpected event"),
3732 }
3733 }
3734
3735 assert_eq!(results.len(), 2);
3736 assert_eq!(
3737 results,
3738 vec![
3739 hashmap! {sym!("x") => term!(1)},
3740 hashmap! {sym!("x") => term!(1)},
3741 ]
3742 );
3743 }
3744
3745 #[test]
3746 fn test_sort_rules() {
3747 let bar_rule = GenericRule::new(
3749 sym!("bar"),
3750 vec![
3751 Arc::new(rule!("bar", ["_"; instance!("b"), "_"; instance!("a"), value!(3)])),
3752 Arc::new(rule!("bar", ["_"; instance!("a"), "_"; instance!("a"), value!(1)])),
3753 Arc::new(rule!("bar", ["_"; instance!("a"), "_"; instance!("b"), value!(2)])),
3754 Arc::new(rule!("bar", ["_"; instance!("b"), "_"; instance!("b"), value!(4)])),
3755 ],
3756 );
3757
3758 let mut kb = KnowledgeBase::new();
3759 kb.add_generic_rule(bar_rule);
3760
3761 let external_instance = Value::ExternalInstance(ExternalInstance {
3762 instance_id: 1,
3763 constructor: None,
3764 repr: None,
3765 class_repr: None,
3766 class_id: None,
3767 });
3768
3769 let mut vm = PolarVirtualMachine::new_test(
3770 Arc::new(RwLock::new(kb)),
3771 false,
3772 vec![query!(call!(
3773 "bar",
3774 [external_instance.clone(), external_instance, sym!("z")]
3775 ))],
3776 );
3777
3778 let mut results = Vec::new();
3779 loop {
3780 match vm.run(None).unwrap() {
3781 QueryEvent::Done { .. } => break,
3782 QueryEvent::Result { bindings, .. } => results.push(bindings),
3783 QueryEvent::ExternalIsSubSpecializer {
3784 call_id,
3785 left_class_tag,
3786 right_class_tag,
3787 ..
3788 } => {
3789 vm.external_question_result(call_id, left_class_tag < right_class_tag)
3791 .unwrap()
3792 }
3793 QueryEvent::MakeExternal { .. } => (),
3794
3795 QueryEvent::ExternalOp {
3796 operator: Operator::Eq,
3797 call_id,
3798 ..
3799 } => vm.external_question_result(call_id, true).unwrap(),
3800
3801 QueryEvent::ExternalIsa { call_id, .. } => {
3802 vm.external_question_result(call_id, true).unwrap()
3804 }
3805 _ => panic!("Unexpected event"),
3806 }
3807 }
3808
3809 assert_eq!(results.len(), 4);
3810 assert_eq!(
3811 results,
3812 vec![
3813 hashmap! {sym!("z") => term!(1)},
3814 hashmap! {sym!("z") => term!(2)},
3815 hashmap! {sym!("z") => term!(3)},
3816 hashmap! {sym!("z") => term!(4)},
3817 ]
3818 );
3819 }
3820
3821 #[test]
3822 fn test_is_subspecializer() {
3823 let mut vm = PolarVirtualMachine::default();
3824
3825 let arg = term!(Value::ExternalInstance(ExternalInstance {
3830 instance_id: 1,
3831 constructor: None,
3832 repr: None,
3833 class_repr: None,
3834 class_id: None,
3835 }));
3836 let left = term!(value!(Pattern::Instance(InstanceLiteral {
3837 tag: sym!("Any"),
3838 fields: Dictionary {
3839 fields: btreemap! {}
3840 }
3841 })));
3842 let right = term!(Value::Pattern(Pattern::Dictionary(Dictionary {
3843 fields: btreemap! {sym!("a") => term!("a")},
3844 })));
3845
3846 let answer = vm.kb.read().unwrap().gensym("is_subspecializer");
3847
3848 match vm.is_subspecializer(&answer, &left, &right, &arg).unwrap() {
3849 QueryEvent::None => (),
3850 event => panic!("Expected None, got {:?}", event),
3851 }
3852
3853 assert_eq!(
3854 vm.deref(&term!(Value::Variable(answer))),
3855 term!(value!(true))
3856 );
3857 }
3858
3859 #[test]
3860 fn test_timeout() {
3861 let vm = PolarVirtualMachine::default();
3862 assert!(vm.query_timeout_ms == DEFAULT_TIMEOUT_MS);
3863
3864 std::env::set_var("POLAR_TIMEOUT_MS", "0");
3865 let vm = PolarVirtualMachine::default();
3866 std::env::remove_var("POLAR_TIMEOUT_MS");
3867 assert!(vm.is_query_timeout_disabled());
3868
3869 std::env::set_var("POLAR_TIMEOUT_MS", "500");
3870 let mut vm = PolarVirtualMachine::default();
3871 std::env::remove_var("POLAR_TIMEOUT_MS");
3872 vm.set_stack_limit(std::usize::MAX);
3874
3875 loop {
3876 vm.push_goal(Goal::Noop).unwrap();
3877 vm.push_goal(Goal::MakeExternal {
3878 constructor: Term::from(true),
3879 instance_id: 1,
3880 })
3881 .unwrap();
3882 let result = vm.run(None);
3883 match result {
3884 Ok(event) => assert!(matches!(event, QueryEvent::MakeExternal { .. })),
3885 Err(err) => {
3886 assert!(matches!(
3887 err.0,
3888 ErrorKind::Runtime(RuntimeError::QueryTimeout { .. })
3889 ));
3890
3891 break;
3893 }
3894 }
3895 }
3896 }
3897
3898 #[test]
3899 fn test_prefiltering() {
3900 let bar_rule = GenericRule::new(
3901 sym!("bar"),
3902 vec![
3903 Arc::new(rule!("bar", [value!([1])])),
3904 Arc::new(rule!("bar", [value!([2])])),
3905 ],
3906 );
3907
3908 let mut kb = KnowledgeBase::new();
3909 kb.add_generic_rule(bar_rule);
3910
3911 let mut vm = PolarVirtualMachine::new_test(Arc::new(RwLock::new(kb)), false, vec![]);
3912 vm.bind(&sym!("x"), term!(1)).unwrap();
3913 let _ = vm.run(None);
3914 let _ = vm.next(Rc::new(query!(call!("bar", [value!([sym!("x")])]))));
3915 let next_goal = vm
3918 .goals
3919 .iter()
3920 .find(|g| matches!(g.as_ref(), Goal::FilterRules { .. }))
3921 .unwrap();
3922 let goal_debug = format!("{:#?}", next_goal);
3923 assert!(
3924 matches!(next_goal.as_ref(), Goal::FilterRules {
3925 ref applicable_rules, ref unfiltered_rules, ..
3926 } if unfiltered_rules.len() == 1 && applicable_rules.is_empty()),
3927 "Goal should contain just one prefiltered rule: {}",
3928 goal_debug
3929 );
3930 }
3931
3932 #[test]
3933 fn choose_conditional() {
3934 let mut vm = PolarVirtualMachine::new_test(
3935 Arc::new(RwLock::new(KnowledgeBase::new())),
3936 false,
3937 vec![],
3938 );
3939 let consequent = Goal::Debug {
3940 message: "consequent".to_string(),
3941 };
3942 let alternative = Goal::Debug {
3943 message: "alternative".to_string(),
3944 };
3945
3946 vm.choose_conditional(
3948 vec![Goal::Noop],
3949 vec![consequent.clone()],
3950 vec![alternative.clone()],
3951 )
3952 .unwrap();
3953 assert_query_events!(vm, [
3954 QueryEvent::Debug { message } if &message[..] == "consequent" && vm.is_halted(),
3955 QueryEvent::Done { result: true }
3956 ]);
3957
3958 vm.choose_conditional(
3960 vec![Goal::Backtrack],
3961 vec![consequent.clone()],
3962 vec![alternative.clone()],
3963 )
3964 .unwrap();
3965 assert_query_events!(vm, [
3966 QueryEvent::Debug { message } if &message[..] == "alternative" && vm.is_halted(),
3967 QueryEvent::Done { result: true }
3968 ]);
3969
3970 vm.choose_conditional(
3972 vec![
3973 Goal::Unify {
3974 left: term!(sym!("x")),
3975 right: term!(true),
3976 },
3977 query!(sym!("x")),
3978 ],
3979 vec![consequent],
3980 vec![alternative],
3981 )
3982 .unwrap();
3983 assert_query_events!(vm, [
3984 QueryEvent::Debug { message } if &message[..] == "consequent" && vm.bindings(true).is_empty() && vm.is_halted(),
3985 QueryEvent::Done { result: true }
3986 ]);
3987 }
3988
3989 #[test]
3990 fn test_log_level_should_print_for_level() {
3991 use LogLevel::*;
3992
3993 assert!(Trace.should_print_on_level(Trace));
3995 assert!(Trace.should_print_on_level(Debug));
3996 assert!(Trace.should_print_on_level(Info));
3997
3998 assert!(!Debug.should_print_on_level(Trace));
4000 assert!(Debug.should_print_on_level(Debug));
4001 assert!(Debug.should_print_on_level(Info));
4002
4003 assert!(!Info.should_print_on_level(Trace));
4005 assert!(!Info.should_print_on_level(Debug));
4006 assert!(Info.should_print_on_level(Info));
4007 }
4008}