use crate::arena::*; use crate::atom_table::*; use crate::forms::*; use crate::heap_iter::*; use crate::machine::attributed_variables::*; use crate::machine::copier::*; use crate::machine::heap::*; use crate::machine::machine_errors::*; use crate::machine::machine_indices::*; use crate::machine::machine_state::*; use crate::machine::partial_string::*; use crate::machine::stack::*; use crate::machine::unify::*; use crate::offset_table::*; use crate::parser::ast::*; use crate::parser::dashu::{Integer, Rational}; use crate::types::*; use indexmap::IndexSet; use std::cmp::Ordering; use std::convert::TryFrom; impl MachineState { pub(crate) fn new() -> Self { let mut heap = Heap::with_cell_capacity(256 * 256).unwrap(); // this is an interstitial cell reserved for use by the runtime. heap.push_cell(empty_list_as_cell!()).unwrap(); heap.store_resource_error(); MachineState { arena: Arena::new(), atom_tbl: AtomTable::new(), pdl: Vec::with_capacity(1024), s: HeapPtr::default(), s_offset: 0, p: 0, oip: 0, iip: 0, b: 0, b0: 0, e: 0, num_of_args: 0, cp: 0, attr_var_init: AttrVarInitializer::new(0), fail: false, heap, mode: MachineMode::Write, stack: Stack::new(), registers: [heap_loc_as_cell!(0); MAX_ARITY + 1], // self.registers[0] is never used. trail: vec![], tr: 0, hb: 0, block: 0, scc_block: 0, ball: Ball::new(), ball_stack: vec![], lifted_heap: Heap::new(), interms: vec![Number::default(); 256], cont_pts: Vec::with_capacity(256), cwil: CWIL::new(), flags: MachineFlags::default(), cc: 0, global_clock: 0, dynamic_mode: FirstOrNext::First, unify_fn: MachineState::unify, bind_fn: MachineState::bind, run_cleaners_fn: |_| false, } } #[inline] pub(crate) fn store(&self, value: HeapCellValue) -> HeapCellValue { read_heap_cell!(value, (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { self.heap[h] } (HeapCellValueTag::StackVar, s) => { self.stack[s] } _ => { value } ) } #[inline] pub fn deref(&self, mut addr: HeapCellValue) -> HeapCellValue { loop { let value = self.store(addr); if value.is_var() && value != addr { addr = value; continue; } return addr; } } pub fn trail(&mut self, r: TrailRef) { match r { TrailRef::Ref(r) => { let h = r.get_value() as usize; match r.get_tag() { RefTag::HeapCell => { if h < self.hb { self.trail.push(TrailEntry::build_with( TrailEntryTag::TrailedHeapVar, h as u64, )); self.tr += 1; } } RefTag::StackCell => { if h < self.b { self.trail.push(TrailEntry::build_with( TrailEntryTag::TrailedStackVar, h as u64, )); self.tr += 1; } } RefTag::AttrVar => { if h < self.hb { self.trail.push(TrailEntry::build_with( TrailEntryTag::TrailedAttrVar, h as u64, )); self.tr += 1; } } } } TrailRef::AttrVarListLink(h, l) => { if h < self.hb { self.trail.push(TrailEntry::build_with( TrailEntryTag::TrailedAttrVarListLink, h as u64, )); self.trail.push(TrailEntry::build_with( TrailEntryTag::TrailedAttachedValue, l as u64, )); self.tr += 2; } } TrailRef::BlackboardEntry(key_atom) => { self.trail.push(TrailEntry::build_with( TrailEntryTag::TrailedBlackboardEntry, key_atom.index, )); self.tr += 1; } TrailRef::BlackboardOffset(key_atom, value_cell) => { self.trail.push(TrailEntry::build_with( TrailEntryTag::TrailedBlackboardOffset, key_atom.index, )); self.trail .push(TrailEntry::from_bytes(value_cell.into_bytes())); self.tr += 2; } } } pub 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 fn bind(&mut self, r1: Ref, a2: HeapCellValue) { let t1 = self.store(r1.as_heap_cell_value()); let t2 = self.store(a2); if t1.is_var() && (!t2.is_var() || a2 < r1) { match r1.get_tag() { RefTag::StackCell => { self.stack[r1.get_value() as usize] = t2; self.trail(TrailRef::Ref(r1)); } RefTag::HeapCell => { self.heap[r1.get_value() as usize] = t2; self.trail(TrailRef::Ref(r1)); } RefTag::AttrVar => { self.bind_attr_var(r1.get_value() as usize, t2); } }; } else { read_heap_cell!(a2, (HeapCellValueTag::StackVar, s) => { self.stack[s] = t1; self.trail(TrailRef::Ref(Ref::stack_cell(s))); } (HeapCellValueTag::Var, h) => { self.heap[h] = t1; self.trail(TrailRef::Ref(Ref::heap_cell(h))); } (HeapCellValueTag::AttrVar, h) => { self.bind_attr_var(h, t1); } _ => { unreachable!(); } ); } } pub fn bind_attr_var(&mut self, h: usize, addr: HeapCellValue) { read_heap_cell!(addr, (HeapCellValueTag::Var, hc) => { self.heap[hc] = attr_var_as_cell!(h); self.trail(TrailRef::Ref(Ref::heap_cell(hc))); } (HeapCellValueTag::StackVar, hc) => { self.stack[hc] = attr_var_as_cell!(h); self.trail(TrailRef::Ref(Ref::stack_cell(hc))); } _ => { self.push_attr_var_binding(h, addr); self.heap[h] = addr; self.trail(TrailRef::Ref(Ref::attr_var(h))); } ) } #[inline] pub(super) fn bind_with_occurs_check_wrapper(&mut self, r: Ref, value: HeapCellValue) { let mut unifier = CompositeUnifierForOccursCheck::from(DefaultUnifier::from(self)); unifier.bind(r, value); } #[inline] pub(super) fn bind_with_occurs_check_with_error_wrapper( &mut self, r: Ref, value: HeapCellValue, ) { let mut unifier = CompositeUnifierForOccursCheckWithError::from(DefaultUnifier::from(self)); unifier.bind(r, value); } pub fn unify(&mut self) { let mut unifier = DefaultUnifier::from(self); unifier.unify_internal(); } pub fn unify_atom(&mut self, atom: Atom, value: HeapCellValue) { let mut unifier = DefaultUnifier::from(self); unifier.unify_atom(atom, value); } pub fn unify_char(&mut self, c: char, value: HeapCellValue) { let mut unifier = DefaultUnifier::from(self); unifier.unify_char(c, value); } pub fn unify_fixnum(&mut self, n1: Fixnum, value: HeapCellValue) { let mut unifier = DefaultUnifier::from(self); unifier.unify_fixnum(n1, value); } pub fn unify_big_int(&mut self, n1: TypedArenaPtr, value: HeapCellValue) { let mut unifier = DefaultUnifier::from(self); unifier.unify_big_integer(n1, value); } pub fn unify_rational(&mut self, n1: TypedArenaPtr, value: HeapCellValue) { let mut unifier = DefaultUnifier::from(self); unifier.unify_big_rational(n1, value); } pub fn unify_f64(&mut self, f1: F64Ptr, value: HeapCellValue) { let mut unifier = DefaultUnifier::from(self); unifier.unify_f64(f1, value); } pub fn unify_constant(&mut self, ptr: UntypedArenaPtr, value: HeapCellValue) { let mut unifier = DefaultUnifier::from(self); unifier.unify_constant(ptr, value); } pub(super) fn unify_with_occurs_check_with_error(&mut self) { let mut unifier = CompositeUnifierForOccursCheckWithError::from(DefaultUnifier::from(self)); unifier.unify_internal(); } pub(super) fn unify_with_occurs_check(&mut self) { let mut unifier = CompositeUnifierForOccursCheck::from(DefaultUnifier::from(self)); unifier.unify_internal(); } #[inline(always)] pub(super) fn effective_block(&self) -> usize { std::cmp::max(self.block, self.scc_block) } pub(super) fn set_ball(&mut self) { self.ball.reset(); let addr = self.registers[1]; self.ball.boundary = self.heap.cell_len(); self.ball.pstr_boundary = step_or_resource_error!( self, copy_term( CopyBallTerm::new( &mut self.attr_var_init.attr_var_queue, &mut self.stack, &mut self.heap, &mut self.ball.stub, ), addr, AttrVarPolicy::DeepCopy, ) ); } #[inline(always)] pub(super) fn unwind_stack(&mut self) { self.b = self.effective_block(); self.fail = true; } // return the read value and the succeeding HeapPtr pub(crate) fn read_s(&mut self) -> HeapCellValue { match self.s { HeapPtr::HeapCell(h) => self.deref(self.heap[h + self.s_offset]), HeapPtr::PStr(h) => { let mut char_iter = self.heap.char_iter(h); if self.s_offset == 0 { // read the car of the list let c = char_iter.next().unwrap(); char_as_cell!(c) } else { // read the (self.s_offset)^{th} cdr of the list let byte_offset: usize = char_iter.take(self.s_offset).map(|c| c.len_utf8()).sum(); let new_h = h + byte_offset; self.s_offset = 0; if self.heap.char_iter(new_h).next().is_some() { self.s = HeapPtr::PStr(new_h); pstr_loc_as_cell!(new_h) } else { let h = Heap::pstr_tail_idx(new_h); self.s = HeapPtr::HeapCell(h); self.deref(heap_loc_as_cell!(h)) } } } } } pub fn compare_term_test(&mut self, var_comparison: VarComparison) -> Option { let mut tabu_list = IndexSet::new(); while let Some(s1) = self.pdl.pop() { let s1 = self.deref(s1); let s2 = self.pdl.pop().unwrap(); let s2 = self.deref(s2); if s1 == s2 { continue; } let v1 = self.store(s1); let v2 = self.store(s2); let order_cat_v1 = v1.order_category(&self.heap); let order_cat_v2 = v2.order_category(&self.heap); if order_cat_v1 != order_cat_v2 { self.pdl.clear(); return Some(order_cat_v1.cmp(&order_cat_v2)); } match order_cat_v1 { Some(TermOrderCategory::Variable) => { if let VarComparison::Distinct = var_comparison { let v1 = v1.as_var().unwrap(); let v2 = v2.as_var().unwrap(); if v1 != v2 { self.pdl.clear(); return Some(v1.cmp(&v2)); } } } Some(TermOrderCategory::FloatingPoint) => { let v1 = cell_as_f64_ptr!(v1); let v2 = cell_as_f64_ptr!(v2); if v1 != v2 { self.pdl.clear(); return Some(v1.cmp(&v2)); } } Some(TermOrderCategory::Integer) => { let v1 = Number::try_from(v1).unwrap(); let v2 = Number::try_from(v2).unwrap(); if v1 != v2 { self.pdl.clear(); return Some(v1.cmp(&v2)); } } Some(TermOrderCategory::Atom) => { read_heap_cell!(v1, (HeapCellValueTag::Atom, (n1, _a1)) => { read_heap_cell!(v2, (HeapCellValueTag::Atom, (n2, _a2)) => { if n1 != n2 { self.pdl.clear(); return Some(n1.cmp(&n2)); } } /* (HeapCellValueTag::Char, c2) => { if let Some(c1) = n1.as_char() { if c1 != c2 { self.pdl.clear(); return Some(c1.cmp(&c2)); } } else { self.pdl.clear(); return Some( n1.as_str().chars().next().cmp(&Some(c2)) .then(Ordering::Greater) ); } } */ (HeapCellValueTag::Str, s) => { let n2 = cell_as_atom_cell!(self.heap[s]) .get_name(); if n1 != n2 { self.pdl.clear(); return Some(n1.cmp(&n2)); } } _ => { unreachable!(); } ) } /* (HeapCellValueTag::Char, c1) => { read_heap_cell!(v2, (HeapCellValueTag::Atom, (n2, _a2)) => { if let Some(c2) = n2.as_char() { if c1 != c2 { self.pdl.clear(); return Some(c1.cmp(&c2)); } } else { self.pdl.clear(); return Some( Some(c1).cmp(&n2.as_str().chars().next()) .then(Ordering::Less) ); } } (HeapCellValueTag::Char, c2) => { if c1 != c2 { self.pdl.clear(); return Some(c1.cmp(&c2)); } } (HeapCellValueTag::Str, s) => { let n2 = cell_as_atom_cell!(self.heap[s]) .get_name(); if let Some(c2) = n2.as_char() { if c1 != c2 { self.pdl.clear(); return Some(c1.cmp(&c2)); } } else { self.pdl.clear(); return Some( Some(c1).cmp(&n2.as_str().chars().next()) .then(Ordering::Less) ); } } _ => { unreachable!() } ) } */ (HeapCellValueTag::Str, s) => { let n1 = cell_as_atom_cell!(self.heap[s]) .get_name(); read_heap_cell!(v2, (HeapCellValueTag::Atom, (n2, _a2)) => { if n1 != n2 { self.pdl.clear(); return Some(n1.cmp(&n2)); } } /* (HeapCellValueTag::Char, c2) => { if let Some(c1) = n1.as_char() { if c1 != c2 { self.pdl.clear(); return Some(c1.cmp(&c2)); } } else { self.pdl.clear(); return Some( n1.as_str().chars().next().cmp(&Some(c2)) .then(Ordering::Greater) ); } } */ (HeapCellValueTag::Str, s) => { let n2 = cell_as_atom_cell!(self.heap[s]) .get_name(); if n1 != n2 { self.pdl.clear(); return Some(n1.cmp(&n2)); } } _ => { unreachable!(); } ) } _ => { unreachable!() } ) } Some(TermOrderCategory::Compound) => { read_heap_cell!(v1, (HeapCellValueTag::Lis, l1) => { read_heap_cell!(v2, (HeapCellValueTag::PStrLoc, l2) => { if tabu_list.contains(&(l1, l2)) { continue; } tabu_list.insert((l1, l2)); // like the action of // partial_string_to_pdl here but // the ordering of PDL pushes is // (crucially for comparison // correctness) different. let (c, succ_cell) = self.heap.last_str_char_and_tail(l2); self.pdl.push(succ_cell); self.pdl.push(heap_loc_as_cell!(l1 + 1)); self.pdl.push(char_as_cell!(c)); self.pdl.push(heap_loc_as_cell!(l1)); } (HeapCellValueTag::Lis, l2) => { if tabu_list.contains(&(l1, l2)) { continue; } tabu_list.insert((l1, l2)); self.pdl.push(self.heap[l2 + 1]); self.pdl.push(self.heap[l1 + 1]); self.pdl.push(self.heap[l2]); self.pdl.push(self.heap[l1]); } (HeapCellValueTag::Str, s2) => { if tabu_list.contains(&(l1, s2)) { continue; } let (name, arity) = cell_as_atom_cell!(self.heap[s2]) .get_name_and_arity(); match (2, atom!(".")).cmp(&(arity, name)) { Ordering::Equal => { tabu_list.insert((l1, s2)); self.pdl.push(self.heap[s2 + 2]); self.pdl.push(self.heap[l1 + 1]); self.pdl.push(self.heap[s2 + 1]); self.pdl.push(self.heap[l1]); } ordering => { self.pdl.clear(); return Some(ordering); } } } _ => { unreachable!(); } ) } (HeapCellValueTag::PStrLoc, l1) => { read_heap_cell!(v2, (HeapCellValueTag::PStrLoc, l2) => { match self.heap.compare_pstr_segments(l1, l2) { PStrSegmentCmpResult::Continue(v1, v2) => { self.pdl.push(v1); self.pdl.push(v2); } PStrSegmentCmpResult::Less => { return Some(Ordering::Less); } PStrSegmentCmpResult::Greater => { return Some(Ordering::Greater); } } } (HeapCellValueTag::Lis, l2) => { let (c, succ_cell) = self.heap.last_str_char_and_tail(l1); self.pdl.push(succ_cell); self.pdl.push(heap_loc_as_cell!(l2 + 1)); self.pdl.push(char_as_cell!(c)); self.pdl.push(heap_loc_as_cell!(l2)); } (HeapCellValueTag::Str, s) => { let (name, arity) = cell_as_atom_cell!(self.heap[s]) .get_name_and_arity(); if name == atom!(".") && arity == 2 { let (c, succ_cell) = self.heap.last_str_char_and_tail(l1); self.pdl.push(heap_loc_as_cell!(s+2)); self.pdl.push(succ_cell); self.pdl.push(heap_loc_as_cell!(s+1)); self.pdl.push(char_as_cell!(c)); } else { self.fail = true; } } _ => { unreachable!() } ); } (HeapCellValueTag::Str, s1) => { read_heap_cell!(v2, (HeapCellValueTag::Str, s2) => { if tabu_list.contains(&(s1, s2)) { continue; } let (n1, a1) = cell_as_atom_cell!(self.heap[s1]) .get_name_and_arity(); let (n2, a2) = cell_as_atom_cell!(self.heap[s2]) .get_name_and_arity(); match (a1,n1).cmp(&(a2, n2)) { Ordering::Equal => { tabu_list.insert((s1, s2)); for idx in (1 .. a1+1).rev() { self.pdl.push(self.heap[s2+idx]); self.pdl.push(self.heap[s1+idx]); } } ordering => { self.pdl.clear(); return Some(ordering); } } } (HeapCellValueTag::Lis, l2) => { if tabu_list.contains(&(s1, l2)) { continue; } tabu_list.insert((s1, l2)); let (n1, a1) = cell_as_atom_cell!(self.heap[s1]) .get_name_and_arity(); match (a1,n1).cmp(&(2, atom!("."))) { Ordering::Equal => { self.pdl.push(self.heap[l2]); self.pdl.push(self.heap[s1+1]); self.pdl.push(self.heap[l2+1]); self.pdl.push(self.heap[s1+2]); } ordering => { self.pdl.clear(); return Some(ordering); } } } (HeapCellValueTag::PStrLoc, l2) => { let (name, arity) = cell_as_atom_cell!(self.heap[s1]) .get_name_and_arity(); if name == atom!(".") && arity == 2 { let (c, succ_cell) = self.heap.last_str_char_and_tail(l2); self.pdl.push(succ_cell); self.pdl.push(heap_loc_as_cell!(s1+2)); self.pdl.push(char_as_cell!(c)); self.pdl.push(heap_loc_as_cell!(s1+1)); } else { self.fail = true; } } _ => { unreachable!() } ) } _ => { unreachable!() } ); } None => { if v1 != v2 { self.pdl.clear(); return None; } } } } Some(Ordering::Equal) } pub(crate) fn setup_call_n_init_goal_info( &mut self, goal: HeapCellValue, arity: usize, ) -> Result<(Atom, usize, usize), MachineStub> { Ok(read_heap_cell!(goal, (HeapCellValueTag::Str, s) => { let (name, narity) = cell_as_atom_cell!(self.heap[s]) .get_name_and_arity(); if narity + arity > MAX_ARITY { let stub = functor_stub(atom!("call"), arity + 1); let err = self.representation_error(RepFlag::MaxArity); return Err(self.error_form(err, stub)); } (name, narity, s) } (HeapCellValueTag::Atom, (name, arity)) => { debug_assert_eq!(arity, 0); if name == atom!("[]") { let stub = functor_stub(atom!("call"), arity + 1); let err = self.type_error(ValidType::Callable, goal); return Err(self.error_form(err, stub)); } (name, 0, 0) } /* (HeapCellValueTag::Char, c) => { (AtomTable::build_with(&self.atom_tbl, &c.to_string()), 0, 0) } */ (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => { let stub = functor_stub(atom!("call"), arity + 1); let err = self.instantiation_error(); return Err(self.error_form(err, stub)); } _ => { let stub = functor_stub(atom!("call"), arity + 1); let err = self.type_error(ValidType::Callable, goal); return Err(self.error_form(err, stub)); } )) } pub(crate) fn setup_call_n(&mut self, arity: usize) -> Result { let addr = self.store(self.deref(self.registers[arity])); let (name, narity, s) = self.setup_call_n_init_goal_info(addr, arity)?; if narity > 0 { for i in (1..arity).rev() { self.registers[i + narity] = self.registers[i]; } for i in 1..narity + 1 { self.registers[i] = self.heap[s + i]; } } Ok((name, arity + narity - 1)) } #[inline] pub fn is_cyclic_term(&mut self, term_loc: usize) -> bool { if self.heap[term_loc].is_stack_var() { return false; } let mut iter = cycle_detecting_stackless_preorder_iter(&mut self.heap, term_loc); for _ in iter.by_ref() {} iter.cycle_found() } // arg(+N, +Term, ?Arg) pub fn try_arg(&mut self) -> CallResult { let stub_gen = || functor_stub(atom!("arg"), 3); let n = self.store(self.deref(self.registers[1])); read_heap_cell!(n, (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => { // 8.5.2.3 a) let err = self.instantiation_error(); return Err(self.error_form(err, stub_gen())); } _ => { let n = match Number::try_from(n) { Ok(Number::Fixnum(n)) => Number::Fixnum(n), Ok(Number::Integer(n)) => Number::Integer(n), _ => { let err = self.type_error(ValidType::Integer, n); return Err(self.error_form(err, stub_gen())); } }; if n < 0 { // 8.5.2.3 e) let err = self.domain_error(DomainErrorType::NotLessThanZero, n); return Err(self.error_form(err, stub_gen())); } let n = match n { Number::Fixnum(n) => n.get_num() as usize, Number::Integer(n) if usize::try_from(&*n).is_ok() => (&*n).try_into().unwrap(), _ => { self.fail = true; return Ok(()); } }; let term = self.deref(self.registers[2]); read_heap_cell!(self.store(term), (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => { let err = self.instantiation_error(); return Err(self.error_form(err, stub_gen())); } (HeapCellValueTag::Str, o) => { let arity = cell_as_atom_cell!(self.heap[o]).get_arity(); if 1 <= n && n <= arity { let a3 = self.registers[3]; unify_fn!(*self, a3, heap_loc_as_cell!(o + n)); } else { self.fail = true; } } (HeapCellValueTag::Lis, l) => { if n == 1 || n == 2 { let a3 = self.registers[3]; unify_fn!(*self, a3, heap_loc_as_cell!(l + n - 1)); } else { self.fail = true; } } (HeapCellValueTag::PStrLoc, pstr_loc) => { if n == 1 || n == 2 { let a3 = self.registers[3]; // let (h, offset) = pstr_loc_and_offset(&self.heap, pstr_loc); let mut char_iter = self.heap.char_iter(pstr_loc); // let pstr = cell_as_string!(self.heap[h]); // let offset = offset.get_num() as usize; if let Some(c) = char_iter.next() { // pstr.as_str_from(offset).chars().next() { if n == 1 { self.unify_char(c, a3); } else { // let offset = (offset + c.len_utf8()) as i64; // let h_len = self.heap.len(); // let pstr_atom: Atom = pstr.into(); if char_iter.next().is_some() { unify_fn!(*self, pstr_loc_as_cell!(pstr_loc + c.len_utf8()), a3); } else { let tail_idx = Heap::pstr_tail_idx(pstr_loc); unify_fn!(*self, self.heap[tail_idx]); } /* if pstr_atom.len() > offset as usize { self.heap.push(pstr_offset_as_cell!(h)); self.heap.push(fixnum_as_cell!(Fixnum::build_with(offset))); unify_fn!(*self, pstr_loc_as_cell!(h_len), a3); } else { match self.heap[h].get_tag() { HeapCellValueTag::CStr => { self.unify_atom(atom!("[]"), self.store(self.deref(a3))); } HeapCellValueTag::PStr => { unify_fn!(*self, self.heap[h+1], a3); } _ => { unreachable!(); } } } */ } } else { unreachable!() } } else { self.fail = true; } } /* (HeapCellValueTag::CStr, cstr_atom) => { let cstr = PartialString::from(cstr_atom); if let Some(c) = cstr.as_str_from(0).chars().next() { if n == 1 { self.unify_char(c, self.store(self.deref(self.registers[3]))); } else if n == 2 { let offset = c.len_utf8() as i64; let h_len = self.heap.len(); if cstr_atom.len() > offset as usize { self.heap.push(atom_as_cstr_cell!(cstr_atom)); self.heap.push(pstr_offset_as_cell!(h_len)); self.heap.push(fixnum_as_cell!(Fixnum::build_with(offset))); unify_fn!(*self, pstr_loc_as_cell!(h_len+1), self.registers[3]); } else { self.unify_atom(atom!("[]"), self.store(self.deref(self.registers[3]))); } } else { self.fail = true; } } else { unreachable!() } } */ _ => { // 8.5.2.3 d) let err = self.type_error(ValidType::Compound, term); return Err(self.error_form(err, stub_gen())); } ) } ); Ok(()) } // returns true on failure, false on success. pub fn eq_test(&mut self, h1: HeapCellValue, h2: HeapCellValue) -> bool { if h1 == h2 { return false; } compare_term_test!(self, h1, h2) .map(|o| o != Ordering::Equal) .unwrap_or(true) } #[inline(always)] fn try_functor_compound_case(&mut self, name: Atom, arity: usize) { self.try_functor_unify_components(atom_as_cell!(name), arity); } fn try_functor_unify_components(&mut self, name: HeapCellValue, arity: usize) { let a2 = self.deref(self.registers[2]); unify!(self, a2, name); if !self.fail { let a3 = self.store(self.deref(self.registers[3])); self.unify_fixnum(Fixnum::build_with(arity as i64), a3); } } fn try_functor_fabricate_struct( &mut self, name: Atom, arity: usize, r: Ref, ) -> Result<(), usize> { let h = self.heap.cell_len(); let mut writer = self.heap.reserve(arity + 1)?; let f_a = if name == atom!(".") && arity == 2 { writer.write_with(|section| { section.push_cell(heap_loc_as_cell!(h)); section.push_cell(heap_loc_as_cell!(h + 1)); }); list_loc_as_cell!(h) } else { writer.write_with(|section| { section.push_cell(atom_as_cell!(name, arity)); for i in 0..arity { section.push_cell(heap_loc_as_cell!(h + i + 1)); } }); if arity == 0 { heap_loc_as_cell!(h) } else { str_loc_as_cell!(h) } }; (self.bind_fn)(self, r, f_a); Ok(()) } pub fn try_functor(&mut self) -> CallResult { let stub_gen = || functor_stub(atom!("functor"), 3); let a1 = self.store(self.deref(self.registers[1])); read_heap_cell!(a1, (HeapCellValueTag::Cons | HeapCellValueTag::Fixnum | // | HeapCellValueTag::Char HeapCellValueTag::F64) => { self.try_functor_unify_components(a1, 0); } (HeapCellValueTag::Atom, (_name, arity)) => { debug_assert_eq!(arity, 0); self.try_functor_unify_components(a1, 0); } (HeapCellValueTag::Str, s) => { let (name, arity) = cell_as_atom_cell!(self.heap[s]).get_name_and_arity(); self.try_functor_compound_case(name, arity); } (HeapCellValueTag::Lis | HeapCellValueTag::PStrLoc) => { // | HeapCellValueTag::CStr) => { self.try_functor_compound_case(atom!("."), 2); } (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => { let deref_name = self.deref(self.registers[2]); let store_name = self.store(deref_name); let arity = self.store(self.deref(self.registers[3])); if store_name.is_var() || arity.is_var() { // 8.5.1.3 a) & 8.5.1.3 b) let err = self.instantiation_error(); return Err(self.error_form(err, stub_gen())); } let mut type_error = |arity| { let err = self.type_error(ValidType::Integer, arity); Err(self.error_form(err, stub_gen())) }; let arity = match Number::try_from(arity) { Ok(Number::Float(_)) => { return type_error(arity); } Ok(Number::Rational(n)) if !n.denominator().is_one() => { return type_error(arity); } Ok(n) if n > MAX_ARITY => { // 8.5.1.3 f) let err = self.representation_error(RepFlag::MaxArity); return Err(self.error_form(err, stub_gen())); } Ok(n) if n < 0 => { // 8.5.1.3 g) let err = self.domain_error(DomainErrorType::NotLessThanZero, n); return Err(self.error_form(err, stub_gen())); } Ok(Number::Rational(n)) => { let value: i64 = n.numerator().try_into().unwrap(); value }, Ok(Number::Fixnum(n)) => n.get_num(), Ok(Number::Integer(n)) => { let value: i64 = (&*n).try_into().unwrap(); value }, Err(_) => { return type_error(arity); } }; read_heap_cell!(store_name, (HeapCellValueTag::Cons | HeapCellValueTag::Fixnum | // HeapCellValueTag::Char | HeapCellValueTag::F64) if arity == 0 => { self.bind(a1.as_var().unwrap(), deref_name); } (HeapCellValueTag::Atom, (name, atom_arity)) => { debug_assert_eq!(atom_arity, 0); resource_error_call_result!( self, self.try_functor_fabricate_struct( name, arity as usize, a1.as_var().unwrap(), ) ); } (HeapCellValueTag::Str, s) => { let (name, atom_arity) = cell_as_atom_cell!(self.heap[s]) .get_name_and_arity(); if atom_arity == 0 { resource_error_call_result!( self, self.try_functor_fabricate_struct( name, arity as usize, a1.as_var().unwrap(), ) ); } else { let err = self.type_error(ValidType::Atomic, store_name); return Err(self.error_form(err, stub_gen())); } } /* (HeapCellValueTag::Char, c) => { let c = AtomTable::build_with(&self.atom_tbl, &c.to_string()); resource_error_call_result!( self, self.try_functor_fabricate_struct( c, arity as usize, a1.as_var().unwrap(), ) ); } */ (HeapCellValueTag::Cons | HeapCellValueTag::Fixnum | HeapCellValueTag::F64) if arity != 0 => { let err = self.type_error(ValidType::Atom, store_name); return Err(self.error_form(err, stub_gen())); // 8.5.1.3 e) } _ => { let err = self.type_error(ValidType::Atomic, store_name); return Err(self.error_form(err, stub_gen())); // 8.5.1.3 c) } ); } _ => { self.fail = true; } ); Ok(()) } pub fn try_from_list( &mut self, value: HeapCellValue, stub_gen: impl Fn() -> MachineStub, ) -> Result, MachineStub> { let value = self.store(self.deref(value)); read_heap_cell!(value, (HeapCellValueTag::Lis, l) => { self.try_from_inner_list(vec![], l, stub_gen, value) } (HeapCellValueTag::PStrLoc, pstr_loc) => { self.try_from_partial_string(vec![], pstr_loc, stub_gen, value) } (HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar | HeapCellValueTag::Var) => { let err = self.instantiation_error(); Err(self.error_form(err, stub_gen())) } (HeapCellValueTag::Str, s) => { let (name, arity) = cell_as_atom_cell!(self.heap[s]) .get_name_and_arity(); if name == atom!("[]") && arity == 0 { Ok(vec![]) } else { let err = self.type_error(ValidType::List, value); Err(self.error_form(err, stub_gen())) } } (HeapCellValueTag::Atom, (name, arity)) => { if name == atom!("[]") && arity == 0 { Ok(vec![]) } else { let err = self.type_error(ValidType::List, value); Err(self.error_form(err, stub_gen())) } } /* (HeapCellValueTag::CStr, cstr_atom) => { let cstr = cstr_atom.as_str(); Ok(cstr.chars().map(|c| char_as_cell!(c)).collect()) } */ _ => { let err = self.type_error(ValidType::List, value); Err(self.error_form(err, stub_gen())) } ) } fn try_from_inner_list( &mut self, mut result: Vec, mut l: usize, stub_gen: impl Fn() -> MachineStub, a1: HeapCellValue, ) -> Result, MachineStub> { result.push(self.heap[l]); l += 1; loop { let value = self.store(self.deref(self.heap[l])); read_heap_cell!(value, (HeapCellValueTag::Lis, hcp) => { result.push(self.heap[hcp]); l = hcp + 1; } (HeapCellValueTag::PStrLoc, pstr_loc) => { return self.try_from_partial_string(result, pstr_loc, stub_gen, a1); } (HeapCellValueTag::Str, s) => { let (name, arity) = cell_as_atom_cell!(self.heap[s]) .get_name_and_arity(); if name == atom!("[]") && arity == 0 { break; } else { let err = self.type_error(ValidType::List, a1); return Err(self.error_form(err, stub_gen())); } } (HeapCellValueTag::Atom, (name, arity)) => { if name == atom!("[]") && arity == 0 { break; } else { let err = self.type_error(ValidType::List, a1); return Err(self.error_form(err, stub_gen())); } } _ => { if value.is_var() { let err = self.instantiation_error(); return Err(self.error_form(err, stub_gen())); } else { let err = self.type_error(ValidType::List, a1); return Err(self.error_form(err, stub_gen())); } } ); } Ok(result) } fn try_from_partial_string( &mut self, mut chars: Vec, pstr_loc: usize, stub_gen: impl Fn() -> MachineStub, a1: HeapCellValue, ) -> Result, MachineStub> { self.heap[0] = pstr_loc_as_cell!(pstr_loc); let mut heap_pstr_iter = HeapPStrIter::new(&self.heap, 0); while let Some(iteratee) = heap_pstr_iter.next() { match iteratee { PStrIteratee::Char { value: c, .. } => chars.push(char_as_cell!(c)), PStrIteratee::PStrSlice { slice_loc, slice_len, } => { let pstr = heap_pstr_iter.heap.slice_to_str(slice_loc, slice_len); chars.extend(pstr.chars().map(|c| char_as_cell!(c))); } } } let end_cell = heap_pstr_iter.heap[heap_pstr_iter.focus()]; if heap_pstr_iter.is_cyclic() || end_cell == empty_list_as_cell!() { let err = self.type_error(ValidType::List, a1); return Err(self.error_form(err, stub_gen())); } Ok(chars) } // returns true on failure. pub fn ground_test(&mut self) -> bool { let iter = eager_stackful_preorder_iter(&mut self.heap, self.registers[1]); for term in iter { if term.is_var() { return true; } } false } pub fn integers_to_bytevec( &mut self, value: HeapCellValue, stub_gen: impl Fn() -> MachineStub, ) -> Vec { let mut bytes: Vec = Vec::new(); match self.try_from_list(value, stub_gen) { Err(_) => { unreachable!() } Ok(addrs) => { for addr in addrs { let addr = self.store(self.deref(addr)); match Number::try_from(addr) { Ok(Number::Fixnum(n)) => { if let Ok(b) = u8::try_from(n.get_num()) { bytes.push(b) } } Ok(Number::Integer(n)) => { let b: u8 = (&*n).try_into().unwrap(); bytes.push(b); } _ => {} } } } } bytes } // see 8.4.4.3 of Draft Technical Corrigendum 2 for an error guide. pub fn project_onto_key(&mut self, value: HeapCellValue) -> Result { let stub_gen = || functor_stub(atom!("keysort"), 2); let store_v = self.store(self.deref(value)); if store_v.is_var() { let err = self.instantiation_error(); return Err(self.error_form(err, stub_gen())); } read_heap_cell!(store_v, (HeapCellValueTag::Str, s) => { let (name, arity) = cell_as_atom_cell!(self.heap[s]).get_name_and_arity(); if name == atom!("-") && arity == 2 { Ok(heap_loc_as_cell!(s + 1)) } else { let err = self.type_error(ValidType::Pair, self.heap[s]); Err(self.error_form(err, stub_gen())) } } _ => { let err = self.type_error(ValidType::Pair, store_v); Err(self.error_form(err, stub_gen())) } ) } pub 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; if self.e > self.b { let frame = self.stack.index_and_frame(self.e); let size = AndFrame::size_of(frame.prelude.num_cells); self.stack.truncate(self.e + size); } self.p += 1; } pub fn throw_interrupt_exception(&mut self) { let err = self.interrupt_error(); let src = functor_stub(atom!("repl"), 0); let err = self.error_form(err, src); self.throw_exception(err); } }