From b5368207d676ad140f0fa344628b5382cd7b6e42 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sun, 3 May 2020 18:49:15 -0600 Subject: [PATCH 01/18] merge with master --- Cargo.lock | 18 + Cargo.toml | 1 + src/main.rs | 1 + src/prolog/clause_types.rs | 19 +- src/prolog/heap_iter.rs | 3 + src/prolog/heap_print.rs | 93 ++++- src/prolog/lib/builtins.pl | 23 +- src/prolog/machine/compile.rs | 10 +- src/prolog/machine/heap.rs | 20 +- src/prolog/machine/machine_errors.rs | 25 +- src/prolog/machine/machine_indices.rs | 11 +- src/prolog/machine/machine_state.rs | 89 +++-- src/prolog/machine/machine_state_impl.rs | 8 +- src/prolog/machine/mod.rs | 11 +- src/prolog/machine/streams.rs | 259 +++++++++++++- src/prolog/machine/system_calls.rs | 414 +++++++++++++++++++---- src/prolog/toplevel.pl | 2 +- src/prolog/write.rs | 11 +- 18 files changed, 852 insertions(+), 166 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a766ca2f..a4357f12 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -228,6 +228,17 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi 0.3.8", +] + [[package]] name = "indexmap" version = "1.3.2" @@ -310,6 +321,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + [[package]] name = "maybe-uninit" version = "2.0.0" @@ -627,6 +644,7 @@ dependencies = [ "divrem", "downcast", "git-version", + "hostname", "indexmap", "lazy_static", "libc", diff --git a/Cargo.toml b/Cargo.toml index 37b4ef3a..a70173af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ dirs = "2.0.2" divrem = "0.1.0" downcast = "0.10.0" git-version = "0.3.4" +hostname = "0.3.1" indexmap = "1.0.2" lazy_static = "1.4.0" libc = "0.2.62" diff --git a/src/main.rs b/src/main.rs index fc38f3f0..83196004 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ extern crate divrem; #[macro_use] extern crate downcast; extern crate git_version; +extern crate hostname; extern crate indexmap; #[macro_use] extern crate lazy_static; diff --git a/src/prolog/clause_types.rs b/src/prolog/clause_types.rs index d05c8559..b5bfae0e 100644 --- a/src/prolog/clause_types.rs +++ b/src/prolog/clause_types.rs @@ -167,6 +167,7 @@ pub enum SystemClauseType { CheckCutPoint, CopyToLiftedHeap, CreatePartialString, + CurrentHostname, CurrentInput, CurrentOutput, DeleteAttribute, @@ -254,6 +255,10 @@ pub enum SystemClauseType { SetSeed, SkipMaxList, Sleep, + SocketClientOpen, + SocketServerOpen, + SocketServerAccept, + SocketServerClose, Succeed, TermAttributedVariables, TermVariables, @@ -291,6 +296,7 @@ impl SystemClauseType { &SystemClauseType::CopyTermWithoutAttrVars => clause_name!("$copy_term_without_attr_vars"), &SystemClauseType::CreatePartialString => clause_name!("$create_partial_string"), &SystemClauseType::CurrentInput => clause_name!("$current_input"), + &SystemClauseType::CurrentHostname => clause_name!("$current_hostname"), &SystemClauseType::CurrentOutput => clause_name!("$current_output"), &SystemClauseType::REPL(REPLCodePtr::CompileBatch) => clause_name!("$compile_batch"), &SystemClauseType::REPL(REPLCodePtr::UseModule) => clause_name!("$use_module"), @@ -410,6 +416,10 @@ impl SystemClauseType { &SystemClauseType::SetDoubleQuotes => clause_name!("$set_double_quotes"), &SystemClauseType::SkipMaxList => clause_name!("$skip_max_list"), &SystemClauseType::Sleep => clause_name!("$sleep"), + &SystemClauseType::SocketClientOpen => clause_name!("$socket_client_open"), + &SystemClauseType::SocketServerOpen => clause_name!("$socket_server_open"), + &SystemClauseType::SocketServerAccept => clause_name!("$socket_server_accept"), + &SystemClauseType::SocketServerClose => clause_name!("$socket_server_close"), &SystemClauseType::Succeed => clause_name!("$succeed"), &SystemClauseType::TermAttributedVariables => clause_name!("$term_attributed_variables"), &SystemClauseType::TermVariables => clause_name!("$term_variables"), @@ -450,6 +460,7 @@ impl SystemClauseType { ("$check_cp", 1) => Some(SystemClauseType::CheckCutPoint), ("$compile_batch", 0) => Some(SystemClauseType::REPL(REPLCodePtr::CompileBatch)), ("$copy_to_lh", 2) => Some(SystemClauseType::CopyToLiftedHeap), + ("$current_hostname", 1) => Some(SystemClauseType::CurrentHostname), ("$current_input", 1) => Some(SystemClauseType::CurrentInput), ("$current_output", 1) => Some(SystemClauseType::CurrentOutput), ("$del_attr_non_head", 1) => Some(SystemClauseType::DeleteAttribute), @@ -523,8 +534,8 @@ impl SystemClauseType { ("$install_new_block", 1) => Some(SystemClauseType::InstallNewBlock), ("$quoted_token", 1) => Some(SystemClauseType::QuotedToken), ("$nextEP", 3) => Some(SystemClauseType::NextEP), - ("$read_query_term", 2) => Some(SystemClauseType::ReadQueryTerm), - ("$read_term", 2) => Some(SystemClauseType::ReadTerm), + ("$read_query_term", 5) => Some(SystemClauseType::ReadQueryTerm), + ("$read_term", 5) => Some(SystemClauseType::ReadTerm), ("$read_term_from_chars", 2) => Some(SystemClauseType::ReadTermFromChars), ("$reset_block", 1) => Some(SystemClauseType::ResetBlock), ("$reset_cont_marker", 0) => Some(SystemClauseType::ResetContinuationMarker), @@ -538,6 +549,10 @@ impl SystemClauseType { ("$set_seed", 1) => Some(SystemClauseType::SetSeed), ("$skip_max_list", 4) => Some(SystemClauseType::SkipMaxList), ("$sleep", 1) => Some(SystemClauseType::Sleep), + ("$socket_client_open", 7) => Some(SystemClauseType::SocketClientOpen), + ("$socket_server_open", 3) => Some(SystemClauseType::SocketServerOpen), + ("$socket_server_accept", 7) => Some(SystemClauseType::SocketServerAccept), + ("$socket_server_close", 1) => Some(SystemClauseType::SocketServerClose), ("$store_global_var", 2) => Some(SystemClauseType::StoreGlobalVar), ("$store_global_var_with_offset", 2) => Some(SystemClauseType::StoreGlobalVarWithOffset), ("$term_attributed_variables", 2) => Some(SystemClauseType::TermAttributedVariables), diff --git a/src/prolog/heap_iter.rs b/src/prolog/heap_iter.rs index de398ccb..28cdaf85 100644 --- a/src/prolog/heap_iter.rs +++ b/src/prolog/heap_iter.rs @@ -48,6 +48,9 @@ impl<'a> HCPreOrderIterator<'a> { HeapCellValue::Stream(_) => { Addr::Stream(h) } + &HeapCellValue::TcpListener(_) => { + Addr::TcpListener(h) + } } } diff --git a/src/prolog/heap_print.rs b/src/prolog/heap_print.rs index 5ddbcdd4..b68a580a 100644 --- a/src/prolog/heap_print.rs +++ b/src/prolog/heap_print.rs @@ -6,6 +6,7 @@ use crate::prolog::heap_iter::*; use crate::prolog::machine::heap::*; use crate::prolog::machine::machine_indices::*; use crate::prolog::machine::machine_state::*; +use crate::prolog::machine::streams::*; use crate::prolog::ordered_float::OrderedFloat; use crate::prolog::rug::{Integer, Rational}; @@ -14,6 +15,7 @@ use indexmap::{IndexMap, IndexSet}; use std::cell::Cell; use std::convert::TryFrom; use std::iter::{FromIterator, once}; +use std::net::{IpAddr, TcpListener}; use std::ops::{Range, RangeFrom}; use std::rc::Rc; @@ -170,10 +172,12 @@ enum TokenOrRedirect { NumberedVar(String), CompositeRedirect(usize, DirectedOp), FunctorRedirect(usize), + IpAddr(IpAddr), Number(Number, Option), Open, Close, Comma, + RawPtr(*const u8), Space, LeftCurly, RightCurly, @@ -643,8 +647,8 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } self.state_stack.pop(); - self.state_stack.push(TokenOrRedirect::Open); + self.state_stack.push(TokenOrRedirect::Open); self.state_stack.push(TokenOrRedirect::Atom(name)); true @@ -964,6 +968,18 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { }); } + #[inline] + fn print_ip_addr(&mut self, ip: IpAddr) { + self.push_char('\''); + self.append_str(&format!("{}", ip)); + self.push_char('\''); + } + + #[inline] + fn print_raw_ptr(&mut self, ptr: *const u8) { + self.append_str(&format!("0x{:x}", ptr as usize)); + } + fn print_number(&mut self, n: Number, op: &Option) { let add_brackets = if let Some(op) = op { op.is_negative_sign() && n.is_positive() @@ -1330,6 +1346,66 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } } + fn print_tcp_listener( + &mut self, + iter: &mut HCPreOrderIterator, + tcp_listener: &TcpListener, + max_depth: usize, + ) { + let (ip, port) = + if let Some(addr) = tcp_listener.local_addr().ok() { + (addr.ip(), Number::from(addr.port() as isize)) + } else { + let disconnected_atom = clause_name!("$disconnected_tcp_listener"); + self.state_stack.push(TokenOrRedirect::Atom(disconnected_atom)); + + return; + }; + + if self.format_struct(iter, max_depth, 1, clause_name!("$tcp_listener")) { + let atom = self.state_stack.pop().unwrap(); + + self.state_stack.pop(); + self.state_stack.pop(); + + self.state_stack.push(TokenOrRedirect::Number(port, None)); + self.state_stack.push(TokenOrRedirect::Comma); + self.state_stack.push(TokenOrRedirect::IpAddr(ip)); + + self.state_stack.push(TokenOrRedirect::Open); + self.state_stack.push(atom); + } + } + + fn print_stream( + &mut self, + iter: &mut HCPreOrderIterator, + stream: &Stream, + max_depth: usize, + ) { + if let Some(alias) = &stream.options.alias { + self.print_atom(alias); + } else { + if self.format_struct(iter, max_depth, 1, clause_name!("$stream")) { + let atom = + if stream.is_stdout() || stream.is_stdin() { + TokenOrRedirect::Atom(clause_name!("user")) + } else { + TokenOrRedirect::RawPtr(stream.as_ptr()) + }; + + let stream_root = self.state_stack.pop().unwrap(); + + self.state_stack.pop(); + self.state_stack.pop(); + + self.state_stack.push(atom); + self.state_stack.push(TokenOrRedirect::Open); + self.state_stack.push(stream_root); + } + } + } + fn handle_heap_term( &mut self, iter: &mut HCPreOrderIterator, @@ -1440,15 +1516,10 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { self.print_number(Number::Rational(n.clone()), &op); } &HeapCellValue::Stream(ref stream) => { - if let Some(alias) = &stream.options.alias { - self.print_atom(alias); - } else { - if stream.is_stdout() || stream.is_stdin() { - self.print_atom(&clause_name!("user")); - } else { - self.format_struct(iter, max_depth, 1, clause_name!("$stream")); - } - } + self.print_stream(iter, stream, max_depth); + } + &HeapCellValue::TcpListener(ref tcp_listener) => { + self.print_tcp_listener(iter, tcp_listener, max_depth); } _ => { unreachable!() @@ -1486,6 +1557,8 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { self.handle_heap_term(&mut iter, None, true, max_depth) } TokenOrRedirect::Close => self.push_char(')'), + TokenOrRedirect::IpAddr(ip) => self.print_ip_addr(ip), + TokenOrRedirect::RawPtr(ptr) => self.print_raw_ptr(ptr), TokenOrRedirect::Open => self.push_char('('), TokenOrRedirect::OpenList(delimit) => { if !self.at_cdr(",") { diff --git a/src/prolog/lib/builtins.pl b/src/prolog/lib/builtins.pl index 0cebab9b..1d5080e5 100644 --- a/src/prolog/lib/builtins.pl +++ b/src/prolog/lib/builtins.pl @@ -420,25 +420,16 @@ parse_read_term_options(Options, OptionValues, Stub) :- parse_options_list(Options, parse_read_term_options_, DefaultOptions, OptionValues, Stub). -parse_read_term_options_(singletons(Vars), singletons-Vars) :- - ( '$skip_max_list'(Vars, _, -1, Tail), Tail == [], ! - ; - throw(error(domain_error(read_option, singletons(Vars)), _)) - ). -parse_read_term_options_(variables(Vars), variables-Vars) :- - ( '$skip_max_list'(Vars, _, -1, Tail), Tail == [], ! - ; - throw(error(domain_error(read_option, variables(Vars)), _)) - ). -parse_read_term_options_(variable_names(Vars), variable_names-Vars) :- - ( '$skip_max_list'(Vars, _, -1, Tail), Tail == [], ! - ; - throw(error(domain_error(read_option, variable_names(Vars)), _)) - ). +parse_read_term_options_(singletons(Vars), singletons-Vars). +parse_read_term_options_(variables(Vars), variables-Vars). +parse_read_term_options_(variable_names(Vars), variable_names-Vars). +parse_read_term_options_(E,_) :- + throw(error(domain_error(read_option, E), _)). + read_term(Stream, Term, Options) :- - parse_read_term_options(Options, [Singletons, Variables, VariableNames], read_term/3), + parse_read_term_options(Options, [Singletons, VariableNames, Variables], read_term/3), '$read_term'(Stream, Term, Singletons, Variables, VariableNames). read_term(Term, Options) :- diff --git a/src/prolog/machine/compile.rs b/src/prolog/machine/compile.rs index 10bc4d22..4777e054 100644 --- a/src/prolog/machine/compile.rs +++ b/src/prolog/machine/compile.rs @@ -617,7 +617,7 @@ fn load_library( ) } None => { - let err = ExistenceError::SourceSink(ModuleSource::Library( + let err = ExistenceError::ModuleSource(ModuleSource::Library( name.clone() )); @@ -708,7 +708,7 @@ impl ListingCompiler { Ok(wam_indices.insert_module(submodule)) } else { - let err = ExistenceError::SourceSink(ModuleSource::File( + let err = ExistenceError::ModuleSource(ModuleSource::File( module_name, )); @@ -746,7 +746,7 @@ impl ListingCompiler { Ok(wam_indices.insert_module(submodule)) } else { - let err = ExistenceError::SourceSink(ModuleSource::File( + let err = ExistenceError::ModuleSource(ModuleSource::File( module_name )); @@ -1080,7 +1080,7 @@ impl ListingCompiler { insert_or_refresh_term_dir_quantum(term_dir, key, term_dirs); } None => { - let err = ExistenceError::SourceSink(ModuleSource::File( + let err = ExistenceError::ModuleSource(ModuleSource::File( module_name, )); @@ -1438,7 +1438,7 @@ pub(super) fn setup_indices( wam.indices.insert_module(module); result } else { - let err = ExistenceError::SourceSink(ModuleSource::Library( + let err = ExistenceError::ModuleSource(ModuleSource::Library( module )); diff --git a/src/prolog/machine/heap.rs b/src/prolog/machine/heap.rs index 9d8cbfe7..3e813fe8 100644 --- a/src/prolog/machine/heap.rs +++ b/src/prolog/machine/heap.rs @@ -171,15 +171,18 @@ impl HeapTemplate { &HeapCellValue::NamedStr(arity, ref name, ref op) => { HeapCellValue::NamedStr(arity, name.clone(), op.clone()) } - &HeapCellValue::Rational(ref r) => { - HeapCellValue::Rational(r.clone()) - } &HeapCellValue::PartialString(..) => { HeapCellValue::Addr(Addr::PStrLocation(h, 0)) } + &HeapCellValue::Rational(ref r) => { + HeapCellValue::Rational(r.clone()) + } &HeapCellValue::Stream(_) => { HeapCellValue::Addr(Addr::Stream(h)) } + &HeapCellValue::TcpListener(_) => { + HeapCellValue::Addr(Addr::TcpListener(h)) + } } } @@ -294,9 +297,6 @@ impl HeapTemplate { val @ HeapCellValue::NamedStr(..) => { Addr::Str(self.push(val)) } - val @ HeapCellValue::Stream(..) => { - Addr::Stream(self.push(val)) - } HeapCellValue::PartialString(pstr, has_tail) => { let h = self.push(HeapCellValue::PartialString(pstr, has_tail)); @@ -306,6 +306,12 @@ impl HeapTemplate { Addr::Con(h) } + val @ HeapCellValue::Stream(..) => { + Addr::Stream(self.push(val)) + } + val @ HeapCellValue::TcpListener(..) => { + Addr::TcpListener(self.push(val)) + } } } @@ -517,7 +523,7 @@ impl HeapTemplate { pub fn index_addr<'a>(&'a self, addr: &Addr) -> RefOrOwned<'a, HeapCellValue> { match addr { - &Addr::Con(h) | &Addr::Str(h) | &Addr::Stream(h) => { + &Addr::Con(h) | &Addr::Str(h) | &Addr::Stream(h) | &Addr::TcpListener(h) => { RefOrOwned::Borrowed(&self[h]) } addr => { diff --git a/src/prolog/machine/machine_errors.rs b/src/prolog/machine/machine_errors.rs index fd1e9397..a46345e2 100644 --- a/src/prolog/machine/machine_errors.rs +++ b/src/prolog/machine/machine_errors.rs @@ -17,7 +17,7 @@ enum ErrorProvenance { } #[derive(Debug)] -pub(super) struct MachineError { +pub(crate) struct MachineError { stub: MachineStub, location: Option<(usize, usize)>, // line_num, col_num from: ErrorProvenance, @@ -74,7 +74,7 @@ impl TypeError for Number { } } -pub(super) +pub(crate) trait PermissionError { fn permission_error(self, h: usize, index_str: &'static str, perm: Permission) -> MachineError; } @@ -250,7 +250,7 @@ impl MachineError { from: ErrorProvenance::Constructed, } } - ExistenceError::SourceSink(source) => { + ExistenceError::ModuleSource(source) => { let source_stub = source.as_functor_stub(); let stub = functor!( @@ -265,6 +265,18 @@ impl MachineError { from: ErrorProvenance::Constructed, } } + ExistenceError::SourceSink(culprit) => { + let stub = functor!( + "existence_error", + [atom("source_sink"), addr(culprit)] + ); + + MachineError { + stub, + location: None, + from: ErrorProvenance::Constructed, + } + } ExistenceError::Stream(culprit) => { let stub = functor!( "existence_error", @@ -454,6 +466,7 @@ pub enum Permission { Create, InputStream, Modify, + Open, OutputStream, } @@ -464,6 +477,7 @@ impl Permission { Permission::Create => "create", Permission::InputStream => "input", Permission::Modify => "modify", + Permission::Open => "open", Permission::OutputStream => "output", } } @@ -489,6 +503,7 @@ pub enum ValidType { Pair, // PredicateIndicator, // Variable + TcpListener, } impl ValidType { @@ -511,6 +526,7 @@ impl ValidType { ValidType::Pair => "pair", // ValidType::PredicateIndicator => "predicate_indicator", // ValidType::Variable => "variable" + ValidType::TcpListener => "tcp_listener", } } } @@ -726,8 +742,9 @@ impl MachineState { #[derive(Debug)] pub enum ExistenceError { Module(ClauseName), + ModuleSource(ModuleSource), Procedure(ClauseName, usize), - SourceSink(ModuleSource), + SourceSink(Addr), Stream(Addr), } diff --git a/src/prolog/machine/machine_indices.rs b/src/prolog/machine/machine_indices.rs index f35e76f6..cb43359c 100644 --- a/src/prolog/machine/machine_indices.rs +++ b/src/prolog/machine/machine_indices.rs @@ -23,6 +23,7 @@ use std::collections::{BTreeMap, VecDeque}; use std::convert::TryFrom; use std::fmt; use std::mem; +use std::net::TcpListener; use std::ops::{Add, AddAssign, Sub, SubAssign}; use std::rc::Rc; @@ -69,6 +70,7 @@ pub enum Addr { StackCell(usize, usize), Str(usize), Stream(usize), + TcpListener(usize), Usize(usize), } @@ -230,7 +232,7 @@ impl Addr { Addr::Lis(_) | Addr::PStrLocation(..) | Addr::Str(_) => { Some(TermOrderCategory::Compound) } - Addr::CutPoint(_) | Addr::Stream(_) => { + Addr::CutPoint(_) | Addr::Stream(_) | Addr::TcpListener(_) => { None } } @@ -388,6 +390,7 @@ pub enum HeapCellValue { Rational(Rc), PartialString(PartialString, bool), // the partial string, a bool indicating whether it came from a Constant. Stream(Stream), + TcpListener(TcpListener), } impl HeapCellValue { @@ -410,6 +413,9 @@ impl HeapCellValue { HeapCellValue::Stream(_) => { Addr::Stream(focus) } + HeapCellValue::TcpListener(_) => { + Addr::TcpListener(focus) + } } } @@ -440,6 +446,9 @@ impl HeapCellValue { &HeapCellValue::Stream(_) => { HeapCellValue::Stream(Stream::null_stream()) } + &HeapCellValue::TcpListener(_) => { + HeapCellValue::Atom(clause_name!("$socket_server"), None) + } } } } diff --git a/src/prolog/machine/machine_state.rs b/src/prolog/machine/machine_state.rs index 5f3557fb..46fc5ed9 100644 --- a/src/prolog/machine/machine_state.rs +++ b/src/prolog/machine/machine_state.rs @@ -12,7 +12,6 @@ use crate::prolog::machine::machine_indices::*; use crate::prolog::machine::modules::*; use crate::prolog::machine::stack::*; use crate::prolog::machine::streams::*; -use crate::prolog::read::{PrologStream, readline}; use crate::prolog::rug::Integer; use downcast::Any; @@ -614,40 +613,13 @@ pub struct MachineState { } impl MachineState { - pub(crate) - fn open_parsing_stream( - &self, - stream: Stream, - stub_name: &'static str, - stub_arity: usize, - ) -> Result { - match parsing_stream(stream) { - Ok(stream) => { - Ok(stream) - } - Err(e) => { - let stub = MachineError::functor_stub(clause_name!(stub_name), stub_arity); - let err = MachineError::session_error( - self.heap.h(), - SessionError::from(e), - ); - - Err(self.error_form(err, stub)) - } - } - } - pub(crate) fn read_term( &mut self, - current_input_stream: &mut Stream, + stream: Stream, indices: &mut IndexStore, ) -> CallResult { - let mut stream = self.open_parsing_stream( - current_input_stream.clone(), - "read_term", - 2, - )?; + let mut stream = self.open_parsing_stream(stream, "read_term", 3)?; match self.read( &mut stream, @@ -655,8 +627,8 @@ impl MachineState { &indices.op_dir, ) { Ok(term_write_result) => { - let a1 = self[temp_v!(1)]; - self.unify(Addr::HeapCell(term_write_result.heap_loc), a1); + let term = self[temp_v!(2)]; + self.unify(Addr::HeapCell(term_write_result.heap_loc), term); if self.fail { return Ok(()); @@ -677,11 +649,56 @@ impl MachineState { list_of_var_eqs.push(Addr::Str(h)); } - let a2 = self[temp_v!(2)]; - let list_offset = + let mut var_set: IndexMap = IndexMap::new(); + + for addr in self.acyclic_pre_order_iter(term) { + if let Some(var) = addr.as_var() { + if !var_set.contains_key(&var) { + var_set.insert(var, true); + } else { + var_set.insert(var, false); + } + } + } + + let mut var_list = vec![]; + let mut singleton_var_list = vec![]; + + for addr in self.acyclic_pre_order_iter(term) { + if let Some(var) = addr.as_var() { + if var_set.get(&var) == Some(&true) { + singleton_var_list.push(var.as_addr()); + } + + var_list.push(var.as_addr()); + } + } + + let singleton_addr = self[temp_v!(3)]; + let singletons_offset = + Addr::HeapCell(self.heap.to_list(singleton_var_list.into_iter())); + + self.unify(singletons_offset, singleton_addr); + + if self.fail { + return Ok(()); + } + + let vars_addr = self[temp_v!(4)]; + let vars_offset = + Addr::HeapCell(self.heap.to_list(var_list.into_iter())); + + self.unify(vars_offset, vars_addr); + + if self.fail { + return Ok(()); + } + + let var_names_addr = self[temp_v!(5)]; + let var_names_offset = Addr::HeapCell(self.heap.to_list(list_of_var_eqs.into_iter())); - Ok(self.unify(list_offset, a2)) + Ok(self.unify(var_names_offset, var_names_addr)) } Err(err) => { if let ParserError::UnexpectedEOF = err { @@ -689,7 +706,7 @@ impl MachineState { } // reset the input stream after an input failure. - *current_input_stream = readline::input_stream(); + //*stream = readline::input_stream(); let h = self.heap.h(); let syntax_error = MachineError::syntax_error(h, err); diff --git a/src/prolog/machine/machine_state_impl.rs b/src/prolog/machine/machine_state_impl.rs index 42e45b4d..aa7cb911 100644 --- a/src/prolog/machine/machine_state_impl.rs +++ b/src/prolog/machine/machine_state_impl.rs @@ -1400,9 +1400,11 @@ impl MachineState { let addr = self.store(self.deref(addr)); let offset = match addr { - Addr::HeapCell(_) | Addr::StackCell(..) | - Addr::AttrVar(..) | Addr::Stream(_) => { - v + Addr::Stream(_) | Addr::TcpListener(_) => { + 0 + } + Addr::HeapCell(_) | Addr::StackCell(..) | Addr::AttrVar(..) => { + v } Addr::PStrLocation(..) => { if !self.flags.double_quotes.is_atom() { diff --git a/src/prolog/machine/mod.rs b/src/prolog/machine/mod.rs index da14cc80..48100377 100644 --- a/src/prolog/machine/mod.rs +++ b/src/prolog/machine/mod.rs @@ -297,7 +297,7 @@ impl Machine { Ok(self.indices.insert_module(module)) } else { - let err = ExistenceError::SourceSink(ModuleSource::File( + let err = ExistenceError::ModuleSource(ModuleSource::File( clause_name!("$toplevel"), )); @@ -409,6 +409,15 @@ impl Machine { ) ); + compile_user_module(&mut wam, + Stream::from(PAIRS), + true, + ListingSource::from_file_and_path( + clause_name!("pairs"), + lib_path.clone(), + ) + ); + compile_user_module(&mut wam, Stream::from(LISTS), true, diff --git a/src/prolog/machine/streams.rs b/src/prolog/machine/streams.rs index d9a83934..4e4af96e 100644 --- a/src/prolog/machine/streams.rs +++ b/src/prolog/machine/streams.rs @@ -1,12 +1,16 @@ use crate::prolog_parser::ast::*; use crate::prolog::read::readline::*; +use crate::prolog::machine::machine_errors::*; +use crate::prolog::machine::machine_indices::*; +use crate::prolog::machine::machine_state::*; +use crate::prolog::read::PrologStream; use std::cell::RefCell; use std::error::Error; use std::fmt; use std::fs::File; -use std::io::{stdin, stdout, Cursor, ErrorKind, Read, Write}; +use std::io::{stdout, Cursor, ErrorKind, Read, Write}; use std::hash::{Hash, Hasher}; use std::net::TcpStream; use std::rc::Rc; @@ -32,7 +36,7 @@ pub enum StreamInstance { File(File), Null, ReadlineStream(ReadlineStream), - Stdin, + // Stdin, Stdout, TcpStream(TcpStream), } @@ -48,7 +52,7 @@ impl fmt::Debug for StreamInstance { &StreamInstance::Null => write!(fmt, "Null"), &StreamInstance::ReadlineStream(ref readline_stream) => write!(fmt, "ReadlineStream({:?})", readline_stream), - &StreamInstance::Stdin => write!(fmt, "Stdin"), + // &StreamInstance::Stdin => write!(fmt, "Stdin"), &StreamInstance::Stdout => write!(fmt, "Stdout"), &StreamInstance::TcpStream(ref tcp_stream) => write!(fmt, "TcpStream({:?})", tcp_stream), @@ -140,6 +144,17 @@ pub struct Stream { stream_inst: WrappedStreamInstance, } +impl From for Stream { + fn from(tcp_stream: TcpStream) -> Self { + Stream { + options: StreamOptions::default(), + stream_inst: WrappedStreamInstance::new( + StreamInstance::TcpStream(tcp_stream) + ) + } + } +} + impl From for Stream { fn from(string: String) -> Self { Stream { @@ -187,7 +202,7 @@ impl From for Stream { impl Stream { #[inline] pub(crate) - fn as_ptr(&self) -> *const RefCell { + fn as_ptr(&self) -> *const u8 { let rc = self.stream_inst.0.clone(); let ptr = Rc::into_raw(rc); @@ -196,7 +211,7 @@ impl Stream { let _ = Rc::from_raw(ptr); } - ptr + ptr as *const u8 } #[inline] @@ -210,6 +225,7 @@ impl Stream { } } +/* #[inline] pub(crate) fn stdin() -> Self { @@ -220,6 +236,7 @@ impl Stream { ), } } +*/ #[inline] pub(crate) @@ -249,7 +266,8 @@ impl Stream { pub(crate) fn is_stdin(&self) -> bool { match *self.stream_inst.0.borrow() { - StreamInstance::Stdin | StreamInstance::ReadlineStream(_) => { + //StreamInstance::Stdin | + StreamInstance::ReadlineStream(_) => { true } _ => { @@ -262,12 +280,12 @@ impl Stream { pub(crate) fn is_input_stream(&self) -> bool { match *self.stream_inst.0.borrow() { - StreamInstance::Stdin - | StreamInstance::TcpStream(_) - | StreamInstance::Bytes(_) - | StreamInstance::ReadlineStream(_) - | StreamInstance::DynReadSource(_) - | StreamInstance::File(_) => { + // StreamInstance::Stdin | + StreamInstance::TcpStream(_) | + StreamInstance::Bytes(_) | + StreamInstance::ReadlineStream(_) | + StreamInstance::DynReadSource(_) | + StreamInstance::File(_) => { true } _ => { @@ -293,6 +311,221 @@ impl Stream { } } +impl MachineState { + pub(crate) + fn to_stream_options( + &self, + alias: Addr, + eof_action: Addr, + reposition: Addr, + stream_type: Addr, + ) -> StreamOptions { + let alias = + match self.store(self.deref(alias)) { + Addr::Con(h) if self.heap.atom_at(h) => { + if let HeapCellValue::Atom(ref name, _) = &self.heap[h] { + Some(name.clone()) + } else { + unreachable!() + } + } + _ => { + None + } + }; + + let eof_action = + match self.store(self.deref(eof_action)) { + Addr::Con(h) if self.heap.atom_at(h) => { + if let HeapCellValue::Atom(ref name, _) = &self.heap[h] { + match name.as_str() { + "eof_code" => EOFAction::EOFCode, + "error" => EOFAction::Error, + "reset" => EOFAction::Reset, + _ => unreachable!(), + } + } else { + unreachable!() + } + } + _ => { + unreachable!() + } + }; + + let reposition = + match self.store(self.deref(reposition)) { + Addr::Con(h) if self.heap.atom_at(h) => { + if let HeapCellValue::Atom(ref name, _) = &self.heap[h] { + name.as_str() == "true" + } else { + unreachable!() + } + } + _ => { + unreachable!() + } + }; + + let stream_type = + match self.store(self.deref(stream_type)) { + Addr::Con(h) if self.heap.atom_at(h) => { + if let HeapCellValue::Atom(ref name, _) = &self.heap[h] { + match name.as_str() { + "text" => StreamType::Text, + "binary" => StreamType::Binary, + _ => unreachable!() + } + } else { + unreachable!() + } + } + _ => { + unreachable!() + } + }; + + let mut options = StreamOptions::default(); + + options.stream_type = stream_type; + options.reposition = reposition; + options.alias = alias; + options.eof_action = eof_action; + + options + } + + pub(crate) + fn get_stream_or_alias( + &mut self, + addr: Addr, + indices: &IndexStore, + caller: &'static str, + arity: usize, + ) -> Result + { + Ok(match addr { + Addr::Con(h) if self.heap.atom_at(h) => { + if let HeapCellValue::Atom(ref atom, ref spec) = self.heap.clone(h) { + match indices.stream_aliases.get(atom) { + Some(stream) => { + stream.clone() + } + None => { + let stub = MachineError::functor_stub(clause_name!(caller), arity); + let h = self.heap.h(); + + let addr = self.heap.to_unifiable( + HeapCellValue::Atom(atom.clone(), spec.clone()) + ); + + return Err(self.error_form( + MachineError::existence_error(h + 1, ExistenceError::Stream(addr)), + stub, + )); + } + } + } else { + unreachable!() + } + } + Addr::Stream(h) => { + if let HeapCellValue::Stream(ref stream) = &self.heap[h] { + stream.clone() + } else { + unreachable!() + } + } + _ => { + let stub = MachineError::functor_stub(clause_name!(caller), arity); + + return Err(self.error_form( + MachineError::domain_error(DomainErrorType::StreamOrAlias, addr), + stub, + )); + } + }) + } + + pub(crate) + fn open_parsing_stream( + &self, + stream: Stream, + stub_name: &'static str, + stub_arity: usize, + ) -> Result { + match parsing_stream(stream) { + Ok(stream) => { + Ok(stream) + } + Err(e) => { + let stub = MachineError::functor_stub(clause_name!(stub_name), stub_arity); + let err = MachineError::session_error( + self.heap.h(), + SessionError::from(e), + ); + + Err(self.error_form(err, stub)) + } + } + } + + pub(crate) + fn open_permission_error( + &self, + culprit: T, + stub_name: &'static str, + stub_arity: usize, + ) -> MachineStub { + let stub = MachineError::functor_stub(clause_name!(stub_name), stub_arity); + let err = MachineError::permission_error( + self.heap.h(), + Permission::Open, + "source_sink", + culprit, + ); + + return self.error_form(err, stub); + } + + pub(crate) + fn occupied_alias_permission_error( + &self, + alias: ClauseName, + stub_name: &'static str, + stub_arity: usize, + ) -> MachineStub { + let stub = MachineError::functor_stub(clause_name!(stub_name), stub_arity); + let err = MachineError::permission_error( + self.heap.h(), + Permission::Open, + "source_sink", + functor!("alias", [clause_name(alias)]), + ); + + return self.error_form(err, stub); + } + + pub(crate) + fn reposition_error( + &self, + stub_name: &'static str, + stub_arity: usize, + ) -> MachineStub { + let stub = MachineError::functor_stub(clause_name!(stub_name), stub_arity); + let rep_stub = functor!("reposition", [atom("true")]); + + let err = MachineError::permission_error( + self.heap.h(), + Permission::Open, + "source_sink", + rep_stub, + ); + + return self.error_form(err, stub); + } +} + impl Read for Stream { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { match *self.stream_inst.0.borrow_mut() { @@ -311,9 +544,11 @@ impl Read for Stream { StreamInstance::Bytes(ref mut cursor) => { cursor.read(buf) } +/* StreamInstance::Stdin => { stdin().read(buf) } +*/ StreamInstance::Stdout | StreamInstance::Null => { Err(std::io::Error::new( ErrorKind::PermissionDenied, diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index 881789ea..1806d639 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -22,9 +22,10 @@ use crate::ref_thread_local::RefThreadLocal; use std::cmp; use std::convert::TryFrom; -use std::io::{stdout, Read, Write}; +use std::io::{stdout, ErrorKind, Read, Write}; use std::iter::{once, FromIterator}; use std::fs::File; +use std::net::{TcpListener, TcpStream}; use std::rc::Rc; use std::time::Duration; @@ -364,56 +365,6 @@ impl MachineState { Ok(()) } - fn get_stream_or_alias( - &mut self, - addr: Addr, - indices: &IndexStore, - caller: &'static str, - ) -> Result - { - Ok(match addr { - Addr::Con(h) if self.heap.atom_at(h) => { - if let HeapCellValue::Atom(ref atom, ref spec) = self.heap.clone(h) { - match indices.stream_aliases.get(atom) { - Some(stream) => { - stream.clone() - } - None => { - let stub = MachineError::functor_stub(clause_name!(caller), 1); - let h = self.heap.h(); - - let addr = self.heap.to_unifiable( - HeapCellValue::Atom(atom.clone(), spec.clone()) - ); - - return Err(self.error_form( - MachineError::existence_error(h + 1, ExistenceError::Stream(addr)), - stub, - )); - } - } - } else { - unreachable!() - } - } - Addr::Stream(h) => { - if let HeapCellValue::Stream(ref stream) = &self.heap[h] { - stream.clone() - } else { - unreachable!() - } - } - _ => { - let stub = MachineError::functor_stub(clause_name!(caller), 1); - - return Err(self.error_form( - MachineError::domain_error(DomainErrorType::StreamOrAlias, addr), - stub, - )); - } - }) - } - #[inline] fn install_new_block(&mut self, r: RegType) -> usize { self.block = self.b; @@ -779,6 +730,29 @@ impl MachineState { self.p = CodePtr::DynamicTransaction(trans_type, p); return Ok(()); } + &SystemClauseType::CurrentHostname => { + match hostname::get().ok() { + Some(host) => { + match host.into_string().ok() { + Some(host) => { + let hostname = self.heap.to_unifiable( + HeapCellValue::Atom(clause_name!(host, indices.atom_tbl), None) + ); + + self.unify(self[temp_v!(1)], hostname); + return return_from_clause!(self.last_call, self); + } + None => { + } + } + } + None => { + } + } + + self.fail = true; + return Ok(()); + } &SystemClauseType::CurrentInput => { let addr = self.store(self.deref(self[temp_v!(1)])); @@ -1539,8 +1513,6 @@ impl MachineState { } &SystemClauseType::FileToChars => { // TODO: Replace this with stream. - use std::io; - let a1 = self.store(self.deref(self[temp_v!(1)])); let a2 = self.store(self.deref(self[temp_v!(2)])); @@ -1570,15 +1542,15 @@ impl MachineState { let h = self.heap.h(); let err = match e.kind() { - io::ErrorKind::NotFound => { + ErrorKind::NotFound => { MachineError::existence_error( h, - ExistenceError::SourceSink( + ExistenceError::ModuleSource( ModuleSource::File(file_name) ), ) } - io::ErrorKind::PermissionDenied => { + ErrorKind::PermissionDenied => { let source_sink = self.store(self.deref(a1)); MachineError::permission_error( @@ -2545,17 +2517,14 @@ impl MachineState { 3, ); - let type_error = self.error_form( + return Err(self.error_form( MachineError::type_error( self.heap.h(), ValidType::Integer, a2, ), stub, - ); - - self.throw_exception(type_error); - return Ok(()); + )); } }; @@ -2868,7 +2837,7 @@ impl MachineState { } &SystemClauseType::SetInput => { let addr = self.store(self.deref(self[temp_v!(1)])); - let stream = self.get_stream_or_alias(addr, indices, "set_input")?; + let stream = self.get_stream_or_alias(addr, indices, "set_input", 1)?; if stream.is_output_stream() { let stub = MachineError::functor_stub( @@ -2894,7 +2863,7 @@ impl MachineState { } &SystemClauseType::SetOutput => { let addr = self.store(self.deref(self[temp_v!(1)])); - let stream = self.get_stream_or_alias(addr, indices, "set_output")?; + let stream = self.get_stream_or_alias(addr, indices, "set_output", 1)?; if stream.is_input_stream() { let stub = MachineError::functor_stub( @@ -3182,14 +3151,29 @@ impl MachineState { } &SystemClauseType::ReadQueryTerm => { readline::set_prompt(true); - let result = self.read_term(current_input_stream, indices); + let result = self.read_term(current_input_stream.clone(), indices); readline::set_prompt(false); - let _ = result?; + match result { + Ok(()) => { + } + Err(e) => { + *current_input_stream = readline::input_stream(); + return Err(e); + } + } } &SystemClauseType::ReadTerm => { readline::set_prompt(false); - self.read_term(current_input_stream, indices)?; + + let stream = self.get_stream_or_alias( + self[temp_v!(1)], + indices, + "read_term", + 3, + )?; + + self.read_term(stream, indices)?; } &SystemClauseType::ReadTermFromChars => { let mut heap_pstr_iter = self.heap_pstr_iter(self[temp_v!(1)]); @@ -3302,6 +3286,304 @@ impl MachineState { let duration = duration.mul_f64(time); ::std::thread::sleep(duration); } + &SystemClauseType::SocketClientOpen => { + let addr = self.store(self.deref(self[temp_v!(1)])); + let port = self.store(self.deref(self[temp_v!(2)])); + + let socket_atom = + match addr { + Addr::Con(h) if self.heap.atom_at(h) => { + if let HeapCellValue::Atom(ref name, _) = &self.heap[h] { + name.clone() + } else { + unreachable!() + } + } + _ => { + unreachable!() + } + }; + + let port = + match port { + Addr::Fixnum(n) => { + n.to_string() + } + Addr::Usize(n) => { + n.to_string() + } + Addr::Con(h) => { + match &self.heap[h] { + HeapCellValue::Atom(ref name, _) => { + name.as_str().to_string() + } + HeapCellValue::Integer(ref n) => { + n.to_string() + } + _ => { + unreachable!() + } + } + } + _ => { + unreachable!() + } + }; + + let socket_addr = + format!( + "{}:{}", + if socket_atom.as_str() == "" { + "127.0.0.1" + } else { + socket_atom.as_str() + }, + port, + ); + + let alias = self[temp_v!(4)]; + let eof_action = self[temp_v!(5)]; + let reposition = self[temp_v!(6)]; + let stream_type = self[temp_v!(7)]; + + let options = + self.to_stream_options(alias, eof_action, reposition, stream_type); + + if options.reposition { + return Err(self.reposition_error("socket_client_open", 3)); + } + + if let Some(ref alias) = &options.alias { + if indices.stream_aliases.contains_key(alias) { + return Err(self.occupied_alias_permission_error( + alias.clone(), + "socket_client_open", + 3, + )); + } + } + + let stream = + match TcpStream::connect(socket_addr).map_err(|e| e.kind()) { + Ok(tcp_stream) => { + let mut stream = Stream::from(tcp_stream); + + if let Some(ref alias) = &options.alias { + indices.stream_aliases.insert(alias.clone(), stream.clone()); + } + + stream.options = options; + self.heap.to_unifiable(HeapCellValue::Stream(stream)) + } + Err(ErrorKind::PermissionDenied) => { + return Err(self.open_permission_error(addr, "socket_client_open", 3)); + } + Err(ErrorKind::NotFound) => { + let stub = MachineError::functor_stub( + clause_name!("socket_client_open"), + 3, + ); + + let err = MachineError::existence_error( + self.heap.h(), + ExistenceError::SourceSink(addr), + ); + + return Err(self.error_form(err, stub)); + } + Err(_) => { + // for now, just fail. expand to meaningful error messages later. + self.fail = true; + return Ok(()); + } + }; + + let stream_addr = self.store(self.deref(self[temp_v!(3)])); + self.bind(stream_addr.as_var().unwrap(), stream); + } + &SystemClauseType::SocketServerOpen => { + let addr = self.store(self.deref(self[temp_v!(1)])); + let socket_atom = + match addr { + Addr::EmptyList => { + "127.0.0.1".to_string() + } + Addr::Con(h) if self.heap.atom_at(h) => { + match &self.heap[h] { + HeapCellValue::Atom(ref name, _) => { + name.as_str().to_string() + } + _ => { + unreachable!() + } + } + } + _ => { + unreachable!() + } + }; + + let port = + match self.store(self.deref(self[temp_v!(2)])) { + Addr::Fixnum(n) => { + n.to_string() + } + Addr::Usize(n) => { + n.to_string() + } + Addr::Con(h) => { + match &self.heap[h] { + HeapCellValue::Integer(ref n) => { + n.to_string() + } + _ => { + unreachable!() + } + } + } + addr if addr.is_ref() => { + "0".to_string() + } + _ => { + unreachable!() + } + }; + + let had_zero_port = &port == "0"; + + let server_addr = if socket_atom.is_empty() { + port + } else { + format!("{}:{}", socket_atom, port) + }; + + let (tcp_listener, port) = + match TcpListener::bind(server_addr).map_err(|e| e.kind()) { + Ok(tcp_listener) => { + let port = tcp_listener.local_addr().map(|addr| addr.port()).ok(); + + if let Some(port) = port { + ( + self.heap.to_unifiable(HeapCellValue::TcpListener(tcp_listener)), + port as usize, + ) + } else { + self.fail = true; + return Ok(()); + } + } + Err(ErrorKind::PermissionDenied) => { + return Err(self.open_permission_error(addr, "socket_server_open", 2)); + } + _ => { + self.fail = true; + return Ok(()); + } + }; + + let addr = self.store(self.deref(self[temp_v!(3)])); + self.bind(addr.as_var().unwrap(), tcp_listener); + + if had_zero_port { + self.unify(self[temp_v!(2)], Addr::Usize(port)); + } + } + &SystemClauseType::SocketServerAccept => { + let alias = self[temp_v!(4)]; + let eof_action = self[temp_v!(5)]; + let reposition = self[temp_v!(6)]; + let stream_type = self[temp_v!(7)]; + + let options = + self.to_stream_options(alias, eof_action, reposition, stream_type); + + if options.reposition { + return Err(self.reposition_error("socket_server_accept", 4)); + } + + match self.store(self.deref(self[temp_v!(1)])) { + Addr::TcpListener(h) => { + match &mut self.heap[h] { + HeapCellValue::TcpListener(ref mut tcp_listener) => { + match tcp_listener.accept().ok() { + Some((tcp_stream, socket_addr)) => { + let mut tcp_stream = Stream::from(tcp_stream); + tcp_stream.options = options; + + let tcp_stream = + self.heap.to_unifiable(HeapCellValue::Stream(tcp_stream)); + + let client = + clause_name!(format!("{}", socket_addr), indices.atom_tbl); + let client = + self.heap.to_unifiable(HeapCellValue::Atom(client, None)); + + let client_addr = self.store(self.deref(self[temp_v!(2)])); + let stream_addr = self.store(self.deref(self[temp_v!(3)])); + + self.bind(client_addr.as_var().unwrap(), client); + self.bind(stream_addr.as_var().unwrap(), tcp_stream); + } + None => { + self.fail = true; + return Ok(()); + } + } + } + culprit => { + let culprit = culprit.as_addr(h); + let stub = MachineError::functor_stub( + clause_name!("socket_server_close"), + 1, + ); + + let err = MachineError::type_error( + self.heap.h(), + ValidType::TcpListener, + culprit, + ); + + return Err(self.error_form(err, stub)); + } + } + } + culprit => { + let stub = MachineError::functor_stub( + clause_name!("socket_server_accept"), + 4, + ); + + let err = MachineError::type_error( + self.heap.h(), + ValidType::TcpListener, + culprit, + ); + + return Err(self.error_form(err, stub)); + } + } + } + &SystemClauseType::SocketServerClose => { + match self.store(self.deref(self[temp_v!(1)])) { + Addr::TcpListener(h) => { + self.heap[h] = HeapCellValue::Addr(Addr::EmptyList); + } + culprit => { + let stub = MachineError::functor_stub( + clause_name!("socket_server_close"), + 1, + ); + + let err = MachineError::type_error( + self.heap.h(), + ValidType::TcpListener, + culprit, + ); + + return Err(self.error_form(err, stub)); + } + } + } &SystemClauseType::StoreGlobalVar => { let key = self[temp_v!(1)]; diff --git a/src/prolog/toplevel.pl b/src/prolog/toplevel.pl index 461e7195..7d50c07f 100644 --- a/src/prolog/toplevel.pl +++ b/src/prolog/toplevel.pl @@ -106,7 +106,7 @@ repl :- repl. read_and_match :- - '$read_query_term'(Term, VarList), + '$read_query_term'(_, Term, _, _, VarList), instruction_match(Term, VarList). % make compile_batch, a system routine, callable. diff --git a/src/prolog/write.rs b/src/prolog/write.rs index 9c2267ae..4c94d955 100644 --- a/src/prolog/write.rs +++ b/src/prolog/write.rs @@ -181,6 +181,9 @@ impl fmt::Display for HeapCellValue { &HeapCellValue::Stream(ref stream) => { write!(f, "$stream({})", stream.as_ptr() as usize) } + &HeapCellValue::TcpListener(ref tcp_listener) => { + write!(f, "$tcp_listener({})", tcp_listener.local_addr().unwrap()) + } } } } @@ -213,6 +216,7 @@ impl fmt::Display for Addr { &Addr::Str(s) => write!(f, "Addr::Str({})", s), &Addr::PStrLocation(h, n) => write!(f, "Addr::PStrLocation({}, {})", h, n), &Addr::Stream(stream) => write!(f, "Addr::Stream({})", stream), + &Addr::TcpListener(tcp_listener) => write!(f, "Addr::TcpListener({})", tcp_listener), &Addr::Usize(cp) => write!(f, "Addr::Usize({})", cp), } } @@ -332,11 +336,14 @@ impl fmt::Display for ExistenceError { &ExistenceError::Module(ref module_name) => { write!(f, "the module {} does not exist", module_name) } + &ExistenceError::ModuleSource(ref module_source) => { + write!(f, "the source/sink {} does not exist", module_source) + } &ExistenceError::Procedure(ref name, arity) => { write!(f, "the procedure {}/{} does not exist", name, arity) } - &ExistenceError::SourceSink(ref module_source) => { - write!(f, "the source/sink {} does not exist", module_source) + &ExistenceError::SourceSink(ref addr) => { + write!(f, "the source/sink {} does not exist", addr) } &ExistenceError::Stream(ref addr) => { write!(f, "the stream at {} does not exist", addr) From d04abc20215571f317aeb0abffa5be8fd49d2679 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sun, 3 May 2020 23:08:53 -0600 Subject: [PATCH 02/18] improve answer substitutions at top-level --- src/prolog/toplevel.pl | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/src/prolog/toplevel.pl b/src/prolog/toplevel.pl index ed17f80a..8c3c6941 100644 --- a/src/prolog/toplevel.pl +++ b/src/prolog/toplevel.pl @@ -235,7 +235,8 @@ write_eqs_and_read_input(B, VarList) :- append([Vars0, AttrVars, AttrGoalVars], Vars), charsio:extend_var_list(Vars, VarList, NewVarList, fabricated), '$get_b_value'(B0), - gather_equations(NewVarList, NewVarList, Goals, AttrGoals), + gather_query_vars(VarList, OrigVars), + gather_equations(NewVarList, OrigVars, Goals, AttrGoals), ( bb_get('$first_answer', true) -> write(' '), bb_put('$first_answer', false) @@ -286,8 +287,8 @@ help_message :- gather_query_vars([_ = Var | Vars], QueryVars) :- ( var(Var) -> - QueryVars = [Var | QueryVars1], - gather_query_vars(Vars, QueryVars1) + QueryVars = [Var | QueryVars0], + gather_query_vars(Vars, QueryVars0) ; gather_query_vars(Vars, QueryVars) ). gather_query_vars([], []). @@ -297,16 +298,38 @@ is_a_different_variable([_ = Binding | Pairs], Value) :- ; is_a_different_variable(Pairs, Var) ). +eq_member(X, [Y|_]) :- X == Y, !. +eq_member(X, [_|Ys]) :- eq_member(X, Ys). + +gather_equations([], _, Goals, Goals). +gather_equations([Var = Value | Pairs], OrigVarList, Goals, Goals1) :- + ( var(Value) -> + eq_member(Value, OrigVarList), + ( ( Pairs == [], NewPairs = [] + ; ( select((OtherVar = OtherValue), Pairs, NewPairs), + Value == OtherValue, Var \== OtherVar + ) + ) -> + Goals = [Var = Value | Goals0], + gather_equations(NewPairs, OrigVarList, Goals0, Goals1) + ; gather_equations(Pairs, OrigVarList, Goals, Goals1) + ) + ; Goals = [Var = Value | Goals0], + gather_equations(Pairs, OrigVarList, Goals0, Goals1) + ). + +/* gather_equations([], MasterList, Goals, Goals). gather_equations([Var = Value | Pairs], MasterList, Goals, Goals1) :- - select((Var = _), MasterList, MasterPairs), ( ( nonvar(Value) - ; is_a_different_variable(MasterPairs, Value) + ; select((Var = _), MasterList, MasterPairs), + is_a_different_variable(MasterPairs, Value) ) -> Goals = [Var = Value | Goals0], gather_equations(Pairs, MasterList, Goals0, Goals1) ; gather_equations(Pairs, MasterList, Goals, Goals1) ). +*/ print_exception(E) :- ( E == error('$interrupt_thrown', repl) -> nl % print the From 0d7a9f32d6207a661ceb283fcc68d91667a2aec2 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Mon, 4 May 2020 14:50:26 -0600 Subject: [PATCH 03/18] add open/3, write_term/3 --- src/prolog/clause_types.rs | 7 +- src/prolog/lib/builtins.pl | 118 +++++++++++--------- src/prolog/machine/compile.rs | 2 +- src/prolog/machine/machine_errors.rs | 4 +- src/prolog/machine/machine_state.rs | 10 +- src/prolog/machine/mod.rs | 2 +- src/prolog/machine/streams.rs | 80 +++++++++----- src/prolog/machine/system_calls.rs | 154 ++++++++++++++++++++++++++- src/prolog/macros.rs | 23 ++++ 9 files changed, 308 insertions(+), 92 deletions(-) diff --git a/src/prolog/clause_types.rs b/src/prolog/clause_types.rs index b5bfae0e..ce5d9e68 100644 --- a/src/prolog/clause_types.rs +++ b/src/prolog/clause_types.rs @@ -216,6 +216,7 @@ pub enum SystemClauseType { NumberToChars, NumberToCodes, OpDeclaration, + Open, PartialStringTail, PointsToContinuationResetMarker, REPL(REPLCodePtr), @@ -352,7 +353,8 @@ impl SystemClauseType { &SystemClauseType::GetSCCCleaner => clause_name!("$get_scc_cleaner"), &SystemClauseType::Halt => clause_name!("$halt"), &SystemClauseType::HeadIsDynamic => clause_name!("$head_is_dynamic"), - &SystemClauseType::OpDeclaration => clause_name!("$op$"), + &SystemClauseType::Open => clause_name!("$open"), + &SystemClauseType::OpDeclaration => clause_name!("$op"), &SystemClauseType::InstallSCCCleaner => clause_name!("$install_scc_cleaner"), &SystemClauseType::InstallInferenceCounter => { clause_name!("$install_inference_counter") @@ -514,6 +516,7 @@ impl SystemClauseType { ("$number_to_chars", 2) => Some(SystemClauseType::NumberToChars), ("$number_to_codes", 2) => Some(SystemClauseType::NumberToCodes), ("$op", 3) => Some(SystemClauseType::OpDeclaration), + ("$open", 7) => Some(SystemClauseType::Open), ("$redo_attr_var_binding", 2) => Some(SystemClauseType::RedoAttrVarBinding), ("$remove_call_policy_check", 1) => Some(SystemClauseType::RemoveCallPolicyCheck), ("$remove_inference_counter", 2) => Some(SystemClauseType::RemoveInferenceCounter), @@ -570,7 +573,7 @@ impl SystemClauseType { Some(SystemClauseType::REPL(REPLCodePtr::UseQualifiedModuleFromFile)), ("$variant", 2) => Some(SystemClauseType::Variant), ("$wam_instructions", 3) => Some(SystemClauseType::WAMInstructions), - ("$write_term", 6) => Some(SystemClauseType::WriteTerm), + ("$write_term", 7) => Some(SystemClauseType::WriteTerm), ("$write_term_to_chars", 7) => Some(SystemClauseType::WriteTermToChars), ("$scryer_prolog_version", 1) => Some(SystemClauseType::ScryerPrologVersion), _ => None, diff --git a/src/prolog/lib/builtins.pl b/src/prolog/lib/builtins.pl index 1d5080e5..2ec5680e 100644 --- a/src/prolog/lib/builtins.pl +++ b/src/prolog/lib/builtins.pl @@ -49,12 +49,14 @@ user:term_expansion((:- op(Pred, Spec, [Op | OtherOps])), OpResults) :- expand_goal/2, expand_term/2, fail/0, false/0, findall/3, findall/4, get_char/1, halt/0, max_arity/1, number_chars/2, number_codes/2, - once/1, op/3, 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, unify_with_occurs_check/2, write/1, - write_canonical/1, write_term/2, writeq/1]). + 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, + unify_with_occurs_check/2, write/1, + write_canonical/1, write_term/2, write_term/3, + writeq/1]). % the maximum arity flag. needs to be replaced with @@ -312,36 +314,39 @@ get_args([Arg|Args], Func, I0, N) :- '$call_with_default_policy'(I1 is I0 + 1), '$call_with_default_policy'(get_args(Args, Func, I1, N)). -% write, write_canonical, writeq, write_term. -is_write_option(Functor) :- - Functor =.. [Name, Arg], - ( Arg == true -> true - ; Arg == false -> true - ; Name == variable_names -> must_be_var_names_list(Arg) - ; Name == max_depth -> integer(Arg), Arg >= 0 - ; var(Arg) -> throw(error(instantiation_error, write_term/2)) - ; throw(error(domain_error(write_option, Functor), write_term/2)) - ), % 8.14.2.3 e) - ( Name == ignore_ops -> true - ; Name == quoted -> true - ; Name == numbervars -> true - ; Name == variable_names -> true - ; Name == max_depth -> true - ; throw(error(domain_error(write_option, Functor), write_term/2)) - ). % 8.14.2.3 e) +parse_write_options(Options, OptionValues, Stub) :- + DefaultOptions = [ignore_ops-false, max_depth-0, numbervars-false, + quoted-false, variable_names-[]], + parse_options_list(Options, parse_write_options_, DefaultOptions, OptionValues, Stub). -inst_member_or([X|Xs], Y, Z) :- - ( var(X) -> throw(error(instantiation_error, write_term/2)) - ; is_write_option(X) -> ( Y = X, ! ; inst_member_or(Xs, Y, Z) ) - ; throw(error(domain_error(write_option, X), write_term/2)) +parse_write_options_(ignore_ops(IgnoreOps), ignore_ops-IgnoreOps) :- + ( nonvar(IgnoreOps), lists:member(IgnoreOps, [true, false]) + ; + throw(error(domain_error(write_option, ignore_ops(IgnoreOps)), _)) + ). +parse_write_options_(quoted(Quoted), quoted-Quoted) :- + ( nonvar(Quoted), lists:member(Quoted, [true, false]) + ; + throw(error(domain_error(write_option, quoted(Quoted)), _)) + ). +parse_write_options_(numbervars(NumberVars), numbervars-NumberVars) :- + ( nonvar(NumberVars), lists:member(NumberVars, [true, false]) + ; + throw(error(domain_error(write_option, numbervars(NumberVars)), _)) + ). +parse_write_options_(variable_names(VNNames), variable_names-VNNames) :- + must_be_var_names_list(VNNames). +parse_write_options_(max_depth(MaxDepth), max_depth-MaxDepth) :- + ( integer(MaxDepth), MaxDepth >= 0 + ; + throw(error(domain_error(write_option, max_depth(MaxDepth)), _)) ). -inst_member_or([], Y, Y). must_be_var_names_list(VarNames) :- '$skip_max_list'(_, -1, VarNames, Tail), ( Tail == [] -> must_be_var_names_list_(VarNames, VarNames) ; var(Tail) -> throw(error(instantiation_error, write_term/2)) - ; throw(error(domain_error(write_options, variable_names(VarNames)), write_term/2)) + ; throw(error(domain_error(write_option, variable_names(VarNames)), write_term/2)) ). must_be_var_names_list_([], List). @@ -350,36 +355,34 @@ must_be_var_names_list_([VarName | VarNames], List) :- ( VarName = (Atom = _) -> ( atom(Atom) -> must_be_var_names_list_(VarNames, List) ; var(Atom) -> throw(error(instantiation_error, write_term/2)) - ; throw(error(domain_error(write_options, variable_names(List)), write_term/2)) + ; throw(error(domain_error(write_option, variable_names(List)), write_term/2)) ) - ; throw(error(domain_error(write_options, variable_names(List)), write_term/2)) + ; throw(error(domain_error(write_option, variable_names(List)), write_term/2)) ) - ; throw(error(instantiation_error, write_term/2)) % throw(error(domain_error(write_options, variable_names(List)), write_term/2)) + ; throw(error(instantiation_error, write_term/2)) ). -write_term(_, Options) :- - var(Options), throw(error(instantiation_error, write_term/2)). + write_term(Term, Options) :- - '$skip_max_list'(_, -1, Options, Options0), - ( var(Options0) -> throw(error(instantiation_error, write_term/2)) - ; Options0 == [] -> true - ; throw(error(type_error(list, Options), write_term/2)) - ), % 8.14.2.3 c) - inst_member_or(Options, ignore_ops(IgnoreOps), ignore_ops(false)), - inst_member_or(Options, numbervars(NumberVars), numbervars(false)), - inst_member_or(Options, quoted(Quoted), quoted(false)), - inst_member_or(Options, variable_names(VarNames), variable_names([])), - inst_member_or(Options, max_depth(MaxDepth), max_depth(0)), - '$write_term'(Term, IgnoreOps, NumberVars, Quoted, VarNames, MaxDepth). + current_output(Stream), + write_term(Stream, Term, Options). + +write_term(Stream, Term, Options) :- + parse_write_options(Options, [IgnoreOps, MaxDepth, NumberVars, Quoted, VNNames], write_term/3), + '$write_term'(Stream, Term, IgnoreOps, NumberVars, Quoted, VNNames, MaxDepth). + write(Term) :- - '$write_term'(Term, false, true, false, [], 0). + current_output(Stream), + '$write_term'(Stream, Term, false, true, false, [], 0). write_canonical(Term) :- - '$write_term'(Term, true, false, true, [], 0). + current_output(Stream), + '$write_term'(Stream, Term, true, false, true, [], 0). writeq(Term) :- - '$write_term'(Term, false, true, true, [], 0). + current_output(Stream), + '$write_term'(Stream, Term, false, true, true, [], 0). @@ -1139,3 +1142,22 @@ parse_stream_options_(eof_action(Action), eof_action-Action) :- ). parse_stream_options_(E, _) :- throw(error(domain_error(stream_option, E), _)). % 8.11.5.3i) + + +open(SourceSink, Mode, Stream) :- + open(SourceSink, Mode, Stream, []). + +open(SourceSink, Mode, Stream, StreamOptions) :- + ( var(SourceSink) -> + throw(error(instantiation_error, open/4)) % 8.11.5.3a) + ; var(Mode) -> + throw(error(instantiation_error, open/4)) % 8.11.5.3b) + ; \+ atom(Mode) -> + throw(error(type_error(atom, Mode), open/4)) % 8.11.5.3d) + ; nonvar(Stream) -> + throw(error(type_error(variable, Stream), open/4)) % 8.11.5.3f) + ; + parse_stream_options(StreamOptions, [Alias, EOFAction, Reposition, Type], open/4), + '$open'(SourceSink, Mode, Stream, Alias, EOFAction, Reposition, Type) + ). + diff --git a/src/prolog/machine/compile.rs b/src/prolog/machine/compile.rs index 8882e2ff..a887cfb6 100644 --- a/src/prolog/machine/compile.rs +++ b/src/prolog/machine/compile.rs @@ -119,7 +119,7 @@ fn load_module_from_file( let mut path_buf = fix_filename(wam.indices.atom_tbl.clone(), path_buf)?; let filename = clause_name!(path_buf.to_string_lossy().to_string(), wam.indices.atom_tbl); - let file_handle = Stream::from(File::open(&path_buf).or_else(|_| { + let file_handle = Stream::from_file_as_input(File::open(&path_buf).or_else(|_| { Err(SessionError::InvalidFileName(filename.clone())) })?); diff --git a/src/prolog/machine/machine_errors.rs b/src/prolog/machine/machine_errors.rs index a46345e2..4a022316 100644 --- a/src/prolog/machine/machine_errors.rs +++ b/src/prolog/machine/machine_errors.rs @@ -274,7 +274,7 @@ impl MachineError { MachineError { stub, location: None, - from: ErrorProvenance::Constructed, + from: ErrorProvenance::Received, } } ExistenceError::Stream(culprit) => { @@ -533,6 +533,7 @@ impl ValidType { #[derive(Debug, Clone, Copy)] pub enum DomainErrorType { + IOMode, NotLessThanZero, Order, Stream, @@ -542,6 +543,7 @@ pub enum DomainErrorType { impl DomainErrorType { pub fn as_str(self) -> &'static str { match self { + DomainErrorType::IOMode => "io_mode", DomainErrorType::NotLessThanZero => "not_less_than_zero", DomainErrorType::Order => "order", DomainErrorType::Stream => "stream", diff --git a/src/prolog/machine/machine_state.rs b/src/prolog/machine/machine_state.rs index 46fc5ed9..b86496b3 100644 --- a/src/prolog/machine/machine_state.rs +++ b/src/prolog/machine/machine_state.rs @@ -723,10 +723,10 @@ impl MachineState { op_dir: &'a OpDir, ) -> Result>, MachineStub> { - let ignore_ops = self.store(self.deref(self[temp_v!(2)])); - let numbervars = self.store(self.deref(self[temp_v!(3)])); - let quoted = self.store(self.deref(self[temp_v!(4)])); - let max_depth = self.store(self.deref(self[temp_v!(6)])); + let ignore_ops = self.store(self.deref(self[temp_v!(3)])); + let numbervars = self.store(self.deref(self[temp_v!(4)])); + let quoted = self.store(self.deref(self[temp_v!(5)])); + let max_depth = self.store(self.deref(self[temp_v!(7)])); let mut printer = HCPrinter::new(&self, op_dir, PrinterOutputter::new()); @@ -776,7 +776,7 @@ impl MachineState { let stub = MachineError::functor_stub(clause_name!("write_term"), 2); - match self.try_from_list(temp_v!(5), stub) { + match self.try_from_list(temp_v!(6), stub) { Ok(addrs) => { let mut var_names: IndexMap = IndexMap::new(); diff --git a/src/prolog/machine/mod.rs b/src/prolog/machine/mod.rs index f4412340..781fa8a3 100644 --- a/src/prolog/machine/mod.rs +++ b/src/prolog/machine/mod.rs @@ -315,7 +315,7 @@ impl Machine { if path.is_file() { let file_src = match File::open(&path) { - Ok(file_handle) => Stream::from(file_handle), + Ok(file_handle) => Stream::from_file_as_input(file_handle), Err(_) => return, }; diff --git a/src/prolog/machine/streams.rs b/src/prolog/machine/streams.rs index 4e4af96e..8cd7cbf0 100644 --- a/src/prolog/machine/streams.rs +++ b/src/prolog/machine/streams.rs @@ -33,7 +33,8 @@ pub enum EOFAction { pub enum StreamInstance { Bytes(Cursor>), DynReadSource(Box), - File(File), + InputFile(File), + OutputFile(File), Null, ReadlineStream(ReadlineStream), // Stdin, @@ -48,7 +49,8 @@ impl fmt::Debug for StreamInstance { write!(fmt, "Bytes({:?})", bytes), &StreamInstance::DynReadSource(_) => write!(fmt, "DynReadSource(_)"), // Hacky solution. - &StreamInstance::File(ref file) => write!(fmt, "File({:?})", file), + &StreamInstance::InputFile(ref file) => write!(fmt, "InputFile({:?})", file), + &StreamInstance::OutputFile(ref file) => write!(fmt, "OutputFile({:?})", file), &StreamInstance::Null => write!(fmt, "Null"), &StreamInstance::ReadlineStream(ref readline_stream) => write!(fmt, "ReadlineStream({:?})", readline_stream), @@ -188,17 +190,6 @@ impl From<&'static str> for Stream { } } -impl From for Stream { - fn from(file: File) -> Stream { - Stream { - options: StreamOptions::default(), - stream_inst: WrappedStreamInstance::new( - StreamInstance::File(file) - ), - } - } -} - impl Stream { #[inline] pub(crate) @@ -225,6 +216,28 @@ impl Stream { } } + #[inline] + pub(crate) + fn from_file_as_output(file: File) -> Self { + Stream { + options: StreamOptions::default(), + stream_inst: WrappedStreamInstance::new( + StreamInstance::OutputFile(file) + ), + } + } + + #[inline] + pub(crate) + fn from_file_as_input(file: File) -> Self { + Stream { + options: StreamOptions::default(), + stream_inst: WrappedStreamInstance::new( + StreamInstance::InputFile(file) + ), + } + } + /* #[inline] pub(crate) @@ -285,9 +298,9 @@ impl Stream { StreamInstance::Bytes(_) | StreamInstance::ReadlineStream(_) | StreamInstance::DynReadSource(_) | - StreamInstance::File(_) => { + StreamInstance::InputFile(_) => { true - } + } _ => { false } @@ -301,7 +314,7 @@ impl Stream { StreamInstance::Stdout | StreamInstance::TcpStream(_) | StreamInstance::Bytes(_) - | StreamInstance::File(_) => { + | StreamInstance::OutputFile(_) => { true } _ => { @@ -404,9 +417,9 @@ impl MachineState { arity: usize, ) -> Result { - Ok(match addr { + Ok(match self.store(self.deref(addr)) { Addr::Con(h) if self.heap.atom_at(h) => { - if let HeapCellValue::Atom(ref atom, ref spec) = self.heap.clone(h) { + if let HeapCellValue::Atom(ref atom, ref spec) = self.heap.clone(h) { match indices.stream_aliases.get(atom) { Some(stream) => { stream.clone() @@ -436,13 +449,20 @@ impl MachineState { unreachable!() } } - _ => { + addr => { let stub = MachineError::functor_stub(clause_name!(caller), arity); - return Err(self.error_form( - MachineError::domain_error(DomainErrorType::StreamOrAlias, addr), - stub, - )); + if addr.is_ref() { + return Err(self.error_form( + MachineError::instantiation_error(), + stub, + )); + } else { + return Err(self.error_form( + MachineError::domain_error(DomainErrorType::StreamOrAlias, addr), + stub, + )); + } } }) } @@ -529,7 +549,7 @@ impl MachineState { impl Read for Stream { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { match *self.stream_inst.0.borrow_mut() { - StreamInstance::File(ref mut file) => { + StreamInstance::InputFile(ref mut file) => { file.read(buf) } StreamInstance::TcpStream(ref mut tcp_stream) => { @@ -549,7 +569,7 @@ impl Read for Stream { stdin().read(buf) } */ - StreamInstance::Stdout | StreamInstance::Null => { + StreamInstance::OutputFile(_) | StreamInstance::Stdout | StreamInstance::Null => { Err(std::io::Error::new( ErrorKind::PermissionDenied, StreamError::ReadFromOutputStream, @@ -562,7 +582,7 @@ impl Read for Stream { impl Write for Stream { fn write(&mut self, buf: &[u8]) -> std::io::Result { match *self.stream_inst.0.borrow_mut() { - StreamInstance::File(ref mut file) => { + StreamInstance::OutputFile(ref mut file) => { file.write(buf) } StreamInstance::TcpStream(ref mut tcp_stream) => { @@ -574,7 +594,8 @@ impl Write for Stream { StreamInstance::Stdout => { stdout().write(buf) } - _ => { + StreamInstance::DynReadSource(_) | StreamInstance::ReadlineStream(_) | + StreamInstance::InputFile(_) | StreamInstance::Null => { Err(std::io::Error::new( ErrorKind::PermissionDenied, StreamError::WriteToInputStream, @@ -585,7 +606,7 @@ impl Write for Stream { fn flush(&mut self) -> std::io::Result<()> { match *self.stream_inst.0.borrow_mut() { - StreamInstance::File(ref mut file) => { + StreamInstance::OutputFile(ref mut file) => { file.flush() } StreamInstance::TcpStream(ref mut tcp_stream) => { @@ -597,7 +618,8 @@ impl Write for Stream { StreamInstance::Stdout => { stdout().flush() } - _ => { + StreamInstance::DynReadSource(_) | StreamInstance::ReadlineStream(_) | + StreamInstance::InputFile(_) | StreamInstance::Null => { Err(std::io::Error::new( ErrorKind::PermissionDenied, StreamError::FlushToInputStream, diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index 1806d639..a0e10b25 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -22,9 +22,9 @@ use crate::ref_thread_local::RefThreadLocal; use std::cmp; use std::convert::TryFrom; -use std::io::{stdout, ErrorKind, Read, Write}; +use std::io::{ErrorKind, Read, Write}; use std::iter::{once, FromIterator}; -use std::fs::File; +use std::fs::{File, OpenOptions}; use std::net::{TcpListener, TcpStream}; use std::rc::Rc; @@ -2206,6 +2206,103 @@ impl MachineState { } }; } + &SystemClauseType::Open => { + let alias = self[temp_v!(4)]; + let eof_action = self[temp_v!(5)]; + let reposition = self[temp_v!(6)]; + let stream_type = self[temp_v!(7)]; + + let options = + self.to_stream_options(alias, eof_action, reposition, stream_type); + + let file_spec = + atom_from!(self, indices, self.store(self.deref(self[temp_v!(1)]))); + + // 8.11.5.3l) + if let Some(ref alias) = &options.alias { + if indices.stream_aliases.contains_key(alias) { + return Err(self.occupied_alias_permission_error( + alias.clone(), + "open", + 4, + )); + } + } + + let mode = + atom_from!(self, indices, self.store(self.deref(self[temp_v!(2)]))); + + let mut open_options = OpenOptions::new(); + + let is_input_file = + match mode.as_str() { + "read" => { + open_options.read(true).write(false).create(false); + true + } + "write" => { + open_options.read(false).write(true).create(true).append(false); + false + } + "append" => { + open_options.read(false).write(true).create(true).append(true); + false + } + _ => { + let stub = MachineError::functor_stub(clause_name!("open"), 4); + let err = MachineError::domain_error( + DomainErrorType::IOMode, + self[temp_v!(2)], + ); + + // 8.11.5.3h) + return Err(self.error_form(err, stub)); + } + }; + + let file = + match open_options.open(file_spec.as_str()).map_err(|e| e.kind()) { + Ok(file) => { + file + } + Err(ErrorKind::NotFound) => { + // 8.11.5.3j) + let stub = MachineError::functor_stub( + clause_name!("open"), + 4, + ); + + let err = MachineError::existence_error( + self.heap.h(), + ExistenceError::SourceSink(self[temp_v!(1)]), + ); + + return Err(self.error_form(err, stub)); + } + Err(ErrorKind::PermissionDenied) => { + // 8.11.5.3k) + return Err(self.open_permission_error(self[temp_v!(1)], "open", 4)); + } + Err(_) => { + // for now, just fail. expand to meaningful error messages later. + self.fail = true; + return Ok(()); + } + }; + + let mut stream = if is_input_file { + Stream::from_file_as_input(file) + } else { + Stream::from_file_as_output(file) + }; + + stream.options = options; + + let stream = self.heap.to_unifiable(HeapCellValue::Stream(stream)); + let stream_var = self.store(self.deref(self[temp_v!(3)])); + + self.bind(stream_var.as_var().unwrap(), stream); + } &SystemClauseType::TruncateIfNoLiftedHeapGrowthDiff => { self.truncate_if_no_lifted_heap_diff(|h| Addr::HeapCell(h)) } @@ -3786,7 +3883,41 @@ impl MachineState { self.unify(listing, listing_var); } &SystemClauseType::WriteTerm => { - let addr = self[temp_v!(1)]; + let mut stream = self.get_stream_or_alias( + self[temp_v!(1)], + indices, + "write_term", + 3, + )?; + + let opt_err = + if !stream.is_output_stream() { + Some("stream") // 8.14.2.3 g) + } else if stream.options.stream_type == StreamType::Binary { + Some("binary_stream") // 8.14.2.3 h) + } else { + None + }; + + if let Some(err_string) = opt_err { + let stub = MachineError::functor_stub(clause_name!("write_term"), 3); + let h = self.heap.h(); + + let addr = self.heap.to_unifiable( + HeapCellValue::Stream(stream) + ); + + let err = MachineError::permission_error( + h + 1, + Permission::OutputStream, + err_string, + addr, + ); + + return Err(self.error_form(err, stub)); + } + + let addr = self[temp_v!(2)]; let printer = match self.write_term(&indices.op_dir)? { @@ -3801,8 +3932,21 @@ impl MachineState { let output = printer.print(addr); - print!("{}", output.result()); - stdout().flush().unwrap(); + match write!(&mut stream, "{}", output.result()) { + Ok(_) => { + } + Err(_) => { + let stub = MachineError::functor_stub(clause_name!("open"), 4); + let err = MachineError::existence_error( + self.heap.h(), + ExistenceError::Stream(self[temp_v!(1)]), + ); + + return Err(self.error_form(err, stub)); + } + } + + stream.flush().unwrap(); } &SystemClauseType::WriteTermToChars => { let addr = self[temp_v!(1)]; diff --git a/src/prolog/macros.rs b/src/prolog/macros.rs index 834ad9f8..6a3e46c1 100644 --- a/src/prolog/macros.rs +++ b/src/prolog/macros.rs @@ -405,3 +405,26 @@ macro_rules! ar_reg { ArithmeticTerm::Reg($r) }; } + +macro_rules! atom_from { + ($self:expr, $indices:expr, $e:expr) => { + match $e { + Addr::Con(h) if $self.heap.atom_at(h) => { + match &$self.heap[h] { + HeapCellValue::Atom(ref atom, _) => { + atom.clone() + } + _ => { + unreachable!() + } + } + } + Addr::Char(c) => { + clause_name!(c.to_string(), $indices.atom_tbl.clone()) + } + _ => { + unreachable!() + } + } + } +} From dd247cd5418040839386422f4ed6ddd59f2208ce Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Mon, 4 May 2020 21:17:06 -0600 Subject: [PATCH 04/18] adapt write_term family, add handling for EOF actions. --- src/prolog/lib/charsio.pl | 19 ++-- src/prolog/machine/machine_state.rs | 161 +++++++++++++++------------- src/prolog/machine/streams.rs | 77 +++++++++++-- src/prolog/machine/system_calls.rs | 4 +- src/prolog/read.rs | 5 + 5 files changed, 169 insertions(+), 97 deletions(-) diff --git a/src/prolog/lib/charsio.pl b/src/prolog/lib/charsio.pl index 69a36430..e929dc81 100644 --- a/src/prolog/lib/charsio.pl +++ b/src/prolog/lib/charsio.pl @@ -124,21 +124,14 @@ read_term_from_chars(Chars, Term) :- write_term_to_chars(_, Options, _) :- var(Options), instantiation_error(write_term_to_chars/3). write_term_to_chars(Term, Options, Chars) :- - '$skip_max_list'(_, -1, Options, Options0), - ( var(Options0) -> - instantiation_error(write_term_to_chars/3) - ; nonvar(Chars) -> + builtins:parse_write_options(Options, + [IgnoreOps, MaxDepth, NumberVars, Quoted, VNNames], + write_term_to_chars/3), + ( nonvar(Chars) -> throw(error(uninstantiation_error(Chars), write_term_to_chars/3)) - ; Options0 == [] -> - true ; - type_error(list, Options, write_term_to_chars/3) + true ), - builtins:inst_member_or(Options, ignore_ops(IgnoreOps), ignore_ops(false)), - builtins:inst_member_or(Options, numbervars(NumberVars), numbervars(false)), - builtins:inst_member_or(Options, quoted(Quoted), quoted(false)), - builtins:inst_member_or(Options, variable_names(VarNames), variable_names([])), - builtins:inst_member_or(Options, max_depth(MaxDepth), max_depth(0)), term_variables(Term, Vars), extend_var_list(Vars, VarNames, NewVarNames, numbervars), - '$write_term_to_chars'(Term, IgnoreOps, NumberVars, Quoted, NewVarNames, MaxDepth, Chars). + '$write_term_to_chars'(Chars, Term, IgnoreOps, NumberVars, Quoted, NewVarNames, MaxDepth). diff --git a/src/prolog/machine/machine_state.rs b/src/prolog/machine/machine_state.rs index b86496b3..691d1374 100644 --- a/src/prolog/machine/machine_state.rs +++ b/src/prolog/machine/machine_state.rs @@ -619,100 +619,109 @@ impl MachineState { stream: Stream, indices: &mut IndexStore, ) -> CallResult { + let mut orig_stream = stream.clone(); let mut stream = self.open_parsing_stream(stream, "read_term", 3)?; - match self.read( - &mut stream, - indices.atom_tbl.clone(), - &indices.op_dir, - ) { - Ok(term_write_result) => { - let term = self[temp_v!(2)]; - self.unify(Addr::HeapCell(term_write_result.heap_loc), term); + loop { + match self.read( + &mut stream, + indices.atom_tbl.clone(), + &indices.op_dir, + ) { + Ok(term_write_result) => { + let term = self[temp_v!(2)]; + self.unify(Addr::HeapCell(term_write_result.heap_loc), term); - if self.fail { - return Ok(()); - } + if self.fail { + return Ok(()); + } - let mut list_of_var_eqs = vec![]; + let mut list_of_var_eqs = vec![]; - for (var, binding) in term_write_result.var_dict.into_iter() { - let var_atom = clause_name!(var.to_string(), indices.atom_tbl); + for (var, binding) in term_write_result.var_dict.into_iter() { + let var_atom = clause_name!(var.to_string(), indices.atom_tbl); - let h = self.heap.h(); - let spec = fetch_atom_op_spec(clause_name!("="), None, &indices.op_dir); + let h = self.heap.h(); + let spec = fetch_atom_op_spec(clause_name!("="), None, &indices.op_dir); - self.heap.push(HeapCellValue::NamedStr(2, clause_name!("="), spec)); - self.heap.push(HeapCellValue::Atom(var_atom, None)); - self.heap.push(HeapCellValue::Addr(binding)); + self.heap.push(HeapCellValue::NamedStr(2, clause_name!("="), spec)); + self.heap.push(HeapCellValue::Atom(var_atom, None)); + self.heap.push(HeapCellValue::Addr(binding)); - list_of_var_eqs.push(Addr::Str(h)); - } + list_of_var_eqs.push(Addr::Str(h)); + } - let mut var_set: IndexMap = IndexMap::new(); + let mut var_set: IndexMap = IndexMap::new(); - for addr in self.acyclic_pre_order_iter(term) { - if let Some(var) = addr.as_var() { - if !var_set.contains_key(&var) { - var_set.insert(var, true); - } else { - var_set.insert(var, false); + for addr in self.acyclic_pre_order_iter(term) { + if let Some(var) = addr.as_var() { + if !var_set.contains_key(&var) { + var_set.insert(var, true); + } else { + var_set.insert(var, false); + } } } - } - let mut var_list = vec![]; - let mut singleton_var_list = vec![]; + let mut var_list = vec![]; + let mut singleton_var_list = vec![]; - for addr in self.acyclic_pre_order_iter(term) { - if let Some(var) = addr.as_var() { - if var_set.get(&var) == Some(&true) { - singleton_var_list.push(var.as_addr()); + for addr in self.acyclic_pre_order_iter(term) { + if let Some(var) = addr.as_var() { + if var_set.get(&var) == Some(&true) { + singleton_var_list.push(var.as_addr()); + } + + var_list.push(var.as_addr()); } - - var_list.push(var.as_addr()); } + + let singleton_addr = self[temp_v!(3)]; + let singletons_offset = + Addr::HeapCell(self.heap.to_list(singleton_var_list.into_iter())); + + self.unify(singletons_offset, singleton_addr); + + if self.fail { + return Ok(()); + } + + let vars_addr = self[temp_v!(4)]; + let vars_offset = + Addr::HeapCell(self.heap.to_list(var_list.into_iter())); + + self.unify(vars_offset, vars_addr); + + if self.fail { + return Ok(()); + } + + let var_names_addr = self[temp_v!(5)]; + let var_names_offset = + Addr::HeapCell(self.heap.to_list(list_of_var_eqs.into_iter())); + + return Ok(self.unify(var_names_offset, var_names_addr)); } + Err(err) => { + if let ParserError::UnexpectedEOF = err { + self.eof_action( + self[temp_v!(2)], + &mut orig_stream, + clause_name!("read_term"), + 3 + )?; - let singleton_addr = self[temp_v!(3)]; - let singletons_offset = - Addr::HeapCell(self.heap.to_list(singleton_var_list.into_iter())); + if orig_stream.options.eof_action == EOFAction::Reset { + if self.fail == false { + continue; + } else { + return Ok(()); + } + } + } - self.unify(singletons_offset, singleton_addr); - - if self.fail { return Ok(()); } - - let vars_addr = self[temp_v!(4)]; - let vars_offset = - Addr::HeapCell(self.heap.to_list(var_list.into_iter())); - - self.unify(vars_offset, vars_addr); - - if self.fail { - return Ok(()); - } - - let var_names_addr = self[temp_v!(5)]; - let var_names_offset = - Addr::HeapCell(self.heap.to_list(list_of_var_eqs.into_iter())); - - Ok(self.unify(var_names_offset, var_names_addr)) - } - Err(err) => { - if let ParserError::UnexpectedEOF = err { - std::process::exit(0); - } - - // reset the input stream after an input failure. - //*stream = readline::input_stream(); - - let h = self.heap.h(); - let syntax_error = MachineError::syntax_error(h, err); - let stub = MachineError::functor_stub(clause_name!("read_term"), 2); - - Err(self.error_form(syntax_error, stub)) } } } @@ -809,9 +818,11 @@ impl MachineState { var_names.insert(var, atom); } - _ => unreachable!(), + _ => { + } }, - _ => unreachable!(), + _ => { + } } } diff --git a/src/prolog/machine/streams.rs b/src/prolog/machine/streams.rs index 8cd7cbf0..1dbb7532 100644 --- a/src/prolog/machine/streams.rs +++ b/src/prolog/machine/streams.rs @@ -1,16 +1,16 @@ use crate::prolog_parser::ast::*; +use crate::prolog::read::PrologStream; use crate::prolog::read::readline::*; use crate::prolog::machine::machine_errors::*; use crate::prolog::machine::machine_indices::*; use crate::prolog::machine::machine_state::*; -use crate::prolog::read::PrologStream; use std::cell::RefCell; use std::error::Error; use std::fmt; use std::fs::File; -use std::io::{stdout, Cursor, ErrorKind, Read, Write}; +use std::io::{stdout, Cursor, ErrorKind, Read, Seek, SeekFrom, Write}; use std::hash::{Hash, Hasher}; use std::net::TcpStream; use std::rc::Rc; @@ -148,6 +148,9 @@ pub struct Stream { impl From for Stream { fn from(tcp_stream: TcpStream) -> Self { + tcp_stream.set_read_timeout(None).unwrap(); + tcp_stream.set_write_timeout(None).unwrap(); + Stream { options: StreamOptions::default(), stream_inst: WrappedStreamInstance::new( @@ -322,9 +325,70 @@ impl Stream { } } } + + // returns true on success. + #[inline] + fn reset(&mut self) -> bool { + match *self.stream_inst.0.borrow_mut() { + StreamInstance::Bytes(ref mut cursor) => { + cursor.set_position(0); + true + } + StreamInstance::InputFile(ref mut file) => { + file.seek(SeekFrom::Start(0)).unwrap(); + true + } + StreamInstance::ReadlineStream(ref mut stream) => { + *stream = ReadlineStream::new(String::new()); + true + } + _ => { + false + } + } + } } impl MachineState { + #[inline] + pub(crate) + fn eof_action( + &mut self, + result: Addr, + stream: &mut Stream, + caller: ClauseName, + arity: usize, + ) -> CallResult { + match stream.options.eof_action { + EOFAction::Error => { + let stub = MachineError::functor_stub(caller, arity); + + let stream = vec![ + HeapCellValue::Stream(stream.clone()) + ]; + + let err = MachineError::permission_error( + self.heap.h(), + Permission::InputStream, + "past_end_of_stream", + stream, + ); + + Err(self.error_form(err, stub)) + } + EOFAction::EOFCode => { + let end_of_stream = self.heap.to_unifiable( + HeapCellValue::Atom(clause_name!("end_of_stream"), None) + ); + + Ok(self.unify(result, end_of_stream)) + } + EOFAction::Reset => { + Ok(self.fail = !stream.reset()) + } + } + } + pub(crate) fn to_stream_options( &self, @@ -426,14 +490,13 @@ impl MachineState { } None => { let stub = MachineError::functor_stub(clause_name!(caller), arity); - let h = self.heap.h(); let addr = self.heap.to_unifiable( HeapCellValue::Atom(atom.clone(), spec.clone()) ); return Err(self.error_form( - MachineError::existence_error(h + 1, ExistenceError::Stream(addr)), + MachineError::existence_error(self.heap.h(), ExistenceError::Stream(addr)), stub, )); } @@ -474,9 +537,9 @@ impl MachineState { stub_name: &'static str, stub_arity: usize, ) -> Result { - match parsing_stream(stream) { - Ok(stream) => { - Ok(stream) + match parsing_stream(stream.clone()) { + Ok(parsing_stream) => { + Ok(parsing_stream) } Err(e) => { let stub = MachineError::functor_stub(clause_name!(stub_name), stub_arity); diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index a0e10b25..83d4eb48 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -3949,7 +3949,7 @@ impl MachineState { stream.flush().unwrap(); } &SystemClauseType::WriteTermToChars => { - let addr = self[temp_v!(1)]; + let addr = self[temp_v!(2)]; let printer = match self.write_term(&indices.op_dir)? { @@ -3965,7 +3965,7 @@ impl MachineState { let result = printer.print(addr).result(); let chars = self.heap.put_complete_string(&result); - let result_addr = self.store(self.deref(self[temp_v!(7)])); + let result_addr = self.store(self.deref(self[temp_v!(1)])); if let Some(var) = result_addr.as_var() { self.bind(var, chars); diff --git a/src/prolog/read.rs b/src/prolog/read.rs index 28d35358..9ed6c6cb 100644 --- a/src/prolog/read.rs +++ b/src/prolog/read.rs @@ -42,6 +42,11 @@ pub mod readline { } impl ReadlineStream { + pub fn new(pending_input: String) -> Self { + let rl = Editor::<()>::new(); + ReadlineStream { rl, pending_input: Cursor::new(pending_input) } + } + pub fn input_stream(pending_input: String) -> Stream { let mut rl = Editor::<()>::new(); rl.bind_sequence(KeyPress::Tab, Cmd::Insert(1, "\t".to_string())); From ab62603c5ac2ad6ff7c8354f96628903a26cba4e Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Tue, 5 May 2020 00:38:21 -0600 Subject: [PATCH 05/18] add close/{1,2}, better EOF action handling in read_term --- src/prolog/clause_types.rs | 3 ++ src/prolog/lib/builtins.pl | 44 ++++++++++++++++++++++-------- src/prolog/machine/streams.rs | 31 +++++++++++++++++++-- src/prolog/machine/system_calls.rs | 28 +++++++++++++++++-- 4 files changed, 90 insertions(+), 16 deletions(-) diff --git a/src/prolog/clause_types.rs b/src/prolog/clause_types.rs index ce5d9e68..e6f577e7 100644 --- a/src/prolog/clause_types.rs +++ b/src/prolog/clause_types.rs @@ -165,6 +165,7 @@ pub enum SystemClauseType { CodesToNumber, CopyTermWithoutAttrVars, CheckCutPoint, + Close, CopyToLiftedHeap, CreatePartialString, CurrentHostname, @@ -310,6 +311,7 @@ impl SystemClauseType { &SystemClauseType::REPL(REPLCodePtr::UseQualifiedModuleFromFile) => { clause_name!("$use_qualified_module_from_file") } + &SystemClauseType::Close => clause_name!("$close"), &SystemClauseType::CopyToLiftedHeap => clause_name!("$copy_to_lh"), &SystemClauseType::DeleteAttribute => clause_name!("$del_attr_non_head"), &SystemClauseType::DeleteHeadAttribute => clause_name!("$del_attr_head"), @@ -462,6 +464,7 @@ impl SystemClauseType { ("$check_cp", 1) => Some(SystemClauseType::CheckCutPoint), ("$compile_batch", 0) => Some(SystemClauseType::REPL(REPLCodePtr::CompileBatch)), ("$copy_to_lh", 2) => Some(SystemClauseType::CopyToLiftedHeap), + ("$close", 2) => Some(SystemClauseType::Close), ("$current_hostname", 1) => Some(SystemClauseType::CurrentHostname), ("$current_input", 1) => Some(SystemClauseType::CurrentInput), ("$current_output", 1) => Some(SystemClauseType::CurrentOutput), diff --git a/src/prolog/lib/builtins.pl b/src/prolog/lib/builtins.pl index 2ec5680e..f1f20f8c 100644 --- a/src/prolog/lib/builtins.pl +++ b/src/prolog/lib/builtins.pl @@ -43,18 +43,18 @@ user:term_expansion((:- op(Pred, Spec, [Op | OtherOps])), OpResults) :- (:)/7, (:)/8, (:)/9, (:)/10, (:)/11, (:)/12, abolish/1, asserta/1, assertz/1, atom_chars/2, atom_codes/2, atom_concat/3, atom_length/2, - bagof/3, catch/3, char_code/2, clause/2, - current_input/1, current_output/1, current_op/3, - current_predicate/1, current_prolog_flag/2, - expand_goal/2, expand_term/2, fail/0, false/0, - findall/3, findall/4, 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, - unify_with_occurs_check/2, write/1, + bagof/3, catch/3, char_code/2, clause/2, close/1, + close/2, current_input/1, current_output/1, + current_op/3, current_predicate/1, + current_prolog_flag/2, expand_goal/2, + expand_term/2, fail/0, false/0, findall/3, + findall/4, 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, unify_with_occurs_check/2, write/1, write_canonical/1, write_term/2, write_term/3, writeq/1]). @@ -1161,3 +1161,23 @@ open(SourceSink, Mode, Stream, StreamOptions) :- '$open'(SourceSink, Mode, Stream, Alias, EOFAction, Reposition, Type) ). + +parse_close_options(Options, OptionValues, Stub) :- + DefaultOptions = [force-false], + parse_options_list(Options, parse_close_options_, DefaultOptions, OptionValues, Stub). + +parse_close_options_(force(Force), force-Force) :- + ( nonvar(Force), lists:member(Force, [true, false]), ! + ; + throw(error(domain_error(close_option, force(Force)), _)) + ). +parse_close_options_(E, _) :- + throw(error(domain_error(close_option, E), _)). + + +close(Stream, CloseOptions) :- + parse_close_options(CloseOptions, [Force], close/2), + '$close'(Stream, CloseOptions). + +close(Stream) :- + close(Stream, []). diff --git a/src/prolog/machine/streams.rs b/src/prolog/machine/streams.rs index 1dbb7532..e4df4734 100644 --- a/src/prolog/machine/streams.rs +++ b/src/prolog/machine/streams.rs @@ -12,7 +12,7 @@ use std::fmt; use std::fs::File; use std::io::{stdout, Cursor, ErrorKind, Read, Seek, SeekFrom, Write}; use std::hash::{Hash, Hasher}; -use std::net::TcpStream; +use std::net::{Shutdown, TcpStream}; use std::rc::Rc; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -42,6 +42,18 @@ pub enum StreamInstance { TcpStream(TcpStream), } +impl Drop for StreamInstance { + fn drop(&mut self) { + match self { + StreamInstance::TcpStream(ref mut tcp_stream) => { + tcp_stream.shutdown(Shutdown::Both).unwrap(); + } + _ => { + } + } + } +} + impl fmt::Debug for StreamInstance { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match self { @@ -140,12 +152,21 @@ impl Default for StreamOptions { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Hash)] pub struct Stream { pub options: StreamOptions, stream_inst: WrappedStreamInstance, } +impl PartialEq for Stream { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.stream_inst == other.stream_inst + } +} + +impl Eq for Stream {} + impl From for Stream { fn from(tcp_stream: TcpStream) -> Self { tcp_stream.set_read_timeout(None).unwrap(); @@ -292,6 +313,12 @@ impl Stream { } } + #[inline] + pub(crate) + fn close(&mut self) { + *self.stream_inst.0.borrow_mut() = StreamInstance::Null; + } + #[inline] pub(crate) fn is_input_stream(&self) -> bool { diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index 83d4eb48..7a73a155 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -1743,6 +1743,26 @@ impl MachineState { } }; } + &SystemClauseType::Close => { + let mut stream = + self.get_stream_or_alias(self[temp_v!(1)], indices, "close", 2)?; + + if stream.is_output_stream() { + stream.flush().unwrap(); // 8.11.6.1b) + } + + if stream == *current_input_stream { + *current_input_stream = readline::input_stream(); + } else if stream == *current_output_stream { + *current_output_stream = Stream::stdout(); + } + + stream.close(); + + if let Some(alias) = stream.options.alias { + indices.stream_aliases.remove(&alias); + } + } &SystemClauseType::CopyToLiftedHeap => { match self.store(self.deref(self[temp_v!(1)])) { Addr::Usize(lh_offset) => { @@ -2298,6 +2318,10 @@ impl MachineState { stream.options = options; + if let Some(ref alias) = &stream.options.alias { + indices.stream_aliases.insert(alias.clone(), stream.clone()); + } + let stream = self.heap.to_unifiable(HeapCellValue::Stream(stream)); let stream_var = self.store(self.deref(self[temp_v!(3)])); @@ -3464,12 +3488,12 @@ impl MachineState { match TcpStream::connect(socket_addr).map_err(|e| e.kind()) { Ok(tcp_stream) => { let mut stream = Stream::from(tcp_stream); + stream.options = options; - if let Some(ref alias) = &options.alias { + if let Some(ref alias) = &stream.options.alias { indices.stream_aliases.insert(alias.clone(), stream.clone()); } - stream.options = options; self.heap.to_unifiable(HeapCellValue::Stream(stream)) } Err(ErrorKind::PermissionDenied) => { From 53ae8b9bd1cd1de4f6a76f352326aad6b930aca0 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Tue, 5 May 2020 14:10:49 -0600 Subject: [PATCH 06/18] add flush_output/{0,1}, past_end_of_stream --- src/prolog/clause_types.rs | 3 + src/prolog/lib/builtins.pl | 25 ++++--- src/prolog/machine/copier.rs | 30 ++------- src/prolog/machine/heap.rs | 8 +-- src/prolog/machine/machine_indices.rs | 4 +- src/prolog/machine/machine_state.rs | 26 ++++++++ src/prolog/machine/streams.rs | 95 +++++++-------------------- src/prolog/machine/system_calls.rs | 35 ++++++++-- 8 files changed, 110 insertions(+), 116 deletions(-) diff --git a/src/prolog/clause_types.rs b/src/prolog/clause_types.rs index e6f577e7..bb39a35e 100644 --- a/src/prolog/clause_types.rs +++ b/src/prolog/clause_types.rs @@ -181,6 +181,7 @@ pub enum SystemClauseType { FetchGlobalVar, FetchGlobalVarWithOffset, FileToChars, + FlushOutput, GetChar, GetSingleChar, ResetAttrVarState, @@ -325,6 +326,7 @@ impl SystemClauseType { clause_name!("$fetch_global_var_with_offset") } &SystemClauseType::FileToChars => clause_name!("$file_to_chars"), + &SystemClauseType::FlushOutput => clause_name!("$flush_output"), &SystemClauseType::GetChar => clause_name!("$get_char"), &SystemClauseType::GetSingleChar => clause_name!("$get_single_char"), &SystemClauseType::ResetAttrVarState => clause_name!("$reset_attr_var_state"), @@ -468,6 +470,7 @@ impl SystemClauseType { ("$current_hostname", 1) => Some(SystemClauseType::CurrentHostname), ("$current_input", 1) => Some(SystemClauseType::CurrentInput), ("$current_output", 1) => Some(SystemClauseType::CurrentOutput), + ("$flush_output", 1) => Some(SystemClauseType::FlushOutput), ("$del_attr_non_head", 1) => Some(SystemClauseType::DeleteAttribute), ("$del_attr_head", 1) => Some(SystemClauseType::DeleteHeadAttribute), ("$get_next_db_ref", 2) => Some(SystemClauseType::GetNextDBRef), diff --git a/src/prolog/lib/builtins.pl b/src/prolog/lib/builtins.pl index f1f20f8c..31cfa75d 100644 --- a/src/prolog/lib/builtins.pl +++ b/src/prolog/lib/builtins.pl @@ -48,13 +48,14 @@ user:term_expansion((:- op(Pred, Spec, [Op | OtherOps])), OpResults) :- current_op/3, current_predicate/1, current_prolog_flag/2, expand_goal/2, expand_term/2, fail/0, false/0, findall/3, - findall/4, 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, unify_with_occurs_check/2, write/1, + 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, + 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, + unify_with_occurs_check/2, write/1, write_canonical/1, write_term/2, write_term/3, writeq/1]). @@ -1180,4 +1181,12 @@ close(Stream, CloseOptions) :- '$close'(Stream, CloseOptions). close(Stream) :- - close(Stream, []). + '$close'(Stream, []). + + +flush_output(S) :- + '$flush_output'(S). + +flush_output :- + current_output(S), + '$flush_output'(S). diff --git a/src/prolog/machine/copier.rs b/src/prolog/machine/copier.rs index 08c41944..dd861731 100644 --- a/src/prolog/machine/copier.rs +++ b/src/prolog/machine/copier.rs @@ -1,6 +1,5 @@ use crate::prolog::machine::machine_indices::*; use crate::prolog::machine::stack::*; -use crate::prolog::machine::streams::*; use std::mem; use std::ops::IndexMut; @@ -215,24 +214,6 @@ impl CopyTermState { } } - fn copy_stream(&mut self, addr: usize) { - let threshold = self.target.threshold(); - - let trail_item = mem::replace( - &mut self.target[addr], - HeapCellValue::Addr(Addr::Stream(threshold)), - ); - - self.trail.push(( - Ref::HeapCell(addr), - trail_item, - )); - - self.target.push(HeapCellValue::Stream(Stream::null_stream())); - - self.scan += 1; - } - fn copy_structure(&mut self, addr: usize) { match self.target[addr].context_free_clone() { HeapCellValue::NamedStr(arity, name, fixity) => { @@ -285,11 +266,12 @@ impl CopyTermState { *self.value_at_scan() = HeapCellValue::Addr(addr); } } - Addr::Lis(h) if h >= self.old_h => { - self.scan += 1; - } Addr::Lis(h) => { - self.copy_list(h); + if h >= self.old_h { + self.scan += 1; + } else { + self.copy_list(h); + } } addr @ Addr::AttrVar(_) | addr @ Addr::HeapCell(_) | @@ -303,7 +285,7 @@ impl CopyTermState { self.copy_partial_string(addr, n); } Addr::Stream(h) => { - self.copy_stream(h); + *self.value_at_scan() = self.target[h].context_free_clone(); } _ => { self.scan += 1; diff --git a/src/prolog/machine/heap.rs b/src/prolog/machine/heap.rs index 3e813fe8..718cbfe3 100644 --- a/src/prolog/machine/heap.rs +++ b/src/prolog/machine/heap.rs @@ -288,10 +288,10 @@ impl HeapTemplate { HeapCellValue::Addr(addr) => { addr } - val @ HeapCellValue::Atom(..) - | val @ HeapCellValue::Integer(_) - | val @ HeapCellValue::DBRef(_) - | val @ HeapCellValue::Rational(_) => { + val @ HeapCellValue::Atom(..) | + val @ HeapCellValue::Integer(_) | + val @ HeapCellValue::DBRef(_) | + val @ HeapCellValue::Rational(_) => { Addr::Con(self.push(val)) } val @ HeapCellValue::NamedStr(..) => { diff --git a/src/prolog/machine/machine_indices.rs b/src/prolog/machine/machine_indices.rs index cb43359c..58ee66be 100644 --- a/src/prolog/machine/machine_indices.rs +++ b/src/prolog/machine/machine_indices.rs @@ -443,8 +443,8 @@ impl HeapCellValue { &HeapCellValue::PartialString(ref pstr, has_tail) => { HeapCellValue::PartialString(pstr.clone(), has_tail) } - &HeapCellValue::Stream(_) => { - HeapCellValue::Stream(Stream::null_stream()) + &HeapCellValue::Stream(ref stream) => { + HeapCellValue::Stream(stream.clone()) } &HeapCellValue::TcpListener(_) => { HeapCellValue::Atom(clause_name!("$socket_server"), None) diff --git a/src/prolog/machine/machine_state.rs b/src/prolog/machine/machine_state.rs index 691d1374..e58a04ec 100644 --- a/src/prolog/machine/machine_state.rs +++ b/src/prolog/machine/machine_state.rs @@ -619,6 +619,32 @@ impl MachineState { stream: Stream, indices: &mut IndexStore, ) -> CallResult { + let opt_err = + if !stream.is_input_stream() { + Some("stream") // 8.14.2.3 g) + } else if stream.options.stream_type == StreamType::Binary { + Some("binary_stream") // 8.14.2.3 h) + } else { + None + }; + + 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(), + Permission::InputStream, + err_string, + addr, + ); + + return Err(self.error_form(err, stub)); + } + let mut orig_stream = stream.clone(); let mut stream = self.open_parsing_stream(stream, "read_term", 3)?; diff --git a/src/prolog/machine/streams.rs b/src/prolog/machine/streams.rs index e4df4734..3f09d6d1 100644 --- a/src/prolog/machine/streams.rs +++ b/src/prolog/machine/streams.rs @@ -37,7 +37,6 @@ pub enum StreamInstance { OutputFile(File), Null, ReadlineStream(ReadlineStream), - // Stdin, Stdout, TcpStream(TcpStream), } @@ -154,6 +153,7 @@ impl Default for StreamOptions { #[derive(Debug, Clone, Hash)] pub struct Stream { + past_end_of_stream: bool, pub options: StreamOptions, stream_inst: WrappedStreamInstance, } @@ -172,45 +172,25 @@ impl From for Stream { tcp_stream.set_read_timeout(None).unwrap(); tcp_stream.set_write_timeout(None).unwrap(); - Stream { - options: StreamOptions::default(), - stream_inst: WrappedStreamInstance::new( - StreamInstance::TcpStream(tcp_stream) - ) - } + Stream::from_inst(StreamInstance::TcpStream(tcp_stream)) } } impl From for Stream { fn from(string: String) -> Self { - Stream { - options: StreamOptions::default(), - stream_inst: WrappedStreamInstance::new( - StreamInstance::Bytes(Cursor::new(string.into_bytes())) - ) - } + Stream::from_inst(StreamInstance::Bytes(Cursor::new(string.into_bytes()))) } } impl From for Stream { fn from(rl_stream: ReadlineStream) -> Self { - Stream { - options: StreamOptions::default(), - stream_inst: WrappedStreamInstance::new( - StreamInstance::ReadlineStream(rl_stream) - ), - } + Stream::from_inst(StreamInstance::ReadlineStream(rl_stream)) } } impl From<&'static str> for Stream { fn from(src: &'static str) -> Stream { - Stream { - options: StreamOptions::default(), - stream_inst: WrappedStreamInstance::new( - StreamInstance::DynReadSource(Box::new(src.as_bytes())) - ), - } + Stream::from_inst(StreamInstance::DynReadSource(Box::new(src.as_bytes()))) } } @@ -229,61 +209,31 @@ impl Stream { ptr as *const u8 } + #[inline] + fn from_inst(stream_inst: StreamInstance) -> Self { + Stream { + past_end_of_stream: false, + options: StreamOptions::default(), + stream_inst: WrappedStreamInstance::new(stream_inst) + } + } + #[inline] pub(crate) fn stdout() -> Self { - Stream { - options: StreamOptions::default(), - stream_inst: WrappedStreamInstance::new( - StreamInstance::Stdout - ), - } + Stream::from_inst(StreamInstance::Stdout) } #[inline] pub(crate) fn from_file_as_output(file: File) -> Self { - Stream { - options: StreamOptions::default(), - stream_inst: WrappedStreamInstance::new( - StreamInstance::OutputFile(file) - ), - } + Stream::from_inst(StreamInstance::OutputFile(file)) } #[inline] pub(crate) fn from_file_as_input(file: File) -> Self { - Stream { - options: StreamOptions::default(), - stream_inst: WrappedStreamInstance::new( - StreamInstance::InputFile(file) - ), - } - } - -/* - #[inline] - pub(crate) - fn stdin() -> Self { - Stream { - options: StreamOptions::default(), - stream_inst: WrappedStreamInstance::new( - StreamInstance::Stdin - ), - } - } -*/ - - #[inline] - pub(crate) - fn null_stream() -> Self { - Stream { - options: StreamOptions::default(), // TODO: null_options? - stream_inst: WrappedStreamInstance::new( - StreamInstance::Null - ), - } + Stream::from_inst(StreamInstance::InputFile(file)) } #[inline] @@ -388,9 +338,10 @@ impl MachineState { ) -> CallResult { match stream.options.eof_action { EOFAction::Error => { - let stub = MachineError::functor_stub(caller, arity); + stream.past_end_of_stream = true; - let stream = vec![ + let stub = MachineError::functor_stub(caller, arity); + let payload = vec![ HeapCellValue::Stream(stream.clone()) ]; @@ -398,7 +349,7 @@ impl MachineState { self.heap.h(), Permission::InputStream, "past_end_of_stream", - stream, + payload, ); Err(self.error_form(err, stub)) @@ -408,10 +359,12 @@ impl MachineState { HeapCellValue::Atom(clause_name!("end_of_stream"), None) ); + stream.past_end_of_stream = true; Ok(self.unify(result, end_of_stream)) } EOFAction::Reset => { - Ok(self.fail = !stream.reset()) + stream.past_end_of_stream = !stream.reset(); + Ok(self.fail = stream.past_end_of_stream) } } } diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index 7a73a155..14df438c 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -1603,8 +1603,8 @@ impl MachineState { let a1 = self[temp_v!(1)]; match result { - Some(Ok(b)) => { - self.unify(Addr::Char(b as char), a1); + Some(Ok(c)) => { + self.unify(Addr::Char(c), a1); } Some(Err(_)) => { let end_of_file = self.heap.to_unifiable(HeapCellValue::Atom( @@ -1623,6 +1623,29 @@ impl MachineState { } } } + &SystemClauseType::FlushOutput => { + let mut stream = + self.get_stream_or_alias(self[temp_v!(1)], indices, "flush_output", 1)?; + + if stream.is_input_stream() { + let stub = MachineError::functor_stub(clause_name!("flush_output"), 1); + + let addr = vec![ + HeapCellValue::Stream(stream) + ]; + + let err = MachineError::permission_error( + self.heap.h(), + Permission::OutputStream, + "stream", + addr, + ); + + return Err(self.error_form(err, stub)); + } + + stream.flush().unwrap(); + } &SystemClauseType::GetSingleChar => { let c = get_single_char(); @@ -3925,14 +3948,12 @@ impl MachineState { if let Some(err_string) = opt_err { let stub = MachineError::functor_stub(clause_name!("write_term"), 3); - let h = self.heap.h(); - - let addr = self.heap.to_unifiable( + let addr = vec![ HeapCellValue::Stream(stream) - ); + ]; let err = MachineError::permission_error( - h + 1, + self.heap.h(), Permission::OutputStream, err_string, addr, From 9a63b1751b574060d98e85b3cbf3ca52a49a2c1d Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Tue, 5 May 2020 14:15:16 -0600 Subject: [PATCH 07/18] change EOFCode eof_action from end_of_stream to end_of_file, add echo_server.pl example --- src/prolog/examples/echo_server.pl | 31 ++++++++++++++++++++++++++++++ src/prolog/machine/streams.rs | 2 +- 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 src/prolog/examples/echo_server.pl diff --git a/src/prolog/examples/echo_server.pl b/src/prolog/examples/echo_server.pl new file mode 100644 index 00000000..4543e60d --- /dev/null +++ b/src/prolog/examples/echo_server.pl @@ -0,0 +1,31 @@ +:- module(echo_server, [echo_server/0, + echo_server/1]). + +:- use_module(library(format)). +:- use_module(library(sockets)). + + +echo_server :- + echo_server('127.0.0.1'). + + +echo_server(Addr) :- + socket_server_open(Addr:Port, ServerSocket), + format("echo_server: connection opened at ~w:~d~n", [Addr, Port]), + socket_server_accept(ServerSocket, Client, Stream, [eof_action(eof_code)]), + format("echo_server: connection accepted from ~a~n", [Client]), + !, + echo_loop(Stream), + socket_server_close(ServerSocket). + + +echo_loop(Stream) :- + read_term(Stream, Term, []), + ( Term == end_of_file -> + true + ; + format("received: ~w~n", [Term]), + !, + echo_loop(Stream) + ). + diff --git a/src/prolog/machine/streams.rs b/src/prolog/machine/streams.rs index 3f09d6d1..0c2d9aac 100644 --- a/src/prolog/machine/streams.rs +++ b/src/prolog/machine/streams.rs @@ -356,7 +356,7 @@ impl MachineState { } EOFAction::EOFCode => { let end_of_stream = self.heap.to_unifiable( - HeapCellValue::Atom(clause_name!("end_of_stream"), None) + HeapCellValue::Atom(clause_name!("end_of_file"), None) ); stream.past_end_of_stream = true; From bfced59949abba8a14c0ee5bb57ec32d65090a56 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Tue, 5 May 2020 16:09:17 -0600 Subject: [PATCH 08/18] 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)]; From 573df892bca30cc6f33ed9781d289994d45266c8 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Tue, 5 May 2020 16:54:40 -0600 Subject: [PATCH 09/18] cleanup on reading predicates, add get_char/{1,2} --- src/prolog/clause_types.rs | 2 +- src/prolog/lib/builtins.pl | 18 ++-- src/prolog/machine/machine_errors.rs | 8 +- src/prolog/machine/machine_state.rs | 31 +++--- src/prolog/machine/streams.rs | 59 ++++++++++ src/prolog/machine/system_calls.rs | 156 +++++++++++++++++++-------- 6 files changed, 196 insertions(+), 78 deletions(-) diff --git a/src/prolog/clause_types.rs b/src/prolog/clause_types.rs index 99282013..614df359 100644 --- a/src/prolog/clause_types.rs +++ b/src/prolog/clause_types.rs @@ -490,7 +490,7 @@ impl SystemClauseType { ("$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_char", 2) => Some(SystemClauseType::GetChar), ("$get_single_char", 1) => Some(SystemClauseType::GetSingleChar), ("$points_to_cont_reset_marker", 1) => { Some(SystemClauseType::PointsToContinuationResetMarker) diff --git a/src/prolog/lib/builtins.pl b/src/prolog/lib/builtins.pl index ca6d738e..22c7619f 100644 --- a/src/prolog/lib/builtins.pl +++ b/src/prolog/lib/builtins.pl @@ -49,10 +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_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, + get_byte/1, get_byte/2, get_char/1, get_char/2, + 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, @@ -1000,11 +1000,11 @@ char_code(Char, Code) :- ). get_char(C) :- - ( var(C) -> '$get_char'(C) - ; C == end_of_file -> '$get_char'(C) - ; atom_length(C, 1) -> '$get_char'(C) - ; throw(error(type_error(in_character, C), get_char/1)) - ). + current_input(S), + '$get_char'(S, C). + +get_char(S, C) :- + '$get_char'(S, C). can_be_number(N, PI) :- ( var(N) -> true diff --git a/src/prolog/machine/machine_errors.rs b/src/prolog/machine/machine_errors.rs index fdbd1d85..5afae443 100644 --- a/src/prolog/machine/machine_errors.rs +++ b/src/prolog/machine/machine_errors.rs @@ -496,7 +496,7 @@ pub enum ValidType { Evaluable, Float, InByte, - // InCharacter, + InCharacter, Integer, List, // Number, @@ -519,7 +519,7 @@ impl ValidType { ValidType::Evaluable => "evaluable", ValidType::Float => "float", ValidType::InByte => "in_byte", - // ValidType::InCharacter => "in_character", + ValidType::InCharacter => "in_character", ValidType::Integer => "integer", ValidType::List => "list", // ValidType::Number => "number", @@ -555,7 +555,7 @@ impl DomainErrorType { // from 7.12.2 f) of 13211-1:1995 #[derive(Debug, Clone, Copy)] pub enum RepFlag { - Character, + // Character, CharacterCode, // InCharacterCode, MaxArity, @@ -566,7 +566,7 @@ pub enum RepFlag { impl RepFlag { pub fn as_str(self) -> &'static str { match self { - RepFlag::Character => "character", + // RepFlag::Character => "character", RepFlag::CharacterCode => "character_code", // RepFlag::InCharacterCode => "in_character_code", RepFlag::MaxArity => "max_arity", diff --git a/src/prolog/machine/machine_state.rs b/src/prolog/machine/machine_state.rs index 9a7a1aa0..b16389d1 100644 --- a/src/prolog/machine/machine_state.rs +++ b/src/prolog/machine/machine_state.rs @@ -616,26 +616,23 @@ impl MachineState { pub(crate) fn read_term( &mut self, - stream: Stream, + mut stream: Stream, indices: &mut IndexStore, ) -> CallResult { - let opt_err = - if !stream.is_input_stream() { - Some("stream") // 8.14.2.3 g) - } else if stream.options.stream_type == StreamType::Binary { - Some("binary_stream") // 8.14.2.3 h) - } else { - None - }; + self.check_stream_properties( + &mut stream, + StreamType::Text, + Some(self[temp_v!(2)]), + clause_name!("read_term"), + 3, + )?; - if let Some(err_string) = opt_err { - return Err(self.stream_permission_error( - Permission::InputStream, - err_string, - stream, - clause_name!("read_term"), - 3, - )); + 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 mut orig_stream = stream.clone(); diff --git a/src/prolog/machine/streams.rs b/src/prolog/machine/streams.rs index 5be540cb..7e11c0df 100644 --- a/src/prolog/machine/streams.rs +++ b/src/prolog/machine/streams.rs @@ -21,6 +21,17 @@ pub enum StreamType { Text, } +impl StreamType { + #[inline] + pub(crate) + fn as_str(&self) -> &'static str { + match self { + StreamType::Binary => "binary_stream", + StreamType::Text => "text_stream", + } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum EOFAction { EOFCode, @@ -615,6 +626,54 @@ impl MachineState { return self.error_form(err, stub); } + + + pub(crate) + fn check_stream_properties( + &mut self, + stream: &mut Stream, + expected_type: StreamType, + input: Option, + caller: ClauseName, + arity: usize, + ) -> CallResult { + 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() { + Some("stream") // 8.14.2.3 g) + } else if stream.options.stream_type != expected_type { + Some(expected_type.as_str()) // 8.14.2.3 h) + } else { + None + }; + + let permission = + if input.is_some() { Permission::InputStream } else { Permission::OutputStream }; + + if let Some(err_string) = opt_err { + return Err(self.stream_permission_error( + permission, + err_string, + stream.clone(), + caller, + arity, + )); + } + + if let Some(input) = input { + if stream.past_end_of_stream { + self.eof_action( + input, + stream, + caller, + arity, + )?; + } + } + + Ok(()) + } } impl Read for Stream { diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index 0837a0ad..43492247 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -1596,33 +1596,15 @@ impl MachineState { 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, - )); - } + self.check_stream_properties( + &mut stream, + StreamType::Binary, + Some(self[temp_v!(2)]), + 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 { @@ -1691,33 +1673,105 @@ impl MachineState { } } &SystemClauseType::GetChar => { - let mut iter = self.open_parsing_stream( - current_input_stream.clone(), - "get_char", - 1, + let mut stream = + self.get_stream_or_alias(self[temp_v!(1)], indices, "get_char", 2)?; + + self.check_stream_properties( + &mut stream, + StreamType::Text, + Some(self[temp_v!(2)]), + clause_name!("get_char"), + 2, )?; - let result = iter.next(); - let a1 = self[temp_v!(1)]; - - match result { - Some(Ok(c)) => { - self.unify(Addr::Char(c), a1); + 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(()); } - Some(Err(_)) => { - let end_of_file = self.heap.to_unifiable(HeapCellValue::Atom( - clause_name!("end_of_file"), - None, - )); + } - self.unify(a1, end_of_file); - } - None => { - let stub = MachineError::functor_stub(clause_name!("get_char"), 1); - let err = MachineError::representation_error(RepFlag::Character); - let err = self.error_form(err, stub); + let mut iter = self.open_parsing_stream( + stream.clone(), + "get_char", + 2, + )?; - return Err(err); + loop { + let result = iter.next(); + + match result { + Some(Ok(c)) => { + 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::Char(c)); + return return_from_clause!(self.last_call, self); + } else { + unreachable!() + } + } + Addr::Con(h) if self.heap.atom_at(h) => { + match &self.heap[h] { + HeapCellValue::Atom(ref atom, _) if atom.is_char() => { + if let Some(d) = atom.as_str().chars().next() { + if c == d { + return return_from_clause!(self.last_call, self); + } else { + self.fail = true; + return Ok(()); + } + } else { + unreachable!() + } + } + _ => { + unreachable!() + } + } + } + Addr::Char(d) => { + if c == d { + return return_from_clause!(self.last_call, self); + } else { + self.fail = true; + return Ok(()); + } + } + culprit => { + let stub = MachineError::functor_stub(clause_name!("get_char"), 2); + let err = MachineError::type_error( + self.heap.h(), + ValidType::InCharacter, + culprit, + ); + + return Err(self.error_form(err, stub)); + } + } + } + _ => { + self.eof_action( + self[temp_v!(2)], + &mut stream, + clause_name!("get_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!("get_char"), 2); + let err = MachineError::representation_error(RepFlag::Character); + let err = self.error_form(err, stub); + + return Err(err); + }*/ } } } @@ -4035,6 +4089,14 @@ impl MachineState { 3, )?; + self.check_stream_properties( + &mut stream, + StreamType::Text, + None, // input + clause_name!("write_term"), + 3, + )?; + let opt_err = if !stream.is_output_stream() { Some("stream") // 8.14.2.3 g) From 47e3a5e75a7eda482fc0c68dad4c739620bbd751 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Tue, 5 May 2020 17:42:18 -0600 Subject: [PATCH 10/18] add put_byte/{1,2}, put_char/{1,2} --- src/prolog/clause_types.rs | 14 ++++ src/prolog/lib/builtins.pl | 17 +++++ src/prolog/machine/machine_errors.rs | 4 +- src/prolog/machine/streams.rs | 21 +++++- src/prolog/machine/system_calls.rs | 105 ++++++++++++++++++++++++++- 5 files changed, 157 insertions(+), 4 deletions(-) diff --git a/src/prolog/clause_types.rs b/src/prolog/clause_types.rs index 614df359..bf0d75a9 100644 --- a/src/prolog/clause_types.rs +++ b/src/prolog/clause_types.rs @@ -222,6 +222,8 @@ pub enum SystemClauseType { Open, PartialStringTail, PointsToContinuationResetMarker, + PutByte, + PutChar, REPL(REPLCodePtr), ReadQueryTerm, ReadTerm, @@ -385,6 +387,12 @@ impl SystemClauseType { &SystemClauseType::PointsToContinuationResetMarker => { clause_name!("$points_to_cont_reset_marker") } + &SystemClauseType::PutByte => { + clause_name!("$put_byte") + } + &SystemClauseType::PutChar => { + clause_name!("$put_char") + } &SystemClauseType::QuotedToken => { clause_name!("$quoted_token") } @@ -495,6 +503,12 @@ impl SystemClauseType { ("$points_to_cont_reset_marker", 1) => { Some(SystemClauseType::PointsToContinuationResetMarker) } + ("$put_byte", 2) => { + Some(SystemClauseType::PutByte) + } + ("$put_char", 2) => { + Some(SystemClauseType::PutChar) + } ("$reset_attr_var_state", 0) => Some(SystemClauseType::ResetAttrVarState), ("$truncate_if_no_lh_growth", 1) => { Some(SystemClauseType::TruncateIfNoLiftedHeapGrowth) diff --git a/src/prolog/lib/builtins.pl b/src/prolog/lib/builtins.pl index 22c7619f..9848b1e6 100644 --- a/src/prolog/lib/builtins.pl +++ b/src/prolog/lib/builtins.pl @@ -52,6 +52,7 @@ user:term_expansion((:- op(Pred, Spec, [Op | OtherOps])), OpResults) :- get_byte/1, get_byte/2, get_char/1, get_char/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_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, @@ -1199,3 +1200,19 @@ get_byte(S, B) :- get_byte(S) :- current_input(S), '$get_byte'(S, B). + + +put_char(C) :- + current_output(S), + '$put_char'(S, C). + +put_char(S, C) :- + '$put_char'(S, C). + + +put_byte(C) :- + current_output(S), + '$put_byte'(S, C). + +put_byte(S, C) :- + '$put_byte'(S, C). diff --git a/src/prolog/machine/machine_errors.rs b/src/prolog/machine/machine_errors.rs index 5afae443..cb448199 100644 --- a/src/prolog/machine/machine_errors.rs +++ b/src/prolog/machine/machine_errors.rs @@ -489,7 +489,7 @@ pub enum ValidType { Atom, Atomic, // Boolean, - // Byte, + Byte, Callable, Character, Compound, @@ -512,7 +512,7 @@ impl ValidType { ValidType::Atom => "atom", ValidType::Atomic => "atomic", // ValidType::Boolean => "boolean", - // ValidType::Byte => "byte", + ValidType::Byte => "byte", ValidType::Callable => "callable", ValidType::Character => "character", ValidType::Compound => "compound", diff --git a/src/prolog/machine/streams.rs b/src/prolog/machine/streams.rs index 7e11c0df..2ac482e7 100644 --- a/src/prolog/machine/streams.rs +++ b/src/prolog/machine/streams.rs @@ -278,6 +278,17 @@ impl Stream { pub(crate) fn close(&mut self) { *self.stream_inst.0.borrow_mut() = StreamInstance::Null; + self.past_end_of_stream = true; + } + + #[inline] + pub(crate) + fn is_null_stream(&self) -> bool { + if let StreamInstance::Null = *self.stream_inst.0.borrow() { + true + } else { + false + } } #[inline] @@ -485,7 +496,15 @@ impl MachineState { } Addr::Stream(h) => { if let HeapCellValue::Stream(ref stream) = &self.heap[h] { - stream.clone() + if stream.is_null_stream() { + return Err(self.open_permission_error( + Addr::Stream(h), + caller, + arity, + )); + } else { + stream.clone() + } } else { unreachable!() } diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index 43492247..7c6c6c88 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -1592,6 +1592,109 @@ impl MachineState { self.unify(complete_string, a2); } + &SystemClauseType::PutChar => { + let mut stream = + self.get_stream_or_alias(self[temp_v!(1)], indices, "put_char", 2)?; + + self.check_stream_properties( + &mut stream, + StreamType::Text, + None, + clause_name!("put_char"), + 2, + )?; + + match self.store(self.deref(self[temp_v!(2)])) { + addr if addr.is_ref() => { + let stub = MachineError::functor_stub(clause_name!("put_char"), 2); + let err = MachineError::instantiation_error(); + + return Err(self.error_form(err, stub)); + } + addr => { + match self.store(self.deref(self[temp_v!(2)])) { + 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() { + write!(&mut stream, "{}", c).unwrap(); + return return_from_clause!(self.last_call, self); + } else { + unreachable!() + } + } + _ => { + unreachable!() + } + } + } + Addr::Char(c) => { + write!(&mut stream, "{}", c).unwrap(); + return return_from_clause!(self.last_call, self); + } + _ => { + } + } + + let stub = MachineError::functor_stub(clause_name!("put_char"), 2); + let err = MachineError::type_error( + self.heap.h(), + ValidType::Character, + addr, + ); + + return Err(self.error_form(err, stub)); + } + } + } + &SystemClauseType::PutByte => { + let mut stream = + self.get_stream_or_alias(self[temp_v!(1)], indices, "put_byte", 2)?; + + self.check_stream_properties( + &mut stream, + StreamType::Binary, + None, + clause_name!("put_byte"), + 2, + )?; + + match self.store(self.deref(self[temp_v!(2)])) { + addr if addr.is_ref() => { + let stub = MachineError::functor_stub(clause_name!("put_byte"), 2); + let err = MachineError::instantiation_error(); + + return Err(self.error_form(err, stub)); + } + addr => { + match Number::try_from((addr, &self.heap)) { + Ok(Number::Integer(n)) => { + if let Some(nb) = n.to_u8() { + stream.write(&mut [nb]).unwrap(); + return return_from_clause!(self.last_call, self); + } + } + Ok(Number::Fixnum(n)) => { + if let Ok(nb) = u8::try_from(n) { + stream.write(&mut [nb]).unwrap(); + return return_from_clause!(self.last_call, self); + } + } + _ => { + } + } + + let stub = MachineError::functor_stub(clause_name!("put_byte"), 2); + let err = MachineError::type_error( + self.heap.h(), + ValidType::Byte, + self[temp_v!(2)], + ); + + return Err(self.error_form(err, stub)); + } + } + } &SystemClauseType::GetByte => { let mut stream = self.get_stream_or_alias(self[temp_v!(1)], indices, "get_byte", 2)?; @@ -2436,7 +2539,7 @@ impl MachineState { true } "write" => { - open_options.read(false).write(true).create(true).append(false); + open_options.read(false).write(true).truncate(true).create(true); false } "append" => { From 414acda9e0f6bf052450d0e191c34a6afc33f5df Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Tue, 5 May 2020 22:42:35 -0600 Subject: [PATCH 11/18] add put_code/{1,2}, get_code/{1,2}, improve get_* predicates --- src/prolog/clause_types.rs | 10 + src/prolog/lib/builtins.pl | 25 +- src/prolog/machine/machine_errors.rs | 41 +++- src/prolog/machine/streams.rs | 11 +- src/prolog/machine/system_calls.rs | 341 ++++++++++++++++++++------- 5 files changed, 340 insertions(+), 88 deletions(-) diff --git a/src/prolog/clause_types.rs b/src/prolog/clause_types.rs index bf0d75a9..d80a01d0 100644 --- a/src/prolog/clause_types.rs +++ b/src/prolog/clause_types.rs @@ -184,6 +184,7 @@ pub enum SystemClauseType { FlushOutput, GetByte, GetChar, + GetCode, GetSingleChar, ResetAttrVarState, TruncateIfNoLiftedHeapGrowthDiff, @@ -224,6 +225,7 @@ pub enum SystemClauseType { PointsToContinuationResetMarker, PutByte, PutChar, + PutCode, REPL(REPLCodePtr), ReadQueryTerm, ReadTerm, @@ -332,6 +334,7 @@ impl SystemClauseType { &SystemClauseType::FlushOutput => clause_name!("$flush_output"), &SystemClauseType::GetByte => clause_name!("$get_byte"), &SystemClauseType::GetChar => clause_name!("$get_char"), + &SystemClauseType::GetCode => clause_name!("$get_code"), &SystemClauseType::GetSingleChar => clause_name!("$get_single_char"), &SystemClauseType::ResetAttrVarState => clause_name!("$reset_attr_var_state"), &SystemClauseType::TruncateIfNoLiftedHeapGrowth => { @@ -393,6 +396,9 @@ impl SystemClauseType { &SystemClauseType::PutChar => { clause_name!("$put_char") } + &SystemClauseType::PutCode => { + clause_name!("$put_code") + } &SystemClauseType::QuotedToken => { clause_name!("$quoted_token") } @@ -499,6 +505,7 @@ impl SystemClauseType { ("$file_to_chars", 2) => Some(SystemClauseType::FileToChars), ("$get_byte", 2) => Some(SystemClauseType::GetByte), ("$get_char", 2) => Some(SystemClauseType::GetChar), + ("$get_code", 2) => Some(SystemClauseType::GetCode), ("$get_single_char", 1) => Some(SystemClauseType::GetSingleChar), ("$points_to_cont_reset_marker", 1) => { Some(SystemClauseType::PointsToContinuationResetMarker) @@ -509,6 +516,9 @@ impl SystemClauseType { ("$put_char", 2) => { Some(SystemClauseType::PutChar) } + ("$put_code", 2) => { + Some(SystemClauseType::PutCode) + } ("$reset_attr_var_state", 0) => Some(SystemClauseType::ResetAttrVarState), ("$truncate_if_no_lh_growth", 1) => { Some(SystemClauseType::TruncateIfNoLiftedHeapGrowth) diff --git a/src/prolog/lib/builtins.pl b/src/prolog/lib/builtins.pl index 9848b1e6..6eac4f15 100644 --- a/src/prolog/lib/builtins.pl +++ b/src/prolog/lib/builtins.pl @@ -50,9 +50,10 @@ user:term_expansion((:- op(Pred, Spec, [Op | OtherOps])), OpResults) :- expand_term/2, fail/0, false/0, findall/3, findall/4, flush_output/0, flush_output/1, get_byte/1, get_byte/2, get_char/1, get_char/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_char/1, put_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, set_prolog_flag/2, set_input/1, set_output/1, setof/3, sub_atom/5, subsumes_term/2, @@ -1197,7 +1198,7 @@ flush_output :- get_byte(S, B) :- '$get_byte'(S, B). -get_byte(S) :- +get_byte(B) :- current_input(S), '$get_byte'(S, B). @@ -1216,3 +1217,19 @@ put_byte(C) :- put_byte(S, C) :- '$put_byte'(S, C). + + +put_code(C) :- + current_output(S), + '$put_code'(S, C). + +put_code(S, C) :- + '$put_code'(S, C). + + +get_code(C) :- + current_input(S), + '$get_code'(S, C). + +get_code(S, C) :- + '$get_code'(S, C). diff --git a/src/prolog/machine/machine_errors.rs b/src/prolog/machine/machine_errors.rs index cb448199..d384185b 100644 --- a/src/prolog/machine/machine_errors.rs +++ b/src/prolog/machine/machine_errors.rs @@ -23,7 +23,7 @@ pub(crate) struct MachineError { from: ErrorProvenance, } -pub(super) +pub(crate) trait TypeError { fn type_error(self, h: usize, valid_type: ValidType) -> MachineError; } @@ -557,7 +557,7 @@ impl DomainErrorType { pub enum RepFlag { // Character, CharacterCode, - // InCharacterCode, + InCharacterCode, MaxArity, // MaxInteger, // MinInteger @@ -568,7 +568,7 @@ impl RepFlag { match self { // RepFlag::Character => "character", RepFlag::CharacterCode => "character_code", - // RepFlag::InCharacterCode => "in_character_code", + RepFlag::InCharacterCode => "in_character_code", RepFlag::MaxArity => "max_arity", // RepFlag::MaxInteger => "max_integer", // RepFlag::MinInteger => "min_integer" @@ -699,6 +699,41 @@ impl MachineState { self.check_for_list_pairs(sorted) } + #[inline] + pub(crate) + fn type_error( + &self, + valid_type: ValidType, + culprit: T, + caller: ClauseName, + arity: usize, + ) -> MachineStub { + let stub = MachineError::functor_stub(caller, arity); + let err = MachineError::type_error( + self.heap.h(), + valid_type, + culprit, + ); + + return self.error_form(err, stub); + } + + #[inline] + pub(crate) + fn representation_error( + &self, + rep_flag: RepFlag, + caller: ClauseName, + arity: usize, + ) -> MachineStub { + let stub = MachineError::functor_stub(caller, arity); + let err = MachineError::representation_error( + rep_flag, + ); + + return self.error_form(err, stub); + } + pub(super) fn error_form(&self, err: MachineError, src: MachineStub) -> MachineStub { let location = err.location; diff --git a/src/prolog/machine/streams.rs b/src/prolog/machine/streams.rs index 2ac482e7..7a368d1c 100644 --- a/src/prolog/machine/streams.rs +++ b/src/prolog/machine/streams.rs @@ -30,6 +30,15 @@ impl StreamType { StreamType::Text => "text_stream", } } + + #[inline] + pub(crate) + fn other(self) -> StreamType { + match self { + StreamType::Binary => StreamType::Text, + StreamType::Text => StreamType::Binary, + } + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -662,7 +671,7 @@ impl MachineState { } else if input.is_none() && stream.is_input_stream() { Some("stream") // 8.14.2.3 g) } else if stream.options.stream_type != expected_type { - Some(expected_type.as_str()) // 8.14.2.3 h) + Some(expected_type.other().as_str()) // 8.14.2.3 h) } else { None }; diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index 7c6c6c88..5e39322e 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -1592,6 +1592,64 @@ impl MachineState { self.unify(complete_string, a2); } + &SystemClauseType::PutCode => { + let mut stream = + self.get_stream_or_alias(self[temp_v!(1)], indices, "put_code", 2)?; + + self.check_stream_properties( + &mut stream, + StreamType::Text, + None, + clause_name!("put_code"), + 2, + )?; + + match self.store(self.deref(self[temp_v!(2)])) { + addr if addr.is_ref() => { + let stub = MachineError::functor_stub(clause_name!("put_code"), 2); + let err = MachineError::instantiation_error(); + + return Err(self.error_form(err, stub)); + } + Addr::CharCode(c) => { + let c = char::try_from(c).unwrap(); + write!(&mut stream, "{}", c).unwrap(); + } + 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()) { + write!(&mut stream, "{}", c).unwrap(); + return return_from_clause!(self.last_call, self); + } + } + Ok(Number::Fixnum(n)) => { + if let Some(c) = u32::try_from(n).ok().and_then(|c| char::try_from(c).ok()) { + write!(&mut stream, "{}", c).unwrap(); + return return_from_clause!(self.last_call, self); + } + } + _ => { + let stub = MachineError::functor_stub(clause_name!("put_code"), 2); + let err = MachineError::type_error( + self.heap.h(), + ValidType::Integer, + self[temp_v!(2)], + ); + + return Err(self.error_form(err, stub)); + } + } + + let stub = MachineError::functor_stub(clause_name!("put_code"), 2); + let err = MachineError::representation_error( + RepFlag::CharacterCode, + ); + + return Err(self.error_form(err, stub)); + } + } + } &SystemClauseType::PutChar => { let mut stream = self.get_stream_or_alias(self[temp_v!(1)], indices, "put_char", 2)?; @@ -1715,48 +1773,63 @@ impl MachineState { } } + 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!("get_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!("get_byte"), + 2, + )); + } + } + _ => { + return Err(self.type_error( + ValidType::InByte, + addr, + clause_name!("get_byte"), + 2, + )); + } + } + } + }; + 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); - } - } - _ => { - } - } - } + if let Some(var) = addr.as_var() { + self.bind(var, Addr::Usize(b[0] as usize)); + break; + } else if addr == Addr::Usize(b[0] as usize) { + break; + } else { + self.fail = true; + return Ok(()); } - - 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( @@ -1801,59 +1874,58 @@ impl MachineState { 2, )?; - loop { - let result = iter.next(); - - match result { - Some(Ok(c)) => { - 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::Char(c)); - return return_from_clause!(self.last_call, self); + 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!() } } - Addr::Con(h) if self.heap.atom_at(h) => { - match &self.heap[h] { - HeapCellValue::Atom(ref atom, _) if atom.is_char() => { - if let Some(d) = atom.as_str().chars().next() { - if c == d { - return return_from_clause!(self.last_call, self); - } else { - self.fail = true; - return Ok(()); - } - } else { - unreachable!() - } - } - _ => { - unreachable!() - } - } - } - Addr::Char(d) => { - if c == d { - return return_from_clause!(self.last_call, self); - } else { - self.fail = true; - return Ok(()); - } - } culprit => { - let stub = MachineError::functor_stub(clause_name!("get_char"), 2); - let err = MachineError::type_error( - self.heap.h(), + return Err(self.type_error( ValidType::InCharacter, - culprit, - ); - - return Err(self.error_form(err, stub)); + culprit.as_addr(h), + clause_name!("get_char"), + 2, + )); } } } + Addr::Char(d) => { + Addr::Char(d) + } + culprit => { + return Err(self.type_error( + ValidType::InCharacter, + culprit, + clause_name!("get_char"), + 2, + )); + } + }; + + loop { + let result = iter.next(); + + match result { + Some(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(()); + } + } _ => { self.eof_action( self[temp_v!(2)], @@ -1878,6 +1950,115 @@ impl MachineState { } } } + &SystemClauseType::GetCode => { + let mut stream = + self.get_stream_or_alias(self[temp_v!(1)], indices, "get_code", 2)?; + + self.check_stream_properties( + &mut stream, + StreamType::Text, + Some(self[temp_v!(2)]), + clause_name!("get_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!("get_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!("get_code"), + 2, + )); + } + } + _ => { + return Err(self.type_error( + ValidType::Integer, + self[temp_v!(2)], + clause_name!("get_code"), + 2, + )); + } + } + } + }; + + let mut iter = self.open_parsing_stream( + stream.clone(), + "get_code", + 2, + )?; + + loop { + let result = iter.next(); + + match result { + Some(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(()); + } + } + _ => { + self.eof_action( + self[temp_v!(2)], + &mut stream, + clause_name!("get_coder"), + 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::FlushOutput => { let mut stream = self.get_stream_or_alias(self[temp_v!(1)], indices, "flush_output", 1)?; From fe05082ddc511249eb2821dbc680d5bf589e3eb1 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Tue, 5 May 2020 22:57:06 -0600 Subject: [PATCH 12/18] add sockets library, use new type_error function from sockets system calls --- src/prolog/lib/sockets.pl | 60 ++++++++++++++++++++++++++++++ src/prolog/machine/system_calls.rs | 44 +++++++--------------- 2 files changed, 74 insertions(+), 30 deletions(-) create mode 100644 src/prolog/lib/sockets.pl diff --git a/src/prolog/lib/sockets.pl b/src/prolog/lib/sockets.pl new file mode 100644 index 00000000..83ea3837 --- /dev/null +++ b/src/prolog/lib/sockets.pl @@ -0,0 +1,60 @@ + +:- module(sockets, [socket_client_open/3, + socket_server_open/2, + socket_server_accept/4, + socket_server_close/1, + current_hostname/1]). + +:- use_module(library(error)). +:- use_module(library(lists)). +:- use_module(library(pairs)). + + +socket_client_open(Addr, Stream, Options) :- + ( var(Addr) -> + throw(error(instantiation_error, socket_client_open/3)) + ; + true + ), + must_be(var, Stream), + must_be(list, Options), + ( Addr = Address:Port, + atom(Address), + ( atom(Port) ; integer(Port) ) -> + true + ; + throw(error(type_error(socket_address, Addr), socket_client_open/3)) + ), + builtins:parse_stream_options(Options, + [Alias, EOFAction, Reposition, Type], + socket_client_open/3), + '$socket_client_open'(Address, Port, Stream, Alias, EOFAction, Reposition, Type). + + +socket_server_open(Addr, ServerSocket) :- + must_be(var, ServerSocket), + ( ( integer(Addr) ; var(Addr) ) -> + '$socket_server_open'([], Addr, ServerSocket) + ; + Addr = Address:Port, + must_be(atom, Address), + can_be(integer, Port), + '$socket_server_open'(Address, Port, ServerSocket) + ). + + +socket_server_accept(ServerSocket, Client, Stream, Options) :- + must_be(var, Client), + must_be(var, Stream), + builtins:parse_stream_options(Options, + [Alias, EOFAction, Reposition, Type], + socket_server_accept/4), + '$socket_server_accept'(ServerSocket, Client, Stream, Alias, EOFAction, Reposition, Type). + + +socket_server_close(ServerSocket) :- + '$socket_server_close'(ServerSocket). + + +current_hostname(HostName) :- + '$current_hostname'(HostName). diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index 5e39322e..4e0d2e39 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -4112,55 +4112,39 @@ impl MachineState { } culprit => { let culprit = culprit.as_addr(h); - let stub = MachineError::functor_stub( - clause_name!("socket_server_close"), - 1, - ); - let err = MachineError::type_error( - self.heap.h(), + return Err(self.type_error( ValidType::TcpListener, culprit, - ); - - return Err(self.error_form(err, stub)); + clause_name!("socket_server_accept"), + 4, + )); } } } culprit => { - let stub = MachineError::functor_stub( - clause_name!("socket_server_accept"), - 4, - ); - - let err = MachineError::type_error( - self.heap.h(), + return Err(self.type_error( ValidType::TcpListener, culprit, - ); - - return Err(self.error_form(err, stub)); + clause_name!("socket_server_accept"), + 4, + )); } } } &SystemClauseType::SocketServerClose => { match self.store(self.deref(self[temp_v!(1)])) { Addr::TcpListener(h) => { - self.heap[h] = HeapCellValue::Addr(Addr::EmptyList); + let closed_tcp_listener = clause_name!("$closed_tcp_listener"); + self.heap[h] = HeapCellValue::Atom(closed_tcp_listener, None); } culprit => { - let stub = MachineError::functor_stub( - clause_name!("socket_server_close"), - 1, - ); - - let err = MachineError::type_error( - self.heap.h(), + return Err(self.type_error( ValidType::TcpListener, culprit, - ); - - return Err(self.error_form(err, stub)); + clause_name!("socket_server_close"), + 1, + )); } } } From b8c50eb0fe88bb0872c6faa443e49b4924da40b9 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Wed, 6 May 2020 01:05:03 -0600 Subject: [PATCH 13/18] add peek_char/{1,2}, peek_byte/{1,2}, peek_code/{1,2} --- Cargo.lock | 1 + Cargo.toml | 3 +- src/prolog/clause_types.rs | 9 + src/prolog/lib/builtins.pl | 32 ++- src/prolog/lib/sockets.pl | 2 - src/prolog/machine/machine_indices.rs | 2 +- src/prolog/machine/streams.rs | 127 ++++++++++- src/prolog/machine/system_calls.rs | 304 +++++++++++++++++++++++++- src/prolog/read.rs | 58 ++++- 9 files changed, 527 insertions(+), 11 deletions(-) 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", + )); + } + _ => { + } + } + } } } } From 5be771842f1b0f1186d32fa5dafb34710ed8cc03 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sat, 9 May 2020 00:56:17 -0600 Subject: [PATCH 14/18] add stream_property/2 --- src/prolog/clause_types.rs | 9 + src/prolog/lib/builtins.pl | 48 ++++- src/prolog/machine/compile.rs | 2 +- src/prolog/machine/machine_indices.rs | 5 +- src/prolog/machine/mod.rs | 27 ++- src/prolog/machine/streams.rs | 226 ++++++++++++++++++----- src/prolog/machine/system_calls.rs | 252 ++++++++++++++++++++++++-- src/prolog/macros.rs | 1 + 8 files changed, 505 insertions(+), 65 deletions(-) diff --git a/src/prolog/clause_types.rs b/src/prolog/clause_types.rs index 4a8d8338..b4f1ee85 100644 --- a/src/prolog/clause_types.rs +++ b/src/prolog/clause_types.rs @@ -181,6 +181,7 @@ pub enum SystemClauseType { FetchGlobalVar, FetchGlobalVarWithOffset, FileToChars, + FirstStream, FlushOutput, GetByte, GetChar, @@ -221,6 +222,7 @@ pub enum SystemClauseType { NumberToCodes, OpDeclaration, Open, + NextStream, PartialStringTail, PeekByte, PeekChar, @@ -245,6 +247,7 @@ pub enum SystemClauseType { SetOutput, StoreGlobalVar, StoreGlobalVarWithOffset, + StreamProperty, InferenceLevel, CleanUpBlock, EraseBall, @@ -334,6 +337,7 @@ impl SystemClauseType { clause_name!("$fetch_global_var_with_offset") } &SystemClauseType::FileToChars => clause_name!("$file_to_chars"), + &SystemClauseType::FirstStream => clause_name!("$first_stream"), &SystemClauseType::FlushOutput => clause_name!("$flush_output"), &SystemClauseType::GetByte => clause_name!("$get_byte"), &SystemClauseType::GetChar => clause_name!("$get_char"), @@ -390,6 +394,7 @@ impl SystemClauseType { &SystemClauseType::ModuleHeadIsDynamic => clause_name!("$module_head_is_dynamic"), &SystemClauseType::ModuleExists => clause_name!("$module_exists"), &SystemClauseType::ModuleOf => clause_name!("$module_of"), + &SystemClauseType::NextStream => clause_name!("$next_stream"), &SystemClauseType::NoSuchPredicate => clause_name!("$no_such_predicate"), &SystemClauseType::NumberToChars => clause_name!("$number_to_chars"), &SystemClauseType::NumberToCodes => clause_name!("$number_to_codes"), @@ -416,6 +421,7 @@ impl SystemClauseType { &SystemClauseType::SetInput => clause_name!("$set_input"), &SystemClauseType::SetOutput => clause_name!("$set_output"), &SystemClauseType::SetSeed => clause_name!("$set_seed"), + &SystemClauseType::StreamProperty => clause_name!("$stream_property"), &SystemClauseType::StoreGlobalVar => clause_name!("$store_global_var"), &SystemClauseType::StoreGlobalVarWithOffset => { clause_name!("$store_global_var_with_offset") @@ -492,6 +498,8 @@ impl SystemClauseType { ("$current_hostname", 1) => Some(SystemClauseType::CurrentHostname), ("$current_input", 1) => Some(SystemClauseType::CurrentInput), ("$current_output", 1) => Some(SystemClauseType::CurrentOutput), + ("$first_stream", 1) => Some(SystemClauseType::FirstStream), + ("$next_stream", 2) => Some(SystemClauseType::NextStream), ("$flush_output", 1) => Some(SystemClauseType::FlushOutput), ("$del_attr_non_head", 1) => Some(SystemClauseType::DeleteAttribute), ("$del_attr_head", 1) => Some(SystemClauseType::DeleteHeadAttribute), @@ -566,6 +574,7 @@ impl SystemClauseType { ("$set_cp", 1) => Some(SystemClauseType::SetCutPoint(temp_v!(1))), ("$set_input", 1) => Some(SystemClauseType::SetInput), ("$set_output", 1) => Some(SystemClauseType::SetOutput), + ("$stream_property", 3) => Some(SystemClauseType::StreamProperty), ("$inference_level", 2) => Some(SystemClauseType::InferenceLevel), ("$clean_up_block", 1) => Some(SystemClauseType::CleanUpBlock), ("$erase_ball", 0) => Some(SystemClauseType::EraseBall), diff --git a/src/prolog/lib/builtins.pl b/src/prolog/lib/builtins.pl index c318c4a5..091aa73d 100644 --- a/src/prolog/lib/builtins.pl +++ b/src/prolog/lib/builtins.pl @@ -58,9 +58,9 @@ user:term_expansion((:- op(Pred, Spec, [Op | OtherOps])), OpResults) :- 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, - unify_with_occurs_check/2, write/1, + setof/3, stream_property/2, sub_atom/5, + subsumes_term/2, term_variables/2, throw/1, + true/0, unify_with_occurs_check/2, write/1, write_canonical/1, write_term/2, write_term/3, writeq/1]). @@ -1259,3 +1259,45 @@ peek_char(C) :- peek_char(S, C) :- '$peek_char'(S, C). + + +check_stream_property(file_name(F), file_name, F) :- + ( var(F) -> true ; atom(F) ). +check_stream_property(mode(M), mode, M) :- + ( var(M) -> true ; lists:member(M, [read, write, append]) ). +check_stream_property(D, direction, D) :- + ( var(D) -> true ; lists:member(D, [input, output, input_output]), ! ). +check_stream_property(alias(A), alias, A) :- + ( var(A) -> true ; atom(A) ). +check_stream_property(position(P), position, P) :- + ( var(P) -> true ; integer(P), P >= 0 ). +check_stream_property(end_of_stream(E), end_of_stream, E) :- + ( var(E) -> true ; lists:member(E, [not, at, past]) ). +check_stream_property(eof_action(A), eof_action, A) :- + ( var(A) -> true ; lists:member(A, [error, eof_code, reset]) ). +check_stream_property(reposition(B), reposition, B) :- + ( var(B) -> true ; lists:member(B, [true, false]) ). +check_stream_property(type(T), type, T) :- + ( var(T) -> true ; lists:member(T, [text, binary]) ). + + +stream_iter_(S, S). +stream_iter_(S, S1) :- + '$next_stream'(S, S0), + stream_iter_(S0, S1). + +stream_iter(S) :- + ( nonvar(S) -> + true + ; '$first_stream'(S0), + stream_iter_(S0, S) + ). + + +stream_property(S, P) :- + ( nonvar(P), \+ check_stream_property(P, _, _) -> + throw(error(domain_error(stream_property, P), stream_property/2)) + ; stream_iter(S), + check_stream_property(P, PropertyName, PropertyValue), + '$stream_property'(S, PropertyName, PropertyValue) + ). diff --git a/src/prolog/machine/compile.rs b/src/prolog/machine/compile.rs index a887cfb6..19b9d1e3 100644 --- a/src/prolog/machine/compile.rs +++ b/src/prolog/machine/compile.rs @@ -119,7 +119,7 @@ fn load_module_from_file( let mut path_buf = fix_filename(wam.indices.atom_tbl.clone(), path_buf)?; let filename = clause_name!(path_buf.to_string_lossy().to_string(), wam.indices.atom_tbl); - let file_handle = Stream::from_file_as_input(File::open(&path_buf).or_else(|_| { + let file_handle = Stream::from_file_as_input(filename.clone(), File::open(&path_buf).or_else(|_| { Err(SessionError::InvalidFileName(filename.clone())) })?); diff --git a/src/prolog/machine/machine_indices.rs b/src/prolog/machine/machine_indices.rs index 3bd730b3..21ff3e0f 100644 --- a/src/prolog/machine/machine_indices.rs +++ b/src/prolog/machine/machine_indices.rs @@ -19,7 +19,7 @@ use indexmap::IndexMap; use std::cell::RefCell; use std::cmp::Ordering; -use std::collections::{BTreeMap, VecDeque}; +use std::collections::{BTreeMap, BTreeSet, VecDeque}; use std::convert::TryFrom; use std::fmt; use std::mem; @@ -824,6 +824,7 @@ impl ModuleStub { pub(crate) type ModuleStubDir = IndexMap; pub(crate) type StreamAliasDir = IndexMap; +pub(crate) type StreamDir = BTreeSet; #[derive(Debug)] pub struct IndexStore { @@ -836,6 +837,7 @@ pub struct IndexStore { pub(super) module_dir: ModuleDir, pub(super) modules: ModuleDir, pub(super) op_dir: OpDir, + pub(super) streams: StreamDir, pub(super) stream_aliases: StreamAliasDir, } @@ -924,6 +926,7 @@ impl IndexStore { op_dir: default_op_dir(), modules: ModuleDir::new(), stream_aliases: StreamAliasDir::new(), + streams: StreamDir::new(), } } diff --git a/src/prolog/machine/mod.rs b/src/prolog/machine/mod.rs index 781fa8a3..78e3215a 100644 --- a/src/prolog/machine/mod.rs +++ b/src/prolog/machine/mod.rs @@ -315,7 +315,10 @@ impl Machine { if path.is_file() { let file_src = match File::open(&path) { - Ok(file_handle) => Stream::from_file_as_input(file_handle), + Ok(file_handle) => Stream::from_file_as_input( + clause_name!(".scryerrc"), + file_handle, + ), Err(_) => return, }; @@ -460,6 +463,28 @@ impl Machine { wam.compile_scryerrc(); + wam.current_input_stream.options.alias = Some(clause_name!("user_input")); + + wam.indices.stream_aliases.insert( + clause_name!("user_input"), + wam.current_input_stream.clone(), + ); + + wam.indices.streams.insert( + wam.current_input_stream.clone() + ); + + wam.current_output_stream.options.alias = Some(clause_name!("user_output")); + + wam.indices.stream_aliases.insert( + clause_name!("user_output"), + wam.current_output_stream.clone(), + ); + + wam.indices.streams.insert( + wam.current_output_stream.clone() + ); + wam } diff --git a/src/prolog/machine/streams.rs b/src/prolog/machine/streams.rs index d47d525c..305c0a20 100644 --- a/src/prolog/machine/streams.rs +++ b/src/prolog/machine/streams.rs @@ -6,6 +6,7 @@ use crate::prolog::machine::machine_errors::*; use crate::prolog::machine::machine_indices::*; use crate::prolog::machine::machine_state::*; +use std::cmp::Ordering; use std::cell::RefCell; use std::error::Error; use std::fmt; @@ -26,8 +27,8 @@ impl StreamType { pub(crate) fn as_str(&self) -> &'static str { match self { - StreamType::Binary => "binary_stream", - StreamType::Text => "text_stream", + StreamType::Binary => "binary", + StreamType::Text => "text", } } @@ -48,23 +49,53 @@ pub enum EOFAction { Reset, } +pub enum AtEndOfStream { + Not, + End, + Past +} + +impl AtEndOfStream { + #[inline] + pub(crate) + fn as_str(&self) -> &'static str { + match self { + AtEndOfStream::Not => "not", + AtEndOfStream::Past => "past", + AtEndOfStream::End => "end", + } + } +} + +impl EOFAction { + #[inline] + pub(crate) + fn as_str(&self) -> &'static str { + match self { + EOFAction::EOFCode => "eof_code", + EOFAction::Error => "error", + EOFAction::Reset => "reset", + } + } +} + /* all these streams are closed automatically when the instance is * dropped. */ pub enum StreamInstance { Bytes(Cursor>), DynReadSource(Box), - InputFile(File), - OutputFile(File), + InputFile(ClauseName, File), + OutputFile(ClauseName, File, bool), // File, append. Null, ReadlineStream(ReadlineStream), Stdout, - TcpStream(TcpStream), + TcpStream(ClauseName, TcpStream), } impl Drop for StreamInstance { fn drop(&mut self) { match self { - StreamInstance::TcpStream(ref mut tcp_stream) => { + StreamInstance::TcpStream(_, ref mut tcp_stream) => { tcp_stream.shutdown(Shutdown::Both).unwrap(); } _ => { @@ -80,14 +111,14 @@ impl fmt::Debug for StreamInstance { write!(fmt, "Bytes({:?})", bytes), &StreamInstance::DynReadSource(_) => write!(fmt, "DynReadSource(_)"), // Hacky solution. - &StreamInstance::InputFile(ref file) => write!(fmt, "InputFile({:?})", file), - &StreamInstance::OutputFile(ref file) => write!(fmt, "OutputFile({:?})", file), + &StreamInstance::InputFile(_, ref file) => write!(fmt, "InputFile({:?})", file), + &StreamInstance::OutputFile(_, ref file, _) => write!(fmt, "OutputFile({:?})", file), &StreamInstance::Null => write!(fmt, "Null"), &StreamInstance::ReadlineStream(ref readline_stream) => write!(fmt, "ReadlineStream({:?})", readline_stream), // &StreamInstance::Stdin => write!(fmt, "Stdin"), &StreamInstance::Stdout => write!(fmt, "Stdout"), - &StreamInstance::TcpStream(ref tcp_stream) => + &StreamInstance::TcpStream(_, ref tcp_stream) => write!(fmt, "TcpStream({:?})", tcp_stream), } } @@ -194,6 +225,20 @@ pub struct Stream { stream_inst: WrappedStreamInstance, } +impl PartialOrd for Stream { + #[inline] + fn partial_cmp(&self, other: &Stream) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Stream { + #[inline] + fn cmp(&self, other: &Stream) -> Ordering { + self.as_ptr().cmp(&other.as_ptr()) + } +} + impl PartialEq for Stream { #[inline] fn eq(&self, other: &Self) -> bool { @@ -203,15 +248,6 @@ impl PartialEq for Stream { impl Eq for Stream {} -impl From for Stream { - fn from(tcp_stream: TcpStream) -> Self { - tcp_stream.set_read_timeout(None).unwrap(); - tcp_stream.set_write_timeout(None).unwrap(); - - Stream::from_inst(StreamInstance::TcpStream(tcp_stream)) - } -} - impl From for Stream { fn from(string: String) -> Self { Stream::from_inst(StreamInstance::Bytes(Cursor::new(string.into_bytes()))) @@ -245,6 +281,105 @@ impl Stream { ptr as *const u8 } + #[inline] + pub(crate) + fn position(&mut self) -> Option { + match *self.stream_inst.0.borrow_mut() { + StreamInstance::InputFile(_, ref mut file) => { + file.seek(SeekFrom::Current(0)).ok() + } + _ => { + None + } + } + } + + #[inline] + pub(crate) + fn position_relative_to_end(&mut self) -> AtEndOfStream { + if self.past_end_of_stream { + return AtEndOfStream::Past; + } + + match *self.stream_inst.0.borrow_mut() { + StreamInstance::InputFile(_, ref mut file) => { + match file.metadata() { + Ok(metadata) => { + if let Ok(position) = file.seek(SeekFrom::Current(0)) { + return match position.cmp(&metadata.len()) { + Ordering::Equal => { + AtEndOfStream::End + } + Ordering::Less => { + AtEndOfStream::Not + } + Ordering::Greater => { + self.past_end_of_stream = true; + AtEndOfStream::Past + } + }; + } else { + self.past_end_of_stream = true; + AtEndOfStream::Past + } + } + _ => { + self.past_end_of_stream = true; + AtEndOfStream::Past + } + } + } + _ => { + AtEndOfStream::Not + } + } + } + + #[inline] + pub(crate) + fn file_name(&self) -> Option { + match *self.stream_inst.0.borrow() { + StreamInstance::InputFile(ref name, _) => { + Some(name.clone()) + } + StreamInstance::OutputFile(ref name, ..) => { + Some(name.clone()) + } + StreamInstance::TcpStream(ref name, _) => { + Some(name.clone()) + } + _ => { + None + } + } + } + + #[inline] + pub(crate) + fn mode(&self) -> &'static str { + match *self.stream_inst.0.borrow() { + StreamInstance::Bytes(_) | + StreamInstance::ReadlineStream(_) | + StreamInstance::DynReadSource(_) | + StreamInstance::InputFile(..) => { + "read" + } + StreamInstance::TcpStream(..) => { + "read_append" + } + StreamInstance::OutputFile(_, _, true) => { + "append" + } + StreamInstance::Stdout | + StreamInstance::OutputFile(_, _, false) => { + "write" + } + StreamInstance::Null => { + "" + } + } + } + #[inline] fn from_inst(stream_inst: StreamInstance) -> Self { Stream { @@ -262,14 +397,23 @@ impl Stream { #[inline] pub(crate) - fn from_file_as_output(file: File) -> Self { - Stream::from_inst(StreamInstance::OutputFile(file)) + fn from_tcp_stream(address: ClauseName, tcp_stream: TcpStream) -> Self { + tcp_stream.set_read_timeout(None).unwrap(); + tcp_stream.set_write_timeout(None).unwrap(); + + Stream::from_inst(StreamInstance::TcpStream(address, tcp_stream)) } #[inline] pub(crate) - fn from_file_as_input(file: File) -> Self { - Stream::from_inst(StreamInstance::InputFile(file)) + fn from_file_as_output(name: ClauseName, file: File, in_append_mode: bool) -> Self { + Stream::from_inst(StreamInstance::OutputFile(name, file, in_append_mode)) + } + + #[inline] + pub(crate) + fn from_file_as_input(name: ClauseName, file: File) -> Self { + Stream::from_inst(StreamInstance::InputFile(name, file)) } #[inline] @@ -321,11 +465,11 @@ impl Stream { fn is_input_stream(&self) -> bool { match *self.stream_inst.0.borrow() { // StreamInstance::Stdin | - StreamInstance::TcpStream(_) | + StreamInstance::TcpStream(..) | StreamInstance::Bytes(_) | StreamInstance::ReadlineStream(_) | StreamInstance::DynReadSource(_) | - StreamInstance::InputFile(_) => { + StreamInstance::InputFile(..) => { true } _ => { @@ -339,9 +483,9 @@ impl Stream { fn is_output_stream(&self) -> bool { match *self.stream_inst.0.borrow() { StreamInstance::Stdout - | StreamInstance::TcpStream(_) + | StreamInstance::TcpStream(..) | StreamInstance::Bytes(_) - | StreamInstance::OutputFile(_) => { + | StreamInstance::OutputFile(..) => { true } _ => { @@ -358,7 +502,7 @@ impl Stream { cursor.set_position(0); true } - StreamInstance::InputFile(ref mut file) => { + StreamInstance::InputFile(_, ref mut file) => { file.seek(SeekFrom::Start(0)).unwrap(); true } @@ -393,7 +537,7 @@ impl Stream { } } } - StreamInstance::InputFile(ref mut file) => { + StreamInstance::InputFile(_, ref mut file) => { let mut b = [0u8; 1]; match file.read(&mut b)? { @@ -412,7 +556,7 @@ impl Stream { StreamInstance::ReadlineStream(ref mut stream) => { stream.peek_byte() } - StreamInstance::TcpStream(ref mut tcp_stream) => { + StreamInstance::TcpStream(_, ref mut tcp_stream) => { let mut b = [0u8; 1]; tcp_stream.peek(&mut b)?; Ok(b[0]) @@ -432,7 +576,7 @@ impl Stream { use unicode_reader::CodePoints; match *self.stream_inst.0.borrow_mut() { - StreamInstance::InputFile(ref mut file) => { + StreamInstance::InputFile(_, ref mut file) => { let c = { let mut iter = CodePoints::from(&*file); @@ -453,7 +597,7 @@ impl Stream { StreamInstance::ReadlineStream(ref mut stream) => { stream.peek_char() } - StreamInstance::TcpStream(ref tcp_stream) => { + StreamInstance::TcpStream(_, ref tcp_stream) => { let c = { let mut buf = [0u8; 8]; tcp_stream.peek(&mut buf)?; @@ -832,10 +976,10 @@ impl MachineState { impl Read for Stream { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { match *self.stream_inst.0.borrow_mut() { - StreamInstance::InputFile(ref mut file) => { + StreamInstance::InputFile(_, ref mut file) => { file.read(buf) } - StreamInstance::TcpStream(ref mut tcp_stream) => { + StreamInstance::TcpStream(_, ref mut tcp_stream) => { tcp_stream.read(buf) } StreamInstance::ReadlineStream(ref mut rl_stream) => { @@ -852,7 +996,7 @@ impl Read for Stream { stdin().read(buf) } */ - StreamInstance::OutputFile(_) | StreamInstance::Stdout | StreamInstance::Null => { + StreamInstance::OutputFile(..) | StreamInstance::Stdout | StreamInstance::Null => { Err(std::io::Error::new( ErrorKind::PermissionDenied, StreamError::ReadFromOutputStream, @@ -865,10 +1009,10 @@ impl Read for Stream { impl Write for Stream { fn write(&mut self, buf: &[u8]) -> std::io::Result { match *self.stream_inst.0.borrow_mut() { - StreamInstance::OutputFile(ref mut file) => { + StreamInstance::OutputFile(_, ref mut file, _) => { file.write(buf) } - StreamInstance::TcpStream(ref mut tcp_stream) => { + StreamInstance::TcpStream(_, ref mut tcp_stream) => { tcp_stream.write(buf) } StreamInstance::Bytes(ref mut cursor) => { @@ -878,7 +1022,7 @@ impl Write for Stream { stdout().write(buf) } StreamInstance::DynReadSource(_) | StreamInstance::ReadlineStream(_) | - StreamInstance::InputFile(_) | StreamInstance::Null => { + StreamInstance::InputFile(..) | StreamInstance::Null => { Err(std::io::Error::new( ErrorKind::PermissionDenied, StreamError::WriteToInputStream, @@ -889,10 +1033,10 @@ impl Write for Stream { fn flush(&mut self) -> std::io::Result<()> { match *self.stream_inst.0.borrow_mut() { - StreamInstance::OutputFile(ref mut file) => { + StreamInstance::OutputFile(_, ref mut file, _) => { file.flush() } - StreamInstance::TcpStream(ref mut tcp_stream) => { + StreamInstance::TcpStream(_, ref mut tcp_stream) => { tcp_stream.flush() } StreamInstance::Bytes(ref mut cursor) => { @@ -902,7 +1046,7 @@ impl Write for Stream { stdout().flush() } StreamInstance::DynReadSource(_) | StreamInstance::ReadlineStream(_) | - StreamInstance::InputFile(_) | StreamInstance::Null => { + StreamInstance::InputFile(..) | StreamInstance::Null => { Err(std::io::Error::new( ErrorKind::PermissionDenied, StreamError::FlushToInputStream, @@ -911,5 +1055,3 @@ impl Write for Stream { } } } - -//TODO: write a Seek instance. diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index a97ec40c..73c62e49 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -21,11 +21,13 @@ use crate::prolog::rug::Integer; use crate::ref_thread_local::RefThreadLocal; use std::cmp; +use std::collections::BTreeSet; use std::convert::TryFrom; use std::io::{ErrorKind, Read, Write}; use std::iter::{once, FromIterator}; use std::fs::{File, OpenOptions}; use std::net::{TcpListener, TcpStream}; +use std::ops::Sub; use std::rc::Rc; use std::time::Duration; @@ -2361,11 +2363,75 @@ impl MachineState { } } } + &SystemClauseType::FirstStream => { + let mut first_stream = None; + let mut null_streams = BTreeSet::new(); + + for stream in indices.streams.iter().cloned() { + if !stream.is_null_stream() { + first_stream = Some(stream); + break; + } else { + null_streams.insert(stream); + } + } + + indices.streams = indices.streams.sub(&null_streams); + + if let Some(first_stream) = first_stream { + let stream = self.heap.to_unifiable(HeapCellValue::Stream(first_stream)); + + let var = self.store(self.deref(self[temp_v!(1)])).as_var().unwrap(); + self.bind(var, stream); + } else { + self.fail = true; + return Ok(()); + } + } + &SystemClauseType::NextStream => { + let prev_stream = + match self.store(self.deref(self[temp_v!(1)])) { + Addr::Stream(h) => { + if let HeapCellValue::Stream(ref stream) = &self.heap[h] { + stream.clone() + } else { + unreachable!() + } + } + _ => { + unreachable!() + } + }; + + let mut next_stream = None; + let mut null_streams = BTreeSet::new(); + + for stream in indices.streams.range(prev_stream.clone() ..).skip(1).cloned() { + if !stream.is_null_stream() { + next_stream = Some(stream); + break; + } else { + null_streams.insert(stream); + } + } + + indices.streams = indices.streams.sub(&null_streams); + + if let Some(next_stream) = next_stream { + let var = self.store(self.deref(self[temp_v!(2)])).as_var().unwrap(); + let next_stream = self.heap.to_unifiable(HeapCellValue::Stream(next_stream)); + + self.bind(var, next_stream); + } else { + self.fail = true; + return Ok(()); + } + } &SystemClauseType::FlushOutput => { let mut stream = self.get_stream_or_alias(self[temp_v!(1)], indices, "flush_output", 1)?; - if stream.is_input_stream() { + if !stream.is_output_stream() { let stub = MachineError::functor_stub(clause_name!("flush_output"), 1); let addr = vec![ @@ -2508,14 +2574,24 @@ impl MachineState { let mut stream = self.get_stream_or_alias(self[temp_v!(1)], indices, "close", 2)?; - if stream.is_output_stream() { + if !stream.is_input_stream() { stream.flush().unwrap(); // 8.11.6.1b) } + indices.streams.remove(&stream); + if stream == *current_input_stream { - *current_input_stream = readline::input_stream(); + *current_input_stream = indices.stream_aliases.get( + &clause_name!("user_input") + ).cloned().unwrap(); + + indices.streams.insert(current_input_stream.clone()); } else if stream == *current_output_stream { - *current_output_stream = Stream::stdout(); + *current_output_stream = indices.stream_aliases.get( + &clause_name!("user_output") + ).cloned().unwrap(); + + indices.streams.insert(current_output_stream.clone()); } stream.close(); @@ -3015,19 +3091,19 @@ impl MachineState { let mut open_options = OpenOptions::new(); - let is_input_file = + let (is_input_file, in_append_mode) = match mode.as_str() { "read" => { open_options.read(true).write(false).create(false); - true + (true, false) } "write" => { open_options.read(false).write(true).truncate(true).create(true); - false + (false, false) } "append" => { open_options.read(false).write(true).create(true).append(true); - false + (false, true) } _ => { let stub = MachineError::functor_stub(clause_name!("open"), 4); @@ -3072,13 +3148,15 @@ impl MachineState { }; let mut stream = if is_input_file { - Stream::from_file_as_input(file) + Stream::from_file_as_input(file_spec, file) } else { - Stream::from_file_as_output(file) + Stream::from_file_as_output(file_spec, file, in_append_mode) }; stream.options = options; + indices.streams.insert(stream.clone()); + if let Some(ref alias) = &stream.options.alias { indices.stream_aliases.insert(alias.clone(), stream.clone()); } @@ -3721,7 +3799,7 @@ impl MachineState { let addr = self.store(self.deref(self[temp_v!(1)])); let stream = self.get_stream_or_alias(addr, indices, "set_input", 1)?; - if stream.is_output_stream() { + if !stream.is_input_stream() { let stub = MachineError::functor_stub( clause_name!("set_input"), 1, @@ -3747,7 +3825,7 @@ impl MachineState { let addr = self.store(self.deref(self[temp_v!(1)])); let stream = self.get_stream_or_alias(addr, indices, "set_output", 1)?; - if stream.is_input_stream() { + if !stream.is_output_stream() { let stub = MachineError::functor_stub( clause_name!("set_input"), 1, @@ -4246,15 +4324,19 @@ impl MachineState { } let stream = - match TcpStream::connect(socket_addr).map_err(|e| e.kind()) { + match TcpStream::connect(&socket_addr).map_err(|e| e.kind()) { Ok(tcp_stream) => { - let mut stream = Stream::from(tcp_stream); + let socket_addr = clause_name!(socket_addr, indices.atom_tbl.clone()); + + let mut stream = Stream::from_tcp_stream(socket_addr, tcp_stream); stream.options = options; if let Some(ref alias) = &stream.options.alias { indices.stream_aliases.insert(alias.clone(), stream.clone()); } + indices.streams.insert(stream.clone()); + self.heap.to_unifiable(HeapCellValue::Stream(stream)) } Err(ErrorKind::PermissionDenied) => { @@ -4383,20 +4465,42 @@ impl MachineState { return Err(self.reposition_error("socket_server_accept", 4)); } + if let Some(ref alias) = &options.alias { + if indices.stream_aliases.contains_key(alias) { + return Err(self.occupied_alias_permission_error( + alias.clone(), + "socket_server_accept", + 4, + )); + } + } + match self.store(self.deref(self[temp_v!(1)])) { Addr::TcpListener(h) => { match &mut self.heap[h] { HeapCellValue::TcpListener(ref mut tcp_listener) => { match tcp_listener.accept().ok() { Some((tcp_stream, socket_addr)) => { - let mut tcp_stream = Stream::from(tcp_stream); + let client = + clause_name!(format!("{}", socket_addr), indices.atom_tbl); + + let mut tcp_stream = + Stream::from_tcp_stream(client.clone(), tcp_stream); + tcp_stream.options = options; + if let Some(ref alias) = &tcp_stream.options.alias { + indices.stream_aliases.insert( + alias.clone(), + tcp_stream.clone(), + ); + } + + indices.streams.insert(tcp_stream.clone()); + let tcp_stream = self.heap.to_unifiable(HeapCellValue::Stream(tcp_stream)); - let client = - clause_name!(format!("{}", socket_addr), indices.atom_tbl); let client = self.heap.to_unifiable(HeapCellValue::Atom(client, None)); @@ -4450,6 +4554,120 @@ impl MachineState { } } } + &SystemClauseType::StreamProperty => { + let mut stream = self.get_stream_or_alias( + self[temp_v!(1)], + indices, + "stream_property", + 2, + )?; + + let property = + match self.store(self.deref(self[temp_v!(2)])) { + Addr::Con(h) if self.heap.atom_at(h) => { + match &self.heap[h] { + HeapCellValue::Atom(ref name, _) => { + match name.as_str() { + "file_name" => { + if let Some(file_name) = stream.file_name() { + HeapCellValue::Atom( + file_name, + None, + ) + } else { + self.fail = true; + return Ok(()); + } + } + "mode" => { + HeapCellValue::Atom( + clause_name!(stream.mode()), + None, + ) + } + "direction" => { + HeapCellValue::Atom( + if stream.is_input_stream() && stream.is_output_stream() { + clause_name!("input_output") + } else if stream.is_input_stream() { + clause_name!("input") + } else { + clause_name!("output") + }, + None, + ) + } + "alias" => { + if let Some(alias) = &stream.options.alias { + HeapCellValue::Atom( + alias.clone(), + None, + ) + } else { + self.fail = true; + return Ok(()); + } + } + "position" => { + if stream.options.reposition { + if let Some(position) = stream.position() { + HeapCellValue::Addr(Addr::Usize(position as usize)) + } else { + unreachable!() + } + } else { + self.fail = true; + return Ok(()); + } + } + "end_of_stream" => { + let end_of_stream_pos = stream.position_relative_to_end(); + + HeapCellValue::Atom( + clause_name!(end_of_stream_pos.as_str()), + None, + ) + } + "eof_action" => { + HeapCellValue::Atom( + clause_name!(stream.options.eof_action.as_str()), + None, + ) + } + "reposition" => { + HeapCellValue::Atom( + clause_name!(if stream.options.reposition { + "true" + } else { + "false" + }), + None, + ) + } + "type" => { + HeapCellValue::Atom( + clause_name!(stream.options.stream_type.as_str()), + None, + ) + } + _ => { + unreachable!() + } + } + } + _ => { + unreachable!() + } + } + } + _ => { + unreachable!() + } + }; + + let property = self.heap.to_unifiable(property); + self.unify(self[temp_v!(3)], property); + } &SystemClauseType::StoreGlobalVar => { let key = self[temp_v!(1)]; diff --git a/src/prolog/macros.rs b/src/prolog/macros.rs index 6a3e46c1..5ac0f450 100644 --- a/src/prolog/macros.rs +++ b/src/prolog/macros.rs @@ -358,6 +358,7 @@ macro_rules! index_store { op_dir: $op_dir, modules: $modules, stream_aliases: StreamAliasDir::new(), + streams: StreamDir::new(), } }; } From ff420f1e67bb9b93df47fa3187b7314c73e9d094 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sat, 9 May 2020 01:34:56 -0600 Subject: [PATCH 15/18] carry past_end_of_stream in WrappedStreamInstance --- src/prolog/machine/machine_state.rs | 2 +- src/prolog/machine/streams.rs | 81 +++++++++++++++++------------ src/prolog/machine/system_calls.rs | 20 +++---- 3 files changed, 60 insertions(+), 43 deletions(-) diff --git a/src/prolog/machine/machine_state.rs b/src/prolog/machine/machine_state.rs index b16389d1..6f6c5365 100644 --- a/src/prolog/machine/machine_state.rs +++ b/src/prolog/machine/machine_state.rs @@ -627,7 +627,7 @@ impl MachineState { 3, )?; - if stream.past_end_of_stream { + 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 { diff --git a/src/prolog/machine/streams.rs b/src/prolog/machine/streams.rs index 305c0a20..66ad2b32 100644 --- a/src/prolog/machine/streams.rs +++ b/src/prolog/machine/streams.rs @@ -14,6 +14,7 @@ use std::fs::File; use std::io::{stdout, Cursor, ErrorKind, Read, Seek, SeekFrom, Write}; use std::hash::{Hash, Hasher}; use std::net::{Shutdown, TcpStream}; +use std::ops::DerefMut; use std::rc::Rc; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -125,12 +126,14 @@ impl fmt::Debug for StreamInstance { } #[derive(Debug, Clone)] -struct WrappedStreamInstance(Rc>); +struct WrappedStreamInstance(Rc>); impl WrappedStreamInstance { #[inline] fn new(stream_inst: StreamInstance) -> Self { - WrappedStreamInstance(Rc::new(RefCell::new(stream_inst))) + WrappedStreamInstance( + Rc::new(RefCell::new((false, stream_inst))) + ) } } @@ -220,7 +223,6 @@ impl Default for StreamOptions { #[derive(Debug, Clone, Hash)] pub struct Stream { - pub past_end_of_stream: bool, pub options: StreamOptions, stream_inst: WrappedStreamInstance, } @@ -284,7 +286,7 @@ impl Stream { #[inline] pub(crate) fn position(&mut self) -> Option { - match *self.stream_inst.0.borrow_mut() { + match self.stream_inst.0.borrow_mut().1 { StreamInstance::InputFile(_, ref mut file) => { file.seek(SeekFrom::Current(0)).ok() } @@ -294,15 +296,27 @@ impl Stream { } } + #[inline] + pub(crate) + fn past_end_of_stream(&self) -> bool { + self.stream_inst.0.borrow_mut().0 + } + + #[inline] + pub(crate) + fn set_past_end_of_stream(&mut self) { + self.stream_inst.0.borrow_mut().0 = true; + } + #[inline] pub(crate) fn position_relative_to_end(&mut self) -> AtEndOfStream { - if self.past_end_of_stream { + if self.past_end_of_stream() { return AtEndOfStream::Past; } - match *self.stream_inst.0.borrow_mut() { - StreamInstance::InputFile(_, ref mut file) => { + match self.stream_inst.0.borrow_mut().deref_mut() { + (past_end_of_stream, StreamInstance::InputFile(_, ref mut file)) => { match file.metadata() { Ok(metadata) => { if let Ok(position) = file.seek(SeekFrom::Current(0)) { @@ -314,17 +328,17 @@ impl Stream { AtEndOfStream::Not } Ordering::Greater => { - self.past_end_of_stream = true; + *past_end_of_stream = true; //self.set_past_end_of_stream(); AtEndOfStream::Past } }; } else { - self.past_end_of_stream = true; + *past_end_of_stream = true; //self.set_past_end_of_stream(); AtEndOfStream::Past } } _ => { - self.past_end_of_stream = true; + *past_end_of_stream = true; //self.set_past_end_of_stream(); AtEndOfStream::Past } } @@ -338,7 +352,7 @@ impl Stream { #[inline] pub(crate) fn file_name(&self) -> Option { - match *self.stream_inst.0.borrow() { + match self.stream_inst.0.borrow().1 { StreamInstance::InputFile(ref name, _) => { Some(name.clone()) } @@ -357,7 +371,7 @@ impl Stream { #[inline] pub(crate) fn mode(&self) -> &'static str { - match *self.stream_inst.0.borrow() { + match self.stream_inst.0.borrow().1 { StreamInstance::Bytes(_) | StreamInstance::ReadlineStream(_) | StreamInstance::DynReadSource(_) | @@ -383,7 +397,6 @@ impl Stream { #[inline] fn from_inst(stream_inst: StreamInstance) -> Self { Stream { - past_end_of_stream: false, options: StreamOptions::default(), stream_inst: WrappedStreamInstance::new(stream_inst) } @@ -419,7 +432,7 @@ impl Stream { #[inline] pub(crate) fn is_stdout(&self) -> bool { - match *self.stream_inst.0.borrow() { + match self.stream_inst.0.borrow().1 { StreamInstance::Stdout => { true } @@ -432,7 +445,7 @@ impl Stream { #[inline] pub(crate) fn is_stdin(&self) -> bool { - match *self.stream_inst.0.borrow() { + match self.stream_inst.0.borrow().1 { //StreamInstance::Stdin | StreamInstance::ReadlineStream(_) => { true @@ -446,14 +459,13 @@ impl Stream { #[inline] pub(crate) fn close(&mut self) { - *self.stream_inst.0.borrow_mut() = StreamInstance::Null; - self.past_end_of_stream = true; + self.stream_inst.0.borrow_mut().1 = StreamInstance::Null; } #[inline] pub(crate) fn is_null_stream(&self) -> bool { - if let StreamInstance::Null = *self.stream_inst.0.borrow() { + if let StreamInstance::Null = self.stream_inst.0.borrow().1 { true } else { false @@ -463,7 +475,7 @@ impl Stream { #[inline] pub(crate) fn is_input_stream(&self) -> bool { - match *self.stream_inst.0.borrow() { + match self.stream_inst.0.borrow().1 { // StreamInstance::Stdin | StreamInstance::TcpStream(..) | StreamInstance::Bytes(_) | @@ -481,7 +493,7 @@ impl Stream { #[inline] pub(crate) fn is_output_stream(&self) -> bool { - match *self.stream_inst.0.borrow() { + match self.stream_inst.0.borrow().1 { StreamInstance::Stdout | StreamInstance::TcpStream(..) | StreamInstance::Bytes(_) @@ -497,7 +509,7 @@ impl Stream { // returns true on success. #[inline] fn reset(&mut self) -> bool { - match *self.stream_inst.0.borrow_mut() { + match self.stream_inst.0.borrow_mut().1 { StreamInstance::Bytes(ref mut cursor) => { cursor.set_position(0); true @@ -519,7 +531,7 @@ impl Stream { #[inline] pub(crate) fn peek_byte(&mut self) -> std::io::Result { - match *self.stream_inst.0.borrow_mut() { + match self.stream_inst.0.borrow_mut().1 { StreamInstance::Bytes(ref mut cursor) => { let mut b = [0u8; 1]; let pos = cursor.position(); @@ -575,7 +587,7 @@ impl Stream { fn peek_char(&mut self) -> std::io::Result { use unicode_reader::CodePoints; - match *self.stream_inst.0.borrow_mut() { + match self.stream_inst.0.borrow_mut().1 { StreamInstance::InputFile(_, ref mut file) => { let c = { let mut iter = CodePoints::from(&*file); @@ -638,7 +650,7 @@ impl MachineState { ) -> CallResult { match stream.options.eof_action { EOFAction::Error => { - stream.past_end_of_stream = true; + stream.set_past_end_of_stream(); return Err(self.open_past_eos_error(stream.clone(), caller, arity)); } EOFAction::EOFCode => { @@ -646,12 +658,15 @@ impl MachineState { HeapCellValue::Atom(clause_name!("end_of_file"), None) ); - stream.past_end_of_stream = true; + stream.set_past_end_of_stream(); Ok(self.unify(result, end_of_stream)) } EOFAction::Reset => { - stream.past_end_of_stream = !stream.reset(); - Ok(self.fail = stream.past_end_of_stream) + if !stream.reset() { + stream.set_past_end_of_stream(); + } + + Ok(self.fail = stream.past_end_of_stream()) } } } @@ -752,10 +767,10 @@ impl MachineState { Addr::Con(h) if self.heap.atom_at(h) => { if let HeapCellValue::Atom(ref atom, ref spec) = self.heap.clone(h) { match indices.stream_aliases.get(atom) { - Some(stream) => { + Some(stream) if !stream.is_null_stream() => { stream.clone() } - None => { + _ => { let stub = MachineError::functor_stub(clause_name!(caller), arity); let addr = self.heap.to_unifiable( @@ -959,7 +974,7 @@ impl MachineState { } if let Some(input) = input { - if stream.past_end_of_stream { + if stream.past_end_of_stream() { self.eof_action( input, stream, @@ -975,7 +990,7 @@ impl MachineState { impl Read for Stream { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { - match *self.stream_inst.0.borrow_mut() { + match self.stream_inst.0.borrow_mut().1 { StreamInstance::InputFile(_, ref mut file) => { file.read(buf) } @@ -1008,7 +1023,7 @@ impl Read for Stream { impl Write for Stream { fn write(&mut self, buf: &[u8]) -> std::io::Result { - match *self.stream_inst.0.borrow_mut() { + match self.stream_inst.0.borrow_mut().1 { StreamInstance::OutputFile(_, ref mut file, _) => { file.write(buf) } @@ -1032,7 +1047,7 @@ impl Write for Stream { } fn flush(&mut self) -> std::io::Result<()> { - match *self.stream_inst.0.borrow_mut() { + match self.stream_inst.0.borrow_mut().1 { StreamInstance::OutputFile(_, ref mut file, _) => { file.flush() } diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index 73c62e49..43bfba86 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -1183,7 +1183,7 @@ impl MachineState { 2, )?; - if stream.past_end_of_stream { + 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 { @@ -1280,7 +1280,7 @@ impl MachineState { 2, )?; - if stream.past_end_of_stream { + 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 { @@ -1378,7 +1378,7 @@ impl MachineState { 2, )?; - if stream.past_end_of_stream { + 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 { @@ -2069,7 +2069,7 @@ impl MachineState { 2, )?; - if stream.past_end_of_stream { + 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 { @@ -2164,7 +2164,7 @@ impl MachineState { 2, )?; - if stream.past_end_of_stream { + 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 { @@ -2266,7 +2266,7 @@ impl MachineState { 2, )?; - if stream.past_end_of_stream { + 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 { @@ -2594,10 +2594,12 @@ impl MachineState { indices.streams.insert(current_output_stream.clone()); } - stream.close(); + if !stream.is_stdin() && !stream.is_stdout() { + stream.close(); - if let Some(alias) = stream.options.alias { - indices.stream_aliases.remove(&alias); + if let Some(alias) = stream.options.alias { + indices.stream_aliases.remove(&alias); + } } } &SystemClauseType::CopyToLiftedHeap => { From 034f5b16bdaa0f9079c1322664cd8f4cc2809b1d Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sat, 9 May 2020 01:38:23 -0600 Subject: [PATCH 16/18] add at_end_of_stream/{0,1} --- src/prolog/lib/builtins.pl | 59 +++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/src/prolog/lib/builtins.pl b/src/prolog/lib/builtins.pl index 091aa73d..ba1f41fb 100644 --- a/src/prolog/lib/builtins.pl +++ b/src/prolog/lib/builtins.pl @@ -41,26 +41,28 @@ user:term_expansion((:- op(Pred, Spec, [Op | OtherOps])), OpResults) :- :- module(builtins, [(=)/2, (\=)/2, (\+)/1, (',')/2, (->)/2, (;)/2, (=..)/2, (:)/2, (:)/3, (:)/4, (:)/5, (:)/6, (:)/7, (:)/8, (:)/9, (:)/10, (:)/11, (:)/12, - abolish/1, asserta/1, assertz/1, atom_chars/2, - atom_codes/2, atom_concat/3, atom_length/2, - bagof/3, catch/3, char_code/2, clause/2, close/1, - close/2, current_input/1, current_output/1, - current_op/3, current_predicate/1, - 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_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, 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, stream_property/2, sub_atom/5, - subsumes_term/2, term_variables/2, throw/1, - true/0, unify_with_occurs_check/2, write/1, + abolish/1, asserta/1, assertz/1, + at_end_of_stream/0, at_end_of_stream/1, + atom_chars/2, atom_codes/2, atom_concat/3, + atom_length/2, bagof/3, catch/3, char_code/2, + clause/2, close/1, close/2, current_input/1, + current_output/1, current_op/3, + current_predicate/1, 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_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, + 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, + stream_property/2, sub_atom/5, subsumes_term/2, + term_variables/2, throw/1, true/0, + unify_with_occurs_check/2, write/1, write_canonical/1, write_term/2, write_term/3, writeq/1]). @@ -1301,3 +1303,20 @@ stream_property(S, P) :- check_stream_property(P, PropertyName, PropertyValue), '$stream_property'(S, PropertyName, PropertyValue) ). + + +at_end_of_stream(S_or_a) :- + ( atom(S_or_a) -> + stream_property(S, alias(A)) + ; S = S_or_a + ), + stream_property(S, end_of_stream(E)), + !, + ( E = at ; E = past ). + +at_end_of_stream :- + current_input(S), + stream_property(S, end_of_stream(E)), + !, + ( E = at ; E = past ). + From 6f927b99411c629947a0066781d96492ff35abd3 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sat, 9 May 2020 14:20:32 -0600 Subject: [PATCH 17/18] add set_stream_position/2 --- src/prolog/clause_types.rs | 3 ++ src/prolog/lib/builtins.pl | 17 ++++++++--- src/prolog/machine/machine_errors.rs | 3 ++ src/prolog/machine/streams.rs | 25 ++++++++++++++++ src/prolog/machine/system_calls.rs | 45 +++++++++++++++++++++++++++- 5 files changed, 88 insertions(+), 5 deletions(-) diff --git a/src/prolog/clause_types.rs b/src/prolog/clause_types.rs index b4f1ee85..36f278df 100644 --- a/src/prolog/clause_types.rs +++ b/src/prolog/clause_types.rs @@ -248,6 +248,7 @@ pub enum SystemClauseType { StoreGlobalVar, StoreGlobalVarWithOffset, StreamProperty, + SetStreamPosition, InferenceLevel, CleanUpBlock, EraseBall, @@ -422,6 +423,7 @@ impl SystemClauseType { &SystemClauseType::SetOutput => clause_name!("$set_output"), &SystemClauseType::SetSeed => clause_name!("$set_seed"), &SystemClauseType::StreamProperty => clause_name!("$stream_property"), + &SystemClauseType::SetStreamPosition => clause_name!("$set_stream_position"), &SystemClauseType::StoreGlobalVar => clause_name!("$store_global_var"), &SystemClauseType::StoreGlobalVarWithOffset => { clause_name!("$store_global_var_with_offset") @@ -575,6 +577,7 @@ impl SystemClauseType { ("$set_input", 1) => Some(SystemClauseType::SetInput), ("$set_output", 1) => Some(SystemClauseType::SetOutput), ("$stream_property", 3) => Some(SystemClauseType::StreamProperty), + ("$set_stream_position", 2) => Some(SystemClauseType::SetStreamPosition), ("$inference_level", 2) => Some(SystemClauseType::InferenceLevel), ("$clean_up_block", 1) => Some(SystemClauseType::CleanUpBlock), ("$erase_ball", 0) => Some(SystemClauseType::EraseBall), diff --git a/src/prolog/lib/builtins.pl b/src/prolog/lib/builtins.pl index ba1f41fb..798e201a 100644 --- a/src/prolog/lib/builtins.pl +++ b/src/prolog/lib/builtins.pl @@ -59,10 +59,10 @@ user:term_expansion((:- op(Pred, Spec, [Op | OtherOps])), OpResults) :- 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, - stream_property/2, sub_atom/5, subsumes_term/2, - term_variables/2, throw/1, true/0, - unify_with_occurs_check/2, write/1, + set_input/1, set_stream_position/2, set_output/1, + setof/3, stream_property/2, sub_atom/5, + subsumes_term/2, term_variables/2, throw/1, + true/0, unify_with_occurs_check/2, write/1, write_canonical/1, write_term/2, write_term/3, writeq/1]). @@ -1320,3 +1320,12 @@ at_end_of_stream :- !, ( E = at ; E = past ). + +set_stream_position(S_or_a, Position) :- + ( var(Position) -> + throw(error(instantiation_error, set_stream_position/2)) + ; integer(Position), Position >= 0 -> + true + ; throw(error(domain_error(stream_position, Position))) + ), + '$set_stream_position'(S_or_a, Position). diff --git a/src/prolog/machine/machine_errors.rs b/src/prolog/machine/machine_errors.rs index d384185b..93c36a73 100644 --- a/src/prolog/machine/machine_errors.rs +++ b/src/prolog/machine/machine_errors.rs @@ -468,9 +468,11 @@ pub enum Permission { Modify, Open, OutputStream, + Reposition, } impl Permission { + #[inline] pub fn as_str(self) -> &'static str { match self { Permission::Access => "access", @@ -479,6 +481,7 @@ impl Permission { Permission::Modify => "modify", Permission::Open => "open", Permission::OutputStream => "output", + Permission::Reposition => "reposition", } } } diff --git a/src/prolog/machine/streams.rs b/src/prolog/machine/streams.rs index 66ad2b32..68b2ec3b 100644 --- a/src/prolog/machine/streams.rs +++ b/src/prolog/machine/streams.rs @@ -27,6 +27,15 @@ impl StreamType { #[inline] pub(crate) fn as_str(&self) -> &'static str { + match self { + StreamType::Binary => "binary_stream", + StreamType::Text => "text_stream", + } + } + + #[inline] + pub(crate) + fn as_property_str(&self) -> &'static str { match self { StreamType::Binary => "binary", StreamType::Text => "text", @@ -296,6 +305,22 @@ impl Stream { } } + #[inline] + pub(crate) + fn set_position(&mut self, position: u64) { + match self.stream_inst.0.borrow_mut().deref_mut() { + (past_end_of_stream, StreamInstance::InputFile(_, ref mut file)) => { + file.seek(SeekFrom::Start(position)).unwrap(); + + if let Ok(metadata) = file.metadata() { + *past_end_of_stream = position > metadata.len(); + } + } + _ => { + } + } + } + #[inline] pub(crate) fn past_end_of_stream(&self) -> bool { diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index 43bfba86..e01008e0 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -4556,6 +4556,49 @@ impl MachineState { } } } + &SystemClauseType::SetStreamPosition => { + let mut stream = self.get_stream_or_alias( + self[temp_v!(1)], + indices, + "set_stream_position", + 2, + )?; + + if !stream.options.reposition { + let stub = MachineError::functor_stub(clause_name!("set_stream_position"), 2); + + let err = MachineError::permission_error( + self.heap.h(), + Permission::Reposition, + "stream", + vec![HeapCellValue::Stream(stream)], + ); + + return Err(self.error_form(err, stub)); + } + + let position = self.store(self.deref(self[temp_v!(2)])); + + let position = + match Number::try_from((position, &self.heap)) { + Ok(Number::Fixnum(n)) => { + n as u64 + } + Ok(Number::Integer(n)) => { + if let Some(n) = n.to_u64() { + n + } else { + self.fail = true; + return Ok(()); + } + } + _ => { + unreachable!() + } + }; + + stream.set_position(position); + } &SystemClauseType::StreamProperty => { let mut stream = self.get_stream_or_alias( self[temp_v!(1)], @@ -4648,7 +4691,7 @@ impl MachineState { } "type" => { HeapCellValue::Atom( - clause_name!(stream.options.stream_type.as_str()), + clause_name!(stream.options.stream_type.as_property_str()), None, ) } From 22298ddb410bb77862f831039cca74e5d2fa6d60 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sat, 9 May 2020 14:26:08 -0600 Subject: [PATCH 18/18] update README.md --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1df51017..1eb98982 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,10 @@ Extend Scryer Prolog to include the following, among other features: - [x] A _redone_ representation of strings as difference lists of characters, using a packed internal representation. - [x] clp(B) and clp(ℤ) as builtin libraries. -- [ ] Streams and predicates for stream control (_in progress_). +- [x] Streams and predicates for stream control. + - [x] A simple sockets library representing TCP connections as streams. +- [ ] Incremental compilation and loading process, newly written, + primarily in Prolog. (_in progress_) - [ ] A compacting garbage collector satisfying the five properties of "Precise Garbage Collection in Prolog." - [ ] Mode declarations. @@ -372,6 +375,8 @@ The modules that ship with Scryer Prolog are also called Provides *delimited continuations* via `reset/3` and `shift/1`. * [`random`](src/prolog/lib/random.pl) Probabilistic predicates and random number generators. +* [`sockets`](src/prolog/lib/sockets.pl) + Predicates for opening and accepting TCP connections as streams. To read contents of external files, use `phrase_from_file/2` from [`library(pio)`](src/prolog/lib/pio.pl) to apply a DCG to