use crate::arena::*; use crate::atom_table::*; use crate::forms::*; use crate::heap_iter::*; use crate::heap_print::*; use crate::machine::Machine; 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::stack::*; use crate::machine::streams::*; use crate::parser::ast::*; use crate::types::*; use crate::parser::rug::Integer; use indexmap::IndexMap; use std::convert::TryFrom; use std::fmt; use std::ops::{Index, IndexMut}; pub(crate) type Registers = [HeapCellValue; MAX_ARITY + 1]; #[derive(Debug, Clone, Copy)] pub(super) enum MachineMode { Read, Write, } #[derive(Debug, Clone)] pub(super) enum HeapPtr { HeapCell(usize), PStrChar(usize, usize), PStrLocation(usize, usize), } impl Default for HeapPtr { fn default() -> Self { HeapPtr::HeapCell(0) } } #[derive(Debug)] pub enum FirstOrNext { First, Next, } pub struct MachineState { pub atom_tbl: AtomTable, pub arena: Arena, pub(super) pdl: Vec, pub(super) s: HeapPtr, pub(super) s_offset: usize, pub(super) p: usize, pub(super) oip: u32, // first internal code ptr pub(super) iip : u32, // second internal code ptr pub(super) b: usize, pub(super) b0: usize, pub(super) e: usize, pub(super) num_of_args: usize, pub(super) cp: usize, pub(super) attr_var_init: AttrVarInitializer, pub(super) fail: bool, pub heap: Heap, pub(super) mode: MachineMode, pub(crate) stack: Stack, pub(super) registers: Registers, pub(super) trail: Vec, pub(super) tr: usize, pub(super) hb: usize, pub(super) block: usize, // an offset into the OR stack. pub(super) ball: Ball, pub(super) ball_stack: Vec, // save current ball before jumping via, e.g., verify_attr interrupt. pub(super) lifted_heap: Heap, pub(super) interms: Vec, // intermediate numbers. // locations of cleaners, cut points, the previous block. for setup_call_cleanup. pub(super) cont_pts: Vec<(HeapCellValue, usize, usize)>, pub(super) cwil: CWIL, pub(crate) flags: MachineFlags, pub(crate) cc: usize, pub(crate) global_clock: usize, pub(crate) dynamic_mode: FirstOrNext, pub(crate) unify_fn: fn(&mut MachineState), pub(crate) bind_fn: fn(&mut MachineState, Ref, HeapCellValue), pub(crate) run_cleaners_fn: fn(&mut Machine) -> bool, pub(crate) increment_call_count_fn: fn(&mut MachineState) -> CallResult, } impl fmt::Debug for MachineState { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("MachineState") .field("atom_tbl", &self.atom_tbl) .field("arena", &self.arena) .field("s", &self.s) .field("p", &self.p) .field("b", &self.b) .field("b0", &self.b0) .field("e", &self.e) .field("num_of_args", &self.num_of_args) .field("cp", &self.cp) .field("attr_var_init", &self.attr_var_init) .field("fail", &self.fail) .field("heap", &self.heap) .field("mode", &self.mode) .field("stack", &self.stack) .field("registers", &self.registers) .field("trail", &self.trail) .field("tr", &self.tr) .field("hb", &self.hb) .field("block", &self.block) .field("ball", &self.ball) .field("ball_stack", &self.ball_stack) .field("lifted_heap", &self.lifted_heap) .field("interms", &self.interms) .field("flags", &self.flags) .field("cc", &self.cc) .field("global_clock", &self.global_clock) .field("dynamic_mode", &self.dynamic_mode) .field( "unify_fn", if self.unify_fn as usize == MachineState::unify as usize { &"MachineState::unify" } else if self.unify_fn as usize == MachineState::unify_with_occurs_check as usize { &"MachineState::unify_with_occurs_check" } else { &"MachineState::unify_with_occurs_check_with_error" }, ) .field( "bind_fn", if self.bind_fn as usize == MachineState::bind as usize { &"MachineState::bind" } else if self.bind_fn as usize == MachineState::bind_with_occurs_check_wrapper as usize { &"MachineState::bind_with_occurs_check" } else { &"MachineState::bind_with_occurs_check_with_error_wrapper" }, ) .finish() } } impl Index for MachineState { type Output = HeapCellValue; #[inline(always)] fn index(&self, reg: RegType) -> &Self::Output { match reg { RegType::Temp(temp) => &self.registers[temp], RegType::Perm(perm) => { let e = self.e; &self.stack[stack_loc!(AndFrame, e, perm)] } } } } impl IndexMut for MachineState { #[inline(always)] fn index_mut(&mut self, reg: RegType) -> &mut Self::Output { match reg { RegType::Temp(temp) => &mut self.registers[temp], RegType::Perm(perm) => { let e = self.e; &mut self.stack[stack_loc!(AndFrame, e, perm)] } } } } pub type CallResult = Result<(), Vec>; #[inline(always)] pub fn pstr_loc_and_offset(heap: &[HeapCellValue], index: usize) -> (usize, Fixnum) { read_heap_cell!(heap[index], (HeapCellValueTag::PStr | HeapCellValueTag::CStr) => { (index, Fixnum::build_with(0)) } (HeapCellValueTag::PStrOffset, h) => { (h, cell_as_fixnum!(heap[index+1])) } _ => { unreachable!() } ) } #[derive(Debug)] pub struct Ball { pub(super) boundary: usize, pub(super) stub: Heap, } impl Ball { pub(super) fn new() -> Self { Ball { boundary: 0, stub: Heap::new(), } } pub(super) fn reset(&mut self) { self.boundary = 0; self.stub.clear(); } pub(super) fn copy_and_align(&self, h: usize) -> Heap { let diff = self.boundary as i64 - h as i64; self.stub.iter().cloned().map(|heap_value| { heap_value - diff }).collect() } } #[derive(Debug)] pub(super) struct CopyTerm<'a> { state: &'a mut MachineState, } impl<'a> CopyTerm<'a> { pub(super) fn new(state: &'a mut MachineState) -> Self { CopyTerm { state } } } impl<'a> Index for CopyTerm<'a> { type Output = HeapCellValue; #[inline(always)] fn index(&self, index: usize) -> &Self::Output { &self.state.heap[index] } } impl<'a> IndexMut for CopyTerm<'a> { #[inline(always)] fn index_mut(&mut self, index: usize) -> &mut Self::Output { &mut self.state.heap[index] } } impl<'a> CopierTarget for CopyTerm<'a> { #[inline(always)] fn threshold(&self) -> usize { self.state.heap.len() } #[inline(always)] fn push(&mut self, hcv: HeapCellValue) { self.state.heap.push(hcv); } #[inline(always)] fn store(&self, value: HeapCellValue) -> HeapCellValue { self.state.store(value) } #[inline(always)] fn deref(&self, value: HeapCellValue) -> HeapCellValue { self.state.deref(value) } #[inline(always)] fn stack(&mut self) -> &mut Stack { &mut self.state.stack } } #[derive(Debug)] pub(super) struct CopyBallTerm<'a> { stack: &'a mut Stack, heap: &'a mut Heap, heap_boundary: usize, stub: &'a mut Heap, } impl<'a> CopyBallTerm<'a> { pub(super) fn new(stack: &'a mut Stack, heap: &'a mut Heap, stub: &'a mut Heap) -> Self { let hb = heap.len(); CopyBallTerm { stack, heap, heap_boundary: hb, stub, } } } impl<'a> Index for CopyBallTerm<'a> { type Output = HeapCellValue; fn index(&self, index: usize) -> &Self::Output { if index < self.heap_boundary { &self.heap[index] } else { let index = index - self.heap_boundary; &self.stub[index] } } } impl<'a> IndexMut for CopyBallTerm<'a> { fn index_mut(&mut self, index: usize) -> &mut Self::Output { if index < self.heap_boundary { &mut self.heap[index] } else { let index = index - self.heap_boundary; &mut self.stub[index] } } } impl<'a> CopierTarget for CopyBallTerm<'a> { fn threshold(&self) -> usize { self.heap_boundary + self.stub.len() } fn push(&mut self, value: HeapCellValue) { self.stub.push(value); } fn store(&self, value: HeapCellValue) -> HeapCellValue { read_heap_cell!(value, (HeapCellValueTag::Var | HeapCellValueTag::AttrVar, h) => { if h < self.heap_boundary { self.heap[h] } else { let index = h - self.heap_boundary; self.stub[index] } } (HeapCellValueTag::StackVar, s) => { self.stack[s] } _ => { value } ) } fn deref(&self, mut addr: HeapCellValue) -> HeapCellValue { loop { let value = self.store(addr); if value.is_var() && value != addr { addr = value; continue; } return addr; } } fn stack(&mut self) -> &mut Stack { self.stack } } impl MachineState { pub(crate) fn backtrack(&mut self) { let b = self.b; let or_frame = self.stack.index_or_frame(b); self.b0 = or_frame.prelude.b0; self.p = or_frame.prelude.bp; self.oip = or_frame.prelude.boip; self.iip = or_frame.prelude.biip; self.pdl.clear(); self.fail = false; } pub(crate) fn increment_call_count(&mut self) -> CallResult { if self.cwil.inference_limit_exceeded || self.ball.stub.len() > 0 { return Ok(()); } if let Some(&(ref limit, bp)) = self.cwil.limits.last() { if self.cwil.count == *limit { self.cwil.inference_limit_exceeded = true; return Err( functor!(atom!("inference_limit_exceeded"), [fixnum(bp)]) ); } else { self.cwil.count += 1; } } Ok(()) } #[allow(dead_code)] pub(super) fn try_char_list(&mut self, addrs: Vec) -> Result { let mut chars = String::new(); for addr in addrs { let addr = self.store(self.deref(addr)); read_heap_cell!(addr, (HeapCellValueTag::Char, c) => { chars.push(c); continue; } (HeapCellValueTag::Atom, (name, arity)) => { if arity == 0 { if let Some(c) = name.as_char() { chars.push(c); continue; } } } (HeapCellValueTag::Str, s) => { let (name, arity) = cell_as_atom_cell!(self.heap[s]) .get_name_and_arity(); if arity == 0 { if let Some(c) = name.as_char() { chars.push(c); continue; } } } _ => { } ); return Err(self.type_error(ValidType::Character, addr)); } Ok(chars) } pub(super) fn throw_undefined_error(&mut self, name: Atom, arity: usize) -> MachineStub { let stub = functor_stub(name, arity); let err = self.existence_error(ExistenceError::Procedure(name, arity)); self.error_form(err, stub) } #[inline(always)] pub(super) fn call_at_index(&mut self, arity: usize, p: usize) { self.cp = self.p + 1; self.p = p; self.oip = 0; self.iip = 0; self.num_of_args = arity; self.b0 = self.b; } #[inline(always)] pub(super) fn execute_at_index(&mut self, arity: usize, p: usize) { self.p = p; self.oip = 0; self.iip = 0; self.num_of_args = arity; self.b0 = self.b; } #[inline(always)] pub fn neck_cut(&mut self) { let b = self.b; let b0 = self.b0; if b > b0 { self.b = b0; if b > self.e { self.stack.truncate(b); } } } // Safety: the atom_tbl lives for the lifetime of the machine, as does the helper, so the ptr // will always be valid. pub fn read_term_from_user_input(&mut self, stream: Stream, indices: &mut IndexStore) -> CallResult { let atoms_ptr = (&self.atom_tbl.table) as *const indexmap::IndexSet; if let Stream::Readline(ptr) = stream { unsafe { let readline = ptr.as_ptr().as_mut().unwrap(); readline.set_atoms_for_completion(atoms_ptr); let ret = self.read_term(stream, indices); return ret } } unreachable!("Stream must be a Stream::Readline(_)") } pub fn read_term(&mut self, stream: Stream, indices: &mut IndexStore) -> CallResult { fn push_var_eq_functors<'a>( heap: &mut Heap, iter: impl Iterator, atom_tbl: &mut AtomTable, ) -> Vec { let mut list_of_var_eqs = vec![]; for (var, binding) in iter { let var_atom = atom_tbl.build_with(&var.borrow().to_string()); let h = heap.len(); heap.push(atom_as_cell!(atom!("="), 2)); heap.push(atom_as_cell!(var_atom)); heap.push(*binding); list_of_var_eqs.push(str_loc_as_cell!(h)); } list_of_var_eqs } self.check_stream_properties( stream, StreamType::Text, Some(self.registers[2]), atom!("read_term"), 3, )?; if stream.past_end_of_stream() { if EOFAction::Reset != stream.options().eof_action() { return Ok(()); } else if self.fail { return Ok(()); } } loop { match self.read(stream, &indices.op_dir) { Ok(mut term_write_result) => { let heap_loc = read_heap_cell!(self.heap[term_write_result.heap_loc], (HeapCellValueTag::PStr | HeapCellValueTag::PStrOffset) => { pstr_loc_as_cell!(term_write_result.heap_loc) } _ => { heap_loc_as_cell!(term_write_result.heap_loc) } ); let term = self.registers[2]; unify_fn!(*self, heap_loc, term); let term = heap_loc; if self.fail { return Ok(()); } let mut singleton_var_set: IndexMap = IndexMap::new(); for addr in stackful_preorder_iter(&mut self.heap, term) { let addr = unmark_cell_bits!(addr); if let Some(var) = addr.as_var() { if !singleton_var_set.contains_key(&var) { singleton_var_set.insert(var, true); } else { singleton_var_set.insert(var, false); } } } for var in term_write_result.var_dict.values_mut() { *var = heap_bound_deref(&self.heap, *var); } let singleton_var_list = push_var_eq_functors( &mut self.heap, term_write_result.var_dict.iter().filter(|(_, binding)| { if let Some(r) = binding.as_var() { *singleton_var_set.get(&r).unwrap_or(&false) } else { false } }), &mut self.atom_tbl, ); let mut var_list = Vec::with_capacity(singleton_var_set.len()); for (var_name, addr) in term_write_result.var_dict { if let Some(var) = addr.as_var() { let idx = singleton_var_set.get_index_of(&var).unwrap(); var_list.push((var_name, addr, idx)); } } var_list.sort_by(|(_,_,idx_1),(_,_,idx_2)| idx_1.cmp(idx_2)); let list_of_var_eqs = push_var_eq_functors( &mut self.heap, var_list.iter().map(|(var_name, var,_)| (var_name,var)), &mut self.atom_tbl, ); let singleton_addr = self.registers[3]; let singletons_offset = heap_loc_as_cell!( iter_to_heap_list(&mut self.heap, singleton_var_list.into_iter()) ); unify_fn!(*self, singletons_offset, singleton_addr); if self.fail { return Ok(()); } let vars_addr = self.registers[4]; let vars_offset = heap_loc_as_cell!( iter_to_heap_list(&mut self.heap, var_list.into_iter().map(|(_,cell,_)| cell)) ); unify_fn!(*self, vars_offset, vars_addr); if self.fail { return Ok(()); } let var_names_addr = self.registers[5]; let var_names_offset = heap_loc_as_cell!( iter_to_heap_list(&mut self.heap, list_of_var_eqs.into_iter()) ); return Ok(unify_fn!(*self, var_names_offset, var_names_addr)); } Err(err) => { if let CompilationError::ParserError(ParserError::UnexpectedEOF) = err { self.eof_action( self.registers[2], stream, atom!("read_term"), 3, )?; if stream.options().eof_action() == EOFAction::Reset { if self.fail == false { continue; } } return Ok(()); } let stub = functor_stub(atom!("read_term"), 3); let err = self.syntax_error(err); return Err(self.error_form(err, stub)); } } } } pub(crate) fn write_term<'a>( &'a mut self, op_dir: &'a OpDir, ) -> Result>, MachineStub> { let ignore_ops = self.store(self.deref(self.registers[3])); let numbervars = self.store(self.deref(self.registers[4])); let quoted = self.store(self.deref(self.registers[5])); let max_depth = self.store(self.deref(self.registers[7])); let term_to_be_printed = self.store(self.deref(self.registers[2])); let stub_gen = || functor_stub(atom!("write_term"), 2); let printer = match self.try_from_list(self.registers[6], stub_gen) { Ok(addrs) => { let mut var_names: IndexMap = IndexMap::new(); for addr in addrs { read_heap_cell!(addr, (HeapCellValueTag::Str, s) => { let (name, arity) = cell_as_atom_cell!(self.heap[s]) .get_name_and_arity(); if name == atom!("=") && arity == 2 { let atom = self.store(self.deref(self.heap[s+1])); let var = self.store(self.deref(self.heap[s+2])); if var_names.contains_key(&var) { continue; } read_heap_cell!(atom, (HeapCellValueTag::Char, c) => { var_names.insert(var, VarPtr::from(c.to_string())); } (HeapCellValueTag::Atom, (name, _arity)) => { debug_assert_eq!(_arity, 0); var_names.insert(var, VarPtr::from(name.as_str())); } (HeapCellValueTag::Str, s) => { let (name, arity) = cell_as_atom_cell!(self.heap[s]) .get_name_and_arity(); debug_assert_eq!(arity, 0); var_names.insert(var, VarPtr::from(name.as_str())); } _ => { unreachable!(); } ); } } _ => { } ); } let ignore_ops = read_heap_cell!(ignore_ops, (HeapCellValueTag::Atom, (name, _arity)) => { name == atom!("true") } (HeapCellValueTag::Str, s) => { let (name, arity) = cell_as_atom_cell!(self.heap[s]) .get_name_and_arity(); debug_assert_eq!(arity, 0); name == atom!("true") } _ => { unreachable!() } ); let numbervars = read_heap_cell!(numbervars, (HeapCellValueTag::Atom, (name, _arity)) => { name == atom!("true") } (HeapCellValueTag::Str, s) => { let (name, arity) = cell_as_atom_cell!(self.heap[s]) .get_name_and_arity(); debug_assert_eq!(arity, 0); name == atom!("true") } _ => { unreachable!() } ); let quoted = read_heap_cell!(quoted, (HeapCellValueTag::Atom, (name, _arity)) => { name == atom!("true") } (HeapCellValueTag::Str, s) => { let (name, arity) = cell_as_atom_cell!(self.heap[s]) .get_name_and_arity(); debug_assert_eq!(arity, 0); name == atom!("true") } _ => { unreachable!() } ); let mut printer = HCPrinter::new( &mut self.heap, op_dir, PrinterOutputter::new(), term_to_be_printed, ); printer.ignore_ops = ignore_ops; printer.numbervars = numbervars; printer.quoted = quoted; match Number::try_from(max_depth) { Ok(Number::Fixnum(n)) => { if let Ok(n) = usize::try_from(n.get_num()) { printer.max_depth = n; } else { self.fail = true; return Ok(None); } } Ok(Number::Integer(n)) => { if let Some(n) = n.to_usize() { printer.max_depth = n; } else { self.fail = true; return Ok(None); } } _ => { unreachable!(); } } printer.var_names = var_names; printer } Err(err) => { return Err(err); } }; Ok(Some(printer)) } pub(super) fn read_predicate_key(&self, name: HeapCellValue, arity: HeapCellValue) -> (Atom, usize) { let name = cell_as_atom!(self.store(self.deref(name))); let arity = cell_as_fixnum!(self.store(self.deref(arity))); (name, usize::try_from(arity.get_num()).unwrap()) } #[inline(always)] pub(super) fn cut_body(&mut self, value: HeapCellValue) { let b = self.b; read_heap_cell!(value, (HeapCellValueTag::Fixnum, b0) => { let b0 = b0.get_num() as usize; if b > b0 { self.b = b0; } } _ => { self.fail = true; } ); } #[inline(always)] pub(super) fn try_me_else(&mut self, offset: usize) { 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.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 + offset; or_frame.prelude.boip = 0; or_frame.prelude.biip = 0; or_frame.prelude.tr = self.tr; or_frame.prelude.h = self.heap.len(); or_frame.prelude.b0 = self.b0; or_frame.prelude.attr_var_queue_len = self.attr_var_init.attr_var_queue.len(); self.b = b; for i in 0..n { or_frame[i] = self.registers[i+1]; } self.hb = self.heap.len(); self.p += 1; } #[inline(always)] pub(super) fn indexed_try(&mut self, offset: usize) { 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.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; // + 1; in self.iip now! or_frame.prelude.boip = self.oip; or_frame.prelude.biip = self.iip + 1; or_frame.prelude.tr = self.tr; or_frame.prelude.h = self.heap.len(); or_frame.prelude.b0 = self.b0; or_frame.prelude.attr_var_queue_len = self.attr_var_init.attr_var_queue.len(); self.b = b; for i in 0..n { or_frame[i] = self.registers[i+1]; } self.hb = self.heap.len(); self.p = self.p + offset; self.oip = 0; self.iip = 0; } } #[derive(Debug)] pub(crate) struct CWIL { count: Integer, limits: Vec<(Integer, usize)>, inference_limit_exceeded: bool, } impl CWIL { pub(crate) fn new() -> Self { CWIL { count: Integer::from(0), limits: vec![], inference_limit_exceeded: false, } } pub(crate) fn add_limit(&mut self, limit: usize, b: usize) -> &Integer { let mut limit = Integer::from(limit); limit += &self.count; match self.limits.last() { Some((ref inner_limit, _)) if *inner_limit <= limit => {} _ => self.limits.push((limit, b)), }; &self.count } #[inline(always)] pub(crate) fn remove_limit(&mut self, b: usize) -> &Integer { if let Some((_, bp)) = self.limits.last() { if bp == &b { self.limits.pop(); } } &self.count } #[inline(always)] pub(crate) fn reset(&mut self) { self.count = Integer::from(0); self.limits.clear(); self.inference_limit_exceeded = false; } #[inline(always)] pub(crate) fn is_empty(&self) -> bool { self.limits.is_empty() } }