[sc-119] Remove anyhow dependency

This commit is contained in:
Nicolas Abril 2023-11-10 18:08:18 +01:00
parent 600e4536f8
commit e8e81f331d
20 changed files with 129 additions and 159 deletions

7
Cargo.lock generated
View File

@ -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",

View File

@ -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" }

View File

@ -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))

View File

@ -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");

View File

@ -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 {

View File

@ -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::*;

View File

@ -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 {

View File

@ -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."));
}
}
}

View File

@ -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),
}
}

View File

@ -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());
}
}

View File

@ -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(())
}

View File

@ -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;

View File

@ -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)
}
}
}

View File

@ -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))
}
}
}

View File

@ -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 }];

View File

@ -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);

View File

@ -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()
));

View File

@ -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) {

View File

@ -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))

View File

@ -1 +1 @@
Cyclic terms are not supported
Found term that compiles into an inet with a vicious cycle