use prolog_parser::ast::*; use prolog_parser::string_list::StringList; use prolog_parser::tabled_rc::*; use crate::prolog::arithmetic::*; use crate::prolog::clause_types::*; use crate::prolog::forms::*; use crate::prolog::heap_iter::*; use crate::prolog::instructions::*; use crate::prolog::machine::INTERRUPT; use crate::prolog::machine::attributed_variables::*; use crate::prolog::machine::code_repo::CodeRepo; use crate::prolog::machine::copier::*; use crate::prolog::machine::heap::*; use crate::prolog::machine::machine_errors::*; use crate::prolog::machine::machine_indices::*; use crate::prolog::machine::machine_state::*; use crate::prolog::machine::stack::*; use crate::prolog::ordered_float::*; use crate::prolog::read::PrologStream; use crate::prolog::rug::{Integer, Rational}; use indexmap::{IndexMap, IndexSet}; use std::cmp::{max, min, Ordering}; use std::f64; use std::mem; macro_rules! try_numeric_result { ($s: ident, $e: expr, $caller: expr) => {{ match $e { Ok(val) => Ok(val), Err(e) => Err($s.error_form(MachineError::evaluation_error(e), $caller)), } }}; } macro_rules! try_or_fail { ($s:ident, $e:expr) => {{ match $e { Ok(val) => val, Err(msg) => { $s.throw_exception(msg); return; } } }}; } impl MachineState { pub(crate) fn new() -> Self { MachineState { s: 0, p: CodePtr::default(), b: 0, b0: 0, e: 0, num_of_args: 0, cp: LocalCodePtr::default(), attr_var_init: AttrVarInitializer::new(0, 0), fail: false, heap: Heap::with_capacity(1024), mode: MachineMode::Write, stack: Stack::new(), registers: vec![Addr::HeapCell(0); MAX_ARITY + 1], // self.registers[0] is never used. trail: vec![], pstr_trail: vec![], pstr_tr: 0, tr: 0, hb: 0, block: 0, ball: Ball::new(), lifted_heap: Vec::with_capacity(1024), interms: vec![Number::default(); 256], last_call: false, heap_locs: HeapVarDict::new(), flags: MachineFlags::default(), at_end_of_expansion: false } } pub(crate) fn with_capacity(capacity: usize) -> Self { MachineState { s: 0, p: CodePtr::default(), b: 0, b0: 0, e: 0, num_of_args: 0, cp: LocalCodePtr::default(), attr_var_init: AttrVarInitializer::new(0, 0), fail: false, heap: Heap::with_capacity(capacity), mode: MachineMode::Write, stack: Stack::new(), registers: vec![Addr::HeapCell(0); MAX_ARITY + 1], // self.registers[0] is never used. trail: vec![], pstr_trail: vec![], pstr_tr: 0, tr: 0, hb: 0, block: 0, ball: Ball::new(), lifted_heap: Vec::with_capacity(capacity), interms: vec![Number::default(); 0], last_call: false, heap_locs: HeapVarDict::new(), flags: MachineFlags::default(), at_end_of_expansion: false } } #[allow(dead_code)] pub fn print_heap(&self, start: usize) { for h in start .. self.heap.h { println!("{} : {}", h, self.heap[h]); } } #[inline] pub fn machine_flags(&self) -> MachineFlags { self.flags } pub(crate) fn store(&self, addr: Addr) -> Addr { match addr { Addr::AttrVar(h) | Addr::HeapCell(h) => self.heap[h].as_addr(h), Addr::StackCell(fr, sc) => self.stack.index_and_frame(fr)[sc].clone(), addr => addr, } } pub(crate) fn deref(&self, mut addr: Addr) -> Addr { loop { let value = self.store(addr.clone()); if value.is_ref() && value != addr { addr = value; continue; } return addr; } } fn bind_attr_var(&mut self, h: usize, addr: Addr) { match addr.as_var() { Some(Ref::HeapCell(hc)) => { self.heap[hc] = HeapCellValue::Addr(Addr::AttrVar(h)); self.trail(TrailRef::Ref(Ref::HeapCell(hc))); } Some(Ref::StackCell(fr, sc)) => { self.stack.index_and_frame_mut(fr)[sc] = Addr::AttrVar(h); self.trail(TrailRef::Ref(Ref::StackCell(fr, sc))); } _ => { self.push_attr_var_binding(h, addr.clone()); self.heap[h] = HeapCellValue::Addr(addr); self.trail(TrailRef::Ref(Ref::AttrVar(h))); } } } pub(super) fn bind(&mut self, r1: Ref, a2: Addr) { let t1 = self.store(r1.as_addr()); let t2 = self.store(a2.clone()); if t1.is_ref() && (!t2.is_ref() || a2 < r1) { match r1 { Ref::StackCell(fr, sc) => self.stack.index_and_frame_mut(fr)[sc] = t2, Ref::HeapCell(h) => self.heap[h] = HeapCellValue::Addr(t2), Ref::AttrVar(h) => return self.bind_attr_var(h, t2), }; self.trail(TrailRef::from(r1)); } else { match a2.as_var() { Some(Ref::StackCell(fr, sc)) => { self.stack.index_and_frame_mut(fr)[sc] = t1; self.trail(TrailRef::Ref(Ref::StackCell(fr, sc))); } Some(Ref::HeapCell(h)) => { self.heap[h] = HeapCellValue::Addr(t1); self.trail(TrailRef::Ref(Ref::HeapCell(h))); } Some(Ref::AttrVar(h)) => self.bind_attr_var(h, t1), None => {} } } } pub(super) fn unify_strings( &mut self, pdl: &mut Vec, s1: &mut StringList, s2: &mut StringList, ) -> bool { if let Some(c1) = s1.head() { if let Some(c2) = s2.head() { if c1 == c2 { pdl.push(Addr::Con(Constant::String(s1.tail()))); pdl.push(Addr::Con(Constant::String(s2.tail()))); return true; } } else if s2.is_expandable() { self.pstr_trail(s2.clone()); pdl.push(Addr::Con(Constant::String(s2.push_char(c1)))); pdl.push(Addr::Con(Constant::String(s1.tail()))); return true; } } else if s1.is_expandable() { if let Some(c) = s2.head() { self.pstr_trail(s1.clone()); pdl.push(Addr::Con(Constant::String(s1.push_char(c)))); pdl.push(Addr::Con(Constant::String(s2.tail()))); } else if s2.is_expandable() { return s1 == s2; } else { self.pstr_trail(s1.clone()); s1.set_expandable(false); } return true; } else if s2.head().is_none() { if s2.is_expandable() { self.pstr_trail(s2.clone()); } s2.set_expandable(false); return true; } false } fn deconstruct_chars( &mut self, s: &mut StringList, offset: usize, pdl: &mut Vec, ) -> bool { if let Some(c) = s.head() { pdl.push(Addr::Con(Constant::String(s.tail()))); pdl.push(Addr::HeapCell(offset + 1)); pdl.push(Addr::Con(Constant::Char(c))); pdl.push(Addr::HeapCell(offset)); return true; } else if s.is_expandable() { let prev_s = s.clone(); let mut stepper = |c| { let new_s = s.push_char(c); pdl.push(Addr::HeapCell(offset + 1)); pdl.push(Addr::Con(Constant::String(new_s))); }; match self.heap[offset].clone() { HeapCellValue::Addr(Addr::Con(Constant::Char(c))) => { self.pstr_trail(prev_s); stepper(c); return true; } HeapCellValue::Addr(Addr::Con(Constant::Atom(ref a, _))) => { if let Some(c) = a.as_str().chars().next() { if c.len_utf8() == a.as_str().len() { self.pstr_trail(prev_s); stepper(c); return true; } } } _ => {} } } false } fn deconstruct_codes( &mut self, s: &mut StringList, offset: usize, pdl: &mut Vec, ) -> bool { if let Some(c) = s.head() { pdl.push(Addr::Con(Constant::String(s.tail()))); pdl.push(Addr::HeapCell(offset + 1)); pdl.push(Addr::Con(Constant::CharCode(c as u8))); pdl.push(Addr::HeapCell(offset)); return true; } else if s.is_expandable() { let prev_s = s.clone(); let mut stepper = |c| { let new_s = s.push_char(c); pdl.push(Addr::HeapCell(offset + 1)); pdl.push(Addr::Con(Constant::String(new_s))); }; match self.heap[offset].clone() { HeapCellValue::Addr(Addr::Con(Constant::CharCode(c))) => { self.pstr_trail(prev_s); stepper(c as char); return true; } HeapCellValue::Addr(Addr::Con(Constant::Integer(n))) => { if let Some(c) = n.to_u8() { self.pstr_trail(prev_s); stepper(c as char); return true; } } _ => {} } } false } fn bind_with_occurs_check(&mut self, r: Ref, addr: Addr) { let mut fail = false; for value in self.acyclic_pre_order_iter(addr.clone()) { if let HeapCellValue::Addr(addr) = value { if let Some(inner_r) = addr.as_var() { if r == inner_r { fail = true; break; } } } } self.fail = fail; self.bind(r, addr); } pub(super) fn unify_with_occurs_check(&mut self, a1: Addr, a2: Addr) { let mut pdl = vec![a1, a2]; let mut tabu_list: IndexSet<(Addr, Addr)> = IndexSet::new(); self.fail = false; while !(pdl.is_empty() || self.fail) { let d1 = self.deref(pdl.pop().unwrap()); let d2 = self.deref(pdl.pop().unwrap()); if d1 != d2 { let d1 = self.store(d1); let d2 = self.store(d2); if tabu_list.contains(&(d1.clone(), d2.clone())) { continue; } else { tabu_list.insert((d1.clone(), d2.clone())); } match (d1.clone(), d2.clone()) { (Addr::AttrVar(h), addr) | (addr, Addr::AttrVar(h)) => { self.bind_with_occurs_check(Ref::AttrVar(h), addr) } (Addr::HeapCell(h), addr) | (addr, Addr::HeapCell(h)) => { self.bind_with_occurs_check(Ref::HeapCell(h), addr) } (Addr::StackCell(fr, sc), addr) | (addr, Addr::StackCell(fr, sc)) => { self.bind_with_occurs_check(Ref::StackCell(fr, sc), addr) } (Addr::Lis(a1), Addr::Str(a2)) | (Addr::Str(a2), Addr::Lis(a1)) => { if let &HeapCellValue::NamedStr(n2, ref f2, _) = &self.heap[a2] { if f2.as_str() == "." && n2 == 2 { pdl.push(Addr::HeapCell(a1)); pdl.push(Addr::HeapCell(a2 + 1)); pdl.push(Addr::HeapCell(a1 + 1)); pdl.push(Addr::HeapCell(a2 + 2)); continue; } } self.fail = true; } (Addr::Lis(a1), Addr::Con(Constant::String(ref mut s))) | (Addr::Con(Constant::String(ref mut s)), Addr::Lis(a1)) => { if match self.flags.double_quotes { DoubleQuotes::Chars => self.deconstruct_chars(s, a1, &mut pdl), DoubleQuotes::Codes => self.deconstruct_codes(s, a1, &mut pdl), DoubleQuotes::Atom => false, } { continue; } self.fail = true; } (Addr::Con(Constant::EmptyList), Addr::Con(Constant::String(ref s))) | (Addr::Con(Constant::String(ref s)), Addr::Con(Constant::EmptyList)) if !self.flags.double_quotes.is_atom() => { if s.is_expandable() && s.is_empty() { self.pstr_trail(s.clone()); s.set_expandable(false); continue; } self.fail = !s.is_empty(); } (Addr::Lis(a1), Addr::Lis(a2)) => { pdl.push(Addr::HeapCell(a1)); pdl.push(Addr::HeapCell(a2)); pdl.push(Addr::HeapCell(a1 + 1)); pdl.push(Addr::HeapCell(a2 + 1)); } ( Addr::Con(Constant::String(ref mut s1)), Addr::Con(Constant::String(ref mut s2)), ) => { self.fail = !(self.unify_strings(&mut pdl, s1, s2) || self.unify_strings(&mut pdl, s2, s1)) } (Addr::Con(ref c1), Addr::Con(ref c2)) => { if c1 != c2 { self.fail = true; } } (Addr::Str(a1), Addr::Str(a2)) => { let r1 = &self.heap[a1]; let r2 = &self.heap[a2]; if let &HeapCellValue::NamedStr(n1, ref f1, _) = r1 { if let &HeapCellValue::NamedStr(n2, ref f2, _) = r2 { if n1 == n2 && *f1 == *f2 { for i in 1..n1 + 1 { pdl.push(Addr::HeapCell(a1 + i)); pdl.push(Addr::HeapCell(a2 + i)); } continue; } } } self.fail = true; } _ => self.fail = true, }; } } } pub(super) fn unify(&mut self, a1: Addr, a2: Addr) { let mut pdl = vec![a1, a2]; let mut tabu_list: IndexSet<(Addr, Addr)> = IndexSet::new(); self.fail = false; while !(pdl.is_empty() || self.fail) { let d1 = self.deref(pdl.pop().unwrap()); let d2 = self.deref(pdl.pop().unwrap()); if d1 != d2 { let d1 = self.store(d1); let d2 = self.store(d2); if tabu_list.contains(&(d1.clone(), d2.clone())) { continue; } else { tabu_list.insert((d1.clone(), d2.clone())); } match (d1.clone(), d2.clone()) { (Addr::AttrVar(h), addr) | (addr, Addr::AttrVar(h)) => { self.bind(Ref::AttrVar(h), addr) } (Addr::HeapCell(h), _) => self.bind(Ref::HeapCell(h), d2), (_, Addr::HeapCell(h)) => self.bind(Ref::HeapCell(h), d1), (Addr::StackCell(fr, sc), _) => self.bind(Ref::StackCell(fr, sc), d2), (_, Addr::StackCell(fr, sc)) => self.bind(Ref::StackCell(fr, sc), d1), (Addr::Lis(a1), Addr::Str(a2)) | (Addr::Str(a2), Addr::Lis(a1)) => { if let &HeapCellValue::NamedStr(n2, ref f2, _) = &self.heap[a2] { if f2.as_str() == "." && n2 == 2 { pdl.push(Addr::HeapCell(a1)); pdl.push(Addr::HeapCell(a2 + 1)); pdl.push(Addr::HeapCell(a1 + 1)); pdl.push(Addr::HeapCell(a2 + 2)); continue; } } self.fail = true; } (Addr::Lis(a1), Addr::Con(Constant::String(ref mut s))) | (Addr::Con(Constant::String(ref mut s)), Addr::Lis(a1)) => { if match self.flags.double_quotes { DoubleQuotes::Chars => self.deconstruct_chars(s, a1, &mut pdl), DoubleQuotes::Codes => self.deconstruct_codes(s, a1, &mut pdl), DoubleQuotes::Atom => false, } { continue; } self.fail = true; } (Addr::Con(Constant::EmptyList), Addr::Con(Constant::String(ref s))) | (Addr::Con(Constant::String(ref s)), Addr::Con(Constant::EmptyList)) if !self.flags.double_quotes.is_atom() => { if s.is_expandable() && s.is_empty() { self.pstr_trail(s.clone()); s.set_expandable(false); continue; } self.fail = !s.is_empty(); } (Addr::Lis(a1), Addr::Lis(a2)) => { pdl.push(Addr::HeapCell(a1)); pdl.push(Addr::HeapCell(a2)); pdl.push(Addr::HeapCell(a1 + 1)); pdl.push(Addr::HeapCell(a2 + 1)); } ( Addr::Con(Constant::String(ref mut s1)), Addr::Con(Constant::String(ref mut s2)), ) => { self.fail = !(self.unify_strings(&mut pdl, s1, s2) || self.unify_strings(&mut pdl, s2, s1)) } (Addr::Con(ref c1), Addr::Con(ref c2)) => { if c1 != c2 { self.fail = true; } } (Addr::Str(a1), Addr::Str(a2)) => { let r1 = &self.heap[a1]; let r2 = &self.heap[a2]; if let &HeapCellValue::NamedStr(n1, ref f1, _) = r1 { if let &HeapCellValue::NamedStr(n2, ref f2, _) = r2 { if n1 == n2 && *f1 == *f2 { for i in 1..n1 + 1 { pdl.push(Addr::HeapCell(a1 + i)); pdl.push(Addr::HeapCell(a2 + i)); } continue; } } } self.fail = true; } _ => self.fail = true, }; } } } #[inline] fn pstr_trail(&mut self, s: StringList) { if let Some((prev_b, prev_s, _)) = self.pstr_trail.last().cloned() { if prev_b == self.b && prev_s == s { return; } } let truncate_end = s.len() + s.cursor(); self.pstr_trail.push((self.b, s, truncate_end)); self.pstr_tr += 1; } pub(super) fn trail(&mut self, r: TrailRef) { match r { TrailRef::Ref(Ref::HeapCell(h)) => { if h < self.hb { self.trail.push(TrailRef::Ref(Ref::HeapCell(h))); self.tr += 1; } } TrailRef::Ref(Ref::AttrVar(h)) => { if h < self.hb { self.trail.push(TrailRef::Ref(Ref::AttrVar(h))); self.tr += 1; } } TrailRef::AttrVarHeapLink(h) => { if h < self.hb { self.trail.push(TrailRef::AttrVarHeapLink(h)); self.tr += 1; } } TrailRef::AttrVarListLink(h, l) => { if h < self.hb { self.trail.push(TrailRef::AttrVarListLink(h, l)); self.tr += 1; } } TrailRef::Ref(Ref::StackCell(b, sc)) => { if b < self.b { self.trail.push(TrailRef::Ref(Ref::StackCell(b, sc))); self.tr += 1; } } } } pub(super) fn unwind_trail(&mut self, a1: usize, a2: usize) { // the sequence is reversed to respect the chronology of trail // additions, now that deleted attributes can be undeleted by // backtracking. for i in (a1..a2).rev() { match self.trail[i] { TrailRef::Ref(Ref::HeapCell(h)) => { self.heap[h] = HeapCellValue::Addr(Addr::HeapCell(h)) } TrailRef::Ref(Ref::AttrVar(h)) => { self.heap[h] = HeapCellValue::Addr(Addr::AttrVar(h)) } TrailRef::Ref(Ref::StackCell(fr, sc)) => { self.stack.index_and_frame_mut(fr)[sc] = Addr::StackCell(fr, sc) } TrailRef::AttrVarHeapLink(h) => { self.heap[h] = HeapCellValue::Addr(Addr::HeapCell(h)); } TrailRef::AttrVarListLink(h, l) => { self.heap[h] = HeapCellValue::Addr(Addr::Lis(l)); } } } } pub(super) fn unwind_pstr_trail(&mut self, a1: usize, a2: usize) { for i in a1..a2 { let (_, mut s, end) = self.pstr_trail[i].clone(); s.truncate(end); } } pub(super) fn tidy_pstr_trail(&mut self) { if self.b == 0 { return; } let b = self.b; let mut i = self.stack.index_or_frame(b).prelude.pstr_tr; while i < self.pstr_tr { let str_b = self.pstr_trail[i].0; if b < str_b { let pstr_tr = self.pstr_tr; let val = self.pstr_trail[pstr_tr - 1].clone(); self.pstr_trail[i] = val; self.pstr_tr -= 1; } else { i += 1; } } } pub(super) fn tidy_trail(&mut self) { if self.b == 0 { return; } let b = self.b; let hb = self.hb; let mut offset = 0; for i in self.stack.index_or_frame(b).prelude.tr .. self.tr { match self.trail[i] { TrailRef::Ref(Ref::AttrVar(tr_i)) | TrailRef::Ref(Ref::HeapCell(tr_i)) | TrailRef::AttrVarHeapLink(tr_i) | TrailRef::AttrVarListLink(tr_i, _) => { if tr_i >= hb { offset += 1; } else { self.trail[i - offset] = self.trail[i]; } } TrailRef::Ref(Ref::StackCell(b, _)) => { if b < self.b { self.trail[i - offset] = self.trail[i]; } else { offset += 1; } } } } self.tr -= offset; self.trail.truncate(self.tr); } #[inline] fn write_char_to_string(&mut self, s: &mut StringList, c: char) -> bool { self.pstr_trail(s.clone()); let new_s = s.push_char(c); self.heap .push(HeapCellValue::Addr(Addr::Con(Constant::String(new_s)))); false } fn write_constant_to_string(&mut self, s: &mut StringList, c: Constant) -> bool { match c { Constant::EmptyList if !self.flags.double_quotes.is_atom() => !s.is_empty(), Constant::String(ref s2) if s.is_expandable() && s2.starts_with(s) => { self.pstr_trail(s.clone()); s.append_suffix(s2); s.set_expandable(s2.is_expandable()); false } Constant::String(s2) => s.borrow()[s.cursor()..] != s2.borrow()[s2.cursor()..], Constant::Atom(ref a, _) if a.as_str().starts_with(&s.borrow()[s.cursor()..]) => { if let Some(c) = a.as_str().chars().next() { if c.len_utf8() == a.as_str().len() { // detect chars masquerading as atoms. if s.is_empty() { self.write_char_to_string(s, c); } false } else { true } } else { true } } Constant::Char(ref c) if s.is_empty() && s.is_expandable() => { match self.flags.double_quotes { DoubleQuotes::Chars => self.write_char_to_string(s, *c), _ => false, } } Constant::Char(ref c) => match self.flags.double_quotes { DoubleQuotes::Chars => { if s.borrow().chars().next() == Some(*c) && c.len_utf8() == s.len() { s.set_expandable(false); false } else { true } } _ => false, }, Constant::CharCode(ref c) if s.is_empty() && s.is_expandable() => { match self.flags.double_quotes { DoubleQuotes::Codes => self.write_char_to_string(s, *c as char), _ => false, } } Constant::CharCode(ref c) => match self.flags.double_quotes { DoubleQuotes::Codes => { if s.borrow().chars().next() == Some(*c as char) && 1 == s.len() { s.set_expandable(false); false } else { true } } _ => false, }, _ => true, } } pub(super) fn write_constant_to_var(&mut self, addr: Addr, c: Constant) { match self.store(self.deref(addr)) { Addr::Con(Constant::String(ref mut s)) => { self.fail = self.write_constant_to_string(s, c) } Addr::Con(c1) => self.fail = self.eq_test(Addr::Con(c), Addr::Con(c1)), Addr::Lis(l) => self.unify(Addr::Lis(l), Addr::Con(c)), addr => { if let Some(r) = addr.as_var() { self.bind(r, Addr::Con(c)); } else { self.fail = true; } } }; } pub(super) fn get_number(&mut self, at: &ArithmeticTerm) -> Result { match at { &ArithmeticTerm::Reg(r) => self.arith_eval_by_metacall(r), &ArithmeticTerm::Interm(i) => Ok(mem::replace( &mut self.interms[i - 1], Number::Integer(Integer::from(0)), )), &ArithmeticTerm::Number(ref n) => Ok(n.clone()), } } fn rational_from_number( &self, n: Number, caller: &MachineStub, ) -> Result { match n { Number::Rational(r) => Ok(r), Number::Float(OrderedFloat(f)) => Rational::from_f64(f).ok_or_else(|| { self.error_form(MachineError::instantiation_error(), caller.clone()) }), Number::Integer(n) => Ok(Rational::from(n)), } } fn get_rational( &mut self, at: &ArithmeticTerm, caller: &MachineStub, ) -> Result { let n = self.get_number(at)?; self.rational_from_number(n, caller) } pub(super) fn arith_eval_by_metacall(&self, r: RegType) -> Result { let a = self[r].clone(); let caller = MachineError::functor_stub(clause_name!("(is)"), 2); let mut interms: Vec = Vec::with_capacity(64); for heap_val in self.post_order_iter(a) { match heap_val { HeapCellValue::NamedStr(2, name, _) => { let a2 = interms.pop().unwrap(); let a1 = interms.pop().unwrap(); match name.as_str() { "+" => interms.push(try_numeric_result!(self, a1 + a2, caller.clone())?), "-" => interms.push(try_numeric_result!(self, a1 - a2, caller.clone())?), "*" => interms.push(try_numeric_result!(self, a1 * a2, caller.clone())?), "/" => interms.push(self.div(a1, a2)?), "**" => interms.push(self.pow(a1, a2, "(is)")?), "^" => interms.push(self.int_pow(a1, a2)?), "max" => interms.push(self.max(a1, a2)?), "min" => interms.push(self.min(a1, a2)?), "rdiv" => { let r1 = self.rational_from_number(a1, &caller)?; let r2 = self.rational_from_number(a2, &caller)?; let result = Number::Rational(self.rdiv(r1, r2)?); interms.push(result) } "//" => interms.push(Number::Integer(self.idiv(a1, a2)?)), "div" => interms.push(Number::Integer(self.int_floor_div(a1, a2)?)), ">>" => interms.push(Number::Integer(self.shr(a1, a2)?)), "<<" => interms.push(Number::Integer(self.shl(a1, a2)?)), "/\\" => interms.push(Number::Integer(self.and(a1, a2)?)), "\\/" => interms.push(Number::Integer(self.or(a1, a2)?)), "xor" => interms.push(Number::Integer(self.xor(a1, a2)?)), "mod" => interms.push(Number::Integer(self.modulus(a1, a2)?)), "rem" => interms.push(Number::Integer(self.remainder(a1, a2)?)), "atan2" => interms.push(Number::Float(OrderedFloat(self.atan2(a1, a2)?))), "gcd" => interms.push(Number::Integer(self.gcd(a1, a2)?)), _ => { return Err(self.error_form(MachineError::instantiation_error(), caller)) } } } HeapCellValue::NamedStr(1, name, _) => { let a1 = interms.pop().unwrap(); match name.as_str() { "-" => interms.push(-a1), "+" => interms.push(a1), "cos" => interms.push(Number::Float(OrderedFloat(self.cos(a1)?))), "sin" => interms.push(Number::Float(OrderedFloat(self.sin(a1)?))), "tan" => interms.push(Number::Float(OrderedFloat(self.tan(a1)?))), "sqrt" => interms.push(Number::Float(OrderedFloat(self.sqrt(a1)?))), "log" => interms.push(Number::Float(OrderedFloat(self.log(a1)?))), "exp" => interms.push(Number::Float(OrderedFloat(self.exp(a1)?))), "acos" => interms.push(Number::Float(OrderedFloat(self.acos(a1)?))), "asin" => interms.push(Number::Float(OrderedFloat(self.asin(a1)?))), "atan" => interms.push(Number::Float(OrderedFloat(self.atan(a1)?))), "abs" => interms.push(a1.abs()), "float" => interms.push(Number::Float(OrderedFloat(self.float(a1)?))), "truncate" => interms.push(Number::Integer(self.truncate(a1))), "round" => interms.push(Number::Integer(self.round(a1)?)), "ceiling" => interms.push(Number::Integer(self.ceiling(a1))), "floor" => interms.push(Number::Integer(self.floor(a1))), "\\" => interms.push(Number::Integer(self.bitwise_complement(a1)?)), "sign" => interms.push(Number::Integer(self.sign(a1))), _ => { return Err(self.error_form(MachineError::instantiation_error(), caller)) } } } HeapCellValue::Addr(Addr::Con(Constant::Integer(n))) => { interms.push(Number::Integer(n)) } HeapCellValue::Addr(Addr::Con(Constant::Float(n))) => { interms.push(Number::Float(n)) } HeapCellValue::Addr(Addr::Con(Constant::Rational(n))) => { interms.push(Number::Rational(n)) } HeapCellValue::Addr(Addr::Con(Constant::Atom(ref name, _))) if name.as_str() == "pi" => { interms.push(Number::Float(OrderedFloat(f64::consts::PI))) } _ => return Err(self.error_form(MachineError::instantiation_error(), caller)), } } Ok(interms.pop().unwrap()) } fn rdiv(&self, r1: Rational, r2: Rational) -> Result { let stub = MachineError::functor_stub(clause_name!("(rdiv)"), 2); if r2 == 0 { Err(self.error_form(MachineError::evaluation_error(EvalError::ZeroDivisor), stub)) } else { Ok(r1 / r2) } } fn int_floor_div(&self, n1: Number, n2: Number) -> Result { let stub = MachineError::functor_stub(clause_name!("(div)"), 2); match n1 / n2 { Ok(result) => Ok(rnd_i(&result).to_owned()), Err(e) => Err(self.error_form(MachineError::evaluation_error(e), stub)), } } fn idiv(&self, n1: Number, n2: Number) -> Result { let stub = MachineError::functor_stub(clause_name!("(//)"), 2); match (n1, n2) { (Number::Integer(n1), Number::Integer(n2)) => { if n2 == 0 { Err(self .error_form(MachineError::evaluation_error(EvalError::ZeroDivisor), stub)) } else { Ok(n1.div_rem(n2).0) } } (Number::Integer(_), n2) => Err(self.error_form( MachineError::type_error(ValidType::Integer, Addr::Con(n2.to_constant())), stub, )), (n1, _) => Err(self.error_form( MachineError::type_error(ValidType::Integer, Addr::Con(n1.to_constant())), stub, )), } } fn div(&self, n1: Number, n2: Number) -> Result { let stub = MachineError::functor_stub(clause_name!("(/)"), 2); if n2.is_zero() { Err(self.error_form(MachineError::evaluation_error(EvalError::ZeroDivisor), stub)) } else { try_numeric_result!(self, n1 / n2, stub) } } fn atan2(&self, n1: Number, n2: Number) -> Result { let stub = MachineError::functor_stub(clause_name!("(is)"), 2); if n1.is_zero() && n2.is_zero() { Err(self.error_form(MachineError::evaluation_error(EvalError::Undefined), stub)) } else { let f1 = self.float(n1)?; let f2 = self.float(n2)?; self.unary_float_fn_template(Number::Float(OrderedFloat(f1)), |f| f.atan2(f2)) } } fn int_pow(&self, n1: Number, n2: Number) -> Result { if n1.is_zero() && n2.is_negative() { let stub = MachineError::functor_stub(clause_name!("(is)"), 2); return Err(self.error_form(MachineError::evaluation_error(EvalError::Undefined), stub)); } match (n1, n2) { (Number::Integer(n1), Number::Integer(n2)) => { if n1 != 1 && n2 < 0 { let n = Addr::Con(Constant::Integer(n1)); let stub = MachineError::functor_stub(clause_name!("^"), 2); Err(self.error_form(MachineError::type_error(ValidType::Float, n), stub)) } else { Ok(Number::Integer(binary_pow(n1, n2))) } } (n1, Number::Integer(n2)) => { let f1 = self.float(n1)?; let f2 = self.float(Number::Integer(n2))?; self.unary_float_fn_template(Number::Float(OrderedFloat(f1)), |f| f.powf(f2)) .map(|f| Number::Float(OrderedFloat(f))) } (n1, n2) => { let f2 = self.float(n2)?; if n1.is_negative() && f2 != f2.floor() { let stub = MachineError::functor_stub(clause_name!("(is)"), 2); return Err( self.error_form(MachineError::evaluation_error(EvalError::Undefined), stub) ); } let f1 = self.float(n1)?; self.unary_float_fn_template(Number::Float(OrderedFloat(f1)), |f| f.powf(f2)) .map(|f| Number::Float(OrderedFloat(f))) } } } fn gcd(&self, n1: Number, n2: Number) -> Result { match (n1, n2) { (Number::Integer(n1), Number::Integer(n2)) => { Ok(n1.gcd(&n2)) } (Number::Float(f), _) | (_, Number::Float(f)) => { let n = Addr::Con(Constant::Float(f)); let stub = MachineError::functor_stub(clause_name!("gcd"), 2); Err(self.error_form(MachineError::type_error(ValidType::Integer, n), stub)) } (Number::Rational(r), _) | (_, Number::Rational(r)) => { let n = Addr::Con(Constant::Rational(r)); let stub = MachineError::functor_stub(clause_name!("gcd"), 2); Err(self.error_form(MachineError::type_error(ValidType::Integer, n), stub)) } } } fn float_pow(&self, n1: Number, n2: Number) -> Result { let f1 = result_f(&n1, rnd_f); let f2 = result_f(&n2, rnd_f); let stub = MachineError::functor_stub(clause_name!("(**)"), 2); let f1 = try_numeric_result!(self, f1, stub.clone())?; let f2 = try_numeric_result!(self, f2, stub.clone())?; let result = result_f(&Number::Float(OrderedFloat(f1.powf(f2))), rnd_f); Ok(Number::Float(OrderedFloat(try_numeric_result!( self, result, stub )?))) } fn pow(&self, n1: Number, n2: Number, culprit: &'static str) -> Result { if n2.is_negative() && n1.is_zero() { let stub = MachineError::functor_stub(clause_name!(culprit), 2); return Err(self.error_form(MachineError::evaluation_error(EvalError::Undefined), stub)); } match (n1, n2) { (Number::Integer(n1), Number::Integer(n2)) => Ok(Number::Integer(binary_pow(n1, n2))), (n1, n2) => self.float_pow(n1, n2), } } fn unary_float_fn_template(&self, n1: Number, f: FloatFn) -> Result where FloatFn: Fn(f64) -> f64, { let stub = MachineError::functor_stub(clause_name!("(is)"), 2); let f1 = try_numeric_result!(self, result_f(&n1, rnd_f), stub.clone())?; let f1 = result_f(&Number::Float(OrderedFloat(f(f1))), rnd_f); try_numeric_result!(self, f1, stub) } fn sin(&self, n1: Number) -> Result { self.unary_float_fn_template(n1, |f| f.sin()) } fn cos(&self, n1: Number) -> Result { self.unary_float_fn_template(n1, |f| f.cos()) } fn tan(&self, n1: Number) -> Result { self.unary_float_fn_template(n1, |f| f.tan()) } fn log(&self, n1: Number) -> Result { self.unary_float_fn_template(n1, |f| f.log(f64::consts::E)) } fn exp(&self, n1: Number) -> Result { self.unary_float_fn_template(n1, |f| f.exp()) } fn asin(&self, n1: Number) -> Result { self.unary_float_fn_template(n1, |f| f.asin()) } fn acos(&self, n1: Number) -> Result { self.unary_float_fn_template(n1, |f| f.acos()) } fn atan(&self, n1: Number) -> Result { self.unary_float_fn_template(n1, |f| f.atan()) } fn sqrt(&self, n1: Number) -> Result { if n1.is_negative() { let stub = MachineError::functor_stub(clause_name!("(is)"), 2); return Err(self.error_form(MachineError::evaluation_error(EvalError::Undefined), stub)); } self.unary_float_fn_template(n1, |f| f.sqrt()) } fn float(&self, n: Number) -> Result { let stub = MachineError::functor_stub(clause_name!("(is)"), 2); try_numeric_result!(self, result_f(&n, rnd_f), stub) } fn floor(&self, n1: Number) -> Integer { rnd_i(&n1).to_owned() } fn ceiling(&self, n1: Number) -> Integer { -self.floor(-n1) } fn truncate(&self, n: Number) -> Integer { if n.is_negative() { -self.floor(n.abs()) } else { self.floor(n) } } fn round(&self, n: Number) -> Result { let stub = MachineError::functor_stub(clause_name!("(is)"), 2); let result = n + Number::Float(OrderedFloat(0.5f64)); let result = try_numeric_result!(self, result, stub)?; Ok(self.floor(result)) } fn shr(&self, n1: Number, n2: Number) -> Result { let stub = MachineError::functor_stub(clause_name!("(>>)"), 2); match (n1, n2) { (Number::Integer(n1), Number::Integer(n2)) => match n2.to_u32() { Some(n2) => Ok(n1 >> n2), _ => Ok(n1 >> u32::max_value()), }, (Number::Integer(_), n2) => Err(self.error_form( MachineError::type_error(ValidType::Integer, Addr::Con(n2.to_constant())), stub, )), (n1, _) => Err(self.error_form( MachineError::type_error(ValidType::Integer, Addr::Con(n1.to_constant())), stub, )), } } fn shl(&self, n1: Number, n2: Number) -> Result { let stub = MachineError::functor_stub(clause_name!("(<<)"), 2); match (n1, n2) { (Number::Integer(n1), Number::Integer(n2)) => match n2.to_u32() { Some(n2) => Ok(n1 << n2), _ => Ok(n1 << u32::max_value()), }, (Number::Integer(_), n2) => Err(self.error_form( MachineError::type_error(ValidType::Integer, Addr::Con(n2.to_constant())), stub, )), (n1, _) => Err(self.error_form( MachineError::type_error(ValidType::Integer, Addr::Con(n1.to_constant())), stub, )), } } fn bitwise_complement(&self, n1: Number) -> Result { let stub = MachineError::functor_stub(clause_name!("(\\)"), 2); match n1 { Number::Integer(n1) => Ok(!n1), _ => Err(self.error_form( MachineError::type_error(ValidType::Integer, Addr::Con(n1.to_constant())), stub, )), } } fn xor(&self, n1: Number, n2: Number) -> Result { let stub = MachineError::functor_stub(clause_name!("(xor)"), 2); match (n1, n2) { (Number::Integer(n1), Number::Integer(n2)) => Ok(n1 ^ n2), (Number::Integer(_), n2) => Err(self.error_form( MachineError::type_error(ValidType::Integer, Addr::Con(n2.to_constant())), stub, )), (n1, _) => Err(self.error_form( MachineError::type_error(ValidType::Integer, Addr::Con(n1.to_constant())), stub, )), } } fn and(&self, n1: Number, n2: Number) -> Result { let stub = MachineError::functor_stub(clause_name!("(/\\)"), 2); match (n1, n2) { (Number::Integer(n1), Number::Integer(n2)) => Ok(n1 & n2), (Number::Integer(_), n2) => Err(self.error_form( MachineError::type_error(ValidType::Integer, Addr::Con(n2.to_constant())), stub, )), (n1, _) => Err(self.error_form( MachineError::type_error(ValidType::Integer, Addr::Con(n1.to_constant())), stub, )), } } fn modulus(&self, x: Number, y: Number) -> Result { let stub = MachineError::functor_stub(clause_name!("(mod)"), 2); match (x, y) { (Number::Integer(x), Number::Integer(y)) => { if y == 0 { Err(self .error_form(MachineError::evaluation_error(EvalError::ZeroDivisor), stub)) } else { Ok(x.div_rem_floor(y).1) } } (Number::Integer(_), n2) => Err(self.error_form( MachineError::type_error(ValidType::Integer, Addr::Con(n2.to_constant())), stub, )), (n1, _) => Err(self.error_form( MachineError::type_error(ValidType::Integer, Addr::Con(n1.to_constant())), stub, )), } } fn max(&self, n1: Number, n2: Number) -> Result { match (n1, n2) { (Number::Integer(n1), Number::Integer(n2)) => { if n1 > n2 { Ok(Number::Integer(n1)) } else { Ok(Number::Integer(n2)) } } (n1, n2) => { let stub = MachineError::functor_stub(clause_name!("max"), 2); let f1 = try_numeric_result!(self, result_f(&n1, rnd_f), stub.clone())?; let f2 = try_numeric_result!(self, result_f(&n2, rnd_f), stub)?; Ok(Number::Float(max(OrderedFloat(f1), OrderedFloat(f2)))) } } } fn min(&self, n1: Number, n2: Number) -> Result { match (n1, n2) { (Number::Integer(n1), Number::Integer(n2)) => { if n1 < n2 { Ok(Number::Integer(n1)) } else { Ok(Number::Integer(n2)) } } (n1, n2) => { let stub = MachineError::functor_stub(clause_name!("max"), 2); let f1 = try_numeric_result!(self, result_f(&n1, rnd_f), stub.clone())?; let f2 = try_numeric_result!(self, result_f(&n2, rnd_f), stub)?; Ok(Number::Float(min(OrderedFloat(f1), OrderedFloat(f2)))) } } } fn sign(&self, n: Number) -> Integer { if n.is_positive() { Integer::from(1) } else if n.is_negative() { Integer::from(-1) } else { Integer::from(0) } } fn remainder(&self, n1: Number, n2: Number) -> Result { let stub = MachineError::functor_stub(clause_name!("(rem)"), 2); match (n1, n2) { (Number::Integer(n1), Number::Integer(n2)) => { if n2 == 0 { Err(self .error_form(MachineError::evaluation_error(EvalError::ZeroDivisor), stub)) } else { Ok(n1 % n2) } } (Number::Integer(_), n2) => Err(self.error_form( MachineError::type_error(ValidType::Integer, Addr::Con(n2.to_constant())), stub, )), (n1, _) => Err(self.error_form( MachineError::type_error(ValidType::Integer, Addr::Con(n1.to_constant())), stub, )), } } fn or(&self, n1: Number, n2: Number) -> Result { let stub = MachineError::functor_stub(clause_name!("(\\/)"), 2); match (n1, n2) { (Number::Integer(n1), Number::Integer(n2)) => Ok(n1 | n2), (Number::Integer(_), n2) => Err(self.error_form( MachineError::type_error(ValidType::Integer, Addr::Con(n2.to_constant())), stub, )), (n1, _) => Err(self.error_form( MachineError::type_error(ValidType::Integer, Addr::Con(n1.to_constant())), stub, )), } } pub(super) fn execute_arith_instr(&mut self, instr: &ArithmeticInstruction) { let stub = MachineError::functor_stub(clause_name!("(is)"), 2); match instr { &ArithmeticInstruction::Add(ref a1, ref a2, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); let n2 = try_or_fail!(self, self.get_number(a2)); self.interms[t - 1] = try_or_fail!(self, try_numeric_result!(self, n1 + n2, stub)); self.p += 1; } &ArithmeticInstruction::Sub(ref a1, ref a2, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); let n2 = try_or_fail!(self, self.get_number(a2)); self.interms[t - 1] = try_or_fail!(self, try_numeric_result!(self, n1 - n2, stub)); self.p += 1; } &ArithmeticInstruction::Mul(ref a1, ref a2, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); let n2 = try_or_fail!(self, self.get_number(a2)); self.interms[t - 1] = try_or_fail!(self, try_numeric_result!(self, n1 * n2, stub)); self.p += 1; } &ArithmeticInstruction::Max(ref a1, ref a2, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); let n2 = try_or_fail!(self, self.get_number(a2)); self.interms[t - 1] = try_or_fail!(self, self.max(n1, n2)); self.p += 1; } &ArithmeticInstruction::Min(ref a1, ref a2, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); let n2 = try_or_fail!(self, self.get_number(a2)); self.interms[t - 1] = try_or_fail!(self, self.min(n1, n2)); self.p += 1; } &ArithmeticInstruction::IntPow(ref a1, ref a2, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); let n2 = try_or_fail!(self, self.get_number(a2)); self.interms[t - 1] = try_or_fail!(self, self.int_pow(n1, n2)); self.p += 1; } &ArithmeticInstruction::Gcd(ref a1, ref a2, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); let n2 = try_or_fail!(self, self.get_number(a2)); self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.gcd(n1, n2))); self.p += 1; } &ArithmeticInstruction::Pow(ref a1, ref a2, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); let n2 = try_or_fail!(self, self.get_number(a2)); self.interms[t - 1] = try_or_fail!(self, self.pow(n1, n2, "(**)")); self.p += 1; } &ArithmeticInstruction::RDiv(ref a1, ref a2, t) => { let stub = MachineError::functor_stub(clause_name!("(rdiv)"), 2); let r1 = try_or_fail!(self, self.get_rational(a1, &stub)); let r2 = try_or_fail!(self, self.get_rational(a2, &stub)); self.interms[t - 1] = Number::Rational(try_or_fail!(self, self.rdiv(r1, r2))); self.p += 1; } &ArithmeticInstruction::IntFloorDiv(ref a1, ref a2, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); let n2 = try_or_fail!(self, self.get_number(a2)); self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.int_floor_div(n1, n2))); self.p += 1; } &ArithmeticInstruction::IDiv(ref a1, ref a2, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); let n2 = try_or_fail!(self, self.get_number(a2)); self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.idiv(n1, n2))); self.p += 1; } &ArithmeticInstruction::Abs(ref a1, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); self.interms[t - 1] = n1.abs(); self.p += 1; } &ArithmeticInstruction::Sign(ref a1, t) => { let n = try_or_fail!(self, self.get_number(a1)); self.interms[t - 1] = Number::Integer(self.sign(n)); self.p += 1; } &ArithmeticInstruction::Neg(ref a1, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); self.interms[t - 1] = -n1; self.p += 1; } &ArithmeticInstruction::BitwiseComplement(ref a1, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.bitwise_complement(n1))); self.p += 1; } &ArithmeticInstruction::Div(ref a1, ref a2, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); let n2 = try_or_fail!(self, self.get_number(a2)); self.interms[t - 1] = try_or_fail!(self, self.div(n1, n2)); self.p += 1; } &ArithmeticInstruction::Shr(ref a1, ref a2, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); let n2 = try_or_fail!(self, self.get_number(a2)); self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.shr(n1, n2))); self.p += 1; } &ArithmeticInstruction::Shl(ref a1, ref a2, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); let n2 = try_or_fail!(self, self.get_number(a2)); self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.shl(n1, n2))); self.p += 1; } &ArithmeticInstruction::Xor(ref a1, ref a2, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); let n2 = try_or_fail!(self, self.get_number(a2)); self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.xor(n1, n2))); self.p += 1; } &ArithmeticInstruction::And(ref a1, ref a2, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); let n2 = try_or_fail!(self, self.get_number(a2)); self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.and(n1, n2))); self.p += 1; } &ArithmeticInstruction::Or(ref a1, ref a2, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); let n2 = try_or_fail!(self, self.get_number(a2)); self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.or(n1, n2))); self.p += 1; } &ArithmeticInstruction::Mod(ref a1, ref a2, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); let n2 = try_or_fail!(self, self.get_number(a2)); self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.modulus(n1, n2))); self.p += 1; } &ArithmeticInstruction::Rem(ref a1, ref a2, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); let n2 = try_or_fail!(self, self.get_number(a2)); self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.remainder(n1, n2))); self.p += 1; } &ArithmeticInstruction::Cos(ref a1, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); self.interms[t - 1] = Number::Float(OrderedFloat(try_or_fail!(self, self.cos(n1)))); self.p += 1; } &ArithmeticInstruction::Sin(ref a1, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); self.interms[t - 1] = Number::Float(OrderedFloat(try_or_fail!(self, self.sin(n1)))); self.p += 1; } &ArithmeticInstruction::Tan(ref a1, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); self.interms[t - 1] = Number::Float(OrderedFloat(try_or_fail!(self, self.tan(n1)))); self.p += 1; } &ArithmeticInstruction::Sqrt(ref a1, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); self.interms[t - 1] = Number::Float(OrderedFloat(try_or_fail!(self, self.sqrt(n1)))); self.p += 1; } &ArithmeticInstruction::Log(ref a1, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); self.interms[t - 1] = Number::Float(OrderedFloat(try_or_fail!(self, self.log(n1)))); self.p += 1; } &ArithmeticInstruction::Exp(ref a1, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); self.interms[t - 1] = Number::Float(OrderedFloat(try_or_fail!(self, self.exp(n1)))); self.p += 1; } &ArithmeticInstruction::ACos(ref a1, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); self.interms[t - 1] = Number::Float(OrderedFloat(try_or_fail!(self, self.acos(n1)))); self.p += 1; } &ArithmeticInstruction::ASin(ref a1, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); self.interms[t - 1] = Number::Float(OrderedFloat(try_or_fail!(self, self.asin(n1)))); self.p += 1; } &ArithmeticInstruction::ATan(ref a1, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); self.interms[t - 1] = Number::Float(OrderedFloat(try_or_fail!(self, self.atan(n1)))); self.p += 1; } &ArithmeticInstruction::ATan2(ref a1, ref a2, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); let n2 = try_or_fail!(self, self.get_number(a2)); self.interms[t - 1] = Number::Float(OrderedFloat(try_or_fail!(self, self.atan2(n1, n2)))); self.p += 1; } &ArithmeticInstruction::Float(ref a1, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); self.interms[t - 1] = Number::Float(OrderedFloat(try_or_fail!(self, self.float(n1)))); self.p += 1; } &ArithmeticInstruction::Truncate(ref a1, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); self.interms[t - 1] = Number::Integer(self.truncate(n1)); self.p += 1; } &ArithmeticInstruction::Round(ref a1, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.round(n1))); self.p += 1; } &ArithmeticInstruction::Ceiling(ref a1, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); self.interms[t - 1] = Number::Integer(self.ceiling(n1)); self.p += 1; } &ArithmeticInstruction::Floor(ref a1, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); self.interms[t - 1] = Number::Integer(self.floor(n1)); self.p += 1; } &ArithmeticInstruction::Plus(ref a1, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); self.interms[t - 1] = n1; self.p += 1; } }; } fn get_char_list(&mut self, s: &StringList) { let h = self.heap.h; if let Some(c) = s.head() { self.heap .push(HeapCellValue::Addr(Addr::Con(Constant::Char(c)))); self.heap .push(HeapCellValue::Addr(Addr::Con(Constant::String(s.tail())))); self.s = h; self.mode = MachineMode::Read; } else if s.is_expandable() { self.heap .push(HeapCellValue::Addr(Addr::Con(Constant::String(s.clone())))); self.s = h; self.mode = MachineMode::Read; } else { self.fail = true; } } fn get_code_list(&mut self, s: &StringList) { let h = self.heap.h; if let Some(c) = s.head() { self.heap .push(HeapCellValue::Addr(Addr::Con(Constant::CharCode(c as u8)))); self.heap .push(HeapCellValue::Addr(Addr::Con(Constant::String(s.tail())))); self.s = h; self.mode = MachineMode::Read; } else if s.is_expandable() { self.heap .push(HeapCellValue::Addr(Addr::Con(Constant::String(s.clone())))); self.s = h; self.mode = MachineMode::Read; } else { self.fail = true; } } pub(super) fn execute_fact_instr(&mut self, instr: &FactInstruction) { match instr { &FactInstruction::GetConstant(_, ref c, reg) => { let addr = self[reg].clone(); self.write_constant_to_var(addr, c.clone()); } &FactInstruction::GetList(_, reg) => { let addr = self.store(self.deref(self[reg].clone())); match addr { Addr::Con(Constant::String(ref s)) => match self.flags.double_quotes { DoubleQuotes::Chars => self.get_char_list(s), DoubleQuotes::Codes => self.get_code_list(s), _ => self.fail = true, }, addr @ Addr::AttrVar(_) | addr @ Addr::StackCell(..) | addr @ Addr::HeapCell(_) => { let h = self.heap.h; self.heap.push(HeapCellValue::Addr(Addr::Lis(h + 1))); self.bind(addr.as_var().unwrap(), Addr::HeapCell(h)); self.mode = MachineMode::Write; } Addr::Lis(a) => { self.s = a; self.mode = MachineMode::Read; } _ => self.fail = true, }; } &FactInstruction::GetStructure(ref ct, arity, reg) => { let addr = self.deref(self[reg].clone()); match self.store(addr.clone()) { Addr::Str(a) => { let result = &self.heap[a]; if let &HeapCellValue::NamedStr(narity, ref s, _) = result { if narity == arity && ct.name() == *s { self.s = a + 1; self.mode = MachineMode::Read; } else { self.fail = true; } } } Addr::AttrVar(_) | Addr::HeapCell(_) | Addr::StackCell(_, _) => { let h = self.heap.h; self.heap.push(HeapCellValue::Addr(Addr::Str(h + 1))); self.heap .push(HeapCellValue::NamedStr(arity, ct.name(), ct.spec())); self.bind(addr.as_var().unwrap(), Addr::HeapCell(h)); self.mode = MachineMode::Write; } _ => self.fail = true, }; } &FactInstruction::GetVariable(norm, arg) => self[norm] = self.registers[arg].clone(), &FactInstruction::GetValue(norm, arg) => { let norm_addr = self[norm].clone(); let reg_addr = self.registers[arg].clone(); self.unify(norm_addr, reg_addr); } &FactInstruction::UnifyConstant(ref c) => { match self.mode { MachineMode::Read => { let addr = Addr::HeapCell(self.s); self.write_constant_to_var(addr, c.clone()); } MachineMode::Write => { self.heap.push(HeapCellValue::Addr(Addr::Con(c.clone()))); } }; self.s += 1; } &FactInstruction::UnifyVariable(reg) => { match self.mode { MachineMode::Read => self[reg] = self.heap[self.s].as_addr(self.s), MachineMode::Write => { let h = self.heap.h; self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h))); self[reg] = Addr::HeapCell(h); } }; self.s += 1; } &FactInstruction::UnifyLocalValue(reg) => { let s = self.s; match self.mode { MachineMode::Read => { let reg_addr = self[reg].clone(); self.unify(reg_addr, Addr::HeapCell(s)); } MachineMode::Write => { let addr = self.deref(self[reg].clone()); let h = self.heap.h; if let Addr::HeapCell(hc) = addr { if hc < h { let val = self.heap[hc].clone(); self.heap.push(val); self.s += 1; return; } } self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h))); self.bind(Ref::HeapCell(h), addr); } }; self.s += 1; } &FactInstruction::UnifyValue(reg) => { let s = self.s; match self.mode { MachineMode::Read => { let reg_addr = self[reg].clone(); self.unify(reg_addr, Addr::HeapCell(s)); } MachineMode::Write => { let heap_val = self.store(self[reg].clone()); self.heap.push(HeapCellValue::Addr(heap_val)); } }; self.s += 1; } &FactInstruction::UnifyVoid(n) => { match self.mode { MachineMode::Read => self.s += n, MachineMode::Write => { let h = self.heap.h; for i in h..h + n { self.heap.push(HeapCellValue::Addr(Addr::HeapCell(i))); } } }; } }; } pub(super) fn execute_indexing_instr(&mut self, instr: &IndexingInstruction) { match instr { &IndexingInstruction::SwitchOnTerm(v, c, l, s) => { let a1 = self.registers[1].clone(); let addr = self.store(self.deref(a1)); let offset = match addr { Addr::HeapCell(_) | Addr::StackCell(..) | Addr::AttrVar(..) => v, Addr::Con(Constant::String(ref s)) if !self.flags.double_quotes.is_atom() => { if s.is_empty() { if s.is_expandable() { v } else { c } } else { l } } Addr::Con(_) => c, Addr::Lis(_) => l, Addr::Str(_) => s, Addr::DBRef(_) => { self.fail = true; return; } }; match offset { 0 => self.fail = true, o => self.p += o, }; } &IndexingInstruction::SwitchOnConstant(_, ref hm) => { let a1 = self.registers[1].clone(); let addr = self.store(self.deref(a1)); let offset = match addr { Addr::Con(constant) => match hm.get(&constant) { Some(offset) => *offset, _ => 0, }, _ => 0, }; match offset { 0 => self.fail = true, o => self.p += o, }; } &IndexingInstruction::SwitchOnStructure(_, ref hm) => { let a1 = self.registers[1].clone(); let addr = self.store(self.deref(a1)); let offset = match addr { Addr::Str(s) => { if let &HeapCellValue::NamedStr(arity, ref name, _) = &self.heap[s] { match hm.get(&(name.clone(), arity)) { Some(offset) => *offset, _ => 0, } } else { 0 } } _ => 0, }; match offset { 0 => self.fail = true, o => self.p += o, }; } }; } pub(super) fn execute_query_instr(&mut self, instr: &QueryInstruction) { match instr { &QueryInstruction::GetVariable(norm, arg) => self[norm] = self.registers[arg].clone(), &QueryInstruction::PutConstant(_, ref constant, reg) => { self[reg] = Addr::Con(constant.clone()) } &QueryInstruction::PutList(_, reg) => self[reg] = Addr::Lis(self.heap.h), &QueryInstruction::PutStructure(ref ct, arity, reg) => { let h = self.heap.h; self.heap .push(HeapCellValue::NamedStr(arity, ct.name(), ct.spec())); self[reg] = Addr::Str(h); } &QueryInstruction::PutUnsafeValue(n, arg) => { let e = self.e; let addr = self.deref(Addr::StackCell(e, n)); if addr.is_protected(e) { self.registers[arg] = self.store(addr); } else { let h = self.heap.h; self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h))); self.bind(Ref::HeapCell(h), addr); self.registers[arg] = self.heap[h].as_addr(h); } } &QueryInstruction::PutValue(norm, arg) => self.registers[arg] = self[norm].clone(), &QueryInstruction::PutVariable(norm, arg) => { match norm { RegType::Perm(n) => { let e = self.e; self[norm] = Addr::StackCell(e, n); self.registers[arg] = self[norm].clone(); } RegType::Temp(_) => { let h = self.heap.h; self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h))); self[norm] = Addr::HeapCell(h); self.registers[arg] = Addr::HeapCell(h); } }; } &QueryInstruction::SetConstant(ref c) => { self.heap.push(HeapCellValue::Addr(Addr::Con(c.clone()))); } &QueryInstruction::SetLocalValue(reg) => { let addr = self.deref(self[reg].clone()); let h = self.heap.h; if let Addr::HeapCell(hc) = addr { if hc < h { let heap_val = self.heap[hc].clone(); self.heap.push(heap_val); return; } } self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h))); self.bind(Ref::HeapCell(h), addr); } &QueryInstruction::SetVariable(reg) => { let h = self.heap.h; self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h))); self[reg] = Addr::HeapCell(h); } &QueryInstruction::SetValue(reg) => { let heap_val = self[reg].clone(); self.heap.push(HeapCellValue::Addr(heap_val)); } &QueryInstruction::SetVoid(n) => { let h = self.heap.h; for i in h..h + n { self.heap.push(HeapCellValue::Addr(Addr::HeapCell(i))); } } } } pub(super) fn set_ball(&mut self) { self.ball.reset(); let addr = self[temp_v!(1)].clone(); self.ball.boundary = self.heap.h; copy_term( CopyBallTerm::new(&mut self.stack, &mut self.heap, &mut self.ball.stub), addr, AttrVarPolicy::DeepCopy, ); } pub(super) fn handle_internal_call_n(&mut self, arity: usize) { let arity = arity + 1; let pred = self.registers[1].clone(); for i in 2..arity { self.registers[i - 1] = self.registers[i].clone(); } if arity > 1 { self.registers[arity - 1] = pred; return; } self.fail = true; } pub(super) fn setup_call_n(&mut self, arity: usize) -> Option { let stub = MachineError::functor_stub(clause_name!("call"), arity + 1); let addr = self.store(self.deref(self.registers[arity].clone())); let (name, narity) = match addr { Addr::Str(a) => { let result = self.heap[a].clone(); if let HeapCellValue::NamedStr(narity, name, _) = result { if narity + arity > 63 { let representation_error = self.error_form( MachineError::representation_error(RepFlag::MaxArity), stub, ); self.throw_exception(representation_error); return None; } for i in (1 .. arity).rev() { self.registers[i + narity] = self.registers[i].clone(); } for i in 1 .. narity + 1 { self.registers[i] = self.heap[a + i].as_addr(a + i); } (name, narity) } else { self.fail = true; return None; } } Addr::Con(Constant::Atom(name, _)) => (name, 0), Addr::HeapCell(_) | Addr::StackCell(_, _) => { let instantiation_error = self.error_form(MachineError::instantiation_error(), stub); self.throw_exception(instantiation_error); return None; } _ => { let type_error = self.error_form(MachineError::type_error(ValidType::Callable, addr), stub); self.throw_exception(type_error); return None; } }; Some((name, arity + narity - 1)) } pub(super) fn unwind_stack(&mut self) { self.b = self.block; self.fail = true; } pub(crate) fn is_cyclic_term(&self, addr: Addr) -> bool { let mut seen = IndexSet::new(); let mut fail = false; let mut iter = self.pre_order_iter(addr); loop { if let Some(addr) = iter.stack().last() { if !seen.contains(addr) { seen.insert(addr.clone()); } else { fail = true; break; } } if iter.next().is_none() { break; } } fail } // arg(+N, +Term, ?Arg) pub(super) fn try_arg(&mut self) -> CallResult { let stub = MachineError::functor_stub(clause_name!("arg"), 3); let n = self.store(self.deref(self[temp_v!(1)].clone())); match n { Addr::HeapCell(_) | Addr::StackCell(..) => // 8.5.2.3 a) { return Err(self.error_form(MachineError::instantiation_error(), stub)) } Addr::Con(Constant::Integer(n)) => { if n < 0 { // 8.5.2.3 e) let n = Addr::Con(Constant::Integer(n)); let dom_err = MachineError::domain_error(DomainError::NotLessThanZero, n); return Err(self.error_form(dom_err, stub)); } let n = match n.to_usize() { Some(n) => n, None => { self.fail = true; return Ok(()); } }; let term = self.store(self.deref(self[temp_v!(2)].clone())); match term { Addr::HeapCell(_) | Addr::StackCell(..) => // 8.5.2.3 b) { return Err(self.error_form(MachineError::instantiation_error(), stub)) } Addr::Str(o) => match self.heap[o].clone() { HeapCellValue::NamedStr(arity, _, _) if 1 <= n && n <= arity => { let a3 = self[temp_v!(3)].clone(); let h_a = Addr::HeapCell(o + n); self.unify(a3, h_a); } _ => self.fail = true, }, Addr::Lis(l) => { if n == 1 || n == 2 { let a3 = self[temp_v!(3)].clone(); let h_a = Addr::HeapCell(l + n - 1); self.unify(a3, h_a); } else { self.fail = true; } } Addr::Con(Constant::String(ref s)) if !self.flags.double_quotes.is_atom() && !s.is_empty() => { if n == 1 || n == 2 { let a3 = self[temp_v!(3)].clone(); let h_a = if n == 1 { if self.flags.double_quotes.is_chars() { Addr::Con(Constant::Char(s.head().unwrap())) } else { Addr::Con(Constant::CharCode(s.head().unwrap() as u8)) } } else { Addr::Con(Constant::String(s.tail())) }; self.unify(a3, h_a); } else { self.fail = true; } } _ => // 8.5.2.3 d) { return Err(self .error_form(MachineError::type_error(ValidType::Compound, term), stub)) } } } _ => // 8.5.2.3 c) { return Err(self.error_form(MachineError::type_error(ValidType::Integer, n), stub)) } } Ok(()) } fn compare_numbers(&mut self, cmp: CompareNumberQT, n1: Number, n2: Number) { let ordering = n1.cmp(&n2); self.fail = match cmp { CompareNumberQT::GreaterThan if ordering == Ordering::Greater => false, CompareNumberQT::GreaterThanOrEqual if ordering != Ordering::Less => false, CompareNumberQT::LessThan if ordering == Ordering::Less => false, CompareNumberQT::LessThanOrEqual if ordering != Ordering::Greater => false, CompareNumberQT::NotEqual if ordering != Ordering::Equal => false, CompareNumberQT::Equal if ordering == Ordering::Equal => false, _ => true, }; self.p += 1; } pub(super) fn compare_term(&mut self, qt: CompareTermQT) { let a1 = self[temp_v!(1)].clone(); let a2 = self[temp_v!(2)].clone(); match self.compare_term_test(&a1, &a2) { Ordering::Greater => match qt { CompareTermQT::GreaterThan | CompareTermQT::GreaterThanOrEqual => return, _ => self.fail = true, }, Ordering::Equal => match qt { CompareTermQT::GreaterThanOrEqual | CompareTermQT::LessThanOrEqual => return, _ => self.fail = true, }, Ordering::Less => match qt { CompareTermQT::LessThan | CompareTermQT::LessThanOrEqual => return, _ => self.fail = true, }, }; } // returns true on failure. pub(super) fn eq_test(&self, a1: Addr, a2: Addr) -> bool { let mut iter = self.zipped_acyclic_pre_order_iter(a1, a2); while let Some((v1, v2)) = iter.next() { match (v1, v2) { (HeapCellValue::NamedStr(ar1, n1, _), HeapCellValue::NamedStr(ar2, n2, _)) => if ar1 != ar2 || n1 != n2 { return true; }, (HeapCellValue::Addr(Addr::Lis(_)), HeapCellValue::Addr(Addr::Lis(_))) => continue, (HeapCellValue::Addr(Addr::Con(Constant::EmptyList)), HeapCellValue::Addr(Addr::Con(Constant::String(s)))) | (HeapCellValue::Addr(Addr::Con(Constant::String(s))), HeapCellValue::Addr(Addr::Con(Constant::EmptyList))) => return match self.flags.double_quotes { DoubleQuotes::Atom => true, _ => !s.is_empty() }, (HeapCellValue::Addr(a1), HeapCellValue::Addr(a2)) => if a1 != a2 { return true; }, _ => return true, } } // did the two iterators expire at the same step? iter.first_to_expire != Ordering::Equal } pub(super) fn compare_term_test(&self, a1: &Addr, a2: &Addr) -> Ordering { let mut iter = self.zipped_acyclic_pre_order_iter(a1.clone(), a2.clone()); while let Some((v1, v2)) = iter.next() { match (v1, v2) { ( HeapCellValue::Addr(Addr::Lis(_)), HeapCellValue::Addr(Addr::Con(Constant::String(_))), ) | ( HeapCellValue::Addr(Addr::Con(Constant::String(_))), HeapCellValue::Addr(Addr::Lis(_)), ) if !self.flags.double_quotes.is_atom() => {} ( HeapCellValue::Addr(Addr::Con(Constant::EmptyList)), HeapCellValue::Addr(Addr::Con(Constant::String(ref s))), ) if !self.flags.double_quotes.is_atom() => { if s.is_empty() { return Ordering::Equal; } else { return Ordering::Greater; } } ( HeapCellValue::Addr(Addr::Con(Constant::Atom(atom, _))), HeapCellValue::Addr(Addr::Con(Constant::Char(c))), ) => { return if atom.as_str().chars().count() == 1 { atom.as_str().chars().next().cmp(&Some(c)) } else { Ordering::Greater } } ( HeapCellValue::Addr(Addr::Con(Constant::Char(c))), HeapCellValue::Addr(Addr::Con(Constant::Atom(atom, _))), ) => { return if atom.as_str().chars().count() == 1 { Some(c).cmp(&atom.as_str().chars().next()) } else { Ordering::Less } } ( HeapCellValue::Addr(Addr::Con(Constant::String(ref s))), HeapCellValue::Addr(Addr::Con(Constant::EmptyList)), ) if !self.flags.double_quotes.is_atom() => { if s.is_empty() { return Ordering::Equal; } else { return Ordering::Less; } } ( HeapCellValue::Addr(Addr::HeapCell(hc1)), HeapCellValue::Addr(Addr::HeapCell(hc2)), ) | ( HeapCellValue::Addr(Addr::AttrVar(hc1)), HeapCellValue::Addr(Addr::HeapCell(hc2)), ) | ( HeapCellValue::Addr(Addr::HeapCell(hc1)), HeapCellValue::Addr(Addr::AttrVar(hc2)), ) | ( HeapCellValue::Addr(Addr::AttrVar(hc1)), HeapCellValue::Addr(Addr::AttrVar(hc2)), ) => { if hc1 != hc2 { return hc1.cmp(&hc2); } } (HeapCellValue::Addr(Addr::HeapCell(_)), _) | (HeapCellValue::Addr(Addr::AttrVar(_)), _) => return Ordering::Less, ( HeapCellValue::Addr(Addr::StackCell(fr1, sc1)), HeapCellValue::Addr(Addr::StackCell(fr2, sc2)), ) => { if fr1 > fr2 { return Ordering::Greater; } else if fr1 < fr2 || sc1 < sc2 { return Ordering::Less; } else if sc1 > sc2 { return Ordering::Greater; } } ( HeapCellValue::Addr(Addr::StackCell(..)), HeapCellValue::Addr(Addr::HeapCell(_)), ) | ( HeapCellValue::Addr(Addr::StackCell(..)), HeapCellValue::Addr(Addr::AttrVar(_)), ) => return Ordering::Greater, (HeapCellValue::Addr(Addr::StackCell(..)), _) => return Ordering::Less, ( HeapCellValue::Addr(Addr::Con(Constant::Integer(..))), HeapCellValue::Addr(Addr::HeapCell(_)), ) | ( HeapCellValue::Addr(Addr::Con(Constant::Integer(..))), HeapCellValue::Addr(Addr::AttrVar(_)), ) => return Ordering::Greater, ( HeapCellValue::Addr(Addr::Con(Constant::Integer(..))), HeapCellValue::Addr(Addr::StackCell(..)), ) => return Ordering::Greater, ( HeapCellValue::Addr(Addr::Con(Constant::Integer(n1))), HeapCellValue::Addr(Addr::Con(Constant::Integer(n2))), ) => { if n1 != n2 { return n1.cmp(&n2); } } (HeapCellValue::Addr(Addr::Con(Constant::Integer(_))), _) => return Ordering::Less, ( HeapCellValue::Addr(Addr::Con(Constant::Float(..))), HeapCellValue::Addr(Addr::HeapCell(_)), ) | ( HeapCellValue::Addr(Addr::Con(Constant::Float(..))), HeapCellValue::Addr(Addr::AttrVar(_)), ) => return Ordering::Greater, ( HeapCellValue::Addr(Addr::Con(Constant::Float(..))), HeapCellValue::Addr(Addr::StackCell(..)), ) => return Ordering::Greater, ( HeapCellValue::Addr(Addr::Con(Constant::Float(n1))), HeapCellValue::Addr(Addr::Con(Constant::Float(n2))), ) => { if n1 != n2 { return n1.cmp(&n2); } } (HeapCellValue::Addr(Addr::Con(Constant::Float(_))), _) => return Ordering::Less, ( HeapCellValue::Addr(Addr::Con(Constant::Rational(..))), HeapCellValue::Addr(Addr::HeapCell(_)), ) | ( HeapCellValue::Addr(Addr::Con(Constant::Rational(..))), HeapCellValue::Addr(Addr::AttrVar(_)), ) => return Ordering::Greater, ( HeapCellValue::Addr(Addr::Con(Constant::Rational(..))), HeapCellValue::Addr(Addr::StackCell(..)), ) => return Ordering::Greater, ( HeapCellValue::Addr(Addr::Con(Constant::Rational(n1))), HeapCellValue::Addr(Addr::Con(Constant::Rational(n2))), ) => { if n1 != n2 { return n1.cmp(&n2); } } (HeapCellValue::Addr(Addr::Con(Constant::Rational(_))), _) => { return Ordering::Less } ( HeapCellValue::Addr(Addr::Con(Constant::String(..))), HeapCellValue::Addr(Addr::HeapCell(_)), ) | ( HeapCellValue::Addr(Addr::Con(Constant::String(..))), HeapCellValue::Addr(Addr::AttrVar(_)), ) => return Ordering::Greater, ( HeapCellValue::Addr(Addr::Con(Constant::String(..))), HeapCellValue::Addr(Addr::StackCell(..)), ) => return Ordering::Greater, ( HeapCellValue::Addr(Addr::Con(Constant::String(_))), HeapCellValue::Addr(Addr::Con(Constant::Integer(_))), ) => return Ordering::Greater, ( HeapCellValue::Addr(Addr::Con(Constant::String(_))), HeapCellValue::Addr(Addr::Con(Constant::Rational(_))), ) => return Ordering::Greater, ( HeapCellValue::Addr(Addr::Con(Constant::String(_))), HeapCellValue::Addr(Addr::Con(Constant::Float(_))), ) => return Ordering::Greater, ( HeapCellValue::Addr(Addr::Con(Constant::String(s1))), HeapCellValue::Addr(Addr::Con(Constant::String(s2))), ) => { return if s1.is_expandable() { if s2.is_expandable() { s1.cmp(&s2) } else { Ordering::Greater } } else { if s2.is_expandable() { Ordering::Less } else { s1.cmp(&s2) } } } (HeapCellValue::Addr(Addr::Con(Constant::String(_))), _) => return Ordering::Less, ( HeapCellValue::Addr(Addr::Con(Constant::Atom(..))), HeapCellValue::Addr(Addr::HeapCell(_)), ) | ( HeapCellValue::Addr(Addr::Con(Constant::Atom(..))), HeapCellValue::Addr(Addr::AttrVar(_)), ) => return Ordering::Greater, ( HeapCellValue::Addr(Addr::Con(Constant::Atom(..))), HeapCellValue::Addr(Addr::StackCell(..)), ) => return Ordering::Greater, ( HeapCellValue::Addr(Addr::Con(Constant::Atom(..))), HeapCellValue::Addr(Addr::Con(Constant::Float(_))), ) => return Ordering::Greater, ( HeapCellValue::Addr(Addr::Con(Constant::Atom(..))), HeapCellValue::Addr(Addr::Con(Constant::Integer(_))), ) => return Ordering::Greater, ( HeapCellValue::Addr(Addr::Con(Constant::Atom(..))), HeapCellValue::Addr(Addr::Con(Constant::Rational(_))), ) => return Ordering::Greater, ( HeapCellValue::Addr(Addr::Con(Constant::Atom(..))), HeapCellValue::Addr(Addr::Con(Constant::String(_))), ) => return Ordering::Greater, ( HeapCellValue::Addr(Addr::Con(Constant::Atom(s1, _))), HeapCellValue::Addr(Addr::Con(Constant::Atom(s2, _))), ) => { if s1 != s2 { return s1.cmp(&s2); } } (HeapCellValue::Addr(Addr::Con(Constant::Atom(..))), _) => return Ordering::Less, (HeapCellValue::NamedStr(ar1, n1, _), HeapCellValue::NamedStr(ar2, n2, _)) => { if ar1 < ar2 { return Ordering::Less; } else if ar1 > ar2 { return Ordering::Greater; } else if n1 != n2 { return n1.cmp(&n2); } } (HeapCellValue::Addr(Addr::Lis(_)), HeapCellValue::Addr(Addr::Lis(_))) => continue, (HeapCellValue::Addr(Addr::Lis(_)), HeapCellValue::NamedStr(ar, n, _)) | (HeapCellValue::NamedStr(ar, n, _), HeapCellValue::Addr(Addr::Lis(_))) => { if ar == 2 && n.as_str() == "." { continue; } else if ar < 2 { return Ordering::Greater; } else if ar > 2 { return Ordering::Less; } else { return n.as_str().cmp("."); } } (HeapCellValue::NamedStr(..), _) => return Ordering::Greater, (HeapCellValue::Addr(Addr::Lis(_)), _) => return Ordering::Greater, _ => {} } } iter.first_to_expire } pub(super) fn reset_block(&mut self, addr: Addr) { match self.store(addr) { Addr::Con(Constant::Usize(b)) => self.block = b, _ => self.fail = true, }; } pub(super) fn execute_inlined(&mut self, inlined: &InlinedClauseType) { match inlined { &InlinedClauseType::CompareNumber(cmp, ref at_1, ref at_2) => { let n1 = try_or_fail!(self, self.get_number(at_1)); let n2 = try_or_fail!(self, self.get_number(at_2)); self.compare_numbers(cmp, n1, n2); } &InlinedClauseType::IsAtom(r1) => { let d = self.store(self.deref(self[r1].clone())); match d { Addr::Con(Constant::Atom(..)) | Addr::Con(Constant::Char(_)) => self.p += 1, Addr::Con(Constant::EmptyList) => self.p += 1, _ => self.fail = true, }; } &InlinedClauseType::IsAtomic(r1) => { let d = self.store(self.deref(self[r1].clone())); match d { Addr::Con(_) => self.p += 1, _ => self.fail = true, }; } &InlinedClauseType::IsInteger(r1) => { let d = self.store(self.deref(self[r1].clone())); match d { Addr::Con(Constant::Integer(_)) => self.p += 1, Addr::Con(Constant::CharCode(_)) => self.p += 1, Addr::Con(Constant::Rational(r)) => { if r.denom() == &1 { self.p += 1; } else { self.fail = true; } } _ => self.fail = true, }; } &InlinedClauseType::IsCompound(r1) => { let d = self.store(self.deref(self[r1].clone())); match d { Addr::Str(_) | Addr::Lis(_) => self.p += 1, _ => self.fail = true, }; } &InlinedClauseType::IsFloat(r1) => { let d = self.store(self.deref(self[r1].clone())); match d { Addr::Con(Constant::Float(_)) => self.p += 1, _ => self.fail = true, }; } &InlinedClauseType::IsRational(r1) => { let d = self.store(self.deref(self[r1].clone())); match d { Addr::Con(Constant::Rational(_)) => self.p += 1, _ => self.fail = true, }; } &InlinedClauseType::IsString(r1) => { let d = self.store(self.deref(self[r1].clone())); match d { Addr::Con(Constant::String(_)) => self.p += 1, _ => self.fail = true, }; } &InlinedClauseType::IsNonVar(r1) => { let d = self.store(self.deref(self[r1].clone())); match d { Addr::AttrVar(_) | Addr::HeapCell(_) | Addr::StackCell(..) => self.fail = true, _ => self.p += 1, }; } &InlinedClauseType::IsVar(r1) => { let d = self.store(self.deref(self[r1].clone())); match d { Addr::AttrVar(_) | Addr::HeapCell(_) | Addr::StackCell(_, _) => self.p += 1, _ => self.fail = true, }; } &InlinedClauseType::IsPartialString(r1) => { let d = self.store(self.deref(self[r1].clone())); match d { Addr::Con(Constant::String(ref s)) if s.is_expandable() => self.p += 1, _ => self.fail = true, }; } } } fn try_functor_unify_components(&mut self, name: Addr, arity: Addr) { let a2 = self[temp_v!(2)].clone(); let a3 = self[temp_v!(3)].clone(); self.unify(a2, name); if !self.fail { self.unify(a3, arity); } } fn try_functor_compound_case( &mut self, name: ClauseName, arity: usize, spec: Option, ) { let name = Addr::Con(Constant::Atom(name, spec)); let arity = Addr::Con(Constant::Integer(Integer::from(arity))); self.try_functor_unify_components(name, arity); } fn try_functor_fabricate_struct( &mut self, name: ClauseName, arity: isize, spec: Option, op_dir: &OpDir, r: Ref, ) { let spec = fetch_atom_op_spec(name.clone(), spec, op_dir); let f_a = if name.as_str() == "." && arity == 2 { Addr::Lis(self.heap.h) } else { let h = self.heap.h; self.heap .push(HeapCellValue::NamedStr(arity as usize, name, spec)); Addr::Str(h) }; for _ in 0..arity { let h = self.heap.h; self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h))); } self.bind(r, f_a); } pub(super) fn try_functor(&mut self, indices: &IndexStore) -> CallResult { let stub = MachineError::functor_stub(clause_name!("functor"), 3); let a1 = self.store(self.deref(self[temp_v!(1)].clone())); match a1.clone() { Addr::DBRef(_) => self.fail = true, Addr::Con(Constant::String(ref s)) if !self.flags.double_quotes.is_atom() && !s.is_empty() => { let shared_op_desc = fetch_op_spec(clause_name!("."), 2, None, &indices.op_dir); self.try_functor_compound_case(clause_name!("."), 2, shared_op_desc) } Addr::Con(_) => self .try_functor_unify_components(a1, Addr::Con(Constant::Integer(Integer::from(0)))), Addr::Str(o) => match self.heap[o].clone() { HeapCellValue::NamedStr(arity, name, spec) => { let spec = fetch_op_spec(name.clone(), arity, spec, &indices.op_dir); self.try_functor_compound_case(name, arity, spec) } _ => self.fail = true, }, Addr::Lis(_) => { let shared_op_desc = fetch_op_spec(clause_name!("."), 2, None, &indices.op_dir); self.try_functor_compound_case(clause_name!("."), 2, shared_op_desc) } Addr::AttrVar(..) | Addr::HeapCell(_) | Addr::StackCell(..) => { let name = self.store(self.deref(self[temp_v!(2)].clone())); let arity = self.store(self.deref(self[temp_v!(3)].clone())); if name.is_ref() || arity.is_ref() { // 8.5.1.3 a) & 8.5.1.3 b) return Err(self.error_form(MachineError::instantiation_error(), stub)); } if let Addr::Con(Constant::Integer(arity)) = arity { let arity = match arity.to_isize() { Some(arity) => arity, None => { self.fail = true; return Ok(()); } }; if arity > MAX_ARITY as isize { let rep_err = MachineError::representation_error(RepFlag::MaxArity); // 8.5.1.3 f) return Err(self.error_form(rep_err, stub)); } else if arity < 0 { // 8.5.1.3 g) let arity = Integer::from(arity); let dom_err = MachineError::domain_error( DomainError::NotLessThanZero, Addr::Con(Constant::Integer(arity)), ); return Err(self.error_form(dom_err, stub)); } match name { Addr::Con(_) if arity == 0 => self.unify(a1, name), Addr::Con(Constant::Atom(name, spec)) => self.try_functor_fabricate_struct( name, arity, spec, &indices.op_dir, a1.as_var().unwrap(), ), Addr::Con(Constant::Char(c)) => { let name = clause_name!(c.to_string(), indices.atom_tbl); self.try_functor_fabricate_struct( name, arity, None, &indices.op_dir, a1.as_var().unwrap(), ); } Addr::Con(_) => { return Err(self .error_form(MachineError::type_error(ValidType::Atom, name), stub)) } // 8.5.1.3 e) _ => { return Err(self.error_form( MachineError::type_error(ValidType::Atomic, name), stub, )) } // 8.5.1.3 c) }; } else if !arity.is_ref() { // 8.5.1.3 d) return Err( self.error_form(MachineError::type_error(ValidType::Integer, arity), stub) ); } } }; Ok(()) } pub(super) fn term_dedup(&self, list: &mut Vec) { let mut result = vec![]; for a2 in list.iter().cloned() { if let Some(a1) = result.last().cloned() { if self.compare_term_test(&a1, &a2) == Ordering::Equal { continue; } } result.push(a2); } *list = result; } pub(super) fn try_string_list(&self, r: RegType) -> Result { let a1 = self[r].clone(); let a1 = self.store(self.deref(a1)); if let Addr::Con(Constant::String(s)) = a1 { return Ok(s); } else { let stub = MachineError::functor_stub(clause_name!("partial_string"), 2); match self.try_from_list(r, stub.clone()) { Ok(addrs) => Ok(StringList::new( match self.try_char_list(addrs) { Ok(string) => string, Err(err) => { return Err(self.error_form(err, stub)); } }, false, )), Err(err) => return Err(err), } } } pub(super) fn try_from_list( &self, r: RegType, caller: MachineStub, ) -> Result, MachineStub> { let a1 = self.store(self.deref(self[r].clone())); match a1.clone() { Addr::Lis(mut l) => { let mut result = Vec::new(); result.push(self.heap[l].as_addr(l)); l += 1; loop { match self.heap[l].clone() { HeapCellValue::Addr(addr) => match self.store(self.deref(addr)) { Addr::Lis(hcp) => { result.push(self.heap[hcp].as_addr(hcp)); l = hcp + 1; } Addr::Con(Constant::String(ref s)) if !self.flags.double_quotes.is_atom() => { result.push(Addr::Con(Constant::String(s.clone()))); break; } Addr::Con(Constant::EmptyList) => break, Addr::HeapCell(_) | Addr::StackCell(..) => { return Err( self.error_form(MachineError::instantiation_error(), caller) ) } _ => { return Err(self.error_form( MachineError::type_error(ValidType::List, a1), caller, )) } }, _ => { return Err(self .error_form(MachineError::type_error(ValidType::List, a1), caller)) } } } Ok(result) } Addr::Con(Constant::String(ref s)) if !self.flags.double_quotes.is_atom() => { Ok(vec![Addr::Con(Constant::String(s.clone()))]) } Addr::HeapCell(_) | Addr::StackCell(..) => { Err(self.error_form(MachineError::instantiation_error(), caller)) } Addr::Con(Constant::EmptyList) => Ok(vec![]), _ => Err(self.error_form(MachineError::type_error(ValidType::List, a1), caller)), } } // see 8.4.4.3 of Draft Technical Corrigendum 2 for an error guide. pub(super) fn project_onto_key(&self, a: Addr) -> Result { let stub = MachineError::functor_stub(clause_name!("keysort"), 2); match self.store(self.deref(a)) { Addr::HeapCell(_) | Addr::StackCell(..) => { Err(self.error_form(MachineError::instantiation_error(), stub)) } Addr::Str(s) => match self.heap[s].clone() { HeapCellValue::NamedStr(2, ref name, Some(_)) if *name == clause_name!("-") => { Ok(Addr::HeapCell(s + 1)) } _ => Err(self.error_form( MachineError::type_error(ValidType::Pair, self.heap[s].as_addr(s)), stub, )), }, a => Err(self.error_form(MachineError::type_error(ValidType::Pair, a), stub)), } } pub(super) fn copy_term(&mut self, attr_var_policy: AttrVarPolicy) { let old_h = self.heap.h; let a1 = self[temp_v!(1)].clone(); let a2 = self[temp_v!(2)].clone(); copy_term(CopyTerm::new(self), a1, attr_var_policy); self.unify(Addr::HeapCell(old_h), a2); } fn structural_char_list_test(&self, s: &StringList, list_offset: usize) -> bool { if !s.is_empty() { if let HeapCellValue::Addr(Addr::Con(constant)) = self.heap[list_offset].clone() { if let Some(c) = s.head() { // checks equality on atoms, too. if constant == Constant::Char(c) { return true; } } } } false } fn structural_code_list_test(&self, s: &StringList, list_offset: usize) -> bool { if !s.is_empty() { if let HeapCellValue::Addr(Addr::Con(constant)) = self.heap[list_offset].clone() { if let Some(c) = s.head() { // checks equality on integers, too. if constant == Constant::CharCode(c as u8) { return true; } } } } false } // returns true on failure. pub(super) fn structural_eq_test(&self) -> bool { let a1 = self[temp_v!(1)].clone(); let a2 = self[temp_v!(2)].clone(); let mut var_pairs = IndexMap::new(); let iter = self.zipped_acyclic_pre_order_iter(a1, a2); for (v1, v2) in iter { match (v1, v2) { ( HeapCellValue::Addr(Addr::Lis(l)), HeapCellValue::Addr(Addr::Con(Constant::String(ref s))), ) | ( HeapCellValue::Addr(Addr::Con(Constant::String(ref s))), HeapCellValue::Addr(Addr::Lis(l)), ) if !self.flags.double_quotes.is_atom() => match self.flags.double_quotes { DoubleQuotes::Chars => { if self.structural_char_list_test(s, l) { continue; } } DoubleQuotes::Codes => { if self.structural_code_list_test(s, l) { continue; } } DoubleQuotes::Atom => unreachable!(), }, ( HeapCellValue::Addr(Addr::Con(Constant::String(ref s1))), HeapCellValue::Addr(Addr::Con(Constant::String(ref s2))), ) => match s1.head() { Some(c1) => { if let Some(c2) = s2.head() { if c1 != c2 { return true; } } else { return true; } } None => return !s2.is_empty(), }, ( HeapCellValue::Addr(Addr::Con(Constant::String(ref s))), HeapCellValue::Addr(Addr::Con(Constant::EmptyList)), ) | ( HeapCellValue::Addr(Addr::Con(Constant::EmptyList)), HeapCellValue::Addr(Addr::Con(Constant::String(ref s))), ) if !self.flags.double_quotes.is_atom() => { if !s.is_empty() { return true; } } (HeapCellValue::NamedStr(ar1, n1, _), HeapCellValue::NamedStr(ar2, n2, _)) => { if ar1 != ar2 || n1 != n2 { return true; } } (HeapCellValue::Addr(Addr::Lis(_)), HeapCellValue::Addr(Addr::Lis(_))) => continue, ( HeapCellValue::Addr(v1 @ Addr::HeapCell(_)), HeapCellValue::Addr(v2 @ Addr::AttrVar(_)), ) | ( HeapCellValue::Addr(v1 @ Addr::StackCell(..)), HeapCellValue::Addr(v2 @ Addr::AttrVar(_)), ) | ( HeapCellValue::Addr(v1 @ Addr::AttrVar(_)), HeapCellValue::Addr(v2 @ Addr::AttrVar(_)), ) | ( HeapCellValue::Addr(v1 @ Addr::AttrVar(_)), HeapCellValue::Addr(v2 @ Addr::HeapCell(_)), ) | ( HeapCellValue::Addr(v1 @ Addr::AttrVar(_)), HeapCellValue::Addr(v2 @ Addr::StackCell(..)), ) | ( HeapCellValue::Addr(v1 @ Addr::HeapCell(_)), HeapCellValue::Addr(v2 @ Addr::HeapCell(_)), ) | ( HeapCellValue::Addr(v1 @ Addr::HeapCell(_)), HeapCellValue::Addr(v2 @ Addr::StackCell(..)), ) | ( HeapCellValue::Addr(v1 @ Addr::StackCell(..)), HeapCellValue::Addr(v2 @ Addr::StackCell(..)), ) | ( HeapCellValue::Addr(v1 @ Addr::StackCell(..)), HeapCellValue::Addr(v2 @ Addr::HeapCell(_)), ) => match (var_pairs.get(&v1).cloned(), var_pairs.get(&v2).cloned()) { (Some(ref v2_p), Some(ref v1_p)) if *v1_p == v1 && *v2_p == v2 => continue, (Some(_), _) | (_, Some(_)) => return true, (None, None) => { var_pairs.insert(v1.clone(), v2.clone()); var_pairs.insert(v2, v1); } }, (HeapCellValue::Addr(a1), HeapCellValue::Addr(a2)) => { if a1 != a2 { return true; } } _ => return true, } } false } // returns true on failure. pub(super) fn ground_test(&self) -> bool { let a = self.store(self.deref(self[temp_v!(1)].clone())); for v in self.acyclic_pre_order_iter(a) { match v { HeapCellValue::Addr(Addr::HeapCell(..)) => return true, HeapCellValue::Addr(Addr::StackCell(..)) => return true, HeapCellValue::Addr(Addr::AttrVar(..)) => return true, _ => {} } } false } pub(super) fn setup_built_in_call(&mut self, ct: BuiltInClauseType) { self.num_of_args = ct.arity(); self.b0 = self.b; self.p = CodePtr::BuiltInClause(ct, self.p.local()); } pub(super) fn allocate(&mut self, num_cells: usize) { let e = self.stack.allocate_and_frame(num_cells); let and_frame = self.stack.index_and_frame_mut(e); and_frame.prelude.e = self.e; and_frame.prelude.cp = self.cp; self.e = e; self.p += 1; } pub(super) fn deallocate(&mut self) { let e = self.e; let frame = self.stack.index_and_frame(e); self.cp = frame.prelude.cp; self.e = frame.prelude.e; self.p += 1; } fn handle_call_clause( &mut self, indices: &mut IndexStore, code_repo: &CodeRepo, call_policy: &mut Box, cut_policy: &mut Box, parsing_stream: &mut PrologStream, ct: &ClauseType, arity: usize, lco: bool, use_default_cp: bool, ) { let interrupted = INTERRUPT.load(std::sync::atomic::Ordering::Relaxed); if INTERRUPT.compare_and_swap(interrupted, false, std::sync::atomic::Ordering::Relaxed) { self.reset(); self.fail = true; return; } let mut default_call_policy: Box = Box::new(DefaultCallPolicy {}); let call_policy = if use_default_cp { &mut default_call_policy } else { call_policy }; self.last_call = lco; match ct { &ClauseType::BuiltIn(ref ct) => try_or_fail!( self, call_policy.call_builtin(self, ct, indices, parsing_stream) ), &ClauseType::CallN => try_or_fail!( self, call_policy.call_n(self, arity, indices, parsing_stream) ), &ClauseType::Hook(ref hook) => try_or_fail!(self, call_policy.compile_hook(self, hook)), &ClauseType::Inlined(ref ct) => { self.execute_inlined(ct); if lco { self.p = CodePtr::Local(self.cp); } } &ClauseType::Named(ref name, _, ref idx) | &ClauseType::Op(ref name, _, ref idx) => { try_or_fail!( self, call_policy.context_call(self, name.clone(), arity, idx.clone(), indices) ) } &ClauseType::System(ref ct) => try_or_fail!( self, self.system_call( ct, code_repo, indices, call_policy, cut_policy, parsing_stream ) ), }; self.last_call = false; } pub(super) fn execute_ctrl_instr( &mut self, indices: &mut IndexStore, code_repo: &CodeRepo, call_policy: &mut Box, cut_policy: &mut Box, parsing_stream: &mut PrologStream, instr: &ControlInstruction, ) { match instr { &ControlInstruction::Allocate(num_cells) => self.allocate(num_cells), &ControlInstruction::CallClause(ref ct, arity, _, lco, use_default_cp) => self .handle_call_clause( indices, code_repo, call_policy, cut_policy, parsing_stream, ct, arity, lco, use_default_cp, ), &ControlInstruction::Deallocate => self.deallocate(), &ControlInstruction::JmpBy(arity, offset, _, lco) => { if !lco { self.cp.assign_if_local(self.p.clone() + 1); } self.num_of_args = arity; self.b0 = self.b; self.p += offset; } &ControlInstruction::Proceed => self.p = CodePtr::Local(self.cp.clone()) }; } pub(super) fn execute_indexed_choice_instr( &mut self, instr: &IndexedChoiceInstruction, call_policy: &mut Box, ) { match instr { &IndexedChoiceInstruction::Try(offset) => { let n = self.num_of_args; let b = self.stack.allocate_or_frame(n); let or_frame = self.stack.index_or_frame_mut(b); or_frame.prelude.univ_prelude.num_cells = n; or_frame.prelude.e = self.e; or_frame.prelude.cp = self.cp; or_frame.prelude.b = self.b; or_frame.prelude.bp = self.p.local() + 1; or_frame.prelude.tr = self.tr; or_frame.prelude.pstr_tr = self.pstr_tr; or_frame.prelude.h = self.heap.h; or_frame.prelude.b0 = self.b0; or_frame.prelude.attr_var_init_queue_b = self.attr_var_init.attr_var_queue.len(); or_frame.prelude.attr_var_init_bindings_b = self.attr_var_init.bindings.len(); self.b = b; for i in 1 .. n + 1 { self.stack.index_or_frame_mut(b)[i-1] = self.registers[i].clone(); } self.hb = self.heap.h; self.p += offset; } &IndexedChoiceInstruction::Retry(l) => try_or_fail!(self, call_policy.retry(self, l)), &IndexedChoiceInstruction::Trust(l) => try_or_fail!(self, call_policy.trust(self, l)), }; } pub(super) fn execute_choice_instr( &mut self, instr: &ChoiceInstruction, call_policy: &mut Box, ) { match instr { &ChoiceInstruction::TryMeElse(offset) => { let n = self.num_of_args; let b = self.stack.allocate_or_frame(n); let or_frame = self.stack.index_or_frame_mut(b); or_frame.prelude.univ_prelude.num_cells = n; or_frame.prelude.e = self.e; or_frame.prelude.cp = self.cp; or_frame.prelude.b = self.b; or_frame.prelude.bp = self.p.local() + offset; or_frame.prelude.tr = self.tr; or_frame.prelude.pstr_tr = self.pstr_tr; or_frame.prelude.h = self.heap.h; or_frame.prelude.b0 = self.b0; or_frame.prelude.attr_var_init_queue_b = self.attr_var_init.attr_var_queue.len(); or_frame.prelude.attr_var_init_bindings_b = self.attr_var_init.attr_var_queue.len(); self.b = b; for i in 1 .. n + 1 { self.stack.index_or_frame_mut(b)[i-1] = self.registers[i].clone(); } self.hb = self.heap.h; self.p += 1; } &ChoiceInstruction::DefaultRetryMeElse(offset) => { let mut call_policy = DefaultCallPolicy {}; try_or_fail!(self, call_policy.retry_me_else(self, offset)) } &ChoiceInstruction::DefaultTrustMe => { let mut call_policy = DefaultCallPolicy {}; try_or_fail!(self, call_policy.trust_me(self)) } &ChoiceInstruction::RetryMeElse(offset) => { try_or_fail!(self, call_policy.retry_me_else(self, offset)) } &ChoiceInstruction::TrustMe => try_or_fail!(self, call_policy.trust_me(self)), } } pub(super) fn execute_cut_instr( &mut self, instr: &CutInstruction, cut_policy: &mut Box, ) { match instr { &CutInstruction::NeckCut => { let b = self.b; let b0 = self.b0; if b > b0 { self.b = b0; self.tidy_trail(); self.tidy_pstr_trail(); } self.p += 1; } &CutInstruction::GetLevel(r) => { let b0 = self.b0; self[r] = Addr::Con(Constant::CutPoint(b0)); self.p += 1; } &CutInstruction::GetLevelAndUnify(r) => { let b0 = self[perm_v!(1)].clone(); let a = self[r].clone(); self.unify(a, b0); self.p += 1; } &CutInstruction::Cut(r) => { if !cut_policy.cut(self, r) { self.p += 1; } } } } pub fn reset(&mut self) { self.stack.drop_in_place(); self.hb = 0; self.e = 0; self.b = 0; self.b0 = 0; self.s = 0; self.tr = 0; self.pstr_tr = 0; self.p = CodePtr::default(); self.cp = LocalCodePtr::default(); self.attr_var_init.reset(); self.num_of_args = 0; self.fail = false; self.trail.clear(); self.pstr_trail.clear(); self.heap.clear(); self.mode = MachineMode::Write; self.registers = vec![Addr::HeapCell(0); MAX_ARITY + 1]; // self.registers[0] is never used. self.block = 0; self.ball.reset(); self.heap_locs.clear(); self.lifted_heap.clear(); } }