NumberToChars,
NumberToCodes,
OpDeclaration,
+ Open,
PartialStringTail,
PointsToContinuationResetMarker,
REPL(REPLCodePtr),
&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")
("$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),
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,
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
'$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)
-
-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))
- ).
-inst_member_or([], Y, Y).
+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).
+
+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)), _))
+ ).
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).
( 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).
).
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)
+ ).
+
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()))
})?);
MachineError {
stub,
location: None,
- from: ErrorProvenance::Constructed,
+ from: ErrorProvenance::Received,
}
}
ExistenceError::Stream(culprit) => {
#[derive(Debug, Clone, Copy)]
pub enum DomainErrorType {
+ IOMode,
NotLessThanZero,
Order,
Stream,
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",
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());
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();
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,
};
pub enum StreamInstance {
Bytes(Cursor<Vec<u8>>),
DynReadSource(Box<dyn Read>),
- File(File),
+ InputFile(File),
+ OutputFile(File),
Null,
ReadlineStream(ReadlineStream),
// Stdin,
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),
}
}
-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)
}
}
+ #[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)
StreamInstance::Bytes(_) |
StreamInstance::ReadlineStream(_) |
StreamInstance::DynReadSource(_) |
- StreamInstance::File(_) => {
+ StreamInstance::InputFile(_) => {
true
- }
+ }
_ => {
false
}
StreamInstance::Stdout
| StreamInstance::TcpStream(_)
| StreamInstance::Bytes(_)
- | StreamInstance::File(_) => {
+ | StreamInstance::OutputFile(_) => {
true
}
_ => {
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()
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,
+ ));
+ }
}
})
}
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) => {
stdin().read(buf)
}
*/
- StreamInstance::Stdout | StreamInstance::Null => {
+ StreamInstance::OutputFile(_) | StreamInstance::Stdout | StreamInstance::Null => {
Err(std::io::Error::new(
ErrorKind::PermissionDenied,
StreamError::ReadFromOutputStream,
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) => {
StreamInstance::Stdout => {
stdout().write(buf)
}
- _ => {
+ StreamInstance::DynReadSource(_) | StreamInstance::ReadlineStream(_) |
+ StreamInstance::InputFile(_) | StreamInstance::Null => {
Err(std::io::Error::new(
ErrorKind::PermissionDenied,
StreamError::WriteToInputStream,
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) => {
StreamInstance::Stdout => {
stdout().flush()
}
- _ => {
+ StreamInstance::DynReadSource(_) | StreamInstance::ReadlineStream(_) |
+ StreamInstance::InputFile(_) | StreamInstance::Null => {
Err(std::io::Error::new(
ErrorKind::PermissionDenied,
StreamError::FlushToInputStream,
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;
}
};
}
+ &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))
}
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)? {
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)];
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!()
+ }
+ }
+ }
+}