- pattern matching seems to be correct - dynamic dispatch works with the to_string example - template strings as a last-minute addition - interpreter revamp, virtual stack for abort safety
192 lines
6.3 KiB
Rust
192 lines
6.3 KiB
Rust
use std::ffi::OsString;
|
|
use std::fs::File;
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use super::osstring::os_string_lib;
|
|
use crate::facade::system::{IntoSystem, System};
|
|
use crate::foreign::atom::Atomic;
|
|
use crate::foreign::cps_box::CPSBox;
|
|
use crate::foreign::error::ExternResult;
|
|
use crate::foreign::inert::{Inert, InertPayload};
|
|
use crate::foreign::process::Unstable;
|
|
use crate::foreign::to_clause::ToClause;
|
|
use crate::gen::tpl;
|
|
use crate::gen::traits::Gen;
|
|
use crate::gen::tree::{atom_ent, xfn_ent, ConstTree};
|
|
use crate::interpreter::gen_nort::nort_gen;
|
|
use crate::interpreter::handler::HandlerTable;
|
|
use crate::interpreter::nort::{Clause, Expr};
|
|
use crate::libs::io::instances::io_error_handler;
|
|
use crate::libs::io::{Sink, Source};
|
|
use crate::libs::scheduler::system::{SeqScheduler, SharedHandle};
|
|
use crate::libs::std::runtime_error::RuntimeError;
|
|
use crate::utils::combine::Combine;
|
|
use crate::virt_fs::DeclTree;
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct ReadFileCmd(OsString);
|
|
impl InertPayload for ReadFileCmd {
|
|
const TYPE_STR: &'static str = "readfile command";
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct ReadDirCmd(OsString);
|
|
impl InertPayload for ReadDirCmd {
|
|
const TYPE_STR: &'static str = "readdir command";
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct WriteFile {
|
|
name: OsString,
|
|
append: bool,
|
|
}
|
|
impl InertPayload for WriteFile {
|
|
const TYPE_STR: &'static str = "writefile command";
|
|
}
|
|
|
|
#[must_use]
|
|
fn read_file(sched: &SeqScheduler, cmd: &CPSBox<ReadFileCmd>) -> Expr {
|
|
let (ReadFileCmd(name), succ, fail, cont) = cmd.unpack3();
|
|
let name = name.clone();
|
|
let cancel = sched.run_orphan(
|
|
move |_| File::open(name),
|
|
|file, _| match file {
|
|
Err(e) => vec![io_error_handler(e, fail)],
|
|
Ok(f) => {
|
|
let source_handle = SharedHandle::wrap(Source::new(Box::new(f)));
|
|
let tpl = tpl::A(tpl::Slot, tpl::V(Inert(source_handle)));
|
|
vec![tpl.template(nort_gen(succ.location()), [succ])]
|
|
},
|
|
},
|
|
);
|
|
let tpl = tpl::A(tpl::Slot, tpl::V(CPSBox::new(1, cancel)));
|
|
tpl.template(nort_gen(cont.location()), [cont])
|
|
}
|
|
|
|
#[must_use]
|
|
fn read_dir(sched: &SeqScheduler, cmd: &CPSBox<ReadDirCmd>) -> Expr {
|
|
let (ReadDirCmd(name), succ, fail, cont) = cmd.unpack3();
|
|
let name = name.clone();
|
|
let cancel = sched.run_orphan(
|
|
move |_| {
|
|
Path::new(&name)
|
|
.read_dir()?
|
|
.map(|r| r.and_then(|e| Ok((e.file_name(), e.file_type()?.is_dir()))))
|
|
.collect()
|
|
},
|
|
|items: std::io::Result<Vec<(OsString, bool)>>, _| match items {
|
|
Err(e) => vec![io_error_handler(e, fail)],
|
|
Ok(os_namev) => {
|
|
let converted = (os_namev.into_iter())
|
|
.map(|(n, d)| {
|
|
Ok((Inert(n).atom_expr(succ.location()), Inert(d).atom_expr(succ.location())))
|
|
})
|
|
.collect::<Result<Vec<_>, Clause>>();
|
|
match converted {
|
|
Err(e) => {
|
|
let e = e.to_expr(fail.location());
|
|
let tpl = tpl::A(tpl::Slot, tpl::Slot);
|
|
vec![tpl.template(nort_gen(fail.location()), [fail, e])]
|
|
},
|
|
Ok(names) => {
|
|
let names = names.to_expr(succ.location());
|
|
let tpl = tpl::A(tpl::Slot, tpl::Slot);
|
|
vec![tpl.template(nort_gen(succ.location()), [succ, names])]
|
|
},
|
|
}
|
|
},
|
|
},
|
|
);
|
|
let tpl = tpl::A(tpl::Slot, tpl::V(CPSBox::new(1, cancel)));
|
|
tpl.template(nort_gen(cont.location()), [cont])
|
|
}
|
|
|
|
#[must_use]
|
|
fn write_file(sched: &SeqScheduler, cmd: &CPSBox<WriteFile>) -> Expr {
|
|
let (cmd, succ, fail, cont) = cmd.unpack3();
|
|
let cmd = cmd.clone();
|
|
let cancel = sched.run_orphan(
|
|
move |_| File::options().write(true).append(cmd.append).open(&cmd.name),
|
|
|file, _| match file {
|
|
Err(e) => vec![io_error_handler(e, fail)],
|
|
Ok(f) => {
|
|
let sink_handle = SharedHandle::wrap(Box::new(f) as Sink);
|
|
let tpl = tpl::A(tpl::Slot, tpl::V(Inert(sink_handle)));
|
|
vec![tpl.template(nort_gen(succ.location()), [succ])]
|
|
},
|
|
},
|
|
);
|
|
let tpl = tpl::A(tpl::Slot, tpl::V(CPSBox::new(1, cancel)));
|
|
tpl.template(nort_gen(cont.location()), [cont])
|
|
}
|
|
|
|
fn open_file_read_cmd(name: OsString) -> CPSBox<ReadFileCmd> { CPSBox::new(3, ReadFileCmd(name)) }
|
|
|
|
fn read_dir_cmd(name: OsString) -> CPSBox<ReadDirCmd> { CPSBox::new(3, ReadDirCmd(name)) }
|
|
|
|
fn open_file_write_cmd(name: OsString) -> CPSBox<WriteFile> {
|
|
CPSBox::new(3, WriteFile { name, append: false })
|
|
}
|
|
|
|
fn open_file_append_cmd(name: OsString) -> CPSBox<WriteFile> {
|
|
CPSBox::new(3, WriteFile { name, append: true })
|
|
}
|
|
|
|
fn join_paths(root: OsString, sub: OsString) -> OsString {
|
|
let mut path = PathBuf::from(root);
|
|
path.push(sub);
|
|
path.into_os_string()
|
|
}
|
|
|
|
fn pop_path(path: Inert<OsString>) -> Option<(Inert<OsString>, Inert<OsString>)> {
|
|
let mut path = PathBuf::from(path.0);
|
|
let sub = path.file_name()?.to_owned();
|
|
debug_assert!(path.pop(), "file_name above returned Some");
|
|
Some((Inert(path.into_os_string()), Inert(sub)))
|
|
}
|
|
|
|
/// A rudimentary system to read and write files.
|
|
#[derive(Clone)]
|
|
pub struct DirectFS {
|
|
scheduler: SeqScheduler,
|
|
}
|
|
impl DirectFS {
|
|
/// Create a new instance of the system.
|
|
pub fn new(scheduler: SeqScheduler) -> Self { Self { scheduler } }
|
|
}
|
|
|
|
impl IntoSystem<'static> for DirectFS {
|
|
fn into_system(self) -> System<'static> {
|
|
let mut handlers = HandlerTable::new();
|
|
let sched = self.scheduler.clone();
|
|
handlers.register(move |cmd| read_file(&sched, cmd));
|
|
let sched = self.scheduler.clone();
|
|
handlers.register(move |cmd| read_dir(&sched, cmd));
|
|
let sched = self.scheduler;
|
|
handlers.register(move |cmd| write_file(&sched, cmd));
|
|
System {
|
|
name: "system::directfs",
|
|
code: DeclTree::empty(),
|
|
prelude: Vec::new(),
|
|
lexer_plugins: vec![],
|
|
line_parsers: vec![],
|
|
constants: ConstTree::ns("system::fs", [ConstTree::tree([
|
|
xfn_ent("read_file", [open_file_read_cmd]),
|
|
xfn_ent("read_dir", [read_dir_cmd]),
|
|
xfn_ent("write_file", [open_file_write_cmd]),
|
|
xfn_ent("append_file", [open_file_append_cmd]),
|
|
xfn_ent("join_paths", [join_paths]),
|
|
xfn_ent("pop_path", [pop_path]),
|
|
atom_ent("cwd", [Unstable::new(|_| -> ExternResult<_> {
|
|
let path =
|
|
std::env::current_dir().map_err(|e| RuntimeError::ext(e.to_string(), "reading CWD"))?;
|
|
Ok(Inert(path.into_os_string()))
|
|
})]),
|
|
])])
|
|
.combine(os_string_lib())
|
|
.expect("os_string library and directfs conflict"),
|
|
handlers,
|
|
}
|
|
}
|
|
}
|