]> Repositorios git - scryer-prolog.git/commitdiff
add open/3, write_term/3
authorMark Thom <[email protected]>
Mon, 4 May 2020 20:50:26 +0000 (14:50 -0600)
committerMark Thom <[email protected]>
Mon, 4 May 2020 20:50:26 +0000 (14:50 -0600)
src/prolog/clause_types.rs
src/prolog/lib/builtins.pl
src/prolog/machine/compile.rs
src/prolog/machine/machine_errors.rs
src/prolog/machine/machine_state.rs
src/prolog/machine/mod.rs
src/prolog/machine/streams.rs
src/prolog/machine/system_calls.rs
src/prolog/macros.rs

index b5bfae0e9d42396d6f8d20b67d1fdd2463f72c16..ce5d9e68f6037324675293a2a6fe304dad11eb38 100644 (file)
@@ -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,
index 1d5080e576dfcb52b0b6e82fdf666b89a0b1a0d2..2ec5680e1b00b46cc0cd26dc17d3778af6a5fd29 100644 (file)
@@ -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)
-
-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).
@@ -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)
+    ).
+
index 8882e2ff329a1f02ae658d2aead3d3250a4bcf4a..a887cfb6573eee6f97d791be089ac945e853b07d 100644 (file)
@@ -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()))
     })?);
 
index a46345e27b28b246726a9c4a5276ff6ba050800d..4a022316b7dbbec9adba76989d9ccf0e8fcce19d 100644 (file)
@@ -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",
index 46fc5ed93e658f17e5d4e61275530a1f0b2abd4d..b86496b36c5041da2696c1bb8493ea23d66621a4 100644 (file)
@@ -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();
 
index f4412340d1ca3bb8486d3313660a37b317caa452..781fa8a3295420bb4dd7ef63c00b83e4d16c44e3 100644 (file)
@@ -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,
             };
 
index 4e4af96e1498f17dcc88c3efc3b34e1395b8fe9a..8cd7cbf0f29854c34431f4269ec7630bf8c97f7e 100644 (file)
@@ -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,
index 1806d63931d3886ed795205b77ae6922f2ed1040..a0e10b25745d19cf21d9c22e6ab0a39e45d2c2ae 100644 (file)
@@ -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)];
index 834ad9f8a2cbbb9919ab1cd6f11355f9c72caf49..6a3e46c1b65210e3b2e277b89114e8c5a824c8e5 100644 (file)
@@ -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!()
+            }
+        }
+    }
+}