mirror of
https://github.com/HigherOrderCO/Bend.git
synced 2024-09-11 19:57:20 +03:00
[sc-119] Remove anyhow dependency
This commit is contained in:
parent
600e4536f8
commit
e8e81f331d
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -62,12 +62,6 @@ dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.75"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||
|
||||
[[package]]
|
||||
name = "beef"
|
||||
version = "0.5.2"
|
||||
@ -205,7 +199,6 @@ source = "git+https://github.com/HigherOrderCO/hvm-core.git#d802283e7544785af5a2
|
||||
name = "hvm-lang"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chumsky",
|
||||
"clap",
|
||||
"hvm-core",
|
||||
|
@ -18,7 +18,6 @@ default = ["cli"]
|
||||
cli = ["dep:clap"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.72"
|
||||
chumsky = "= 1.0.0-alpha.4"
|
||||
clap = { version = "4.4.1", features = ["derive"], optional = true }
|
||||
hvm-core = { git = "https://github.com/HigherOrderCO/hvm-core.git" }
|
||||
|
16
src/lib.rs
16
src/lib.rs
@ -4,7 +4,7 @@ use hvmc::{
|
||||
ast::{book_to_runtime, name_to_val, net_from_runtime, /*show_net,*/ Net},
|
||||
run::Val,
|
||||
};
|
||||
use net::{hvmc_to_net, nets_to_hvm_core};
|
||||
use net::{hvmc_to_net::hvmc_to_net, net_to_hvmc::nets_to_hvmc};
|
||||
use std::{collections::HashMap, time::Instant};
|
||||
use term::{book_to_nets, net_to_term::net_to_term_non_linear, Book, DefId, DefNames, Term};
|
||||
|
||||
@ -13,24 +13,24 @@ pub mod term;
|
||||
|
||||
pub use term::load_book::load_file_to_book;
|
||||
|
||||
pub fn check_book(mut book: Book) -> anyhow::Result<()> {
|
||||
pub fn check_book(mut book: Book) -> Result<(), String> {
|
||||
// TODO: Do the checks without having to do full compilation
|
||||
compile_book(&mut book)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compile_book(book: &mut Book) -> anyhow::Result<(hvmc::ast::Book, HashMap<Val, DefId>)> {
|
||||
pub fn compile_book(book: &mut Book) -> Result<(hvmc::ast::Book, HashMap<Val, DefId>), String> {
|
||||
let main = book.check_has_main()?;
|
||||
book.check_shared_names()?;
|
||||
book.resolve_refs();
|
||||
book.check_unbound_vars()?;
|
||||
book.make_var_names_unique();
|
||||
book.linearize_vars()?;
|
||||
book.linearize_vars();
|
||||
book.detach_supercombinators();
|
||||
book.simplify_ref_to_ref()?;
|
||||
book.prune(main);
|
||||
let (nets, id_to_hvmc_name) = book_to_nets(book, main)?;
|
||||
let core_book = nets_to_hvm_core(nets, &id_to_hvmc_name)?;
|
||||
let (nets, id_to_hvmc_name) = book_to_nets(book, main);
|
||||
let core_book = nets_to_hvmc(nets, &id_to_hvmc_name)?;
|
||||
let hvmc_name_to_id = id_to_hvmc_name.into_iter().map(|(k, v)| (v, k)).collect();
|
||||
Ok((core_book, hvmc_name_to_id))
|
||||
}
|
||||
@ -52,10 +52,10 @@ pub fn run_compiled(book: &hvmc::ast::Book, mem_size: usize) -> (Net, RunStats)
|
||||
(net, stats)
|
||||
}
|
||||
|
||||
pub fn run_book(mut book: Book, mem_size: usize) -> anyhow::Result<(Term, DefNames, RunInfo)> {
|
||||
pub fn run_book(mut book: Book, mem_size: usize) -> Result<(Term, DefNames, RunInfo), String> {
|
||||
let (compiled, hvmc_name_to_id) = compile_book(&mut book)?;
|
||||
let (res_lnet, stats) = run_compiled(&compiled, mem_size);
|
||||
let net = hvmc_to_net(&res_lnet, &|val| hvmc_name_to_id[&val])?;
|
||||
let net = hvmc_to_net(&res_lnet, &|val| hvmc_name_to_id[&val]);
|
||||
let (res_term, valid_readback) = net_to_term_non_linear(&net, &book);
|
||||
let info = RunInfo { stats, valid_readback, net: res_lnet };
|
||||
Ok((res_term, book.def_names, info))
|
||||
|
@ -32,19 +32,19 @@ enum Mode {
|
||||
Run,
|
||||
}
|
||||
|
||||
fn mem_parser(arg: &str) -> anyhow::Result<usize> {
|
||||
fn mem_parser(arg: &str) -> Result<usize, String> {
|
||||
let (base, mult) = match arg.to_lowercase().chars().last() {
|
||||
None => return Err(anyhow::anyhow!("Mem size argument is empty")),
|
||||
None => return Err("Mem size argument is empty".to_string()),
|
||||
Some('k') => (&arg[0 .. arg.len() - 1], 1 << 10),
|
||||
Some('m') => (&arg[0 .. arg.len() - 1], 1 << 20),
|
||||
Some('g') => (&arg[0 .. arg.len() - 1], 1 << 30),
|
||||
Some(_) => (arg, 1),
|
||||
};
|
||||
let base = base.parse::<usize>()?;
|
||||
let base = base.parse::<usize>().map_err(|e| e.to_string())?;
|
||||
Ok(base * mult)
|
||||
}
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
fn main() -> Result<(), String> {
|
||||
#[cfg(not(feature = "cli"))]
|
||||
compile_error!("The 'cli' feature is needed for the hvm-lang cli");
|
||||
|
||||
|
@ -5,10 +5,9 @@ use hvmc::{
|
||||
run::Val,
|
||||
};
|
||||
|
||||
pub fn hvmc_to_net(net: &Net, hvmc_name_to_id: &impl Fn(Val) -> DefId) -> anyhow::Result<INet> {
|
||||
pub fn hvmc_to_net(net: &Net, hvmc_name_to_id: &impl Fn(Val) -> DefId) -> INet {
|
||||
let inodes = hvmc_to_inodes(net, hvmc_name_to_id);
|
||||
let compat_net = inodes_to_inet(&inodes);
|
||||
Ok(compat_net)
|
||||
inodes_to_inet(&inodes)
|
||||
}
|
||||
|
||||
fn hvmc_to_inodes(net: &Net, hvmc_name_to_id: &impl Fn(Val) -> DefId) -> INodes {
|
||||
|
@ -1,9 +1,6 @@
|
||||
pub mod hvmc_to_net;
|
||||
pub mod net_to_hvmc;
|
||||
|
||||
pub use hvmc_to_net::hvmc_to_net;
|
||||
pub use net_to_hvmc::{compat_net_to_core, nets_to_hvm_core};
|
||||
|
||||
use crate::term::DefId;
|
||||
use hvmc::run::Val;
|
||||
use NodeKind::*;
|
||||
|
@ -6,18 +6,19 @@ use hvmc::{
|
||||
};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
pub fn nets_to_hvm_core(
|
||||
pub fn nets_to_hvmc(
|
||||
nets: HashMap<String, INet>,
|
||||
id_to_hvmc_name: &HashMap<DefId, Val>,
|
||||
) -> anyhow::Result<Book> {
|
||||
) -> Result<Book, String> {
|
||||
let mut book = Book::new();
|
||||
for (name, inet) in nets {
|
||||
book.insert(name, compat_net_to_core(&inet, &|id| id_to_hvmc_name[&id])?);
|
||||
let net = net_to_hvmc(&inet, &|id| id_to_hvmc_name[&id])?;
|
||||
book.insert(name, net);
|
||||
}
|
||||
Ok(book)
|
||||
}
|
||||
|
||||
pub fn compat_net_to_core(inet: &INet, id_to_hvmc_name: &impl Fn(DefId) -> Val) -> anyhow::Result<Net> {
|
||||
pub fn net_to_hvmc(inet: &INet, id_to_hvmc_name: &impl Fn(DefId) -> Val) -> Result<Net, String> {
|
||||
let (net_root, redxs) = get_tree_roots(inet)?;
|
||||
let mut port_to_var_id: HashMap<Port, VarId> = HashMap::new();
|
||||
let root = if let Some(net_root) = net_root {
|
||||
@ -101,7 +102,7 @@ fn var_or_subtree(
|
||||
type VarId = NodeId;
|
||||
|
||||
/// Returns a list of all the tree node roots in the compat inet.
|
||||
fn get_tree_roots(inet: &INet) -> anyhow::Result<(Option<NodeId>, Vec<[NodeId; 2]>)> {
|
||||
fn get_tree_roots(inet: &INet) -> Result<(Option<NodeId>, Vec<[NodeId; 2]>), String> {
|
||||
let mut redx_roots: Vec<[NodeId; 2]> = vec![];
|
||||
let mut explored_nodes = vec![false; inet.nodes.len()];
|
||||
let mut side_links: Vec<Port> = vec![]; // Links between trees
|
||||
@ -141,12 +142,12 @@ fn go_down_tree(
|
||||
root: NodeId,
|
||||
explored_nodes: &mut [bool],
|
||||
side_links: &mut Vec<Port>,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> Result<(), String> {
|
||||
debug_assert!(!explored_nodes[root as usize], "Explored same tree twice");
|
||||
let mut nodes_to_check = vec![root];
|
||||
while let Some(node_id) = nodes_to_check.pop() {
|
||||
if explored_nodes[node_id as usize] {
|
||||
return Err(anyhow::anyhow!("Cyclic terms are not supported"));
|
||||
return Err("Found term that compiles into an inet with a vicious cycle".to_string());
|
||||
}
|
||||
explored_nodes[node_id as usize] = true;
|
||||
for down_slot in [1, 2] {
|
||||
@ -165,12 +166,12 @@ fn go_down_tree(
|
||||
|
||||
/// Goes up a node tree, starting from some given node.
|
||||
/// Returns the root of this tree and the root of its active pair.
|
||||
fn go_up_tree(inet: &INet, start_node: NodeId) -> anyhow::Result<[NodeId; 2]> {
|
||||
fn go_up_tree(inet: &INet, start_node: NodeId) -> Result<[NodeId; 2], String> {
|
||||
let mut explored_nodes = HashSet::new();
|
||||
let mut crnt_node = start_node;
|
||||
loop {
|
||||
if !explored_nodes.insert(crnt_node) {
|
||||
return Err(anyhow::anyhow!("Cyclic terms are not supported"));
|
||||
return Err("Found term that compiles into an inet with a vicious cycle".to_string());
|
||||
}
|
||||
let up = inet.enter_port(Port(crnt_node, 0));
|
||||
if up.slot() == 0 {
|
||||
|
@ -216,7 +216,7 @@ fn useful(ctx: &Ctx, problem: &mut Problem) -> bool {
|
||||
}
|
||||
|
||||
impl Book {
|
||||
pub fn check_exhaustiveness(&self, def_types: &DefinitionTypes) -> anyhow::Result<()> {
|
||||
pub fn check_exhaustiveness(&self, def_types: &DefinitionTypes) -> Result<(), String> {
|
||||
for def in self.defs.values() {
|
||||
if let Some(def_types) = def_types.get(&def.def_id) {
|
||||
// get the type of each argument
|
||||
@ -253,8 +253,7 @@ impl Book {
|
||||
// if the case is useful that means that the rule is not exhaustive
|
||||
if useful(&ctx, &mut problem) {
|
||||
let def_name = self.def_names.name(&def.def_id).unwrap();
|
||||
|
||||
return Err(anyhow::anyhow!("The definition '{def_name}' is not exhaustive."));
|
||||
return Err(format!("The definition '{def_name}' is not exhaustive."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,13 +6,13 @@ pub mod type_check;
|
||||
pub mod unbound_vars;
|
||||
|
||||
impl Book {
|
||||
pub fn check_has_main(&self) -> anyhow::Result<DefId> {
|
||||
pub fn check_has_main(&self) -> Result<DefId, String> {
|
||||
match (
|
||||
self.def_names.def_id(&Name::new(DefNames::ENTRY_POINT)),
|
||||
self.def_names.def_id(&Name::new(DefNames::HVM1_ENTRY_POINT)),
|
||||
) {
|
||||
(None, None) => Err(anyhow::anyhow!("File has no 'main' definition")),
|
||||
(Some(_), Some(_)) => Err(anyhow::anyhow!("File has both 'Main' and 'main' definitions")),
|
||||
(None, None) => Err("File has no 'main' definition".to_string()),
|
||||
(Some(_), Some(_)) => Err("File has both 'Main' and 'main' definitions".to_string()),
|
||||
(None, Some(main)) | (Some(main), None) => Ok(main),
|
||||
}
|
||||
}
|
||||
|
@ -4,24 +4,24 @@ use crate::term::Book;
|
||||
|
||||
impl Book {
|
||||
/// Checks if exists shared names from definitions, adts and constructors.
|
||||
pub fn check_shared_names(&self) -> Result<(), anyhow::Error> {
|
||||
pub fn check_shared_names(&self) -> Result<(), String> {
|
||||
let mut checked = HashSet::new();
|
||||
|
||||
for adt_name in self.adts.keys() {
|
||||
if !checked.insert(adt_name) {
|
||||
return Err(anyhow::anyhow!("Duplicated name '{adt_name}'"));
|
||||
return Err("Duplicated name '{adt_name}'".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
for ctr_name in self.ctrs.keys() {
|
||||
if !checked.insert(ctr_name) {
|
||||
return Err(anyhow::anyhow!("Duplicated name '{ctr_name}'"));
|
||||
return Err("Duplicated name '{ctr_name}'".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
for def_name in self.def_names.names() {
|
||||
if !checked.insert(def_name) {
|
||||
return Err(anyhow::anyhow!("Duplicated name '{def_name}'"));
|
||||
return Err("Duplicated name '{def_name}'".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ pub type DefinitionTypes = HashMap<DefId, Vec<Type>>;
|
||||
impl Book {
|
||||
/// Returns a HashMap from the definition id to the inferred pattern types
|
||||
/// and checks the rules arities based on the first rule arity.
|
||||
pub fn infer_def_types(&self) -> anyhow::Result<DefinitionTypes> {
|
||||
pub fn infer_def_types(&self) -> Result<DefinitionTypes, String> {
|
||||
let mut def_types = HashMap::new();
|
||||
for def in self.defs.values() {
|
||||
self.infer_def_type(def, &mut def_types)?;
|
||||
@ -26,12 +26,12 @@ impl Book {
|
||||
&self,
|
||||
def: &crate::term::Definition,
|
||||
def_types: &mut HashMap<DefId, Vec<Type>>,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
) -> Result<(), String> {
|
||||
let current_arity = def.arity();
|
||||
let mut arg_types = vec![Type::Any; current_arity];
|
||||
for rule in def.rules.iter() {
|
||||
if rule.arity() != current_arity {
|
||||
return Err(anyhow::anyhow!("Arity error."));
|
||||
return Err("Arity error.".to_string());
|
||||
}
|
||||
for (idx, pat) in rule.pats.iter().enumerate() {
|
||||
match pat {
|
||||
@ -47,7 +47,7 @@ impl Book {
|
||||
let t = Type::Adt(nam.clone());
|
||||
unify(t, idx, &mut arg_types)?;
|
||||
}
|
||||
None => return Err(anyhow::anyhow!("Unknown constructor '{nam}'.")),
|
||||
None => return Err("Unknown constructor '{nam}'.".to_string()),
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -57,11 +57,11 @@ impl Book {
|
||||
}
|
||||
}
|
||||
|
||||
fn unify(t: Type, idx: usize, ctx: &mut [Type]) -> Result<(), anyhow::Error> {
|
||||
fn unify(t: Type, idx: usize, ctx: &mut [Type]) -> Result<(), String> {
|
||||
if ctx[idx] == Type::Any {
|
||||
ctx[idx] = t;
|
||||
} else if ctx[idx] != t.clone() {
|
||||
return Err(anyhow::anyhow!("Type mismatch. Found '{}' expected {}.", t, ctx[idx]));
|
||||
return Err(format!("Type mismatch. Found '{}' expected {}.", t, ctx[idx]));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use std::collections::HashMap;
|
||||
|
||||
impl Book {
|
||||
/// Checks that there are no unbound variables in all definitions.
|
||||
pub fn check_unbound_vars(&self) -> anyhow::Result<()> {
|
||||
pub fn check_unbound_vars(&self) -> Result<(), String> {
|
||||
for def in self.defs.values() {
|
||||
// TODO: Enable when pattern matching is done
|
||||
// def.assert_no_pattern_matching_rules();
|
||||
@ -17,14 +17,14 @@ impl Book {
|
||||
impl Term {
|
||||
/// Checks that all variables are bound.
|
||||
/// Precondition: References have been resolved.
|
||||
pub fn check_unbound_vars(&self) -> anyhow::Result<()> {
|
||||
pub fn check_unbound_vars(&self) -> Result<(), String> {
|
||||
let mut globals = HashMap::new();
|
||||
check_uses(self, &mut HashMap::new(), &mut globals)?;
|
||||
|
||||
// Check global vars
|
||||
for (nam, (declared, used)) in globals.into_iter() {
|
||||
if used && !declared {
|
||||
return Err(anyhow::anyhow!("Unbound unscoped variable '${nam}'"));
|
||||
return Err(format!("Unbound unscoped variable '${nam}'"));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@ -37,7 +37,7 @@ pub fn check_uses<'a>(
|
||||
term: &'a Term,
|
||||
scope: &mut HashMap<&'a Name, Val>,
|
||||
globals: &mut HashMap<&'a Name, (bool, bool)>,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> Result<(), String> {
|
||||
// TODO: Don't stop at the first error
|
||||
match term {
|
||||
Term::Lam { nam, bod } => {
|
||||
@ -47,7 +47,7 @@ pub fn check_uses<'a>(
|
||||
}
|
||||
Term::Var { nam } => {
|
||||
if !scope.contains_key(nam) {
|
||||
return Err(anyhow::anyhow!("Unbound variable '{nam}'"));
|
||||
return Err(format!("Unbound variable '{nam}'"));
|
||||
}
|
||||
}
|
||||
Term::Chn { nam, bod } => {
|
||||
@ -89,7 +89,7 @@ pub fn check_uses<'a>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn push_scope<'a> (nam: Option<&'a Name>, scope: &mut HashMap<&'a Name, Val>) {
|
||||
fn push_scope<'a>(nam: Option<&'a Name>, scope: &mut HashMap<&'a Name, Val>) {
|
||||
if let Some(nam) = nam {
|
||||
if let Some(n_declarations) = scope.get_mut(nam) {
|
||||
*n_declarations += 1;
|
||||
|
@ -3,13 +3,13 @@ use itertools::Itertools;
|
||||
use std::path::Path;
|
||||
|
||||
/// Reads a file and parses to a definition book.
|
||||
pub fn load_file_to_book(path: &Path) -> anyhow::Result<Book> {
|
||||
let code = std::fs::read_to_string(path)?;
|
||||
pub fn load_file_to_book(path: &Path) -> Result<Book, String> {
|
||||
let code = std::fs::read_to_string(path).map_err(|e| e.to_string())?;
|
||||
match parse_definition_book(&code) {
|
||||
Ok(book) => Ok(book),
|
||||
Err(errs) => {
|
||||
let msg = errs.into_iter().map(|e| e.to_string()).join("\n");
|
||||
Err(anyhow::anyhow!(msg))
|
||||
Err(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,16 +6,13 @@ use hvmc::{
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub fn book_to_nets(
|
||||
book: &Book,
|
||||
main: DefId,
|
||||
) -> anyhow::Result<(HashMap<String, INet>, HashMap<DefId, Val>)> {
|
||||
pub fn book_to_nets(book: &Book, main: DefId) -> (HashMap<String, INet>, HashMap<DefId, Val>) {
|
||||
let mut nets = HashMap::new();
|
||||
let mut id_to_hvmc_name = HashMap::new();
|
||||
|
||||
for def in book.defs.values() {
|
||||
for rule in def.rules.iter() {
|
||||
let net = term_to_compat_net(&rule.body)?;
|
||||
let net = term_to_compat_net(&rule.body);
|
||||
let name = if def.def_id == main {
|
||||
DefNames::ENTRY_POINT.to_string()
|
||||
} else {
|
||||
@ -27,7 +24,7 @@ pub fn book_to_nets(
|
||||
}
|
||||
}
|
||||
|
||||
Ok((nets, id_to_hvmc_name))
|
||||
(nets, id_to_hvmc_name)
|
||||
}
|
||||
|
||||
/// Converts rules names to unique names compatible with hvm-core:
|
||||
@ -62,12 +59,12 @@ fn def_id_to_hvmc_name(book: &Book, def_id: DefId, nets: &HashMap<String, INet>)
|
||||
}
|
||||
|
||||
/// Converts an IC term into an IC net.
|
||||
pub fn term_to_compat_net(term: &Term) -> anyhow::Result<INet> {
|
||||
pub fn term_to_compat_net(term: &Term) -> INet {
|
||||
let mut inet = INet::new();
|
||||
|
||||
// Encodes the main term.
|
||||
let mut global_vars = HashMap::new();
|
||||
let main = encode_term(&mut inet, term, ROOT, &mut HashMap::new(), &mut vec![], &mut global_vars, &mut 0)?;
|
||||
let main = encode_term(&mut inet, term, ROOT, &mut HashMap::new(), &mut vec![], &mut global_vars, &mut 0);
|
||||
|
||||
for (decl_port, use_port) in global_vars.into_values() {
|
||||
inet.link(decl_port, use_port);
|
||||
@ -76,7 +73,7 @@ pub fn term_to_compat_net(term: &Term) -> anyhow::Result<INet> {
|
||||
link_local(&mut inet, ROOT, main);
|
||||
}
|
||||
|
||||
Ok(inet)
|
||||
inet
|
||||
}
|
||||
|
||||
/// Adds a subterm connected to `up` to the `inet`.
|
||||
@ -92,7 +89,7 @@ fn encode_term(
|
||||
vars: &mut Vec<(Port, Option<Port>)>,
|
||||
global_vars: &mut HashMap<Name, (Port, Port)>,
|
||||
dups: &mut NodeId,
|
||||
) -> anyhow::Result<Option<Port>> {
|
||||
) -> Option<Port> {
|
||||
match term {
|
||||
// A lambda becomes to a con node. Ports:
|
||||
// - 0: points to where the lambda occurs.
|
||||
@ -102,18 +99,18 @@ fn encode_term(
|
||||
Term::Lam { nam, bod } => {
|
||||
let fun = inet.new_node(Con);
|
||||
push_scope(nam, Port(fun, 1), scope, vars);
|
||||
let bod = encode_term(inet, bod, Port(fun, 2), scope, vars, global_vars, dups)?;
|
||||
let bod = encode_term(inet, bod, Port(fun, 2), scope, vars, global_vars, dups);
|
||||
pop_scope(nam, Port(fun, 1), inet, scope);
|
||||
link_local(inet, Port(fun, 2), bod);
|
||||
Ok(Some(Port(fun, 0)))
|
||||
Some(Port(fun, 0))
|
||||
}
|
||||
// core: (var_use bod)
|
||||
Term::Chn { nam, bod } => {
|
||||
let fun = inet.new_node(Con);
|
||||
global_vars.entry(nam.clone()).or_default().0 = Port(fun, 1);
|
||||
let bod = encode_term(inet, bod, Port(fun, 2), scope, vars, global_vars, dups)?;
|
||||
let bod = encode_term(inet, bod, Port(fun, 2), scope, vars, global_vars, dups);
|
||||
link_local(inet, Port(fun, 2), bod);
|
||||
Ok(Some(Port(fun, 0)))
|
||||
Some(Port(fun, 0))
|
||||
}
|
||||
// An application becomes to a con node too. Ports:
|
||||
// - 0: points to the function being applied.
|
||||
@ -122,29 +119,29 @@ fn encode_term(
|
||||
// core: & fun ~ (arg ret) (fun not necessarily main port)
|
||||
Term::App { fun, arg } => {
|
||||
let app = inet.new_node(Con);
|
||||
let fun = encode_term(inet, fun, Port(app, 0), scope, vars, global_vars, dups)?;
|
||||
let fun = encode_term(inet, fun, Port(app, 0), scope, vars, global_vars, dups);
|
||||
link_local(inet, Port(app, 0), fun);
|
||||
let arg = encode_term(inet, arg, Port(app, 1), scope, vars, global_vars, dups)?;
|
||||
let arg = encode_term(inet, arg, Port(app, 1), scope, vars, global_vars, dups);
|
||||
link_local(inet, Port(app, 1), arg);
|
||||
Ok(Some(Port(app, 2)))
|
||||
Some(Port(app, 2))
|
||||
}
|
||||
// core: & cond ~ ? (zero succ) ret
|
||||
// core: & cond ~ (zero succ) ret
|
||||
Term::Match { cond, zero, succ } => {
|
||||
let if_ = inet.new_node(Mat);
|
||||
|
||||
let cond = encode_term(inet, cond, Port(if_, 0), scope, vars, global_vars, dups)?;
|
||||
let cond = encode_term(inet, cond, Port(if_, 0), scope, vars, global_vars, dups);
|
||||
link_local(inet, Port(if_, 0), cond);
|
||||
|
||||
let sel = inet.new_node(Con);
|
||||
inet.link(Port(sel, 0), Port(if_, 1));
|
||||
|
||||
let zero = encode_term(inet, zero, Port(sel, 1), scope, vars, global_vars, dups)?;
|
||||
let zero = encode_term(inet, zero, Port(sel, 1), scope, vars, global_vars, dups);
|
||||
link_local(inet, Port(sel, 1), zero);
|
||||
|
||||
let succ = encode_term(inet, succ, Port(sel, 2), scope, vars, global_vars, dups)?;
|
||||
let succ = encode_term(inet, succ, Port(sel, 2), scope, vars, global_vars, dups);
|
||||
link_local(inet, Port(sel, 2), succ);
|
||||
|
||||
Ok(Some(Port(if_, 2)))
|
||||
Some(Port(if_, 2))
|
||||
}
|
||||
// A dup becomes a dup node too. Ports:
|
||||
// - 0: points to the value projected.
|
||||
@ -154,16 +151,16 @@ fn encode_term(
|
||||
Term::Dup { fst, snd, val, nxt } => {
|
||||
let dup = inet.new_node(Dup { lab: u8::try_from(*dups).unwrap() });
|
||||
*dups += 1;
|
||||
let val = encode_term(inet, val, Port(dup, 0), scope, vars, global_vars, dups)?;
|
||||
let val = encode_term(inet, val, Port(dup, 0), scope, vars, global_vars, dups);
|
||||
link_local(inet, Port(dup, 0), val);
|
||||
|
||||
push_scope(fst, Port(dup, 1), scope, vars);
|
||||
push_scope(snd, Port(dup, 2), scope, vars);
|
||||
let nxt = encode_term(inet, nxt, up, scope, vars, global_vars, dups)?;
|
||||
let nxt = encode_term(inet, nxt, up, scope, vars, global_vars, dups);
|
||||
pop_scope(snd, Port(dup, 2), inet, scope);
|
||||
pop_scope(fst, Port(dup, 1), inet, scope);
|
||||
|
||||
Ok(nxt)
|
||||
nxt
|
||||
}
|
||||
Term::Var { nam } => {
|
||||
// We assume this variable to be valid, bound and correctly scoped.
|
||||
@ -178,32 +175,32 @@ fn encode_term(
|
||||
debug_assert!(use_port.is_none(), "Variable {nam} used more than once");
|
||||
inet.link(up, *declare_port);
|
||||
*use_port = Some(up);
|
||||
Ok(Some(*declare_port))
|
||||
Some(*declare_port)
|
||||
}
|
||||
Term::Lnk { nam } => {
|
||||
global_vars.entry(nam.clone()).or_default().1 = up;
|
||||
Ok(None)
|
||||
None
|
||||
}
|
||||
// core: @def_id
|
||||
Term::Ref { def_id } => {
|
||||
let node = inet.new_node(Ref { def_id: *def_id });
|
||||
inet.link(Port(node, 1), Port(node, 2));
|
||||
inet.link(up, Port(node, 0));
|
||||
Ok(Some(Port(node, 0)))
|
||||
Some(Port(node, 0))
|
||||
}
|
||||
Term::Let { pat: LetPat::Tup(l_nam, r_nam), val, nxt } => {
|
||||
let dup = inet.new_node(Tup);
|
||||
|
||||
let val = encode_term(inet, val, Port(dup, 0), scope, vars, global_vars, dups)?;
|
||||
let val = encode_term(inet, val, Port(dup, 0), scope, vars, global_vars, dups);
|
||||
link_local(inet, Port(dup, 0), val);
|
||||
|
||||
push_scope(l_nam, Port(dup, 1), scope, vars);
|
||||
push_scope(r_nam, Port(dup, 2), scope, vars);
|
||||
let nxt = encode_term(inet, nxt, up, scope, vars, global_vars, dups)?;
|
||||
let nxt = encode_term(inet, nxt, up, scope, vars, global_vars, dups);
|
||||
pop_scope(r_nam, Port(dup, 2), inet, scope);
|
||||
pop_scope(l_nam, Port(dup, 1), inet, scope);
|
||||
|
||||
Ok(nxt)
|
||||
nxt
|
||||
}
|
||||
Term::Let { .. } => unreachable!(), // Removed in earlier poss
|
||||
Term::Sup { .. } => unreachable!(), // Not supported in syntax
|
||||
@ -214,7 +211,7 @@ fn encode_term(
|
||||
let node = inet.new_node(Num { val: *val });
|
||||
// This representation only has nodes of arity 2, so we connect the two aux ports that are not used.
|
||||
inet.link(Port(node, 1), Port(node, 2));
|
||||
Ok(Some(Port(node, 0)))
|
||||
Some(Port(node, 0))
|
||||
}
|
||||
// core: & #op ~ <fst <snd ret>>
|
||||
Term::Opx { op, fst, snd } => {
|
||||
@ -224,27 +221,27 @@ fn encode_term(
|
||||
let fst_node = inet.new_node(Op2);
|
||||
inet.link(Port(op_node, 0), Port(fst_node, 0));
|
||||
|
||||
let fst = encode_term(inet, fst, Port(fst_node, 1), scope, vars, global_vars, dups)?;
|
||||
let fst = encode_term(inet, fst, Port(fst_node, 1), scope, vars, global_vars, dups);
|
||||
link_local(inet, Port(fst_node, 1), fst);
|
||||
|
||||
let snd_node = inet.new_node(Op2);
|
||||
inet.link(Port(fst_node, 2), Port(snd_node, 0));
|
||||
|
||||
let snd = encode_term(inet, snd, Port(snd_node, 1), scope, vars, global_vars, dups)?;
|
||||
let snd = encode_term(inet, snd, Port(snd_node, 1), scope, vars, global_vars, dups);
|
||||
link_local(inet, Port(snd_node, 1), snd);
|
||||
|
||||
Ok(Some(Port(snd_node, 2)))
|
||||
Some(Port(snd_node, 2))
|
||||
}
|
||||
Term::Tup { fst, snd } => {
|
||||
let tup = inet.new_node(Tup);
|
||||
|
||||
let fst = encode_term(inet, fst, Port(tup, 1), scope, vars, global_vars, dups)?;
|
||||
let fst = encode_term(inet, fst, Port(tup, 1), scope, vars, global_vars, dups);
|
||||
link_local(inet, Port(tup, 1), fst);
|
||||
|
||||
let snd = encode_term(inet, snd, Port(tup, 2), scope, vars, global_vars, dups)?;
|
||||
let snd = encode_term(inet, snd, Port(tup, 2), scope, vars, global_vars, dups);
|
||||
link_local(inet, Port(tup, 2), snd);
|
||||
|
||||
Ok(Some(Port(tup, 0)))
|
||||
Some(Port(tup, 0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -244,7 +244,6 @@ impl Combinator {
|
||||
fn comb_ref(self, names: &mut DefNames, defs: &mut Combinators) -> Term {
|
||||
let name = Name::new(&format!("${:?}", self));
|
||||
let def_id = names.def_id(&name).unwrap_or_else(|| {
|
||||
|
||||
let def_id = names.insert(name);
|
||||
let body = self.into();
|
||||
let rules = vec![Rule { pats: Vec::new(), body }];
|
||||
|
@ -15,22 +15,20 @@ use std::collections::HashMap;
|
||||
/// If they're use more times: add dups for all the uses, put the let body at the root dup.
|
||||
/// Precondition: All variables are bound and have unique names within each definition.
|
||||
impl Book {
|
||||
pub fn linearize_vars(&mut self) -> anyhow::Result<()> {
|
||||
pub fn linearize_vars(&mut self) {
|
||||
for def in self.defs.values_mut() {
|
||||
for rule in def.rules.iter_mut() {
|
||||
rule.body.linearize_vars()?;
|
||||
rule.body.linearize_vars();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Term {
|
||||
pub fn linearize_vars(&mut self) -> anyhow::Result<()> {
|
||||
pub fn linearize_vars(&mut self) {
|
||||
let mut var_uses = HashMap::new();
|
||||
count_var_uses_in_term(self, &mut var_uses);
|
||||
term_to_affine(self, &mut var_uses, &mut HashMap::new())?;
|
||||
Ok(())
|
||||
term_to_affine(self, &mut var_uses, &mut HashMap::new());
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,23 +76,19 @@ fn count_var_uses_in_term(term: &Term, uses: &mut HashMap<Name, Val>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn term_to_affine(
|
||||
term: &mut Term,
|
||||
var_uses: &mut HashMap<Name, Val>,
|
||||
let_bodies: &mut HashMap<Name, Term>,
|
||||
) -> anyhow::Result<()> {
|
||||
fn term_to_affine(term: &mut Term, var_uses: &mut HashMap<Name, Val>, let_bodies: &mut HashMap<Name, Term>) {
|
||||
match term {
|
||||
// Var-declaring terms
|
||||
Term::Lam { nam, bod } => {
|
||||
if let Some(nam_some) = nam {
|
||||
if let Some(uses) = var_uses.get(nam_some).copied() {
|
||||
term_to_affine(bod, var_uses, let_bodies)?;
|
||||
term_to_affine(bod, var_uses, let_bodies);
|
||||
duplicate_lam(nam, bod, uses);
|
||||
} else {
|
||||
term_to_affine(bod, var_uses, let_bodies)?;
|
||||
term_to_affine(bod, var_uses, let_bodies);
|
||||
}
|
||||
} else {
|
||||
term_to_affine(bod, var_uses, let_bodies)?;
|
||||
term_to_affine(bod, var_uses, let_bodies);
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,17 +96,17 @@ fn term_to_affine(
|
||||
let uses = var_uses[nam];
|
||||
match uses {
|
||||
0 => {
|
||||
term_to_affine(nxt, var_uses, let_bodies)?;
|
||||
term_to_affine(nxt, var_uses, let_bodies);
|
||||
}
|
||||
1 => {
|
||||
term_to_affine(val, var_uses, let_bodies)?;
|
||||
term_to_affine(val, var_uses, let_bodies);
|
||||
let_bodies.insert(nam.clone(), std::mem::replace(val.as_mut(), Term::Era));
|
||||
term_to_affine(nxt, var_uses, let_bodies)?;
|
||||
term_to_affine(nxt, var_uses, let_bodies);
|
||||
}
|
||||
uses => {
|
||||
term_to_affine(val, var_uses, let_bodies)?;
|
||||
term_to_affine(nxt, var_uses, let_bodies)?;
|
||||
duplicate_let(&nam, nxt, uses, val);
|
||||
term_to_affine(val, var_uses, let_bodies);
|
||||
term_to_affine(nxt, var_uses, let_bodies);
|
||||
duplicate_let(nam, nxt, uses, val);
|
||||
}
|
||||
}
|
||||
*term = std::mem::replace(nxt.as_mut(), Term::Era);
|
||||
@ -121,8 +115,8 @@ fn term_to_affine(
|
||||
Term::Dup { fst, snd, val, nxt } | Term::Let { pat: LetPat::Tup(fst, snd), val, nxt } => {
|
||||
let uses_fst = get_var_uses(fst.as_ref(), var_uses);
|
||||
let uses_snd = get_var_uses(snd.as_ref(), var_uses);
|
||||
term_to_affine(val, var_uses, let_bodies)?;
|
||||
term_to_affine(nxt, var_uses, let_bodies)?;
|
||||
term_to_affine(val, var_uses, let_bodies);
|
||||
term_to_affine(nxt, var_uses, let_bodies);
|
||||
duplicate_lam(fst, nxt, uses_fst);
|
||||
duplicate_lam(snd, nxt, uses_snd);
|
||||
}
|
||||
@ -134,28 +128,27 @@ fn term_to_affine(
|
||||
if let Some(subst) = let_bodies.remove(nam) {
|
||||
*term = subst.clone();
|
||||
} else {
|
||||
*nam = dup_name(&nam, uses);
|
||||
*nam = dup_name(nam, uses);
|
||||
}
|
||||
}
|
||||
|
||||
// Others
|
||||
Term::Chn { bod, .. } => term_to_affine(bod, var_uses, let_bodies)?,
|
||||
Term::Chn { bod, .. } => term_to_affine(bod, var_uses, let_bodies),
|
||||
Term::App { fun, arg } => {
|
||||
term_to_affine(fun, var_uses, let_bodies)?;
|
||||
term_to_affine(arg, var_uses, let_bodies)?;
|
||||
term_to_affine(fun, var_uses, let_bodies);
|
||||
term_to_affine(arg, var_uses, let_bodies);
|
||||
}
|
||||
Term::Sup { fst, snd } | Term::Tup { fst, snd } | Term::Opx { fst, snd, .. } => {
|
||||
term_to_affine(fst, var_uses, let_bodies)?;
|
||||
term_to_affine(snd, var_uses, let_bodies)?;
|
||||
term_to_affine(fst, var_uses, let_bodies);
|
||||
term_to_affine(snd, var_uses, let_bodies);
|
||||
}
|
||||
Term::Match { cond, zero, succ } => {
|
||||
term_to_affine(cond, var_uses, let_bodies)?;
|
||||
term_to_affine(zero, var_uses, let_bodies)?;
|
||||
term_to_affine(succ, var_uses, let_bodies)?;
|
||||
term_to_affine(cond, var_uses, let_bodies);
|
||||
term_to_affine(zero, var_uses, let_bodies);
|
||||
term_to_affine(succ, var_uses, let_bodies);
|
||||
}
|
||||
Term::Era | Term::Lnk { .. } | Term::Ref { .. } | Term::Num { .. } => (),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_var_uses(nam: Option<&Name>, var_uses: &HashMap<Name, Val>) -> Val {
|
||||
@ -163,7 +156,7 @@ fn get_var_uses(nam: Option<&Name>, var_uses: &HashMap<Name, Val>) -> Val {
|
||||
}
|
||||
|
||||
fn make_dup_tree(nam: &Name, nxt: &mut Term, uses: Val, dup_body: Option<&mut Term>) {
|
||||
// TODO: Is there a difference between a list of dups and a complete binary tree of dups?
|
||||
// TODO: Is there a difference between a list of dups and a complete binary tree of dups
|
||||
// Creates this: "dup x1 x1_dup = body; dup x2 x2_dup = x1_dup; dup x3 x4 = x2_dup; nxt"
|
||||
for i in (1 .. uses).rev() {
|
||||
let old_nxt = std::mem::replace(nxt, Term::Era);
|
||||
|
@ -7,7 +7,7 @@ impl Book {
|
||||
// When we find a function that is simply directly calling another function,
|
||||
// substitutes all occurences of that function to the one being called, avoiding the unnecessary redirect.
|
||||
// In case there is a long chaing of ref-to-ref-to-ref, we substitute values by the last function in the chain.
|
||||
pub fn simplify_ref_to_ref(&mut self) -> anyhow::Result<()> {
|
||||
pub fn simplify_ref_to_ref(&mut self) -> Result<(), String> {
|
||||
let mut ref_map: HashMap<DefId, DefId> = HashMap::new();
|
||||
// Find to which defs we're mapping the ones that are just references.
|
||||
for def_id in self.def_names.def_ids() {
|
||||
@ -16,7 +16,7 @@ impl Book {
|
||||
let mut is_ref_to_ref = false;
|
||||
while let Term::Ref { def_id: next_ref_id } = &self.defs.get(ref_id).unwrap().rules[0].body {
|
||||
if next_ref_id == def_id {
|
||||
return Err(anyhow::anyhow!(
|
||||
return Err(format!(
|
||||
"Definition {} is a reference to itself",
|
||||
self.def_names.name(def_id).unwrap()
|
||||
));
|
||||
|
@ -68,7 +68,6 @@ fn unique_var_names(term: &mut Term, name_map: &mut UniqueNameScope, name_count:
|
||||
}
|
||||
Term::Lnk { .. } | Term::Ref { .. } | Term::Era | Term::Num { .. } => (),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
fn push_name(name: Option<&Name>, name_map: &mut UniqueNameScope, name_count: &mut VarId) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use hvm_lang::{
|
||||
compile_book,
|
||||
net::{compat_net_to_core, hvmc_to_net},
|
||||
net::{hvmc_to_net::hvmc_to_net, net_to_hvmc::net_to_hvmc},
|
||||
run_book,
|
||||
term::{
|
||||
net_to_term::net_to_term_non_linear,
|
||||
@ -19,32 +19,26 @@ use std::{
|
||||
use stdext::function_name;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
fn do_parse_book(code: &str) -> anyhow::Result<Book> {
|
||||
fn do_parse_book(code: &str) -> Result<Book, String> {
|
||||
match parse_definition_book(code) {
|
||||
Ok(book) => Ok(book),
|
||||
Err(errs) => {
|
||||
let msg = errs.into_iter().map(|e| e.to_string()).join("\n");
|
||||
Err(anyhow::anyhow!(msg))
|
||||
}
|
||||
Err(errs) => Err(errs.into_iter().map(|e| e.to_string()).join("\n")),
|
||||
}
|
||||
}
|
||||
|
||||
fn do_parse_term(code: &str) -> anyhow::Result<Term> {
|
||||
parse_term(code).map_err(|errs| {
|
||||
let msg = errs.into_iter().map(|e| e.to_string()).join("\n");
|
||||
anyhow::anyhow!(msg)
|
||||
})
|
||||
fn do_parse_term(code: &str) -> Result<Term, String> {
|
||||
parse_term(code).map_err(|errs| errs.into_iter().map(|e| e.to_string()).join("\n"))
|
||||
}
|
||||
|
||||
fn do_parse_net(code: &str) -> anyhow::Result<hvmc::ast::Net> {
|
||||
parse_net(&mut code.chars().peekable()).map_err(|e| anyhow::anyhow!(e))
|
||||
fn do_parse_net(code: &str) -> Result<hvmc::ast::Net, String> {
|
||||
parse_net(&mut code.chars().peekable())
|
||||
}
|
||||
|
||||
fn run_single_golden_test(
|
||||
path: &Path,
|
||||
run: &dyn Fn(&Path, &str) -> anyhow::Result<String>,
|
||||
) -> anyhow::Result<()> {
|
||||
let code = fs::read_to_string(path)?;
|
||||
run: &dyn Fn(&Path, &str) -> Result<String, String>,
|
||||
) -> Result<(), String> {
|
||||
let code = fs::read_to_string(path).map_err(|e| e.to_string())?;
|
||||
let result = match run(path, &code) {
|
||||
Ok(res) => res,
|
||||
Err(err) => err.to_string(),
|
||||
@ -54,13 +48,13 @@ fn run_single_golden_test(
|
||||
assert_eq!(result, to_check, "Testing file '{}'", path.display());
|
||||
Ok(())
|
||||
} else {
|
||||
let mut file = fs::File::create(golden_path)?;
|
||||
file.write_all(result.as_bytes())?;
|
||||
let mut file = fs::File::create(golden_path).map_err(|e| e.to_string())?;
|
||||
file.write_all(result.as_bytes()).map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn run_golden_test_dir(test_name: &str, run: &dyn Fn(&Path, &str) -> anyhow::Result<String>) {
|
||||
fn run_golden_test_dir(test_name: &str, run: &dyn Fn(&Path, &str) -> Result<String, String>) {
|
||||
let root = PathBuf::from(format!(
|
||||
"{}/tests/golden_tests/{}",
|
||||
env!("CARGO_MANIFEST_DIR"),
|
||||
@ -92,9 +86,9 @@ fn compile_term() {
|
||||
let mut term = do_parse_term(code)?;
|
||||
term.check_unbound_vars()?;
|
||||
term.make_var_names_unique();
|
||||
term.linearize_vars()?;
|
||||
let compat_net = term_to_compat_net(&term)?;
|
||||
let net = compat_net_to_core(&compat_net, &|def_id| def_id.to_internal())?;
|
||||
term.linearize_vars();
|
||||
let compat_net = term_to_compat_net(&term);
|
||||
let net = net_to_hvmc(&compat_net, &|def_id| def_id.to_internal())?;
|
||||
Ok(show_net(&net))
|
||||
})
|
||||
}
|
||||
@ -128,7 +122,7 @@ fn readback_lnet() {
|
||||
run_golden_test_dir(function_name!(), &|_, code| {
|
||||
let net = do_parse_net(code)?;
|
||||
let book = Book::default();
|
||||
let compat_net = hvmc_to_net(&net, &|val| DefId::from_internal(val))?;
|
||||
let compat_net = hvmc_to_net(&net, &|val| DefId::from_internal(val));
|
||||
let (term, valid) = net_to_term_non_linear(&compat_net, &book);
|
||||
if valid {
|
||||
Ok(term.to_string(&book.def_names))
|
||||
|
@ -1 +1 @@
|
||||
Cyclic terms are not supported
|
||||
Found term that compiles into an inet with a vicious cycle
|
Loading…
Reference in New Issue
Block a user