From bfced59949abba8a14c0ee5bb57ec32d65090a56 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Tue, 5 May 2020 16:09:17 -0600 Subject: [PATCH] clean up stream error handling, add get_byte/{1,2} --- src/prolog/clause_types.rs | 3 + src/prolog/lib/builtins.pl | 15 +++- src/prolog/machine/machine_errors.rs | 4 +- src/prolog/machine/machine_state.rs | 17 ++-- src/prolog/machine/streams.rs | 58 ++++++++++---- src/prolog/machine/system_calls.rs | 114 ++++++++++++++++++++++++--- 6 files changed, 168 insertions(+), 43 deletions(-) diff --git a/src/prolog/clause_types.rs b/src/prolog/clause_types.rs index bb39a35e..99282013 100644 --- a/src/prolog/clause_types.rs +++ b/src/prolog/clause_types.rs @@ -182,6 +182,7 @@ pub enum SystemClauseType { FetchGlobalVarWithOffset, FileToChars, FlushOutput, + GetByte, GetChar, GetSingleChar, ResetAttrVarState, @@ -327,6 +328,7 @@ impl SystemClauseType { } &SystemClauseType::FileToChars => clause_name!("$file_to_chars"), &SystemClauseType::FlushOutput => clause_name!("$flush_output"), + &SystemClauseType::GetByte => clause_name!("$get_byte"), &SystemClauseType::GetChar => clause_name!("$get_char"), &SystemClauseType::GetSingleChar => clause_name!("$get_single_char"), &SystemClauseType::ResetAttrVarState => clause_name!("$reset_attr_var_state"), @@ -487,6 +489,7 @@ impl SystemClauseType { ("$fetch_global_var", 2) => Some(SystemClauseType::FetchGlobalVar), ("$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_single_char", 1) => Some(SystemClauseType::GetSingleChar), ("$points_to_cont_reset_marker", 1) => { diff --git a/src/prolog/lib/builtins.pl b/src/prolog/lib/builtins.pl index 31cfa75d..ca6d738e 100644 --- a/src/prolog/lib/builtins.pl +++ b/src/prolog/lib/builtins.pl @@ -49,9 +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_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, 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, @@ -1190,3 +1191,11 @@ flush_output(S) :- flush_output :- current_output(S), '$flush_output'(S). + + +get_byte(S, B) :- + '$get_byte'(S, B). + +get_byte(S) :- + current_input(S), + '$get_byte'(S, B). diff --git a/src/prolog/machine/machine_errors.rs b/src/prolog/machine/machine_errors.rs index 4a022316..fdbd1d85 100644 --- a/src/prolog/machine/machine_errors.rs +++ b/src/prolog/machine/machine_errors.rs @@ -495,7 +495,7 @@ pub enum ValidType { Compound, Evaluable, Float, - // InByte, + InByte, // InCharacter, Integer, List, @@ -518,7 +518,7 @@ impl ValidType { ValidType::Compound => "compound", ValidType::Evaluable => "evaluable", ValidType::Float => "float", - // ValidType::InByte => "in_byte", + ValidType::InByte => "in_byte", // ValidType::InCharacter => "in_character", ValidType::Integer => "integer", ValidType::List => "list", diff --git a/src/prolog/machine/machine_state.rs b/src/prolog/machine/machine_state.rs index e58a04ec..9a7a1aa0 100644 --- a/src/prolog/machine/machine_state.rs +++ b/src/prolog/machine/machine_state.rs @@ -629,20 +629,13 @@ impl MachineState { }; if let Some(err_string) = opt_err { - let stub = MachineError::functor_stub(clause_name!("read_term"), 3); - - let addr = vec![ - HeapCellValue::Stream(stream) - ]; - - let err = MachineError::permission_error( - self.heap.h(), + return Err(self.stream_permission_error( Permission::InputStream, err_string, - addr, - ); - - return Err(self.error_form(err, stub)); + stream, + clause_name!("read_term"), + 3, + )); } let mut orig_stream = stream.clone(); diff --git a/src/prolog/machine/streams.rs b/src/prolog/machine/streams.rs index 0c2d9aac..5be540cb 100644 --- a/src/prolog/machine/streams.rs +++ b/src/prolog/machine/streams.rs @@ -153,7 +153,7 @@ impl Default for StreamOptions { #[derive(Debug, Clone, Hash)] pub struct Stream { - past_end_of_stream: bool, + pub past_end_of_stream: bool, pub options: StreamOptions, stream_inst: WrappedStreamInstance, } @@ -339,20 +339,7 @@ impl MachineState { match stream.options.eof_action { EOFAction::Error => { stream.past_end_of_stream = true; - - let stub = MachineError::functor_stub(caller, arity); - let payload = vec![ - HeapCellValue::Stream(stream.clone()) - ]; - - let err = MachineError::permission_error( - self.heap.h(), - Permission::InputStream, - "past_end_of_stream", - payload, - ); - - Err(self.error_form(err, stub)) + return Err(self.open_past_eos_error(stream.clone(), caller, arity)); } EOFAction::EOFCode => { let end_of_stream = self.heap.to_unifiable( @@ -533,6 +520,47 @@ impl MachineState { } } + pub(crate) + fn stream_permission_error( + &self, + perm: Permission, + err_string: &'static str, + stream: Stream, + caller: ClauseName, + arity: usize, + ) -> MachineStub { + let stub = MachineError::functor_stub(caller, arity); + let payload = vec![ + HeapCellValue::Stream(stream) + ]; + + let err = MachineError::permission_error( + self.heap.h(), + perm, + err_string, + payload, + ); + + return self.error_form(err, stub); + } + + #[inline] + pub(crate) + fn open_past_eos_error( + &self, + stream: Stream, + caller: ClauseName, + arity: usize, + ) -> MachineStub { + self.stream_permission_error( + Permission::InputStream, + "past_end_of_stream", + stream, + caller, + arity, + ) + } + pub(crate) fn open_permission_error( &self, diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index 14df438c..0837a0ad 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -1592,6 +1592,104 @@ impl MachineState { self.unify(complete_string, a2); } + &SystemClauseType::GetByte => { + 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, + )); + } + + 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 { + return Ok(()); + } + } + + loop { + let mut b = [0u8; 1]; + + match stream.read(&mut b) { + Ok(1) => { + 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::Usize(b[0] as usize)); + return return_from_clause!(self.last_call, self); + } else { + unreachable!() + } + } + addr => { + match Number::try_from((addr, &self.heap)) { + Ok(Number::Integer(n)) => { + if let Some(nb) = n.to_u8() { + self.fail = b[0] != nb; + return return_from_clause!(self.last_call, self); + } + } + Ok(Number::Fixnum(n)) => { + if let Ok(nb) = u8::try_from(n) { + self.fail = b[0] != nb; + return return_from_clause!(self.last_call, self); + } + } + _ => { + } + } + } + } + + let stub = MachineError::functor_stub(clause_name!("get_byte"), 2); + let err = MachineError::type_error( + self.heap.h(), + ValidType::InByte, + self[temp_v!(2)], + ); + + return Err(self.error_form(err, stub)); + } + _ => { + 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 { + return Ok(()); + } + } + } + } + } &SystemClauseType::GetChar => { let mut iter = self.open_parsing_stream( current_input_stream.clone(), @@ -3947,19 +4045,13 @@ impl MachineState { }; if let Some(err_string) = opt_err { - let stub = MachineError::functor_stub(clause_name!("write_term"), 3); - let addr = vec![ - HeapCellValue::Stream(stream) - ]; - - let err = MachineError::permission_error( - self.heap.h(), + return Err(self.stream_permission_error( Permission::OutputStream, err_string, - addr, - ); - - return Err(self.error_form(err, stub)); + stream, + clause_name!("write_term"), + 3, + )); } let addr = self[temp_v!(2)]; -- 2.54.0