Merge branch 'master' into library-use-case

This commit is contained in:
Nicolas Luck
2023-11-02 11:12:54 +01:00
21 changed files with 787 additions and 549 deletions

View File

@@ -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

View File

@@ -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)
}

0
log.txt Normal file
View File

View File

@@ -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)),

View File

@@ -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 {

View File

@@ -202,7 +202,7 @@ pub enum QueryTerm {
// register, clause type, subterms, clause call policy.
Clause(Cell<RegType>, ClauseType, Vec<Term>, 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

View File

@@ -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<HeapCellValue>,
pub(crate) fn cycle_detecting_stackless_preorder_iter<'a>(
heap: &'a mut [HeapCellValue],
start: usize,
) -> StacklessPreOrderHeapIter<IteratorUMP> {
StacklessPreOrderHeapIter::<IteratorUMP>::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<CycleDetectorUMP> {
StacklessPreOrderHeapIter::<CycleDetectorUMP>::new(heap, start)
}
#[inline(always)]
pub(crate) fn stackful_preorder_iter<'a, ElideLists: ListElisionPolicy>(
heap: &'a mut Vec<HeapCellValue>,
@@ -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<StacklessPreOrderHeapIter<'a, IteratorUMP>>;
#[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<StacklessPreOrderHeapIter<'a, IteratorUMP>>;
#[inline(always)]
pub(crate) fn stackless_preorder_iter(
heap: &mut Vec<HeapCellValue>,
start: usize,
) -> StacklessPreOrderHeapIter<IteratorUMP> {
StacklessPreOrderHeapIter::<IteratorUMP>::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);

View File

@@ -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,

View File

@@ -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).

View File

@@ -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([], _).

View File

@@ -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<Number,
};
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
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<f64, MachineStubGen> {
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]
pub(crate) fn float_fractional_part(n1: Number) -> Result<f64, MachineStubGen> {
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))
))),

View 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() {}
}
}

View File

@@ -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<usize>),
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<usize>,
global_cut_var_num_override: Option<usize>,
}
#[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) {

View File

@@ -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);
}

View File

@@ -10,16 +10,13 @@ pub(crate) trait UnmarkPolicy {
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 var_rooted_cycle(_iter: &mut StacklessPreOrderHeapIter<Self>, _next: usize)
where
Self: Sized {}
fn detect_list_tail_cycle(_iter: &mut StacklessPreOrderHeapIter<Self>) where Self: Sized {}
fn list_head_cycle_detecting_backward(
iter: &mut StacklessPreOrderHeapIter<Self>,
) -> bool where Self: Sized {
iter.backward()
#[inline]
fn report_var_link(iter: &StacklessPreOrderHeapIter<Self>) -> 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<Self>) 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<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 {}
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<HeapCellValue> {
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());
}

View File

@@ -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)]

View File

@@ -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)

View File

@@ -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::*;

View File

@@ -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;

View File

@@ -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!()
}
);

View File

@@ -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]).