From 573df892bca30cc6f33ed9781d289994d45266c8 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Tue, 5 May 2020 16:54:40 -0600 Subject: [PATCH] cleanup on reading predicates, add get_char/{1,2} --- src/prolog/clause_types.rs | 2 +- src/prolog/lib/builtins.pl | 18 ++-- src/prolog/machine/machine_errors.rs | 8 +- src/prolog/machine/machine_state.rs | 31 +++--- src/prolog/machine/streams.rs | 59 +++++++++++ src/prolog/machine/system_calls.rs | 152 +++++++++++++++++++-------- 6 files changed, 194 insertions(+), 76 deletions(-) diff --git a/src/prolog/clause_types.rs b/src/prolog/clause_types.rs index 99282013..614df359 100644 --- a/src/prolog/clause_types.rs +++ b/src/prolog/clause_types.rs @@ -490,7 +490,7 @@ impl SystemClauseType { ("$fetch_global_var_with_offset", 3) => Some(SystemClauseType::FetchGlobalVarWithOffset), ("$file_to_chars", 2) => Some(SystemClauseType::FileToChars), ("$get_byte", 2) => Some(SystemClauseType::GetByte), - ("$get_char", 1) => Some(SystemClauseType::GetChar), + ("$get_char", 2) => Some(SystemClauseType::GetChar), ("$get_single_char", 1) => Some(SystemClauseType::GetSingleChar), ("$points_to_cont_reset_marker", 1) => { Some(SystemClauseType::PointsToContinuationResetMarker) diff --git a/src/prolog/lib/builtins.pl b/src/prolog/lib/builtins.pl index ca6d738e..22c7619f 100644 --- a/src/prolog/lib/builtins.pl +++ b/src/prolog/lib/builtins.pl @@ -49,10 +49,10 @@ user:term_expansion((:- op(Pred, Spec, [Op | OtherOps])), OpResults) :- current_prolog_flag/2, expand_goal/2, expand_term/2, fail/0, false/0, findall/3, findall/4, flush_output/0, flush_output/1, - get_byte/1, get_byte/2, get_char/1, halt/0, - max_arity/1, number_chars/2, number_codes/2, - once/1, op/3, open/3, open/4, read_term/2, - read_term/3, repeat/0, retract/1, + get_byte/1, get_byte/2, get_char/1, get_char/2, + halt/0, max_arity/1, number_chars/2, + number_codes/2, once/1, op/3, open/3, open/4, + read_term/2, read_term/3, repeat/0, retract/1, set_prolog_flag/2, set_input/1, set_output/1, setof/3, sub_atom/5, subsumes_term/2, term_variables/2, throw/1, true/0, @@ -1000,11 +1000,11 @@ char_code(Char, Code) :- ). get_char(C) :- - ( var(C) -> '$get_char'(C) - ; C == end_of_file -> '$get_char'(C) - ; atom_length(C, 1) -> '$get_char'(C) - ; throw(error(type_error(in_character, C), get_char/1)) - ). + current_input(S), + '$get_char'(S, C). + +get_char(S, C) :- + '$get_char'(S, C). can_be_number(N, PI) :- ( var(N) -> true diff --git a/src/prolog/machine/machine_errors.rs b/src/prolog/machine/machine_errors.rs index fdbd1d85..5afae443 100644 --- a/src/prolog/machine/machine_errors.rs +++ b/src/prolog/machine/machine_errors.rs @@ -496,7 +496,7 @@ pub enum ValidType { Evaluable, Float, InByte, - // InCharacter, + InCharacter, Integer, List, // Number, @@ -519,7 +519,7 @@ impl ValidType { ValidType::Evaluable => "evaluable", ValidType::Float => "float", ValidType::InByte => "in_byte", - // ValidType::InCharacter => "in_character", + ValidType::InCharacter => "in_character", ValidType::Integer => "integer", ValidType::List => "list", // ValidType::Number => "number", @@ -555,7 +555,7 @@ impl DomainErrorType { // from 7.12.2 f) of 13211-1:1995 #[derive(Debug, Clone, Copy)] pub enum RepFlag { - Character, + // Character, CharacterCode, // InCharacterCode, MaxArity, @@ -566,7 +566,7 @@ pub enum RepFlag { impl RepFlag { pub fn as_str(self) -> &'static str { match self { - RepFlag::Character => "character", + // RepFlag::Character => "character", RepFlag::CharacterCode => "character_code", // RepFlag::InCharacterCode => "in_character_code", RepFlag::MaxArity => "max_arity", diff --git a/src/prolog/machine/machine_state.rs b/src/prolog/machine/machine_state.rs index 9a7a1aa0..b16389d1 100644 --- a/src/prolog/machine/machine_state.rs +++ b/src/prolog/machine/machine_state.rs @@ -616,26 +616,23 @@ impl MachineState { pub(crate) fn read_term( &mut self, - stream: Stream, + mut stream: Stream, indices: &mut IndexStore, ) -> CallResult { - let opt_err = - if !stream.is_input_stream() { - Some("stream") // 8.14.2.3 g) - } else if stream.options.stream_type == StreamType::Binary { - Some("binary_stream") // 8.14.2.3 h) - } else { - None - }; + self.check_stream_properties( + &mut stream, + StreamType::Text, + Some(self[temp_v!(2)]), + clause_name!("read_term"), + 3, + )?; - if let Some(err_string) = opt_err { - return Err(self.stream_permission_error( - Permission::InputStream, - err_string, - stream, - clause_name!("read_term"), - 3, - )); + if stream.past_end_of_stream { + if EOFAction::Reset != stream.options.eof_action { + return return_from_clause!(self.last_call, self); + } else if self.fail { + return Ok(()); + } } let mut orig_stream = stream.clone(); diff --git a/src/prolog/machine/streams.rs b/src/prolog/machine/streams.rs index 5be540cb..7e11c0df 100644 --- a/src/prolog/machine/streams.rs +++ b/src/prolog/machine/streams.rs @@ -21,6 +21,17 @@ pub enum StreamType { Text, } +impl StreamType { + #[inline] + pub(crate) + fn as_str(&self) -> &'static str { + match self { + StreamType::Binary => "binary_stream", + StreamType::Text => "text_stream", + } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum EOFAction { EOFCode, @@ -615,6 +626,54 @@ impl MachineState { return self.error_form(err, stub); } + + + pub(crate) + fn check_stream_properties( + &mut self, + stream: &mut Stream, + expected_type: StreamType, + input: Option, + caller: ClauseName, + arity: usize, + ) -> CallResult { + let opt_err = + if input.is_some() && !stream.is_input_stream() { + Some("stream") // 8.14.2.3 g) + } else if input.is_none() && stream.is_input_stream() { + Some("stream") // 8.14.2.3 g) + } else if stream.options.stream_type != expected_type { + Some(expected_type.as_str()) // 8.14.2.3 h) + } else { + None + }; + + let permission = + if input.is_some() { Permission::InputStream } else { Permission::OutputStream }; + + if let Some(err_string) = opt_err { + return Err(self.stream_permission_error( + permission, + err_string, + stream.clone(), + caller, + arity, + )); + } + + if let Some(input) = input { + if stream.past_end_of_stream { + self.eof_action( + input, + stream, + caller, + arity, + )?; + } + } + + Ok(()) + } } impl Read for Stream { diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index 0837a0ad..43492247 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -1596,33 +1596,15 @@ impl MachineState { let mut stream = self.get_stream_or_alias(self[temp_v!(1)], indices, "get_byte", 2)?; - let opt_err = - if !stream.is_input_stream() { - Some("stream") // 8.14.2.3 g) - } else if stream.options.stream_type == StreamType::Text { - Some("text_stream") // 8.14.2.3 h) - } else { - None - }; - - if let Some(err_string) = opt_err { - return Err(self.stream_permission_error( - Permission::InputStream, - err_string, - stream, - clause_name!("get_byte"), - 2, - )); - } + self.check_stream_properties( + &mut stream, + StreamType::Binary, + Some(self[temp_v!(2)]), + clause_name!("get_byte"), + 2, + )?; if stream.past_end_of_stream { - self.eof_action( - self[temp_v!(2)], - &mut stream, - clause_name!("get_byte"), - 2, - )?; - if EOFAction::Reset != stream.options.eof_action { return return_from_clause!(self.last_call, self); } else if self.fail { @@ -1691,33 +1673,105 @@ impl MachineState { } } &SystemClauseType::GetChar => { + let mut stream = + self.get_stream_or_alias(self[temp_v!(1)], indices, "get_char", 2)?; + + self.check_stream_properties( + &mut stream, + StreamType::Text, + Some(self[temp_v!(2)]), + clause_name!("get_char"), + 2, + )?; + + if stream.past_end_of_stream { + if EOFAction::Reset != stream.options.eof_action { + return return_from_clause!(self.last_call, self); + } else if self.fail { + return Ok(()); + } + } + let mut iter = self.open_parsing_stream( - current_input_stream.clone(), + stream.clone(), "get_char", - 1, + 2, )?; - let result = iter.next(); - let a1 = self[temp_v!(1)]; + loop { + let result = iter.next(); - match result { - Some(Ok(c)) => { - self.unify(Addr::Char(c), a1); - } - Some(Err(_)) => { - let end_of_file = self.heap.to_unifiable(HeapCellValue::Atom( - clause_name!("end_of_file"), - None, - )); + match result { + Some(Ok(c)) => { + match self.store(self.deref(self[temp_v!(2)])) { + addr if addr.is_ref() => { + if let Some(var) = addr.as_var() { + self.bind(var, Addr::Char(c)); + return return_from_clause!(self.last_call, self); + } else { + unreachable!() + } + } + Addr::Con(h) if self.heap.atom_at(h) => { + match &self.heap[h] { + HeapCellValue::Atom(ref atom, _) if atom.is_char() => { + if let Some(d) = atom.as_str().chars().next() { + if c == d { + return return_from_clause!(self.last_call, self); + } else { + self.fail = true; + return Ok(()); + } + } else { + unreachable!() + } + } + _ => { + unreachable!() + } + } + } + Addr::Char(d) => { + if c == d { + return return_from_clause!(self.last_call, self); + } else { + self.fail = true; + return Ok(()); + } + } + culprit => { + let stub = MachineError::functor_stub(clause_name!("get_char"), 2); + let err = MachineError::type_error( + self.heap.h(), + ValidType::InCharacter, + culprit, + ); + + return Err(self.error_form(err, stub)); + } + } + } + _ => { + self.eof_action( + self[temp_v!(2)], + &mut stream, + clause_name!("get_char"), + 2, + )?; - self.unify(a1, end_of_file); - } - None => { - let stub = MachineError::functor_stub(clause_name!("get_char"), 1); - let err = MachineError::representation_error(RepFlag::Character); - let err = self.error_form(err, stub); + if EOFAction::Reset != stream.options.eof_action { + return return_from_clause!(self.last_call, self); + } else if self.fail { + return Ok(()); + } + }/* + _ => { + let stub = MachineError::functor_stub(clause_name!("get_char"), 2); + let err = MachineError::representation_error(RepFlag::Character); + let err = self.error_form(err, stub); - return Err(err); + return Err(err); + }*/ } } } @@ -4035,6 +4089,14 @@ impl MachineState { 3, )?; + self.check_stream_properties( + &mut stream, + StreamType::Text, + None, // input + clause_name!("write_term"), + 3, + )?; + let opt_err = if !stream.is_output_stream() { Some("stream") // 8.14.2.3 g) -- 2.54.0