Merge branch 'master' into library-use-case
This commit is contained in:
47
.github/workflows/ci.yml
vendored
47
.github/workflows/ci.yml
vendored
@@ -17,14 +17,18 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- { os: windows-latest, rust-version: stable, target: 'x86_64-pc-windows-msvc'}
|
# operating systems
|
||||||
- { os: macos-11, rust-version: stable, target: 'x86_64-apple-darwin' }
|
- { os: windows-latest, rust-version: stable, publish: true, target: 'x86_64-pc-windows-msvc'}
|
||||||
- { os: ubuntu-22.04, rust-version: stable, target: 'x86_64-unknown-linux-gnu' }
|
- { os: macos-11, rust-version: stable, publish: true, target: 'x86_64-apple-darwin' }
|
||||||
- { os: ubuntu-20.04, rust-version: stable, target: 'x86_64-unknown-linux-gnu', extra: true }
|
- { os: ubuntu-20.04, rust-version: stable, publish: true, target: 'x86_64-unknown-linux-gnu' }
|
||||||
- { os: ubuntu-20.04, rust-version: stable, target: 'i686-unknown-linux-gnu' }
|
# architectures
|
||||||
- { os: ubuntu-20.04, rust-version: "1.70", target: 'x86_64-unknown-linux-gnu'}
|
- { os: ubuntu-22.04, rust-version: stable, publish: true, target: 'x86_64-unknown-linux-gnu', extra: true }
|
||||||
- { os: ubuntu-20.04, rust-version: beta, target: 'x86_64-unknown-linux-gnu'}
|
- { os: ubuntu-22.04, rust-version: stable, publish: true, target: 'i686-unknown-linux-gnu' }
|
||||||
- { os: ubuntu-20.04, rust-version: nightly, target: 'x86_64-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:
|
defaults:
|
||||||
run:
|
run:
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -53,16 +57,13 @@ jobs:
|
|||||||
~/.cargo/git/db/
|
~/.cargo/git/db/
|
||||||
target/
|
target/
|
||||||
key: ${{ matrix.os }}_${{ matrix.target }}_rustc-${{ steps.toolchain.outputs.cachekey }}_cargo-${{ hashFiles('**/Cargo.lock') }}
|
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.
|
# Build and test.
|
||||||
- name: Build library
|
- 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
|
- name: Test
|
||||||
if: "!matrix.extra"
|
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
|
# Extra steps only run once to avoid duplication, when matrix.extra is true
|
||||||
- name: Test and report
|
- name: Test and report
|
||||||
@@ -95,26 +96,16 @@ jobs:
|
|||||||
# artifact. These binaries could be useful for testing the pipeline but
|
# artifact. These binaries could be useful for testing the pipeline but
|
||||||
# are only retained by github for 90 days.
|
# are only retained by github for 90 days.
|
||||||
- name: Build release binary
|
- name: Build release binary
|
||||||
if: contains(matrix.rust-version,'stable')
|
if: matrix.publish
|
||||||
run: |
|
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
|
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
|
- name: Publish release binary artifact
|
||||||
if: contains(matrix.rust-version,'stable')
|
if: matrix.publish
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
path: target/${{ matrix.target }}/release/scryer-prolog*
|
path: target/${{ matrix.target }}/release/scryer-prolog*
|
||||||
name: scryer-prolog_${{ matrix.os }}_${{ matrix.target }}
|
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:
|
logtalk-test:
|
||||||
# if: false # uncomment to disable job
|
# 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-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_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 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
|
- name: Release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
with:
|
with:
|
||||||
@@ -185,4 +176,4 @@ jobs:
|
|||||||
scryer-prolog_ubuntu-20.04.zip
|
scryer-prolog_ubuntu-20.04.zip
|
||||||
scryer-prolog_ubuntu-22.04.zip
|
scryer-prolog_ubuntu-22.04.zip
|
||||||
scryer-prolog_windows-latest.zip
|
scryer-prolog_windows-latest.zip
|
||||||
scryer-prolog_unknown-wasm32.zip
|
scryer-prolog_wasm32.zip
|
||||||
|
|||||||
@@ -660,6 +660,8 @@ enum InstructionTemplate {
|
|||||||
// cut instruction
|
// cut instruction
|
||||||
#[strum_discriminants(strum(props(Arity = "1", Name = "cut")))]
|
#[strum_discriminants(strum(props(Arity = "1", Name = "cut")))]
|
||||||
Cut(RegType),
|
Cut(RegType),
|
||||||
|
#[strum_discriminants(strum(props(Arity = "1", Name = "cut_prev")))]
|
||||||
|
CutPrev(RegType),
|
||||||
#[strum_discriminants(strum(props(Arity = "1", Name = "get_level")))]
|
#[strum_discriminants(strum(props(Arity = "1", Name = "get_level")))]
|
||||||
GetLevel(RegType),
|
GetLevel(RegType),
|
||||||
#[strum_discriminants(strum(props(Arity = "1", Name = "get_prev_level")))]
|
#[strum_discriminants(strum(props(Arity = "1", Name = "get_prev_level")))]
|
||||||
@@ -764,20 +766,6 @@ enum InstructionTemplate {
|
|||||||
Neg(ArithmeticTerm, usize),
|
Neg(ArithmeticTerm, usize),
|
||||||
#[strum_discriminants(strum(props(Arity = "1", Name = "plus")))]
|
#[strum_discriminants(strum(props(Arity = "1", Name = "plus")))]
|
||||||
Plus(ArithmeticTerm, usize),
|
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")))]
|
#[strum_discriminants(strum(props(Arity = "1", Name = "bitwise_complement")))]
|
||||||
BitwiseComplement(ArithmeticTerm, usize),
|
BitwiseComplement(ArithmeticTerm, usize),
|
||||||
// control instructions
|
// control instructions
|
||||||
@@ -1347,6 +1335,10 @@ fn generate_instruction_preface() -> TokenStream {
|
|||||||
let rt_stub = reg_type_into_functor(r);
|
let rt_stub = reg_type_into_functor(r);
|
||||||
functor!(atom!("cut"), [str(h, 0)], [rt_stub])
|
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) => {
|
&Instruction::GetLevel(r) => {
|
||||||
let rt_stub = reg_type_into_functor(r);
|
let rt_stub = reg_type_into_functor(r);
|
||||||
functor!(atom!("get_level"), [str(h, 0)], [rt_stub])
|
functor!(atom!("get_level"), [str(h, 0)], [rt_stub])
|
||||||
@@ -1449,30 +1441,9 @@ fn generate_instruction_preface() -> TokenStream {
|
|||||||
&Instruction::ATan(ref at, t) => {
|
&Instruction::ATan(ref at, t) => {
|
||||||
arith_instr_unary_functor(h, atom!("atan"), arena, 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) => {
|
&Instruction::Sqrt(ref at, t) => {
|
||||||
arith_instr_unary_functor(h, atom!("sqrt"), arena, 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) => {
|
&Instruction::Abs(ref at, t) => {
|
||||||
arith_instr_unary_functor(h, atom!("abs"), arena, at, t)
|
arith_instr_unary_functor(h, atom!("abs"), arena, at, t)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -197,13 +197,6 @@ impl<'a> ArithmeticEvaluator<'a> {
|
|||||||
atom!("sin") => Ok(Instruction::Sin(a1, t)),
|
atom!("sin") => Ok(Instruction::Sin(a1, t)),
|
||||||
atom!("tan") => Ok(Instruction::Tan(a1, t)),
|
atom!("tan") => Ok(Instruction::Tan(a1, t)),
|
||||||
atom!("log") => Ok(Instruction::Log(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!("exp") => Ok(Instruction::Exp(a1, t)),
|
||||||
atom!("sqrt") => Ok(Instruction::Sqrt(a1, t)),
|
atom!("sqrt") => Ok(Instruction::Sqrt(a1, t)),
|
||||||
atom!("acos") => Ok(Instruction::ACos(a1, t)),
|
atom!("acos") => Ok(Instruction::ACos(a1, t)),
|
||||||
|
|||||||
@@ -950,10 +950,15 @@ impl<'b> CodeGenerator<'b> {
|
|||||||
code.push_back(instr!("proceed"));
|
code.push_back(instr!("proceed"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&QueryTerm::LocalCut(var_num) => {
|
&QueryTerm::LocalCut { var_num, cut_prev } => {
|
||||||
let code = branch_code_stack.code(code);
|
let code = branch_code_stack.code(code);
|
||||||
let r = self.marker.get_binding(var_num);
|
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.in_tail_position {
|
||||||
if self.marker.var_data.allocates {
|
if self.marker.var_data.allocates {
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ pub enum QueryTerm {
|
|||||||
// register, clause type, subterms, clause call policy.
|
// register, clause type, subterms, clause call policy.
|
||||||
Clause(Cell<RegType>, ClauseType, Vec<Term>, CallPolicy),
|
Clause(Cell<RegType>, ClauseType, Vec<Term>, CallPolicy),
|
||||||
Fail,
|
Fail,
|
||||||
LocalCut(usize), // var_num
|
LocalCut { var_num: usize, cut_prev: bool }, // var_num
|
||||||
GlobalCut(usize), // var_num
|
GlobalCut(usize), // var_num
|
||||||
GetCutPoint { var_num: usize, prev_b: bool },
|
GetCutPoint { var_num: usize, prev_b: bool },
|
||||||
GetLevel(usize), // var_num
|
GetLevel(usize), // var_num
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) use crate::machine::gc::{IteratorUMP};
|
pub(crate) use crate::machine::gc::StacklessPreOrderHeapIter;
|
||||||
pub(crate) use crate::machine::gc::{CycleDetectorUMP, StacklessPreOrderHeapIter};
|
|
||||||
|
|
||||||
use crate::atom_table::*;
|
use crate::atom_table::*;
|
||||||
|
use crate::machine::cycle_detection::*;
|
||||||
use crate::machine::heap::*;
|
use crate::machine::heap::*;
|
||||||
use crate::machine::stack::*;
|
use crate::machine::stack::*;
|
||||||
use crate::types::*;
|
use crate::types::*;
|
||||||
@@ -505,24 +505,16 @@ impl<'a, ElideLists: ListElisionPolicy> Iterator for StackfulPreOrderHeapIter<'a
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn stackless_preorder_iter(
|
pub(crate) fn cycle_detecting_stackless_preorder_iter<'a>(
|
||||||
heap: &mut Vec<HeapCellValue>,
|
heap: &'a mut [HeapCellValue],
|
||||||
start: usize,
|
start: usize,
|
||||||
) -> StacklessPreOrderHeapIter<IteratorUMP> {
|
) -> CycleDetectingIter<'a, true> {
|
||||||
StacklessPreOrderHeapIter::<IteratorUMP>::new(heap, start)
|
// 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<CycleDetectorUMP> {
|
|
||||||
StacklessPreOrderHeapIter::<CycleDetectorUMP>::new(heap, start)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn stackful_preorder_iter<'a, ElideLists: ListElisionPolicy>(
|
pub(crate) fn stackful_preorder_iter<'a, ElideLists: ListElisionPolicy>(
|
||||||
heap: &'a mut Vec<HeapCellValue>,
|
heap: &'a mut Vec<HeapCellValue>,
|
||||||
@@ -666,10 +658,22 @@ pub(crate) fn stackful_post_order_iter<'a, ElideLists: ListElisionPolicy>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::machine::mock_wam::*;
|
||||||
|
use crate::machine::gc::{IteratorUMP};
|
||||||
|
|
||||||
pub(crate) type RightistPostOrderHeapIter<'a> =
|
pub(crate) type RightistPostOrderHeapIter<'a> =
|
||||||
PostOrderIterator<StacklessPreOrderHeapIter<'a, IteratorUMP>>;
|
PostOrderIterator<StacklessPreOrderHeapIter<'a, IteratorUMP>>;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[inline(always)]
|
||||||
|
pub(crate) fn stackless_preorder_iter(
|
||||||
|
heap: &mut Vec<HeapCellValue>,
|
||||||
|
start: usize,
|
||||||
|
) -> StacklessPreOrderHeapIter<IteratorUMP> {
|
||||||
|
StacklessPreOrderHeapIter::<IteratorUMP>::new(heap, start)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn stackless_post_order_iter<'a>(
|
pub(crate) fn stackless_post_order_iter<'a>(
|
||||||
heap: &'a mut Heap,
|
heap: &'a mut Heap,
|
||||||
@@ -678,11 +682,6 @@ pub(crate) fn stackless_post_order_iter<'a>(
|
|||||||
PostOrderIterator::new(stackless_preorder_iter(heap, start))
|
PostOrderIterator::new(stackless_preorder_iter(heap, start))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::machine::mock_wam::*;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn heap_stackless_iter_tests() {
|
fn heap_stackless_iter_tests() {
|
||||||
let mut wam = MockWAM::new();
|
let mut wam = MockWAM::new();
|
||||||
@@ -1267,6 +1266,10 @@ mod tests {
|
|||||||
unmark_cell_bits!(iter.next().unwrap()),
|
unmark_cell_bits!(iter.next().unwrap()),
|
||||||
list_loc_as_cell!(1)
|
list_loc_as_cell!(1)
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
unmark_cell_bits!(iter.next().unwrap()),
|
||||||
|
list_loc_as_cell!(1)
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(iter.next(), None);
|
assert_eq!(iter.next(), None);
|
||||||
}
|
}
|
||||||
@@ -2254,9 +2257,7 @@ mod tests {
|
|||||||
list_loc_as_cell!(1)
|
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().unwrap(), cyclic_link);
|
||||||
|
|
||||||
assert_eq!(iter.next().unwrap(), cyclic_link);
|
assert_eq!(iter.next().unwrap(), cyclic_link);
|
||||||
|
|
||||||
assert_eq!(iter.next(), None);
|
assert_eq!(iter.next(), None);
|
||||||
|
|||||||
@@ -888,6 +888,19 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
|||||||
var_opt => {
|
var_opt => {
|
||||||
if is_cyclic && cell.is_compound(self.iter.heap) {
|
if is_cyclic && cell.is_compound(self.iter.heap) {
|
||||||
// self-referential variables are marked "cyclic".
|
// 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 {
|
match var_opt {
|
||||||
Some(var) => {
|
Some(var) => {
|
||||||
// If the term is bound to a named variable,
|
// If the term is bound to a named variable,
|
||||||
|
|||||||
@@ -105,8 +105,8 @@ extend_var_list_([V|Vs], N, VarList, NewVarList, VarType) :-
|
|||||||
% - `symbolic_control`
|
% - `symbolic_control`
|
||||||
% - `symbolic_hexadecimal`
|
% - `symbolic_hexadecimal`
|
||||||
% - `upper`
|
% - `upper`
|
||||||
% - `to_lower(Lower)`
|
% - `lower(Lower)`
|
||||||
% - `to_upper(Upper)`
|
% - `upper(Upper)`
|
||||||
% - `whitespace`
|
% - `whitespace`
|
||||||
%
|
%
|
||||||
% An example:
|
% An example:
|
||||||
@@ -124,8 +124,8 @@ extend_var_list_([V|Vs], N, VarList, NewVarList, VarType) :-
|
|||||||
% ; Type = octet
|
% ; Type = octet
|
||||||
% ; Type = prolog
|
% ; Type = prolog
|
||||||
% ; Type = symbolic_control
|
% ; Type = symbolic_control
|
||||||
% ; Type = to_lower("a")
|
% ; Type = lower("a")
|
||||||
% ; Type = to_upper("A")
|
% ; Type = upper("A")
|
||||||
% ; false.
|
% ; false.
|
||||||
% ```
|
% ```
|
||||||
%
|
%
|
||||||
@@ -168,8 +168,8 @@ ctype(sign).
|
|||||||
ctype(solo).
|
ctype(solo).
|
||||||
ctype(symbolic_control).
|
ctype(symbolic_control).
|
||||||
ctype(symbolic_hexadecimal).
|
ctype(symbolic_hexadecimal).
|
||||||
ctype(to_lower(_)).
|
ctype(lower(_)).
|
||||||
ctype(to_upper(_)).
|
ctype(upper(_)).
|
||||||
ctype(upper).
|
ctype(upper).
|
||||||
ctype(whitespace).
|
ctype(whitespace).
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ reinforce_goal(Goal0, Goal) :-
|
|||||||
term_variables(Goal0, Vars),
|
term_variables(Goal0, Vars),
|
||||||
dif:vars_remove_goal(Vars, Goal0),
|
dif:vars_remove_goal(Vars, Goal0),
|
||||||
Goal0 = (L \== R),
|
Goal0 = (L \== R),
|
||||||
dif(L, R)
|
dif:dif(L, R)
|
||||||
).
|
).
|
||||||
|
|
||||||
append_goals([], _).
|
append_goals([], _).
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use dashu::base::{Abs, Gcd, UnsignedAbs};
|
use dashu::base::{Abs, Gcd, Signed, UnsignedAbs};
|
||||||
use dashu::integer::IBig;
|
use dashu::integer::IBig;
|
||||||
use dashu::integer::fast_div::ConstDivisor;
|
use dashu::integer::fast_div::ConstDivisor;
|
||||||
use divrem::*;
|
use divrem::*;
|
||||||
@@ -850,13 +850,18 @@ pub(crate) fn modulus(x: Number, y: Number, arena: &mut Arena) -> Result<Number,
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn ibig_rem_floor(n1: &Integer, n2: &Integer) -> Integer {
|
fn ibig_rem_floor(n1: &Integer, n2: &Integer) -> 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
|
|
||||||
} else {
|
|
||||||
let ring = ConstDivisor::new(n2.unsigned_abs());
|
let ring = ConstDivisor::new(n2.unsigned_abs());
|
||||||
let n1 = n1.clone();
|
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 {
|
||||||
IBig::from(ring.reduce(n1).residue())
|
IBig::from(ring.reduce(n1).residue())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1048,55 +1053,6 @@ pub(crate) fn atan(n1: Number) -> Result<f64, MachineStubGen> {
|
|||||||
unary_float_fn_template(n1, |f| f.atan())
|
unary_float_fn_template(n1, |f| f.atan())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn asinh(n1: Number) -> Result<f64, MachineStubGen> {
|
|
||||||
unary_float_fn_template(n1, |f| f.asinh())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn acosh(n1: Number) -> Result<f64, MachineStubGen> {
|
|
||||||
unary_float_fn_template(n1, |f| f.acosh())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn atanh(n1: Number) -> Result<f64, MachineStubGen> {
|
|
||||||
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<f64, MachineStubGen> {
|
|
||||||
unary_float_fn_template(n1, |f| f.sinh())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn cosh(n1: Number) -> Result<f64, MachineStubGen> {
|
|
||||||
unary_float_fn_template(n1, |f| f.cosh())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn tanh(n1: Number) -> Result<f64, MachineStubGen> {
|
|
||||||
unary_float_fn_template(n1, |f| f.tanh())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn log10(n1: Number) -> Result<f64, MachineStubGen> {
|
|
||||||
unary_float_fn_template(n1, |f| f.log(10f64))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn float_fractional_part(n1: Number) -> Result<f64, MachineStubGen> {
|
pub(crate) fn float_fractional_part(n1: Number) -> Result<f64, MachineStubGen> {
|
||||||
unary_float_fn_template(n1, |f| f.fract())
|
unary_float_fn_template(n1, |f| f.fract())
|
||||||
@@ -1361,27 +1317,6 @@ impl MachineState {
|
|||||||
atom!("tan") => self.interms.push(Number::Float(OrderedFloat(
|
atom!("tan") => self.interms.push(Number::Float(OrderedFloat(
|
||||||
drop_iter_on_err!(self, iter, tan(a1))
|
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(
|
atom!("float_fractional_part") => self.interms.push(Number::Float(OrderedFloat(
|
||||||
drop_iter_on_err!(self, iter, float_fractional_part(a1))
|
drop_iter_on_err!(self, iter, float_fractional_part(a1))
|
||||||
))),
|
))),
|
||||||
|
|||||||
429
src/machine/cycle_detection.rs
Normal file
429
src/machine/cycle_detection.rs
Normal file
@@ -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<usize> {
|
||||||
|
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<HeapCellValue> {
|
||||||
|
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::Item> {
|
||||||
|
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() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -154,8 +154,11 @@ enum TraversalState {
|
|||||||
Fail,
|
Fail,
|
||||||
GetCutPoint { var_num: usize, prev_b: bool },
|
GetCutPoint { var_num: usize, prev_b: bool },
|
||||||
Cut { var_num: usize, is_global: bool },
|
Cut { var_num: usize, is_global: bool },
|
||||||
|
CutPrev(usize),
|
||||||
ResetCallPolicy(CallPolicy),
|
ResetCallPolicy(CallPolicy),
|
||||||
Term(Term),
|
Term(Term),
|
||||||
|
OverrideGlobalCutVar(usize),
|
||||||
|
ResetGlobalCutVarOverride(Option<usize>),
|
||||||
RemoveBranchNum, // pop the current_branch_num and from the root set.
|
RemoveBranchNum, // pop the current_branch_num and from the root set.
|
||||||
AddBranchNum(BranchNumber), // set current_branch_num, add it to 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
|
RepBranchNum(BranchNumber), // replace current_branch_num and the latest in the root set
|
||||||
@@ -171,6 +174,7 @@ pub struct VariableClassifier {
|
|||||||
var_num: usize,
|
var_num: usize,
|
||||||
root_set: RootSet,
|
root_set: RootSet,
|
||||||
global_cut_var_num: Option<usize>,
|
global_cut_var_num: Option<usize>,
|
||||||
|
global_cut_var_num_override: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
@@ -252,6 +256,7 @@ impl VariableClassifier {
|
|||||||
root_set: RootSet::new(),
|
root_set: RootSet::new(),
|
||||||
var_num: 0,
|
var_num: 0,
|
||||||
global_cut_var_num: None,
|
global_cut_var_num: None,
|
||||||
|
global_cut_var_num_override: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -517,6 +522,12 @@ impl VariableClassifier {
|
|||||||
self.probe_in_situ_var(var_num);
|
self.probe_in_situ_var(var_num);
|
||||||
build_stack.push_chunk_term(QueryTerm::GetCutPoint { var_num, prev_b });
|
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 } => {
|
TraversalState::Cut { var_num, is_global } => {
|
||||||
if self.try_set_chunk_at_inlined_boundary() {
|
if self.try_set_chunk_at_inlined_boundary() {
|
||||||
build_stack.add_chunk();
|
build_stack.add_chunk();
|
||||||
@@ -527,9 +538,18 @@ impl VariableClassifier {
|
|||||||
build_stack.push_chunk_term(if is_global {
|
build_stack.push_chunk_term(if is_global {
|
||||||
QueryTerm::GlobalCut(var_num)
|
QueryTerm::GlobalCut(var_num)
|
||||||
} else {
|
} 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 => {
|
TraversalState::Fail => {
|
||||||
build_stack.push_chunk_term(QueryTerm::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::BuildDisjunct(build_stack_len));
|
||||||
state_stack.push(TraversalState::Fail);
|
state_stack.push(TraversalState::Fail);
|
||||||
state_stack.push(TraversalState::Cut {
|
state_stack.push(TraversalState::CutPrev(self.var_num));
|
||||||
var_num: self.var_num,
|
state_stack.push(TraversalState::ResetGlobalCutVarOverride(self.global_cut_var_num_override));
|
||||||
is_global: false,
|
|
||||||
});
|
|
||||||
state_stack.push(TraversalState::Term(not_term));
|
state_stack.push(TraversalState::Term(not_term));
|
||||||
|
state_stack.push(TraversalState::OverrideGlobalCutVar(self.var_num));
|
||||||
state_stack.push(TraversalState::GetCutPoint {
|
state_stack.push(TraversalState::GetCutPoint {
|
||||||
var_num: self.var_num,
|
var_num: self.var_num,
|
||||||
prev_b: true,
|
prev_b: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
self.current_chunk_type = ChunkType::Mid;
|
self.current_chunk_type = ChunkType::Mid;
|
||||||
@@ -786,17 +805,23 @@ impl VariableClassifier {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
Term::Literal(_, Literal::Atom(atom!("!")) | Literal::Char('!')) => {
|
Term::Literal(_, Literal::Atom(atom!("!")) | Literal::Char('!')) => {
|
||||||
if self.global_cut_var_num.is_none() {
|
let (var_num, is_global) =
|
||||||
self.global_cut_var_num = Some(self.var_num);
|
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.global_cut_var_num = Some(var_num);
|
||||||
self.var_num += 1;
|
self.var_num += 1;
|
||||||
}
|
|
||||||
|
|
||||||
self.probe_in_situ_var(self.global_cut_var_num.unwrap());
|
(var_num, true)
|
||||||
|
};
|
||||||
|
|
||||||
state_stack.push(TraversalState::Cut {
|
self.probe_in_situ_var(var_num);
|
||||||
var_num: self.global_cut_var_num.unwrap(),
|
|
||||||
is_global: true,
|
state_stack.push(TraversalState::Cut { var_num, is_global });
|
||||||
});
|
|
||||||
}
|
}
|
||||||
Term::Literal(_, Literal::Atom(name)) => {
|
Term::Literal(_, Literal::Atom(name)) => {
|
||||||
if update_chunk_data(self, name, 0) {
|
if update_chunk_data(self, name, 0) {
|
||||||
|
|||||||
@@ -917,69 +917,6 @@ impl Machine {
|
|||||||
|
|
||||||
self.machine_st.p += 1;
|
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) => {
|
&Instruction::Float(ref a1, t) => {
|
||||||
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1));
|
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;
|
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) => {
|
&Instruction::Allocate(num_cells) => {
|
||||||
self.machine_st.allocate(num_cells);
|
self.machine_st.allocate(num_cells);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,16 +10,13 @@ pub(crate) trait UnmarkPolicy {
|
|||||||
where
|
where
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
fn invert_marker(iter: &mut StacklessPreOrderHeapIter<Self>) where Self: Sized;
|
fn invert_marker(iter: &mut StacklessPreOrderHeapIter<Self>) where Self: Sized;
|
||||||
fn cycle_detected(&mut self) where Self: Sized;
|
|
||||||
fn mark_phase(&self) -> bool;
|
fn mark_phase(&self) -> bool;
|
||||||
fn var_rooted_cycle(_iter: &mut StacklessPreOrderHeapIter<Self>, _next: usize)
|
#[inline]
|
||||||
where
|
fn report_var_link(iter: &StacklessPreOrderHeapIter<Self>) -> bool where Self: Sized {
|
||||||
Self: Sized {}
|
iter.heap[iter.next as usize].get_mark_bit() == iter.iter_state.mark_phase()
|
||||||
fn detect_list_tail_cycle(_iter: &mut StacklessPreOrderHeapIter<Self>) where Self: Sized {}
|
}
|
||||||
fn list_head_cycle_detecting_backward(
|
#[inline(always)]
|
||||||
iter: &mut StacklessPreOrderHeapIter<Self>,
|
fn record_focus(_iter: &mut StacklessPreOrderHeapIter<Self>) where Self: Sized {
|
||||||
) -> bool where Self: Sized {
|
|
||||||
iter.backward()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,65 +49,12 @@ impl UnmarkPolicy for IteratorUMP {
|
|||||||
invert_marker(iter);
|
invert_marker(iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn cycle_detected(&mut self) {}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn mark_phase(&self) -> bool {
|
fn mark_phase(&self) -> bool {
|
||||||
self.mark_phase
|
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<Self>) -> Option<HeapCellValue> {
|
|
||||||
iter.forward_var()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn invert_marker(iter: &mut StacklessPreOrderHeapIter<Self>) {
|
|
||||||
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<Self>,
|
|
||||||
) -> 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<Self>) {
|
|
||||||
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<Self>, 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 {}
|
struct MarkerUMP {}
|
||||||
|
|
||||||
impl UnmarkPolicy for MarkerUMP {
|
impl UnmarkPolicy for MarkerUMP {
|
||||||
@@ -138,9 +82,6 @@ impl UnmarkPolicy for MarkerUMP {
|
|||||||
fn mark_phase(&self) -> bool {
|
fn mark_phase(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn cycle_detected(&mut self) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[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> {
|
impl<'a> StacklessPreOrderHeapIter<'a, IteratorUMP> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) fn new(heap: &'a mut [HeapCellValue], start: usize) -> Self {
|
pub(crate) fn new(heap: &'a mut [HeapCellValue], start: usize) -> Self {
|
||||||
@@ -241,9 +139,7 @@ impl<'a> StacklessPreOrderHeapIter<'a, IteratorUMP> {
|
|||||||
start,
|
start,
|
||||||
current: start,
|
current: start,
|
||||||
next,
|
next,
|
||||||
iter_state: IteratorUMP {
|
iter_state: IteratorUMP { mark_phase: true,},
|
||||||
mark_phase: true,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -278,78 +174,44 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> {
|
|||||||
None
|
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<HeapCellValue> {
|
fn forward(&mut self) -> Option<HeapCellValue> {
|
||||||
loop {
|
loop {
|
||||||
if self.heap[self.current].get_mark_bit() != self.iter_state.mark_phase() {
|
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());
|
self.heap[self.current].set_mark_bit(self.iter_state.mark_phase());
|
||||||
|
|
||||||
|
UMP::record_focus(self);
|
||||||
|
|
||||||
match self.heap[self.current].get_tag() {
|
match self.heap[self.current].get_tag() {
|
||||||
HeapCellValueTag::AttrVar => {
|
HeapCellValueTag::AttrVar => {
|
||||||
let next = self.next;
|
let next = self.next as usize;
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(cell) = UMP::forward_attr_var(self) {
|
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);
|
return Some(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.next < self.heap.len() as u64 {
|
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;
|
let tag = HeapCellValueTag::AttrVar;
|
||||||
return Some(HeapCellValue::build_with(tag, next));
|
return Some(HeapCellValue::build_with(tag, next as u64));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
HeapCellValueTag::Var => {
|
HeapCellValueTag::Var => {
|
||||||
let next = self.next;
|
let next = self.next as usize;
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(cell) = self.forward_var() {
|
if let Some(cell) = self.forward_var() {
|
||||||
if self.is_cyclic(current, next as usize, f) {
|
|
||||||
self.iter_state.cycle_detected();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Some(cell);
|
return Some(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.next < self.heap.len() as u64 {
|
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;
|
let tag = HeapCellValueTag::Var;
|
||||||
return Some(HeapCellValue::build_with(tag, next));
|
return Some(HeapCellValue::build_with(tag, next as u64));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
HeapCellValueTag::Str => {
|
HeapCellValueTag::Str => {
|
||||||
if self.heap[self.next as usize + 1].get_forwarding_bit() {
|
if self.heap[self.next as usize + 1].get_forwarding_bit() {
|
||||||
self.iter_state.cycle_detected();
|
|
||||||
return Some(self.backward_and_return());
|
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;
|
let last_cell_loc = self.next as usize + 1;
|
||||||
|
|
||||||
if self.heap[last_cell_loc].get_forwarding_bit() {
|
if self.heap[last_cell_loc].get_forwarding_bit() {
|
||||||
self.iter_state.cycle_detected();
|
|
||||||
return Some(self.backward_and_return());
|
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);
|
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));
|
return Some(list_loc_as_cell!(last_cell_loc - 1));
|
||||||
}
|
}
|
||||||
HeapCellValueTag::PStrLoc => {
|
HeapCellValueTag::PStrLoc => {
|
||||||
let h = self.next as usize;
|
let h = self.next as usize;
|
||||||
|
|
||||||
if self.heap[h + 1].get_forwarding_bit() {
|
if self.heap[h + 1].get_forwarding_bit() {
|
||||||
self.iter_state.cycle_detected();
|
|
||||||
return Some(self.backward_and_return());
|
return Some(self.backward_and_return());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -432,7 +273,6 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> {
|
|||||||
let last_cell_loc = h + 1;
|
let last_cell_loc = h + 1;
|
||||||
|
|
||||||
if self.heap[last_cell_loc].get_forwarding_bit() {
|
if self.heap[last_cell_loc].get_forwarding_bit() {
|
||||||
self.iter_state.cycle_detected();
|
|
||||||
return Some(self.backward_and_return());
|
return Some(self.backward_and_return());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)]
|
#[derive(Debug)]
|
||||||
|
|||||||
@@ -1133,27 +1133,21 @@ impl MachineState {
|
|||||||
pub fn is_cyclic_term(&mut self, value: HeapCellValue) -> bool {
|
pub fn is_cyclic_term(&mut self, value: HeapCellValue) -> bool {
|
||||||
let value = self.store(self.deref(value));
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let h = self.heap.len();
|
let h = self.heap.len();
|
||||||
self.heap.push(value);
|
self.heap.push(value);
|
||||||
|
|
||||||
let found_cycle = {
|
let cycle_found = {
|
||||||
let mut iter = cycle_detecting_stackless_preorder_iter(&mut self.heap, h);
|
let mut iter = cycle_detecting_stackless_preorder_iter(&mut self.heap, h);
|
||||||
|
while let Some(_) = iter.next() {}
|
||||||
while let Some(_) = iter.next() {
|
iter.cycle_found()
|
||||||
if iter.found_cycle() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
iter.found_cycle()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
self.heap.pop();
|
self.heap.pop();
|
||||||
found_cycle
|
cycle_found
|
||||||
}
|
}
|
||||||
|
|
||||||
// arg(+N, +Term, ?Arg)
|
// arg(+N, +Term, ?Arg)
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ pub use crate::machine::machine_state::*;
|
|||||||
pub use crate::machine::stack::*;
|
pub use crate::machine::stack::*;
|
||||||
pub use crate::machine::streams::*;
|
pub use crate::machine::streams::*;
|
||||||
pub use crate::machine::*;
|
pub use crate::machine::*;
|
||||||
pub use crate::macros::*;
|
|
||||||
pub use crate::parser::ast::*;
|
pub use crate::parser::ast::*;
|
||||||
use crate::read::*;
|
use crate::read::*;
|
||||||
pub use crate::types::*;
|
pub use crate::types::*;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ pub mod loader;
|
|||||||
pub mod compile;
|
pub mod compile;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod copier;
|
pub mod copier;
|
||||||
|
pub mod cycle_detection;
|
||||||
pub mod disjuncts;
|
pub mod disjuncts;
|
||||||
pub mod dispatch;
|
pub mod dispatch;
|
||||||
pub mod gc;
|
pub mod gc;
|
||||||
|
|||||||
@@ -2936,10 +2936,9 @@ impl Machine {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
self.machine_st.fail = true; // This predicate fails by default.
|
|
||||||
|
|
||||||
read_heap_cell!(a2,
|
read_heap_cell!(a2,
|
||||||
(HeapCellValueTag::Atom, (chars, _arity)) => {
|
(HeapCellValueTag::Atom, (chars, _arity)) => {
|
||||||
|
self.machine_st.fail = true; // This predicate fails by default.
|
||||||
macro_rules! macro_check {
|
macro_rules! macro_check {
|
||||||
($id:ident, $name:expr) => {
|
($id:ident, $name:expr) => {
|
||||||
if $id!(c) && chars == $name {
|
if $id!(c) && chars == $name {
|
||||||
@@ -3005,24 +3004,22 @@ impl Machine {
|
|||||||
.get_name_and_arity();
|
.get_name_and_arity();
|
||||||
|
|
||||||
match (name, arity) {
|
match (name, arity) {
|
||||||
(atom!("to_upper"), 1) => {
|
(atom!("upper"), 1) => {
|
||||||
let reg = self.machine_st.deref(self.machine_st.heap[s+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 atom = AtomTable::build_with(&self.machine_st.atom_tbl, &c.to_uppercase().to_string());
|
||||||
let upper_str = string_as_cstr_cell!(atom);
|
let upper_str = string_as_cstr_cell!(atom);
|
||||||
unify!(self.machine_st, reg, upper_str);
|
unify!(self.machine_st, reg, upper_str);
|
||||||
self.machine_st.fail = false;
|
|
||||||
}
|
}
|
||||||
(atom!("to_lower"), 1) => {
|
(atom!("lower"), 1) => {
|
||||||
let reg = self.machine_st.deref(self.machine_st.heap[s+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 atom = AtomTable::build_with(&self.machine_st.atom_tbl, &c.to_lowercase().to_string());
|
||||||
let lower_str = string_as_cstr_cell!(atom);
|
let lower_str = string_as_cstr_cell!(atom);
|
||||||
unify!(self.machine_st, reg, lower_str);
|
unify!(self.machine_st, reg, lower_str);
|
||||||
self.machine_st.fail = false;
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
:- use_module(library(dcgs)).
|
||||||
:- use_module(library(format)).
|
:- use_module(library(format)).
|
||||||
|
|
||||||
term1(A) :-
|
term1(A) :-
|
||||||
@@ -28,6 +29,16 @@ term6(A) :-
|
|||||||
A=[B|B],
|
A=[B|B],
|
||||||
B=[C|C].
|
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", (
|
test("acyclic_term_1", (
|
||||||
L = [_Y,[M,B],B|M], acyclic_term(L)
|
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)
|
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)
|
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)
|
T = [_A|T], \+ acyclic_term(T)
|
||||||
)).
|
)).
|
||||||
|
|
||||||
test("acyclic_term_15", (
|
test("acyclic_term_16", (
|
||||||
T = [T|_L], \+ acyclic_term(T)
|
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)
|
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)
|
T = [_A| [[[[L|T]|[]]]]], acyclic_term(L)
|
||||||
)).
|
)).
|
||||||
|
|
||||||
test("acyclic_term_18", (
|
test("acyclic_term_19", (
|
||||||
T = [A| [[[[_L|T]|[]]]]], acyclic_term(A)
|
T = [A| [[[[_L|T]|[]]]]], acyclic_term(A)
|
||||||
)).
|
)).
|
||||||
|
|
||||||
test("acyclic_term_19", (
|
test("acyclic_term_20", (
|
||||||
T = [_A| [[[[_L|T]|[]]]]], \+ acyclic_term(T)
|
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)
|
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)
|
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)
|
_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)
|
T = [[_A, T]], G = [1|T], \+ acyclic_term(G)
|
||||||
)).
|
)).
|
||||||
|
|
||||||
test("acyclic_term_24", (
|
test("acyclic_term_25", (
|
||||||
T = [[_A, T]], \+ acyclic_term(T)
|
T = [[_A, T]], \+ acyclic_term(T)
|
||||||
)).
|
)).
|
||||||
|
|
||||||
test("acyclic_term_25", (
|
test("acyclic_term_26", (
|
||||||
T = [[_, _], T], \+ acyclic_term(T)
|
T = [[_, _], T], \+ acyclic_term(T)
|
||||||
)).
|
)).
|
||||||
|
|
||||||
test("acyclic_term_26", (
|
test("acyclic_term_27", (
|
||||||
T = [[T, _], 1], \+ acyclic_term(T)
|
T = [[T, _], 1], \+ acyclic_term(T)
|
||||||
)).
|
)).
|
||||||
|
|
||||||
test("acyclic_term_27", (
|
test("acyclic_term_28", (
|
||||||
T = str(A,A), acyclic_term(T)
|
T = str(A,A), acyclic_term(T)
|
||||||
)).
|
)).
|
||||||
|
|
||||||
test("acyclic_term_28", (
|
test("acyclic_term_29", (
|
||||||
T = str(A,A,A), acyclic_term(T)
|
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),
|
A = s(B, d(Y)), Y = B, acyclic_term(A),
|
||||||
acyclic_term(B), acyclic_term(Y)
|
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),
|
A=str(B,B,B), C=str(A,_D,B), acyclic_term(C),
|
||||||
acyclic_term(A), acyclic_term(B)
|
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", (
|
test("acyclic_term#2111_1", (
|
||||||
term1(A), \+ acyclic_term(A)
|
term1(A), \+ acyclic_term(A)
|
||||||
)).
|
)).
|
||||||
@@ -199,6 +221,45 @@ test("acyclic_term#2121", (
|
|||||||
acyclic_term(A), acyclic_term(B)
|
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 :-
|
main :-
|
||||||
findall(test(Name, Goal), test(Name, Goal), Tests),
|
findall(test(Name, Goal), test(Name, Goal), Tests),
|
||||||
run_tests(Tests, Failed),
|
run_tests(Tests, Failed),
|
||||||
@@ -231,3 +292,15 @@ run_tests_quiet([test(Name, Goal)|Tests], Failed) :-
|
|||||||
; Failed = [Name|Failed1]
|
; Failed = [Name|Failed1]
|
||||||
),
|
),
|
||||||
run_tests_quiet(Tests, 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]).
|
||||||
|
|||||||
Reference in New Issue
Block a user