diff --git a/src/prolog/ast.rs b/src/prolog/ast.rs index b1208c0a..bc5e5620 100644 --- a/src/prolog/ast.rs +++ b/src/prolog/ast.rs @@ -537,7 +537,7 @@ impl fmt::Display for Constant { &Constant::Atom(ref atom) => write!(f, "{}", atom), &Constant::Char(c) => - write!(f, "{}", c as u8), + write!(f, "'{}'", c as u8), &Constant::EmptyList => write!(f, "[]"), &Constant::Number(ref n) => @@ -1360,13 +1360,6 @@ impl Addr { _ => true } } - - pub fn is_empty_list(&self) -> bool { - match self { - &Addr::Con(Constant::EmptyList) => true, - _ => false - } - } } impl From for Addr { diff --git a/src/prolog/builtins.rs b/src/prolog/builtins.rs index bb88d6bf..2f5fbc9c 100644 --- a/src/prolog/builtins.rs +++ b/src/prolog/builtins.rs @@ -644,6 +644,7 @@ fn get_builtins() -> Code { keysort_execute!(), // keysort/2, 484. acyclic_term_execute!(), // acyclic_term/1, 485. cyclic_term_execute!(), // cyclic_term/1, 486. + skip_max_list_execute!() // '$skip_max_list', 487. ] } @@ -763,7 +764,8 @@ pub fn build_code_and_op_dirs() -> (CodeDir, OpDir) code_dir.insert((clause_name!("keysort"), 2), CodeIndex::from((484, builtin.clone()))); code_dir.insert((clause_name!("acyclic_term"), 1), CodeIndex::from((485, builtin.clone()))); code_dir.insert((clause_name!("cyclic_term"), 1), CodeIndex::from((486, builtin.clone()))); - + code_dir.insert((clause_name!("$skip_max_list"), 4), CodeIndex::from((487, builtin.clone()))); + (code_dir, op_dir) } @@ -826,7 +828,8 @@ pub fn builtin_module() -> Module (clause_name!("sort"), 2), (clause_name!("keysort"), 2), (clause_name!("acyclic_term"), 1), - (clause_name!("cyclic_term"), 1)]); + (clause_name!("cyclic_term"), 1), + (clause_name!("$skip_max_list"), 4)]); for arity in 0 .. 63 { module_decl.exports.push((clause_name!("call"), arity)); diff --git a/src/prolog/machine/machine_state_impl.rs b/src/prolog/machine/machine_state_impl.rs index 83181aa7..5b6d11d6 100644 --- a/src/prolog/machine/machine_state_impl.rs +++ b/src/prolog/machine/machine_state_impl.rs @@ -29,9 +29,10 @@ macro_rules! try_or_fail { // used by '$skip_max_list'. enum CycleSearchResult { EmptyList, - NotList, - PartialOrProperList(usize, usize), // returns the list length (up to max), and an offset into the heap. - UntouchedList(usize) // return the offset of an uniterated Addr::Lis(offset). + NotList, + PartialList(usize, usize), // the list length (up to max), and an offset into the heap. + ProperList(usize), // the list length. + UntouchedList(usize) // the address of an uniterated Addr::Lis(address). } impl MachineState { @@ -1686,8 +1687,7 @@ impl MachineState { // detect cycles. match self.detect_cycles(usize::max_value(), a1.clone()) { - CycleSearchResult::PartialOrProperList(_, h) - if self.store(self.deref(self.heap[h].as_addr(h))).is_empty_list() => {}, + CycleSearchResult::ProperList(_) => {}, _ => return Err(functor!("type_error", 2, [heap_atom!("list"), HeapCellValue::Addr(a1)])) }; @@ -1752,7 +1752,7 @@ impl MachineState { loop { if steps == max_steps { - return CycleSearchResult::PartialOrProperList(steps, hare); + return CycleSearchResult::PartialList(steps, hare); } match self.heap[hare].clone() { @@ -1768,13 +1768,8 @@ impl MachineState { } }, HeapCellValue::Addr(Addr::Con(Constant::EmptyList)) => - return CycleSearchResult::PartialOrProperList(steps, hare), - HeapCellValue::Addr(Addr::HeapCell(hc)) if hc == hare => - return CycleSearchResult::PartialOrProperList(steps, hare), - HeapCellValue::Addr(ref sc @ Addr::StackCell(..)) - if *sc == self.store(self.deref(sc.clone())) => - return CycleSearchResult::PartialOrProperList(steps, hare), - _ => return CycleSearchResult::NotList + return CycleSearchResult::ProperList(steps), + _ => return CycleSearchResult::PartialList(steps, hare) } } } @@ -1788,17 +1783,7 @@ impl MachineState { self.unify(addr, xs); } } - -/* - '$skip_max_list'(N, Max, Xs0, Xs): - valid modes: Max is always +Max (a non-negative integer), Xs, Xs0 and N are all ?_. - - Modes | Conditions for success - ====================================================================================== - ?N, -Xs0 : N = 0, Xs = Xs0. - ?N, +Xs0 : Xs0 is a proper or partial list, Xs0 = [X1, X2, ..., XN | Xs], N = Max, - if |Xs0| >= Max, or, Xs = [] and N = |Xs0|. -*/ + pub(super) fn skip_max_list(&mut self) { let max = self.store(self.deref(self[temp_v!(2)].clone())); @@ -1822,10 +1807,14 @@ impl MachineState { self.finalize_skip_max_list(0, Addr::Lis(l)), CycleSearchResult::EmptyList => self.finalize_skip_max_list(0, Addr::Con(Constant::EmptyList)), - CycleSearchResult::PartialOrProperList(n, hc) => + CycleSearchResult::PartialList(n, hc) => self.finalize_skip_max_list(n, Addr::HeapCell(hc)), - CycleSearchResult::NotList => - self.fail = true + CycleSearchResult::ProperList(n) => + self.finalize_skip_max_list(n, Addr::Con(Constant::EmptyList)), + CycleSearchResult::NotList => { + let xs0 = self[temp_v!(3)].clone(); + self.finalize_skip_max_list(0, xs0); + } } } } diff --git a/src/prolog/macros.rs b/src/prolog/macros.rs index 799f666d..2cd018bc 100644 --- a/src/prolog/macros.rs +++ b/src/prolog/macros.rs @@ -763,6 +763,12 @@ macro_rules! cyclic_term_execute { ) } +macro_rules! skip_max_list_execute { + () => ( + Line::Control(ControlInstruction::CallClause(ClauseType::SkipMaxList, 4, 0, true)) + ) +} + macro_rules! return_from_clause { ($lco:expr, $machine_st:expr) => {{ if $lco { diff --git a/src/tests.rs b/src/tests.rs index b970af2a..4066300e 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1644,4 +1644,8 @@ fn test_queries_on_skip_max_list() { assert_prolog_failure!(&mut wam, "?- Xs = [a,b|Xs], '$skip_max_list'(3, 5, X, Xs0)."); assert_prolog_failure!(&mut wam, "?- X = [a,b|Y], Y = [c,d|X], '$skip_max_list'(4, 5, X, Xs0)."); assert_prolog_failure!(&mut wam, "?- X = [a,b|Y], Y = [c,d|X], '$skip_max_list'(4, 3, X, Xs0)."); + + // tests on non lists. + assert_prolog_success!(&mut wam, "?- '$skip_max_list'(N, 9, non_list, Xs).", + [["Xs = non_list", "N = 0"]]); }