add peek_char/{1,2}, peek_byte/{1,2}, peek_code/{1,2}
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<u8> {
|
||||
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<char> {
|
||||
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)
|
||||
|
||||
@@ -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,
|
||||
)?;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user