-
Notifications
You must be signed in to change notification settings - Fork 442
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #236 from HigherOrderCO/feature/sc-509/implement-h…
…vm1-builtins Implement remaining `hvm1` builtins.
- Loading branch information
Showing
27 changed files
with
566 additions
and
113 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
use std::sync::{Arc, Mutex}; | ||
|
||
use hvmc::{ | ||
host::Host, | ||
run::{Def, LabSet, Mode, Net, Port, Tag, Wire}, | ||
stdlib::{ArcDef, AsArcDef, HostedDef}, | ||
}; | ||
|
||
use crate::builtins::util::AsDefFunction; | ||
|
||
use super::util::FunctionLikeHosted; | ||
|
||
pub(crate) fn add_exit_def(host: Arc<Mutex<Host>>) { | ||
/// `HVM.exit`. | ||
/// Implements the following reduction rule | ||
/// | ||
/// ```txt | ||
/// ExitDef ~ (a b) | ||
/// --- | ||
/// ExitDefGetStatus ~ a & <dangling> ~ b | ||
/// ``` | ||
struct ExitDef; | ||
|
||
impl AsDefFunction for ExitDef { | ||
fn call<M: Mode>(&self, net: &mut Net<M>, input: Wire, output: Wire) { | ||
// Purposefully deadlock "output" to prevent further reduction | ||
drop(output); | ||
|
||
net.link_wire_port(input, ArcDef::new_arc_port(LabSet::ALL, ExitDefGetStatus)); | ||
} | ||
} | ||
|
||
/// Def that scans for a number, and exits the program with that number as the exit code | ||
/// | ||
/// ```txt | ||
/// ExitDefGetStatus ~ {lab a b} // also works on match and op | ||
/// --- | ||
/// ExitDefGetStatus ~ a & ExitDefGetStatus ~ b | ||
/// | ||
/// ExitDefGetStatus ~ #num | ||
/// --- | ||
/// process::exit(num) | ||
/// | ||
/// ExitDefGetStatus ~ other | ||
/// --- | ||
/// process::exit(-1) | ||
/// ``` | ||
struct ExitDefGetStatus; | ||
impl AsArcDef for ExitDefGetStatus { | ||
fn call<M: Mode>(slf: Arc<Def<Self>>, net: &mut Net<M>, port: Port) { | ||
match port.tag() { | ||
Tag::Red | Tag::Var | Tag::Num => { | ||
std::process::exit(port.num().try_into().unwrap_or(-1)); | ||
} | ||
Tag::Ref => { | ||
std::process::exit(-1); | ||
} | ||
_ => { | ||
// Commute | ||
let node = port.consume_node(); | ||
net.link_wire_port(node.p1, ArcDef::to_port(slf.clone())); | ||
net.link_wire_port(node.p2, ArcDef::to_port(slf)); | ||
} | ||
} | ||
} | ||
} | ||
|
||
host | ||
.lock() | ||
.unwrap() | ||
.insert_def("HVM.exit", unsafe { HostedDef::new_hosted(LabSet::ALL, FunctionLikeHosted(ExitDef)) }); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
use std::sync::{Arc, Mutex}; | ||
|
||
use hvmc::{ | ||
ast, dispatch_dyn_net, | ||
host::Host, | ||
run::{LabSet, Port, Trg, Wire}, | ||
stdlib::{ArcDef, HostedDef}, | ||
}; | ||
use term_to_net::term_to_compat_net; | ||
|
||
use crate::{ | ||
builtins::util::{AsDefFunction, FunctionLike, FunctionLikeHosted}, | ||
net::net_to_hvmc::net_to_hvmc, | ||
readback_hvmc, | ||
term::{ | ||
term_to_net::{self, Labels}, | ||
AdtEncoding, Book, Term, | ||
}, | ||
}; | ||
|
||
#[derive(Clone)] | ||
struct ReadbackData { | ||
book: Arc<Book>, | ||
host: Arc<Mutex<Host>>, | ||
labels: Arc<Labels>, | ||
adt_encoding: AdtEncoding, | ||
} | ||
|
||
const VICIOUS_CIRCLE_MSG: &str = "Found vicious circle"; | ||
const FILENAME_NOT_VALID_MSG: &str = "Filename is not valid string."; | ||
const CONTENTS_NOT_VALID_MSG: &str = "Content is not valid string."; | ||
const FS_ERROR_MSG: &str = "Filesystem error: "; | ||
const INVALID_UTF8_MSG: &str = "UTF-8 error: "; | ||
|
||
/// Adds the filesystem definitions (`HVM.store` and `HVM.load`) to the book | ||
pub(crate) fn add_fs_defs( | ||
book: Arc<Book>, | ||
host: Arc<Mutex<Host>>, | ||
labels: Arc<Labels>, | ||
adt_encoding: AdtEncoding, | ||
) { | ||
#[derive(Clone)] | ||
struct Fs0 { | ||
readback_data: ReadbackData, | ||
save: bool, | ||
} | ||
impl AsDefFunction for Fs0 { | ||
fn call<M: hvmc::run::Mode>(&self, net: &mut hvmc::run::Net<M>, input: Wire, output: Wire) { | ||
let (wire, port) = net.create_wire(); | ||
let slf = self.clone(); | ||
let readback_node = hvmc::stdlib::readback(slf.readback_data.host.clone(), port, move |net, tree| { | ||
dispatch_dyn_net!(net => { | ||
net.link_wire_port(wire, Port::ERA); | ||
let (term, _errs) = readback_hvmc(&ast::Net { root: tree,redexes: vec![]} , &slf.readback_data.book, &slf.readback_data.labels, false, slf.readback_data.adt_encoding); | ||
let filename = if let Term::Str { ref val } = term { | ||
Some(val.to_string()) | ||
} else { | ||
None | ||
}; | ||
net.link_wire_port(output, ArcDef::new_arc_port(LabSet::ALL, FunctionLike(Fs1 { readback_data: slf.readback_data, save: slf.save, filename }))); | ||
}) | ||
}); | ||
net.link_wire_port(input, readback_node); | ||
} | ||
} | ||
|
||
#[derive(Clone)] | ||
struct Fs1 { | ||
readback_data: ReadbackData, | ||
save: bool, | ||
filename: Option<String>, | ||
} | ||
impl AsDefFunction for Fs1 { | ||
fn call<M: hvmc::run::Mode>(&self, net: &mut hvmc::run::Net<M>, input: Wire, output: Wire) { | ||
if self.save { | ||
let (wire, port) = net.create_wire(); | ||
let slf = self.clone(); | ||
let mut labels = (*self.readback_data.labels).clone(); | ||
let host = self.readback_data.host.clone(); | ||
let readback_node = hvmc::stdlib::readback( | ||
self.readback_data.host.clone(), | ||
port, | ||
move |net, tree| { | ||
dispatch_dyn_net!(net => { | ||
let (term, _errs) = readback_hvmc(&ast::Net { root: tree,redexes: vec![]} , &slf.readback_data.book, &slf.readback_data.labels, false, slf.readback_data.adt_encoding); | ||
let contents = if let Term::Str { ref val } = term { | ||
Some(val.to_string()) | ||
} else { | ||
None | ||
}; | ||
// Save file | ||
let result = match (slf.filename, contents) { | ||
(None, _) => { | ||
Term::encode_err(Term::encode_str(FILENAME_NOT_VALID_MSG)) | ||
}, | ||
(_, None) => { | ||
Term::encode_err(Term::encode_str(CONTENTS_NOT_VALID_MSG)) | ||
}, | ||
(Some(filename), Some(contents)) => { | ||
match std::fs::write(filename, contents) { | ||
Ok(_) => Term::encode_ok(Term::Era), | ||
Err(e) => Term::encode_err(Term::encode_str(&format!("{FS_ERROR_MSG}{e}"))), | ||
} | ||
}, | ||
}; | ||
let result = term_to_compat_net(&result, &mut labels); | ||
match net_to_hvmc(&result) { | ||
Ok(result) => { | ||
// Return λx (x result) | ||
let app = net.create_node(hvmc::run::Tag::Ctr, 0); | ||
let lam = net.create_node(hvmc::run::Tag::Ctr, 0); | ||
host.lock().unwrap().encode_net(net, Trg::port(app.p1), &result); | ||
net.link_wire_port(wire, Port::ERA); | ||
net.link_port_port(app.p0, lam.p1); | ||
net.link_port_port(app.p2, lam.p2); | ||
|
||
net.link_wire_port(output, lam.p0); | ||
}, | ||
Err(_) => { | ||
// If this happens, we can't even report an error to | ||
// the hvm program, so simply print an error, and plug in an ERA | ||
// The other option would be panicking. | ||
eprintln!("{VICIOUS_CIRCLE_MSG}"); | ||
net.link_wire_port(output, Port::ERA); | ||
}, | ||
} | ||
}) | ||
}, | ||
); | ||
net.link_wire_port(input, readback_node); | ||
} else { | ||
let app = net.create_node(hvmc::run::Tag::Ctr, 0); | ||
let result = self | ||
.filename | ||
.as_ref() | ||
.ok_or(FILENAME_NOT_VALID_MSG.to_owned()) | ||
.and_then(|filename| std::fs::read(filename).map_err(|e| format!("{FS_ERROR_MSG}{e}"))) | ||
.and_then(|x| { | ||
std::str::from_utf8(&x).map(|x| x.to_string()).map_err(|e| format!("{INVALID_UTF8_MSG}{e}")) | ||
}); | ||
let result = match result { | ||
Ok(s) => Term::encode_ok(Term::encode_str(&s)), | ||
Err(s) => Term::encode_err(Term::encode_str(&s)), | ||
}; | ||
let mut labels = (*self.readback_data.labels).clone(); | ||
let result = term_to_compat_net(&result, &mut labels); | ||
if let Ok(result) = net_to_hvmc(&result) { | ||
self.readback_data.host.lock().unwrap().encode_net(net, Trg::port(app.p1), &result); | ||
} else { | ||
eprintln!("{VICIOUS_CIRCLE_MSG}"); | ||
net.link_port_port(Port::ERA, app.p1); | ||
} | ||
net.link_wire_port(output, app.p2); | ||
net.link_wire_port(input, app.p0); | ||
} | ||
} | ||
} | ||
let readback_data = ReadbackData { book, host: host.clone(), labels, adt_encoding }; | ||
host.lock().unwrap().insert_def("HVM.store", unsafe { | ||
HostedDef::new_hosted( | ||
LabSet::ALL, | ||
FunctionLikeHosted(Fs0 { readback_data: readback_data.clone(), save: true }), | ||
) | ||
}); | ||
host.lock().unwrap().insert_def("HVM.load", unsafe { | ||
HostedDef::new_hosted( | ||
LabSet::ALL, | ||
FunctionLikeHosted(Fs0 { readback_data: readback_data.clone(), save: false }), | ||
) | ||
}); | ||
} |
Oops, something went wrong.