From: Mark Thom Date: Wed, 6 May 2020 07:05:03 +0000 (-0600) Subject: add peek_char/{1,2}, peek_byte/{1,2}, peek_code/{1,2} X-Git-Tag: v0.8.123~57^2~5 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=b8c50eb0fe88bb0872c6faa443e49b4924da40b9;p=scryer-prolog.git add peek_char/{1,2}, peek_byte/{1,2}, peek_code/{1,2} --- diff --git a/Cargo.lock b/Cargo.lock index a4357f12..bb5eaed1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -655,6 +655,7 @@ dependencies = [ "ref_thread_local", "rug", "rustyline", + "unicode_reader", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index a70173af..2112dcba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ default = ["rug", "prolog_parser/rug"] num = ["num-rug-adapter", "prolog_parser/num"] [dependencies] +cpu-time = "1.0.0" crossterm = "0.16.0" dirs = "2.0.2" divrem = "0.1.0" @@ -32,4 +33,4 @@ prolog_parser = { version = "0.8.56", default-features = false } ref_thread_local = "0.0.0" rug = { version = "1.4.0", optional = true } rustyline = "6.0.0" -cpu-time = "1.0.0" +unicode_reader = "1.0.0" diff --git a/src/prolog/clause_types.rs b/src/prolog/clause_types.rs index d80a01d0..4a8d8338 100644 --- a/src/prolog/clause_types.rs +++ b/src/prolog/clause_types.rs @@ -222,6 +222,9 @@ pub enum SystemClauseType { OpDeclaration, Open, PartialStringTail, + PeekByte, + PeekChar, + PeekCode, PointsToContinuationResetMarker, PutByte, PutChar, @@ -372,6 +375,9 @@ impl SystemClauseType { } &SystemClauseType::IsPartialString => clause_name!("$is_partial_string"), &SystemClauseType::PartialStringTail => clause_name!("$partial_string_tail"), + &SystemClauseType::PeekByte => clause_name!("$peek_byte"), + &SystemClauseType::PeekChar => clause_name!("$peek_char"), + &SystemClauseType::PeekCode => clause_name!("$peek_code"), &SystemClauseType::LiftedHeapLength => clause_name!("$lh_length"), &SystemClauseType::Maybe => clause_name!("maybe"), &SystemClauseType::CpuNow => clause_name!("$cpu_now"), @@ -497,6 +503,9 @@ impl SystemClauseType { ("$enqueue_attribute_goal", 1) => Some(SystemClauseType::EnqueueAttributeGoal), ("$enqueue_attr_var", 1) => Some(SystemClauseType::EnqueueAttributedVar), ("$partial_string_tail", 2) => Some(SystemClauseType::PartialStringTail), + ("$peek_byte", 2) => Some(SystemClauseType::PeekByte), + ("$peek_char", 2) => Some(SystemClauseType::PeekChar), + ("$peek_code", 2) => Some(SystemClauseType::PeekCode), ("$is_partial_string", 1) => Some(SystemClauseType::IsPartialString), ("$expand_term", 2) => Some(SystemClauseType::ExpandTerm), ("$expand_goal", 2) => Some(SystemClauseType::ExpandGoal), diff --git a/src/prolog/lib/builtins.pl b/src/prolog/lib/builtins.pl index 6eac4f15..c318c4a5 100644 --- a/src/prolog/lib/builtins.pl +++ b/src/prolog/lib/builtins.pl @@ -52,9 +52,11 @@ user:term_expansion((:- op(Pred, Spec, [Op | OtherOps])), OpResults) :- get_byte/1, get_byte/2, get_char/1, get_char/2, get_code/1, get_code/2, halt/0, max_arity/1, number_chars/2, number_codes/2, once/1, op/3, - open/3, open/4, put_byte/1, put_byte/2, - put_code/1, put_code/2, put_char/1, put_char/2, - read_term/2, read_term/3, repeat/0, retract/1, + open/3, open/4, peek_byte/1, peek_byte/2, + peek_char/1, peek_char/2, peek_code/1, + peek_code/2, put_byte/1, put_byte/2, put_code/1, + put_code/2, put_char/1, put_char/2, 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, @@ -1233,3 +1235,27 @@ get_code(C) :- get_code(S, C) :- '$get_code'(S, C). + + +peek_byte(S, B) :- + '$peek_byte'(S, B). + +peek_byte(B) :- + current_input(S), + '$peek_byte'(S, B). + + +peek_code(C) :- + current_input(S), + '$peek_code'(S, C). + +peek_code(S, C) :- + '$peek_code'(S, C). + + +peek_char(C) :- + current_input(S), + '$peek_char'(S, C). + +peek_char(S, C) :- + '$peek_char'(S, C). diff --git a/src/prolog/lib/sockets.pl b/src/prolog/lib/sockets.pl index 83ea3837..a006d6a2 100644 --- a/src/prolog/lib/sockets.pl +++ b/src/prolog/lib/sockets.pl @@ -6,8 +6,6 @@ current_hostname/1]). :- use_module(library(error)). -:- use_module(library(lists)). -:- use_module(library(pairs)). socket_client_open(Addr, Stream, Options) :- diff --git a/src/prolog/machine/machine_indices.rs b/src/prolog/machine/machine_indices.rs index 58ee66be..3bd730b3 100644 --- a/src/prolog/machine/machine_indices.rs +++ b/src/prolog/machine/machine_indices.rs @@ -447,7 +447,7 @@ impl HeapCellValue { HeapCellValue::Stream(stream.clone()) } &HeapCellValue::TcpListener(_) => { - HeapCellValue::Atom(clause_name!("$socket_server"), None) + HeapCellValue::Atom(clause_name!("$tcp_listener"), None) } } } diff --git a/src/prolog/machine/streams.rs b/src/prolog/machine/streams.rs index 7a368d1c..d47d525c 100644 --- a/src/prolog/machine/streams.rs +++ b/src/prolog/machine/streams.rs @@ -128,6 +128,10 @@ impl Hash for WrappedStreamInstance { #[derive(Debug)] enum StreamError { + PeekByteFailed, + PeekByteFromNonPeekableStream, + PeekCharFailed, + PeekCharFromNonPeekableStream, ReadFromOutputStream, WriteToInputStream, FlushToInputStream, @@ -136,6 +140,18 @@ enum StreamError { impl fmt::Display for StreamError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { + StreamError::PeekByteFailed => { + write!(f, "peek byte failed!") + } + StreamError::PeekByteFromNonPeekableStream => { + write!(f, "attempted to peek byte from a non-peekable input stream") + } + StreamError::PeekCharFailed => { + write!(f, "peek char failed!") + } + StreamError::PeekCharFromNonPeekableStream => { + write!(f, "attempted to peek char from a non-peekable input stream") + } StreamError::ReadFromOutputStream => { write!(f, "attempted to read from a write-only stream") } @@ -355,6 +371,115 @@ impl Stream { } } } + + #[inline] + pub(crate) + fn peek_byte(&mut self) -> std::io::Result { + match *self.stream_inst.0.borrow_mut() { + StreamInstance::Bytes(ref mut cursor) => { + let mut b = [0u8; 1]; + let pos = cursor.position(); + + match cursor.read(&mut b)? { + 1 => { + cursor.set_position(pos); + Ok(b[0]) + } + _ => { + Err(std::io::Error::new( + ErrorKind::UnexpectedEof, + "end of file", + )) + } + } + } + StreamInstance::InputFile(ref mut file) => { + let mut b = [0u8; 1]; + + match file.read(&mut b)? { + 1 => { + file.seek(SeekFrom::Current(-1))?; + Ok(b[0]) + } + _ => { + Err(std::io::Error::new( + ErrorKind::UnexpectedEof, + StreamError::PeekByteFailed, + )) + } + } + } + StreamInstance::ReadlineStream(ref mut stream) => { + stream.peek_byte() + } + StreamInstance::TcpStream(ref mut tcp_stream) => { + let mut b = [0u8; 1]; + tcp_stream.peek(&mut b)?; + Ok(b[0]) + } + _ => { + Err(std::io::Error::new( + ErrorKind::PermissionDenied, + StreamError::PeekByteFromNonPeekableStream, + )) + } + } + } + + #[inline] + pub(crate) + fn peek_char(&mut self) -> std::io::Result { + use unicode_reader::CodePoints; + + match *self.stream_inst.0.borrow_mut() { + StreamInstance::InputFile(ref mut file) => { + let c = { + let mut iter = CodePoints::from(&*file); + + if let Some(Ok(c)) = iter.next() { + c + } else { + return Err(std::io::Error::new( + ErrorKind::UnexpectedEof, + StreamError::PeekCharFailed + )); + } + }; + + file.seek(SeekFrom::Current(- (c.len_utf8() as i64)))?; + + Ok(c) + } + StreamInstance::ReadlineStream(ref mut stream) => { + stream.peek_char() + } + StreamInstance::TcpStream(ref tcp_stream) => { + let c = { + let mut buf = [0u8; 8]; + tcp_stream.peek(&mut buf)?; + + let mut iter = CodePoints::from(buf.bytes()); + + if let Some(Ok(c)) = iter.next() { + c + } else { + return Err(std::io::Error::new( + ErrorKind::UnexpectedEof, + StreamError::PeekCharFailed + )); + } + }; + + Ok(c) + } + _ => { + Err(std::io::Error::new( + ErrorKind::PermissionDenied, + StreamError::PeekCharFromNonPeekableStream, + )) + } + } + } } impl MachineState { @@ -668,7 +793,7 @@ impl MachineState { 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() { + } else if input.is_none() && !stream.is_output_stream() { Some("stream") // 8.14.2.3 g) } else if stream.options.stream_type != expected_type { Some(expected_type.other().as_str()) // 8.14.2.3 h) diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index 4e0d2e39..a97ec40c 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -1169,6 +1169,308 @@ impl MachineState { } } } + &SystemClauseType::PeekByte => { + let mut stream = + self.get_stream_or_alias(self[temp_v!(1)], indices, "peek_byte", 2)?; + + self.check_stream_properties( + &mut stream, + StreamType::Binary, + Some(self[temp_v!(2)]), + clause_name!("peek_byte"), + 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 addr = + match self.store(self.deref(self[temp_v!(2)])) { + addr if addr.is_ref() => { + addr + } + addr => { + match Number::try_from((addr, &self.heap)) { + Ok(Number::Integer(n)) => { + if let Some(nb) = n.to_u8() { + Addr::Usize(nb as usize) + } else { + return Err(self.type_error( + ValidType::InByte, + addr, + clause_name!("peek_byte"), + 2, + )); + } + } + Ok(Number::Fixnum(n)) => { + if let Ok(nb) = u8::try_from(n) { + Addr::Usize(nb as usize) + } else { + return Err(self.type_error( + ValidType::InByte, + addr, + clause_name!("peek_byte"), + 2, + )); + } + } + _ => { + return Err(self.type_error( + ValidType::InByte, + addr, + clause_name!("peek_byte"), + 2, + )); + } + } + } + }; + + loop { + match stream.peek_byte().map_err(|e| e.kind()) { + Ok(b) => { + if let Some(var) = addr.as_var() { + self.bind(var, Addr::Usize(b as usize)); + break; + } else if addr == Addr::Usize(b as usize) { + break; + } else { + self.fail = true; + return Ok(()); + } + } + Err(ErrorKind::PermissionDenied) => { + self.fail = true; + break; + } + _ => { + self.eof_action( + self[temp_v!(2)], + &mut stream, + clause_name!("peek_byte"), + 2, + )?; + + if EOFAction::Reset != stream.options.eof_action { + return return_from_clause!(self.last_call, self); + } else if self.fail { + return Ok(()); + } + } + } + } + } + &SystemClauseType::PeekChar => { + let mut stream = + self.get_stream_or_alias(self[temp_v!(1)], indices, "peek_char", 2)?; + + self.check_stream_properties( + &mut stream, + StreamType::Text, + Some(self[temp_v!(2)]), + clause_name!("peek_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 addr = + match self.store(self.deref(self[temp_v!(2)])) { + addr if addr.is_ref() => { + addr + } + Addr::Con(h) if self.heap.atom_at(h) => { + match &self.heap[h] { + HeapCellValue::Atom(ref atom, _) if atom.is_char() => { + if let Some(c) = atom.as_str().chars().next() { + Addr::Char(c) + } else { + unreachable!() + } + } + culprit => { + return Err(self.type_error( + ValidType::InCharacter, + culprit.as_addr(h), + clause_name!("peek_char"), + 2, + )); + } + } + } + Addr::Char(d) => { + Addr::Char(d) + } + culprit => { + return Err(self.type_error( + ValidType::InCharacter, + culprit, + clause_name!("peek_char"), + 2, + )); + } + }; + + loop { + match stream.peek_char().map_err(|e| e.kind()) { + Ok(d) => { + if let Some(var) = addr.as_var() { + self.bind(var, Addr::Char(d)); + break; + } else if addr == Addr::Char(d) { + break; + } else { + self.fail = true; + return Ok(()); + } + } + Err(ErrorKind::PermissionDenied) => { + self.fail = true; + break; + } + _ => { + self.eof_action( + self[temp_v!(2)], + &mut stream, + clause_name!("peek_char"), + 2, + )?; + + 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!("peek_char"), 2); + let err = MachineError::representation_error(RepFlag::Character); + let err = self.error_form(err, stub); + + return Err(err); + }*/ + } + } + } + &SystemClauseType::PeekCode => { + let mut stream = + self.get_stream_or_alias(self[temp_v!(1)], indices, "peek_code", 2)?; + + self.check_stream_properties( + &mut stream, + StreamType::Text, + Some(self[temp_v!(2)]), + clause_name!("peek_code"), + 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 addr = + match self.store(self.deref(self[temp_v!(2)])) { + addr if addr.is_ref() => { + addr + } + Addr::CharCode(d) => { + Addr::CharCode(d) + } + addr => { + match Number::try_from((addr, &self.heap)) { + Ok(Number::Integer(n)) => { + if let Some(c) = n.to_u32().and_then(|c| char::try_from(c).ok()) { + Addr::CharCode(c as u32) + } else { + return Err(self.representation_error( + RepFlag::InCharacterCode, + clause_name!("peek_code"), + 2, + )); + } + } + Ok(Number::Fixnum(n)) => { + if let Some(c) = u32::try_from(n).ok().and_then(|c| char::try_from(c).ok()) { + Addr::CharCode(c as u32) + } else { + return Err(self.representation_error( + RepFlag::InCharacterCode, + clause_name!("peek_code"), + 2, + )); + } + } + _ => { + return Err(self.type_error( + ValidType::Integer, + self[temp_v!(2)], + clause_name!("peek_code"), + 2, + )); + } + } + } + }; + + loop { + let result = stream.peek_char(); + + match result.map_err(|e| e.kind()) { + Ok(c) => { + if let Some(var) = addr.as_var() { + self.bind(var, Addr::CharCode(c as u32)); + break; + } else if addr == Addr::CharCode(c as u32) { + break; + } else { + self.fail = true; + return Ok(()); + } + } + Err(ErrorKind::PermissionDenied) => { + self.fail = true; + break; + } + _ => { + self.eof_action( + self[temp_v!(2)], + &mut stream, + clause_name!("peek_code"), + 2, + )?; + + 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); + }*/ + } + } + } &SystemClauseType::NumberToChars => { let n = self[temp_v!(1)]; let chs = self[temp_v!(2)]; @@ -2039,7 +2341,7 @@ impl MachineState { self.eof_action( self[temp_v!(2)], &mut stream, - clause_name!("get_coder"), + clause_name!("get_code"), 2, )?; diff --git a/src/prolog/read.rs b/src/prolog/read.rs index 9ed6c6cb..29b37fcb 100644 --- a/src/prolog/read.rs +++ b/src/prolog/read.rs @@ -18,7 +18,7 @@ pub mod readline { use crate::prolog::machine::streams::Stream; use crate::prolog::rustyline::error::ReadlineError; use crate::prolog::rustyline::{Cmd, Editor, KeyPress}; - use std::io::{Cursor, Read}; + use std::io::{Cursor, Error, ErrorKind, Read}; static mut PROMPT: bool = false; @@ -73,7 +73,61 @@ pub mod readline { Ok(0) } Err(e) => { - Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, e)) + Err(Error::new(ErrorKind::InvalidInput, e)) + } + } + } + + pub fn peek_byte(&mut self) -> std::io::Result { + set_prompt(false); + + loop { + match self.pending_input.get_ref().bytes().next() { + Some(b) => { + return Ok(b); + } + None => { + match self.call_readline(&mut []) { + Err(e) => { + return Err(e); + } + Ok(0) => { + return Err(Error::new( + ErrorKind::UnexpectedEof, + "end of file", + )); + } + _ => { + } + } + } + } + } + } + + pub fn peek_char(&mut self) -> std::io::Result { + set_prompt(false); + + loop { + match self.pending_input.get_ref().chars().next() { + Some(c) => { + return Ok(c); + } + None => { + match self.call_readline(&mut []) { + Err(e) => { + return Err(e); + } + Ok(0) => { + return Err(Error::new( + ErrorKind::UnexpectedEof, + "end of file", + )); + } + _ => { + } + } + } } } }