add open/3, write_term/3
This commit is contained in:
@@ -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()))
|
||||
})?);
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -723,10 +723,10 @@ impl MachineState {
|
||||
op_dir: &'a OpDir,
|
||||
) -> Result<Option<HCPrinter<'a, PrinterOutputter>>, 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<Addr, String> = IndexMap::new();
|
||||
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -33,7 +33,8 @@ pub enum EOFAction {
|
||||
pub enum StreamInstance {
|
||||
Bytes(Cursor<Vec<u8>>),
|
||||
DynReadSource(Box<dyn Read>),
|
||||
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<File> 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<Stream, MachineStub>
|
||||
{
|
||||
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<usize> {
|
||||
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<usize> {
|
||||
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,
|
||||
|
||||
@@ -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)];
|
||||
|
||||
Reference in New Issue
Block a user