polar_core/
vm.rs

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, // cuts all choices in range [choice_index..]
57    },
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 the `runnable`.
132    Run {
133        runnable: Box<dyn Runnable>,
134    },
135
136    /// Add a new constraint
137    AddConstraint {
138        term: Term,
139    },
140
141    /// TODO hack.
142    /// Add a new constraint
143    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,              // binding stack pointer
152    pub goals: GoalStack,  // goal stack snapshot
153    queries: Queries,      // query stack snapshot
154    trace: Vec<Rc<Trace>>, // trace snapshot
155    trace_stack: TraceStack,
156}
157
158pub type Choices = Vec<Choice>;
159/// Shortcut type alias for a list of goals
160pub 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    // Coerce booleans to integers.
196    // FIXME(gw) why??
197    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    /// Stacks.
229    pub goals: GoalStack,
230    binding_manager: BindingManager,
231    choices: Choices,
232    pub queries: Queries,
233
234    pub tracing: bool,
235    pub trace_stack: TraceStack, // Stack of traces higher up the tree.
236    pub trace: Vec<Rc<Trace>>,   // Traces for the current level of the trace tree.
237
238    // Errors from outside the vm.
239    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    /// Maximum size of goal stack
248    stack_limit: usize,
249
250    /// Binding stack constant below here.
251    csp: Bsp,
252
253    /// Interactive debugger.
254    pub debugger: Debugger,
255
256    /// Rules and types.
257    pub kb: Arc<RwLock<KnowledgeBase>>,
258
259    /// Call ID -> result variable name table.
260    call_id_symbols: HashMap<u64, Symbol>,
261
262    /// Logging flag.
263    log_level: Option<LogLevel>,
264
265    polar_log_stderr: bool,
266    polar_trace_mute: bool,
267
268    // Other flags.
269    pub query_contains_partial: bool,
270    pub inverting: bool,
271
272    /// Output messages.
273    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            // Messages will not be exposed, only use default() for testing.
283            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
295// Methods which aren't goals/instructions.
296impl PolarVirtualMachine {
297    /// Make a new virtual machine with an initial list of goals.
298    /// Reverse the goal list for the sanity of callers.
299    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` controls internal VM logging
332            log_level: None,
333            // `polar_log_stderr` prints things immediately to stderr
334            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        // TODO: @patrickod remove `RUST_LOG` from node lib & drop this option.
360        self.log_level = if rust_log.is_some() {
361            Some(LogLevel::Trace)
362        } else {
363            None
364        };
365
366        // The values `off` and `0` mute all logging and take precedence over any other coexisting value.
367        // If POLAR_LOG is specified we attempt to match the level requested, other default to INFO
368        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    /// Clone self, replacing the goal stack and retaining only the current bindings.
415    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    /// Try to achieve one goal. Return `Some(QueryEvent)` if an external
460    /// result is needed to achieve it, or `None` if it can run internally.
461    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    /// Push a goal onto the goal stack.
549    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    /// Push a non-trivial choice onto the choice stack.
564    ///
565    /// Params:
566    ///
567    /// - `alternatives`: an ordered list of alternatives to try in the choice.
568    ///   The first element is the first alternative to try.
569    ///
570    /// Do not modify the goals stack.  This function defers execution of the
571    /// choice until a backtrack occurs.  To immediately execute the choice on
572    /// top of the current stack, use `choose`.
573    fn push_choice<I>(&mut self, alternatives: I) -> PolarResult<()>
574    where
575        I: IntoIterator<Item = Goals>,
576        I::IntoIter: std::iter::DoubleEndedIterator,
577    {
578        // Make sure that alternatives are executed in order of first to last.
579        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    /// Push a choice onto the choice stack, and execute immediately by
601    /// pushing the first alternative onto the goals stack
602    ///
603    /// Params:
604    ///
605    /// - `alternatives`: an ordered list of alternatives to try in the choice.
606    ///   The first element is the first alternative to try.
607    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    /// If each goal of `conditional` succeeds, execute `consequent`;
622    /// otherwise, execute `alternative`. The branches are entered only
623    /// by backtracking so that bindings established during the execution
624    /// of `conditional` are always unwound.
625    fn choose_conditional(
626        &mut self,
627        mut conditional: Goals,
628        consequent: Goals,
629        mut alternative: Goals,
630    ) -> PolarResult<()> {
631        // If the conditional fails, cut the consequent.
632        let cut_consequent = Goal::Cut {
633            choice_index: self.choices.len(),
634        };
635        alternative.insert(0, cut_consequent);
636
637        // If the conditional succeeds, cut the alternative and backtrack to this choice point.
638        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    /// Push multiple goals onto the stack in reverse order.
649    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    /// Rebind an external answer variable.
658    ///
659    /// DO NOT USE THIS TO REBIND ANOTHER VARIABLE (see unsafe_rebind doc string).
660    fn rebind_external_answer(&mut self, var: &Symbol, val: Term) {
661        self.binding_manager.unsafe_rebind(var, val);
662    }
663
664    /// Push a binding onto the binding stack.
665    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    /// Add a single constraint operation to the variables referenced in it.
687    /// Precondition: Operation is either binary or ternary (binary + result var),
688    /// and at least one of the first two arguments is an unbound variable.
689    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    /// Augment the bindings stack with constants from a hash map.
699    /// There must be no temporaries bound yet.
700    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    /// Retrieve the current non-constant bindings as a hash map.
709    pub fn bindings(&self, include_temps: bool) -> Bindings {
710        self.binding_manager
711            .bindings_after(include_temps, &self.csp)
712    }
713
714    /// Retrieve internal binding stack for debugger.
715    pub fn bindings_debug(&self) -> &BindingStack {
716        self.binding_manager.bindings_debug()
717    }
718
719    /// Returns bindings for all vars used by terms in terms.
720    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    /// Return the current binding stack pointer.
729    fn bsp(&self) -> Bsp {
730        self.binding_manager.bsp()
731    }
732
733    /// Investigate the state of a variable at some point and return a variable state variant.
734    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    /// Investigate the current state of a variable and return a variable state variant.
739    fn variable_state(&self, variable: &Symbol) -> VariableState {
740        self.binding_manager.variable_state(variable)
741    }
742
743    /// Recursively dereference variables in a term, including subterms, except operations.
744    fn deref(&self, term: &Term) -> Term {
745        self.binding_manager.deep_deref(term)
746    }
747
748    /// Generate a fresh set of variables for a rule.
749    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    /// Push or print a message to the output stream.
756    #[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    /// Push or print a message to the WASM output stream.
767    #[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            // preserve the old `polar_log_mute` behavior which omits parameter
784            // specialization checking Unify, IsA and other events from the log
785            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                    // print BINDINGS: { .. } only for TRACE logs
800                    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    /// Get the query stack as a string for printing in error messages.
823    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        // Build linear stack from trace tree. Not just using query stack because it doesn't
828        // know about rules, query stack should really use this too.
829        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        // Only index queries, not rules. Rule nodes are just used as context for where the query
841        // comes from.
842        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            // Useful for debugging
895            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
907/// Implementations of instructions.
908impl PolarVirtualMachine {
909    /// Remove all bindings after the last choice point, and try the
910    /// next available alternative. If no choice is possible, halt.
911    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    /// Commit to the current choice.
956    fn cut(&mut self, index: usize) {
957        self.choices.truncate(index);
958    }
959
960    /// Clean up the query stack after completing a query.
961    fn pop_query(&mut self) {
962        self.queries.pop();
963    }
964
965    /// Interact with the debugger.
966    fn debug(&mut self, message: &str) -> QueryEvent {
967        // Query start time is reset when a debug event occurs.
968        self.query_start_time.take();
969
970        QueryEvent::Debug {
971            message: message.to_string(),
972        }
973    }
974
975    /// Halt the VM by clearing all goals and choices.
976    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    /// Comparison operator that essentially performs partial unification.
984    #[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                // A union (currently) only matches itself.
1000                //
1001                // TODO(gj): when we have unions beyond `Actor` and `Resource`, we'll need to be
1002                // smarter about this check since UnionA is more specific than UnionB if UnionA is
1003                // a member of UnionB.
1004                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            // TODO(gj): (Var, Rest) + (Rest, Var) cases might be unreachable.
1013            (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                // Two variables.
1018                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                // Check that the left is more specific than the right.
1057                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 each field on the right, isa its value against the corresponding value on
1064                // the left.
1065                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 each field in the dict, look up the corresponding field on the instance and
1080                // then isa them.
1081                for (field, right_value) in right.fields.iter() {
1082                    // Generate symbol for the lookup result and leave the variable unbound, so that unification with the result does not fail.
1083                    // Unification with the lookup result happens in `fn external_call_result()`.
1084                    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                // Check fields
1102                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                // attempt an in-core IsA check if we have the necessary
1110                // class_id information
1111                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                // default to IsaExternal when no `class_id` information is available
1134                } else {
1135                    // Check class
1136                    self.push_goal(Goal::IsaExternal {
1137                        instance: left.clone(),
1138                        literal: right_literal.clone(),
1139                    })?;
1140                }
1141            }
1142
1143            // Default case: x isa y if x = y.
1144            _ => 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                // Produce a constraint like left.field = value
1183                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                // TODO(gj): assert that a simplified expression contains at most 1 unification
1197                // involving a particular variable.
1198                // TODO(gj): Ensure `op!(And) matches X{}` doesn't die after these changes.
1199                let var = left.as_symbol()?;
1200
1201                // Get the existing partial on the LHS variable.
1202                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                // TODO (dhatch): what if there is more than one var = dot_op constraint?
1213                // What if the one there is is in a not, or an or, or something
1214                let lhss_of_matches = simplified
1215                    .constraints()
1216                    .into_iter()
1217                    .filter_map(|c| {
1218                        // If the simplified partial includes a constraint of form:
1219                        // `v = dot_op`, `dot_op = v`, or `v in dot_op`
1220                        // and the receiver of the dot operation is either
1221                        // `var` or an alias thereof, use the dot op as the LHS of the matches.
1222                        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                            // only look for var on the RHS of a unfication (i.e. not on the RHS of an `in`)
1229                            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                // Construct field-less matches operation.
1238                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                // Construct field constraints.
1256                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                // Run compatibility check.
1267                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            // if the RHS isn't a pattern or a dictionary, we'll fall back to unifying
1277            // this is not the _best_ behaviour, but it's what we've been doing
1278            // previously
1279            _ => self.push_goal(Goal::Unify {
1280                left: left.clone(),
1281                right: right.clone(),
1282            })?,
1283        }
1284        Ok(())
1285    }
1286
1287    /// To evaluate `left matches Union`, look up `Union`'s member classes and create a choicepoint
1288    /// to check if `left` matches any of them.
1289    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                    // attempt to unify dict key with field
1317                    // if `field` is bound, unification will only succeed for the matching key
1318                    // if `field` is unbound, unification will succeed for all keys
1319                    goals.push(Goal::Unify {
1320                        left: field.clone_with_value(Value::String(k.clone().0)),
1321                        right: field.clone(),
1322                    });
1323                    // attempt to unify dict value with result
1324                    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    /// Return an external call event to look up a field's value
1350    /// in an external instance. Push a `Goal::LookupExternal` as
1351    /// an alternative on the last choice point to poll for results.
1352    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        // add an empty choice point; lookups return only one value
1383        // but we'll want to cut if we get back nothing
1384        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        // add another choice point for the next result
1437        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    /// Query for the provided term.
1474    ///
1475    /// Uses the knowledge base to get an ordered list of rules.
1476    /// Creates a choice point over each rule, where each alternative
1477    /// consists of unifying the rule head with the arguments, then
1478    /// querying for each body clause.
1479    fn query(&mut self, term: &Term) -> PolarResult<QueryEvent> {
1480        // - Print INFO event for queries for rules.
1481        // - Print TRACE (a superset of INFO) event for all other queries.
1482        // - We filter out single-element ANDs, which many rule bodies take the form of, to instead
1483        //   log only their inner operations for readability|brevity reasons.
1484        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                        // variable was unbound
1521                        // apply a constraint to variable that it must be truthy
1522                        Goal::Unify {
1523                            left: term.clone(),
1524                            right: term!(true),
1525                        }
1526                    },
1527                )?
1528            }
1529            Value::Boolean(value) => {
1530                if !value {
1531                    // Backtrack if the boolean is false.
1532                    self.push_goal(Goal::Backtrack)?;
1533                }
1534
1535                return Ok(QueryEvent::None);
1536            }
1537            _ => {
1538                // everything else dies horribly and in pain
1539                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    /// Select applicable rules for predicate.
1552    /// Sort applicable rules by specificity.
1553    /// Create a choice over the applicable rules.
1554    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                // Pre-filter rules.
1577                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                // Filter rules by applicability.
1583                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                // Query for each conjunct.
1604                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                // Make an alternative Query for each disjunct.
1610                self.choose(args.into_iter().map(|term| vec![Goal::Query { term }]))?;
1611            }
1612            Operator::Not => {
1613                // Query in a sub-VM and invert the results.
1614                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                // Push a `Unify` goal
1659                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()?; // Ensure `result` is a variable.
1718                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                // A goal is used here in case the result is already bound to some external
1738                // instance.
1739                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                // Remove all choices created before this cut that are in the
1756                // current rule body.
1757                let mut choice_index = self.choices.len();
1758                for choice in self.choices.iter().rev() {
1759                    // Comparison excludes the rule body & cut operator (the last two elements of self.queries)
1760                    let prefix = &self.queries[..(self.queries.len() - 2)];
1761                    if choice.queries.starts_with(prefix) {
1762                        // If the choice has the same query stack as the current
1763                        // query stack, remove it.
1764                        choice_index -= 1;
1765                    } else {
1766                        break;
1767                    }
1768                }
1769
1770                self.push_goal(Goal::Cut { choice_index })?;
1771            }
1772            Operator::Isa => {
1773                // TODO (dhatch): Use query op helper.
1774                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                // For all is implemented as !(condition, !action).
1788                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    /// Handle variables & constraints as arguments to various operations.
1811    /// Calls the `eval` method to handle ground terms.
1812    ///
1813    /// Arguments:
1814    ///
1815    /// - handle_unbound_left_var: If set to `false`, allow `eval` to handle
1816    ///   operations with an unbound left variable, instead of adding a constraint.
1817    ///   Some operations, like `In`, emit new goals or choice points when the left
1818    ///   operand is a variable.
1819    /// - handle_unbound_right_var: Same as above but for the RHS. `Dot` uses this.
1820    #[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            // We may be querying a partial from the simplifier, which can contain
1842            // embedded binary (as opposed to ternary) dot operations. In that case
1843            // we introduce a new variable, unify it with the dot lookup, then query
1844            // against the variable instead.
1845            //
1846            // TODO(gw) take these out after the simplifier/inverter work better ...
1847            //
1848            // dot on the left
1849            (
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            // dot on the right
1871            (
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            // otherwise this isn't allowed.
1893            (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    /// Evaluate comparison operations.
1941    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                // Generate a symbol for the external result and bind to `false` (default).
1953                let (call_id, answer) = self.new_call_var("external_op_result", false.into());
1954
1955                // Check that the external result is `true` when we return.
1956                self.push_goal(Goal::Unify {
1957                    left: answer,
1958                    right: Term::from(true),
1959                })?;
1960
1961                // Emit an event for the external operation.
1962                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    // TODO(ap, dhatch): Rewrite 3-arg arithmetic ops as 2-arg + unify,
1978    // like we do for dots; e.g., `+(a, b, c)` → `c = +(a, b)`.
1979    /// Evaluate arithmetic operations.
1980    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()?; // Ensure `result` is a variable.
1990
1991        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    /// Push appropriate goals for lookups on dictionaries and instances.
2016    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            // Push a `Lookup` goal for simple field lookups on dictionaries.
2029            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            // Push an `ExternalLookup` goal for external instances and built-ins.
2039            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                // Translate `.(object, field, value)` → `value = .(object, field)`.
2068                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            // Unify item with each element of the list, skipping non-matching ground terms.
2098            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            // Unify item with each (k, v) pair of the dict, skipping non-matching ground terms.
2117            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            // Unify item with each element of the string
2138            // FIXME (gw): this seems strange, wouldn't a substring search make more sense?
2139            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            // Push an `ExternalLookup` goal for external instances
2153            Value::ExternalInstance(_) => {
2154                // Generate symbol for next result and leave the variable unbound, so that unification with the result does not fail
2155                // Unification of the `next_sym` variable with the result of `NextExternal` happens in `fn external_call_result()`
2156                // `external_call_result` is the handler for results from both `LookupExternal` and `NextExternal`, so neither can bind the
2157                // call ID variable to `false`.
2158                let next_sym = self.kb.read().unwrap().gensym("next_value");
2159                let call_id = self.new_call_id(&next_sym);
2160
2161                // append unify goal to be evaluated after
2162                // next result is fetched
2163                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    /// Unify `left` and `right` terms.
2188    ///
2189    /// Outcomes of a unification are:
2190    ///  - Successful unification => bind zero or more variables to values
2191    ///  - Recursive unification => more `Unify` goals are pushed onto the stack
2192    ///  - Failure => backtrack
2193    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                    // this branch handles dot ops that were rewritten for inclusion
2198                    // in a partial by Vm::dot_op_helper(), but then queried again after
2199                    // the partial was bound by Vm::bind().
2200                    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                    // otherwise this should never happen.
2213                    _ => {
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            // Unify two variables.
2229            // TODO(gj): (Var, Rest) + (Rest, Var) cases might be unreachable.
2230            (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                // FIXME(gw):
2235                // if the variables are the same the unification succeeds, so
2236                // we don't need to do anything. but this causes an inconsistency
2237                // with NaN where `nan = nan` is false but `x = nan and x = x` is
2238                // true. if we really want to keep the NaN equality semantics
2239                // maybe we can have `nan = nan` but not `nan == nan`?
2240                if l != r {
2241                    match (self.variable_state(l), self.variable_state(r)) {
2242                        (VariableState::Bound(x), VariableState::Bound(y)) => {
2243                            // Both variables are bound. Unify their values.
2244                            self.push_goal(Goal::Unify { left: x, right: y })?;
2245                        }
2246                        _ => {
2247                            // At least one variable is unbound. Bind it.
2248                            if self.bind(l, right.clone()).is_err() {
2249                                self.push_goal(Goal::Backtrack)?;
2250                            }
2251                        }
2252                    }
2253                }
2254            }
2255
2256            // FIXME(gw): i think we might actually want this, see the comment
2257            // above about unifying variables.
2258            // (Value::Number(Numeric::Float(a)),
2259            //  Value::Number(Numeric::Float(b)))
2260            //     if a.is_nan() && b.is_nan() => (),
2261
2262            // Unify/bind a variable on the left with/to the term on the right.
2263            (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            // Unify/bind a variable on the right with/to the term on the left.
2278            (_, 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            // Unify predicates like unifying heads
2293            (Value::Call(left), Value::Call(right)) => {
2294                if left.kwargs.is_some() || right.kwargs.is_some() {
2295                    // Handled in the parser.
2296                    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            // Unify lists by recursively unifying their elements.
2311            (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                // Check that the set of keys are the same.
2318                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 each value, push a unify goal.
2326                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            // Unify integers by value.
2336            (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            // Unify bools by value.
2349            (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            // If either operand is an external instance, let the host
2365            // compare them for equality. This handles unification between
2366            // "equivalent" host and native types transparently.
2367            (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            // Anything else fails.
2377            (_, _) => self.push_goal(Goal::Backtrack)?,
2378        }
2379
2380        Ok(())
2381    }
2382
2383    /// "Unify" two lists element-wise, respecting rest-variables.
2384    /// Used by both `unify` and `isa`; hence the third argument,
2385    /// a closure that builds sub-goals.
2386    #[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            // No rest-variables; unify element-wise.
2399            self.append_goals(left.iter().zip(right).map(unify))
2400        } else {
2401            self.push_goal(Goal::Backtrack)
2402        }
2403    }
2404
2405    /// Unify two list that end with a rest-variable with each other.
2406    /// A helper method for `unify_lists`.
2407    #[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    /// Unify a list that ends with a rest-variable with another that doesn't.
2450    /// A helper method for `unify_lists`.
2451    #[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    /// Filter rules to just those applicable to a list of arguments,
2478    /// then sort them by specificity.
2479    #[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            // The rules have been filtered. Sort them.
2488
2489            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            // Check one rule for applicability.
2501            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); // wrong arity
2511            }
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            // The prefilter already checks applicability for ground rules.
2522            if rule.is_ground() {
2523                return self.push_goal(applicable);
2524            }
2525
2526            // Rename the variables in the rule (but not the args).
2527            // This avoids clashes between arg vars and rule vars.
2528            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) = &param.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    /// Sort a list of rules with respect to a list of arguments
2548    /// using an explicit-state insertion sort.
2549    ///
2550    /// We maintain two indices for the sort, `outer` and `inner`. The `outer` index tracks our
2551    /// sorting progress. Every rule at or below `outer` is sorted; every rule above it is
2552    /// unsorted. The `inner` index tracks our search through the sorted sublist for the correct
2553    /// position of the candidate rule (the rule at the head of the unsorted portion of the
2554    /// list).
2555    #[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        // Because `outer` starts as `1`, if there is only one rule in the `Rules`, this check
2580        // fails and we jump down to the evaluation of that lone rule.
2581        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                // If the comparison fails, break out of the inner loop.
2599                // If the comparison succeeds, continue the inner loop with the swapped rules.
2600                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            // We're done; the rules are sorted.
2609            // Make alternatives for calling them.
2610
2611            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                // Unify the arguments with the formal parameters.
2641                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) = &param.specializer {
2647                        goals.push(Goal::Isa {
2648                            left: param.parameter.clone(),
2649                            right: specializer.clone(),
2650                        });
2651                    }
2652                }
2653
2654                // Query for the body clauses.
2655                goals.push(Goal::Query { term: body.clone() });
2656                goals.push(Goal::TraceStackPop);
2657
2658                alternatives.push(goals)
2659            }
2660
2661            // Choose the first alternative, and push a choice for the rest.
2662            self.choose(alternatives)?;
2663        }
2664        Ok(())
2665    }
2666
2667    /// Succeed if `left` is more specific than `right` with respect to `args`.
2668    #[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                // If both specs are unions, they have the same specificity regardless of whether
2674                // they're the same or different unions.
2675                //
2676                // TODO(gj): when we have unions beyond `Actor` and `Resource`, we'll need to be
2677                // smarter about this check since UnionA is more specific than UnionB if UnionA is
2678                // a member of UnionB.
2679                (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                // If left is a union and right is not, left cannot be more specific, so we
2683                // backtrack.
2684                (Some(left_spec), Some(_)) if self.kb.read().unwrap().is_union(left_spec) => {
2685                    return self.push_goal(Goal::Backtrack)
2686                }
2687                // If right is a union and left is not, left IS more specific, so we return.
2688                (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 you find two non-equal specializers, that comparison determines the relative
2694                    // specificity of the two rules completely. As soon as you have two specializers
2695                    // that aren't the same and you can compare them and ask which one is more specific
2696                    // to the relevant argument, you're done.
2697                    if left_spec != right_spec {
2698                        let answer = self.kb.read().unwrap().gensym("is_subspecializer");
2699                        // Bind answer to false as a starting point in case is subspecializer doesn't
2700                        // bind any result.
2701                        // This is done here for safety to avoid a bug where `answer` is unbound by
2702                        // `IsSubspecializer` and the `Unify` Goal just assigns it to `true` instead
2703                        // of checking that is is equal to `true`.
2704                        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                // If the left rule has no specializer and the right does, it is NOT more specific,
2721                // so we Backtrack (fail)
2722                (None, Some(_)) => return self.push_goal(Goal::Backtrack),
2723                // If the left rule has a specializer and the right does not, the left IS more specific,
2724                // so we return
2725                (Some(_), None) => return Ok(()),
2726                // If neither has a specializer, neither is more specific, so we continue to the next argument.
2727                (None, None) => (),
2728            }
2729        }
2730        // Fail on any of the above branches that do not return
2731        self.push_goal(Goal::Backtrack)
2732    }
2733
2734    /// Determine if `left` is a more specific specializer ("subspecializer") than `right`
2735    #[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                // check ordering based on the classes
2767                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                // The dictionary with more fields is taken as more specific.
2783                // The assumption here is that rules have already been filtered
2784                // for applicability.
2785                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    /// Handle an error coming from outside the vm.
2843    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    /// Run the virtual machine. While there are goals on the stack,
2851    /// pop them off and execute them one at a time until we have a
2852    /// `QueryEvent` to return. May be called multiple times to restart
2853    /// the machine.
2854    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                    // simplification succeeds
2903                    bindings = bs;
2904                }
2905                Ok(None) => {
2906                    // incompatible bindings; simplification fails
2907                    // do not return result
2908                    return Ok(QueryEvent::None);
2909                }
2910
2911                Err(RuntimeError::UnhandledPartial { term, ref var }) => {
2912                    // use the debugger to get the nicest possible version of this binding
2913                    let Binding(original_var_name, simplified) = get_binding_for_var(&var.0, self);
2914
2915                    // TODO(gj): `t` is a partial constructed in the VM, so we don't have any
2916                    // source context for it. We make a best effort to track down some relevant
2917                    // context by walking `t` in search of the first piece of source context we
2918                    // find.
2919                    //
2920                    // For a future refactor, we might consider using the `Term::clone_with_value`
2921                    // API to preserve source context when initially binding a variable to an
2922                    // `Expression`.
2923                    fn try_to_add_context(t: &Term, simplified: Term) -> Term {
2924                        /// `GetSource` walks a term & returns the _1st_ piece of source info it finds.
2925                        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                    // there was an unhandled partial in the bindings
2951                    // grab the context from the variable that was defined and
2952                    // set the context before returning
2953                    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(); // open curly & newline
2977                    for (key, value) in &bindings {
2978                        writeln!(out, "  {}: {}", key, value).unwrap(); // key-value pairs spaced w/ newlines
2979                    }
2980                    out.push('}'); // closing curly
2981                    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 we pushed a debug goal, push an error goal underneath it.
2992        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    /// Handle response to a predicate posed to the application, e.g., `ExternalIsa`.
3003    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    /// Handle an external result provided by the application.
3010    ///
3011    /// If the value is `Some(_)` then we have a result, and unify the
3012    /// symbol associated with the call ID to the result value. If the
3013    /// value is `None` then the external has no (more) results, so we
3014    /// backtrack to the choice point left by `Goal::LookupExternal`.
3015    fn external_call_result(&mut self, call_id: u64, term: Option<Term>) -> PolarResult<()> {
3016        // TODO: Open question if we need to pass errors back down to rust.
3017        // For example what happens if the call asked for a field that doesn't exist?
3018
3019        if let Some(value) = term {
3020            self.log(LogLevel::Trace, || format!("=> {}", value), &[]);
3021
3022            // Fetch variable to unify with call result.
3023            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            // No more results. Clean up, cut out the retry alternative,
3033            // and backtrack.
3034            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    /// Drive debugger.
3055    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        /// Return true if there is nothing left to do.
3081        fn is_halted(&self) -> bool {
3082            self.goals.is_empty() && self.choices.is_empty()
3083        }
3084    }
3085
3086    /// Shorthand for constructing Goal::Query.
3087    ///
3088    /// A one argument invocation assumes the 1st argument is the same
3089    /// parameters that can be passed to the term! macro.  In this invocation,
3090    /// typically the form `query!(op!(And, term!(TERM)))` will be used. The
3091    /// one argument form allows for queries with a top level operator other
3092    /// than AND.
3093    ///
3094    /// Multiple arguments `query!(f1, f2, f3)` result in a query with a root
3095    /// AND operator term.
3096    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 takes two arguments, the vm and a list-like structure of
3110    /// QueryEvents to expect.  It will call run() for each event in the second
3111    /// argument and pattern match to check that the event matches what is
3112    /// expected.  Then `vm.is_halted()` is checked.
3113    ///
3114    /// The QueryEvent list elements can either be:
3115    ///   - QueryEvent::Result{EXPR} where EXPR is a HashMap<Symbol, Term>.
3116    ///     This is shorthand for QueryEvent::Result{bindings} if bindings == EXPR.
3117    ///     Use btreemap! for EXPR from the maplit package to write inline hashmaps
3118    ///     to assert on.
3119    ///   - A pattern with optional guard accepted by matches!. (QueryEvent::Result
3120    ///     cannot be matched on due to the above rule.)
3121    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        // TODO (dhatch) Be able to use btreemap! to match on specific bindings.
3142    }
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        // Querying for f(1)
3170        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        // Querying for f(1), f(2)
3178        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        // Querying for f(3)
3185        vm.push_goal(query!(op!(And, f3.clone()))).unwrap();
3186        assert_query_events!(vm, [QueryEvent::Done { result: true }]);
3187
3188        // Querying for f(1), f(2), f(3)
3189        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        // [] isa []
3230        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        // [1,2] isa [1,2]
3245        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        // [1,2] isNOTa [2,1]
3260        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        // [1] isNOTa [1,2]
3272        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        // [1,2] isNOTa [1]
3284        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        // [1] isNOTa []
3296        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        // [] isNOTa [1]
3308        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        // [1] isNOTa 1
3320        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        // 1 isNOTa [1]
3332        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        // [1,2] isa [1, *rest]
3344        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        // Dicts with identical keys and different values DO NOT isa.
3375        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        // {} isa {}.
3389        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        // Non-empty dicts should isa against an empty dict.
3397        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        // Empty dicts should NOT isa against a non-empty dict.
3405        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        // Superset dict isa subset dict.
3414        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        // Subset dict isNOTa superset dict.
3422        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        // Dicts with identical keys and values unify.
3435        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        // Dicts with identical keys and different values DO NOT unify.
3451        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        // Empty dicts unify.
3463        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        // Empty dict should not unify against a non-empty dict.
3471        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        // Subset match should fail.
3479        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        // Lookup with incorrect value
3524        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        // Lookup with unbound value
3534        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        // Left variable bound to bound right variable.
3604        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        // Left variable bound to value.
3615        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        // Left variable bound to value, unify with something else, backtrack.
3625        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                    // Return `true` if the specified `class_tag` is `"a"`.
3708                    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        // Test sort rule by mocking ExternalIsSubSpecializer and ExternalIsa.
3748        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                    // For this test we sort classes lexically.
3790                    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                    // For this test, anything is anything.
3803                    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        // Test `is_subspecializer` case where:
3826        // - arg: `ExternalInstance`
3827        // - left: `InstanceLiteral`
3828        // - right: `Dictionary`
3829        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        // Turn this off so we don't hit it.
3873        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                    // End test.
3892                    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        // After calling the query goal we should be left with the
3916        // prefiltered rules
3917        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        // Check consequent path when conditional succeeds.
3947        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        // Check alternative path when conditional fails.
3959        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        // Ensure bindings are cleaned up after conditional.
3971        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        // TRACE
3994        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        // DEBUG
3999        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        // INFO
4004        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}