From 0457b38e41918d0833e89f715d56d28329281483 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Wed, 19 Feb 2020 21:34:09 -0700 Subject: [PATCH] add redone partial strings (#24, #95) --- src/prolog/clause_types.rs | 11 +- src/prolog/heap_iter.rs | 62 ++- src/prolog/heap_print.rs | 23 +- src/prolog/lib/builtins.pl | 2 +- src/prolog/lib/non_iso.pl | 21 +- src/prolog/machine/copier.rs | 187 +++++-- src/prolog/machine/heap.rs | 92 +++- src/prolog/machine/machine_errors.rs | 11 +- src/prolog/machine/machine_indices.rs | 96 +++- src/prolog/machine/machine_state.rs | 26 +- src/prolog/machine/machine_state_impl.rs | 597 +++++++++++++++++++---- src/prolog/machine/mod.rs | 1 + src/prolog/machine/partial_string.rs | 161 ++++++ src/prolog/machine/raw_block.rs | 30 +- src/prolog/machine/system_calls.rs | 383 +++++++++++---- src/prolog/write.rs | 5 + 16 files changed, 1420 insertions(+), 288 deletions(-) create mode 100644 src/prolog/machine/partial_string.rs diff --git a/src/prolog/clause_types.rs b/src/prolog/clause_types.rs index 7d61cab7b..6f7a9187a 100644 --- a/src/prolog/clause_types.rs +++ b/src/prolog/clause_types.rs @@ -171,6 +171,7 @@ pub enum SystemClauseType { CopyTermWithoutAttrVars, CheckCutPoint, CopyToLiftedHeap, + CreatePartialString, DeleteAttribute, DeleteHeadAttribute, DynamicModuleResolution(usize), @@ -194,6 +195,7 @@ pub enum SystemClauseType { GetModuleClause, GetNextDBRef, GetNextOpDBRef, + IsPartialString, LookupDBRef, LookupOpDBRef, Halt, @@ -215,6 +217,7 @@ pub enum SystemClauseType { NumberToChars, NumberToCodes, OpDeclaration, + PartialStringTail, PointsToContinuationResetMarker, REPL(REPLCodePtr), ReadQueryTerm, @@ -275,11 +278,12 @@ impl SystemClauseType { &SystemClauseType::CallContinuation => clause_name!("$call_continuation"), &SystemClauseType::CharCode => clause_name!("$char_code"), &SystemClauseType::CharsToNumber => clause_name!("$chars_to_number"), + &SystemClauseType::CheckCutPoint => clause_name!("$check_cp"), &SystemClauseType::ClearAttributeGoals => clause_name!("$clear_attribute_goals"), &SystemClauseType::CloneAttributeGoals => clause_name!("$clone_attribute_goals"), &SystemClauseType::CodesToNumber => clause_name!("$codes_to_number"), &SystemClauseType::CopyTermWithoutAttrVars => clause_name!("$copy_term_without_attr_vars"), - &SystemClauseType::CheckCutPoint => clause_name!("$check_cp"), + &SystemClauseType::CreatePartialString => clause_name!("$create_partial_string"), &SystemClauseType::REPL(REPLCodePtr::CompileBatch) => clause_name!("$compile_batch"), &SystemClauseType::REPL(REPLCodePtr::UseModule) => clause_name!("$use_module"), &SystemClauseType::REPL(REPLCodePtr::UseQualifiedModule) => { @@ -339,6 +343,8 @@ impl SystemClauseType { &SystemClauseType::InstallInferenceCounter => { clause_name!("$install_inference_counter") } + &SystemClauseType::IsPartialString => clause_name!("$is_partial_string"), + &SystemClauseType::PartialStringTail => clause_name!("$partial_string_tail"), &SystemClauseType::LiftedHeapLength => clause_name!("$lh_length"), &SystemClauseType::Maybe => clause_name!("maybe"), &SystemClauseType::ModuleAssertDynamicPredicateToFront => { @@ -423,6 +429,7 @@ impl SystemClauseType { ("$clone_attribute_goals", 1) => Some(SystemClauseType::CloneAttributeGoals), ("$codes_to_number", 2) => Some(SystemClauseType::CodesToNumber), ("$copy_term_without_attr_vars", 2) => Some(SystemClauseType::CopyTermWithoutAttrVars), + ("$create_partial_string", 3) => Some(SystemClauseType::CreatePartialString), ("$check_cp", 1) => Some(SystemClauseType::CheckCutPoint), ("$compile_batch", 0) => Some(SystemClauseType::REPL(REPLCodePtr::CompileBatch)), ("$copy_to_lh", 2) => Some(SystemClauseType::CopyToLiftedHeap), @@ -435,6 +442,8 @@ impl SystemClauseType { ("$module_call", _) => Some(SystemClauseType::DynamicModuleResolution(arity - 2)), ("$enqueue_attribute_goal", 1) => Some(SystemClauseType::EnqueueAttributeGoal), ("$enqueue_attr_var", 1) => Some(SystemClauseType::EnqueueAttributedVar), + ("$partial_string_tail", 2) => Some(SystemClauseType::PartialStringTail), + ("$is_partial_string", 1) => Some(SystemClauseType::IsPartialString), ("$expand_term", 2) => Some(SystemClauseType::ExpandTerm), ("$expand_goal", 2) => Some(SystemClauseType::ExpandGoal), ("$fetch_attribute_goals", 1) => Some(SystemClauseType::FetchAttributeGoals), diff --git a/src/prolog/heap_iter.rs b/src/prolog/heap_iter.rs index 13f2b3557..ca0c25a19 100644 --- a/src/prolog/heap_iter.rs +++ b/src/prolog/heap_iter.rs @@ -28,14 +28,19 @@ impl<'a> HCPreOrderIterator<'a> { fn follow_heap(&mut self, h: usize) -> Addr { match &self.machine_st.heap[h] { - &HeapCellValue::NamedStr(arity, _, _) => { + HeapCellValue::NamedStr(arity, _, _) => { for idx in (1..arity + 1).rev() { self.state_stack.push(Addr::HeapCell(h + idx)); } - Addr::HeapCell(h) + Addr::Str(h) + } + HeapCellValue::Addr(ref a) => { + self.follow(a.clone()) + } + HeapCellValue::PartialString(_) => { + self.follow(Addr::PStrLocation(h, 0)) } - &HeapCellValue::Addr(ref a) => self.follow(a.clone()), } } @@ -51,7 +56,7 @@ impl<'a> HCPreOrderIterator<'a> { if s.len() > n { if let Some(c) = s[n ..].chars().next() { let o = c.len_utf8(); - + self.state_stack.push(Addr::Con(Constant::String(n+o, s.clone()))); if self.machine_st.machine_flags().double_quotes.is_codes() { @@ -76,7 +81,28 @@ impl<'a> HCPreOrderIterator<'a> { da } - Addr::AttrVar(_) | Addr::HeapCell(_) | Addr::StackCell(_, _) => { + Addr::PStrLocation(h, n) => { + if let HeapCellValue::PartialString(ref pstr) = &self.machine_st.heap[h] { + let s = pstr.block_as_str(); + + if let Some(c) = s[n ..].chars().next() { + if pstr.len() > n + c.len_utf8() { + self.state_stack.push(Addr::PStrLocation(h, n + c.len_utf8())); + } else { + self.state_stack.push(Addr::PStrTail(h, n + c.len_utf8())); + } + + self.state_stack.push(Addr::Con(Constant::Char(c))); + } else { + unreachable!() + } + } else { + unreachable!() + } + + Addr::PStrLocation(h, n) + } + Addr::AttrVar(_) | Addr::HeapCell(_) | Addr::StackCell(_, _) | Addr::PStrTail(..) => { da } Addr::Str(s) => { @@ -92,7 +118,31 @@ impl<'a> Iterator for HCPreOrderIterator<'a> { fn next(&mut self) -> Option { self.state_stack.pop().map(|a| match self.follow(a) { Addr::HeapCell(h) => { - self.machine_st.heap[h].clone() + HeapCellValue::Addr(self.machine_st.heap[h].as_addr(h)) + } + Addr::Str(s) => { + match &self.machine_st.heap[s] { + val @ HeapCellValue::NamedStr(..) => { + val.clone() + } + _ => { + unreachable!() + } + } + } + Addr::PStrTail(h, n) => { + match &self.machine_st.heap[h] { + HeapCellValue::PartialString(ref pstr) => { + if pstr.len() > n { + HeapCellValue::Addr(Addr::PStrLocation(h, n)) + } else { + HeapCellValue::Addr(pstr.tail_addr().clone()) + } + } + _ => { + unreachable!() + } + } } Addr::StackCell(fr, sc) => { HeapCellValue::Addr(self.machine_st.stack.index_and_frame(fr)[sc].clone()) diff --git a/src/prolog/heap_print.rs b/src/prolog/heap_print.rs index a89bbd4a4..407f57a38 100644 --- a/src/prolog/heap_print.rs +++ b/src/prolog/heap_print.rs @@ -616,10 +616,18 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } match addr { - Addr::AttrVar(h) => Some(format!("_{}", h)), - Addr::HeapCell(h) | Addr::Lis(h) | Addr::Str(h) => Some(format!("_{}", h)), - Addr::StackCell(fr, sc) => Some(format!("_s_{}_{}", fr, sc)), - _ => None, + Addr::AttrVar(h) => { + Some(format!("_{}", h)) + } + Addr::HeapCell(h) | Addr::Lis(h) | Addr::Str(h) | Addr::PStrTail(h, _) => { + Some(format!("_{}", h)) + } + Addr::StackCell(fr, sc) => { + Some(format!("_s_{}_{}", fr, sc)) + } + _ => { + None + } } } @@ -944,7 +952,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } } HeapCellValue::Addr(Addr::Con(c)) => self.print_constant(c, &op), - HeapCellValue::Addr(Addr::Lis(_)) => { + HeapCellValue::Addr(Addr::Lis(_)) | HeapCellValue::Addr(Addr::PStrLocation(..)) => { if self.ignore_ops { self.format_struct(2, clause_name!(".")); } else { @@ -958,6 +966,11 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { }) } } + _ => { + // This is the partial string case. We never clone a partial string + // for printing purposes, so.. this. + unreachable!() + } } } diff --git a/src/prolog/lib/builtins.pl b/src/prolog/lib/builtins.pl index 5092d4c56..5ff87d073 100644 --- a/src/prolog/lib/builtins.pl +++ b/src/prolog/lib/builtins.pl @@ -236,7 +236,7 @@ :- non_counted_backtracking univ_errors/3. univ_errors(Term, List, N) :- '$skip_max_list'(N, -1, List, R), - ( var(R) -> + ( var(R) -> ( var(Term), throw(error(instantiation_error, (=..)/2)) % 8.5.3.3 a) ; true ) diff --git a/src/prolog/lib/non_iso.pl b/src/prolog/lib/non_iso.pl index 136e1e3a5..6c35f7793 100644 --- a/src/prolog/lib/non_iso.pl +++ b/src/prolog/lib/non_iso.pl @@ -5,7 +5,9 @@ :- module(non_iso, [bb_b_put/2, bb_get/2, bb_put/2, call_cleanup/2, call_with_inference_limit/3, forall/2, maybe/0, - set_random/1, setup_call_cleanup/3, variant/2]). + partial_string/1, partial_string/3, + partial_string_tail/2, set_random/1, + setup_call_cleanup/3, variant/2]). forall(Generate, Test) :- \+ (Generate, \+ Test). @@ -154,3 +156,20 @@ ) ; throw(error(instantiation_error, set_random/1)) ). + +partial_string(String, L, L0) :- + ( String == [] -> throw(error(type_error(list, []), partial_string/3)) + ; catch(atom_chars(Atom, String), + error(E, _), + throw(error(E, partial_string/3))) + ), + '$create_partial_string'(Atom, L, L0). + +partial_string(String) :- + '$is_partial_string'(String). + +partial_string_tail(String, Tail) :- + ( partial_string(String) -> + '$partial_string_tail'(String, Tail) + ; throw(error(type_error(partial_string, String), partial_string_tail/2)) + ). diff --git a/src/prolog/machine/copier.rs b/src/prolog/machine/copier.rs index 8fe112662..c95208480 100644 --- a/src/prolog/machine/copier.rs +++ b/src/prolog/machine/copier.rs @@ -13,9 +13,9 @@ pub enum AttrVarPolicy { pub(crate) trait CopierTarget: IndexMut { fn threshold(&self) -> usize; - fn push(&mut self, _: HeapCellValue); - fn store(&self, _: Addr) -> Addr; - fn deref(&self, _: Addr) -> Addr; + fn push(&mut self, val: HeapCellValue); + fn store(&self, val: Addr) -> Addr; + fn deref(&self, val: Addr) -> Addr; fn stack(&mut self) -> &mut Stack; } @@ -51,10 +51,10 @@ impl CopyTermState { } fn copied_list(&mut self, addr: usize) -> bool { - match self.target[addr].clone() { + match &self.target[addr] { HeapCellValue::Addr(Addr::Lis(addr)) | HeapCellValue::Addr(Addr::HeapCell(addr)) => { - if addr >= self.old_h { - *self.value_at_scan() = HeapCellValue::Addr(Addr::Lis(addr)); + if *addr >= self.old_h { + *self.value_at_scan() = HeapCellValue::Addr(Addr::Lis(*addr)); self.scan += 1; return true; } @@ -65,34 +65,52 @@ impl CopyTermState { false } + fn copied_partial_string(&mut self, addr: usize) -> bool { + if let HeapCellValue::PartialString(ref pstr) = &self.target[addr] { + if let Addr::PStrLocation(h, n) = pstr.tail_addr() { + if *h >= self.old_h { + *self.value_at_scan() = HeapCellValue::Addr(Addr::PStrLocation(*h, *n)); + self.scan += 1; + return true; + } + } + } + + false + } + fn copy_list(&mut self, addr: usize) { if self.copied_list(addr) { return; } let threshold = self.target.threshold(); + *self.value_at_scan() = HeapCellValue::Addr(Addr::Lis(threshold)); - let hcv = self.target[addr].clone(); + let ra = self.target[addr].as_addr(threshold); + let rd = self.target.store(self.target.deref(ra.clone())); - let ra = hcv.as_addr(threshold); - let rd = self.target.store(self.target.deref(ra)); + self.target.push(HeapCellValue::Addr(ra.clone())); + + let hcv = HeapCellValue::Addr(self.target[addr + 1].as_addr(addr + 1)); - self.target.push(hcv); - - let hcv = self.target[addr + 1].clone(); self.target.push(hcv); match rd.clone() { - Addr::AttrVar(h) | Addr::HeapCell(h) if h >= self.old_h => { - self.target[threshold] = HeapCellValue::Addr(rd) - } - ra @ Addr::AttrVar(_) | ra @ Addr::HeapCell(..) | ra @ Addr::StackCell(..) => { + Addr::AttrVar(h) | Addr::HeapCell(h) | Addr::PStrTail(h, _) + if h >= self.old_h => { + self.target[threshold] = HeapCellValue::Addr(rd) + } + var @ Addr::AttrVar(_) + | var @ Addr::HeapCell(..) + | var @ Addr::StackCell(..) + | var @ Addr::PStrTail(..) => { if ra == rd { - self.reinstantiate_var(ra, threshold); + self.reinstantiate_var(var, threshold); if let AttrVarPolicy::StripAttributes = self.attr_var_policy { - self.trail.push((Ref::HeapCell(addr), self.target[addr].clone())); + self.trail.push((Ref::HeapCell(addr), HeapCellValue::Addr(ra))); self.target[addr] = HeapCellValue::Addr(Addr::HeapCell(threshold)); } } else { @@ -100,7 +118,11 @@ impl CopyTermState { } } _ => { - self.trail.push((Ref::HeapCell(addr), self.target[addr].clone())); + self.trail.push(( + Ref::HeapCell(addr), + HeapCellValue::Addr(self.target[addr].as_addr(addr)), + )); + self.target[addr] = HeapCellValue::Addr(Addr::Lis(threshold)) } }; @@ -108,6 +130,74 @@ impl CopyTermState { self.scan += 1; } + + fn copy_partial_string(&mut self, addr: usize, n: usize) { + let threshold = self.target.threshold(); + + let tail_addr = + match &self.target[addr] { + HeapCellValue::PartialString(ref pstr) => { + self.trail.push(( + Ref::PStrTail(addr, 0), + HeapCellValue::Addr(pstr.tail.clone()), + )); + + self.target.store(self.target.deref(pstr.tail.clone())) + } + _ => { + unreachable!() + } + }; + + let pstr = + match &mut self.target[addr] { + HeapCellValue::PartialString(ref mut pstr) => { + let mut new_pstr = pstr.clone_from_offset(n); + + if let Addr::PStrTail(h, n) = &tail_addr { + new_pstr.tail = if *h == addr { + Addr::PStrTail(threshold, *n) + } else { + Addr::HeapCell(threshold + 1) + }; + } else { + new_pstr.tail = Addr::HeapCell(threshold + 1); + } + + pstr.tail = Addr::PStrLocation(threshold, 0); + new_pstr + } + _ => { + unreachable!() + } + }; + + match tail_addr { + Addr::PStrTail(h, _) if h == addr => { + self.target.push(HeapCellValue::PartialString(pstr)); + } + addr => { + self.target.push(HeapCellValue::PartialString(pstr)); + self.target.push(HeapCellValue::Addr(addr)); + } + } + } + + fn copy_partial_string_from(&mut self, addr: usize, n: usize) { + if self.copied_partial_string(addr) { + return; + } + + let threshold = self.target.threshold(); + + self.target[self.scan] = + HeapCellValue::Addr(Addr::PStrLocation(threshold, n)); + + self.scan += 1; + + self.copy_partial_string(addr, n); + } + fn reinstantiate_var(&mut self, addr: Addr, frontier: usize) { match addr { Addr::HeapCell(h) => { @@ -126,6 +216,22 @@ impl CopyTermState { HeapCellValue::Addr(Addr::StackCell(fr, sc)), )); } + Addr::PStrTail(h, n) => { + match &mut self.target[h] { + HeapCellValue::PartialString(ref mut pstr) => { + pstr.tail = Addr::PStrTail(frontier, n); + } + _ => { + unreachable!() + } + } + + self.target[frontier] = HeapCellValue::Addr(Addr::PStrTail(frontier, n)); + self.trail.push(( + Ref::PStrTail(h, n), + HeapCellValue::Addr(Addr::PStrTail(h, n)) + )); + } Addr::AttrVar(h) => { let threshold = if let AttrVarPolicy::DeepCopy = self.attr_var_policy { self.target.threshold() @@ -203,16 +309,35 @@ impl CopyTermState { self.target.push(HeapCellValue::Addr(addr)); while self.scan < self.target.threshold() { - match self.value_at_scan().clone() { - HeapCellValue::NamedStr(..) => self.scan += 1, - HeapCellValue::Addr(addr) => match addr { - Addr::Lis(addr) => self.copy_list(addr), - addr @ Addr::AttrVar(_) - | addr @ Addr::HeapCell(_) - | addr @ Addr::StackCell(..) => self.copy_var(addr), - Addr::Str(addr) => self.copy_structure(addr), - Addr::Con(_) | Addr::DBRef(_) => self.scan += 1, - }, + match self.value_at_scan() { + HeapCellValue::NamedStr(..) => { + self.scan += 1; + } + HeapCellValue::Addr(ref addr) => { + match addr.clone() { + Addr::Lis(addr) => { + self.copy_list(addr); + } + addr @ Addr::AttrVar(_) + | addr @ Addr::HeapCell(_) + | addr @ Addr::StackCell(..) + | addr @ Addr::PStrTail(..) => { + self.copy_var(addr); + } + Addr::Str(addr) => { + self.copy_structure(addr); + } + Addr::PStrLocation(addr, n) => { + self.copy_partial_string_from(addr, n); + } + Addr::Con(_) | Addr::DBRef(_) => { + self.scan += 1; + } + } + } + HeapCellValue::PartialString(_) => { + self.scan += 1; + } } } @@ -224,6 +349,10 @@ impl CopyTermState { match r { Ref::AttrVar(h) | Ref::HeapCell(h) => self.target[h] = value, + Ref::PStrTail(h, _) => + if let HeapCellValue::PartialString(ref mut pstr) = &mut self.target[h] { + pstr.tail = value.as_addr(0); + }, Ref::StackCell(fr, sc) => self.target.stack().index_and_frame_mut(fr)[sc] = value.as_addr(0), } diff --git a/src/prolog/machine/heap.rs b/src/prolog/machine/heap.rs index b3d406e3e..6fe60b851 100644 --- a/src/prolog/machine/heap.rs +++ b/src/prolog/machine/heap.rs @@ -3,6 +3,7 @@ use core::marker::PhantomData; use crate::prolog_parser::ast::*; use crate::prolog::machine::machine_indices::*; +use crate::prolog::machine::partial_string::*; use crate::prolog::machine::raw_block::*; use std::mem; @@ -149,12 +150,93 @@ impl HeapTemplate { #[inline] pub(crate) - fn take(&mut self) -> HeapTemplate { - unsafe { - HeapTemplate { - buf: mem::transmute::, RawBlock>(self.buf.take()), - _marker: PhantomData, + fn allocate_pstr(&mut self, mut src: &str) -> Option { + let orig_h = self.h(); + + loop { + if src == "" { + return if orig_h == self.h() { + None + } else { + let prev_h = self.h() - 1; + + match &mut self[prev_h] { + HeapCellValue::PartialString(ref mut pstr) => { + let s = pstr.block_as_str(); + pstr.tail = Addr::PStrTail(prev_h, s.len()); + } + _ => { + unreachable!() + } + } + + Some(Addr::PStrLocation(orig_h, 0)) + }; } + + let h = self.h(); + + let (mut pstr, rest_src) = + match PartialString::new(src, h) { + Some(tuple) => { + tuple + } + None => { + if src.len() > '\u{0}'.len_utf8() { + src = &src['\u{0}'.len_utf8() ..]; + continue; + } else if orig_h == h { + return None; + } else { + let prev_h = h - 1; + + match &mut self[prev_h] { + HeapCellValue::PartialString(ref mut pstr) => { + let s = pstr.block_as_str(); + pstr.tail = Addr::PStrTail(prev_h, s.len()); + } + _ => { + unreachable!() + } + } + + return Some(Addr::PStrLocation(orig_h, 0)); + } + } + }; + + let new_top = unsafe { + self.buf.new_block(mem::size_of::()) + }; + + if rest_src != "" { + pstr.tail = Addr::PStrLocation(h+1, 0); + src = rest_src; + } else { + pstr.tail = Addr::PStrTail(h, src.len()); + } + + unsafe{ + ptr::write( + self.buf.top as *mut _, + HeapCellValue::PartialString(pstr), + ); + } + + self.buf.top = new_top; + + if rest_src == "" { + return Some(Addr::PStrLocation(orig_h, 0)); + } + } + } + + #[inline] + pub(crate) + fn take(&mut self) -> Self { + HeapTemplate { + buf: self.buf.take(), + _marker: PhantomData, } } diff --git a/src/prolog/machine/machine_errors.rs b/src/prolog/machine/machine_errors.rs index c084c6459..798626197 100644 --- a/src/prolog/machine/machine_errors.rs +++ b/src/prolog/machine/machine_errors.rs @@ -391,10 +391,13 @@ impl EvalError { pub(super) enum CycleSearchResult { EmptyList, NotList, - PartialList(usize, usize), // the list length (up to max), and an offset into the heap. - ProperList(usize), // the list length. - String(usize, usize, Rc), // the number of bytes iterated, the offset, the string. - UntouchedList(usize), // the address of an uniterated Addr::Lis(address). + PartialList(usize, Ref), // the list length (up to max), and an offset into the heap. + ProperList(usize), // the list length. + CompleteString(usize, Rc), // the string length (in bytes), the string. + UntouchedString(usize, Rc), // the cut off, past which is the untouched string. + PStrLocation(usize, usize, usize), // the list length (up to max), the heap offset, byte offset into the string. + PStrTail(usize, usize, usize), // the list length (up to max), the heap offset, byte offset into the string. + UntouchedList(usize), // the address of an uniterated Addr::Lis(address). } impl MachineState { diff --git a/src/prolog/machine/machine_indices.rs b/src/prolog/machine/machine_indices.rs index 44e086b12..9805699a1 100644 --- a/src/prolog/machine/machine_indices.rs +++ b/src/prolog/machine/machine_indices.rs @@ -7,6 +7,7 @@ use crate::prolog::forms::*; use crate::prolog::machine::code_repo::CodeRepo; use crate::prolog::machine::Ball; use crate::prolog::machine::heap::*; +use crate::prolog::machine::partial_string::*; use crate::prolog::machine::raw_block::RawBlockTraits; use crate::prolog::instructions::*; use crate::prolog::rug::Integer; @@ -46,6 +47,8 @@ pub enum Addr { HeapCell(usize), StackCell(usize, usize), Str(usize), + PStrLocation(usize, usize), // location of pstr in heap, offset into string in bytes. + PStrTail(usize, usize), // location of pstr in heap, offset into string in bytes. } #[derive(Clone, Copy, Hash, Eq, PartialEq)] @@ -53,6 +56,7 @@ pub enum Ref { AttrVar(usize), HeapCell(usize), StackCell(usize, usize), + PStrTail(usize, usize), } impl Ref { @@ -61,6 +65,7 @@ impl Ref { Ref::AttrVar(h) => Addr::AttrVar(h), Ref::HeapCell(h) => Addr::HeapCell(h), Ref::StackCell(fr, sc) => Addr::StackCell(fr, sc), + Ref::PStrTail(h, n) => Addr::PStrTail(h, n), } } } @@ -75,23 +80,51 @@ impl PartialEq for Addr { impl PartialOrd for Addr { fn partial_cmp(&self, r: &Ref) -> Option { match self { - &Addr::StackCell(fr, sc) => match *r { - Ref::AttrVar(_) | Ref::HeapCell(_) => Some(Ordering::Greater), - Ref::StackCell(fr1, sc1) => { - if fr1 < fr || (fr1 == fr && sc1 < sc) { + &Addr::StackCell(fr, sc) => { + match *r { + Ref::AttrVar(_) | Ref::HeapCell(_) | Ref::PStrTail(..) => { Some(Ordering::Greater) - } else if fr1 == fr && sc1 == sc { - Some(Ordering::Equal) - } else { + } + Ref::StackCell(fr1, sc1) => { + if fr1 < fr || (fr1 == fr && sc1 < sc) { + Some(Ordering::Greater) + } else if fr1 == fr && sc1 == sc { + Some(Ordering::Equal) + } else { + Some(Ordering::Less) + } + } + } + } + &Addr::HeapCell(h) | &Addr::AttrVar(h) => { + match r { + Ref::StackCell(..) => { Some(Ordering::Less) } + Ref::AttrVar(h1) | Ref::HeapCell(h1) => { + h.partial_cmp(h1) + } + Ref::PStrTail(h1, _) => { + h.partial_cmp(h1) + } } - }, - &Addr::HeapCell(h) | &Addr::AttrVar(h) => match r { - &Ref::StackCell(..) => Some(Ordering::Less), - &Ref::AttrVar(h1) | &Ref::HeapCell(h1) => h.partial_cmp(&h1), - }, - _ => None, + } + &Addr::PStrTail(h, n) => { + match r { + Ref::StackCell(..) => { + Some(Ordering::Less) + } + Ref::AttrVar(h1) | Ref::HeapCell(h1) => { + h.partial_cmp(h1) + } + Ref::PStrTail(h1, n1) => { + Some(h.cmp(h1).then_with(|| n.cmp(n1))) + } + } + } + _ => { + None + } } } } @@ -99,8 +132,12 @@ impl PartialOrd for Addr { impl Addr { pub fn is_ref(&self) -> bool { match self { - &Addr::AttrVar(_) | &Addr::HeapCell(_) | &Addr::StackCell(_, _) => true, - _ => false, + Addr::HeapCell(_) | Addr::StackCell(_, _) | Addr::AttrVar(_) | Addr::PStrTail(..) => { + true + } + _ => { + false + } } } @@ -109,6 +146,7 @@ impl Addr { &Addr::AttrVar(h) => Some(Ref::AttrVar(h)), &Addr::HeapCell(h) => Some(Ref::HeapCell(h)), &Addr::StackCell(fr, sc) => Some(Ref::StackCell(fr, sc)), + &Addr::PStrTail(h, n) => Some(Ref::PStrTail(h, n)), _ => None, } } @@ -130,6 +168,8 @@ impl Add for Addr { Addr::AttrVar(h) => Addr::AttrVar(h + rhs), Addr::HeapCell(h) => Addr::HeapCell(h + rhs), Addr::Str(s) => Addr::Str(s + rhs), + Addr::PStrLocation(h, n) => Addr::PStrLocation(h + rhs, n), + Addr::PStrTail(h, n) => Addr::PStrTail(h + rhs, n), _ => self, } } @@ -145,6 +185,8 @@ impl Sub for Addr { Addr::AttrVar(h) => Addr::AttrVar(h + rhs.abs() as usize), Addr::HeapCell(h) => Addr::HeapCell(h + rhs.abs() as usize), Addr::Str(s) => Addr::Str(s + rhs.abs() as usize), + Addr::PStrTail(h, n) => Addr::PStrTail(h + rhs.abs() as usize, n), + Addr::PStrLocation(h, n) => Addr::PStrLocation(h + rhs.abs() as usize, n), _ => self, } } else { @@ -162,6 +204,8 @@ impl Sub for Addr { Addr::AttrVar(h) => Addr::AttrVar(h - rhs), Addr::HeapCell(h) => Addr::HeapCell(h - rhs), Addr::Str(s) => Addr::Str(s - rhs), + Addr::PStrTail(h, n) => Addr::PStrTail(h - rhs, n), + Addr::PStrLocation(h, n) => Addr::PStrLocation(h - rhs, n), _ => self, } } @@ -173,16 +217,6 @@ impl SubAssign for Addr { } } -impl From for Addr { - fn from(r: Ref) -> Self { - match r { - Ref::AttrVar(h) => Addr::AttrVar(h), - Ref::HeapCell(h) => Addr::HeapCell(h), - Ref::StackCell(fr, sc) => Addr::StackCell(fr, sc), - } - } -} - #[derive(Clone, Copy)] pub enum TrailRef { Ref(Ref), @@ -200,13 +234,21 @@ impl From for TrailRef { pub enum HeapCellValue { Addr(Addr), NamedStr(usize, ClauseName, Option), // arity, name, precedence/Specifier if it has one. + PartialString(PartialString), } impl HeapCellValue { pub fn as_addr(&self, focus: usize) -> Addr { match self { - &HeapCellValue::Addr(ref a) => a.clone(), - &HeapCellValue::NamedStr(_, _, _) => Addr::Str(focus), + HeapCellValue::Addr(ref a) => { + a.clone() + } + HeapCellValue::NamedStr(_, _, _) => { + Addr::Str(focus) + } + HeapCellValue::PartialString(_) => { + Addr::PStrLocation(focus, 0) + } } } } diff --git a/src/prolog/machine/machine_state.rs b/src/prolog/machine/machine_state.rs index 5eb4dccc4..4c626b0d2 100644 --- a/src/prolog/machine/machine_state.rs +++ b/src/prolog/machine/machine_state.rs @@ -25,19 +25,22 @@ pub struct Ball { } impl Ball { - pub(super) fn new() -> Self { + pub(super) + fn new() -> Self { Ball { boundary: 0, stub: Heap::new(), } } - pub(super) fn reset(&mut self) { + pub(super) + fn reset(&mut self) { self.boundary = 0; self.stub.clear(); } - pub(super) fn take(&mut self) -> Ball { + pub(super) + fn take(&mut self) -> Ball { let boundary = self.boundary; self.boundary = 0; @@ -47,14 +50,20 @@ impl Ball { } } - pub(super) fn copy_and_align(&self, h: usize) -> Heap { + pub(super) + fn copy_and_align(&self, h: usize) -> Heap { let diff = self.boundary as i64 - h as i64; let mut stub = Heap::new(); - for heap_value in self.stub.iter_from(0).cloned() { + for heap_value in self.stub.iter_from(0) { stub.push(match heap_value { - HeapCellValue::Addr(addr) => HeapCellValue::Addr(addr - diff), - heap_value => heap_value, + HeapCellValue::Addr(ref addr) => HeapCellValue::Addr(addr.clone() - diff), + HeapCellValue::PartialString(ref pstr) => { + let mut new_pstr = pstr.clone(); + new_pstr.tail = pstr.tail.clone() - diff; + HeapCellValue::PartialString(new_pstr) + } + heap_value => heap_value.clone(), }); } @@ -524,7 +533,8 @@ pub(crate) trait CallPolicy: Any { machine_st.attr_var_init.backtrack( attr_var_init_queue_b, - attr_var_init_bindings_b); + attr_var_init_bindings_b, + ); machine_st.stack.truncate(machine_st.b); machine_st.b = machine_st.stack.index_or_frame(b).prelude.b; diff --git a/src/prolog/machine/machine_state_impl.rs b/src/prolog/machine/machine_state_impl.rs index e18cee940..d93144a12 100644 --- a/src/prolog/machine/machine_state_impl.rs +++ b/src/prolog/machine/machine_state_impl.rs @@ -119,15 +119,34 @@ impl MachineState { self.flags } - pub(crate) fn store(&self, addr: Addr) -> Addr { + pub(crate) + fn store(&self, addr: Addr) -> Addr { match addr { - Addr::AttrVar(h) | Addr::HeapCell(h) => self.heap[h].as_addr(h), - Addr::StackCell(fr, sc) => self.stack.index_and_frame(fr)[sc].clone(), - addr => addr, + Addr::AttrVar(h) | Addr::HeapCell(h) => { + self.heap[h].as_addr(h) + } + Addr::StackCell(fr, sc) => { + self.stack.index_and_frame(fr)[sc].clone() + } + Addr::PStrTail(h, n) => { + if let HeapCellValue::PartialString(ref pstr) = &self.heap[h] { + if pstr.len() > n { + Addr::PStrLocation(h, n) + } else { + pstr.tail.clone() + } + } else { + unreachable!() + } + } + addr => { + addr + } } } - pub(crate) fn deref(&self, mut addr: Addr) -> Addr { + pub(crate) + fn deref(&self, mut addr: Addr) -> Addr { loop { let value = self.store(addr.clone()); @@ -158,15 +177,38 @@ impl MachineState { } } + fn bind_pstr_tail(&mut self, h: usize, t2: Addr) { + let pstr_len = match &mut self.heap[h] { + HeapCellValue::PartialString(ref mut pstr) => { + pstr.tail = t2; + pstr.len() + } + _ => { + unreachable!() + } + }; + + self.trail(TrailRef::from(Ref::PStrTail(h, pstr_len))); + } + pub(super) fn bind(&mut self, r1: Ref, a2: Addr) { let t1 = self.store(r1.as_addr()); let t2 = self.store(a2.clone()); if t1.is_ref() && (!t2.is_ref() || a2 < r1) { match r1 { - Ref::StackCell(fr, sc) => self.stack.index_and_frame_mut(fr)[sc] = t2, - Ref::HeapCell(h) => self.heap[h] = HeapCellValue::Addr(t2), - Ref::AttrVar(h) => return self.bind_attr_var(h, t2), + Ref::StackCell(fr, sc) => { + self.stack.index_and_frame_mut(fr)[sc] = t2; + } + Ref::HeapCell(h) => { + self.heap[h] = HeapCellValue::Addr(t2); + } + Ref::AttrVar(h) => { + return self.bind_attr_var(h, t2); + } + Ref::PStrTail(h, _) => { + return self.bind_pstr_tail(h, t2); + } }; self.trail(TrailRef::from(r1)); @@ -180,9 +222,14 @@ impl MachineState { self.heap[h] = HeapCellValue::Addr(t1); self.trail(TrailRef::Ref(Ref::HeapCell(h))); } - Some(Ref::AttrVar(h)) => - self.bind_attr_var(h, t1), - None => {} + Some(Ref::AttrVar(h)) => { + self.bind_attr_var(h, t1); + } + Some(Ref::PStrTail(h, _)) => { + self.bind_pstr_tail(h, t1); + } + None => { + } } } } @@ -258,6 +305,9 @@ impl MachineState { (Addr::StackCell(fr, sc), addr) | (addr, Addr::StackCell(fr, sc)) => { self.bind_with_occurs_check(Ref::StackCell(fr, sc), addr) } + (Addr::PStrTail(h, n), addr) | (addr, Addr::PStrTail(h, n)) => { + self.bind_with_occurs_check(Ref::PStrTail(h, n), addr); + } (Addr::Lis(a1), Addr::Str(a2)) | (Addr::Str(a2), Addr::Lis(a1)) => { if let &HeapCellValue::NamedStr(n2, ref f2, _) = &self.heap[a2] { if f2.as_str() == "." && n2 == 2 { @@ -274,7 +324,9 @@ impl MachineState { self.fail = true; } (Addr::Lis(a1), Addr::Con(Constant::String(n, s))) - | (Addr::Con(Constant::String(n, s)), Addr::Lis(a1)) => { + | (Addr::Con(Constant::String(n, s)), Addr::Lis(a1)) + if !self.flags.double_quotes.is_atom() => + { if self.deconstruct_chars(s, n, a1, &mut pdl) { continue; } @@ -291,6 +343,63 @@ impl MachineState { self.fail = true; } + (Addr::PStrLocation(h, n), Addr::Lis(l)) + | (Addr::Lis(l), Addr::PStrLocation(h, n)) => { + if let HeapCellValue::PartialString(ref pstr) = &self.heap[h] { + let s = pstr.block_as_str(); + + if let Some(c) = s[n ..].chars().next() { + pdl.push(Addr::PStrTail(h, n + c.len_utf8())); + pdl.push(Addr::HeapCell(l + 1)); + + pdl.push(Addr::Con(Constant::Char(c))); + pdl.push(Addr::HeapCell(l)); + } + } + } + (Addr::PStrLocation(h, n), Addr::Con(Constant::String(n1, s))) + | (Addr::Con(Constant::String(n1, s)), Addr::PStrLocation(h, n)) + if self.flags.double_quotes.is_chars() => { + if let HeapCellValue::PartialString(ref pstr) = &self.heap[h] { + let pstr_s = pstr.block_as_str(); + + if let Some(c) = pstr_s[n ..].chars().next() { + if let Some(c1) = s[n1 ..].chars().next() { + if c == c1 { + pdl.push(Addr::Con(Constant::String(n1 + c.len_utf8(), s))); + pdl.push(Addr::PStrTail(h, n + c.len_utf8())); + + continue; + } + } + } + + self.fail = true; + break; + } + } + (Addr::PStrLocation(h1, n1), Addr::PStrLocation(h2, n2)) => { + if let HeapCellValue::PartialString(ref pstr1) = &self.heap[h1] { + if let HeapCellValue::PartialString(ref pstr2) = &self.heap[h2] { + let pstr_s1 = pstr1.block_as_str(); + let pstr_s2 = pstr2.block_as_str(); + + if let Some(c1) = pstr_s1[n1 ..].chars().next() { + if let Some(c2) = pstr_s2[n2 ..].chars().next() { + if c1 == c2 { + pdl.push(Addr::PStrTail(h1, n1 + c1.len_utf8())); + pdl.push(Addr::PStrTail(h2, n2 + c2.len_utf8())); + + continue; + } + } + } + + self.fail = true; + break; + } + } + } (Addr::Lis(a1), Addr::Lis(a2)) => { pdl.push(Addr::HeapCell(a1)); pdl.push(Addr::HeapCell(a2)); @@ -353,17 +462,14 @@ impl MachineState { (Addr::AttrVar(h), addr) | (addr, Addr::AttrVar(h)) => { self.bind(Ref::AttrVar(h), addr); } - (Addr::HeapCell(h), _) => { - self.bind(Ref::HeapCell(h), d2); - } - (_, Addr::HeapCell(h)) => { - self.bind(Ref::HeapCell(h), d1); + (Addr::HeapCell(h), addr) | (addr, Addr::HeapCell(h)) => { + self.bind(Ref::HeapCell(h), addr); } - (Addr::StackCell(fr, sc), _) => { - self.bind(Ref::StackCell(fr, sc), d2); + (Addr::StackCell(fr, sc), addr) | (addr, Addr::StackCell(fr, sc)) => { + self.bind(Ref::StackCell(fr, sc), addr); } - (_, Addr::StackCell(fr, sc)) => { - self.bind(Ref::StackCell(fr, sc), d1); + (Addr::PStrTail(h, n), addr) | (addr, Addr::PStrTail(h, n)) => { + self.bind(Ref::PStrTail(h, n), addr); } (Addr::Lis(a1), Addr::Str(a2)) | (Addr::Str(a2), Addr::Lis(a1)) => { if let &HeapCellValue::NamedStr(n2, ref f2, _) = &self.heap[a2] { @@ -381,13 +487,14 @@ impl MachineState { self.fail = true; } (Addr::Lis(a1), Addr::Con(Constant::String(n, s))) - | (Addr::Con(Constant::String(n, s)), Addr::Lis(a1)) => { - if self.deconstruct_chars(s, n, a1, &mut pdl) { - continue; - } + | (Addr::Con(Constant::String(n, s)), Addr::Lis(a1)) + if !self.flags.double_quotes.is_atom() => { + if self.deconstruct_chars(s, n, a1, &mut pdl) { + continue; + } - self.fail = true; - } + self.fail = true; + } (Addr::Con(Constant::EmptyList), Addr::Con(Constant::String(n, ref s))) | (Addr::Con(Constant::String(n, ref s)), Addr::Con(Constant::EmptyList)) if !self.flags.double_quotes.is_atom() => @@ -398,6 +505,63 @@ impl MachineState { self.fail = true; } + (Addr::PStrLocation(h, n), Addr::Lis(l)) + | (Addr::Lis(l), Addr::PStrLocation(h, n)) => { + if let HeapCellValue::PartialString(ref pstr) = &self.heap[h] { + let s = pstr.block_as_str(); + + if let Some(c) = s[n ..].chars().next() { + pdl.push(Addr::PStrTail(h, n + c.len_utf8())); + pdl.push(Addr::HeapCell(l + 1)); + + pdl.push(Addr::Con(Constant::Char(c))); + pdl.push(Addr::HeapCell(l)); + } + } + } + (Addr::PStrLocation(h, n), Addr::Con(Constant::String(n1, s))) + | (Addr::Con(Constant::String(n1, s)), Addr::PStrLocation(h, n)) + if self.flags.double_quotes.is_chars() => { + if let HeapCellValue::PartialString(ref pstr) = &self.heap[h] { + let pstr_s = pstr.block_as_str(); + + if let Some(c) = pstr_s[n ..].chars().next() { + if let Some(c1) = s[n1 ..].chars().next() { + if c == c1 { + pdl.push(Addr::Con(Constant::String(n1 + c.len_utf8(), s))); + pdl.push(Addr::PStrTail(h, n + c.len_utf8())); + + continue; + } + } + } + + self.fail = true; + break; + } + } + (Addr::PStrLocation(h1, n1), Addr::PStrLocation(h2, n2)) => { + if let HeapCellValue::PartialString(ref pstr1) = &self.heap[h1] { + if let HeapCellValue::PartialString(ref pstr2) = &self.heap[h2] { + let pstr_s1 = pstr1.block_as_str(); + let pstr_s2 = pstr2.block_as_str(); + + if let Some(c1) = pstr_s1[n1 ..].chars().next() { + if let Some(c2) = pstr_s2[n2 ..].chars().next() { + if c1 == c2 { + pdl.push(Addr::PStrTail(h1, n1 + c1.len_utf8())); + pdl.push(Addr::PStrTail(h2, n2 + c2.len_utf8())); + + continue; + } + } + } + + self.fail = true; + break; + } + } + } (Addr::Lis(a1), Addr::Lis(a2)) => { pdl.push(Addr::HeapCell(a1)); pdl.push(Addr::HeapCell(a2)); @@ -449,6 +613,12 @@ impl MachineState { self.tr += 1; } } + TrailRef::Ref(Ref::PStrTail(h, n)) => { + if h < self.hb { + self.trail.push(TrailRef::Ref(Ref::PStrTail(h, n))); + self.tr += 1; + } + } TrailRef::AttrVarHeapLink(h) => { if h < self.hb { self.trail.push(TrailRef::AttrVarHeapLink(h)); @@ -485,6 +655,14 @@ impl MachineState { TrailRef::Ref(Ref::StackCell(fr, sc)) => { self.stack.index_and_frame_mut(fr)[sc] = Addr::StackCell(fr, sc) } + TrailRef::Ref(Ref::PStrTail(h, n)) => { + if let HeapCellValue::PartialString(ref mut pstr) = &mut self.heap[h] { + pstr.truncate(n); + pstr.tail = Addr::PStrTail(h, n); + + self.tr += 1; + } + } TrailRef::AttrVarHeapLink(h) => { self.heap[h] = HeapCellValue::Addr(Addr::HeapCell(h)); } @@ -508,6 +686,7 @@ impl MachineState { match self.trail[i] { TrailRef::Ref(Ref::AttrVar(tr_i)) | TrailRef::Ref(Ref::HeapCell(tr_i)) + | TrailRef::Ref(Ref::PStrTail(tr_i, _)) | TrailRef::AttrVarHeapLink(tr_i) | TrailRef::AttrVarListLink(tr_i, _) => { if tr_i >= hb { @@ -1375,7 +1554,8 @@ impl MachineState { }; } - fn get_char_list(&mut self, offset: usize, s: Rc) { + fn get_char_list(&mut self, offset: usize, s: Rc) + { if let Some(c) = s[offset ..].chars().next() { let h = self.heap.h(); @@ -1390,6 +1570,29 @@ impl MachineState { } } + fn get_partial_string_list(&mut self, pstr_h: usize, offset: usize) + { + let (c, pstr_tail) = + if let HeapCellValue::PartialString(ref pstr) = &self.heap[pstr_h] { + let s = pstr.block_as_str(); + + if let Some(c) = s[offset ..].chars().next() { + (c, Addr::PStrTail(pstr_h, offset + c.len_utf8())) + } else { + unreachable!() + } + } else { + unreachable!() + }; + + self.s = self.heap.h(); + + self.heap.push(HeapCellValue::Addr(Addr::Con(Constant::Char(c)))); + self.heap.push(HeapCellValue::Addr(pstr_tail)); + + self.mode = MachineMode::Read; + } + pub(super) fn execute_fact_instr(&mut self, instr: &FactInstruction) { match instr { &FactInstruction::GetConstant(_, ref c, reg) => { @@ -1409,10 +1612,14 @@ impl MachineState { _ => { self.fail = true } - }, + }, + Addr::PStrLocation(h, n) => { + self.get_partial_string_list(h, n) + } addr @ Addr::AttrVar(_) | addr @ Addr::StackCell(..) - | addr @ Addr::HeapCell(_) => { + | addr @ Addr::HeapCell(_) + | addr @ Addr::PStrTail(..) => { let h = self.heap.h(); self.heap.push(HeapCellValue::Addr(Addr::Lis(h + 1))); @@ -1558,7 +1765,10 @@ impl MachineState { let addr = self.store(self.deref(a1)); let offset = match addr { - Addr::HeapCell(_) | Addr::StackCell(..) | Addr::AttrVar(..) => v, + Addr::HeapCell(_) | Addr::StackCell(..) + | Addr::AttrVar(..) | Addr::PStrTail(..) => { + v + } Addr::Con(Constant::String(n, ref s)) => { if !self.flags.double_quotes.is_atom() { if n >= s.len() { @@ -1570,9 +1780,15 @@ impl MachineState { c } } - Addr::Con(_) => c, - Addr::Lis(_) => l, - Addr::Str(_) => s, + Addr::Con(_) => { + c + } + Addr::Lis(_) | Addr::PStrLocation(..) => { + l + } + Addr::Str(_) => { + s + } Addr::DBRef(_) => { self.fail = true; return; @@ -1827,7 +2043,7 @@ impl MachineState { let n = self.store(self.deref(self[temp_v!(1)].clone())); match n { - Addr::HeapCell(_) | Addr::StackCell(..) => + Addr::HeapCell(_) | Addr::StackCell(..) | Addr::PStrTail(..) => // 8.5.2.3 a) { return Err(self.error_form(MachineError::instantiation_error(), stub)) @@ -1852,7 +2068,7 @@ impl MachineState { let term = self.store(self.deref(self[temp_v!(2)].clone())); match term { - Addr::HeapCell(_) | Addr::StackCell(..) => + Addr::HeapCell(_) | Addr::StackCell(..) | Addr::PStrTail(..) => // 8.5.2.3 b) { return Err(self.error_form(MachineError::instantiation_error(), stub)) @@ -1876,6 +2092,31 @@ impl MachineState { self.fail = true; } } + Addr::PStrLocation(h, offset) => { + if n == 1 || n == 2 { + let a3 = self[temp_v!(3)].clone(); + let h_a = + if let HeapCellValue::PartialString(ref pstr) = &self.heap[h] { + let s = pstr.block_as_str(); + + if let Some(c) = s[offset ..].chars().next() { + if n == 1 { + Addr::Con(Constant::Char(c)) + } else { + Addr::PStrTail(h, offset + c.len_utf8()) + } + } else { + unreachable!() + } + } else { + unreachable!() + }; + + self.unify(a3, h_a); + } else { + self.fail = true; + } + } Addr::Con(Constant::String(o, ref s)) if !self.flags.double_quotes.is_atom() && !s[o ..].is_empty() => { @@ -1986,6 +2227,52 @@ impl MachineState { while let Some((v1, v2)) = iter.next() { match (v1, v2) { + ( + HeapCellValue::Addr(Addr::Lis(_)), + HeapCellValue::Addr(Addr::PStrLocation(..)), + ) + | ( + HeapCellValue::Addr(Addr::PStrLocation(..)), + HeapCellValue::Addr(Addr::Lis(_)), + ) + | ( + HeapCellValue::Addr(Addr::PStrLocation(..)), + HeapCellValue::Addr(Addr::PStrLocation(..)), + ) + | ( + HeapCellValue::Addr(Addr::PStrTail(..)), + HeapCellValue::Addr(Addr::PStrTail(..)), + ) => { + } + ( + HeapCellValue::Addr(Addr::PStrLocation(h1, _)), + HeapCellValue::Addr(Addr::PStrTail(h2, _)), + ) => { + return if h1 == h2 { + Ordering::Less + } else { + h1.cmp(&h2) + }; + } + ( + HeapCellValue::Addr(Addr::PStrTail(h2, _)), + HeapCellValue::Addr(Addr::PStrLocation(h1, _)), + ) => { + return if h1 == h2 { + Ordering::Greater + } else { + h2.cmp(&h1) + }; + } + ( + HeapCellValue::Addr(Addr::PStrLocation(..)), + HeapCellValue::Addr(Addr::Con(Constant::String(..))), + ) + | ( + HeapCellValue::Addr(Addr::Con(Constant::String(..))), + HeapCellValue::Addr(Addr::PStrLocation(..)), + ) if self.flags.double_quotes.is_chars() => { + } ( HeapCellValue::Addr(Addr::Lis(_)), HeapCellValue::Addr(Addr::Con(Constant::String(..))), @@ -2055,8 +2342,27 @@ impl MachineState { return hc1.cmp(&hc2); } } + ( + HeapCellValue::Addr(Addr::PStrTail(hc1, _)), + HeapCellValue::Addr(Addr::HeapCell(hc2)), + ) + | ( + HeapCellValue::Addr(Addr::HeapCell(hc1)), + HeapCellValue::Addr(Addr::PStrTail(hc2, _)), + ) + | ( + HeapCellValue::Addr(Addr::PStrTail(hc1, _)), + HeapCellValue::Addr(Addr::AttrVar(hc2)), + ) + | ( + HeapCellValue::Addr(Addr::AttrVar(hc1)), + HeapCellValue::Addr(Addr::PStrTail(hc2, _)), + ) => { + return hc1.cmp(&hc2); + } (HeapCellValue::Addr(Addr::HeapCell(_)), _) - | (HeapCellValue::Addr(Addr::AttrVar(_)), _) => { + | (HeapCellValue::Addr(Addr::AttrVar(_)), _) + | (HeapCellValue::Addr(Addr::PStrTail(..)), _) => { return Ordering::Less; } ( @@ -2075,6 +2381,10 @@ impl MachineState { HeapCellValue::Addr(Addr::StackCell(..)), HeapCellValue::Addr(Addr::HeapCell(_)), ) + | ( + HeapCellValue::Addr(Addr::StackCell(..)), + HeapCellValue::Addr(Addr::PStrTail(..)), + ) | ( HeapCellValue::Addr(Addr::StackCell(..)), HeapCellValue::Addr(Addr::AttrVar(_)), @@ -2091,7 +2401,11 @@ impl MachineState { | ( HeapCellValue::Addr(Addr::Con(Constant::Integer(..))), HeapCellValue::Addr(Addr::AttrVar(_)), - ) => { + ) + | ( + HeapCellValue::Addr(Addr::Con(Constant::Integer(..))), + HeapCellValue::Addr(Addr::PStrTail(..)), + )=> { return Ordering::Greater; } ( @@ -2116,6 +2430,10 @@ impl MachineState { | ( HeapCellValue::Addr(Addr::Con(Constant::Float(..))), HeapCellValue::Addr(Addr::AttrVar(_)), + ) + | ( + HeapCellValue::Addr(Addr::Con(Constant::Float(..))), + HeapCellValue::Addr(Addr::PStrTail(..)), ) => { return Ordering::Greater; } @@ -2141,6 +2459,10 @@ impl MachineState { | ( HeapCellValue::Addr(Addr::Con(Constant::Rational(..))), HeapCellValue::Addr(Addr::AttrVar(_)), + ) + | ( + HeapCellValue::Addr(Addr::Con(Constant::Rational(..))), + HeapCellValue::Addr(Addr::PStrTail(..)), ) => { return Ordering::Greater; } @@ -2168,6 +2490,10 @@ impl MachineState { | ( HeapCellValue::Addr(Addr::Con(Constant::String(..))), HeapCellValue::Addr(Addr::AttrVar(_)), + ) + | ( + HeapCellValue::Addr(Addr::Con(Constant::String(..))), + HeapCellValue::Addr(Addr::PStrTail(..)), ) => { return Ordering::Greater; } @@ -2216,6 +2542,10 @@ impl MachineState { HeapCellValue::Addr(Addr::Con(Constant::Atom(..))), HeapCellValue::Addr(Addr::StackCell(..)), ) => return Ordering::Greater, + ( + HeapCellValue::Addr(Addr::Con(Constant::Atom(..))), + HeapCellValue::Addr(Addr::PStrTail(..)), + ) => return Ordering::Greater, ( HeapCellValue::Addr(Addr::Con(Constant::Atom(..))), HeapCellValue::Addr(Addr::Con(Constant::Float(_))), @@ -2330,7 +2660,7 @@ impl MachineState { let d = self.store(self.deref(self[r1].clone())); match d { - Addr::Str(_) | Addr::Lis(_) => self.p += 1, + Addr::Str(_) | Addr::Lis(_) | Addr::PStrLocation(..) => self.p += 1, _ => self.fail = true, }; } @@ -2362,16 +2692,24 @@ impl MachineState { let d = self.store(self.deref(self[r1].clone())); match d { - Addr::AttrVar(_) | Addr::HeapCell(_) | Addr::StackCell(..) => self.fail = true, - _ => self.p += 1, + Addr::AttrVar(_) | Addr::HeapCell(_) | Addr::StackCell(..) | Addr::PStrTail(..) => { + self.fail = true; + } + _ => { + self.p += 1; + } }; } &InlinedClauseType::IsVar(r1) => { let d = self.store(self.deref(self[r1].clone())); match d { - Addr::AttrVar(_) | Addr::HeapCell(_) | Addr::StackCell(_, _) => self.p += 1, - _ => self.fail = true, + Addr::AttrVar(_) | Addr::HeapCell(_) | Addr::StackCell(_, _) | Addr::PStrTail(..) => { + self.p += 1; + } + _ => { + self.fail = true; + } }; } } @@ -2414,8 +2752,8 @@ impl MachineState { Addr::Lis(self.heap.h()) } else { let h = self.heap.h(); - self.heap - .push(HeapCellValue::NamedStr(arity as usize, name, spec)); + self.heap.push(HeapCellValue::NamedStr(arity as usize, name, spec)); + Addr::Str(h) }; @@ -2448,11 +2786,11 @@ impl MachineState { } _ => self.fail = true, }, - Addr::Lis(_) => { + Addr::Lis(_) | Addr::PStrLocation(..) => { let shared_op_desc = fetch_op_spec(clause_name!("."), 2, None, &indices.op_dir); self.try_functor_compound_case(clause_name!("."), 2, shared_op_desc) } - Addr::AttrVar(..) | Addr::HeapCell(_) | Addr::StackCell(..) => { + Addr::AttrVar(..) | Addr::HeapCell(_) | Addr::StackCell(..) | Addr::PStrTail(..) => { let name = self.store(self.deref(self[temp_v!(2)].clone())); let arity = self.store(self.deref(self[temp_v!(3)].clone())); @@ -2551,48 +2889,8 @@ impl MachineState { let a1 = self.store(self.deref(self[r].clone())); match a1.clone() { - Addr::Lis(mut l) => { - let mut result = Vec::new(); - - result.push(self.heap[l].as_addr(l)); - l += 1; - - loop { - match self.heap[l].clone() { - HeapCellValue::Addr(addr) => match self.store(self.deref(addr)) { - Addr::Lis(hcp) => { - result.push(self.heap[hcp].as_addr(hcp)); - l = hcp + 1; - } - Addr::Con(Constant::String(n, ref s)) - if !self.flags.double_quotes.is_atom() => - { - result.push(Addr::Con(Constant::String(n, s.clone()))); - break; - } - Addr::Con(Constant::EmptyList) => { - break; - } - Addr::HeapCell(_) | Addr::StackCell(..) => { - return Err( - self.error_form(MachineError::instantiation_error(), caller) - ) - } - _ => { - return Err(self.error_form( - MachineError::type_error(ValidType::List, a1), - caller, - )) - } - }, - _ => { - return Err(self - .error_form(MachineError::type_error(ValidType::List, a1), caller)) - } - } - } - - Ok(result) + Addr::Lis(l) => { + self.try_from_inner_list(vec![], l, caller, a1) } Addr::Con(Constant::String(n, ref s)) if !self.flags.double_quotes.is_atom() => { if s.len() > n { @@ -2601,11 +2899,117 @@ impl MachineState { Ok(vec![]) } } - Addr::HeapCell(_) | Addr::StackCell(..) => { + Addr::PStrLocation(h, n) => { + self.try_from_partial_string(vec![], h, n, caller, a1) + } + Addr::HeapCell(_) | Addr::StackCell(..) | Addr::PStrTail(..) => { Err(self.error_form(MachineError::instantiation_error(), caller)) } - Addr::Con(Constant::EmptyList) => Ok(vec![]), - _ => Err(self.error_form(MachineError::type_error(ValidType::List, a1), caller)), + Addr::Con(Constant::EmptyList) => { + Ok(vec![]) + } + _ => { + Err(self.error_form(MachineError::type_error(ValidType::List, a1), caller)) + } + } + } + + fn try_from_inner_list( + &self, + mut result: Vec, + mut l: usize, + caller: MachineStub, + a1: Addr, + ) -> Result, MachineStub> { + result.push(self.heap[l].as_addr(l)); + l += 1; + + loop { + match &self.heap[l] { + HeapCellValue::Addr(ref addr) => + match self.store(self.deref(addr.clone())) { + Addr::Lis(hcp) => { + result.push(self.heap[hcp].as_addr(hcp)); + l = hcp + 1; + } + Addr::Con(Constant::String(n, ref s)) + if !self.flags.double_quotes.is_atom() => + { + result.push(Addr::Con(Constant::String(n, s.clone()))); + break; + } + Addr::PStrLocation(h, n) => { + return self.try_from_partial_string(result, h, n, caller, a1); + } + Addr::Con(Constant::EmptyList) => { + break; + } + Addr::HeapCell(_) | Addr::StackCell(..) => { + return Err(self.error_form( + MachineError::instantiation_error(), + caller, + )) + } + _ => { + return Err(self.error_form( + MachineError::type_error(ValidType::List, a1), + caller, + )) + } + }, + _ => { + return Err(self.error_form( + MachineError::type_error(ValidType::List, a1), + caller, + )) + } + } + } + + Ok(result) + } + + fn try_from_partial_string( + &self, + mut chars: Vec, + mut h: usize, + mut n: usize, + caller: MachineStub, + a1: Addr, + ) -> Result, MachineStub> { + loop { + if let HeapCellValue::PartialString(ref pstr) = &self.heap[h] { + let s = pstr.block_as_str(); + + chars.extend(s[n ..].chars().map(|c| Addr::Con(Constant::Char(c)))); + + match self.store(self.deref(pstr.tail.clone())) { + Addr::Con(Constant::EmptyList) => { + return Ok(chars); + } + Addr::Con(Constant::String(n, ref s)) + if !self.flags.double_quotes.is_atom() => + { + chars.push(Addr::Con(Constant::String(n, s.clone()))); + return Ok(chars); + } + Addr::Lis(l) => { + return self.try_from_inner_list(chars, l, caller, a1); + } + Addr::PStrLocation(h1, n1) => { + h = h1; + n = n1; + } + _ => { + return Err(self.error_form( + MachineError::type_error(ValidType::List, a1), + caller, + )) + } + } + } else { + unreachable!() + } } } @@ -2738,7 +3142,8 @@ impl MachineState { } // returns true on failure. - pub(super) fn ground_test(&self) -> bool { + pub(super) + fn ground_test(&self) -> bool { let a = self.store(self.deref(self[temp_v!(1)].clone())); for v in self.acyclic_pre_order_iter(a) { @@ -2760,7 +3165,8 @@ impl MachineState { self.p = CodePtr::BuiltInClause(ct, self.p.local()); } - pub(super) fn allocate(&mut self, num_cells: usize) { + pub(super) + fn allocate(&mut self, num_cells: usize) { let e = self.stack.allocate_and_frame(num_cells); let and_frame = self.stack.index_and_frame_mut(e); @@ -2771,7 +3177,8 @@ impl MachineState { self.p += 1; } - pub(super) fn deallocate(&mut self) { + pub(super) + fn deallocate(&mut self) { let e = self.e; let frame = self.stack.index_and_frame(e); diff --git a/src/prolog/machine/mod.rs b/src/prolog/machine/mod.rs index e1f0265b0..6415dd4e1 100644 --- a/src/prolog/machine/mod.rs +++ b/src/prolog/machine/mod.rs @@ -19,6 +19,7 @@ pub mod machine_errors; pub mod machine_indices; pub(super) mod machine_state; pub mod modules; +mod partial_string; mod raw_block; mod stack; pub(super) mod term_expansion; diff --git a/src/prolog/machine/partial_string.rs b/src/prolog/machine/partial_string.rs new file mode 100644 index 000000000..189a8bae7 --- /dev/null +++ b/src/prolog/machine/partial_string.rs @@ -0,0 +1,161 @@ +use crate::prolog::machine::machine_indices::*; +use crate::prolog::machine::raw_block::*; + +use std::mem; +use std::ptr; +use std::slice; +use std::str; + +pub(crate) struct PartialStringTraits {} + +impl RawBlockTraits for PartialStringTraits { + #[inline] + fn init_size() -> usize { + 0 + } + + #[inline] + fn align() -> usize { + mem::align_of::() + } +} + +pub struct PartialString { + pub(super) buf: RawBlock, + pub(super) tail: Addr, +} + +impl Clone for PartialString { + #[inline] + fn clone(&self) -> Self { + self.clone_from_offset(0) + } +} + +impl PartialEq for PartialString { + #[inline] + fn eq(&self, other: &Self) -> bool { + self as *const _ == other as *const _ + } +} + +fn scan_for_terminator(src: &str) -> usize { + let mut terminator_idx = 0; + + for c in src.chars() { + if c == '\u{0}' { + break; + } + + terminator_idx += c.len_utf8(); + } + + terminator_idx +} + +impl PartialString { + pub(super) + fn new(src: &str, h: usize) -> Option<(Self, &str)> { + let pstr = PartialString { + buf: RawBlock::with_capacity(src.len() + '\u{0}'.len_utf8()), + tail: Addr::PStrTail(h, 0), + }; + + unsafe { + pstr.append_chars(src) + } + } + + unsafe fn append_chars(mut self, src: &str) -> Option<(Self, &str)> { + let terminator_idx = scan_for_terminator(src); + + if terminator_idx == 0 { + return None; + } + + let new_top = self.buf.new_block(terminator_idx + '\u{0}'.len_utf8()); + + ptr::copy( + src.as_ptr(), + self.buf.top as *mut _, + terminator_idx, + ); + + self.buf.top = (new_top as usize - '\u{0}'.len_utf8()) as *const _; + self.write_terminator_at(terminator_idx); + + Some(if terminator_idx != src.len() { + (self, &src[terminator_idx + '\u{0}'.len_utf8() ..]) + } else { + (self, "") + }) + } + + /* Ordinarily cloning of heap cell values is done in copy_term, + * so we rely on it to set the tail correctly. here it's set to PStrTail(0, 0), + * because we don't know its heap location. */ + pub(super) + fn clone_from_offset(&self, n: usize) -> Self { + let mut pstr = PartialString { + buf: RawBlock::with_capacity(self.len() + '\u{0}'.len_utf8()), + tail: Addr::PStrTail(0, 0), + }; + + unsafe { + let len = if self.len() > n { self.len() - n } else { 0 }; + let new_top = pstr.buf.new_block(len + '\u{0}'.len_utf8()); + + if len > 0 { + ptr::copy( + (self.buf.base as usize + n) as *mut u8, + pstr.buf.base as *mut _, + len, + ); + } + + pstr.write_terminator_at(len); + pstr.buf.top = (new_top as usize - '\u{0}'.len_utf8()) as *const _; + } + + pstr + } + + #[inline] + pub(super) + fn write_terminator_at(&mut self, index: usize) { + unsafe { + ptr::write( + (self.buf.base as usize + index) as *mut u8, + 0u8, + ); + } + } + + #[inline] + pub(crate) + fn block_as_str(&self) -> &str { + unsafe { + let slice = slice::from_raw_parts(self.buf.base, self.len()); + str::from_utf8(slice).unwrap() + } + } + + #[inline] + pub(crate) + fn tail_addr(&self) -> &Addr { + &self.tail + } + + #[inline] + pub fn len(&self) -> usize { + self.buf.top as usize - self.buf.base as usize + } + + #[inline] + pub fn truncate(&mut self, len: usize) { + if (len + self.buf.base as usize) < self.buf.top as usize { + self.buf.top = (len + self.buf.base as usize) as *const _; + self.write_terminator_at(len); + } + } +} diff --git a/src/prolog/machine/raw_block.rs b/src/prolog/machine/raw_block.rs index b9e7f7edb..3ae383d57 100644 --- a/src/prolog/machine/raw_block.rs +++ b/src/prolog/machine/raw_block.rs @@ -37,14 +37,32 @@ impl RawBlock { } pub(crate) - unsafe fn grow(&mut self) { - if self.size == 0 { - let layout = alloc::Layout::from_size_align_unchecked(T::init_size(), T::align()); + fn with_capacity(cap: usize) -> Self { + let mut block = RawBlock { size: 0, + base: ptr::null(), + top: ptr::null(), + _marker: PhantomData }; + + unsafe { + block.init_at_size(cap); + } + + block + } + + unsafe fn init_at_size(&mut self, cap: usize) { + let layout = alloc::Layout::from_size_align_unchecked(cap, T::align()); - self.base = alloc::alloc(layout) as *const _; - self.size = T::init_size(); + self.base = alloc::alloc(layout) as *const _; + self.size = cap; - self.top = T::base_offset(self.base); + self.top = T::base_offset(self.base); + } + + pub(super) + unsafe fn grow(&mut self) { + if self.size == 0 { + self.init_at_size(T::init_size()); } else { let layout = alloc::Layout::from_size_align_unchecked(T::init_size(), T::align()); let top_dist = self.top as usize - self.base as usize; diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index 1eb3f912f..6915a8638 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -50,19 +50,54 @@ pub fn next_keypress() -> ContinueResult { } struct BrentAlgState { - hare: usize, - tortoise: usize, + hare: Addr, + tortoise: Addr, power: usize, steps: usize, } impl BrentAlgState { - fn new(hare: usize) -> Self { + fn new(hare: Addr) -> Self { BrentAlgState { - hare, + hare: hare.clone(), tortoise: hare, power: 2, - steps: 1, + steps: 0, + } + } + + fn step(&mut self, hare: Addr) -> Option { + self.hare = hare; + self.steps += 1; + + if self.tortoise == self.hare { + return Some(CycleSearchResult::NotList); + } else if self.steps == self.power { + self.tortoise = self.hare.clone(); + self.power <<= 1; + } + + + None + } + + fn to_result(self) -> CycleSearchResult { + match self.hare { + addr @ Addr::HeapCell(_) | addr @ Addr::StackCell(..) | addr @ Addr::AttrVar(_) => { + CycleSearchResult::PartialList(self.steps, addr.as_var().unwrap()) + } + Addr::PStrLocation(h, n) => { + CycleSearchResult::PStrLocation(self.steps, h, n) + } + Addr::PStrTail(h, n) => { + CycleSearchResult::PStrTail(self.steps, h, n) + } + Addr::Con(Constant::EmptyList) => { + CycleSearchResult::ProperList(self.steps) + } + _ => { + CycleSearchResult::NotList + } } } } @@ -77,56 +112,107 @@ fn is_builtin_predicate(name: &ClauseName) -> bool { impl MachineState { // a step in Brent's algorithm. fn brents_alg_step(&self, brent_st: &mut BrentAlgState) -> Option { - match self.heap[brent_st.hare].clone() { - HeapCellValue::NamedStr(..) => Some(CycleSearchResult::NotList), - HeapCellValue::Addr(addr) => match self.store(self.deref(addr)) { - Addr::Con(Constant::EmptyList) => { - Some(CycleSearchResult::ProperList(brent_st.steps)) - } - Addr::HeapCell(_) | Addr::StackCell(..) => Some(CycleSearchResult::PartialList( + match self.store(self.deref(brent_st.hare.clone())) { + Addr::Con(Constant::EmptyList) => { + Some(CycleSearchResult::ProperList(brent_st.steps)) + } + addr @ Addr::HeapCell(_) | addr @ Addr::StackCell(..) | addr @ Addr::AttrVar(_) => { + Some(CycleSearchResult::PartialList( brent_st.steps, - brent_st.hare, - )), - Addr::Con(Constant::String(n, ref s)) if !self.flags.double_quotes.is_atom() => { - Some(CycleSearchResult::String(brent_st.steps, n, s.clone())) + addr.as_var().unwrap(), + )) + } + Addr::Con(Constant::String(n, s)) if !self.flags.double_quotes.is_atom() => { + if let Some(c) = s.chars().next() { + brent_st.step(Addr::Con(Constant::String(n + c.len_utf8(), s))) + } else { + Some(CycleSearchResult::CompleteString(s.len(), s)) } - Addr::Lis(l) => { - brent_st.hare = l + 1; - brent_st.steps += 1; + } + Addr::PStrTail(h, n) => { + Some(CycleSearchResult::PStrTail(brent_st.steps, h, n)) + } + Addr::PStrLocation(h, n) => { + match &self.heap[h] { + HeapCellValue::PartialString(ref pstr) => { + let s = pstr.block_as_str(); - if brent_st.tortoise == brent_st.hare { - return Some(CycleSearchResult::NotList); - } else if brent_st.steps == brent_st.power { - brent_st.tortoise = brent_st.hare; - brent_st.power <<= 1; + if let Some(c) = s[n ..].chars().next() { + brent_st.step(Addr::PStrTail(h, n + c.len_utf8())) + } else { + unreachable!() + } + } + _ => { + unreachable!() } - - None - } - _ => { - Some(CycleSearchResult::NotList) } - }, + } + Addr::Lis(l) => { + brent_st.step(Addr::HeapCell(l + 1)) + } + _ => { + Some(CycleSearchResult::NotList) + } } } - pub(super) fn detect_cycles_with_max(&self, max_steps: usize, addr: Addr) -> CycleSearchResult { - let addr = self.store(self.deref(addr)); - let hare = match addr { - Addr::Lis(offset) if max_steps > 0 => offset + 1, - Addr::Lis(offset) => return CycleSearchResult::UntouchedList(offset), - Addr::Con(Constant::EmptyList) => return CycleSearchResult::EmptyList, - Addr::Con(Constant::String(n, ref s)) if !self.flags.double_quotes.is_atom() => { - return CycleSearchResult::String(0, n, s.clone()) + pub(super) + fn detect_cycles_with_max(&self, max_steps: usize, addr: Addr) -> CycleSearchResult { + let hare = match self.store(self.deref(addr)) { + Addr::Lis(offset) if max_steps > 0 => { + Addr::Lis(offset) + } + Addr::Lis(offset) => { + return CycleSearchResult::UntouchedList(offset); + } + Addr::PStrLocation(h, n) if max_steps > 0 => { + Addr::PStrLocation(h, n) + } + Addr::PStrLocation(h, _) => { + return CycleSearchResult::UntouchedList(h); + } + Addr::PStrTail(h, n) => { + return CycleSearchResult::PStrTail(0, h, n); + } + Addr::Con(Constant::EmptyList) => { + return CycleSearchResult::EmptyList; + } + Addr::Con(Constant::String(0, ref s)) + if max_steps > 0 && !self.flags.double_quotes.is_atom() => { + if max_steps >= s.len() { + return CycleSearchResult::CompleteString(s.len(), s.clone()); + } else { + return CycleSearchResult::UntouchedString(max_steps, s.clone()); + } + } + Addr::Con(Constant::String(0, ref s)) + if !self.flags.double_quotes.is_atom() => { + return CycleSearchResult::UntouchedString(0, s.clone()); + } + Addr::Con(Constant::String(n, ref s)) + if max_steps > 0 && !self.flags.double_quotes.is_atom() => { + if max_steps >= s.len() - n { + let s = Rc::new(String::from(&s[n ..])); + return CycleSearchResult::CompleteString(s.len(), s); + } else { + return CycleSearchResult::UntouchedString(max_steps, s.clone()); + } + } + Addr::Con(Constant::String(n, ref s)) + if !self.flags.double_quotes.is_atom() => { + return CycleSearchResult::UntouchedString(n, s.clone()); + } + _ => { + return CycleSearchResult::NotList; } - _ => return CycleSearchResult::NotList, }; let mut brent_st = BrentAlgState::new(hare); loop { if brent_st.steps == max_steps { - return CycleSearchResult::PartialList(brent_st.steps, brent_st.hare); + return brent_st.to_result(); } if let Some(result) = self.brents_alg_step(&mut brent_st) { @@ -135,15 +221,32 @@ impl MachineState { } } - pub(super) fn detect_cycles(&self, addr: Addr) -> CycleSearchResult { + pub(super) + fn detect_cycles(&self, addr: Addr) -> CycleSearchResult { let addr = self.store(self.deref(addr)); let hare = match addr { - Addr::Lis(offset) => offset + 1, - Addr::Con(Constant::EmptyList) => return CycleSearchResult::EmptyList, + Addr::Lis(offset) => { + Addr::Lis(offset) + } + Addr::Con(Constant::EmptyList) => { + return CycleSearchResult::EmptyList; + } + Addr::PStrLocation(h, n) => { + Addr::PStrLocation(h, n) + } + Addr::PStrTail(h, n) => { + return CycleSearchResult::PStrTail(0, h, n); + } + Addr::Con(Constant::String(0, ref s)) if !self.flags.double_quotes.is_atom() => { + return CycleSearchResult::CompleteString(s.len(), s.clone()); + } Addr::Con(Constant::String(n, ref s)) if !self.flags.double_quotes.is_atom() => { - return CycleSearchResult::String(0, n, s.clone()) + let s = Rc::new(String::from(&s[n ..])); + return CycleSearchResult::CompleteString(s.len(), s); + } + _ => { + return CycleSearchResult::NotList; } - _ => return CycleSearchResult::NotList, }; let mut brent_st = BrentAlgState::new(hare); @@ -181,45 +284,32 @@ impl MachineState { self.unify(xs0, xs); } _ => { - let (max_steps, search_result) = + let search_result = if let Some(max_steps) = max_steps.to_isize() { - ( - max_steps, - if max_steps == -1 { - self.detect_cycles(self[temp_v!(3)].clone()) - } else { - self.detect_cycles_with_max( - max_steps as usize, - self[temp_v!(3)].clone(), - ) - }, - ) + if max_steps == -1 { + self.detect_cycles(self[temp_v!(3)].clone()) + } else { + self.detect_cycles_with_max( + max_steps as usize, + self[temp_v!(3)].clone(), + ) + } } else { - (-1, self.detect_cycles(self[temp_v!(3)].clone())) + self.detect_cycles(self[temp_v!(3)].clone()) }; match search_result { - CycleSearchResult::String(n, offset, s) => { - if max_steps == -1 { - self.finalize_skip_max_list( - s[offset ..].len(), - Addr::Con(Constant::EmptyList), - ) - } else { - let i = (max_steps as usize) - n; - - if s.len() < i { - self.finalize_skip_max_list( - s[n + offset + i..].len(), - Addr::Con(Constant::EmptyList), - ) - } else { - self.finalize_skip_max_list( - i + n + offset, - Addr::Con(Constant::String(n + i + offset, s)), - ) - } - } + CycleSearchResult::PStrTail(steps, h, n) => { + self.finalize_skip_max_list(steps, Addr::PStrTail(h, n)); + } + CycleSearchResult::PStrLocation(steps, h, n) => { + self.finalize_skip_max_list(steps, Addr::PStrLocation(h, n)); + } + CycleSearchResult::CompleteString(n, _) => { + self.finalize_skip_max_list(n, Addr::Con(Constant::EmptyList)); + } + CycleSearchResult::UntouchedString(n, s) => { + self.finalize_skip_max_list(0, Addr::Con(Constant::String(n, s))); } CycleSearchResult::UntouchedList(l) => { self.finalize_skip_max_list(0, Addr::Lis(l)) @@ -227,11 +317,11 @@ impl MachineState { CycleSearchResult::EmptyList => { self.finalize_skip_max_list(0, Addr::Con(Constant::EmptyList)) } - CycleSearchResult::PartialList(n, hc) => { - self.finalize_skip_max_list(n, Addr::HeapCell(hc)) + CycleSearchResult::PartialList(n, r) => { + self.finalize_skip_max_list(n, r.as_addr()) } - CycleSearchResult::ProperList(n) => { - self.finalize_skip_max_list(n, Addr::Con(Constant::EmptyList)) + CycleSearchResult::ProperList(steps) => { + self.finalize_skip_max_list(steps, Addr::Con(Constant::EmptyList)) } CycleSearchResult::NotList => { let xs0 = self[temp_v!(3)].clone(); @@ -329,7 +419,7 @@ impl MachineState { fn copy_findall_solution(&mut self, lh_offset: usize, copy_target: Addr) -> usize { let threshold = self.lifted_heap.h() - lh_offset; - + let mut copy_ball_term = CopyBallTerm::new( &mut self.stack, &mut self.heap, @@ -341,6 +431,7 @@ impl MachineState { copy_ball_term.push(HeapCellValue::Addr(Addr::HeapCell(threshold + 2))); copy_term(copy_ball_term, copy_target, AttrVarPolicy::DeepCopy); + threshold + lh_offset + 2 } @@ -364,8 +455,7 @@ impl MachineState { self.lifted_heap.truncate(lh_offset); } else { let threshold = self.lifted_heap.h() - lh_offset; - self.lifted_heap - .push(HeapCellValue::Addr(addr_constr(threshold))); + self.lifted_heap.push(HeapCellValue::Addr(addr_constr(threshold))); } } _ => self.fail = true, @@ -569,7 +659,8 @@ impl MachineState { } } - pub(super) fn system_call( + pub(super) + fn system_call( &mut self, ct: &SystemClauseType, code_repo: &CodeRepo, @@ -835,6 +926,78 @@ impl MachineState { }, } } + &SystemClauseType::CreatePartialString => { + let atom = match self.store(self.deref(self[temp_v!(1)].clone())) { + Addr::Con(Constant::Atom(atom, _)) => { + atom + } + _ => { + unreachable!() + } + }; + + let h = self.heap.h(); + let pstr = + match self.heap.allocate_pstr(atom.as_str()) { + Some(pstr_addr) => { + pstr_addr + } + None => { + let stub = MachineError::functor_stub(clause_name!("partial_string"), 3); + let err = MachineError::type_error( + ValidType::Character, + Addr::Con(Constant::Char('\u{0}')), + ); + + return Err(self.error_form(err, stub)); + } + }; + + let pstr_tail = match &self.heap[h] { + HeapCellValue::PartialString(ref pstr) => { + pstr.tail_addr().clone() + } + _ => { + unreachable!() + } + }; + + self.unify(self[temp_v!(2)].clone(), pstr); + + if !self.fail { + self.unify(self[temp_v!(3)].clone(), pstr_tail); + } + } + &SystemClauseType::IsPartialString => { + let pstr = self.store(self.deref(self[temp_v!(1)].clone())); + + match pstr { + Addr::PStrLocation(..) => { + } + _ => { + self.fail = true; + } + } + } + &SystemClauseType::PartialStringTail => { + let pstr = self.store(self.deref(self[temp_v!(1)].clone())); + + match pstr { + Addr::PStrLocation(h, _) => { + let tail = if let HeapCellValue::PartialString(ref pstr) = &self.heap[h] { + pstr.tail.clone() + } else { + unreachable!() + }; + + let target = self[temp_v!(2)].clone(); + self.unify(tail, target); + } + _ => { + unreachable!() + } + } + } &SystemClauseType::NumberToChars => { let n = self[temp_v!(1)].clone(); let chs = self[temp_v!(2)].clone(); @@ -925,7 +1088,7 @@ impl MachineState { Addr::Con(Constant::Integer(n)) => { let c = self.int_to_char_code(&n, "char_code", 2)?; let c = std::char::from_u32(c).unwrap(); - + self.unify(Addr::Con(Constant::Char(c)), addr.clone()); } _ => self.fail = true, @@ -1119,9 +1282,12 @@ impl MachineState { for addr in self.lifted_heap.iter_mut_from(old_threshold + 1) { match addr { - &mut HeapCellValue::Addr(ref mut addr) => { + HeapCellValue::Addr(ref mut addr) => { *addr -= self.heap.h() + lh_offset } + HeapCellValue::PartialString(ref mut pstr) => { + pstr.tail -= self.heap.h() + lh_offset; + } _ => {} } } @@ -1579,22 +1745,34 @@ impl MachineState { for value in self.lifted_heap.iter_from(lh_offset) { last_index = self.heap.h(); - - match value.clone() { - HeapCellValue::Addr(addr) => { - self.heap.push(HeapCellValue::Addr(addr + h)); + + match value { + HeapCellValue::Addr(ref addr) => { + self.heap.push(HeapCellValue::Addr(addr.clone() + h)); + } + HeapCellValue::PartialString(ref pstr) => { + let mut new_pstr = pstr.clone(); + new_pstr.tail = pstr.tail.clone() + h; + self.heap.push(HeapCellValue::PartialString(new_pstr)); } value => { - self.heap.push(value); + self.heap.push(value.clone()); } } } if last_index < self.heap.h() { - if let HeapCellValue::Addr(addr) = self.heap[last_index].clone() { + let addr_opt = + if let HeapCellValue::Addr(ref addr) = &self.heap[last_index] { + Some(addr.clone()) + } else { + None + }; + + addr_opt.map(|addr| { let diff = self[temp_v!(3)].clone(); self.unify(diff, addr); - } + }); } self.lifted_heap.truncate(lh_offset); @@ -1617,13 +1795,18 @@ impl MachineState { } else { let h = self.heap.h(); - for addr in self.lifted_heap.iter_from(lh_offset).cloned() { + for addr in self.lifted_heap.iter_from(lh_offset) { match addr { - HeapCellValue::Addr(addr) => { - self.heap.push(HeapCellValue::Addr(addr + h)) + HeapCellValue::Addr(ref addr) => { + self.heap.push(HeapCellValue::Addr(addr.clone() + h)) + } + HeapCellValue::PartialString(ref pstr) => { + let mut new_pstr = pstr.clone(); + new_pstr.tail = pstr.tail.clone() + h; + self.heap.push(HeapCellValue::PartialString(new_pstr)); } value => { - self.heap.push(value); + self.heap.push(value.clone()); } } } diff --git a/src/prolog/write.rs b/src/prolog/write.rs index f76694fc2..96a529196 100644 --- a/src/prolog/write.rs +++ b/src/prolog/write.rs @@ -154,6 +154,9 @@ impl fmt::Display for HeapCellValue { &HeapCellValue::NamedStr(arity, ref name, None) => { write!(f, "{}/{}", name.as_str(), arity) } + &HeapCellValue::PartialString(ref pstr) => { + write!(f, "pstr ( buf: {}, tail: {} )", pstr.block_as_str(), pstr.tail_addr()) + } } } } @@ -179,6 +182,8 @@ impl fmt::Display for Addr { &Addr::HeapCell(h) => write!(f, "Addr::HeapCell({})", h), &Addr::StackCell(fr, sc) => write!(f, "Addr::StackCell({}, {})", fr, sc), &Addr::Str(s) => write!(f, "Addr::Str({})", s), + &Addr::PStrLocation(h, n) => write!(f, "Addr::PStrLocation({}, {})", h, n), + &Addr::PStrTail(h, n) => write!(f, "Addr::PStrTail({}, {})", h, n), } } }