From 7b5a2786dad60edbc5d259aab4e9a0b4c7186242 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Thu, 1 Feb 2018 23:03:57 -0700 Subject: [PATCH] add support for setup_call_cleanup --- README.md | 1 + src/prolog/allocator.rs | 2 +- src/prolog/ast.rs | 17 +- src/prolog/builtins.rs | 126 +- src/prolog/codegen.rs | 16 +- src/prolog/debray_allocator.rs | 2 +- src/prolog/io.rs | 18 +- src/prolog/iterators.rs | 13 +- src/prolog/machine/machine_state.rs | 1771 +-------------------- src/prolog/machine/machine_state_impl.rs | 1827 ++++++++++++++++++++++ src/prolog/machine/mod.rs | 2 + src/prolog/macros.rs | 32 +- src/prolog/parser | 2 +- src/tests.rs | 57 +- 14 files changed, 2076 insertions(+), 1810 deletions(-) create mode 100644 src/prolog/machine/machine_state_impl.rs diff --git a/README.md b/README.md index 84030de3..3e609d35 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,7 @@ The following predicates are built-in to rusty-wam. * `once/1` * `reverse/2` * `select/3` +* `setup_call_cleanup/3` * `throw/1` * `true/0` * `var/1` diff --git a/src/prolog/allocator.rs b/src/prolog/allocator.rs index 7aa0eb7c..aa30a415 100644 --- a/src/prolog/allocator.rs +++ b/src/prolog/allocator.rs @@ -18,7 +18,7 @@ pub trait Allocator<'a> fn reset(&mut self); fn reset_contents(&mut self) {} fn reset_arg(&mut self, usize); - fn reset_arg_at_head(&mut self, &Term); + fn reset_arg_at_head(&mut self, &'a Term); fn advance_arg(&mut self); diff --git a/src/prolog/ast.rs b/src/prolog/ast.rs index 7574e536..dc31d649 100644 --- a/src/prolog/ast.rs +++ b/src/prolog/ast.rs @@ -381,6 +381,7 @@ pub enum QueryTerm { Inlined(InlinedQueryTerm), Is(Vec>), Jump(JumpStub), + SetupCallCleanup(Vec>), Term(Term), Throw(Vec>) } @@ -399,6 +400,7 @@ impl QueryTerm { &QueryTerm::Jump(ref vars) => vars.len(), &QueryTerm::CallN(ref terms) => terms.len(), &QueryTerm::Cut => 0, + &QueryTerm::SetupCallCleanup(_) => 3, &QueryTerm::Term(ref term) => term.arity(), } } @@ -422,6 +424,7 @@ pub enum ClauseType<'a> { Functor, Is, Root(&'a TabledRc), + SetupCallCleanup, Throw, } @@ -437,6 +440,7 @@ impl<'a> ClauseType<'a> { &ClauseType::Functor => "functor", &ClauseType::Is => "is", &ClauseType::Root(name) => name.as_str(), + &ClauseType::SetupCallCleanup => "setup_call_cleanup", &ClauseType::Throw => "throw" } } @@ -478,8 +482,8 @@ pub enum ChoiceInstruction { } pub enum CutInstruction { - Cut, - GetLevel, + Cut(RegType), + GetLevel(RegType), NeckCut } @@ -792,6 +796,7 @@ pub enum BuiltInInstruction { GetCutPoint(RegType), DynamicCompareNumber(CompareNumberQT), DynamicIs, + InstallCleaner, InstallNewBlock, InternalCallN, IsAtomic(RegType), @@ -805,6 +810,7 @@ pub enum BuiltInInstruction { UnwindStack } +#[derive(Clone)] pub enum ControlInstruction { Allocate(usize), // num_frames. ArgCall, @@ -813,6 +819,7 @@ pub enum ControlInstruction { CallN(usize), // arity. CatchCall, CatchExecute, + CheckCpExecute, DisplayCall, DisplayExecute, Deallocate, @@ -822,10 +829,11 @@ pub enum ControlInstruction { ExecuteN(usize), FunctorCall, FunctorExecute, - JmpByCall(usize, usize), // arity, global_offset. - JmpByExecute(usize, usize), + GetCleanerCall, GotoCall(usize, usize), // p, arity. GotoExecute(usize, usize), // p, arity. + JmpByCall(usize, usize), // arity, global_offset. + JmpByExecute(usize, usize), IsCall(RegType, ArithmeticTerm), IsExecute(RegType, ArithmeticTerm), Proceed, @@ -852,6 +860,7 @@ impl ControlInstruction { &ControlInstruction::FunctorExecute => true, &ControlInstruction::ThrowCall => true, &ControlInstruction::ThrowExecute => true, + &ControlInstruction::GetCleanerCall => true, &ControlInstruction::GotoCall(..) => true, &ControlInstruction::GotoExecute(..) => true, &ControlInstruction::Proceed => true, diff --git a/src/prolog/builtins.rs b/src/prolog/builtins.rs index 672e43ff..7cae5c8c 100644 --- a/src/prolog/builtins.rs +++ b/src/prolog/builtins.rs @@ -65,10 +65,10 @@ fn get_builtins(atom_tbl: TabledData) -> Code { goto_execute!(32, 2), // goto handle_ball/2. try_me_else!(10), // handle_ball/2, 32. allocate!(2), - get_level!(), + get_level!(perm_v!(1)), fact![get_var_in_fact!(perm_v!(2), 3)], unify!(), - cut!(), + cut!(perm_v!(1)), erase_ball!(), query![put_value!(perm_v!(2), 1)], deallocate!(), @@ -96,9 +96,9 @@ fn get_builtins(atom_tbl: TabledData) -> Code { fail!(), // false/0, 61. try_me_else!(7), // not/1, 62. allocate!(1), - get_level!(), + get_level!(perm_v!(1)), call_n!(1), - cut!(), + cut!(perm_v!(1)), deallocate!(), goto_execute!(61, 0), trust_me!(), @@ -145,8 +145,8 @@ fn get_builtins(atom_tbl: TabledData) -> Code { deallocate!(), goto_execute!(83, 3), retry_me_else!(10), - allocate!(1), - get_level!(), + allocate!(2), + get_level!(perm_v!(2)), fact![get_constant!(atom!("!", atom_tbl), temp_v!(2)), get_var_in_fact!(perm_v!(1), 3)], neck_cut!(), @@ -300,7 +300,7 @@ fn get_builtins(atom_tbl: TabledData) -> Code { proceed!(), retry_me_else!(11), allocate!(4), - get_level!(), + get_level!(perm_v!(1)), fact![get_var_in_fact!(perm_v!(3), 1), get_list!(Level::Shallow, temp_v!(2)), unify_variable!(temp_v!(2)), @@ -309,7 +309,7 @@ fn get_builtins(atom_tbl: TabledData) -> Code { query![put_value!(perm_v!(3), 1), put_var!(perm_v!(2), 3)], functor_call!(), - cut!(), + cut!(perm_v!(1)), query![put_unsafe_value!(4, 1), put_value!(perm_v!(3), 2), put_constant!(Level::Shallow, integer!(1), temp_v!(3)), @@ -318,7 +318,7 @@ fn get_builtins(atom_tbl: TabledData) -> Code { goto_execute!(236, 4), // goto get_args/4. trust_me!(), allocate!(5), - get_level!(), + get_level!(perm_v!(1)), fact![get_var_in_fact!(perm_v!(3), 1), get_list!(Level::Shallow, temp_v!(2)), unify_variable!(perm_v!(5)), @@ -330,7 +330,7 @@ fn get_builtins(atom_tbl: TabledData) -> Code { put_value!(perm_v!(5), 2), put_value!(perm_v!(2), 3)], functor_call!(), - cut!(), + cut!(perm_v!(1)), query![put_unsafe_value!(4, 1), put_value!(perm_v!(3), 2), put_constant!(Level::Shallow, integer!(1), temp_v!(3)), @@ -370,7 +370,7 @@ fn get_builtins(atom_tbl: TabledData) -> Code { query![put_value!(perm_v!(5), 1), put_value!(perm_v!(3), 2), put_value!(temp_v!(5), 3)], - get_arg_call!(), + get_arg_call!(), add!(ArithmeticTerm::Reg(perm_v!(5)), ArithmeticTerm::Number(rc_integer!(1)), 1), @@ -389,17 +389,17 @@ fn get_builtins(atom_tbl: TabledData) -> Code { neck_cut!(), query![put_value!(temp_v!(4), 1), put_constant!(Level::Shallow, integer!(0), temp_v!(2))], - goto_execute!(281, 3), // goto length/3, 281. + goto_execute!(281, 3), // goto length/3, 281. retry_me_else!(10), allocate!(1), - get_level!(), + get_level!(perm_v!(1)), fact![get_var_in_fact!(temp_v!(4), 1), get_var_in_fact!(temp_v!(3), 2)], - is_integer!(temp_v!(3)), + is_integer!(temp_v!(3)), query![put_value!(temp_v!(4), 1), put_constant!(Level::Shallow, integer!(0), temp_v!(2))], goto_call!(281, 3), // goto length/3, 281. - cut!(), + cut!(perm_v!(1)), deallocate!(), proceed!(), trust_me!(), @@ -413,9 +413,9 @@ fn get_builtins(atom_tbl: TabledData) -> Code { None), set_constant!(atom!("integer_expected", atom_tbl)), set_value!(temp_v!(4))], - goto_execute!(59, 1), // goto throw/1, 59. + goto_execute!(59, 1), // goto throw/1, 59. switch_on_term!(1,2,5,0), // length/3, 281. - try_me_else!(3), + try_me_else!(3), fact![get_constant!(Constant::EmptyList, temp_v!(1)), get_var_in_fact!(temp_v!(4), 2), get_value!(temp_v!(4), 3)], @@ -436,7 +436,92 @@ fn get_builtins(atom_tbl: TabledData) -> Code { put_unsafe_value!(2, 2), put_value!(perm_v!(3), 3)], deallocate!(), - goto_execute!(281, 3) // goto length/3, 281. + goto_execute!(281, 3), // goto length/3, 281. + allocate!(4), // setup_call_cleanup/3, 294. + get_level!(perm_v!(1)), + fact![get_var_in_fact!(perm_v!(2), 2), + get_var_in_fact!(perm_v!(3), 3)], + call_n!(1), + cut!(perm_v!(1)), + query![put_var!(perm_v!(4), 1)], + get_current_block!(), + query![put_value!(perm_v!(3), 1), + put_unsafe_value!(4, 2), + put_value!(perm_v!(2), 3)], + deallocate!(), + jmp_execute!(3, 1), + try_me_else!(5), // 304. + is_var!(temp_v!(1)), + neck_cut!(), + query![put_constant!(Level::Shallow, atom!("instantiation_error", atom_tbl), + temp_v!(1))], + goto_execute!(59, 1), + trust_me!(), + query![get_var_in_query!(temp_v!(4), 2), + put_value!(temp_v!(3), 2), + get_var_in_query!(temp_v!(5), 3), + put_value!(temp_v!(4), 3)], + goto_execute!(312, 3), + try_me_else!(13), // sgc_helper/3, 312. + allocate!(4), + fact![get_var_in_fact!(perm_v!(4), 1), + get_var_in_fact!(perm_v!(3), 2), + get_var_in_fact!(perm_v!(2), 3)], + get_level!(perm_v!(1)), + query![put_value!(perm_v!(4), 1)], + install_cleaner!(), + query![put_var!(temp_v!(2), 1)], + install_new_block!(), + query![put_value!(perm_v!(3), 1)], + call_n!(1), + query![put_value!(perm_v!(2), 1), + put_unsafe_value!(1, 2)], + deallocate!(), + check_cp_execute!(), + retry_me_else!(10), + allocate!(1), + query![put_value!(temp_v!(3), 1)], + reset_block!(), + query![put_var!(perm_v!(1), 1)], + get_ball!(), + goto_call!(337, 0), // goto run_cleaners_with_handling/0, 337. + query![put_value!(perm_v!(1), 1)], + deallocate!(), + goto_execute!(59, 1), + trust_me!(), + goto_execute!(349, 0), // goto run_cleaners_without_handling/0, 349. + try_me_else!(10), // run_cleaners_with_handling/0, 337. + allocate!(2), + get_level!(perm_v!(1)), + query![put_var!(perm_v!(2), 1)], + get_cleaner_call!(), + query![put_value!(perm_v!(2), 1), + put_var!(temp_v!(4), 2), + put_constant!(Level::Shallow, atom!("true", atom_tbl), temp_v!(3))], + goto_call!(5, 3), // goto catch/3, 5. + cut!(perm_v!(1)), + deallocate!(), + goto_execute!(337, 0), // goto run_cleaners_with_handling/0, 337. + trust_me!(), + proceed!(), + try_me_else!(10), // run_cleaners_without_handling/1, 349. + allocate!(2), + get_level!(perm_v!(1)), + query![put_var!(perm_v!(2), 1)], + get_cleaner_call!(), + query![put_value!(perm_v!(2), 1)], + call_n!(1), + cut!(perm_v!(1)), + deallocate!(), + goto_execute!(349, 0), // goto run_cleaners_without_handling/1, 349. + trust_me!(), + proceed!(), + allocate!(1), // sgc_on_success/2, 361. + fact![get_var_in_fact!(perm_v!(1), 2)], + reset_block!(), + cut!(perm_v!(1)), + deallocate!(), + proceed!() ] } @@ -486,7 +571,7 @@ pub fn build_code_dir(atom_tbl: TabledData) -> (Code, CodeDir, OpDir) op_dir.insert((tabled_rc!("->", atom_tbl), Fixity::In), (XFY, 1050)); op_dir.insert((tabled_rc!("=..", atom_tbl), Fixity::In), (XFX, 700)); - + // there are 63 registers in the VM, so call/N is defined for all 0 <= N <= 62 // (an extra register is needed for the predicate name) for arity in 0 .. 63 { @@ -522,6 +607,7 @@ pub fn build_code_dir(atom_tbl: TabledData) -> (Code, CodeDir, OpDir) code_dir.insert((tabled_rc!("=..", atom_tbl), 2), (PredicateKeyType::BuiltIn, 208)); code_dir.insert((tabled_rc!("length", atom_tbl), 2), (PredicateKeyType::BuiltIn, 261)); - + code_dir.insert((tabled_rc!("setup_call_cleanup", atom_tbl), 3), (PredicateKeyType::BuiltIn, 294)); + (builtin_code, code_dir, op_dir) } diff --git a/src/prolog/codegen.rs b/src/prolog/codegen.rs index 63de49e1..5e6e416a 100644 --- a/src/prolog/codegen.rs +++ b/src/prolog/codegen.rs @@ -245,6 +245,8 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker> fn add_conditional_call(code: &mut Code, qt: &QueryTerm, pvs: usize) { match qt { + &QueryTerm::SetupCallCleanup(_) => + code.push(goto_call!(294, 3)), &QueryTerm::Arg(_) => { let call = ControlInstruction::ArgCall; code.push(Line::Control(call)); @@ -287,6 +289,8 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker> match code.last_mut() { Some(&mut Line::Control(ref mut ctrl)) => match ctrl.clone() { + ControlInstruction::GotoCall(p, arity) => + *ctrl = ControlInstruction::GotoExecute(p, arity), ControlInstruction::ArgCall => *ctrl = ControlInstruction::ArgExecute, ControlInstruction::Call(name, arity, _) => @@ -310,7 +314,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker> ControlInstruction::Proceed => {}, _ => dealloc_index += 1 // = code.len() }, - Some(&mut Line::Cut(CutInstruction::Cut)) => + Some(&mut Line::Cut(CutInstruction::Cut(_))) => dealloc_index += 1, _ => {} }; @@ -402,7 +406,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker> code.push(if chunk_num == 0 { Line::Cut(CutInstruction::NeckCut) } else { - Line::Cut(CutInstruction::Cut) + Line::Cut(CutInstruction::Cut(perm_v!(1))) }); }, &QueryTerm::Is(ref terms) => { @@ -460,7 +464,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker> body.push(Line::Control(ControlInstruction::Allocate(perm_vars))); if conjunct_info.has_deep_cut { - body.push(Line::Cut(CutInstruction::GetLevel)); + body.push(Line::Cut(CutInstruction::GetLevel(perm_v!(1)))); } } } @@ -491,7 +495,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker> let mut code = Vec::new(); if let &QueryTerm::Term(ref term) = p0 { - self.marker.reset_arg_at_head(term); + self.marker.reset_arg(term.arity()); self.compile_seq_prelude(&conjunct_info, &mut code); if let &Term::Clause(..) = term { @@ -503,6 +507,8 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker> } } + self.marker.reset_arg_at_head(term); + let iter = ChunkedIterator::from_rule_body(p1, clauses); try!(self.compile_seq(iter, &conjunct_info, &mut code, false)); @@ -562,7 +568,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker> vs.populate_restricting_sets(); self.marker.drain_var_data(vs); - self.marker.reset_arg_at_head(term); + self.marker.reset_arg(term.arity()); let mut code = Vec::new(); diff --git a/src/prolog/debray_allocator.rs b/src/prolog/debray_allocator.rs index e922d60a..fdfba43b 100644 --- a/src/prolog/debray_allocator.rs +++ b/src/prolog/debray_allocator.rs @@ -346,7 +346,7 @@ impl<'a> Allocator<'a> for DebrayAllocator<'a> self.temp_lb = arity + 1; } - fn reset_arg_at_head(&mut self, term: &Term) { + fn reset_arg_at_head(&mut self, term: &'a Term) { self.arg_c = 1; self.temp_lb = term.arity() + 1; diff --git a/src/prolog/io.rs b/src/prolog/io.rs index 694176de..5d467db6 100644 --- a/src/prolog/io.rs +++ b/src/prolog/io.rs @@ -110,9 +110,11 @@ impl fmt::Display for ControlInstruction { &ControlInstruction::CallN(arity) => write!(f, "call_N {}", arity), &ControlInstruction::CatchCall => - write!(f, "call_catch"), + write!(f, "catch_call"), &ControlInstruction::CatchExecute => - write!(f, "execute_catch"), + write!(f, "catch_execute"), + &ControlInstruction::CheckCpExecute => + write!(f, "check_cp_execute"), &ControlInstruction::DisplayCall => write!(f, "display_call"), &ControlInstruction::DisplayExecute => @@ -131,6 +133,8 @@ impl fmt::Display for ControlInstruction { write!(f, "deallocate"), &ControlInstruction::Execute(ref name, arity) => write!(f, "execute {}/{}", name, arity), + &ControlInstruction::GetCleanerCall => + write!(f, "get_cleaner_call"), &ControlInstruction::GotoCall(p, arity) => write!(f, "goto_call {}/{}", p, arity), &ControlInstruction::GotoExecute(p, arity) => @@ -189,6 +193,8 @@ impl fmt::Display for BuiltInInstruction { write!(f, "get_current_block X1"), &BuiltInInstruction::GetCutPoint(r) => write!(f, "get_cp {}", r), + &BuiltInInstruction::InstallCleaner => + write!(f, "install_cleaner"), &BuiltInInstruction::InstallNewBlock => write!(f, "install_new_block"), &BuiltInInstruction::InternalCallN => @@ -307,12 +313,12 @@ impl fmt::Display for ArithmeticInstruction { impl fmt::Display for CutInstruction { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - &CutInstruction::Cut => - write!(f, "cut"), + &CutInstruction::Cut(r) => + write!(f, "cut {}", r), &CutInstruction::NeckCut => write!(f, "neck_cut"), - &CutInstruction::GetLevel => - write!(f, "get_level") + &CutInstruction::GetLevel(r) => + write!(f, "get_level {}", r) } } } diff --git a/src/prolog/iterators.rs b/src/prolog/iterators.rs index 3f58f3b8..80146fc1 100644 --- a/src/prolog/iterators.rs +++ b/src/prolog/iterators.rs @@ -39,7 +39,7 @@ impl<'a> QueryIterator<'a> { &QueryTerm::Catch(ref terms) => { let state = TermIterState::Clause(0, ClauseType::Catch, terms); QueryIterator { state_stack: vec![state] } - }, + }, &QueryTerm::DuplicateTerm(ref terms) => { let state = TermIterState::Clause(0, ClauseType::DuplicateTerm, terms); QueryIterator { state_stack: vec![state] } @@ -48,6 +48,10 @@ impl<'a> QueryIterator<'a> { let state = TermIterState::Clause(0, ClauseType::Arg, terms); QueryIterator { state_stack: vec![state] } }, + &QueryTerm::SetupCallCleanup(ref terms) => { + let state = TermIterState::Clause(0, ClauseType::SetupCallCleanup, terms); + QueryIterator { state_stack: vec![state] } + }, &QueryTerm::Functor(ref terms) => { let state = TermIterState::Clause(0, ClauseType::Functor, terms); QueryIterator { state_stack: vec![state] } @@ -60,7 +64,7 @@ impl<'a> QueryIterator<'a> { | &QueryTerm::Is(ref terms) => { let state = TermIterState::Clause(0, ClauseType::Is, terms); QueryIterator { state_stack: vec![state] } - }, + }, &QueryTerm::Inlined(InlinedQueryTerm::IsAtomic(ref terms)) | &QueryTerm::Inlined(InlinedQueryTerm::IsInteger(ref terms)) | &QueryTerm::Inlined(InlinedQueryTerm::IsVar(ref terms)) => @@ -290,6 +294,11 @@ impl<'a> ChunkedIterator<'a> break; } }, + &QueryTerm::SetupCallCleanup(_) => { + result.push(term); + arity = 3; + break; + }, &QueryTerm::CallN(ref child_terms) => { result.push(term); arity = child_terms.len() + 1; diff --git a/src/prolog/machine/machine_state.rs b/src/prolog/machine/machine_state.rs index 3222866e..0bc19e57 100644 --- a/src/prolog/machine/machine_state.rs +++ b/src/prolog/machine/machine_state.rs @@ -1,25 +1,17 @@ use prolog::and_stack::*; use prolog::ast::*; -use prolog::builtins::*; use prolog::copier::*; -use prolog::heap_iter::*; -use prolog::heap_print::*; -use prolog::num::{Integer, ToPrimitive, Zero}; -use prolog::num::bigint::{BigInt, BigUint}; -use prolog::num::rational::Ratio; use prolog::or_stack::*; use prolog::tabled_rc::*; -use std::cmp::max; use std::ops::{Index, IndexMut}; -use std::rc::Rc; -struct DuplicateTerm<'a> { +pub(super) struct DuplicateTerm<'a> { state: &'a mut MachineState } impl<'a> DuplicateTerm<'a> { - fn new(state: &'a mut MachineState) -> Self { + pub(super) fn new(state: &'a mut MachineState) -> Self { DuplicateTerm { state: state } } } @@ -65,13 +57,13 @@ impl<'a> CopierTarget for DuplicateTerm<'a> { } } -struct DuplicateBallTerm<'a> { +pub(super) struct DuplicateBallTerm<'a> { state: &'a mut MachineState, heap_boundary: usize } impl<'a> DuplicateBallTerm<'a> { - fn new(state: &'a mut MachineState) -> Self { + pub(super) fn new(state: &'a mut MachineState) -> Self { let hb = state.heap.len(); DuplicateBallTerm { state: state, heap_boundary: hb } } @@ -154,18 +146,6 @@ impl IndexMut for MachineState { } } -macro_rules! try_or_fail { - ($s:ident, $e:expr) => {{ - match $e { - Ok(val) => val, - Err(msg) => { - $s.throw_exception(msg); - return; - } - } - }} -} - #[derive(Clone, Copy)] pub(super) enum MachineMode { Read, @@ -192,1745 +172,6 @@ pub struct MachineState { pub(super) hb: usize, pub(super) block: usize, // an offset into the OR stack. pub(super) ball: (usize, Vec), // heap boundary, and a term copy - pub(super) interms: Vec // intermediate numbers. -} - -impl MachineState { - pub(super) fn new(atom_tbl: TabledData) -> MachineState { - MachineState { atom_tbl, - s: 0, - p: CodePtr::default(), - b: 0, - b0: 0, - e: 0, - num_of_args: 0, - cp: CodePtr::default(), - fail: false, - heap: Heap::with_capacity(256), - mode: MachineMode::Write, - and_stack: AndStack::new(), - or_stack: OrStack::new(), - registers: vec![Addr::HeapCell(0); 64], - trail: Vec::new(), - tr: 0, - hb: 0, - block: 0, - ball: (0, Vec::new()), - interms: vec![Number::default(); 256], - } - } - - fn next_global_index(&self) -> usize { - max(if self.and_stack.len() > 0 { self.and_stack[self.e].global_index } else { 0 }, - if self.b > 0 { self.or_stack[self.b - 1].global_index } else { 0 }) + 1 - } - - pub(crate) fn store(&self, a: Addr) -> Addr { - match a { - Addr::HeapCell(r) => self.heap[r].as_addr(r), - Addr::StackCell(fr, sc) => self.and_stack[fr][sc].clone(), - addr => addr - } - } - - pub(crate) fn deref(&self, mut a: Addr) -> Addr { - loop { - let value = self.store(a.clone()); - - if value.is_ref() && value != a { - a = value; - continue; - } - - return a; - }; - } - - fn bind(&mut self, r1: Ref, a2: Addr) { - let t2 = self.store(a2); - - match r1 { - Ref::StackCell(fr, sc) => - self.and_stack[fr][sc] = t2, - Ref::HeapCell(hc) => - self.heap[hc] = HeapCellValue::Addr(t2) - }; - - self.trail(r1); - } - - pub(super) fn print_term(&self, a: Addr, fmt: Fmt, output: Outputter) -> Outputter - where Fmt: HeapCellValueFormatter, Outputter: HeapCellValueOutputter - { - let iter = HeapCellPreOrderIterator::new(&self, a); - let printer = HeapCellPrinter::new(iter, fmt, output); - - printer.print() - } - - fn unify(&mut self, a1: Addr, a2: Addr) { - let mut pdl = vec![a1, a2]; - - 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 { - match (self.store(d1.clone()), self.store(d2.clone())) { - (Addr::HeapCell(hc), _) => - self.bind(Ref::HeapCell(hc), d2), - (_, Addr::HeapCell(hc)) => - self.bind(Ref::HeapCell(hc), 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::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(c1), Addr::Con(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 - }; - } - } - } - - fn trail(&mut self, r: Ref) { - match r { - Ref::HeapCell(hc) => { - if hc < self.hb { - self.trail.push(r); - self.tr += 1; - } - }, - Ref::StackCell(fr, _) => { - let fr_gi = self.and_stack[fr].global_index; - let b_gi = if !self.or_stack.is_empty() { - if self.b > 0 { - let b = self.b - 1; - self.or_stack[b].global_index - } else { - 0 - } - } else { - 0 - }; - - if fr_gi < b_gi { - self.trail.push(r); - self.tr += 1; - } - } - } - } - - fn unwind_trail(&mut self, a1: usize, a2: usize) { - for i in a1 .. a2 { - match self.trail[i] { - Ref::HeapCell(r) => - self.heap[r] = HeapCellValue::Addr(Addr::HeapCell(r)), - Ref::StackCell(fr, sc) => - self.and_stack[fr][sc] = Addr::StackCell(fr, sc) - } - } - } - - fn tidy_trail(&mut self) { - if self.b == 0 { - return; - } - - let b = self.b - 1; - let mut i = self.or_stack[b].tr; - - while i < self.tr { - let tr_i = self.trail[i]; - let hb = self.hb; - - match tr_i { - Ref::HeapCell(tr_i) => - if tr_i < hb { //|| ((h < tr_i) && tr_i < b) { - i += 1; - } else { - let tr = self.tr; - let val = self.trail[tr - 1]; - self.trail[i] = val; - }, - Ref::StackCell(fr, _) => { - let b = self.b - 1; - let fr_gi = self.and_stack[fr].global_index; - let b_gi = if !self.or_stack.is_empty() { - self.or_stack[b].global_index - } else { - 0 - }; - - if fr_gi < b_gi { - i += 1; - } else { - let tr = self.tr; - let val = self.trail[tr - 1]; - self.trail[i] = val; - } - } - }; - } - } - - fn write_constant_to_var(&mut self, addr: Addr, c: Constant) { - let addr = self.deref(addr); - - match self.store(addr) { - Addr::HeapCell(hc) => { - self.heap[hc] = HeapCellValue::Addr(Addr::Con(c.clone())); - self.trail(Ref::HeapCell(hc)); - }, - Addr::StackCell(fr, sc) => { - self.and_stack[fr][sc] = Addr::Con(c.clone()); - self.trail(Ref::StackCell(fr, sc)); - }, - Addr::Con(c1) => { - if c1 != c { - self.fail = true; - } - }, - _ => self.fail = true - }; - } - - fn get_number(&self, at: &ArithmeticTerm) -> Result> { - - match at { - &ArithmeticTerm::Reg(r) => { - let addr = self[r].clone(); - let item = self.store(self.deref(addr)); - - match item { - Addr::Con(Constant::Number(n)) => Ok(n), - _ => { - let atom_tbl = self.atom_tbl.clone(); - Err(functor!(self.atom_tbl, - "instantiation_error", - 1, - [heap_atom!("(is)/2", atom_tbl)])) - } - } - }, - &ArithmeticTerm::Interm(i) => Ok(self.interms[i-1].clone()), - &ArithmeticTerm::Number(ref n) => Ok(n.clone()), - } - } - - fn get_rational(&self, at: &ArithmeticTerm) -> Result>, Vec> { - let n = self.get_number(at)?; - - match n { - Number::Rational(r) => Ok(r), - Number::Float(fl) => - if let Some(r) = Ratio::from_float(fl.into_inner()) { - Ok(Rc::new(r)) - } else { - Err(functor!(self.atom_tbl, - "instantiation_error", - 1, - [heap_atom!("(is)/2", self.atom_tbl)])) - }, - Number::Integer(bi) => - Ok(Rc::new(Ratio::from_integer((*bi).clone()))) - } - } - - fn signed_bitwise_op(&self, n1: &BigInt, n2: &BigInt, f: Op) -> Rc - where Op: FnOnce(&BigUint, &BigUint) -> BigUint - { - let n1_b = n1.to_signed_bytes_le(); - let n2_b = n2.to_signed_bytes_le(); - - let u_n1 = BigUint::from_bytes_le(&n1_b); - let u_n2 = BigUint::from_bytes_le(&n2_b); - - Rc::new(BigInt::from_signed_bytes_le(&f(&u_n1, &u_n2).to_bytes_le())) - } - - fn arith_eval_by_metacall(&self, r: RegType) -> Result> - { - let instantiation_err = functor!(self.atom_tbl.clone(), "instantiation_error", 1, - [heap_atom!("(is)/2", self.atom_tbl.clone())]); - - let a = self[r].clone(); - - if let &Addr::Con(Constant::Number(ref n)) = &a { - return Ok(n.clone()); - } - - let mut interms: Vec = Vec::with_capacity(64); - - for heap_val in self.post_order_iter(a) { - match heap_val { - HeapCellValue::NamedStr(2, name, Some(Fixity::In)) => { - let a2 = interms.pop().unwrap(); - let a1 = interms.pop().unwrap(); - - match name.as_str() { - "+" => interms.push(a1 + a2), - "-" => interms.push(a1 - a2), - "*" => interms.push(a1 * a2), - "rdiv" => - match NumberPair::from(a1, a2) { - NumberPair::Rational(r1, r2) => - interms.push(Number::Rational(self.rdiv(r1, r2)?)), - _ => - return Err(instantiation_err) - }, - "//" => interms.push(Number::Integer(self.idiv(a1, a2)?)), - "div" => interms.push(Number::Integer(self.fidiv(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)?)), - _ => return Err(instantiation_err) - } - }, - HeapCellValue::NamedStr(1, name, Some(Fixity::Pre)) => { - let a1 = interms.pop().unwrap(); - - match name.as_str() { - "-" => interms.push(- a1), - _ => return Err(instantiation_err) - } - }, - HeapCellValue::Addr(Addr::Con(Constant::Number(n))) => - interms.push(n), - _ => - return Err(instantiation_err) - } - }; - - Ok(interms.pop().unwrap()) - } - - fn rdiv(&self, r1: Rc>, r2: Rc>) - -> Result>, Vec> - { - if *r2 == Ratio::zero() { - Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, - [heap_atom!("zero_divisor", self.atom_tbl.clone())])) - } else { - Ok(Rc::new(&*r1 / &*r2)) - } - } - - fn fidiv(&self, n1: Number, n2: Number) -> Result, Vec> - { - match (n1, n2) { - (Number::Integer(n1), Number::Integer(n2)) => - if *n2 == BigInt::zero() { - Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, - [heap_atom!("zero_divisor", self.atom_tbl.clone())])) - } else { - Ok(Rc::new(n1.div_floor(&n2))) - }, - _ => Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, - [heap_atom!("expected_integer_args", self.atom_tbl.clone())])) - } - } - - fn idiv(&self, n1: Number, n2: Number) -> Result, Vec> - { - match (n1, n2) { - (Number::Integer(n1), Number::Integer(n2)) => - if *n2 == BigInt::zero() { - Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, - [heap_atom!("zero_divisor", self.atom_tbl.clone())])) - } else { - Ok(Rc::new(&*n1 / &*n2)) - }, - _ => - Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, - [heap_atom!("expected_integer_args", self.atom_tbl.clone())])) - } - } - - fn div(&self, n1: Number, n2: Number) -> Result> - { - if n2.is_zero() { - Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, - [heap_atom!("zero_divisor", self.atom_tbl.clone())])) - } else { - Ok(n1 / n2) - } - } - - fn shr(&self, n1: Number, n2: Number) -> Result, Vec> - { - match (n1, n2) { - (Number::Integer(n1), Number::Integer(n2)) => - match n2.to_usize() { - Some(n2) => Ok(Rc::new(&*n1 >> n2)), - _ => Ok(Rc::new(&*n1 >> usize::max_value())) - }, - _ => - Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, - [heap_atom!("expected_integer_args", self.atom_tbl.clone())])) - } - } - - fn shl(&self, n1: Number, n2: Number) -> Result, Vec> - { - match (n1, n2) { - (Number::Integer(n1), Number::Integer(n2)) => - match n2.to_usize() { - Some(n2) => Ok(Rc::new(&*n1 << n2)), - _ => Ok(Rc::new(&*n1 << usize::max_value())) - }, - _ => - Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, - [heap_atom!("expected_integer_args", self.atom_tbl.clone())])) - } - } - - fn xor(&self, n1: Number, n2: Number) -> Result, Vec> - { - match (n1, n2) { - (Number::Integer(n1), Number::Integer(n2)) => - Ok(self.signed_bitwise_op(&*n1, &*n2, |u_n1, u_n2| u_n1 ^ u_n2)), - _ => - Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, - [heap_atom!("expected_integer_args", self.atom_tbl.clone())])) - } - } - - fn and(&self, n1: Number, n2: Number) -> Result, Vec> - { - match (n1, n2) { - (Number::Integer(n1), Number::Integer(n2)) => - Ok(self.signed_bitwise_op(&*n1, &*n2, |u_n1, u_n2| u_n1 & u_n2)), - _ => - Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, - [heap_atom!("expected_integer_args", self.atom_tbl.clone())])) - } - } - - fn modulus(&self, n1: Number, n2: Number) -> Result, Vec> - { - match (n1, n2) { - (Number::Integer(n1), Number::Integer(n2)) => - if *n2 == BigInt::zero() { - Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, - [heap_atom!("zero_divisor", self.atom_tbl.clone())])) - } else { - Ok(Rc::new(n1.mod_floor(&n2))) - }, - _ => - Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, - [heap_atom!("expected_integer_args", self.atom_tbl.clone())])) - } - } - - fn remainder(&self, n1: Number, n2: Number) -> Result, Vec> - { - match (n1, n2) { - (Number::Integer(n1), Number::Integer(n2)) => - if *n2 == BigInt::zero() { - Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, - [heap_atom!("zero_divisor", self.atom_tbl.clone())])) - } else { - Ok(Rc::new(&*n1 % &*n2)) - }, - _ => - Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, - [heap_atom!("expected_integer_args", self.atom_tbl.clone())])) - } - } - - fn or(&self, n1: Number, n2: Number) -> Result, Vec> - { - match (n1, n2) { - (Number::Integer(n1), Number::Integer(n2)) => - Ok(self.signed_bitwise_op(&*n1, &*n2, |u_n1, u_n2| u_n1 & u_n2)), - _ => - Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, - [heap_atom!("expected_integer_args", self.atom_tbl.clone())])) - } - } - - pub(super) fn execute_arith_instr(&mut self, instr: &ArithmeticInstruction) { - 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] = n1 + n2; - 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] = n1 - n2; - 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] = n1 * n2; - self.p += 1; - }, - &ArithmeticInstruction::RDiv(ref a1, ref a2, t) => { - let r1 = try_or_fail!(self, self.get_rational(a1)); - let r2 = try_or_fail!(self, self.get_rational(a2)); - - self.interms[t - 1] = Number::Rational(try_or_fail!(self, self.rdiv(r1, r2))); - self.p += 1; - }, - &ArithmeticInstruction::FIDiv(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.fidiv(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::Neg(ref a1, t) => { - let n1 = try_or_fail!(self, self.get_number(a1)); - - self.interms[t - 1] = - 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; - } - }; - } - - 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.deref(self[reg].clone()); - - match self.store(addr.clone()) { - Addr::HeapCell(hc) => { - let h = self.heap.h; - - self.heap.push(HeapCellValue::Addr(Addr::Lis(h+1))); - self.bind(Ref::HeapCell(hc), Addr::HeapCell(h)); - - self.mode = MachineMode::Write; - }, - Addr::StackCell(fr, sc) => { - let h = self.heap.h; - - self.heap.push(HeapCellValue::Addr(Addr::Lis(h+1))); - self.bind(Ref::StackCell(fr, sc), Addr::HeapCell(h)); - - self.mode = MachineMode::Write; - }, - Addr::Lis(a) => { - self.s = a; - self.mode = MachineMode::Read; - }, - _ => self.fail = true - }; - }, - &FactInstruction::GetStructure(_, ref name, arity, reg, fixity) => { - 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 str, _) = result { - if narity == arity && *name == *str { - self.s = a + 1; - self.mode = MachineMode::Read; - } else { - self.fail = true; - } - } - }, - Addr::HeapCell(_) | Addr::StackCell(_, _) => { - let h = self.heap.h; - - self.heap.push(HeapCellValue::Addr(Addr::Str(h + 1))); - self.heap.push(HeapCellValue::NamedStr(arity, name.clone(), fixity)); - - 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(_, _) => v, - Addr::Con(_) => c, - Addr::Lis(_) => l, - Addr::Str(_) => s - }; - - 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 name, arity, reg, fixity) => { - let h = self.heap.h; - - self.heap.push(HeapCellValue::NamedStr(arity, name.clone(), fixity)); - 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 { - self.heap.push(HeapCellValue::Addr(addr)); - 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))); - } - } - } - } - - fn try_call_predicate(&mut self, code_dir: &CodeDir, name: TabledRc, arity: usize) - { - let compiled_tl_index = code_dir.get(&(name, arity)).map(|index| index.1); - - match compiled_tl_index { - Some(compiled_tl_index) => { - self.cp = self.p + 1; - self.num_of_args = arity; - self.b0 = self.b; - self.p = CodePtr::DirEntry(compiled_tl_index); - }, - None => self.fail = true - }; - } - - fn try_execute_predicate(&mut self, code_dir: &CodeDir, name: TabledRc, arity: usize) - { - let compiled_tl_index = code_dir.get(&(name, arity)).map(|index| index.1); - - match compiled_tl_index { - Some(compiled_tl_index) => { - self.num_of_args = arity; - self.b0 = self.b; - self.p = CodePtr::DirEntry(compiled_tl_index); - }, - None => self.fail = true - }; - } - - fn handle_internal_call_n(&mut self, code_dir: &CodeDir) - { - let arity = self.num_of_args + 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; - - if let Some((name, arity)) = self.setup_call_n(arity - 1) { - self.try_execute_predicate(code_dir, name, arity); - } - } else { - self.fail = true; - } - } - - fn goto_throw(&mut self) { - self.num_of_args = 1; - self.b0 = self.b; - self.p = CodePtr::DirEntry(59); - } - - fn throw_exception(&mut self, hcv: Vec) { - let h = self.heap.h; - - self.registers[1] = Addr::HeapCell(h); - - self.heap.append(hcv); - self.goto_throw(); - } - - fn setup_call_n(&mut self, arity: usize) -> Option - { - 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 atom_tbl = self.atom_tbl.clone(); - self.throw_exception(functor!(atom_tbl, - "representation_error", - 1, - [heap_atom!("exceeds_max_arity", atom_tbl)])); - 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 atom_tbl = self.atom_tbl.clone(); - self.throw_exception(functor!(atom_tbl, "instantiation_error", 0, [])); - return None; - }, - _ => { - let atom_tbl = self.atom_tbl.clone(); - self.throw_exception(functor!(atom_tbl, - "type_error", - 2, - [heap_atom!("callable", atom_tbl), - HeapCellValue::Addr(addr)])); - return None; - } - }; - - Some((name, arity + narity - 1)) - } - - pub(super) fn copy_and_align_ball_to_heap(&mut self) { - let diff = self.ball.0 - self.heap.h; - - for heap_value in self.ball.1.iter().cloned() { - self.heap.push(match heap_value { - HeapCellValue::Addr(Addr::Con(c)) => - HeapCellValue::Addr(Addr::Con(c)), - HeapCellValue::Addr(Addr::Lis(a)) => - HeapCellValue::Addr(Addr::Lis(a - diff)), - HeapCellValue::Addr(Addr::HeapCell(hc)) => - HeapCellValue::Addr(Addr::HeapCell(hc - diff)), - HeapCellValue::Addr(Addr::Str(s)) => - HeapCellValue::Addr(Addr::Str(s - diff)), - _ => heap_value - }); - } - } - - fn try_get_arg(&mut self) -> Result<(), Vec> - { - let a1 = self.store(self.deref(self[temp_v!(1)].clone())); - - if let Addr::Con(Constant::Number(Number::Integer(i))) = a1 { - let a2 = self.store(self.deref(self[temp_v!(2)].clone())); - - if let Addr::Str(o) = a2 { - match self.heap[o].clone() { - HeapCellValue::NamedStr(arity, _, _) => - match i.to_usize() { - Some(i) if 1 <= i && i <= arity => { - let a3 = self[temp_v!(3)].clone(); - let h_a = Addr::HeapCell(o + i); - - self.unify(a3, h_a); - }, - _ => self.fail = true - }, - _ => self.fail = true - }; - } else { - return Err(functor!(self.atom_tbl, - "type_error", - 1, - [heap_atom!("compound_expected", self.atom_tbl)])); - } - } - - Ok(()) - } - - fn compare_numbers(&mut self, cmp: CompareNumberQT, n1: Number, n2: Number) { - self.fail = match cmp { - CompareNumberQT::GreaterThan if !(n1.gt(n2)) => true, - CompareNumberQT::GreaterThanOrEqual if !(n1.gte(n2)) => true, - CompareNumberQT::LessThan if !(n1.lt(n2)) => true, - CompareNumberQT::LessThanOrEqual if !(n1.lte(n2)) => true, - CompareNumberQT::NotEqual if !(n1.ne(n2)) => true, - CompareNumberQT::Equal if !(n1.eq(n2)) => true, - _ => false - }; - - self.p += 1; - } - - pub(super) fn execute_built_in_instr(&mut self, code_dir: &CodeDir, instr: &BuiltInInstruction) - { - match instr { - &BuiltInInstruction::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); - }, - &BuiltInInstruction::DynamicCompareNumber(cmp) => { - let n1 = try_or_fail!(self, self.arith_eval_by_metacall(temp_v!(1))); - let n2 = try_or_fail!(self, self.arith_eval_by_metacall(temp_v!(2))); - - self.compare_numbers(cmp, n1, n2); - }, - &BuiltInInstruction::DynamicIs => { - let a = self[temp_v!(1)].clone(); - let result = try_or_fail!(self, self.arith_eval_by_metacall(temp_v!(2))); - - self.unify(a, Addr::Con(Constant::Number(result))); - self.p += 1; - }, - &BuiltInInstruction::GetArgCall => - try_or_fail!(self, { - let val = self.try_get_arg(); - self.p += 1; - val - }), - &BuiltInInstruction::GetArgExecute => - try_or_fail!(self, { - let val = self.try_get_arg(); - self.p = self.cp; - val - }), - &BuiltInInstruction::GetCurrentBlock => { - let c = Constant::Usize(self.block); - let addr = self[temp_v!(1)].clone(); - - self.write_constant_to_var(addr, c); - self.p += 1; - }, - &BuiltInInstruction::EraseBall => { - self.ball.0 = 0; - self.ball.1.truncate(0); - self.p += 1; - }, - &BuiltInInstruction::GetBall => { - let addr = self.store(self.deref(self[temp_v!(1)].clone())); - let h = self.heap.h; - - if self.ball.1.len() > 0 { - self.copy_and_align_ball_to_heap(); - } else { - self.fail = true; - return; - } - - let ball = self.heap[h].as_addr(h); - - match addr.as_var() { - Some(r) => { - self.bind(r, ball); - self.p += 1; - }, - _ => self.fail = true - }; - }, - &BuiltInInstruction::GetCutPoint(r) => { - let c = Constant::Usize(self.b); - self[r] = Addr::Con(c); - - self.p += 1; - }, - &BuiltInInstruction::SetBall => { - let addr = self[temp_v!(1)].clone(); - self.ball.0 = self.heap.h; - - { - let mut duplicator = DuplicateBallTerm::new(self); - duplicator.duplicate_term(addr); - } - - self.p += 1; - }, - &BuiltInInstruction::SetCutPoint(r) => { - let addr = self.store(self.deref(self[r].clone())); - - match addr { - Addr::Con(Constant::Usize(nb)) => { - if self.b > nb { - self.b = nb; - self.tidy_trail(); - } - - self.p += 1; - }, - _ => self.fail = true - }; - }, - &BuiltInInstruction::CleanUpBlock => { - let nb = self.store(self.deref(self[temp_v!(1)].clone())); - - match nb { - Addr::Con(Constant::Usize(nb)) => { - let b = self.b - 1; - - if nb > 0 && self.or_stack[b].b == nb { - self.b = self.or_stack[nb - 1].b; - self.or_stack.truncate(self.b); - } - - self.p += 1; - }, - _ => self.fail = true - }; - }, - &BuiltInInstruction::InstallNewBlock => { - self.block = self.b; - let c = Constant::Usize(self.block); - let addr = self[temp_v!(1)].clone(); - - self.write_constant_to_var(addr, c); - self.p += 1; - }, - &BuiltInInstruction::ResetBlock => { - let addr = self.deref(self[temp_v!(1)].clone()); - - match self.store(addr) { - Addr::Con(Constant::Usize(b)) => { - self.block = b; - self.p += 1; - }, - _ => self.fail = true - }; - }, - &BuiltInInstruction::UnwindStack => { - self.b = self.block; - self.or_stack.truncate(self.b); - - self.fail = true; - }, - &BuiltInInstruction::IsAtomic(r) => { - let d = self.store(self.deref(self[r].clone())); - - match d { - Addr::Con(_) => self.p += 1, - _ => self.fail = true - }; - }, - &BuiltInInstruction::IsInteger(r) => { - let d = self.store(self.deref(self[r].clone())); - - match d { - Addr::Con(Constant::Number(Number::Integer(_))) => self.p += 1, - _ => self.fail = true - }; - }, - &BuiltInInstruction::IsVar(r) => { - let d = self.store(self.deref(self[r].clone())); - - match d { - Addr::HeapCell(_) | Addr::StackCell(_,_) => self.p += 1, - _ => self.fail = true - }; - }, - &BuiltInInstruction::InternalCallN => - self.handle_internal_call_n(code_dir), - &BuiltInInstruction::Fail => { - self.fail = true; - self.p += 1; - }, - &BuiltInInstruction::Succeed => { - self.p += 1; - }, - &BuiltInInstruction::Unify => { - let a1 = self[temp_v!(1)].clone(); - let a2 = self[temp_v!(2)].clone(); - - self.unify(a1, a2); - self.p += 1; - } - }; - } - - fn try_functor(&mut self) -> Result<(), Vec> { - let a1 = self.store(self.deref(self[temp_v!(1)].clone())); - - match a1.clone() { - Addr::Str(o) => - match self.heap[o].clone() { - HeapCellValue::NamedStr(arity, name, _) => { - let name = Addr::Con(Constant::Atom(name)); // A2 - let arity = Addr::Con(Constant::Number(rc_integer!(arity))); - - let a2 = self[temp_v!(2)].clone(); - self.unify(a2, name); - - if !self.fail { - let a3 = self[temp_v!(3)].clone(); - self.unify(a3, arity); - } - }, - _ => self.fail = true - }, - 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 let Addr::Con(Constant::Atom(name)) = name { - if let Addr::Con(Constant::Number(Number::Integer(arity))) = arity { - let f_a = Addr::Str(self.heap.h); - let arity = match arity.to_usize() { - Some(arity) => arity, - None => { - self.fail = true; - return Ok(()); - } - }; - - if arity > 0 { - self.heap.push(HeapCellValue::NamedStr(arity, name, None)); - } else { - let c = Constant::Atom(name.clone()); - self.heap.push(HeapCellValue::Addr(Addr::Con(c))); - } - - for _ in 0 .. arity { - let h = self.heap.h; - self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h))); - } - - self.unify(a1, f_a); - } else { - return Err(functor!(self.atom_tbl, "instantiation_error", 0, [])); - } - } else { - return Err(functor!(self.atom_tbl, "instantiation_error", 0, [])); - } - }, - _ => { - let a2 = self[temp_v!(2)].clone(); - self.unify(a1, a2); - - if !self.fail { - let a3 = self[temp_v!(3)].clone(); - self.unify(a3, Addr::Con(Constant::Number(rc_integer!(0)))); - } - } - }; - - Ok(()) - } - - fn duplicate_term(&mut self) { - let old_h = self.heap.h; - - let a1 = self[temp_v!(1)].clone(); - let a2 = self[temp_v!(2)].clone(); - - // drop the mutable references contained in gadget - // once the term has been duplicated. - { - let mut gadget = DuplicateTerm::new(self); - gadget.duplicate_term(a1); - } - - self.unify(Addr::HeapCell(old_h), a2); - } - - pub(super) fn execute_ctrl_instr(&mut self, code_dir: &CodeDir, instr: &ControlInstruction) - { - match instr { - &ControlInstruction::Allocate(num_cells) => { - let gi = self.next_global_index(); - - self.p += 1; - - if self.e + 1 < self.and_stack.len() { - let and_gi = self.and_stack[self.e].global_index; - let or_gi = self.or_stack.top() - .map(|or_fr| or_fr.global_index) - .unwrap_or(0); - - if and_gi > or_gi { - let index = self.e + 1; - - self.and_stack[index].e = self.e; - self.and_stack[index].cp = self.cp; - self.and_stack[index].global_index = gi; - - self.and_stack.resize(index, num_cells); - - self.e = index; - - return; - } - } - - self.and_stack.push(gi, self.e, self.cp, num_cells); - self.e = self.and_stack.len() - 1; - }, - &ControlInstruction::ArgCall => { - self.cp = self.p + 1; - self.num_of_args = 3; - self.b0 = self.b; - self.p = CodePtr::DirEntry(150); - }, - &ControlInstruction::ArgExecute => { - self.num_of_args = 3; - self.b0 = self.b; - self.p = CodePtr::DirEntry(150); - }, - &ControlInstruction::Call(ref name, arity, _) => - self.try_call_predicate(code_dir, name.clone(), arity), - &ControlInstruction::CatchCall => { - self.cp = self.p + 1; - self.num_of_args = 3; - self.b0 = self.b; - self.p = CodePtr::DirEntry(5); - }, - &ControlInstruction::CatchExecute => { - self.num_of_args = 3; - self.b0 = self.b; - self.p = CodePtr::DirEntry(5); - }, - &ControlInstruction::CallN(arity) => - if let Some((name, arity)) = self.setup_call_n(arity) { - self.try_call_predicate(code_dir, name, arity); - }, - &ControlInstruction::Deallocate => { - let e = self.e; - - self.cp = self.and_stack[e].cp; - self.e = self.and_stack[e].e; - - self.p += 1; - }, - &ControlInstruction::DisplayCall => { - let output = self.print_term(self[temp_v!(1)].clone(), - DisplayFormatter {}, - PrinterOutputter::new()); - - println!("{}", output.result()); - - self.p += 1; - }, - &ControlInstruction::DisplayExecute => { - let output = self.print_term(self[temp_v!(1)].clone(), - DisplayFormatter {}, - PrinterOutputter::new()); - - println!("{}", output.result()); - - self.p = self.cp; - }, - &ControlInstruction::DuplicateTermCall => { - self.duplicate_term(); - self.p += 1; - }, - &ControlInstruction::DuplicateTermExecute => { - self.duplicate_term(); - self.p = self.cp; - }, - &ControlInstruction::Execute(ref name, arity) => - self.try_execute_predicate(code_dir, name.clone(), arity), - &ControlInstruction::ExecuteN(arity) => - if let Some((name, arity)) = self.setup_call_n(arity) { - self.try_execute_predicate(code_dir, name, arity); - }, - &ControlInstruction::FunctorCall => - try_or_fail!(self, { - let val = self.try_functor(); - self.p += 1; - val - }), - &ControlInstruction::FunctorExecute => - try_or_fail!(self, { - let val = self.try_functor(); - self.p = self.cp; - val - }), - &ControlInstruction::GotoCall(p, arity) => { - self.cp = self.p + 1; - self.num_of_args = arity; - self.b0 = self.b; - self.p = CodePtr::DirEntry(p); - }, - &ControlInstruction::GotoExecute(p, arity) => { - self.num_of_args = arity; - self.b0 = self.b; - self.p = CodePtr::DirEntry(p); - }, - &ControlInstruction::IsCall(r, ref at) => { - let a1 = self[r].clone(); - let a2 = try_or_fail!(self, self.get_number(at)); - - self.unify(a1, Addr::Con(Constant::Number(a2))); - self.p += 1; - }, - &ControlInstruction::IsExecute(r, ref at) => { - let a1 = self[r].clone(); - let a2 = try_or_fail!(self, self.get_number(at)); - - self.unify(a1, Addr::Con(Constant::Number(a2))); - self.p = self.cp; - }, - &ControlInstruction::JmpByCall(arity, offset) => { - self.cp = self.p + 1; - self.num_of_args = arity; - self.b0 = self.b; - self.p += offset; - }, - &ControlInstruction::JmpByExecute(arity, offset) => { - self.num_of_args = arity; - self.b0 = self.b; - self.p += offset; - }, - &ControlInstruction::Proceed => - self.p = self.cp, - &ControlInstruction::ThrowCall => { - self.cp = self.p + 1; - self.goto_throw(); - }, - &ControlInstruction::ThrowExecute => { - self.goto_throw(); - }, - }; - } - - pub(super) fn execute_indexed_choice_instr(&mut self, instr: &IndexedChoiceInstruction) - { - match instr { - &IndexedChoiceInstruction::Try(l) => { - let n = self.num_of_args; - let gi = self.next_global_index(); - - self.or_stack.push(gi, - self.e, - self.cp, - self.b, - self.p + 1, - self.tr, - self.heap.h, - self.b0, - self.num_of_args); - - self.b = self.or_stack.len(); - let b = self.b - 1; - - for i in 1 .. n + 1 { - self.or_stack[b][i] = self.registers[i].clone(); - } - - self.hb = self.heap.h; - self.p += l; - }, - &IndexedChoiceInstruction::Retry(l) => { - let b = self.b - 1; - let n = self.or_stack[b].num_args(); - - for i in 1 .. n + 1 { - self.registers[i] = self.or_stack[b][i].clone(); - } - - self.e = self.or_stack[b].e; - self.cp = self.or_stack[b].cp; - - self.or_stack[b].bp = self.p + 1; - - let old_tr = self.or_stack[b].tr; - let curr_tr = self.tr; - - self.unwind_trail(old_tr, curr_tr); - self.tr = self.or_stack[b].tr; - - self.trail.truncate(self.tr); - self.heap.truncate(self.or_stack[b].h); - - self.hb = self.heap.h; - - self.p += l; - }, - &IndexedChoiceInstruction::Trust(l) => { - let b = self.b - 1; - let n = self.or_stack[b].num_args(); - - for i in 1 .. n + 1 { - self.registers[i] = self.or_stack[b][i].clone(); - } - - self.e = self.or_stack[b].e; - self.cp = self.or_stack[b].cp; - - let old_tr = self.or_stack[b].tr; - let curr_tr = self.tr; - - self.unwind_trail(old_tr, curr_tr); - - self.tr = self.or_stack[b].tr; - self.trail.truncate(self.tr); - - self.heap.truncate(self.or_stack[b].h); - self.b = self.or_stack[b].b; - - self.or_stack.truncate(self.b); - - self.hb = self.heap.h; - self.p += l; - }, - }; - } - - pub(super) fn execute_choice_instr(&mut self, instr: &ChoiceInstruction) - { - match instr { - &ChoiceInstruction::TryMeElse(offset) => { - let n = self.num_of_args; - let gi = self.next_global_index(); - - self.or_stack.push(gi, - self.e, - self.cp, - self.b, - self.p + offset, - self.tr, - self.heap.h, - self.b0, - self.num_of_args); - - self.b = self.or_stack.len(); - let b = self.b - 1; - - for i in 1 .. n + 1 { - self.or_stack[b][i] = self.registers[i].clone(); - } - - self.hb = self.heap.h; - self.p += 1; - }, - &ChoiceInstruction::RetryMeElse(offset) => { - let b = self.b - 1; - let n = self.or_stack[b].num_args(); - - for i in 1 .. n + 1 { - self.registers[i] = self.or_stack[b][i].clone(); - } - - self.e = self.or_stack[b].e; - self.cp = self.or_stack[b].cp; - - self.or_stack[b].bp = self.p + offset; - - let old_tr = self.or_stack[b].tr; - let curr_tr = self.tr; - - self.unwind_trail(old_tr, curr_tr); - self.tr = self.or_stack[b].tr; - - self.trail.truncate(self.tr); - self.heap.truncate(self.or_stack[b].h); - - self.hb = self.heap.h; - - self.p += 1; - }, - &ChoiceInstruction::TrustMe => { - let b = self.b - 1; - let n = self.or_stack[b].num_args(); - - for i in 1 .. n + 1 { - self.registers[i] = self.or_stack[b][i].clone(); - } - - self.e = self.or_stack[b].e; - self.cp = self.or_stack[b].cp; - - let old_tr = self.or_stack[b].tr; - let curr_tr = self.tr; - - self.unwind_trail(old_tr, curr_tr); - - self.tr = self.or_stack[b].tr; - self.trail.truncate(self.tr); - - self.heap.truncate(self.or_stack[b].h); - - self.b = self.or_stack[b].b; - - self.or_stack.truncate(self.b); - - self.hb = self.heap.h; - self.p += 1; - } - } - } - - pub(super) fn execute_cut_instr(&mut self, instr: &CutInstruction) { - match instr { - &CutInstruction::NeckCut => { - let b = self.b; - let b0 = self.b0; - - if b > b0 { - self.b = b0; - self.tidy_trail(); - } - - self.p += 1; - }, - &CutInstruction::GetLevel => { - let b0 = self.b0; - let e = self.e; - - self.and_stack[e].b0 = b0; - self.p += 1; - }, - &CutInstruction::Cut => { - let b = self.b; - let e = self.e; - let b0 = self.and_stack[e].b0; // STACK[E+2+1] - - if b > b0 { - self.b = b0; - self.tidy_trail(); - } - - self.p += 1; - } - } - } - - pub(super) fn reset(&mut self) { - self.hb = 0; - self.e = 0; - self.b = 0; - self.b0 = 0; - self.s = 0; - self.tr = 0; - self.p = CodePtr::default(); - self.cp = CodePtr::default(); - self.num_of_args = 0; - - self.fail = false; - self.trail.clear(); - self.heap.clear(); - self.mode = MachineMode::Write; - self.and_stack.clear(); - self.or_stack.clear(); - self.registers = vec![Addr::HeapCell(0); 64]; - self.block = 0; - self.ball = (0, Vec::new()); - } + pub(super) interms: Vec, // intermediate numbers. + pub(super) sgc_cps: Vec<(Addr, usize, usize)>, // locations of cleaners/cut points, prev. block } diff --git a/src/prolog/machine/machine_state_impl.rs b/src/prolog/machine/machine_state_impl.rs new file mode 100644 index 00000000..caea4b3d --- /dev/null +++ b/src/prolog/machine/machine_state_impl.rs @@ -0,0 +1,1827 @@ +use prolog::and_stack::*; +use prolog::ast::*; +use prolog::builtins::*; +use prolog::copier::*; +use prolog::heap_iter::*; +use prolog::heap_print::*; +use prolog::machine::machine_state::*; +use prolog::num::{Integer, ToPrimitive, Zero}; +use prolog::num::bigint::{BigInt, BigUint}; +use prolog::num::rational::Ratio; +use prolog::or_stack::*; +use prolog::tabled_rc::*; + +use std::cmp::max; +use std::rc::Rc; + +macro_rules! try_or_fail { + ($s:ident, $e:expr) => {{ + match $e { + Ok(val) => val, + Err(msg) => { + $s.throw_exception(msg); + return; + } + } + }} +} + +impl MachineState { + pub(super) fn new(atom_tbl: TabledData) -> MachineState { + MachineState { atom_tbl, + s: 0, + p: CodePtr::default(), + b: 0, + b0: 0, + e: 0, + num_of_args: 0, + cp: CodePtr::default(), + fail: false, + heap: Heap::with_capacity(256), + mode: MachineMode::Write, + and_stack: AndStack::new(), + or_stack: OrStack::new(), + registers: vec![Addr::HeapCell(0); 64], + trail: Vec::new(), + tr: 0, + hb: 0, + block: 0, + ball: (0, Vec::new()), + interms: vec![Number::default(); 256], + sgc_cps: vec![] + } + } + + fn next_global_index(&self) -> usize { + max(if self.and_stack.len() > 0 { self.and_stack[self.e].global_index } else { 0 }, + if self.b > 0 { self.or_stack[self.b - 1].global_index } else { 0 }) + 1 + } + + pub(crate) fn store(&self, a: Addr) -> Addr { + match a { + Addr::HeapCell(r) => self.heap[r].as_addr(r), + Addr::StackCell(fr, sc) => self.and_stack[fr][sc].clone(), + addr => addr + } + } + + pub(crate) fn deref(&self, mut a: Addr) -> Addr { + loop { + let value = self.store(a.clone()); + + if value.is_ref() && value != a { + a = value; + continue; + } + + return a; + }; + } + + fn bind(&mut self, r1: Ref, a2: Addr) { + let t2 = self.store(a2); + + match r1 { + Ref::StackCell(fr, sc) => + self.and_stack[fr][sc] = t2, + Ref::HeapCell(hc) => + self.heap[hc] = HeapCellValue::Addr(t2) + }; + + self.trail(r1); + } + + pub(super) fn print_term(&self, a: Addr, fmt: Fmt, output: Outputter) -> Outputter + where Fmt: HeapCellValueFormatter, Outputter: HeapCellValueOutputter + { + let iter = HeapCellPreOrderIterator::new(&self, a); + let printer = HeapCellPrinter::new(iter, fmt, output); + + printer.print() + } + + fn unify(&mut self, a1: Addr, a2: Addr) { + let mut pdl = vec![a1, a2]; + + 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 { + match (self.store(d1.clone()), self.store(d2.clone())) { + (Addr::HeapCell(hc), _) => + self.bind(Ref::HeapCell(hc), d2), + (_, Addr::HeapCell(hc)) => + self.bind(Ref::HeapCell(hc), 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::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(c1), Addr::Con(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 + }; + } + } + } + + fn trail(&mut self, r: Ref) { + match r { + Ref::HeapCell(hc) => { + if hc < self.hb { + self.trail.push(r); + self.tr += 1; + } + }, + Ref::StackCell(fr, _) => { + let fr_gi = self.and_stack[fr].global_index; + let b_gi = if !self.or_stack.is_empty() { + if self.b > 0 { + let b = self.b - 1; + self.or_stack[b].global_index + } else { + 0 + } + } else { + 0 + }; + + if fr_gi < b_gi { + self.trail.push(r); + self.tr += 1; + } + } + } + } + + fn unwind_trail(&mut self, a1: usize, a2: usize) { + for i in a1 .. a2 { + match self.trail[i] { + Ref::HeapCell(r) => + self.heap[r] = HeapCellValue::Addr(Addr::HeapCell(r)), + Ref::StackCell(fr, sc) => + self.and_stack[fr][sc] = Addr::StackCell(fr, sc) + } + } + } + + fn tidy_trail(&mut self) { + if self.b == 0 { + return; + } + + let b = self.b - 1; + let mut i = self.or_stack[b].tr; + + while i < self.tr { + let tr_i = self.trail[i]; + let hb = self.hb; + + match tr_i { + Ref::HeapCell(tr_i) => + if tr_i < hb { //|| ((h < tr_i) && tr_i < b) { + i += 1; + } else { + let tr = self.tr; + let val = self.trail[tr - 1]; + self.trail[i] = val; + }, + Ref::StackCell(fr, _) => { + let b = self.b - 1; + let fr_gi = self.and_stack[fr].global_index; + let b_gi = if !self.or_stack.is_empty() { + self.or_stack[b].global_index + } else { + 0 + }; + + if fr_gi < b_gi { + i += 1; + } else { + let tr = self.tr; + let val = self.trail[tr - 1]; + self.trail[i] = val; + } + } + }; + } + } + + fn write_constant_to_var(&mut self, addr: Addr, c: Constant) { + let addr = self.deref(addr); + + match self.store(addr) { + Addr::HeapCell(hc) => { + self.heap[hc] = HeapCellValue::Addr(Addr::Con(c.clone())); + self.trail(Ref::HeapCell(hc)); + }, + Addr::StackCell(fr, sc) => { + self.and_stack[fr][sc] = Addr::Con(c.clone()); + self.trail(Ref::StackCell(fr, sc)); + }, + Addr::Con(c1) => { + if c1 != c { + self.fail = true; + } + }, + _ => self.fail = true + }; + } + + fn get_number(&self, at: &ArithmeticTerm) -> Result> { + + match at { + &ArithmeticTerm::Reg(r) => { + let addr = self[r].clone(); + let item = self.store(self.deref(addr)); + + match item { + Addr::Con(Constant::Number(n)) => Ok(n), + _ => { + let atom_tbl = self.atom_tbl.clone(); + Err(functor!(self.atom_tbl, + "instantiation_error", + 1, + [heap_atom!("(is)/2", atom_tbl)])) + } + } + }, + &ArithmeticTerm::Interm(i) => Ok(self.interms[i-1].clone()), + &ArithmeticTerm::Number(ref n) => Ok(n.clone()), + } + } + + fn get_rational(&self, at: &ArithmeticTerm) -> Result>, Vec> { + let n = self.get_number(at)?; + + match n { + Number::Rational(r) => Ok(r), + Number::Float(fl) => + if let Some(r) = Ratio::from_float(fl.into_inner()) { + Ok(Rc::new(r)) + } else { + Err(functor!(self.atom_tbl, + "instantiation_error", + 1, + [heap_atom!("(is)/2", self.atom_tbl)])) + }, + Number::Integer(bi) => + Ok(Rc::new(Ratio::from_integer((*bi).clone()))) + } + } + + fn signed_bitwise_op(&self, n1: &BigInt, n2: &BigInt, f: Op) -> Rc + where Op: FnOnce(&BigUint, &BigUint) -> BigUint + { + let n1_b = n1.to_signed_bytes_le(); + let n2_b = n2.to_signed_bytes_le(); + + let u_n1 = BigUint::from_bytes_le(&n1_b); + let u_n2 = BigUint::from_bytes_le(&n2_b); + + Rc::new(BigInt::from_signed_bytes_le(&f(&u_n1, &u_n2).to_bytes_le())) + } + + fn arith_eval_by_metacall(&self, r: RegType) -> Result> + { + let instantiation_err = functor!(self.atom_tbl.clone(), "instantiation_error", 1, + [heap_atom!("(is)/2", self.atom_tbl.clone())]); + + let a = self[r].clone(); + + if let &Addr::Con(Constant::Number(ref n)) = &a { + return Ok(n.clone()); + } + + let mut interms: Vec = Vec::with_capacity(64); + + for heap_val in self.post_order_iter(a) { + match heap_val { + HeapCellValue::NamedStr(2, name, Some(Fixity::In)) => { + let a2 = interms.pop().unwrap(); + let a1 = interms.pop().unwrap(); + + match name.as_str() { + "+" => interms.push(a1 + a2), + "-" => interms.push(a1 - a2), + "*" => interms.push(a1 * a2), + "rdiv" => + match NumberPair::from(a1, a2) { + NumberPair::Rational(r1, r2) => + interms.push(Number::Rational(self.rdiv(r1, r2)?)), + _ => + return Err(instantiation_err) + }, + "//" => interms.push(Number::Integer(self.idiv(a1, a2)?)), + "div" => interms.push(Number::Integer(self.fidiv(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)?)), + _ => return Err(instantiation_err) + } + }, + HeapCellValue::NamedStr(1, name, Some(Fixity::Pre)) => { + let a1 = interms.pop().unwrap(); + + match name.as_str() { + "-" => interms.push(- a1), + _ => return Err(instantiation_err) + } + }, + HeapCellValue::Addr(Addr::Con(Constant::Number(n))) => + interms.push(n), + _ => + return Err(instantiation_err) + } + }; + + Ok(interms.pop().unwrap()) + } + + fn rdiv(&self, r1: Rc>, r2: Rc>) + -> Result>, Vec> + { + if *r2 == Ratio::zero() { + Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, + [heap_atom!("zero_divisor", self.atom_tbl.clone())])) + } else { + Ok(Rc::new(&*r1 / &*r2)) + } + } + + fn fidiv(&self, n1: Number, n2: Number) -> Result, Vec> + { + match (n1, n2) { + (Number::Integer(n1), Number::Integer(n2)) => + if *n2 == BigInt::zero() { + Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, + [heap_atom!("zero_divisor", self.atom_tbl.clone())])) + } else { + Ok(Rc::new(n1.div_floor(&n2))) + }, + _ => Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, + [heap_atom!("expected_integer_args", self.atom_tbl.clone())])) + } + } + + fn idiv(&self, n1: Number, n2: Number) -> Result, Vec> + { + match (n1, n2) { + (Number::Integer(n1), Number::Integer(n2)) => + if *n2 == BigInt::zero() { + Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, + [heap_atom!("zero_divisor", self.atom_tbl.clone())])) + } else { + Ok(Rc::new(&*n1 / &*n2)) + }, + _ => + Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, + [heap_atom!("expected_integer_args", self.atom_tbl.clone())])) + } + } + + fn div(&self, n1: Number, n2: Number) -> Result> + { + if n2.is_zero() { + Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, + [heap_atom!("zero_divisor", self.atom_tbl.clone())])) + } else { + Ok(n1 / n2) + } + } + + fn shr(&self, n1: Number, n2: Number) -> Result, Vec> + { + match (n1, n2) { + (Number::Integer(n1), Number::Integer(n2)) => + match n2.to_usize() { + Some(n2) => Ok(Rc::new(&*n1 >> n2)), + _ => Ok(Rc::new(&*n1 >> usize::max_value())) + }, + _ => + Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, + [heap_atom!("expected_integer_args", self.atom_tbl.clone())])) + } + } + + fn shl(&self, n1: Number, n2: Number) -> Result, Vec> + { + match (n1, n2) { + (Number::Integer(n1), Number::Integer(n2)) => + match n2.to_usize() { + Some(n2) => Ok(Rc::new(&*n1 << n2)), + _ => Ok(Rc::new(&*n1 << usize::max_value())) + }, + _ => + Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, + [heap_atom!("expected_integer_args", self.atom_tbl.clone())])) + } + } + + fn xor(&self, n1: Number, n2: Number) -> Result, Vec> + { + match (n1, n2) { + (Number::Integer(n1), Number::Integer(n2)) => + Ok(self.signed_bitwise_op(&*n1, &*n2, |u_n1, u_n2| u_n1 ^ u_n2)), + _ => + Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, + [heap_atom!("expected_integer_args", self.atom_tbl.clone())])) + } + } + + fn and(&self, n1: Number, n2: Number) -> Result, Vec> + { + match (n1, n2) { + (Number::Integer(n1), Number::Integer(n2)) => + Ok(self.signed_bitwise_op(&*n1, &*n2, |u_n1, u_n2| u_n1 & u_n2)), + _ => + Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, + [heap_atom!("expected_integer_args", self.atom_tbl.clone())])) + } + } + + fn modulus(&self, n1: Number, n2: Number) -> Result, Vec> + { + match (n1, n2) { + (Number::Integer(n1), Number::Integer(n2)) => + if *n2 == BigInt::zero() { + Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, + [heap_atom!("zero_divisor", self.atom_tbl.clone())])) + } else { + Ok(Rc::new(n1.mod_floor(&n2))) + }, + _ => + Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, + [heap_atom!("expected_integer_args", self.atom_tbl.clone())])) + } + } + + fn remainder(&self, n1: Number, n2: Number) -> Result, Vec> + { + match (n1, n2) { + (Number::Integer(n1), Number::Integer(n2)) => + if *n2 == BigInt::zero() { + Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, + [heap_atom!("zero_divisor", self.atom_tbl.clone())])) + } else { + Ok(Rc::new(&*n1 % &*n2)) + }, + _ => + Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, + [heap_atom!("expected_integer_args", self.atom_tbl.clone())])) + } + } + + fn or(&self, n1: Number, n2: Number) -> Result, Vec> + { + match (n1, n2) { + (Number::Integer(n1), Number::Integer(n2)) => + Ok(self.signed_bitwise_op(&*n1, &*n2, |u_n1, u_n2| u_n1 & u_n2)), + _ => + Err(functor!(self.atom_tbl.clone(), "evaluation_error", 1, + [heap_atom!("expected_integer_args", self.atom_tbl.clone())])) + } + } + + pub(super) fn execute_arith_instr(&mut self, instr: &ArithmeticInstruction) { + 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] = n1 + n2; + 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] = n1 - n2; + 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] = n1 * n2; + self.p += 1; + }, + &ArithmeticInstruction::RDiv(ref a1, ref a2, t) => { + let r1 = try_or_fail!(self, self.get_rational(a1)); + let r2 = try_or_fail!(self, self.get_rational(a2)); + + self.interms[t - 1] = Number::Rational(try_or_fail!(self, self.rdiv(r1, r2))); + self.p += 1; + }, + &ArithmeticInstruction::FIDiv(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.fidiv(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::Neg(ref a1, t) => { + let n1 = try_or_fail!(self, self.get_number(a1)); + + self.interms[t - 1] = - 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; + } + }; + } + + 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.deref(self[reg].clone()); + + match self.store(addr.clone()) { + Addr::HeapCell(hc) => { + let h = self.heap.h; + + self.heap.push(HeapCellValue::Addr(Addr::Lis(h+1))); + self.bind(Ref::HeapCell(hc), Addr::HeapCell(h)); + + self.mode = MachineMode::Write; + }, + Addr::StackCell(fr, sc) => { + let h = self.heap.h; + + self.heap.push(HeapCellValue::Addr(Addr::Lis(h+1))); + self.bind(Ref::StackCell(fr, sc), Addr::HeapCell(h)); + + self.mode = MachineMode::Write; + }, + Addr::Lis(a) => { + self.s = a; + self.mode = MachineMode::Read; + }, + _ => self.fail = true + }; + }, + &FactInstruction::GetStructure(_, ref name, arity, reg, fixity) => { + 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 str, _) = result { + if narity == arity && *name == *str { + self.s = a + 1; + self.mode = MachineMode::Read; + } else { + self.fail = true; + } + } + }, + Addr::HeapCell(_) | Addr::StackCell(_, _) => { + let h = self.heap.h; + + self.heap.push(HeapCellValue::Addr(Addr::Str(h + 1))); + self.heap.push(HeapCellValue::NamedStr(arity, name.clone(), fixity)); + + 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(_, _) => v, + Addr::Con(_) => c, + Addr::Lis(_) => l, + Addr::Str(_) => s + }; + + 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 name, arity, reg, fixity) => { + let h = self.heap.h; + + self.heap.push(HeapCellValue::NamedStr(arity, name.clone(), fixity)); + 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 { + self.heap.push(HeapCellValue::Addr(addr)); + 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))); + } + } + } + } + + fn try_call_predicate(&mut self, code_dir: &CodeDir, name: TabledRc, arity: usize) + { + let compiled_tl_index = code_dir.get(&(name, arity)).map(|index| index.1); + + match compiled_tl_index { + Some(compiled_tl_index) => { + self.cp = self.p + 1; + self.num_of_args = arity; + self.b0 = self.b; + self.p = CodePtr::DirEntry(compiled_tl_index); + }, + None => self.fail = true + }; + } + + fn try_execute_predicate(&mut self, code_dir: &CodeDir, name: TabledRc, arity: usize) + { + let compiled_tl_index = code_dir.get(&(name, arity)).map(|index| index.1); + + match compiled_tl_index { + Some(compiled_tl_index) => { + self.num_of_args = arity; + self.b0 = self.b; + self.p = CodePtr::DirEntry(compiled_tl_index); + }, + None => self.fail = true + }; + } + + fn handle_internal_call_n(&mut self, code_dir: &CodeDir) + { + let arity = self.num_of_args + 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; + + if let Some((name, arity)) = self.setup_call_n(arity - 1) { + self.try_execute_predicate(code_dir, name, arity); + } + } else { + self.fail = true; + } + } + + fn goto_throw(&mut self) { + self.num_of_args = 1; + self.b0 = self.b; + self.p = CodePtr::DirEntry(59); + } + + fn throw_exception(&mut self, hcv: Vec) { + let h = self.heap.h; + + self.registers[1] = Addr::HeapCell(h); + + self.heap.append(hcv); + self.goto_throw(); + } + + fn setup_call_n(&mut self, arity: usize) -> Option + { + 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 atom_tbl = self.atom_tbl.clone(); + self.throw_exception(functor!(atom_tbl, + "representation_error", + 1, + [heap_atom!("exceeds_max_arity", atom_tbl)])); + 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 atom_tbl = self.atom_tbl.clone(); + self.throw_exception(functor!(atom_tbl, "instantiation_error", 0, [])); + return None; + }, + _ => { + let atom_tbl = self.atom_tbl.clone(); + self.throw_exception(functor!(atom_tbl, + "type_error", + 2, + [heap_atom!("callable", atom_tbl), + HeapCellValue::Addr(addr)])); + return None; + } + }; + + Some((name, arity + narity - 1)) + } + + pub(super) fn copy_and_align_ball_to_heap(&mut self) { + let diff = self.ball.0 - self.heap.h; + + for heap_value in self.ball.1.iter().cloned() { + self.heap.push(match heap_value { + HeapCellValue::Addr(Addr::Con(c)) => + HeapCellValue::Addr(Addr::Con(c)), + HeapCellValue::Addr(Addr::Lis(a)) => + HeapCellValue::Addr(Addr::Lis(a - diff)), + HeapCellValue::Addr(Addr::HeapCell(hc)) => + HeapCellValue::Addr(Addr::HeapCell(hc - diff)), + HeapCellValue::Addr(Addr::Str(s)) => + HeapCellValue::Addr(Addr::Str(s - diff)), + _ => heap_value + }); + } + } + + fn try_get_arg(&mut self) -> Result<(), Vec> + { + let a1 = self.store(self.deref(self[temp_v!(1)].clone())); + + if let Addr::Con(Constant::Number(Number::Integer(i))) = a1 { + let a2 = self.store(self.deref(self[temp_v!(2)].clone())); + + if let Addr::Str(o) = a2 { + match self.heap[o].clone() { + HeapCellValue::NamedStr(arity, _, _) => + match i.to_usize() { + Some(i) if 1 <= i && i <= arity => { + let a3 = self[temp_v!(3)].clone(); + let h_a = Addr::HeapCell(o + i); + + self.unify(a3, h_a); + }, + _ => self.fail = true + }, + _ => self.fail = true + }; + } else { + return Err(functor!(self.atom_tbl, + "type_error", + 1, + [heap_atom!("compound_expected", self.atom_tbl)])); + } + } + + Ok(()) + } + + fn compare_numbers(&mut self, cmp: CompareNumberQT, n1: Number, n2: Number) { + self.fail = match cmp { + CompareNumberQT::GreaterThan if !(n1.gt(n2)) => true, + CompareNumberQT::GreaterThanOrEqual if !(n1.gte(n2)) => true, + CompareNumberQT::LessThan if !(n1.lt(n2)) => true, + CompareNumberQT::LessThanOrEqual if !(n1.lte(n2)) => true, + CompareNumberQT::NotEqual if !(n1.ne(n2)) => true, + CompareNumberQT::Equal if !(n1.eq(n2)) => true, + _ => false + }; + + self.p += 1; + } + + fn reset_block(&mut self, addr: Addr) { + // let addr = self.deref(self[temp_v!(1)].clone()); + + match self.store(addr) { + Addr::Con(Constant::Usize(b)) => { + self.block = b; + self.p += 1; + }, + _ => self.fail = true + }; + } + + pub(super) fn execute_built_in_instr(&mut self, code_dir: &CodeDir, instr: &BuiltInInstruction) + { + match instr { + &BuiltInInstruction::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); + }, + &BuiltInInstruction::DynamicCompareNumber(cmp) => { + let n1 = try_or_fail!(self, self.arith_eval_by_metacall(temp_v!(1))); + let n2 = try_or_fail!(self, self.arith_eval_by_metacall(temp_v!(2))); + + self.compare_numbers(cmp, n1, n2); + }, + &BuiltInInstruction::DynamicIs => { + let a = self[temp_v!(1)].clone(); + let result = try_or_fail!(self, self.arith_eval_by_metacall(temp_v!(2))); + + self.unify(a, Addr::Con(Constant::Number(result))); + self.p += 1; + }, + &BuiltInInstruction::GetArgCall => + try_or_fail!(self, { + let val = self.try_get_arg(); + self.p += 1; + val + }), + &BuiltInInstruction::GetArgExecute => + try_or_fail!(self, { + let val = self.try_get_arg(); + self.p = self.cp; + val + }), + &BuiltInInstruction::GetCurrentBlock => { + let c = Constant::Usize(self.block); + let addr = self[temp_v!(1)].clone(); + + self.write_constant_to_var(addr, c); + self.p += 1; + }, + &BuiltInInstruction::EraseBall => { + self.ball.0 = 0; + self.ball.1.truncate(0); + self.p += 1; + }, + &BuiltInInstruction::GetBall => { + let addr = self.store(self.deref(self[temp_v!(1)].clone())); + let h = self.heap.h; + + if self.ball.1.len() > 0 { + self.copy_and_align_ball_to_heap(); + } else { + self.fail = true; + return; + } + + let ball = self.heap[h].as_addr(h); + + match addr.as_var() { + Some(r) => { + self.bind(r, ball); + self.p += 1; + }, + _ => self.fail = true + }; + }, + &BuiltInInstruction::GetCutPoint(r) => { + let c = Constant::Usize(self.b); + self[r] = Addr::Con(c); + + self.p += 1; + }, + &BuiltInInstruction::InstallCleaner => { + let addr = self[temp_v!(1)].clone(); + let b = self.b; + let block = self.block; + + self.sgc_cps.push((addr, b, block)); + self.p += 1; + }, + &BuiltInInstruction::SetBall => { + let addr = self[temp_v!(1)].clone(); + self.ball.0 = self.heap.h; + + { + let mut duplicator = DuplicateBallTerm::new(self); + duplicator.duplicate_term(addr); + } + + self.p += 1; + }, + &BuiltInInstruction::SetCutPoint(r) => { + let addr = self.store(self.deref(self[r].clone())); + + match addr { + Addr::Con(Constant::Usize(nb)) => { + if self.b > nb { + self.b = nb; + self.tidy_trail(); + self.or_stack.truncate(self.b); + } + + self.p += 1; + }, + _ => self.fail = true + }; + }, + &BuiltInInstruction::CleanUpBlock => { + let nb = self.store(self.deref(self[temp_v!(1)].clone())); + + match nb { + Addr::Con(Constant::Usize(nb)) => { + let b = self.b - 1; + + if nb > 0 && self.or_stack[b].b == nb { + self.b = self.or_stack[nb - 1].b; + self.or_stack.truncate(self.b); + } + + self.p += 1; + }, + _ => self.fail = true + }; + }, + &BuiltInInstruction::InstallNewBlock => { + self.block = self.b; + let c = Constant::Usize(self.block); + let addr = self[temp_v!(1)].clone(); + + self.write_constant_to_var(addr, c); + self.p += 1; + }, + &BuiltInInstruction::ResetBlock => { + let addr = self.deref(self[temp_v!(1)].clone()); + self.reset_block(addr); + }, + &BuiltInInstruction::UnwindStack => { + self.b = self.block; + self.or_stack.truncate(self.b); + + self.fail = true; + }, + &BuiltInInstruction::IsAtomic(r) => { + let d = self.store(self.deref(self[r].clone())); + + match d { + Addr::Con(_) => self.p += 1, + _ => self.fail = true + }; + }, + &BuiltInInstruction::IsInteger(r) => { + let d = self.store(self.deref(self[r].clone())); + + match d { + Addr::Con(Constant::Number(Number::Integer(_))) => self.p += 1, + _ => self.fail = true + }; + }, + &BuiltInInstruction::IsVar(r) => { + let d = self.store(self.deref(self[r].clone())); + + match d { + Addr::HeapCell(_) | Addr::StackCell(_,_) => self.p += 1, + _ => self.fail = true + }; + }, + &BuiltInInstruction::InternalCallN => + self.handle_internal_call_n(code_dir), + &BuiltInInstruction::Fail => { + self.fail = true; + self.p += 1; + }, + &BuiltInInstruction::Succeed => { + self.p += 1; + }, + &BuiltInInstruction::Unify => { + let a1 = self[temp_v!(1)].clone(); + let a2 = self[temp_v!(2)].clone(); + + self.unify(a1, a2); + self.p += 1; + } + }; + } + + fn try_functor(&mut self) -> Result<(), Vec> { + let a1 = self.store(self.deref(self[temp_v!(1)].clone())); + + match a1.clone() { + Addr::Str(o) => + match self.heap[o].clone() { + HeapCellValue::NamedStr(arity, name, _) => { + let name = Addr::Con(Constant::Atom(name)); // A2 + let arity = Addr::Con(Constant::Number(rc_integer!(arity))); + + let a2 = self[temp_v!(2)].clone(); + self.unify(a2, name); + + if !self.fail { + let a3 = self[temp_v!(3)].clone(); + self.unify(a3, arity); + } + }, + _ => self.fail = true + }, + 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 let Addr::Con(Constant::Atom(name)) = name { + if let Addr::Con(Constant::Number(Number::Integer(arity))) = arity { + let f_a = Addr::Str(self.heap.h); + let arity = match arity.to_usize() { + Some(arity) => arity, + None => { + self.fail = true; + return Ok(()); + } + }; + + if arity > 0 { + self.heap.push(HeapCellValue::NamedStr(arity, name, None)); + } else { + let c = Constant::Atom(name.clone()); + self.heap.push(HeapCellValue::Addr(Addr::Con(c))); + } + + for _ in 0 .. arity { + let h = self.heap.h; + self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h))); + } + + self.unify(a1, f_a); + } else { + return Err(functor!(self.atom_tbl, "instantiation_error", 0, [])); + } + } else { + return Err(functor!(self.atom_tbl, "instantiation_error", 0, [])); + } + }, + _ => { + let a2 = self[temp_v!(2)].clone(); + self.unify(a1, a2); + + if !self.fail { + let a3 = self[temp_v!(3)].clone(); + self.unify(a3, Addr::Con(Constant::Number(rc_integer!(0)))); + } + } + }; + + Ok(()) + } + + fn duplicate_term(&mut self) { + let old_h = self.heap.h; + + let a1 = self[temp_v!(1)].clone(); + let a2 = self[temp_v!(2)].clone(); + + // drop the mutable references contained in gadget + // once the term has been duplicated. + { + let mut gadget = DuplicateTerm::new(self); + gadget.duplicate_term(a1); + } + + self.unify(Addr::HeapCell(old_h), a2); + } + + pub(super) fn execute_ctrl_instr(&mut self, code_dir: &CodeDir, instr: &ControlInstruction) + { + match instr { + &ControlInstruction::Allocate(num_cells) => { + let gi = self.next_global_index(); + + self.p += 1; + + if self.e + 1 < self.and_stack.len() { + let and_gi = self.and_stack[self.e].global_index; + let or_gi = self.or_stack.top() + .map(|or_fr| or_fr.global_index) + .unwrap_or(0); + + if and_gi > or_gi { + let index = self.e + 1; + + self.and_stack[index].e = self.e; + self.and_stack[index].cp = self.cp; + self.and_stack[index].global_index = gi; + + self.and_stack.resize(index, num_cells); + + self.e = index; + + return; + } + } + + self.and_stack.push(gi, self.e, self.cp, num_cells); + self.e = self.and_stack.len() - 1; + }, + &ControlInstruction::ArgCall => { + self.cp = self.p + 1; + self.num_of_args = 3; + self.b0 = self.b; + self.p = CodePtr::DirEntry(150); + }, + &ControlInstruction::ArgExecute => { + self.num_of_args = 3; + self.b0 = self.b; + self.p = CodePtr::DirEntry(150); + }, + &ControlInstruction::Call(ref name, arity, _) => + self.try_call_predicate(code_dir, name.clone(), arity), + &ControlInstruction::CatchCall => { + self.cp = self.p + 1; + self.num_of_args = 3; + self.b0 = self.b; + self.p = CodePtr::DirEntry(5); + }, + &ControlInstruction::CatchExecute => { + self.num_of_args = 3; + self.b0 = self.b; + self.p = CodePtr::DirEntry(5); + }, + &ControlInstruction::CallN(arity) => + if let Some((name, arity)) = self.setup_call_n(arity) { + self.try_call_predicate(code_dir, name, arity); + }, + &ControlInstruction::CheckCpExecute => { + let a = self.store(self.deref(self[temp_v!(2)].clone())); + + match a { + Addr::Con(Constant::Usize(old_b)) if self.b > old_b + 1 => { + self.p = self.cp; + }, + _ => { + self.num_of_args = 2; + self.b0 = self.b; + self.p = CodePtr::DirEntry(361); // goto sgc_on_success/2, 361. + } + }; + }, + &ControlInstruction::Deallocate => { + let e = self.e; + + self.cp = self.and_stack[e].cp; + self.e = self.and_stack[e].e; + + self.p += 1; + }, + &ControlInstruction::DisplayCall => { + let output = self.print_term(self[temp_v!(1)].clone(), + DisplayFormatter {}, + PrinterOutputter::new()); + + println!("{}", output.result()); + + self.p += 1; + }, + &ControlInstruction::DisplayExecute => { + let output = self.print_term(self[temp_v!(1)].clone(), + DisplayFormatter {}, + PrinterOutputter::new()); + + println!("{}", output.result()); + + self.p = self.cp; + }, + &ControlInstruction::DuplicateTermCall => { + self.duplicate_term(); + self.p += 1; + }, + &ControlInstruction::DuplicateTermExecute => { + self.duplicate_term(); + self.p = self.cp; + }, + &ControlInstruction::Execute(ref name, arity) => + self.try_execute_predicate(code_dir, name.clone(), arity), + &ControlInstruction::ExecuteN(arity) => + if let Some((name, arity)) = self.setup_call_n(arity) { + self.try_execute_predicate(code_dir, name, arity); + }, + &ControlInstruction::FunctorCall => + try_or_fail!(self, { + let val = self.try_functor(); + self.p += 1; + val + }), + &ControlInstruction::FunctorExecute => + try_or_fail!(self, { + let val = self.try_functor(); + self.p = self.cp; + val + }), + &ControlInstruction::GetCleanerCall => { + let dest = self[temp_v!(1)].clone(); + + if let Some((cleaner_addr, b_cutoff, prev_block)) = self.sgc_cps.pop() { + self.p += 1; + + if self.b <= b_cutoff + 1 { + self.block = prev_block; + + if let Some(r) = dest.as_var() { + self.bind(r, cleaner_addr); + return; + } + } else { + self.sgc_cps.push((cleaner_addr, b_cutoff, prev_block)); + } + } + + self.fail = true; + }, + &ControlInstruction::GotoCall(p, arity) => { + self.cp = self.p + 1; + self.num_of_args = arity; + self.b0 = self.b; + self.p = CodePtr::DirEntry(p); + }, + &ControlInstruction::GotoExecute(p, arity) => { + self.num_of_args = arity; + self.b0 = self.b; + self.p = CodePtr::DirEntry(p); + }, + &ControlInstruction::IsCall(r, ref at) => { + let a1 = self[r].clone(); + let a2 = try_or_fail!(self, self.get_number(at)); + + self.unify(a1, Addr::Con(Constant::Number(a2))); + self.p += 1; + }, + &ControlInstruction::IsExecute(r, ref at) => { + let a1 = self[r].clone(); + let a2 = try_or_fail!(self, self.get_number(at)); + + self.unify(a1, Addr::Con(Constant::Number(a2))); + self.p = self.cp; + }, + &ControlInstruction::JmpByCall(arity, offset) => { + self.cp = self.p + 1; + self.num_of_args = arity; + self.b0 = self.b; + self.p += offset; + }, + &ControlInstruction::JmpByExecute(arity, offset) => { + self.num_of_args = arity; + self.b0 = self.b; + self.p += offset; + }, + &ControlInstruction::Proceed => + self.p = self.cp, + &ControlInstruction::ThrowCall => { + self.cp = self.p + 1; + self.goto_throw(); + }, + &ControlInstruction::ThrowExecute => { + self.goto_throw(); + }, + }; + } + + pub(super) fn execute_indexed_choice_instr(&mut self, instr: &IndexedChoiceInstruction) + { + match instr { + &IndexedChoiceInstruction::Try(l) => { + let n = self.num_of_args; + let gi = self.next_global_index(); + + self.or_stack.push(gi, + self.e, + self.cp, + self.b, + self.p + 1, + self.tr, + self.heap.h, + self.b0, + self.num_of_args); + + self.b = self.or_stack.len(); + let b = self.b - 1; + + for i in 1 .. n + 1 { + self.or_stack[b][i] = self.registers[i].clone(); + } + + self.hb = self.heap.h; + self.p += l; + }, + &IndexedChoiceInstruction::Retry(l) => { + let b = self.b - 1; + let n = self.or_stack[b].num_args(); + + for i in 1 .. n + 1 { + self.registers[i] = self.or_stack[b][i].clone(); + } + + self.e = self.or_stack[b].e; + self.cp = self.or_stack[b].cp; + + self.or_stack[b].bp = self.p + 1; + + let old_tr = self.or_stack[b].tr; + let curr_tr = self.tr; + + self.unwind_trail(old_tr, curr_tr); + self.tr = self.or_stack[b].tr; + + self.trail.truncate(self.tr); + self.heap.truncate(self.or_stack[b].h); + + self.hb = self.heap.h; + + self.p += l; + }, + &IndexedChoiceInstruction::Trust(l) => { + let b = self.b - 1; + let n = self.or_stack[b].num_args(); + + for i in 1 .. n + 1 { + self.registers[i] = self.or_stack[b][i].clone(); + } + + self.e = self.or_stack[b].e; + self.cp = self.or_stack[b].cp; + + let old_tr = self.or_stack[b].tr; + let curr_tr = self.tr; + + self.unwind_trail(old_tr, curr_tr); + + self.tr = self.or_stack[b].tr; + self.trail.truncate(self.tr); + + self.heap.truncate(self.or_stack[b].h); + self.b = self.or_stack[b].b; + + self.or_stack.truncate(self.b); + + self.hb = self.heap.h; + self.p += l; + }, + }; + } + + pub(super) fn execute_choice_instr(&mut self, instr: &ChoiceInstruction) + { + match instr { + &ChoiceInstruction::TryMeElse(offset) => { + let n = self.num_of_args; + let gi = self.next_global_index(); + + self.or_stack.push(gi, + self.e, + self.cp, + self.b, + self.p + offset, + self.tr, + self.heap.h, + self.b0, + self.num_of_args); + + self.b = self.or_stack.len(); + let b = self.b - 1; + + for i in 1 .. n + 1 { + self.or_stack[b][i] = self.registers[i].clone(); + } + + self.hb = self.heap.h; + self.p += 1; + }, + &ChoiceInstruction::RetryMeElse(offset) => { + let b = self.b - 1; + let n = self.or_stack[b].num_args(); + + for i in 1 .. n + 1 { + self.registers[i] = self.or_stack[b][i].clone(); + } + + self.e = self.or_stack[b].e; + self.cp = self.or_stack[b].cp; + + self.or_stack[b].bp = self.p + offset; + + let old_tr = self.or_stack[b].tr; + let curr_tr = self.tr; + + self.unwind_trail(old_tr, curr_tr); + self.tr = self.or_stack[b].tr; + + self.trail.truncate(self.tr); + self.heap.truncate(self.or_stack[b].h); + + self.hb = self.heap.h; + + self.p += 1; + }, + &ChoiceInstruction::TrustMe => { + let b = self.b - 1; + let n = self.or_stack[b].num_args(); + + for i in 1 .. n + 1 { + self.registers[i] = self.or_stack[b][i].clone(); + } + + self.e = self.or_stack[b].e; + self.cp = self.or_stack[b].cp; + + let old_tr = self.or_stack[b].tr; + let curr_tr = self.tr; + + self.unwind_trail(old_tr, curr_tr); + + self.tr = self.or_stack[b].tr; + self.trail.truncate(self.tr); + + self.heap.truncate(self.or_stack[b].h); + + self.b = self.or_stack[b].b; + + self.or_stack.truncate(self.b); + + self.hb = self.heap.h; + self.p += 1; + } + } + } + + pub(super) fn execute_cut_instr(&mut self, instr: &CutInstruction) { + match instr { + &CutInstruction::NeckCut => { + let b = self.b; + let b0 = self.b0; + + if b > b0 { + self.b = b0; + self.tidy_trail(); + self.or_stack.truncate(self.b); + } + + self.p += 1; + }, + &CutInstruction::GetLevel(r) => { + let b0 = self.b0; + + self[r] = Addr::Con(Constant::Usize(b0)); + self.p += 1; + }, + &CutInstruction::Cut(r) => { + let b = self.b; + + if let Addr::Con(Constant::Usize(b0)) = self[r].clone() { + if b > b0 { + self.b = b0; + self.tidy_trail(); + self.or_stack.truncate(self.b); + } + } else { + self.fail = true; + return; + } + + self.p += 1; + + if !self.sgc_cps.is_empty() { + self.cp = self.p; + self.num_of_args = 0; + self.b0 = self.b; + self.p = CodePtr::DirEntry(349); // goto_call run_cleaners_without_handling/0. + } + } + } + } + + pub(super) fn reset(&mut self) { + self.hb = 0; + self.e = 0; + self.b = 0; + self.b0 = 0; + self.s = 0; + self.tr = 0; + self.p = CodePtr::default(); + self.cp = CodePtr::default(); + self.num_of_args = 0; + + self.fail = false; + self.trail.clear(); + self.heap.clear(); + self.mode = MachineMode::Write; + self.and_stack.clear(); + self.or_stack.clear(); + self.registers = vec![Addr::HeapCell(0); 64]; + self.block = 0; + self.ball = (0, Vec::new()); + } +} diff --git a/src/prolog/machine/mod.rs b/src/prolog/machine/mod.rs index 11ff6cab..7b9a3bc7 100644 --- a/src/prolog/machine/mod.rs +++ b/src/prolog/machine/mod.rs @@ -6,6 +6,8 @@ use prolog::fixtures::*; use prolog::tabled_rc::*; pub(crate) mod machine_state; +#[macro_use] +mod machine_state_impl; use std::cell::RefCell; use std::collections::{HashMap, HashSet}; diff --git a/src/prolog/macros.rs b/src/prolog/macros.rs index 8219e59f..2195a245 100644 --- a/src/prolog/macros.rs +++ b/src/prolog/macros.rs @@ -199,8 +199,8 @@ macro_rules! proceed { } macro_rules! cut { - () => ( - Line::Cut(CutInstruction::Cut) + ($r:expr) => ( + Line::Cut(CutInstruction::Cut($r)) ) } @@ -301,8 +301,8 @@ macro_rules! duplicate_term { } macro_rules! get_level { - () => ( - Line::Cut(CutInstruction::GetLevel) + ($r:expr) => ( + Line::Cut(CutInstruction::GetLevel($r)) ) } @@ -490,6 +490,12 @@ macro_rules! jmp_call { ) } +macro_rules! jmp_execute { + ($arity:expr, $offset:expr) => ( + Line::Control(ControlInstruction::JmpByExecute($arity, $offset)) + ) +} + macro_rules! get_list { ($lvl:expr, $r:expr) => ( FactInstruction::GetList($lvl, $r) @@ -501,3 +507,21 @@ macro_rules! unify_constant { FactInstruction::UnifyConstant($c) ) } + +macro_rules! install_cleaner { + () => ( + Line::BuiltIn(BuiltInInstruction::InstallCleaner) + ) +} + +macro_rules! check_cp_execute { + () => ( + Line::Control(ControlInstruction::CheckCpExecute) + ) +} + +macro_rules! get_cleaner_call { + () => ( + Line::Control(ControlInstruction::GetCleanerCall) + ) +} diff --git a/src/prolog/parser b/src/prolog/parser index 2579afe5..5d8398b4 160000 --- a/src/prolog/parser +++ b/src/prolog/parser @@ -1 +1 @@ -Subproject commit 2579afe5fd8a843398e55fa458fbf83a819d7055 +Subproject commit 5d8398b4e68def750e23d422b9b762c07a333ea8 diff --git a/src/tests.rs b/src/tests.rs index 0c715e3e..bf0df94b 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1168,7 +1168,7 @@ fn test_queries_on_conditionals() submit(&mut wam, "test(X, [X]) :- (atomic(X) -> true ; throw(type_error(atomic_expected, X))). test(_, _)."); - + assert_prolog_success!(&mut wam, "?- catch(test(a, [a]), type_error(E), true).", [["E = _6"], ["E = _6"]]); @@ -1291,3 +1291,58 @@ fn test_queries_on_builtins() assert_prolog_success!(&mut wam, "?- duplicate_term(f(X), f(X)).", [["X = _1"]]); } + +#[test] +fn test_queries_on_setup_call_cleanup() +{ + let mut wam = Machine::new(); + + // Test examples from the ISO Prolog page for setup_call_catch. + assert_prolog_failure!(&mut wam, "?- setup_call_cleanup(false, _, _)."); + assert_prolog_success!(&mut wam, "?- catch(setup_call_cleanup(true, throw(unthrown), _), instantiation_error, true)."); + assert_prolog_success!(&mut wam, "?- setup_call_cleanup(true, true, (true ; throw(x)))."); + assert_prolog_success!(&mut wam, "?- setup_call_cleanup(true, X = 1, X = 2).", + [["X = 1"]]); + assert_prolog_success!(&mut wam, "?- setup_call_cleanup(true, true, X = 2).", + [["X = 2"]]); + assert_prolog_success!(&mut wam, "?- catch(setup_call_cleanup(true, X=true, X), E, true).", + [["E = instantiation_error", "X = _1"]]); + assert_prolog_success!(&mut wam, "?- catch(setup_call_cleanup(X=throw(ex), true, X), E, true).", + [["E = ex", "X = _3"]]); + assert_prolog_success!(&mut wam, "?- setup_call_cleanup(true, true, false)."); + assert_prolog_success!(&mut wam, "?- setup_call_cleanup(S = 1, G = 2, C = 3).", + [["S = 1", "G = 2", "C = 3"]]); + assert_prolog_success!(&mut wam, "?- setup_call_cleanup((S=1;S=2), G=3, C=4).", + [["S = 1", "G = 3", "C = 4"]]); + assert_prolog_success!(&mut wam, "?- setup_call_cleanup(S=1, G=2, display(S+G)).", + [["S = 1", "G = 2"]]); + assert_prolog_success!(&mut wam, "?- setup_call_cleanup(S=1, (G=2;G=3), display(S+G)).", + [["S = 1", "G = 2"], + ["S = 1", "G = 3"]]); + assert_prolog_success!(&mut wam, "?- setup_call_cleanup(S=1, G=2, display(S+G>A+B)), A=3, B=4.", + [["S = 1", "G = 2", "A = 3", "B = 4"]]); + assert_prolog_success!(&mut wam, + "?- catch(setup_call_cleanup(S=1, (G=2;G=3,throw(x)), display(S+G)), E, true).", + [["S = 1", "G = 2", "E = _26"], ["G = _4", "E = x", "S = _1"]]); + assert_prolog_success!(&mut wam, + "?- setup_call_cleanup(S=1, (G=2;G=3),display(S+G>B)), B=4, !.", + [["S = 1", "B = 4", "G = 2"]]); + assert_prolog_success!(&mut wam, + "?- setup_call_cleanup(S=1,G=2,display(S+G>B)),B=3,!.", + [["S = 1", "G = 2", "B = 3"]]); + assert_prolog_success!(&mut wam, + "?- setup_call_cleanup(S=1,(G=2;false),display(S+G>B)),B=3,!.", + [["S = 1", "G = 2", "B = 3"]]); + assert_prolog_success!(&mut wam, + "?- setup_call_cleanup(S=1,(G=2;S=2),display(S+G>B)), B=3, !.", + [["S = 1", "B = 3", "G = 2"]]); + assert_prolog_failure!(&mut wam, + "?- setup_call_cleanup(S=1,(G=2;G=3), display(S+G>B)), B=4, !, throw(x)."); + assert_prolog_success!(&mut wam, + "?- setup_call_cleanup(true, (X=1;X=2), display(a)), setup_call_cleanup(true,(Y=1;Y=2),display(b)), !.", + [["Y = 1", "X = 1"]]); + assert_prolog_success!(&mut wam, "?- catch(setup_call_cleanup(true,throw(goal),throw(cl)), Pat, true).", + [["Pat = goal"]]); + assert_prolog_success!(&mut wam, "?- catch(( setup_call_cleanup(true,(G=1;G=2),throw(cl)), throw(cont)), Pat, true).", + [["Pat = cont", "G = _1"]]); +} -- 2.54.0