"ref_thread_local",
"rug",
"rustyline",
+ "unicode_reader",
]
[[package]]
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"
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"
OpDeclaration,
Open,
PartialStringTail,
+ PeekByte,
+ PeekChar,
+ PeekCode,
PointsToContinuationResetMarker,
PutByte,
PutChar,
}
&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"),
("$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),
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,
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).
current_hostname/1]).
:- use_module(library(error)).
-:- use_module(library(lists)).
-:- use_module(library(pairs)).
socket_client_open(Addr, Stream, Options) :-
HeapCellValue::Stream(stream.clone())
}
&HeapCellValue::TcpListener(_) => {
- HeapCellValue::Atom(clause_name!("$socket_server"), None)
+ HeapCellValue::Atom(clause_name!("$tcp_listener"), None)
}
}
}
#[derive(Debug)]
enum StreamError {
+ PeekByteFailed,
+ PeekByteFromNonPeekableStream,
+ PeekCharFailed,
+ PeekCharFromNonPeekableStream,
ReadFromOutputStream,
WriteToInputStream,
FlushToInputStream,
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")
}
}
}
}
+
+ #[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 {
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)
}
}
}
+ &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)];
self.eof_action(
self[temp_v!(2)],
&mut stream,
- clause_name!("get_coder"),
+ clause_name!("get_code"),
2,
)?;
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;
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<u8> {
+ 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<char> {
+ 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",
+ ));
+ }
+ _ => {
+ }
+ }
+ }
}
}
}