]> Repositorios git - scryer-prolog.git/commitdiff
add peek_char/{1,2}, peek_byte/{1,2}, peek_code/{1,2}
authorMark Thom <[email protected]>
Wed, 6 May 2020 07:05:03 +0000 (01:05 -0600)
committerMark Thom <[email protected]>
Wed, 6 May 2020 07:05:03 +0000 (01:05 -0600)
Cargo.lock
Cargo.toml
src/prolog/clause_types.rs
src/prolog/lib/builtins.pl
src/prolog/lib/sockets.pl
src/prolog/machine/machine_indices.rs
src/prolog/machine/streams.rs
src/prolog/machine/system_calls.rs
src/prolog/read.rs

index a4357f1276b512f16b24a90d3d7c59bb868689ee..bb5eaed1e482c49f8529bfe88bc4f05f71d1bf70 100644 (file)
@@ -655,6 +655,7 @@ dependencies = [
  "ref_thread_local",
  "rug",
  "rustyline",
+ "unicode_reader",
 ]
 
 [[package]]
index a70173af69f96a823fbbd41d882abc7c0949413a..2112dcba212f6556dd5aad530e9667230aafdf21 100644 (file)
@@ -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"
index d80a01d00d4d8f39dac46d0499d04604cf24b8ea..4a8d8338f2af5d377b9c8e8e4a6fd62bf05ab081 100644 (file)
@@ -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),
index 6eac4f153491feac8a286a33655d5a24d91285e1..c318c4a530b11382009d6e6a43797804d721cf60 100644 (file)
@@ -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).
index 83ea3837f305b77ab5bc4aba4d8f786a1bedcc52..a006d6a215d30ad5c6a2a4e30532da46548d0efc 100644 (file)
@@ -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) :-
index 58ee66befc324885feb55d7bff3df79b3d2a391f..3bd730b394daf5b822b4d30405f1b6046b4a131c 100644 (file)
@@ -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)
             }
         }
     }
index 7a368d1c2b0c7682c1e1a8900cd38e2247cac4bb..d47d525c9e329fa7931eb48fdf09465f4ebb5171 100644 (file)
@@ -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)
index 4e0d2e393d2a2fc1070bb74a4b106983dcbd33df..a97ec40c70449177886bf796653894a6034a8357 100644 (file)
@@ -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,
                             )?;
 
index 9ed6c6cbcc97430a5657c615c4b569eac1247ab1..29b37fcbdb22b9b7b0913e4137b36b66406ca146 100644 (file)
@@ -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<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",
+                                ));
+                            }
+                            _ => {
+                            }
+                        }
+                    }
                 }
             }
         }