From 9dbadac14fc38925abee4de3ab36aabe7829dcb8 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sun, 28 Apr 2019 21:09:56 -0600 Subject: [PATCH] correct various bugs --- Cargo.toml | 4 +- src/prolog/lib/builtins.pl | 57 +++++++++++++++++------- src/prolog/machine/machine_errors.rs | 2 + src/prolog/machine/machine_state.rs | 51 ++++++++++++--------- src/prolog/machine/machine_state_impl.rs | 31 ++++++++++++- src/prolog/machine/system_calls.rs | 57 +++++++++++++++++------- 6 files changed, 145 insertions(+), 57 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 384e1f8f..0bcfb2d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "scryer-prolog" -version = "0.8.73" +version = "0.8.74" authors = ["Mark Thom "] repository = "https://github.com/mthom/scryer-prolog" description = "A modern Prolog implementation written mostly in Rust." @@ -14,7 +14,7 @@ cfg-if = "0.1.7" downcast = "0.10.0" num = "0.2" ordered-float = "0.5.0" -prolog_parser = "0.8.23" +prolog_parser = "0.8.24" readline_rs_compat = { version = "0.1.9", optional = true } ref_thread_local = "0.0.0" diff --git a/src/prolog/lib/builtins.pl b/src/prolog/lib/builtins.pl index 3a8d81e7..1c8ea40d 100644 --- a/src/prolog/lib/builtins.pl +++ b/src/prolog/lib/builtins.pl @@ -733,27 +733,31 @@ atom_length(Atom, Length) :- ; throw(error(type_error(atom, Atom), atom_length/2)) % 8.16.1.3 b) ). -no_var_in_list([]). -no_var_in_list([X|Xs]) :- var(X), !, '$fail'. -no_var_in_list([_|Xs]) :- nonvar(Xs), no_var_in_list(Xs). - atom_chars(Atom, List) :- + '$skip_max_list'(_, -1, List, Tail), + ( ( Tail == [] ; var(Tail) ) -> true + ; throw(error(type_error(list, List), atom_chars/2)) + ), ( var(Atom) -> - ( var(List) -> throw(error(instantiation_error, atom_chars/2)) - ; can_be_list(List, atom_chars/3), no_var_in_list(List) -> '$atom_chars'(Atom, List) + ( var(Tail) -> throw(error(instantiation_error, atom_chars/2)) + ; ground(List), Tail == [] -> '$atom_chars'(Atom, List) ; throw(error(instantiation_error, atom_chars/2)) ) - ; atom(Atom) -> '$atom_chars'(Atom, List) + ; atom(Atom) -> can_be_chars_or_vars(List, atom_chars/2), '$atom_chars'(Atom, List) ; throw(error(type_error(atom, Atom), atom_chars/2)) ). atom_codes(Atom, List) :- + '$skip_max_list'(_, -1, List, Tail), + ( ( Tail == [] ; var(Tail) ) -> true + ; throw(error(type_error(list, List), atom_codes/2)) + ), ( var(Atom) -> - ( var(List) -> throw(error(instantiation_error, atom_codes/2)) - ; can_be_list(List, atom_codes/3), no_var_in_list(List) -> '$atom_codes'(Atom, List) + ( var(Tail) -> throw(error(instantiation_error, atom_codes/2)) + ; ground(List), Tail == [] -> '$atom_codes'(Atom, List) ; throw(error(instantiation_error, atom_codes/2)) ) - ; atom(Atom) -> '$atom_codes'(Atom, List) + ; atom(Atom) -> can_be_codes_or_vars(List, atom_codes/2), '$atom_codes'(Atom, List) ; throw(error(type_error(atom, Atom), atom_codes/2)) ). @@ -789,16 +793,35 @@ must_be_number(N, PI) :- ; throw(error(instantiation_error, PI)) ). -must_be_chars([], _). -must_be_chars([C|Cs], PI) :- +can_be_chars_or_vars(Cs, _) :- var(Cs), !. +can_be_chars_or_vars(Cs, PI) :- chars_or_vars(Cs, PI). + +chars_or_vars([], _). +chars_or_vars([C|Cs], PI) :- ( nonvar(C) -> - ( atom_length(C, 1) -> - ( nonvar(Cs) -> must_be_chars(Cs, PI) - ; false %% throw(error(type_error(list, Cs), PI)) + ( catch(atom_length(C, 1), _, false) -> + ( nonvar(Cs) -> chars_or_vars(Cs, PI) + ; false ) ; throw(error(type_error(character, C), PI)) ) - ; must_be_chars(Cs, PI) + ; chars_or_vars(Cs, PI) + ). + +can_be_codes_or_vars(Cs, _) :- var(Cs), !. +can_be_codes_or_vars(Cs, PI) :- codes_or_vars(Cs, PI). + +codes_or_vars([], _). +codes_or_vars([C|Cs], PI) :- + ( nonvar(C) -> + ( catch(char_code(_, C), _, false) -> + ( nonvar(Cs) -> codes_or_vars(Cs, PI) + ; false + ) + ; integer(C) -> throw(error(representation_error(character_code), PI)) + ; throw(error(type_error(integer, C), PI)) + ) + ; codes_or_vars(Cs, PI) ). number_chars(N, Chs) :- @@ -810,7 +833,7 @@ number_chars(N, Chs) :- ; must_be_number(N, number_chars/2), ( var(Chs) -> true ; can_be_list(Chs, number_chars/2) - , must_be_chars(Chs, number_chars/2) + , chars_or_vars(Chs, number_chars/2) ), '$number_to_chars'(N, Chsx), Chsx = Chs diff --git a/src/prolog/machine/machine_errors.rs b/src/prolog/machine/machine_errors.rs index bdfdd343..e9eb56f3 100644 --- a/src/prolog/machine/machine_errors.rs +++ b/src/prolog/machine/machine_errors.rs @@ -1,4 +1,5 @@ use prolog_parser::ast::*; +use prolog_parser::string_list::*; use prolog::machine::machine_indices::*; use prolog::machine::machine_state::*; @@ -273,6 +274,7 @@ pub(super) enum CycleSearchResult { NotList, PartialList(usize, usize), // the list length (up to max), and an offset into the heap. ProperList(usize), // the list length. + String(usize, StringList), // the number of elements iterated, the string tail. UntouchedList(usize) // the address of an uniterated Addr::Lis(address). } diff --git a/src/prolog/machine/machine_state.rs b/src/prolog/machine/machine_state.rs index a870c21e..3994787c 100644 --- a/src/prolog/machine/machine_state.rs +++ b/src/prolog/machine/machine_state.rs @@ -239,6 +239,36 @@ pub struct MachineState { } impl MachineState { + pub(super) + fn try_char_list(&self, addrs: Vec) -> Result + { + let mut chars = String::new(); + let mut iter = addrs.iter(); + + while let Some(addr) = iter.next() { + match addr { + &Addr::Con(Constant::String(ref s)) + if self.flags.double_quotes.is_chars() => { + chars += s.borrow().as_str(); + + if iter.next().is_some() { + return Err(MachineError::type_error(ValidType::Character, addr.clone())); + } + }, + &Addr::Con(Constant::Char(c)) => + chars.push(c), + &Addr::Con(Constant::Atom(ref name, _)) + if name.as_str().len() == 1 => { + chars += name.as_str(); + }, + _ => + return Err(MachineError::type_error(ValidType::Character, addr.clone())) + } + } + + Ok(chars) + } + fn call_at_index(&mut self, arity: usize, p: usize) { self.cp.assign_if_local(self.p.clone() + 1); @@ -320,27 +350,6 @@ fn try_in_situ(machine_st: &mut MachineState, name: ClauseName, arity: usize, } } -pub(super) -fn try_char_list(addrs: Vec) -> Result -{ - let mut chars = String::new(); - - for addr in addrs.iter() { - match addr { - &Addr::Con(Constant::Char(c)) => - chars.push(c), - &Addr::Con(Constant::Atom(ref name, _)) - if name.as_str().len() == 1 => { - chars += name.as_str(); - }, - _ => - return Err(MachineError::type_error(ValidType::Character, addr.clone())) - } - } - - Ok(chars) -} - pub(crate) type CallResult = Result<(), Vec>; pub(crate) trait CallPolicy: Any { diff --git a/src/prolog/machine/machine_state_impl.rs b/src/prolog/machine/machine_state_impl.rs index f022f0d9..a5d11bb8 100644 --- a/src/prolog/machine/machine_state_impl.rs +++ b/src/prolog/machine/machine_state_impl.rs @@ -1691,6 +1691,21 @@ impl MachineState { } else { self.fail = true; }, + Addr::Con(Constant::String(ref s)) + if self.flags.double_quotes.is_chars() && !s.is_empty() => { + if n == 1 || n == 2 { + let a3 = self[temp_v!(3)].clone(); + let h_a = if n == 1 { + Addr::Con(Constant::Char(s.head().unwrap())) + } else { + Addr::Con(Constant::String(s.tail())) + }; + + self.unify(a3, h_a); + } else { + self.fail = true; + } + }, _ => // 8.5.2.3 d) return Err(self.error_form(MachineError::type_error(ValidType::Compound, term), stub)) @@ -2089,6 +2104,11 @@ impl MachineState { match a1.clone() { Addr::DBRef(_) => self.fail = true, + Addr::Con(Constant::String(ref s)) + if self.flags.double_quotes.is_chars() && !s.is_empty() => { + 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::Con(_) => self.try_functor_unify_components(a1, Addr::Con(integer!(0))), Addr::Str(o) => @@ -2187,7 +2207,7 @@ impl MachineState { match self.try_from_list(r, stub.clone()) { Ok(addrs) => - Ok(StringList::new(match try_char_list(addrs) { + Ok(StringList::new(match self.try_char_list(addrs) { Ok(string) => string, Err(err) => { return Err(self.error_form(err, stub)); @@ -2218,6 +2238,11 @@ impl MachineState { result.push(self.heap[hcp].as_addr(hcp)); l = hcp + 1; }, + Addr::Con(Constant::String(ref s)) + if self.flags.double_quotes.is_chars() => { + result.push(Addr::Con(Constant::String(s.clone()))); + break; + }, Addr::Con(Constant::EmptyList) => break, Addr::HeapCell(_) | Addr::StackCell(..) => @@ -2229,11 +2254,13 @@ impl MachineState { _ => return Err(self.error_form(MachineError::type_error(ValidType::List, a1), caller)) - }; + } } Ok(result) }, + Addr::Con(Constant::String(ref s)) if self.flags.double_quotes.is_chars() => + Ok(vec![Addr::Con(Constant::String(s.clone()))]), Addr::HeapCell(_) | Addr::StackCell(..) => Err(self.error_form(MachineError::instantiation_error(), caller)), Addr::Con(Constant::EmptyList) => diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index e463b812..bf0735fc 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -1,5 +1,6 @@ use prolog_parser::ast::*; use prolog_parser::parser::*; +use prolog_parser::string_list::*; use prolog_parser::tabled_rc::*; use prolog::clause_types::*; @@ -49,6 +50,9 @@ impl MachineState { Some(CycleSearchResult::ProperList(brent_st.steps)), Addr::HeapCell(_) | Addr::StackCell(..) => Some(CycleSearchResult::PartialList(brent_st.steps, brent_st.hare)), + Addr::Con(Constant::String(ref s)) + if self.flags.double_quotes.is_chars() => + Some(CycleSearchResult::String(brent_st.steps, s.clone())), Addr::Lis(l) => { brent_st.hare = l + 1; brent_st.steps += 1; @@ -75,6 +79,9 @@ impl MachineState { 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(ref s)) + if self.flags.double_quotes.is_chars() => + return CycleSearchResult::String(0, s.clone()), _ => return CycleSearchResult::NotList }; @@ -97,6 +104,9 @@ impl MachineState { let hare = match addr { Addr::Lis(offset) => offset + 1, Addr::Con(Constant::EmptyList) => return CycleSearchResult::EmptyList, + Addr::Con(Constant::String(ref s)) + if self.flags.double_quotes.is_chars() => + return CycleSearchResult::String(0, s.clone()), _ => return CycleSearchResult::NotList }; @@ -135,18 +145,35 @@ impl MachineState { self.unify(xs0, xs); }, _ => { - let search_result = if let Some(max_steps) = max_steps.to_isize() { - if max_steps == -1 { - self.detect_cycles(self[temp_v!(3)].clone()) + let (max_steps, 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()) + }) } else { - self.detect_cycles_with_max(max_steps as usize, - self[temp_v!(3)].clone()) - } - } else { - self.detect_cycles(self[temp_v!(3)].clone()) - }; + (-1, self.detect_cycles(self[temp_v!(3)].clone())) + }; match search_result { + CycleSearchResult::String(n, s) => + if max_steps == -1 { + self.finalize_skip_max_list(n + s.len(), + Addr::Con(Constant::EmptyList)) + } else { + let i = max_steps.to_usize().unwrap() - n; + + if s.len() < i { + self.finalize_skip_max_list(n + s.len(), + Addr::Con(Constant::EmptyList)) + } else { + let s = StringList::new(s.char_span(i), s.is_expandable()); + self.finalize_skip_max_list(i + n, + Addr::Con(Constant::String(s))) + } + }, CycleSearchResult::UntouchedList(l) => self.finalize_skip_max_list(0, Addr::Lis(l)), CycleSearchResult::EmptyList => @@ -395,7 +422,7 @@ impl MachineState { match self.try_from_list(temp_v!(2), stub.clone()) { Err(e) => return Err(e), Ok(addrs) => - match try_char_list(addrs) { + match self.try_char_list(addrs) { Ok(string) => { let chars = clause_name!(string, indices.atom_tbl); self.unify(addr.clone(), Addr::Con(Constant::Atom(chars, None))); @@ -453,7 +480,7 @@ impl MachineState { &Addr::Con(Constant::CharCode(c)) => chars.push(c as char), _ => { - let err = MachineError::representation_error(RepFlag::CharacterCode); + let err = MachineError::type_error(ValidType::Integer, addr.clone()); return Err(self.error_form(err, stub)); } } @@ -489,19 +516,19 @@ impl MachineState { match self.try_from_list(temp_v!(1), stub.clone()) { Err(e) => return Err(e), Ok(addrs) => - match try_char_list(addrs) { + match self.try_char_list(addrs) { Ok(mut string) => { if let Some(c) = string.chars().last() { if layout_char!(c) { let err = ParserError::UnexpectedChar(c); - + let h = self.heap.h; let err = MachineError::syntax_error(h, err); return Err(self.error_form(err, stub)); } } - + string.push('.'); let mut stream = parsing_stream(std::io::Cursor::new(string)); @@ -521,7 +548,7 @@ impl MachineState { self.unify(nx, Addr::Con(Constant::CharCode(c))), _ => { let err = ParserError::ParseBigInt; - + let h = self.heap.h; let err = MachineError::syntax_error(h, err); -- 2.54.0