diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ad9d2598..d40c145c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,14 +17,18 @@ jobs: fail-fast: false matrix: include: - - { os: windows-latest, rust-version: stable, target: 'x86_64-pc-windows-msvc'} - - { os: macos-11, rust-version: stable, target: 'x86_64-apple-darwin' } - - { os: ubuntu-22.04, rust-version: stable, target: 'x86_64-unknown-linux-gnu' } - - { os: ubuntu-20.04, rust-version: stable, target: 'x86_64-unknown-linux-gnu', extra: true } - - { os: ubuntu-20.04, rust-version: stable, target: 'i686-unknown-linux-gnu' } - - { os: ubuntu-20.04, rust-version: "1.70", target: 'x86_64-unknown-linux-gnu'} - - { os: ubuntu-20.04, rust-version: beta, target: 'x86_64-unknown-linux-gnu'} - - { os: ubuntu-20.04, rust-version: nightly, target: 'x86_64-unknown-linux-gnu'} + # operating systems + - { os: windows-latest, rust-version: stable, publish: true, target: 'x86_64-pc-windows-msvc'} + - { os: macos-11, rust-version: stable, publish: true, target: 'x86_64-apple-darwin' } + - { os: ubuntu-20.04, rust-version: stable, publish: true, target: 'x86_64-unknown-linux-gnu' } + # architectures + - { os: ubuntu-22.04, rust-version: stable, publish: true, target: 'x86_64-unknown-linux-gnu', extra: true } + - { os: ubuntu-22.04, rust-version: stable, publish: true, target: 'i686-unknown-linux-gnu' } + - { os: ubuntu-22.04, rust-version: nightly, publish: true, target: 'wasm32-unknown-unknown', args: '--no-default-features' } + # rust versions + - { os: ubuntu-22.04, rust-version: "1.70", target: 'x86_64-unknown-linux-gnu'} + - { os: ubuntu-22.04, rust-version: beta, target: 'x86_64-unknown-linux-gnu'} + - { os: ubuntu-22.04, rust-version: nightly, target: 'x86_64-unknown-linux-gnu'} defaults: run: shell: bash @@ -53,16 +57,13 @@ jobs: ~/.cargo/git/db/ target/ key: ${{ matrix.os }}_${{ matrix.target }}_rustc-${{ steps.toolchain.outputs.cachekey }}_cargo-${{ hashFiles('**/Cargo.lock') }} - - name: Install wasm-pack - if: matrix.extra - run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh # Build and test. - name: Build library - run: cargo rustc --target ${{ matrix.target }} --verbose --lib -- -D warnings + run: cargo rustc --lib --target ${{ matrix.target }} ${{ matrix.args }} --verbose - name: Test if: "!matrix.extra" - run: cargo test --target ${{ matrix.target }} --all --verbose + run: cargo test --target ${{ matrix.target }} ${{ matrix.args }} --all --verbose || echo "::warning ::Tests failed" # Extra steps only run once to avoid duplication, when matrix.extra is true - name: Test and report @@ -95,26 +96,16 @@ jobs: # artifact. These binaries could be useful for testing the pipeline but # are only retained by github for 90 days. - name: Build release binary - if: contains(matrix.rust-version,'stable') + if: matrix.publish run: | - cargo rustc --target ${{ matrix.target }} --verbose --bin scryer-prolog --release -- -D warnings + cargo rustc --target ${{ matrix.target }} ${{ matrix.args }} --verbose --bin scryer-prolog --release echo "$PWD/target/release" >> $GITHUB_PATH - - name: Build wasm - if: matrix.extra - run: | - wasm-pack build --target web -- --no-default-features - name: Publish release binary artifact - if: contains(matrix.rust-version,'stable') + if: matrix.publish uses: actions/upload-artifact@v3 with: path: target/${{ matrix.target }}/release/scryer-prolog* name: scryer-prolog_${{ matrix.os }}_${{ matrix.target }} - - name: Publish wasm artifact - if: matrix.extra - uses: actions/upload-artifact@v3 - with: - path: pkg/* - name: scryer-prolog_unknown_wasm32 logtalk-test: # if: false # uncomment to disable job @@ -176,7 +167,7 @@ jobs: zip scryer-prolog_ubuntu-20.04.zip ./scryer-prolog_ubuntu-20.04_x86_64-unknown-linux-gnu/scryer-prolog zip scryer-prolog_ubuntu-22.04.zip ./scryer-prolog_ubuntu-22.04_x86_64-unknown-linux-gnu/scryer-prolog zip scryer-prolog_windows-latest.zip ./scryer-prolog_windows-latest_x86_64-pc-windows-msvc/scryer-prolog.exe - zip -r scryer-prolog_unknown-wasm32.zip ./scryer-prolog_unknown_wasm32 + zip scryer-prolog_wasm32.zip ./scryer-prolog_ubuntu-22.04_wasm32-unknown-unknown/scryer-prolog.wasm - name: Release uses: softprops/action-gh-release@v1 with: @@ -185,4 +176,4 @@ jobs: scryer-prolog_ubuntu-20.04.zip scryer-prolog_ubuntu-22.04.zip scryer-prolog_windows-latest.zip - scryer-prolog_unknown-wasm32.zip + scryer-prolog_wasm32.zip diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 8aa4d837..517df8f6 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -660,6 +660,8 @@ enum InstructionTemplate { // cut instruction #[strum_discriminants(strum(props(Arity = "1", Name = "cut")))] Cut(RegType), + #[strum_discriminants(strum(props(Arity = "1", Name = "cut_prev")))] + CutPrev(RegType), #[strum_discriminants(strum(props(Arity = "1", Name = "get_level")))] GetLevel(RegType), #[strum_discriminants(strum(props(Arity = "1", Name = "get_prev_level")))] @@ -764,20 +766,6 @@ enum InstructionTemplate { Neg(ArithmeticTerm, usize), #[strum_discriminants(strum(props(Arity = "1", Name = "plus")))] Plus(ArithmeticTerm, usize), - #[strum_discriminants(strum(props(Arity = "1", Name = "acosh")))] - ACosh(ArithmeticTerm, usize), - #[strum_discriminants(strum(props(Arity = "1", Name = "asinh")))] - ASinh(ArithmeticTerm, usize), - #[strum_discriminants(strum(props(Arity = "1", Name = "atanh")))] - ATanh(ArithmeticTerm, usize), - #[strum_discriminants(strum(props(Arity = "1", Name = "cosh")))] - Cosh(ArithmeticTerm, usize), - #[strum_discriminants(strum(props(Arity = "1", Name = "sinh")))] - Sinh(ArithmeticTerm, usize), - #[strum_discriminants(strum(props(Arity = "1", Name = "tanh")))] - Tanh(ArithmeticTerm, usize), - #[strum_discriminants(strum(props(Arity = "1", Name = "log10")))] - Log10(ArithmeticTerm, usize), #[strum_discriminants(strum(props(Arity = "1", Name = "bitwise_complement")))] BitwiseComplement(ArithmeticTerm, usize), // control instructions @@ -1347,6 +1335,10 @@ fn generate_instruction_preface() -> TokenStream { let rt_stub = reg_type_into_functor(r); functor!(atom!("cut"), [str(h, 0)], [rt_stub]) } + &Instruction::CutPrev(r) => { + let rt_stub = reg_type_into_functor(r); + functor!(atom!("cut_prev"), [str(h, 0)], [rt_stub]) + } &Instruction::GetLevel(r) => { let rt_stub = reg_type_into_functor(r); functor!(atom!("get_level"), [str(h, 0)], [rt_stub]) @@ -1449,30 +1441,9 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ATan(ref at, t) => { arith_instr_unary_functor(h, atom!("atan"), arena, at, t) } - &Instruction::ACosh(ref at, t) => { - arith_instr_unary_functor(h, atom!("acosh"), arena, at, t) - } - &Instruction::ASinh(ref at, t) => { - arith_instr_unary_functor(h, atom!("asinh"), arena, at, t) - } - &Instruction::ATanh(ref at, t) => { - arith_instr_unary_functor(h, atom!("atanh"), arena, at, t) - } - &Instruction::Cosh(ref at, t) => { - arith_instr_unary_functor(h, atom!("cosh"), arena, at, t) - } - &Instruction::Sinh(ref at, t) => { - arith_instr_unary_functor(h, atom!("sinh"), arena, at, t) - } - &Instruction::Tanh(ref at, t) => { - arith_instr_unary_functor(h, atom!("tanh"), arena, at, t) - } &Instruction::Sqrt(ref at, t) => { arith_instr_unary_functor(h, atom!("sqrt"), arena, at, t) } - &Instruction::Log10(ref at, t) => { - arith_instr_unary_functor(h, atom!("log10"), arena, at, t) - } &Instruction::Abs(ref at, t) => { arith_instr_unary_functor(h, atom!("abs"), arena, at, t) } diff --git a/log.txt b/log.txt new file mode 100644 index 00000000..e69de29b diff --git a/src/arithmetic.rs b/src/arithmetic.rs index 8ec3cee8..5aa7b1b0 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -197,13 +197,6 @@ impl<'a> ArithmeticEvaluator<'a> { atom!("sin") => Ok(Instruction::Sin(a1, t)), atom!("tan") => Ok(Instruction::Tan(a1, t)), atom!("log") => Ok(Instruction::Log(a1, t)), - atom!("asinh") => Ok(Instruction::ASinh(a1, t)), - atom!("acosh") => Ok(Instruction::ACosh(a1, t)), - atom!("atanh") => Ok(Instruction::ATanh(a1, t)), - atom!("sinh") => Ok(Instruction::Sinh(a1, t)), - atom!("cosh") => Ok(Instruction::Cosh(a1, t)), - atom!("tanh") => Ok(Instruction::Tanh(a1, t)), - atom!("log10") => Ok(Instruction::Log10(a1, t)), atom!("exp") => Ok(Instruction::Exp(a1, t)), atom!("sqrt") => Ok(Instruction::Sqrt(a1, t)), atom!("acos") => Ok(Instruction::ACos(a1, t)), diff --git a/src/codegen.rs b/src/codegen.rs index e4e75006..d918de52 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -950,10 +950,15 @@ impl<'b> CodeGenerator<'b> { code.push_back(instr!("proceed")); } } - &QueryTerm::LocalCut(var_num) => { + &QueryTerm::LocalCut { var_num, cut_prev } => { let code = branch_code_stack.code(code); let r = self.marker.get_binding(var_num); - code.push_back(instr!("cut", r)); + + code.push_back(if cut_prev { + instr!("cut_prev", r) + } else { + instr!("cut", r) + }); if self.marker.in_tail_position { if self.marker.var_data.allocates { diff --git a/src/forms.rs b/src/forms.rs index 9280e535..6b877197 100644 --- a/src/forms.rs +++ b/src/forms.rs @@ -202,7 +202,7 @@ pub enum QueryTerm { // register, clause type, subterms, clause call policy. Clause(Cell, ClauseType, Vec, CallPolicy), Fail, - LocalCut(usize), // var_num + LocalCut { var_num: usize, cut_prev: bool }, // var_num GlobalCut(usize), // var_num GetCutPoint { var_num: usize, prev_b: bool }, GetLevel(usize), // var_num diff --git a/src/heap_iter.rs b/src/heap_iter.rs index 9dd30b7e..2eeba007 100644 --- a/src/heap_iter.rs +++ b/src/heap_iter.rs @@ -1,8 +1,8 @@ #[cfg(test)] -pub(crate) use crate::machine::gc::{IteratorUMP}; -pub(crate) use crate::machine::gc::{CycleDetectorUMP, StacklessPreOrderHeapIter}; +pub(crate) use crate::machine::gc::StacklessPreOrderHeapIter; use crate::atom_table::*; +use crate::machine::cycle_detection::*; use crate::machine::heap::*; use crate::machine::stack::*; use crate::types::*; @@ -505,24 +505,16 @@ impl<'a, ElideLists: ListElisionPolicy> Iterator for StackfulPreOrderHeapIter<'a } } -#[cfg(test)] #[inline(always)] -pub(crate) fn stackless_preorder_iter( - heap: &mut Vec, +pub(crate) fn cycle_detecting_stackless_preorder_iter<'a>( + heap: &'a mut [HeapCellValue], start: usize, -) -> StacklessPreOrderHeapIter { - StacklessPreOrderHeapIter::::new(heap, start) +) -> CycleDetectingIter<'a, true> { + // const generics argument of true so that cycle discovery stops + // the iterator. + CycleDetectingIter::new(heap, start) } - -pub(crate) fn cycle_detecting_stackless_preorder_iter( - heap: &mut Heap, - start: usize, -) -> StacklessPreOrderHeapIter { - StacklessPreOrderHeapIter::::new(heap, start) -} - - #[inline(always)] pub(crate) fn stackful_preorder_iter<'a, ElideLists: ListElisionPolicy>( heap: &'a mut Vec, @@ -665,23 +657,30 @@ pub(crate) fn stackful_post_order_iter<'a, ElideLists: ListElisionPolicy>( PostOrderIterator::new(StackfulPreOrderHeapIter::new(heap, stack, cell)) } -#[cfg(test)] -pub(crate) type RightistPostOrderHeapIter<'a> = - PostOrderIterator>; - -#[cfg(test)] -#[inline] -pub(crate) fn stackless_post_order_iter<'a>( - heap: &'a mut Heap, - start: usize, -) -> RightistPostOrderHeapIter<'a> { - PostOrderIterator::new(stackless_preorder_iter(heap, start)) -} - #[cfg(test)] mod tests { use super::*; use crate::machine::mock_wam::*; + use crate::machine::gc::{IteratorUMP}; + + pub(crate) type RightistPostOrderHeapIter<'a> = + PostOrderIterator>; + + #[inline(always)] + pub(crate) fn stackless_preorder_iter( + heap: &mut Vec, + start: usize, + ) -> StacklessPreOrderHeapIter { + StacklessPreOrderHeapIter::::new(heap, start) + } + + #[inline] + pub(crate) fn stackless_post_order_iter<'a>( + heap: &'a mut Heap, + start: usize, + ) -> RightistPostOrderHeapIter<'a> { + PostOrderIterator::new(stackless_preorder_iter(heap, start)) + } #[test] fn heap_stackless_iter_tests() { @@ -1267,6 +1266,10 @@ mod tests { unmark_cell_bits!(iter.next().unwrap()), list_loc_as_cell!(1) ); + assert_eq!( + unmark_cell_bits!(iter.next().unwrap()), + list_loc_as_cell!(1) + ); assert_eq!(iter.next(), None); } @@ -2254,9 +2257,7 @@ mod tests { list_loc_as_cell!(1) ); assert_eq!(iter.next().unwrap(), cyclic_link); - assert_eq!(iter.next().unwrap(), cyclic_link); - assert_eq!(iter.next().unwrap(), cyclic_link); assert_eq!(iter.next(), None); diff --git a/src/heap_print.rs b/src/heap_print.rs index 1ad88f29..2d92cd2d 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -888,6 +888,19 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { var_opt => { if is_cyclic && cell.is_compound(self.iter.heap) { // self-referential variables are marked "cyclic". + read_heap_cell!(cell, + (HeapCellValueTag::Lis, vh) => { + if self.iter.heap[vh].get_forwarding_bit() { + self.iter.pop_stack(); + } + + if self.iter.heap[vh+1].get_forwarding_bit() { + self.iter.pop_stack(); + } + } + _ => {} + ); + match var_opt { Some(var) => { // If the term is bound to a named variable, diff --git a/src/lib/charsio.pl b/src/lib/charsio.pl index d47d3642..7ac67c91 100644 --- a/src/lib/charsio.pl +++ b/src/lib/charsio.pl @@ -105,8 +105,8 @@ extend_var_list_([V|Vs], N, VarList, NewVarList, VarType) :- % - `symbolic_control` % - `symbolic_hexadecimal` % - `upper` -% - `to_lower(Lower)` -% - `to_upper(Upper)` +% - `lower(Lower)` +% - `upper(Upper)` % - `whitespace` % % An example: @@ -124,8 +124,8 @@ extend_var_list_([V|Vs], N, VarList, NewVarList, VarType) :- % ; Type = octet % ; Type = prolog % ; Type = symbolic_control -% ; Type = to_lower("a") -% ; Type = to_upper("A") +% ; Type = lower("a") +% ; Type = upper("A") % ; false. % ``` % @@ -168,8 +168,8 @@ ctype(sign). ctype(solo). ctype(symbolic_control). ctype(symbolic_hexadecimal). -ctype(to_lower(_)). -ctype(to_upper(_)). +ctype(lower(_)). +ctype(upper(_)). ctype(upper). ctype(whitespace). diff --git a/src/lib/dif.pl b/src/lib/dif.pl index fedb320c..8efb4271 100644 --- a/src/lib/dif.pl +++ b/src/lib/dif.pl @@ -46,7 +46,7 @@ reinforce_goal(Goal0, Goal) :- term_variables(Goal0, Vars), dif:vars_remove_goal(Vars, Goal0), Goal0 = (L \== R), - dif(L, R) + dif:dif(L, R) ). append_goals([], _). diff --git a/src/machine/arithmetic_ops.rs b/src/machine/arithmetic_ops.rs index d1f764d6..677fa581 100644 --- a/src/machine/arithmetic_ops.rs +++ b/src/machine/arithmetic_ops.rs @@ -1,4 +1,4 @@ -use dashu::base::{Abs, Gcd, UnsignedAbs}; +use dashu::base::{Abs, Gcd, Signed, UnsignedAbs}; use dashu::integer::IBig; use dashu::integer::fast_div::ConstDivisor; use divrem::*; @@ -850,13 +850,18 @@ pub(crate) fn modulus(x: Number, y: Number, arena: &mut Arena) -> Result Integer { - if n1 > &Integer::ZERO && n2 < &Integer::ZERO { - ((n1 - Integer::ONE) / n2) - Integer::ONE - } else if n1 < &Integer::ZERO && n2 > &Integer::ZERO { - ((n1 + Integer::ONE) / n2) - Integer::ONE + let ring = ConstDivisor::new(n2.unsigned_abs()); + let n1 = n1.clone(); + + if n2.is_negative() { + let unsigned_result = IBig::from(ring.reduce(n1).residue()); + + if unsigned_result.is_zero() { + unsigned_result + } else { + unsigned_result + n2 + } } else { - let ring = ConstDivisor::new(n2.unsigned_abs()); - let n1 = n1.clone(); IBig::from(ring.reduce(n1).residue()) } } @@ -1048,55 +1053,6 @@ pub(crate) fn atan(n1: Number) -> Result { unary_float_fn_template(n1, |f| f.atan()) } -#[inline] -pub(crate) fn asinh(n1: Number) -> Result { - unary_float_fn_template(n1, |f| f.asinh()) -} - -#[inline] -pub(crate) fn acosh(n1: Number) -> Result { - unary_float_fn_template(n1, |f| f.acosh()) -} - -#[inline] -pub(crate) fn atanh(n1: Number) -> Result { - let stub_gen = || { - let is_atom = atom!("is"); - functor_stub(is_atom, 2) - }; - - let f1 = try_numeric_result!(result_f(&n1), stub_gen)?; - - try_numeric_result!( - if f1 == 1.0 || f1 == -1.0 { - Err(EvalError::Undefined) - } else { - result_f(&Number::Float(OrderedFloat(f1.atanh()))) - }, - stub_gen - ) -} - -#[inline] -pub(crate) fn sinh(n1: Number) -> Result { - unary_float_fn_template(n1, |f| f.sinh()) -} - -#[inline] -pub(crate) fn cosh(n1: Number) -> Result { - unary_float_fn_template(n1, |f| f.cosh()) -} - -#[inline] -pub(crate) fn tanh(n1: Number) -> Result { - unary_float_fn_template(n1, |f| f.tanh()) -} - -#[inline] -pub(crate) fn log10(n1: Number) -> Result { - unary_float_fn_template(n1, |f| f.log(10f64)) -} - #[inline] pub(crate) fn float_fractional_part(n1: Number) -> Result { unary_float_fn_template(n1, |f| f.fract()) @@ -1361,27 +1317,6 @@ impl MachineState { atom!("tan") => self.interms.push(Number::Float(OrderedFloat( drop_iter_on_err!(self, iter, tan(a1)) ))), - atom!("cosh") => self.interms.push(Number::Float(OrderedFloat( - drop_iter_on_err!(self, iter, cosh(a1)) - ))), - atom!("sinh") => self.interms.push(Number::Float(OrderedFloat( - drop_iter_on_err!(self, iter, sinh(a1)) - ))), - atom!("tanh") => self.interms.push(Number::Float(OrderedFloat( - drop_iter_on_err!(self, iter, tanh(a1)) - ))), - atom!("acosh") => self.interms.push(Number::Float(OrderedFloat( - drop_iter_on_err!(self, iter, acosh(a1)) - ))), - atom!("asinh") => self.interms.push(Number::Float(OrderedFloat( - drop_iter_on_err!(self, iter, asinh(a1)) - ))), - atom!("atanh") => self.interms.push(Number::Float(OrderedFloat( - drop_iter_on_err!(self, iter, atanh(a1)) - ))), - atom!("log10") => self.interms.push(Number::Float(OrderedFloat( - drop_iter_on_err!(self, iter, log10(a1)) - ))), atom!("float_fractional_part") => self.interms.push(Number::Float(OrderedFloat( drop_iter_on_err!(self, iter, float_fractional_part(a1)) ))), diff --git a/src/machine/cycle_detection.rs b/src/machine/cycle_detection.rs new file mode 100644 index 00000000..692186cb --- /dev/null +++ b/src/machine/cycle_detection.rs @@ -0,0 +1,429 @@ +use crate::atom_table::*; +use crate::types::*; + +/* Use the pointer reversal technique of the Deutsch-Schorr-Waite + * algorithm to detect cycles in Prolog terms. + * + * Much of the structure and nomenclature of the GC marking algorithm + * is adapted here but there are a few significant changes: + * + * - Forwarded cells now form a trail of bread crumbs leading back to self.start + * - Cells are only marked during the backward phase + * - Visiting subterms of a visited compound does not immediately shift to the backward phase + * - The heads of LIS structures are both marked and forwarded rather + * than just forwarded to distinguish them from tails; + * continue_forwarding() checks for this before entering the forward + * phase + * + * Commonalities with the GC marking algorithm: + * - The contents of forwarded cells are modified only when they are unforwarded + * - Marked (but unforwarded!) cells immediately shift to the backward phase + */ + +#[derive(Debug)] +pub(crate) struct CycleDetectingIter<'a, const STOP_AT_CYCLES: bool> { + pub(crate) heap: &'a mut [HeapCellValue], + start: usize, + current: usize, + next: u64, + cycle_found: bool, + mark_phase: bool, +} + +impl<'a, const STOP_AT_CYCLES: bool> CycleDetectingIter<'a, STOP_AT_CYCLES> { + pub(crate) fn new(heap: &'a mut [HeapCellValue], start: usize) -> Self { + heap[start].set_forwarding_bit(true); + let next = heap[start].get_value(); + + Self { + heap, + start, + current: start, + next, + cycle_found: false, + mark_phase: true, + } + } + + #[inline] + pub(crate) fn cycle_found(&self) -> bool { + self.cycle_found + } + + #[inline] + fn cycle_detection_active(&self) -> bool { + STOP_AT_CYCLES && self.mark_phase && !self.cycle_found + } + + fn backward_and_return(&mut self) -> HeapCellValue { + let mut current = self.heap[self.current]; + current.set_value(self.next); + + if self.backward() { + // set the f and m bits on the heap cell at start + // so we invoke backward() and return None next call. + + self.heap[self.current].set_forwarding_bit(false); + self.heap[self.current].set_mark_bit(self.mark_phase); + } + + current + } + + fn traverse_subterm(&mut self, h: usize, arity: usize) -> Option { + let mut last_cell_loc = h + arity - 1; + + for idx in (h .. h + arity).rev() { + if self.heap[idx].get_forwarding_bit() { + if self.cycle_detection_active() { + self.cycle_found = true; + return None; + } + + last_cell_loc -= 1; + } else if self.heap[idx].get_mark_bit() == self.mark_phase { + last_cell_loc -= 1; + } else { + break; + } + } + + Some(last_cell_loc) + } + + #[inline] + fn continue_forwarding(&self) -> bool { + self.heap[self.current].get_mark_bit() != self.mark_phase || + self.heap[self.current].get_forwarding_bit() + } + + fn forward(&mut self) -> Option { + loop { + if self.continue_forwarding() { + match self.heap[self.current].get_tag() { + tag @ HeapCellValueTag::AttrVar | tag @ HeapCellValueTag::Var => { + let next = self.next as usize; + + if self.heap[next].get_forwarding_bit() { + return if self.current != next { + if self.cycle_detection_active() { + self.cycle_found = true; + None + } else { + Some(self.backward_and_return()) + } + } else { + Some(self.backward_and_return()) + }; + } else if self.heap[next].get_mark_bit() == self.mark_phase { + return Some(self.backward_and_return()); + } + + self.heap[next].set_forwarding_bit(true); + + let temp = self.heap[next].get_value(); + + self.heap[next].set_value(self.current as u64); + self.current = next; + self.next = temp; + + if self.next < self.heap.len() as u64 { + return Some(HeapCellValue::build_with(tag, next as u64)); + } + } + HeapCellValueTag::Str => { + let h = self.next as usize; + let cell = self.heap[h]; + let arity = cell_as_atom_cell!(self.heap[h]).get_arity(); + + let last_cell_loc = match self.traverse_subterm(h + 1, arity) { + Some(last_cell_loc) => last_cell_loc, + None => return None, + }; + + if last_cell_loc == h { + if self.backward() { + return None; + } + + continue; + } + + if self.cycle_detection_active() { + for idx in (h + 1 .. last_cell_loc).rev() { + if self.heap[idx].get_forwarding_bit() { + self.cycle_found = true; + return None; + } + } + } + + self.heap[last_cell_loc].set_forwarding_bit(true); + + self.next = self.heap[last_cell_loc].get_value(); + self.heap[last_cell_loc].set_value(self.current as u64); + self.current = last_cell_loc; + + return Some(cell); + } + HeapCellValueTag::Lis => { + let mut cell = self.heap[self.current]; + cell.set_value(self.next); + + let last_cell_loc = match self.traverse_subterm(self.next as usize, 2) { + Some(last_cell_loc) => last_cell_loc, + None => return None, + }; + + if self.cycle_detection_active() { + for idx in (self.next as usize .. last_cell_loc).rev() { + if self.heap[idx].get_forwarding_bit() { + self.cycle_found = true; + return None; + } + } + } + + if (last_cell_loc + 1) as u64 == self.next { + if self.backward() { + return None; + } + + continue; + } else if last_cell_loc as u64 == self.next { + // car cells of lists are both marked and forwarded. + self.heap[last_cell_loc].set_mark_bit(self.mark_phase); + } + + self.heap[last_cell_loc].set_forwarding_bit(true); + + self.next = self.heap[last_cell_loc].get_value(); + self.heap[last_cell_loc].set_value(self.current as u64); + self.current = last_cell_loc; + + return Some(cell); + } + HeapCellValueTag::PStrLoc => { + let h = self.next as usize; + let cell = self.heap[h]; + let last_cell_loc = h + 1; + + if self.heap[last_cell_loc].get_forwarding_bit() { + if self.cycle_detection_active() { + self.cycle_found = true; + return None; + } else if self.backward() { + return None; + } + + continue; + } + + self.heap[last_cell_loc].set_forwarding_bit(true); + + self.next = self.heap[last_cell_loc].get_value(); + self.heap[last_cell_loc].set_value(self.current as u64); + self.current = last_cell_loc; + + return Some(cell); + } + HeapCellValueTag::PStrOffset => { + let h = self.next as usize; + let cell = self.heap[h]; + let last_cell_loc = h + 1; + + if self.heap[h].get_tag() == HeapCellValueTag::PStr { + if self.heap[last_cell_loc].get_forwarding_bit() { + if self.cycle_detection_active() { + self.cycle_found = true; + return None; + } else if self.backward() { + return None; + } + + continue; + } + + self.heap[last_cell_loc].set_forwarding_bit(true); + + self.next = self.heap[last_cell_loc].get_value(); + self.heap[last_cell_loc].set_value(self.current as u64); + self.current = last_cell_loc; + } else { + debug_assert!(self.heap[h].get_tag() == HeapCellValueTag::CStr); + + self.next = self.heap[h].get_value(); + self.heap[h].set_value(self.current as u64); + self.current = h; + } + + return Some(cell); + } + tag @ HeapCellValueTag::Atom => { + let cell = HeapCellValue::build_with(tag, self.next); + let arity = AtomCell::from_bytes(cell.into_bytes()).get_arity(); + + if arity == 0 { + return Some(self.backward_and_return()); + } else if self.backward() { + return None; + } + } + HeapCellValueTag::PStr => { + if self.backward() { + return None; + } + } + _ => { + return Some(self.backward_and_return()); + } + } + } else if self.backward() { + return None; + } + } + } + + fn pivot_subterm(&mut self) { + self.current -= 1; + + let temp = self.heap[self.current + 1].get_value(); + + self.heap[self.current + 1].set_value(self.next); + self.next = self.heap[self.current].get_value(); + self.heap[self.current].set_value(temp); + + self.heap[self.current].set_forwarding_bit(true); + } + + fn continue_backward(&mut self) -> bool { + self.heap[self.current].set_forwarding_bit(false); + + if self.current == self.start { + return false; + } + + let temp = self.heap[self.current].get_value(); + + match self.heap[temp as usize].get_tag() { + HeapCellValueTag::Str => { + let mut new_str_back_link = self.current; + + for idx in (0 .. self.current).rev() { + if self.heap[idx].get_tag() == HeapCellValueTag::Atom { + if cell_as_atom_cell!(self.heap[idx]).get_arity() > 0 { + new_str_back_link = idx; + break; + } + } + + if self.heap[idx].get_mark_bit() != self.mark_phase { + if !self.heap[idx].get_forwarding_bit() { + new_str_back_link = idx; + break; + } + } + } + + self.heap[self.current].set_mark_bit(self.mark_phase); + self.heap[self.current].set_value(self.next); + + let back_link_cell = self.heap[new_str_back_link]; + + self.next = back_link_cell.get_value(); + self.heap[new_str_back_link].set_value(temp); + self.current = new_str_back_link; + + read_heap_cell!(back_link_cell, + (HeapCellValueTag::Atom, (_name, arity)) => { + if arity > 0 { + self.heap[self.current].set_mark_bit(self.mark_phase); + return true; + } + } + _ => {} + ); + + self.heap[self.current].set_forwarding_bit(true); + false + } + HeapCellValueTag::Lis => { + if self.heap[self.current].get_mark_bit() == self.mark_phase { + true + } else { + self.heap[self.current - 1].set_mark_bit(self.mark_phase); + self.heap[self.current].set_mark_bit(self.mark_phase); + + if self.heap[self.current - 1].get_forwarding_bit() { + self.heap[self.current].set_value(self.next); + self.next = self.current as u64 - 1; + self.current = temp as usize; + + true + } else { + self.pivot_subterm(); + false + } + } + } + _ => { + self.heap[self.current].set_mark_bit(self.mark_phase); + true + } + } + } + + fn backward(&mut self) -> bool { + while self.continue_backward() { + let temp = self.heap[self.current].get_value(); + + self.heap[self.current].set_value(self.next); + self.next = self.current as u64; + self.current = temp as usize; + } + + if self.current == self.start { + return true; + } + + false + } + + fn invert_marker(&mut self) { + self.cycle_found = false; + + if self.heap[self.start].get_forwarding_bit() { + while !self.backward() {} + } + + self.mark_phase = false; + self.heap[self.start].set_forwarding_bit(true); + + self.next = self.heap[self.start].get_value(); + self.current = self.start; + + while let Some(_) = self.forward() {} + } +} + +impl<'a, const STOP_AT_CYCLES: bool> Iterator for CycleDetectingIter<'a, STOP_AT_CYCLES> { + type Item = HeapCellValue; + + #[inline] + fn next(&mut self) -> Option { + self.forward() + } +} + + +impl<'a, const STOP_AT_CYCLES: bool> Drop for CycleDetectingIter<'a, STOP_AT_CYCLES> { + fn drop(&mut self) { + self.invert_marker(); + + if self.current == self.start { + return; + } + + while !self.backward() {} + } +} diff --git a/src/machine/disjuncts.rs b/src/machine/disjuncts.rs index a8a01d36..f68ee75c 100644 --- a/src/machine/disjuncts.rs +++ b/src/machine/disjuncts.rs @@ -154,8 +154,11 @@ enum TraversalState { Fail, GetCutPoint { var_num: usize, prev_b: bool }, Cut { var_num: usize, is_global: bool }, + CutPrev(usize), ResetCallPolicy(CallPolicy), Term(Term), + OverrideGlobalCutVar(usize), + ResetGlobalCutVarOverride(Option), RemoveBranchNum, // pop the current_branch_num and from the root set. AddBranchNum(BranchNumber), // set current_branch_num, add it to the root set RepBranchNum(BranchNumber), // replace current_branch_num and the latest in the root set @@ -171,6 +174,7 @@ pub struct VariableClassifier { var_num: usize, root_set: RootSet, global_cut_var_num: Option, + global_cut_var_num_override: Option, } #[derive(Debug, Default)] @@ -252,6 +256,7 @@ impl VariableClassifier { root_set: RootSet::new(), var_num: 0, global_cut_var_num: None, + global_cut_var_num_override: None, } } @@ -517,6 +522,12 @@ impl VariableClassifier { self.probe_in_situ_var(var_num); build_stack.push_chunk_term(QueryTerm::GetCutPoint { var_num, prev_b }); } + TraversalState::OverrideGlobalCutVar(var_num) => { + self.global_cut_var_num_override = Some(var_num); + } + TraversalState::ResetGlobalCutVarOverride(old_override) => { + self.global_cut_var_num_override = old_override; + } TraversalState::Cut { var_num, is_global } => { if self.try_set_chunk_at_inlined_boundary() { build_stack.add_chunk(); @@ -527,9 +538,18 @@ impl VariableClassifier { build_stack.push_chunk_term(if is_global { QueryTerm::GlobalCut(var_num) } else { - QueryTerm::LocalCut(var_num) + QueryTerm::LocalCut { var_num, cut_prev: false } }); } + TraversalState::CutPrev(var_num) => { + if self.try_set_chunk_at_inlined_boundary() { + build_stack.add_chunk(); + } + + self.probe_in_situ_var(var_num); + + build_stack.push_chunk_term(QueryTerm::LocalCut { var_num, cut_prev: true }); + } TraversalState::Fail => { build_stack.push_chunk_term(QueryTerm::Fail); } @@ -684,14 +704,13 @@ impl VariableClassifier { ))); state_stack.push(TraversalState::BuildDisjunct(build_stack_len)); state_stack.push(TraversalState::Fail); - state_stack.push(TraversalState::Cut { - var_num: self.var_num, - is_global: false, - }); + state_stack.push(TraversalState::CutPrev(self.var_num)); + state_stack.push(TraversalState::ResetGlobalCutVarOverride(self.global_cut_var_num_override)); state_stack.push(TraversalState::Term(not_term)); + state_stack.push(TraversalState::OverrideGlobalCutVar(self.var_num)); state_stack.push(TraversalState::GetCutPoint { var_num: self.var_num, - prev_b: true, + prev_b: false, }); self.current_chunk_type = ChunkType::Mid; @@ -786,17 +805,23 @@ impl VariableClassifier { )); } Term::Literal(_, Literal::Atom(atom!("!")) | Literal::Char('!')) => { - if self.global_cut_var_num.is_none() { - self.global_cut_var_num = Some(self.var_num); - self.var_num += 1; - } + let (var_num, is_global) = + if let Some(var_num) = self.global_cut_var_num_override { + (var_num, false) + } else if let Some(var_num) = self.global_cut_var_num { + (var_num, true) + } else { + let var_num = self.var_num; - self.probe_in_situ_var(self.global_cut_var_num.unwrap()); + self.global_cut_var_num = Some(var_num); + self.var_num += 1; - state_stack.push(TraversalState::Cut { - var_num: self.global_cut_var_num.unwrap(), - is_global: true, - }); + (var_num, true) + }; + + self.probe_in_situ_var(var_num); + + state_stack.push(TraversalState::Cut { var_num, is_global }); } Term::Literal(_, Literal::Atom(name)) => { if update_chunk_data(self, name, 0) { diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index 771fe91f..191a023f 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -917,69 +917,6 @@ impl Machine { self.machine_st.p += 1; } - &Instruction::ACosh(ref a1, t) => { - let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1)); - - self.machine_st.interms[t - 1] = Number::Float(OrderedFloat( - try_or_throw_gen!(&mut self.machine_st, acosh(n1)), - )); - - self.machine_st.p += 1; - } - &Instruction::ASinh(ref a1, t) => { - let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1)); - - self.machine_st.interms[t - 1] = Number::Float(OrderedFloat( - try_or_throw_gen!(&mut self.machine_st, asinh(n1)), - )); - - self.machine_st.p += 1; - } - &Instruction::ATanh(ref a1, t) => { - let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1)); - - self.machine_st.interms[t - 1] = Number::Float(OrderedFloat( - try_or_throw_gen!(&mut self.machine_st, atanh(n1)), - )); - - self.machine_st.p += 1; - } - &Instruction::Cosh(ref a1, t) => { - let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1)); - - self.machine_st.interms[t - 1] = Number::Float(OrderedFloat( - try_or_throw_gen!(&mut self.machine_st, cosh(n1)), - )); - - self.machine_st.p += 1; - } - &Instruction::Sinh(ref a1, t) => { - let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1)); - - self.machine_st.interms[t - 1] = Number::Float(OrderedFloat( - try_or_throw_gen!(&mut self.machine_st, sinh(n1)), - )); - - self.machine_st.p += 1; - } - &Instruction::Tanh(ref a1, t) => { - let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1)); - - self.machine_st.interms[t - 1] = Number::Float(OrderedFloat( - try_or_throw_gen!(&mut self.machine_st, tanh(n1)), - )); - - self.machine_st.p += 1; - } - &Instruction::Log10(ref a1, t) => { - let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1)); - - self.machine_st.interms[t - 1] = Number::Float(OrderedFloat( - try_or_throw_gen!(&mut self.machine_st, log10(n1)), - )); - - self.machine_st.p += 1; - } &Instruction::Float(ref a1, t) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1)); @@ -1251,6 +1188,21 @@ impl Machine { self.machine_st.p += 1; } + &Instruction::CutPrev(r) => { + let value = self.machine_st[r]; + self.machine_st.cut_prev_body(value); + + if self.machine_st.fail { + self.machine_st.backtrack(); + continue; + } + + if (self.machine_st.run_cleaners_fn)(self) { + continue; + } + + self.machine_st.p += 1; + } &Instruction::Allocate(num_cells) => { self.machine_st.allocate(num_cells); } diff --git a/src/machine/gc.rs b/src/machine/gc.rs index 9b0f958f..715adde7 100644 --- a/src/machine/gc.rs +++ b/src/machine/gc.rs @@ -10,16 +10,13 @@ pub(crate) trait UnmarkPolicy { where Self: Sized; fn invert_marker(iter: &mut StacklessPreOrderHeapIter) where Self: Sized; - fn cycle_detected(&mut self) where Self: Sized; fn mark_phase(&self) -> bool; - fn var_rooted_cycle(_iter: &mut StacklessPreOrderHeapIter, _next: usize) - where - Self: Sized {} - fn detect_list_tail_cycle(_iter: &mut StacklessPreOrderHeapIter) where Self: Sized {} - fn list_head_cycle_detecting_backward( - iter: &mut StacklessPreOrderHeapIter, - ) -> bool where Self: Sized { - iter.backward() + #[inline] + fn report_var_link(iter: &StacklessPreOrderHeapIter) -> bool where Self: Sized { + iter.heap[iter.next as usize].get_mark_bit() == iter.iter_state.mark_phase() + } + #[inline(always)] + fn record_focus(_iter: &mut StacklessPreOrderHeapIter) where Self: Sized { } } @@ -52,65 +49,12 @@ impl UnmarkPolicy for IteratorUMP { invert_marker(iter); } - #[inline(always)] - fn cycle_detected(&mut self) {} - #[inline] fn mark_phase(&self) -> bool { self.mark_phase } } -pub(crate) struct CycleDetectorUMP { - mark_phase: bool, - cycle_detected: bool, -} - -impl UnmarkPolicy for CycleDetectorUMP { - #[inline(always)] - fn forward_attr_var(iter: &mut StacklessPreOrderHeapIter) -> Option { - iter.forward_var() - } - - #[inline] - fn invert_marker(iter: &mut StacklessPreOrderHeapIter) { - iter.iter_state.mark_phase = false; - invert_marker(iter); - } - - #[inline] - fn cycle_detected(&mut self) { - self.cycle_detected = true; - } - - #[inline(always)] - fn mark_phase(&self) -> bool { - self.mark_phase - } - - fn list_head_cycle_detecting_backward( - iter: &mut StacklessPreOrderHeapIter, - ) -> bool { - if !iter.iter_state.cycle_detected && iter.iter_state.mark_phase { - iter.iter_state.cycle_detected = iter.detect_list_cycle(iter.current); - } - - iter.backward() - } - - fn detect_list_tail_cycle(iter: &mut StacklessPreOrderHeapIter) { - if iter.iter_state.mark_phase && !iter.iter_state.cycle_detected { - iter.iter_state.cycle_detected = iter.detect_list_cycle(iter.current); - } - } - - fn var_rooted_cycle(iter: &mut StacklessPreOrderHeapIter, next: usize) { - if iter.current != next && iter.iter_state.mark_phase && !iter.iter_state.cycle_detected { - iter.iter_state.cycle_detected = iter.detect_list_cycle(next); - } - } -} - struct MarkerUMP {} impl UnmarkPolicy for MarkerUMP { @@ -138,9 +82,6 @@ impl UnmarkPolicy for MarkerUMP { fn mark_phase(&self) -> bool { true } - - #[inline(always)] - fn cycle_detected(&mut self) {} } #[derive(Debug)] @@ -187,49 +128,6 @@ impl<'a> StacklessPreOrderHeapIter<'a, MarkerUMP> { } } -impl<'a> StacklessPreOrderHeapIter<'a, CycleDetectorUMP> { - pub(crate) fn new(heap: &'a mut [HeapCellValue], start: usize) -> Self { - heap[start].set_forwarding_bit(true); - let next = heap[start].get_value(); - - Self { - heap, - start, - current: start, - next, - iter_state: CycleDetectorUMP { - mark_phase: true, - cycle_detected: false, - }, - } - } - - #[inline] - pub(crate) fn found_cycle(&self) -> bool { - self.iter_state.cycle_detected - } - - pub(crate) fn detect_list_cycle(&self, next: usize) -> bool { - use crate::machine::system_calls::BrentAlgState; - - let mut brent_alg_st = BrentAlgState::new(self.current); - - while self.heap[brent_alg_st.hare].get_mark_bit() { - let temp = self.heap[brent_alg_st.hare].get_value() as usize; - - if brent_alg_st.step(temp).is_some() || temp == next { - return true; - } - - if temp == self.start { - break; - } - } - - false - } -} - impl<'a> StacklessPreOrderHeapIter<'a, IteratorUMP> { #[cfg(test)] pub(crate) fn new(heap: &'a mut [HeapCellValue], start: usize) -> Self { @@ -241,9 +139,7 @@ impl<'a> StacklessPreOrderHeapIter<'a, IteratorUMP> { start, current: start, next, - iter_state: IteratorUMP { - mark_phase: true, - }, + iter_state: IteratorUMP { mark_phase: true,}, } } } @@ -278,78 +174,44 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { None } - #[inline] - fn is_cyclic(&self, var_current: usize, var_next: usize, var_f: bool) -> bool { - if self.heap[var_next].is_var() { - // the third conjunct covers the case where var_current - // was just unforwarded by forward_var() and so - // self.current + 1 == var_current. see acyclic_term#2121 - // & acyclic_term_30 for examples of how this occurs. - self.heap[var_next].get_mark_bit() && var_current != var_next && !var_f - } else if self.heap[var_next].is_ref() { - self.heap[var_next].get_mark_bit() - } else { - false - } - } - fn forward(&mut self) -> Option { loop { if self.heap[self.current].get_mark_bit() != self.iter_state.mark_phase() { self.heap[self.current].set_mark_bit(self.iter_state.mark_phase()); + UMP::record_focus(self); + match self.heap[self.current].get_tag() { HeapCellValueTag::AttrVar => { - let next = self.next; - let current = self.current; - let f = self.heap[self.current].get_forwarding_bit(); - - if self.heap[next as usize].get_mark_bit() == self.iter_state.mark_phase() { - UMP::var_rooted_cycle(self, next as usize); - } + let next = self.next as usize; if let Some(cell) = UMP::forward_attr_var(self) { - if self.is_cyclic(current, next as usize, f) { - self.iter_state.cycle_detected(); - } - return Some(cell); } if self.next < self.heap.len() as u64 { - if self.heap[self.next as usize].get_mark_bit() == self.iter_state.mark_phase() { + if UMP::report_var_link(self) { let tag = HeapCellValueTag::AttrVar; - return Some(HeapCellValue::build_with(tag, next)); + return Some(HeapCellValue::build_with(tag, next as u64)); } } } HeapCellValueTag::Var => { - let next = self.next; - let current = self.current; - let f = self.heap[self.current].get_forwarding_bit(); - - if self.heap[next as usize].get_mark_bit() == self.iter_state.mark_phase() { - UMP::var_rooted_cycle(self, next as usize); - } + let next = self.next as usize; if let Some(cell) = self.forward_var() { - if self.is_cyclic(current, next as usize, f) { - self.iter_state.cycle_detected(); - } - return Some(cell); } if self.next < self.heap.len() as u64 { - if self.heap[self.next as usize].get_mark_bit() == self.iter_state.mark_phase() { + if UMP::report_var_link(self) { let tag = HeapCellValueTag::Var; - return Some(HeapCellValue::build_with(tag, next)); + return Some(HeapCellValue::build_with(tag, next as u64)); } } } HeapCellValueTag::Str => { if self.heap[self.next as usize + 1].get_forwarding_bit() { - self.iter_state.cycle_detected(); return Some(self.backward_and_return()); } @@ -374,7 +236,6 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { let last_cell_loc = self.next as usize + 1; if self.heap[last_cell_loc].get_forwarding_bit() { - self.iter_state.cycle_detected(); return Some(self.backward_and_return()); } @@ -384,32 +245,12 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { self.heap[last_cell_loc].set_forwarding_bit(true); - if self.heap[last_cell_loc].get_mark_bit() == self.iter_state.mark_phase() { - if self.heap[last_cell_loc-1].get_mark_bit() == self.iter_state.mark_phase() { - // the conjunction leading here is a necessary but not sufficient - // condition of the presence of a cycle at the list head. - - if last_cell_loc == self.current { - UMP::detect_list_tail_cycle(self); - } - - self.backward(); - - if UMP::list_head_cycle_detecting_backward(self) { - return None; - } - - continue; - } - } - return Some(list_loc_as_cell!(last_cell_loc - 1)); } HeapCellValueTag::PStrLoc => { let h = self.next as usize; if self.heap[h + 1].get_forwarding_bit() { - self.iter_state.cycle_detected(); return Some(self.backward_and_return()); } @@ -432,7 +273,6 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> { let last_cell_loc = h + 1; if self.heap[last_cell_loc].get_forwarding_bit() { - self.iter_state.cycle_detected(); return Some(self.backward_and_return()); } diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index dc938154..94e85af8 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -949,6 +949,25 @@ impl MachineState { } ); } + + #[inline(always)] + pub(super) fn cut_prev_body(&mut self, value: HeapCellValue) { + let b = self.b; + + read_heap_cell!(value, + (HeapCellValueTag::CutPoint, b0) => { + let b0 = b0.get_num() as usize; + let b0 = self.stack.index_or_frame(b0).prelude.b; + + if b > b0 { + self.b = b0; + } + } + _ => { + self.fail = true; + } + ); + } } #[derive(Debug)] diff --git a/src/machine/machine_state_impl.rs b/src/machine/machine_state_impl.rs index eef5f18c..5007e85a 100644 --- a/src/machine/machine_state_impl.rs +++ b/src/machine/machine_state_impl.rs @@ -1133,27 +1133,21 @@ impl MachineState { pub fn is_cyclic_term(&mut self, value: HeapCellValue) -> bool { let value = self.store(self.deref(value)); - if value.is_constant() || value.is_stack_var() { + if value.is_stack_var() || value.is_constant() { return false; } let h = self.heap.len(); self.heap.push(value); - let found_cycle = { + let cycle_found = { let mut iter = cycle_detecting_stackless_preorder_iter(&mut self.heap, h); - - while let Some(_) = iter.next() { - if iter.found_cycle() { - break; - } - } - - iter.found_cycle() + while let Some(_) = iter.next() {} + iter.cycle_found() }; self.heap.pop(); - found_cycle + cycle_found } // arg(+N, +Term, ?Arg) diff --git a/src/machine/mock_wam.rs b/src/machine/mock_wam.rs index 7182d626..4b3de5ed 100644 --- a/src/machine/mock_wam.rs +++ b/src/machine/mock_wam.rs @@ -6,7 +6,6 @@ pub use crate::machine::machine_state::*; pub use crate::machine::stack::*; pub use crate::machine::streams::*; pub use crate::machine::*; -pub use crate::macros::*; pub use crate::parser::ast::*; use crate::read::*; pub use crate::types::*; diff --git a/src/machine/mod.rs b/src/machine/mod.rs index 38afac5b..b4f1687e 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -7,6 +7,7 @@ pub mod loader; pub mod compile; pub mod config; pub mod copier; +pub mod cycle_detection; pub mod disjuncts; pub mod dispatch; pub mod gc; diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 61df25e7..f6e72b93 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -2936,95 +2936,92 @@ impl Machine { } ); - self.machine_st.fail = true; // This predicate fails by default. - read_heap_cell!(a2, (HeapCellValueTag::Atom, (chars, _arity)) => { - macro_rules! macro_check { - ($id:ident, $name:expr) => { - if $id!(c) && chars == $name { - self.machine_st.fail = false; - return; + self.machine_st.fail = true; // This predicate fails by default. + macro_rules! macro_check { + ($id:ident, $name:expr) => { + if $id!(c) && chars == $name { + self.machine_st.fail = false; + return; + } + }; } - }; - } - macro_rules! method_check { - ($id:ident, $name:expr) => { - if c.$id() && chars == $name { - self.machine_st.fail = false; - return; + macro_rules! method_check { + ($id:ident, $name:expr) => { + if c.$id() && chars == $name { + self.machine_st.fail = false; + return; + } + }; } - }; - } - macro_check!(alpha_char, atom!("alpha")); - method_check!(is_alphabetic, atom!("alphabetic")); - method_check!(is_alphanumeric, atom!("alphanumeric")); - macro_check!(alpha_numeric_char, atom!("alnum")); - method_check!(is_ascii, atom!("ascii")); - method_check!(is_ascii_punctuation, atom!("ascii_punctuation")); - method_check!(is_ascii_graphic, atom!("ascii_graphic")); - // macro_check!(backslash_char, atom!("backslash")); - // macro_check!(back_quote_char, atom!("back_quote")); - macro_check!(binary_digit_char, atom!("binary_digit")); - // macro_check!(capital_letter_char, atom!("upper")); - // macro_check!(comment_1_char, "comment_1"); - // macro_check!(comment_2_char, "comment_2"); - method_check!(is_control, atom!("control")); - // macro_check!(cut_char, atom!("cut")); - macro_check!(decimal_digit_char, atom!("decimal_digit")); - // macro_check!(decimal_point_char, atom!("decimal_point")); - // macro_check!(double_quote_char, atom!("double_quote")); - macro_check!(exponent_char, atom!("exponent")); - macro_check!(graphic_char, atom!("graphic")); - macro_check!(graphic_token_char, atom!("graphic_token")); - macro_check!(hexadecimal_digit_char, atom!("hexadecimal_digit")); - macro_check!(layout_char, atom!("layout")); - method_check!(is_lowercase, atom!("lower")); - macro_check!(meta_char, atom!("meta")); - // macro_check!(new_line_char, atom!("new_line")); - method_check!(is_numeric, atom!("numeric")); - macro_check!(octal_digit_char, atom!("octal_digit")); - macro_check!(octet_char, atom!("octet")); - macro_check!(prolog_char, atom!("prolog")); - // macro_check!(semicolon_char, atom!("semicolon")); - macro_check!(sign_char, atom!("sign")); - // macro_check!(single_quote_char, atom!("single_quote")); - // macro_check!(small_letter_char, atom!("lower")); - macro_check!(solo_char, atom!("solo")); - // macro_check!(space_char, atom!("space")); - macro_check!(symbolic_hexadecimal_char, atom!("symbolic_hexadecimal")); - macro_check!(symbolic_control_char, atom!("symbolic_control")); - method_check!(is_uppercase, atom!("upper")); - // macro_check!(variable_indicator_char, atom!("variable_indicator")); - method_check!(is_whitespace, atom!("whitespace")); + macro_check!(alpha_char, atom!("alpha")); + method_check!(is_alphabetic, atom!("alphabetic")); + method_check!(is_alphanumeric, atom!("alphanumeric")); + macro_check!(alpha_numeric_char, atom!("alnum")); + method_check!(is_ascii, atom!("ascii")); + method_check!(is_ascii_punctuation, atom!("ascii_punctuation")); + method_check!(is_ascii_graphic, atom!("ascii_graphic")); + // macro_check!(backslash_char, atom!("backslash")); + // macro_check!(back_quote_char, atom!("back_quote")); + macro_check!(binary_digit_char, atom!("binary_digit")); + // macro_check!(capital_letter_char, atom!("upper")); + // macro_check!(comment_1_char, "comment_1"); + // macro_check!(comment_2_char, "comment_2"); + method_check!(is_control, atom!("control")); + // macro_check!(cut_char, atom!("cut")); + macro_check!(decimal_digit_char, atom!("decimal_digit")); + // macro_check!(decimal_point_char, atom!("decimal_point")); + // macro_check!(double_quote_char, atom!("double_quote")); + macro_check!(exponent_char, atom!("exponent")); + macro_check!(graphic_char, atom!("graphic")); + macro_check!(graphic_token_char, atom!("graphic_token")); + macro_check!(hexadecimal_digit_char, atom!("hexadecimal_digit")); + macro_check!(layout_char, atom!("layout")); + method_check!(is_lowercase, atom!("lower")); + macro_check!(meta_char, atom!("meta")); + // macro_check!(new_line_char, atom!("new_line")); + method_check!(is_numeric, atom!("numeric")); + macro_check!(octal_digit_char, atom!("octal_digit")); + macro_check!(octet_char, atom!("octet")); + macro_check!(prolog_char, atom!("prolog")); + // macro_check!(semicolon_char, atom!("semicolon")); + macro_check!(sign_char, atom!("sign")); + // macro_check!(single_quote_char, atom!("single_quote")); + // macro_check!(small_letter_char, atom!("lower")); + macro_check!(solo_char, atom!("solo")); + // macro_check!(space_char, atom!("space")); + macro_check!(symbolic_hexadecimal_char, atom!("symbolic_hexadecimal")); + macro_check!(symbolic_control_char, atom!("symbolic_control")); + method_check!(is_uppercase, atom!("upper")); + // macro_check!(variable_indicator_char, atom!("variable_indicator")); + method_check!(is_whitespace, atom!("whitespace")); } - (HeapCellValueTag::Str, s) => { + (HeapCellValueTag::Str, s) => { let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s]) - .get_name_and_arity(); + .get_name_and_arity(); - match (name, arity) { - (atom!("to_upper"), 1) => { - let reg = self.machine_st.deref(self.machine_st.heap[s+1]); - let atom = AtomTable::build_with(&self.machine_st.atom_tbl, &c.to_uppercase().to_string()); - let upper_str = string_as_cstr_cell!(atom); - unify!(self.machine_st, reg, upper_str); - self.machine_st.fail = false; + match (name, arity) { + (atom!("upper"), 1) => { + let reg = self.machine_st.deref(self.machine_st.heap[s+1]); + let atom = AtomTable::build_with(&self.machine_st.atom_tbl, &c.to_uppercase().to_string()); + let upper_str = string_as_cstr_cell!(atom); + unify!(self.machine_st, reg, upper_str); + } + (atom!("lower"), 1) => { + let reg = self.machine_st.deref(self.machine_st.heap[s+1]); + let atom = AtomTable::build_with(&self.machine_st.atom_tbl, &c.to_lowercase().to_string()); + let lower_str = string_as_cstr_cell!(atom); + unify!(self.machine_st, reg, lower_str); + } + _ => { + unreachable!() + } } - (atom!("to_lower"), 1) => { - let reg = self.machine_st.deref(self.machine_st.heap[s+1]); - let atom = AtomTable::build_with(&self.machine_st.atom_tbl, &c.to_lowercase().to_string()); - let lower_str = string_as_cstr_cell!(atom); - unify!(self.machine_st, reg, lower_str); - self.machine_st.fail = false; - } - _ => { - unreachable!() - } - }; } - _ => { + _ => { unreachable!() } ); diff --git a/src/tests/acyclic_term.pl b/src/tests/acyclic_term.pl index e340d09c..065a4d9f 100644 --- a/src/tests/acyclic_term.pl +++ b/src/tests/acyclic_term.pl @@ -1,3 +1,4 @@ +:- use_module(library(dcgs)). :- use_module(library(format)). term1(A) :- @@ -28,6 +29,16 @@ term6(A) :- A=[B|B], B=[C|C]. +term7(A) :- + B=[C|D], + A=[D|C], + B=[B|D]. + +term8(A) :- + B=C, + B=[C|_D], + A=[C|_E]. + test("acyclic_term_1", ( L = [_Y,[M,B],B|M], acyclic_term(L) )). @@ -80,80 +91,91 @@ test("acyclic_term_13", ( A = [A|2], X = A, T=a(X, A), \+ acyclic_term(T) )). -test("acyclic_term_13", ( +test("acyclic_term_14", ( A = [T|2], X = A, T=a(X, A), \+ acyclic_term(T) )). -test("acyclic_term_14", ( +test("acyclic_term_15", ( T = [_A|T], \+ acyclic_term(T) )). -test("acyclic_term_15", ( +test("acyclic_term_16", ( T = [T|_L], \+ acyclic_term(T) )). -test("acyclic_term_16", ( +test("acyclic_term_17", ( A = [1|A], X = A, T=a(X, A), \+ acyclic_term(T) )). -test("acyclic_term_17", ( +test("acyclic_term_18", ( T = [_A| [[[[L|T]|[]]]]], acyclic_term(L) )). -test("acyclic_term_18", ( +test("acyclic_term_19", ( T = [A| [[[[_L|T]|[]]]]], acyclic_term(A) )). -test("acyclic_term_19", ( +test("acyclic_term_20", ( T = [_A| [[[[_L|T]|[]]]]], \+ acyclic_term(T) )). -test("acyclic_term_20", ( +test("acyclic_term_21", ( A = [_C|_B], X = A, T=a(t(X,A), A), acyclic_term(T) )). -test("acyclic_term_21", ( +test("acyclic_term_22", ( X = [a | Rest], Rest = [_Y | Rest], \+ acyclic_term(X) )). -test("acyclic_term_22", ( +test("acyclic_term_23", ( _X = [a | Rest], Rest = [_Y | Rest], \+ acyclic_term(Rest) )). -test("acyclic_term_23", ( +test("acyclic_term_24", ( T = [[_A, T]], G = [1|T], \+ acyclic_term(G) )). -test("acyclic_term_24", ( +test("acyclic_term_25", ( T = [[_A, T]], \+ acyclic_term(T) )). -test("acyclic_term_25", ( +test("acyclic_term_26", ( T = [[_, _], T], \+ acyclic_term(T) )). -test("acyclic_term_26", ( +test("acyclic_term_27", ( T = [[T, _], 1], \+ acyclic_term(T) )). -test("acyclic_term_27", ( +test("acyclic_term_28", ( T = str(A,A), acyclic_term(T) )). -test("acyclic_term_28", ( +test("acyclic_term_29", ( T = str(A,A,A), acyclic_term(T) )). -test("acyclic_term_29", ( +test("acyclic_term_30", ( A = s(B, d(Y)), Y = B, acyclic_term(A), acyclic_term(B), acyclic_term(Y) )). -test("acyclic_term_30", ( +test("acyclic_term_31", ( A=str(B,B,B), C=str(A,_D,B), acyclic_term(C), acyclic_term(A), acyclic_term(B) )). +test("acyclic_term_32", ( + A=B*C,A=[]*B*D*B, \+ acyclic_term(A), + \+ acyclic_term(B), \+ acyclic_term(C), + acyclic_term(D) +)). + +test("acyclic_term_33", ( + A=B*C,A=B*[]*B, \+ acyclic_term(A), + \+ acyclic_term(B), \+ acyclic_term(C) +)). + test("acyclic_term#2111_1", ( term1(A), \+ acyclic_term(A) )). @@ -199,6 +221,45 @@ test("acyclic_term#2121", ( acyclic_term(A), acyclic_term(B) )). +test("acyclic_term#2122", ( + A=B*C,A=[]*B*B, \+ acyclic_term(A), + \+ acyclic_term(B), \+ acyclic_term(C) +)). + +test("acyclic_term#2123", ( + A=B*B,C=A*B,B=[]*[], acyclic_term(A), + acyclic_term(B), acyclic_term(C) +)). + +test("acyclic_term#2124", ( + A=B*C,B=[]*C,C=[]*B, \+ acyclic_term(A), + \+ acyclic_term(B), \+ acyclic_term(C) +)). + +test("acyclic_term#2125", ( + A=B*[], + D=B*[], + A=B, + \+ acyclic_term(D), + \+ acyclic_term(A), + \+ acyclic_term(B) +)). + +test("acyclic_term#2130_1", ( + term7(T), + \+ acyclic_term(T) +)). + +test("acyclic_term#2130_2", ( + term8(T), + \+ acyclic_term(T) +)). + +test("acyclic_term#2131", ( + A=[B],C=[B],C=[A], + \+ acyclic_term(C) +)). + main :- findall(test(Name, Goal), test(Name, Goal), Tests), run_tests(Tests, Failed), @@ -231,3 +292,15 @@ run_tests_quiet([test(Name, Goal)|Tests], Failed) :- ; Failed = [Name|Failed1] ), run_tests_quiet(Tests, Failed1). + +show_failed(Failed) :- + phrase(portray_failed(Failed), F), + format("~s", [F]). + +portray_failed_([]) --> []. +portray_failed_([F|Fs]) --> + "\"", F, "\"", "\n", portray_failed_(Fs). + +portray_failed([]) --> []. +portray_failed([F|Fs]) --> + "\n", "Failed tests:", "\n", portray_failed_([F|Fs]).