use crate::heap_print::*; pub use crate::machine::heap::*; pub use crate::machine::machine_state::*; pub use crate::machine::streams::*; pub use crate::machine::*; pub use crate::parser::ast::*; use crate::read::*; pub use crate::types::*; use std::sync::Arc; #[cfg(test)] use crate::machine::copier::CopierTarget; #[cfg(test)] use std::ops::{Deref, DerefMut, Index, IndexMut}; // a mini-WAM for test purposes. pub struct MockWAM { pub machine_st: MachineState, pub op_dir: OpDir, //pub flags: MachineFlags, } #[allow(dead_code)] impl MockWAM { pub fn new() -> Self { let op_dir = default_op_dir(); Self { machine_st: MachineState::new(), op_dir, //flags: MachineFlags::default(), } } pub fn write_parsed_term_to_heap( &mut self, input_stream: Stream, ) -> Result { self.machine_st.read(input_stream, &self.op_dir) } pub fn parse_and_write_parsed_term_to_heap( &mut self, term_string: &'static str, ) -> Result { let stream = Stream::from_static_string(term_string, &mut self.machine_st.arena); self.write_parsed_term_to_heap(stream) } pub fn parse_and_print_term( &mut self, term_string: &'static str, ) -> Result { let term_write_result = self.parse_and_write_parsed_term_to_heap(term_string)?; print_heap_terms(self.machine_st.heap.iter(), term_write_result.heap_loc); let mut printer = HCPrinter::new( &mut self.machine_st.heap, Arc::clone(&self.machine_st.atom_tbl), &mut self.machine_st.stack, &self.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(term_write_result.heap_loc), ); printer.var_names = term_write_result .var_dict .into_iter() .map(|(var, cell)| match var { VarKey::VarPtr(var) => (cell, var.clone()), VarKey::AnonVar(_) => (cell, VarPtr::from(var.to_string())), }) .collect(); Ok(printer.print().result()) } } impl Default for MockWAM { fn default() -> Self { Self::new() } } #[cfg(test)] pub struct TermCopyingMockWAM<'a> { pub wam: &'a mut MockWAM, } #[cfg(test)] impl<'a> Index for TermCopyingMockWAM<'a> { type Output = HeapCellValue; fn index(&self, index: usize) -> &HeapCellValue { &self.wam.machine_st.heap[index] } } #[cfg(test)] impl<'a> IndexMut for TermCopyingMockWAM<'a> { #[inline] fn index_mut(&mut self, index: usize) -> &mut HeapCellValue { &mut self.wam.machine_st.heap[index] } } #[cfg(test)] impl<'a> Deref for TermCopyingMockWAM<'a> { type Target = MockWAM; fn deref(&self) -> &Self::Target { self.wam } } #[cfg(test)] impl<'a> DerefMut for TermCopyingMockWAM<'a> { fn deref_mut(&mut self) -> &mut Self::Target { self.wam } } #[cfg(test)] impl<'a> CopierTarget for TermCopyingMockWAM<'a> { fn store(&self, val: HeapCellValue) -> HeapCellValue { read_heap_cell!(val, (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { self.wam.machine_st.heap[h] } (HeapCellValueTag::StackVar, s) => { self.wam.machine_st.stack[s] } _ => { val } ) } fn deref(&self, mut val: HeapCellValue) -> HeapCellValue { loop { let value = self.store(val); if value.is_var() && value != val { val = value; continue; } return val; } } fn push(&mut self, val: HeapCellValue) { self.wam.machine_st.heap.push(val); } fn push_attr_var_queue(&mut self, attr_var_loc: usize) { self.wam .machine_st .attr_var_init .attr_var_queue .push(attr_var_loc); } fn stack(&mut self) -> &mut Stack { &mut self.wam.machine_st.stack } fn threshold(&self) -> usize { self.wam.machine_st.heap.len() } } #[cfg(test)] pub fn all_cells_marked_and_unforwarded(heap: &[HeapCellValue]) { for (idx, cell) in heap.iter().enumerate() { assert!( cell.get_mark_bit(), "cell {:?} at index {} is not marked", cell, idx ); assert!( !cell.get_forwarding_bit(), "cell {:?} at index {} is forwarded", cell, idx ); } } #[cfg(test)] pub fn all_cells_unmarked(heap: &Heap) { for (idx, cell) in heap.iter().enumerate() { assert!( !cell.get_mark_bit(), "cell {:?} at index {} is still marked", cell, idx ); assert!( !cell.get_forwarding_bit(), "cell {:?} at index {} is still forwarded", cell, idx ); } } #[cfg(test)] pub(crate) fn write_parsed_term_to_heap( machine_st: &mut MachineState, input_stream: Stream, op_dir: &OpDir, ) -> Result { machine_st.read(input_stream, op_dir) } #[cfg(test)] pub(crate) fn parse_and_write_parsed_term_to_heap( machine_st: &mut MachineState, term_string: &'static str, op_dir: &OpDir, ) -> Result { let stream = Stream::from_static_string(term_string, &mut machine_st.arena); write_parsed_term_to_heap(machine_st, stream, op_dir) } impl Machine { pub fn with_test_streams() -> Self { Machine::new(MachineConfig::in_memory()) } pub fn test_load_file(&mut self, file: &str) -> Vec { let stream = Stream::from_owned_string( std::fs::read_to_string(AsRef::::as_ref(file)).unwrap(), &mut self.machine_st.arena, ); self.load_file(file, stream); self.user_output.bytes().map(|b| b.unwrap()).collect() } pub fn test_load_string(&mut self, code: &str) -> Vec { let stream = Stream::from_owned_string(code.to_owned(), &mut self.machine_st.arena); self.load_file("", stream); self.user_output.bytes().map(|b| b.unwrap()).collect() } } #[cfg(test)] mod tests { use super::*; #[test] fn unify_tests() { let mut wam = MachineState::new(); let mut op_dir = default_op_dir(); op_dir.insert((atom!("+"), Fixity::In), OpDesc::build_with(500, YFX)); op_dir.insert((atom!("-"), Fixity::In), OpDesc::build_with(500, YFX)); op_dir.insert((atom!("*"), Fixity::In), OpDesc::build_with(500, YFX)); op_dir.insert((atom!("/"), Fixity::In), OpDesc::build_with(400, YFX)); op_dir.insert((atom!("="), Fixity::In), OpDesc::build_with(700, XFX)); { parse_and_write_parsed_term_to_heap(&mut wam, "f(X,X).", &op_dir).unwrap(); let term_write_result_2 = parse_and_write_parsed_term_to_heap(&mut wam, "f(b,a).", &op_dir).unwrap(); unify!( wam, str_loc_as_cell!(0), str_loc_as_cell!(term_write_result_2.heap_loc) ); assert!(wam.fail); } all_cells_unmarked(&wam.heap); wam.fail = false; wam.heap.clear(); { parse_and_write_parsed_term_to_heap(&mut wam, "f(X,X).", &op_dir).unwrap(); let term_write_result_2 = parse_and_write_parsed_term_to_heap(&mut wam, "f(b,b).", &op_dir).unwrap(); unify!( wam, str_loc_as_cell!(1), heap_loc_as_cell!(term_write_result_2.heap_loc) ); assert!(!wam.fail); } all_cells_unmarked(&wam.heap); wam.fail = false; wam.heap.clear(); { parse_and_write_parsed_term_to_heap(&mut wam, "f(X,X).", &op_dir).unwrap(); let term_write_result_2 = parse_and_write_parsed_term_to_heap(&mut wam, "f(f(A),Y).", &op_dir).unwrap(); unify!( wam, heap_loc_as_cell!(0), heap_loc_as_cell!(term_write_result_2.heap_loc) ); assert!(!wam.fail); } all_cells_unmarked(&wam.heap); wam.fail = false; wam.heap.clear(); { parse_and_write_parsed_term_to_heap(&mut wam, "f(X,X).", &op_dir).unwrap(); let term_write_result_2 = parse_and_write_parsed_term_to_heap(&mut wam, "f(f(A),Y).", &op_dir).unwrap(); unify!( wam, heap_loc_as_cell!(0), heap_loc_as_cell!(term_write_result_2.heap_loc) ); assert!(!wam.fail); } all_cells_unmarked(&wam.heap); wam.fail = false; wam.heap.clear(); { parse_and_write_parsed_term_to_heap(&mut wam, "f(X,X).", &op_dir).unwrap(); let term_write_result_2 = parse_and_write_parsed_term_to_heap(&mut wam, "f(f(A),A).", &op_dir).unwrap(); unify!( wam, heap_loc_as_cell!(0), heap_loc_as_cell!(term_write_result_2.heap_loc) ); assert!(!wam.fail); } all_cells_unmarked(&wam.heap); wam.fail = false; wam.heap.clear(); { parse_and_write_parsed_term_to_heap(&mut wam, "f(X,X).", &op_dir).unwrap(); let term_write_result_2 = parse_and_write_parsed_term_to_heap(&mut wam, "f(A,f(A)).", &op_dir).unwrap(); all_cells_unmarked(&wam.heap); unify!( wam, heap_loc_as_cell!(0), heap_loc_as_cell!(term_write_result_2.heap_loc) ); assert!(!wam.fail); } all_cells_unmarked(&wam.heap); wam.heap.clear(); wam.heap.push(pstr_as_cell!(atom!("this is a string"))); wam.heap.push(heap_loc_as_cell!(1)); wam.heap.push(pstr_as_cell!(atom!("this is a string"))); wam.heap.push(pstr_loc_as_cell!(4)); wam.heap.push(pstr_offset_as_cell!(0)); wam.heap.push(fixnum_as_cell!(Fixnum::build_with(6))); unify!(wam, pstr_loc_as_cell!(0), pstr_loc_as_cell!(2)); assert!(!wam.fail); assert_eq!(wam.heap[1], pstr_loc_as_cell!(4)); all_cells_unmarked(&wam.heap); wam.heap.clear(); wam.heap.push(list_loc_as_cell!(1)); wam.heap.push(atom_as_cell!(atom!("a"))); wam.heap.push(list_loc_as_cell!(3)); wam.heap.push(atom_as_cell!(atom!("b"))); wam.heap.push(heap_loc_as_cell!(0)); wam.heap.push(list_loc_as_cell!(6)); wam.heap.push(atom_as_cell!(atom!("a"))); wam.heap.push(list_loc_as_cell!(8)); wam.heap.push(atom_as_cell!(atom!("b"))); wam.heap.push(heap_loc_as_cell!(5)); unify!(wam, heap_loc_as_cell!(0), heap_loc_as_cell!(5)); assert!(!wam.fail); all_cells_unmarked(&wam.heap); wam.heap.clear(); wam.heap.push(list_loc_as_cell!(1)); wam.heap.push(atom_as_cell!(atom!("a"))); wam.heap.push(list_loc_as_cell!(3)); wam.heap.push(atom_as_cell!(atom!("b"))); wam.heap.push(heap_loc_as_cell!(0)); wam.heap.push(list_loc_as_cell!(6)); wam.heap.push(atom_as_cell!(atom!("a"))); wam.heap.push(list_loc_as_cell!(8)); wam.heap.push(atom_as_cell!(atom!("c"))); wam.heap.push(heap_loc_as_cell!(5)); unify!(wam, heap_loc_as_cell!(0), heap_loc_as_cell!(5)); assert!(wam.fail); wam.fail = false; all_cells_unmarked(&wam.heap); wam.heap.clear(); wam.heap.push(list_loc_as_cell!(1)); wam.heap.push(atom_as_cell!(atom!("a"))); wam.heap.push(list_loc_as_cell!(3)); wam.heap.push(atom_as_cell!(atom!("b"))); wam.heap.push(heap_loc_as_cell!(5)); wam.heap.push(list_loc_as_cell!(6)); wam.heap.push(atom_as_cell!(atom!("a"))); wam.heap.push(list_loc_as_cell!(8)); wam.heap.push(atom_as_cell!(atom!("b"))); wam.heap.push(heap_loc_as_cell!(0)); unify!(wam, heap_loc_as_cell!(0), heap_loc_as_cell!(5)); assert!(!wam.fail); all_cells_unmarked(&wam.heap); wam.heap.clear(); { let term_write_result_1 = parse_and_write_parsed_term_to_heap(&mut wam, "X = g(X,y).", &op_dir).unwrap(); print_heap_terms(wam.heap.iter(), term_write_result_1.heap_loc); unify!(wam, heap_loc_as_cell!(2), str_loc_as_cell!(4)); assert_eq!(wam.heap[2], str_loc_as_cell!(4)); } } #[test] fn test_unify_with_occurs_check() { let mut wam = MachineState::new(); let mut op_dir = default_op_dir(); op_dir.insert((atom!("+"), Fixity::In), OpDesc::build_with(500, YFX)); op_dir.insert((atom!("-"), Fixity::In), OpDesc::build_with(500, YFX)); op_dir.insert((atom!("*"), Fixity::In), OpDesc::build_with(400, YFX)); op_dir.insert((atom!("/"), Fixity::In), OpDesc::build_with(400, YFX)); { parse_and_write_parsed_term_to_heap(&mut wam, "f(X,X).", &op_dir).unwrap(); let term_write_result_2 = parse_and_write_parsed_term_to_heap(&mut wam, "f(A,f(A)).", &op_dir).unwrap(); all_cells_unmarked(&wam.heap); unify_with_occurs_check!( wam, str_loc_as_cell!(0), str_loc_as_cell!(term_write_result_2.heap_loc) ); assert!(wam.fail); } } #[test] fn test_term_compare() { use std::cmp::Ordering; let mut wam = MachineState::new(); wam.heap.push(heap_loc_as_cell!(0)); wam.heap.push(heap_loc_as_cell!(1)); assert_eq!( compare_term_test!(wam, wam.heap[0], wam.heap[1]), Some(Ordering::Less) ); assert_eq!( compare_term_test!(wam, wam.heap[1], wam.heap[0]), Some(Ordering::Greater) ); assert_eq!( compare_term_test!(wam, wam.heap[0], wam.heap[0]), Some(Ordering::Equal) ); assert_eq!( compare_term_test!(wam, wam.heap[1], wam.heap[1]), Some(Ordering::Equal) ); assert_eq!( compare_term_test!( wam, atom_as_cell!(atom!("atom")), atom_as_cstr_cell!(atom!("string")) ), Some(Ordering::Less) ); assert_eq!( compare_term_test!( wam, atom_as_cell!(atom!("atom")), atom_as_cell!(atom!("atom")) ), Some(Ordering::Equal) ); assert_eq!( compare_term_test!( wam, atom_as_cell!(atom!("atom")), atom_as_cell!(atom!("aaa")) ), Some(Ordering::Greater) ); assert_eq!( compare_term_test!( wam, fixnum_as_cell!(Fixnum::build_with(6)), heap_loc_as_cell!(1) ), Some(Ordering::Greater) ); wam.heap.clear(); wam.heap.push(atom_as_cell!(atom!("f"), 1)); wam.heap.push(heap_loc_as_cell!(1)); assert_eq!( compare_term_test!(wam, heap_loc_as_cell!(0), heap_loc_as_cell!(0)), Some(Ordering::Equal) ); assert_eq!( compare_term_test!(wam, heap_loc_as_cell!(0), atom_as_cell!(atom!("a"))), Some(Ordering::Greater) ); wam.heap.clear(); // [1,2,3] wam.heap.push(list_loc_as_cell!(1)); wam.heap.push(fixnum_as_cell!(Fixnum::build_with(1))); wam.heap.push(list_loc_as_cell!(3)); wam.heap.push(fixnum_as_cell!(Fixnum::build_with(2))); wam.heap.push(list_loc_as_cell!(5)); wam.heap.push(fixnum_as_cell!(Fixnum::build_with(3))); wam.heap.push(empty_list_as_cell!()); // [1,2] wam.heap.push(list_loc_as_cell!(8)); wam.heap.push(fixnum_as_cell!(Fixnum::build_with(1))); wam.heap.push(list_loc_as_cell!(10)); wam.heap.push(fixnum_as_cell!(Fixnum::build_with(2))); wam.heap.push(empty_list_as_cell!()); assert_eq!( compare_term_test!(wam, heap_loc_as_cell!(7), heap_loc_as_cell!(7)), Some(Ordering::Equal) ); assert_eq!( compare_term_test!(wam, heap_loc_as_cell!(0), heap_loc_as_cell!(7)), Some(Ordering::Greater) ); assert_eq!( compare_term_test!(wam, empty_list_as_cell!(), heap_loc_as_cell!(7)), Some(Ordering::Less) ); assert_eq!( compare_term_test!( wam, empty_list_as_cell!(), fixnum_as_cell!(Fixnum::build_with(1)) ), Some(Ordering::Greater) ); assert_eq!( compare_term_test!( wam, empty_list_as_cell!(), atom_as_cstr_cell!(atom!("string")) ), Some(Ordering::Less) ); assert_eq!( compare_term_test!(wam, empty_list_as_cell!(), atom_as_cell!(atom!("atom"))), Some(Ordering::Less) ); assert_eq!( compare_term_test!(wam, atom_as_cell!(atom!("atom")), empty_list_as_cell!()), Some(Ordering::Greater) ); let one_p_one = HeapCellValue::from(float_alloc!(1.1, &mut wam.arena)); assert_eq!( compare_term_test!(wam, one_p_one, fixnum_as_cell!(Fixnum::build_with(1))), Some(Ordering::Less) ); assert_eq!( compare_term_test!(wam, fixnum_as_cell!(Fixnum::build_with(1)), one_p_one), Some(Ordering::Greater) ); } #[test] fn is_cyclic_term_tests() { let mut wam = MachineState::new(); assert!(!wam.is_cyclic_term(atom_as_cell!(atom!("f")))); assert!(!wam.is_cyclic_term(fixnum_as_cell!(Fixnum::build_with(555)))); wam.heap.push(heap_loc_as_cell!(0)); assert!(!wam.is_cyclic_term(heap_loc_as_cell!(0))); all_cells_unmarked(&wam.heap); wam.heap.clear(); wam.heap .extend(functor!(atom!("f"), [atom(atom!("a")), atom(atom!("b"))])); assert!(!wam.is_cyclic_term(str_loc_as_cell!(0))); all_cells_unmarked(&wam.heap); assert!(!wam.is_cyclic_term(heap_loc_as_cell!(1))); all_cells_unmarked(&wam.heap); assert!(!wam.is_cyclic_term(heap_loc_as_cell!(2))); all_cells_unmarked(&wam.heap); wam.heap[2] = str_loc_as_cell!(0); print_heap_terms(wam.heap.iter(), 0); assert!(wam.is_cyclic_term(str_loc_as_cell!(0))); all_cells_unmarked(&wam.heap); wam.heap[2] = atom_as_cell!(atom!("b")); wam.heap[1] = str_loc_as_cell!(0); assert!(wam.is_cyclic_term(str_loc_as_cell!(0))); all_cells_unmarked(&wam.heap); assert!(wam.is_cyclic_term(heap_loc_as_cell!(1))); all_cells_unmarked(&wam.heap); wam.heap.clear(); wam.heap.push(pstr_as_cell!(atom!("a string"))); wam.heap.push(empty_list_as_cell!()); assert!(!wam.is_cyclic_term(pstr_loc_as_cell!(0))); } }