diff --git a/.gitignore b/.gitignore index 50f3795aa..a82dc7eae 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ demo/ .check.hs guide.txt .hvm +.hvm/ diff --git a/book/.hvm/Cargo.lock b/book/.hvm/Cargo.lock deleted file mode 100644 index 15bca0d60..000000000 --- a/book/.hvm/Cargo.lock +++ /dev/null @@ -1,16 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "hvm-core" -version = "0.2.18" -dependencies = [ - "nohash-hasher", -] - -[[package]] -name = "nohash-hasher" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" diff --git a/book/.hvm/Cargo.toml b/book/.hvm/Cargo.toml deleted file mode 100644 index 0374a49cb..000000000 --- a/book/.hvm/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -name = "hvm-core" -version = "0.2.18" -edition = "2021" -description = "HVM-Core is a massively parallel Interaction Combinator evaluator." -license = "MIT" - -[[bin]] -name = "hvmc" -path = "src/main.rs" -bench = false - -[lib] -name = "hvmc" -path = "src/lib.rs" -bench = false - -[profile.release] -codegen-units = 1 -lto = "fat" -opt-level = 3 -panic = "abort" - -[features] -default = [] -hvm_cli_options = [] -lazy_mode = [] - -[dependencies] -nohash-hasher = "0.2.0" - diff --git a/book/.hvm/src/ast.rs b/book/.hvm/src/ast.rs deleted file mode 100644 index a6c3a601e..000000000 --- a/book/.hvm/src/ast.rs +++ /dev/null @@ -1,799 +0,0 @@ -// An interaction combinator language -// ---------------------------------- -// This file implements a textual syntax to interact with the runtime. It includes a pure AST for -// nets, as well as functions for parsing, stringifying, and converting pure ASTs to runtime nets. -// On the runtime, a net is represented by a list of active trees, plus a root tree. The textual -// syntax reflects this representation. The grammar is specified on this repo's README. - -use crate::run; -use std::collections::BTreeMap; -use std::collections::HashMap; -use std::collections::HashSet; -use std::iter::Peekable; -use std::str::Chars; - -// AST -// --- - -#[derive(Clone, Hash, PartialEq, Eq, Debug)] -pub enum Tree { - Era, - Con { lft: Box, rgt: Box }, - Tup { lft: Box, rgt: Box }, - Dup { lab: run::Lab, lft: Box, rgt: Box }, - Var { nam: String }, - Ref { nam: run::Val }, - Num { val: run::Val }, - Op1 { opr: run::Lab, lft: run::Val, rgt: Box }, - Op2 { opr: run::Lab, lft: Box, rgt: Box }, - Mat { sel: Box, ret: Box }, -} - -type Redex = Vec<(Tree, Tree)>; - -#[derive(Clone, Hash, PartialEq, Eq, Debug)] -pub struct Net { - pub root: Tree, - pub rdex: Redex, -} - -pub type Book = BTreeMap; - -// Parser -// ------ - -// FIXME: remove after skip is fixed -fn skip_spaces(chars: &mut Peekable) { - while let Some(c) = chars.peek() { - if !c.is_ascii_whitespace() { - break; - } else { - chars.next(); - } - } -} - -// FIXME: detect two '/' for comments, allowing us to remove 'skip_spaces' -fn skip(chars: &mut Peekable) { - while let Some(c) = chars.peek() { - if *c == '/' { - chars.next(); - while let Some(c) = chars.peek() { - if *c == '\n' { - break; - } - chars.next(); - } - } else if !c.is_ascii_whitespace() { - break; - } else { - chars.next(); - } - } -} - -pub fn consume(chars: &mut Peekable, text: &str) -> Result<(), String> { - skip(chars); - for c in text.chars() { - if chars.next() != Some(c) { - return Err(format!("Expected '{}', found {:?}", text, chars.peek())); - } - } - return Ok(()); -} - -pub fn parse_decimal(chars: &mut Peekable) -> Result { - let mut num: u64 = 0; - skip(chars); - if !chars.peek().map_or(false, |c| c.is_digit(10)) { - return Err(format!("Expected a decimal number, found {:?}", chars.peek())); - } - while let Some(c) = chars.peek() { - if !c.is_digit(10) { - break; - } - num = num * 10 + c.to_digit(10).unwrap() as u64; - chars.next(); - } - Ok(num) -} - -pub fn parse_name(chars: &mut Peekable) -> Result { - let mut txt = String::new(); - skip(chars); - if !chars.peek().map_or(false, |c| c.is_alphanumeric() || *c == '_' || *c == '.') { - return Err(format!("Expected a name character, found {:?}", chars.peek())) - } - while let Some(c) = chars.peek() { - if !c.is_alphanumeric() && *c != '_' && *c != '.' { - break; - } - txt.push(*c); - chars.next(); - } - Ok(txt) -} - -pub fn parse_opx_lit(chars: &mut Peekable) -> Result { - let mut opx = String::new(); - skip_spaces(chars); - while let Some(c) = chars.peek() { - if !"+-=*/%<>|&^!?".contains(*c) { - break; - } - opx.push(*c); - chars.next(); - } - Ok(opx) -} - -fn parse_opr(chars: &mut Peekable) -> Result { - let opx = parse_opx_lit(chars)?; - match opx.as_str() { - "+" => Ok(run::ADD), - "-" => Ok(run::SUB), - "*" => Ok(run::MUL), - "/" => Ok(run::DIV), - "%" => Ok(run::MOD), - "==" => Ok(run::EQ), - "!=" => Ok(run::NE), - "<" => Ok(run::LT), - ">" => Ok(run::GT), - "<=" => Ok(run::LTE), - ">=" => Ok(run::GTE), - "&&" => Ok(run::AND), - "||" => Ok(run::OR), - "^" => Ok(run::XOR), - "!" => Ok(run::NOT), - "<<" => Ok(run::LSH), - ">>" => Ok(run::RSH), - _ => Err(format!("Unknown operator: {}", opx)), - } -} - -pub fn parse_tree(chars: &mut Peekable) -> Result { - skip(chars); - match chars.peek() { - Some('*') => { - chars.next(); - Ok(Tree::Era) - } - Some('(') => { - chars.next(); - let lft = Box::new(parse_tree(chars)?); - let rgt = Box::new(parse_tree(chars)?); - consume(chars, ")")?; - Ok(Tree::Con { lft, rgt }) - } - Some('[') => { - chars.next(); - let lab = 1; - let lft = Box::new(parse_tree(chars)?); - let rgt = Box::new(parse_tree(chars)?); - consume(chars, "]")?; - Ok(Tree::Tup { lft, rgt }) - } - Some('{') => { - chars.next(); - let lab = parse_decimal(chars)? as run::Lab; - let lft = Box::new(parse_tree(chars)?); - let rgt = Box::new(parse_tree(chars)?); - consume(chars, "}")?; - Ok(Tree::Dup { lab, lft, rgt }) - } - Some('@') => { - chars.next(); - skip(chars); - let name = parse_name(chars)?; - Ok(Tree::Ref { nam: name_to_val(&name) }) - } - Some('#') => { - chars.next(); - Ok(Tree::Num { val: parse_decimal(chars)? }) - } - Some('<') => { - chars.next(); - if chars.peek().map_or(false, |c| c.is_digit(10)) { - let lft = parse_decimal(chars)?; - let opr = parse_opr(chars)?; - let rgt = Box::new(parse_tree(chars)?); - consume(chars, ">")?; - Ok(Tree::Op1 { opr, lft, rgt }) - } else { - let opr = parse_opr(chars)?; - let lft = Box::new(parse_tree(chars)?); - let rgt = Box::new(parse_tree(chars)?); - consume(chars, ">")?; - Ok(Tree::Op2 { opr, lft, rgt }) - } - } - Some('?') => { - chars.next(); - consume(chars, "<")?; - let sel = Box::new(parse_tree(chars)?); - let ret = Box::new(parse_tree(chars)?); - consume(chars, ">")?; - Ok(Tree::Mat { sel, ret }) - } - _ => { - Ok(Tree::Var { nam: parse_name(chars)? }) - }, - } -} - -pub fn parse_net(chars: &mut Peekable) -> Result { - let mut rdex = Vec::new(); - let root = parse_tree(chars)?; - while let Some(c) = { skip(chars); chars.peek() } { - if *c == '&' { - chars.next(); - let tree1 = parse_tree(chars)?; - consume(chars, "~")?; - let tree2 = parse_tree(chars)?; - rdex.push((tree1, tree2)); - } else { - break; - } - } - Ok(Net { root, rdex }) -} - -pub fn parse_book(chars: &mut Peekable) -> Result { - let mut book = BTreeMap::new(); - while let Some(c) = { skip(chars); chars.peek() } { - if *c == '@' { - chars.next(); - let name = parse_name(chars)?; - consume(chars, "=")?; - let net = parse_net(chars)?; - book.insert(name, net); - } else { - break; - } - } - Ok(book) -} - -fn do_parse(code: &str, parse_fn: impl Fn(&mut Peekable) -> Result) -> T { - let chars = &mut code.chars().peekable(); - match parse_fn(chars) { - Ok(result) => { - if chars.next().is_none() { - result - } else { - eprintln!("Unable to parse the whole input. Is this not an hvmc file?"); - std::process::exit(1); - } - } - Err(err) => { - eprintln!("{}", err); - std::process::exit(1); - } - } -} - -pub fn do_parse_tree(code: &str) -> Tree { - do_parse(code, parse_tree) -} - -pub fn do_parse_net(code: &str) -> Net { - do_parse(code, parse_net) -} - -pub fn do_parse_book(code: &str) -> Book { - do_parse(code, parse_book) -} - -// Stringifier -// ----------- - -pub fn show_opr(opr: run::Lab) -> String { - match opr { - run::ADD => "+".to_string(), - run::SUB => "-".to_string(), - run::MUL => "*".to_string(), - run::DIV => "/".to_string(), - run::MOD => "%".to_string(), - run::EQ => "==".to_string(), - run::NE => "!=".to_string(), - run::LT => "<".to_string(), - run::GT => ">".to_string(), - run::LTE => "<=".to_string(), - run::GTE => ">=".to_string(), - run::AND => "&&".to_string(), - run::OR => "||".to_string(), - run::XOR => "^".to_string(), - run::NOT => "!".to_string(), - run::LSH => "<<".to_string(), - run::RSH => ">>".to_string(), - _ => panic!("Unknown operator label."), - } -} - -pub fn show_tree(tree: &Tree) -> String { - match tree { - Tree::Era => { - "*".to_string() - } - Tree::Con { lft, rgt } => { - format!("({} {})", show_tree(&*lft), show_tree(&*rgt)) - } - Tree::Tup { lft, rgt } => { - format!("[{} {}]", show_tree(&*lft), show_tree(&*rgt)) - } - Tree::Dup { lab, lft, rgt } => { - format!("{{{} {} {}}}", lab, show_tree(&*lft), show_tree(&*rgt)) - } - Tree::Var { nam } => { - nam.clone() - } - Tree::Ref { nam } => { - format!("@{}", val_to_name(*nam)) - } - Tree::Num { val } => { - format!("#{}", (*val).to_string()) - } - Tree::Op1 { opr, lft, rgt } => { - format!("<{}{} {}>", lft, show_opr(*opr), show_tree(rgt)) - } - Tree::Op2 { opr, lft, rgt } => { - format!("<{} {} {}>", show_opr(*opr), show_tree(&*lft), show_tree(&*rgt)) - } - Tree::Mat { sel, ret } => { - format!("?<{} {}>", show_tree(&*sel), show_tree(&*ret)) - } - } -} - -pub fn show_net(net: &Net) -> String { - let mut result = String::new(); - result.push_str(&format!("{}", show_tree(&net.root))); - for (a, b) in &net.rdex { - result.push_str(&format!("\n& {} ~ {}", show_tree(a), show_tree(b))); - } - return result; -} - -pub fn show_book(book: &Book) -> String { - let mut result = String::new(); - for (name, net) in book { - result.push_str(&format!("@{} = {}\n", name, show_net(net))); - } - return result; -} - -pub fn show_runtime_tree(rt_net: &run::NetFields, ptr: run::Ptr) -> String where [(); LAZY as usize]:{ - show_tree(&tree_from_runtime_go(rt_net, ptr, PARENT_ROOT, &mut HashMap::new(), &mut 0)) -} - -pub fn show_runtime_net(rt_net: &run::NetFields) -> String where [(); LAZY as usize]:{ - show_net(&net_from_runtime(rt_net)) -} - -pub fn show_runtime_book(book: &run::Book) -> String { - show_book(&book_from_runtime(book)) -} - -// Conversion -// ---------- - -pub fn num_to_str(mut num: usize) -> String { - let mut txt = String::new(); - num += 1; - while num > 0 { - num -= 1; - let c = ((num % 26) as u8 + b'a') as char; - txt.push(c); - num /= 26; - } - return txt.chars().rev().collect(); -} - -pub const fn tag_to_port(tag: run::Tag) -> run::Port { - match tag { - run::VR1 => run::P1, - run::VR2 => run::P2, - _ => unreachable!(), - } -} - -pub fn port_to_tag(port: run::Port) -> run::Tag { - match port { - run::P1 => run::VR1, - run::P2 => run::VR2, - _ => unreachable!(), - } -} - -pub fn name_to_letters(name: &str) -> Vec { - let mut letters = Vec::new(); - for c in name.chars() { - letters.push(match c { - '0'..='9' => c as u8 - '0' as u8 + 0, - 'A'..='Z' => c as u8 - 'A' as u8 + 10, - 'a'..='z' => c as u8 - 'a' as u8 + 36, - '_' => 62, - '.' => 63, - _ => panic!("Invalid character in name"), - }); - } - return letters; -} - -pub fn letters_to_name(letters: Vec) -> String { - let mut name = String::new(); - for letter in letters { - name.push(match letter { - 0..= 9 => (letter - 0 + '0' as u8) as char, - 10..=35 => (letter - 10 + 'A' as u8) as char, - 36..=61 => (letter - 36 + 'a' as u8) as char, - 62 => '_', - 63 => '.', - _ => panic!("Invalid letter in name"), - }); - } - return name; -} - -pub fn val_to_letters(num: run::Val) -> Vec { - let mut letters = Vec::new(); - let mut num = num; - while num > 0 { - letters.push((num % 64) as u8); - num /= 64; - } - letters.reverse(); - return letters; -} - -pub fn letters_to_val(letters: Vec) -> run::Val { - let mut num = 0; - for letter in letters { - num = num * 64 + letter as run::Val; - } - return num; -} - -pub fn name_to_val(name: &str) -> run::Val { - letters_to_val(name_to_letters(name)) -} - -pub fn val_to_name(num: run::Val) -> String { - letters_to_name(val_to_letters(num)) -} - -// Injection and Readback -// ---------------------- - -// To runtime - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum Parent { - Redex, - Node { loc: run::Loc, port: run::Port }, -} -const PARENT_ROOT: Parent = Parent::Node { loc: run::ROOT.loc(), port: tag_to_port(run::ROOT.tag()) }; - -pub fn tree_to_runtime_go(rt_net: &mut run::NetFields, tree: &Tree, vars: &mut HashMap, parent: Parent) -> run::Ptr where [(); LAZY as usize]: { - match tree { - Tree::Era => { - run::ERAS - } - Tree::Con { lft, rgt } => { - let loc = rt_net.alloc(); - let p1 = tree_to_runtime_go(rt_net, &*lft, vars, Parent::Node { loc, port: run::P1 }); - rt_net.heap.set(loc, run::P1, p1); - let p2 = tree_to_runtime_go(rt_net, &*rgt, vars, Parent::Node { loc, port: run::P2 }); - rt_net.heap.set(loc, run::P2, p2); - run::Ptr::new(run::LAM, 0, loc) - } - Tree::Tup { lft, rgt } => { - let loc = rt_net.alloc(); - let p1 = tree_to_runtime_go(rt_net, &*lft, vars, Parent::Node { loc, port: run::P1 }); - rt_net.heap.set(loc, run::P1, p1); - let p2 = tree_to_runtime_go(rt_net, &*rgt, vars, Parent::Node { loc, port: run::P2 }); - rt_net.heap.set(loc, run::P2, p2); - run::Ptr::new(run::TUP, 1, loc) - } - Tree::Dup { lab, lft, rgt } => { - let loc = rt_net.alloc(); - let p1 = tree_to_runtime_go(rt_net, &*lft, vars, Parent::Node { loc, port: run::P1 }); - rt_net.heap.set(loc, run::P1, p1); - let p2 = tree_to_runtime_go(rt_net, &*rgt, vars, Parent::Node { loc, port: run::P2 }); - rt_net.heap.set(loc, run::P2, p2); - run::Ptr::new(run::DUP, *lab, loc) - } - Tree::Var { nam } => { - if let Parent::Redex = parent { - panic!("By definition, can't have variable on active pairs."); - }; - match vars.get(nam) { - Some(Parent::Redex) => { - unreachable!(); - } - Some(Parent::Node { loc: other_loc, port: other_port }) => { - match parent { - Parent::Redex => { unreachable!(); } - Parent::Node { loc, port } => rt_net.heap.set(*other_loc, *other_port, run::Ptr::new(port_to_tag(port), 0, loc)), - } - return run::Ptr::new(port_to_tag(*other_port), 0, *other_loc); - } - None => { - vars.insert(nam.clone(), parent); - run::NULL - } - } - } - Tree::Ref { nam } => { - run::Ptr::big(run::REF, *nam) - } - Tree::Num { val } => { - run::Ptr::big(run::NUM, *val) - } - Tree::Op1 { opr, lft, rgt } => { - let loc = rt_net.alloc(); - let p1 = run::Ptr::big(run::NUM, *lft); - rt_net.heap.set(loc, run::P1, p1); - let p2 = tree_to_runtime_go(rt_net, rgt, vars, Parent::Node { loc, port: run::P2 }); - rt_net.heap.set(loc, run::P2, p2); - run::Ptr::new(run::OP1, *opr, loc) - } - Tree::Op2 { opr, lft, rgt } => { - let loc = rt_net.alloc(); - let p1 = tree_to_runtime_go(rt_net, &*lft, vars, Parent::Node { loc, port: run::P1 }); - rt_net.heap.set(loc, run::P1, p1); - let p2 = tree_to_runtime_go(rt_net, &*rgt, vars, Parent::Node { loc, port: run::P2 }); - rt_net.heap.set(loc, run::P2, p2); - run::Ptr::new(run::OP2, *opr, loc) - } - Tree::Mat { sel, ret } => { - let loc = rt_net.alloc(); - let p1 = tree_to_runtime_go(rt_net, &*sel, vars, Parent::Node { loc, port: run::P1 }); - rt_net.heap.set(loc, run::P1, p1); - let p2 = tree_to_runtime_go(rt_net, &*ret, vars, Parent::Node { loc, port: run::P2 }); - rt_net.heap.set(loc, run::P2, p2); - run::Ptr::new(run::MAT, 0, loc) - } - } -} - -pub fn tree_to_runtime(rt_net: &mut run::NetFields, tree: &Tree) -> run::Ptr where [(); LAZY as usize]: { - tree_to_runtime_go(rt_net, tree, &mut HashMap::new(), PARENT_ROOT) -} - -pub fn net_to_runtime(rt_net: &mut run::NetFields, net: &Net) where [(); LAZY as usize]: { - let mut vars = HashMap::new(); - let root = tree_to_runtime_go(rt_net, &net.root, &mut vars, PARENT_ROOT); - rt_net.heap.set_root(root); - for (tree1, tree2) in &net.rdex { - let ptr1 = tree_to_runtime_go(rt_net, tree1, &mut vars, Parent::Redex); - let ptr2 = tree_to_runtime_go(rt_net, tree2, &mut vars, Parent::Redex); - rt_net.rdex.push((ptr1, ptr2)); - } -} - -// Holds dup labels and ref ids used by a definition -type InsideLabs = HashSet>; -type InsideRefs = HashSet; -#[derive(Debug)] -pub struct Inside { - labs: InsideLabs, - refs: InsideRefs, -} - -// Collects dup labels and ref ids used by a definition -pub fn runtime_def_get_inside(def: &run::Def) -> Inside { - let mut inside = Inside { - labs: HashSet::with_hasher(std::hash::BuildHasherDefault::default()), - refs: HashSet::new(), - }; - fn register(inside: &mut Inside, ptr: run::Ptr) { - if ptr.is_dup() { - inside.labs.insert(ptr.lab()); - } - if ptr.is_ref() { - inside.refs.insert(ptr.val()); - } - } - for i in 0 .. def.node.len() { - register(&mut inside, def.node[i].1); - register(&mut inside, def.node[i].2); - } - for i in 0 .. def.rdex.len() { - register(&mut inside, def.rdex[i].0); - register(&mut inside, def.rdex[i].1); - } - return inside; -} - -// Computes all dup labels used by a definition, direct or not. -// FIXME: memoize to avoid duplicated work -pub fn runtime_def_get_all_labs(labs: &mut InsideLabs, insides: &HashMap, fid: run::Val, seen: &mut HashSet) { - if seen.contains(&fid) { - return; - } else { - seen.insert(fid); - if let Some(fid_insides) = insides.get(&fid) { - for dup in &fid_insides.labs { - labs.insert(*dup); - } - for child_fid in &fid_insides.refs { - runtime_def_get_all_labs(labs, insides, *child_fid, seen); - } - } - } -} - -// Converts a book from the pure AST representation to the runtime representation. -pub fn book_to_runtime(book: &Book) -> run::Book { - let mut rt_book = run::Book::new(); - - // Convert each net in 'book' to a runtime net and add to 'rt_book' - for (name, net) in book { - let fid = name_to_val(name); - let nodes = run::Heap::::init(1 << 16); - let mut rt = run::NetFields::new(&nodes); - net_to_runtime(&mut rt, net); - rt_book.def(fid, runtime_net_to_runtime_def(&rt)); - } - - // Calculate the 'insides' of each runtime definition - let mut insides = HashMap::new(); - for (fid, def) in &rt_book.defs { - insides.insert(*fid, runtime_def_get_inside(&def)); - } - - // Compute labs labels used in each runtime definition - let mut labs_by_fid = HashMap::new(); - for (fid, _) in &rt_book.defs { - let mut labs = HashSet::with_hasher(std::hash::BuildHasherDefault::default()); - let mut seen = HashSet::new(); - runtime_def_get_all_labs(&mut labs, &insides, *fid, &mut seen); - labs_by_fid.insert(*fid, labs); - } - - // Set the 'labs' field for each definition - for (fid, def) in &mut rt_book.defs { - def.labs = labs_by_fid.get(fid).unwrap().clone(); - //println!("{} {:?}", val_to_name(*fid), def.labs); - } - - rt_book -} - -// Converts to a def. -pub fn runtime_net_to_runtime_def(net: &run::NetFields) -> run::Def where [(); LAZY as usize]: { - let mut node = vec![]; - let mut rdex = vec![]; - let labs = HashSet::with_hasher(std::hash::BuildHasherDefault::default()); - for i in 0 .. net.heap.nodes.len() { - let p0 = run::APtr::new(run::Ptr(0)); - let p1 = net.heap.get(node.len() as run::Loc, run::P1); - let p2 = net.heap.get(node.len() as run::Loc, run::P2); - if p1 != run::NULL || p2 != run::NULL { - node.push(((), p1, p2)); - } else { - break; - } - } - for i in 0 .. net.rdex.len() { - let p1 = net.rdex[i].0; - let p2 = net.rdex[i].1; - rdex.push((p1, p2)); - } - return run::Def { labs, rdex, node }; -} - -// Reads back from a def. -pub fn runtime_def_to_runtime_net<'a, const LAZY: bool>(nodes: &'a run::Nodes, def: &run::Def) -> run::NetFields<'a, LAZY> where [(); LAZY as usize]: { - let mut net = run::NetFields::new(&nodes); - for (i, &(p0, p1, p2)) in def.node.iter().enumerate() { - net.heap.set(i as run::Loc, run::P1, p1); - net.heap.set(i as run::Loc, run::P2, p2); - } - net.rdex = def.rdex.clone(); - net -} - -pub fn tree_from_runtime_go(rt_net: &run::NetFields, ptr: run::Ptr, parent: Parent, vars: &mut HashMap, fresh: &mut usize) -> Tree where [(); LAZY as usize]: { - match ptr.tag() { - run::ERA => { - Tree::Era - } - run::REF => { - Tree::Ref { nam: ptr.val() } - } - run::NUM => { - Tree::Num { val: ptr.val() } - } - run::OP1 => { - let opr = ptr.lab(); - let lft = tree_from_runtime_go(rt_net, rt_net.heap.get(ptr.loc(), run::P1), Parent::Node { loc: ptr.loc(), port: run::P1 }, vars, fresh); - let Tree::Num { val } = lft else { unreachable!() }; - let rgt = tree_from_runtime_go(rt_net, rt_net.heap.get(ptr.loc(), run::P2), Parent::Node { loc: ptr.loc(), port: run::P2 }, vars, fresh); - Tree::Op1 { opr, lft: val, rgt: Box::new(rgt) } - } - run::OP2 => { - let opr = ptr.lab(); - let lft = tree_from_runtime_go(rt_net, rt_net.heap.get(ptr.loc(), run::P1), Parent::Node { loc: ptr.loc(), port: run::P1 }, vars, fresh); - let rgt = tree_from_runtime_go(rt_net, rt_net.heap.get(ptr.loc(), run::P2), Parent::Node { loc: ptr.loc(), port: run::P2 }, vars, fresh); - Tree::Op2 { opr, lft: Box::new(lft), rgt: Box::new(rgt) } - } - run::MAT => { - let sel = tree_from_runtime_go(rt_net, rt_net.heap.get(ptr.loc(), run::P1), Parent::Node { loc: ptr.loc(), port: run::P1 }, vars, fresh); - let ret = tree_from_runtime_go(rt_net, rt_net.heap.get(ptr.loc(), run::P2), Parent::Node { loc: ptr.loc(), port: run::P2 }, vars, fresh); - Tree::Mat { sel: Box::new(sel), ret: Box::new(ret) } - } - run::VR1 | run::VR2 => { - let key = match ptr.tag() { - run::VR1 => Parent::Node { loc: ptr.loc(), port: run::P1 }, - run::VR2 => Parent::Node { loc: ptr.loc(), port: run::P2 }, - _ => unreachable!(), - }; - if let Some(nam) = vars.get(&key) { - Tree::Var { nam: nam.clone() } - } else { - let nam = num_to_str(*fresh); - *fresh += 1; - vars.insert(parent, nam.clone()); - Tree::Var { nam } - } - } - run::LAM => { - let p1 = rt_net.heap.get(ptr.loc(), run::P1); - let p2 = rt_net.heap.get(ptr.loc(), run::P2); - let lft = tree_from_runtime_go(rt_net, p1, Parent::Node { loc: ptr.loc(), port: run::P1 }, vars, fresh); - let rgt = tree_from_runtime_go(rt_net, p2, Parent::Node { loc: ptr.loc(), port: run::P2 }, vars, fresh); - Tree::Con { lft: Box::new(lft), rgt: Box::new(rgt) } - } - run::TUP => { - let p1 = rt_net.heap.get(ptr.loc(), run::P1); - let p2 = rt_net.heap.get(ptr.loc(), run::P2); - let lft = tree_from_runtime_go(rt_net, p1, Parent::Node { loc: ptr.loc(), port: run::P1 }, vars, fresh); - let rgt = tree_from_runtime_go(rt_net, p2, Parent::Node { loc: ptr.loc(), port: run::P2 }, vars, fresh); - Tree::Tup { lft: Box::new(lft), rgt: Box::new(rgt) } - } - run::DUP => { - let p1 = rt_net.heap.get(ptr.loc(), run::P1); - let p2 = rt_net.heap.get(ptr.loc(), run::P2); - let lft = tree_from_runtime_go(rt_net, p1, Parent::Node { loc: ptr.loc(), port: run::P1 }, vars, fresh); - let rgt = tree_from_runtime_go(rt_net, p2, Parent::Node { loc: ptr.loc(), port: run::P2 }, vars, fresh); - Tree::Dup { lab: ptr.lab(), lft: Box::new(lft), rgt: Box::new(rgt) } - } - _ => { - unreachable!() - } - } -} - -pub fn tree_from_runtime(rt_net: &run::NetFields, ptr: run::Ptr) -> Tree where [(); LAZY as usize]: { - let mut vars = HashMap::new(); - let mut fresh = 0; - tree_from_runtime_go(rt_net, ptr, PARENT_ROOT, &mut vars, &mut fresh) -} - -pub fn net_from_runtime(rt_net: &run::NetFields) -> Net where [(); LAZY as usize]: { - let mut vars = HashMap::new(); - let mut fresh = 0; - let mut rdex = Vec::new(); - let root = tree_from_runtime_go(rt_net, rt_net.heap.get_root(), PARENT_ROOT, &mut vars, &mut fresh); - for &(a, b) in &rt_net.rdex { - let tree_a = tree_from_runtime_go(rt_net, a, Parent::Redex, &mut vars, &mut fresh); - let tree_b = tree_from_runtime_go(rt_net, b, Parent::Redex, &mut vars, &mut fresh); - rdex.push((tree_a, tree_b)); - } - Net { root, rdex } -} - -pub fn book_from_runtime(rt_book: &run::Book) -> Book { - let mut book = BTreeMap::new(); - for (fid, def) in rt_book.defs.iter() { - if def.node.len() > 0 { - let name = val_to_name(*fid); - let nodes = run::Heap::::init(def.node.len()); - let net = net_from_runtime(&runtime_def_to_runtime_net(&nodes, &def)); - book.insert(name, net); - } - } - book -} diff --git a/book/.hvm/src/fns.rs b/book/.hvm/src/fns.rs deleted file mode 100644 index 848c4b97f..000000000 --- a/book/.hvm/src/fns.rs +++ /dev/null @@ -1,355 +0,0 @@ -use crate::run::{*}; - -pub const F___main : Val = 0xfbec24b31; -pub const F___foo : Val = 0x3efa9cb2; -pub const F_main : Val = 0xc24b31; -pub const F_a : Val = 0x000024; -pub const F__U60.fib : Val = 0xf9e180fe9b25; -pub const F_b : Val = 0x000025; -pub const F_d : Val = 0x000027; -pub const F_c : Val = 0x000026; - -impl<'a, const LAZY: bool> NetFields<'a, LAZY> where [(); LAZY as usize]: { - - pub fn call_native(&mut self, book: &Book, ptr: Ptr, x: Ptr) -> bool { - match ptr.val() { - F___main => { return self.F___main(ptr, Trg::Ptr(x)); } - F___foo => { return self.F___foo(ptr, Trg::Ptr(x)); } - F_main => { return self.F_main(ptr, Trg::Ptr(x)); } - F_a => { return self.F_a(ptr, Trg::Ptr(x)); } - F__U60.fib => { return self.F__U60.fib(ptr, Trg::Ptr(x)); } - F_b => { return self.F_b(ptr, Trg::Ptr(x)); } - F_d => { return self.F_d(ptr, Trg::Ptr(x)); } - F_c => { return self.F_c(ptr, Trg::Ptr(x)); } - _ => { return false; } - } - } - - pub fn L___main(&mut self, lab: Lab) -> bool { - if lab == 0x5 { return true; } - if lab == 0x3 { return true; } - return false; - } - pub fn F___main(&mut self, ptr: Ptr, trg: Trg) -> bool { - if self.get(trg).is_dup() && !self.L___main(self.get(trg).lab()) { - self.copy(self.swap(trg, NULL), ptr); - return true; - } - let _k1 : Trg = Trg::Ptr(Ptr::big(REF, F__U60.fib)); - let _k1x : Trg; - let _k1y : Trg; - // fast apply - if self.get(_k1).tag() == LAM { - self.rwts.anni += 1; - let got = self.swap(_k1, NULL); - _k1x = Trg::Dir(Ptr::new(VR1, 0, got.loc())); - _k1y = Trg::Dir(Ptr::new(VR2, 0, got.loc())); - } else { - let k2 = self.alloc(); - _k1x = Trg::Ptr(Ptr::new(VR1, 0, k2)); - _k1y = Trg::Ptr(Ptr::new(VR2, 0, k2)); - self.safe_link(Trg::Ptr(Ptr::new(LAM, 0, k2)), _k1); - } - // fast erase - if self.get(_k1x).is_skp() { - self.swap(_k1x, NULL); - self.rwts.eras += 1; - } else { - self.safe_link(_k1x, Trg::Ptr(Ptr::new(NUM, 0x6, 0x0))); - } - self.safe_link(trg, _k1y); - return true; - } - - pub fn L___foo(&mut self, lab: Lab) -> bool { - return false; - } - pub fn F___foo(&mut self, ptr: Ptr, trg: Trg) -> bool { - if self.get(trg).is_dup() && !self.L___foo(self.get(trg).lab()) { - self.copy(self.swap(trg, NULL), ptr); - return true; - } - // fast erase - if self.get(trg).is_skp() { - self.swap(trg, NULL); - self.rwts.eras += 1; - } else { - self.safe_link(trg, Trg::Ptr(Ptr::new(NUM, 0x2d, 0x0))); - } - return true; - } - - pub fn L_main(&mut self, lab: Lab) -> bool { - if lab == 0x5 { return true; } - if lab == 0x3 { return true; } - return false; - } - pub fn F_main(&mut self, ptr: Ptr, trg: Trg) -> bool { - if self.get(trg).is_dup() && !self.L_main(self.get(trg).lab()) { - self.copy(self.swap(trg, NULL), ptr); - return true; - } - self.safe_link(trg, Trg::Ptr(Ptr::big(REF, F___main))); - return true; - } - - pub fn L_a(&mut self, lab: Lab) -> bool { - if lab == 0x5 { return true; } - if lab == 0x3 { return true; } - return false; - } - pub fn F_a(&mut self, ptr: Ptr, trg: Trg) -> bool { - if self.get(trg).is_dup() && !self.L_a(self.get(trg).lab()) { - self.copy(self.swap(trg, NULL), ptr); - return true; - } - let trgx : Trg; - let trgy : Trg; - // fast apply - if self.get(trg).tag() == LAM { - self.rwts.anni += 1; - let got = self.swap(trg, NULL); - trgx = Trg::Dir(Ptr::new(VR1, 0, got.loc())); - trgy = Trg::Dir(Ptr::new(VR2, 0, got.loc())); - } else { - let k1 = self.alloc(); - trgx = Trg::Ptr(Ptr::new(VR1, 0, k1)); - trgy = Trg::Ptr(Ptr::new(VR2, 0, k1)); - self.safe_link(Trg::Ptr(Ptr::new(LAM, 0, k1)), trg); - } - let trgxx : Trg; - let trgxy : Trg; - // fast copy - if self.get(trgx).tag() == NUM { - self.rwts.comm += 1; - let got = self.swap(trgx, NULL); - trgxx = Trg::Ptr(got); - trgxy = Trg::Ptr(got); - } else { - let k2 = self.alloc(); - trgxx = Trg::Ptr(Ptr::new(VR1, 0, k2)); - trgxy = Trg::Ptr(Ptr::new(VR2, 0, k2)); - self.safe_link(Trg::Ptr(Ptr::new(DUP, 3, k2)), trgx); - } - let k3 = self.alloc(); - let k4 = self.alloc(); - self.safe_link(Trg::Ptr(Ptr::new(VR2, 0, k4)), trgy); - self.safe_link(Trg::Ptr(Ptr::new(VR1, 0, k4)), trgxy); - self.safe_link(Trg::Ptr(Ptr::new(LAM, 0, k4)), Trg::Ptr(Ptr::new(VR2, 0, k3))); - let k5 = self.alloc(); - self.safe_link(Trg::Ptr(Ptr::new(VR2, 0, k5)), Trg::Ptr(Ptr::big(REF, F_b))); - self.safe_link(Trg::Ptr(Ptr::new(VR1, 0, k5)), Trg::Ptr(Ptr::big(REF, F_d))); - self.safe_link(Trg::Ptr(Ptr::new(LAM, 0, k5)), Trg::Ptr(Ptr::new(VR1, 0, k3))); - self.safe_link(Trg::Ptr(Ptr::new(MAT, 0, k3)), trgxx); - return true; - } - - pub fn L__U60.fib(&mut self, lab: Lab) -> bool { - if lab == 0x5 { return true; } - if lab == 0x3 { return true; } - return false; - } - pub fn F__U60.fib(&mut self, ptr: Ptr, trg: Trg) -> bool { - if self.get(trg).is_dup() && !self.L__U60.fib(self.get(trg).lab()) { - self.copy(self.swap(trg, NULL), ptr); - return true; - } - let k1 : Trg; - let k2 : Trg; - // fast match - if self.get(trg).tag() == LAM && self.heap.get(self.get(trg).loc(), P1).is_num() { - self.rwts.anni += 2; - self.rwts.oper += 1; - let got = self.swap(trg, NULL); - let trgx = Trg::Dir(Ptr::new(VR1, 0, got.loc())); - let trgy = Trg::Dir(Ptr::new(VR2, 0, got.loc())); - if self.get(trgx).val() == 0 { - self.swap(trgx, NULL); - k1 = trgy; - k2 = Trg::Ptr(ERAS); - } else { - self.swap(trgx, Ptr::big(NUM, self.get(trgx).val() - 1)); - k1 = Trg::Ptr(ERAS); - k2 = trg; - } - } else { - let k3 = self.alloc(); - let k4 = self.alloc(); - let k5 = self.alloc(); - self.heap.set(k3, P1, Ptr::new(MAT, 0, k4)); - self.heap.set(k3, P2, Ptr::new(VR2, 0, k4)); - self.heap.set(k4, P1, Ptr::new(LAM, 0, k5)); - self.heap.set(k4, P2, Ptr::new(VR2, 0, k3)); - self.safe_link(Trg::Ptr(Ptr::new(LAM, 0, k3)), trg); - k1 = Trg::Ptr(Ptr::new(VR1, 0, k5)); - k2 = Trg::Ptr(Ptr::new(VR2, 0, k5)); - } - // fast erase - if self.get(k1).is_skp() { - self.swap(k1, NULL); - self.rwts.eras += 1; - } else { - self.safe_link(k1, Trg::Ptr(Ptr::new(NUM, 0x0, 0x0))); - } - self.safe_link(k2, Trg::Ptr(Ptr::big(REF, F_a))); - return true; - } - - pub fn L_b(&mut self, lab: Lab) -> bool { - if lab == 0x5 { return true; } - if lab == 0x3 { return true; } - return false; - } - pub fn F_b(&mut self, ptr: Ptr, trg: Trg) -> bool { - if self.get(trg).is_dup() && !self.L_b(self.get(trg).lab()) { - self.copy(self.swap(trg, NULL), ptr); - return true; - } - let trgx : Trg; - let trgy : Trg; - // fast apply - if self.get(trg).tag() == LAM { - self.rwts.anni += 1; - let got = self.swap(trg, NULL); - trgx = Trg::Dir(Ptr::new(VR1, 0, got.loc())); - trgy = Trg::Dir(Ptr::new(VR2, 0, got.loc())); - } else { - let k1 = self.alloc(); - trgx = Trg::Ptr(Ptr::new(VR1, 0, k1)); - trgy = Trg::Ptr(Ptr::new(VR2, 0, k1)); - self.safe_link(Trg::Ptr(Ptr::new(LAM, 0, k1)), trg); - } - self.safe_link(trgy, Trg::Ptr(Ptr::big(REF, F_c))); - // fast erase - if self.get(trgx).is_skp() { - self.swap(trgx, NULL); - self.rwts.eras += 1; - } else { - self.safe_link(trgx, Trg::Ptr(Ptr::new(ERA, 0x0, 0x0))); - } - return true; - } - - pub fn L_d(&mut self, lab: Lab) -> bool { - return false; - } - pub fn F_d(&mut self, ptr: Ptr, trg: Trg) -> bool { - if self.get(trg).is_dup() && !self.L_d(self.get(trg).lab()) { - self.copy(self.swap(trg, NULL), ptr); - return true; - } - let trgx : Trg; - let trgy : Trg; - // fast apply - if self.get(trg).tag() == LAM { - self.rwts.anni += 1; - let got = self.swap(trg, NULL); - trgx = Trg::Dir(Ptr::new(VR1, 0, got.loc())); - trgy = Trg::Dir(Ptr::new(VR2, 0, got.loc())); - } else { - let k1 = self.alloc(); - trgx = Trg::Ptr(Ptr::new(VR1, 0, k1)); - trgy = Trg::Ptr(Ptr::new(VR2, 0, k1)); - self.safe_link(Trg::Ptr(Ptr::new(LAM, 0, k1)), trg); - } - // fast erase - if self.get(trgy).is_skp() { - self.swap(trgy, NULL); - self.rwts.eras += 1; - } else { - self.safe_link(trgy, Trg::Ptr(Ptr::new(NUM, 0x1, 0x0))); - } - // fast erase - if self.get(trgx).is_skp() { - self.swap(trgx, NULL); - self.rwts.eras += 1; - } else { - self.safe_link(trgx, Trg::Ptr(Ptr::new(ERA, 0x0, 0x0))); - } - return true; - } - - pub fn L_c(&mut self, lab: Lab) -> bool { - if lab == 0x5 { return true; } - if lab == 0x3 { return true; } - return false; - } - pub fn F_c(&mut self, ptr: Ptr, trg: Trg) -> bool { - if self.get(trg).is_dup() && !self.L_c(self.get(trg).lab()) { - self.copy(self.swap(trg, NULL), ptr); - return true; - } - let _k1 : Trg = Trg::Ptr(Ptr::big(REF, F__U60.fib)); - let _k1x : Trg; - let _k1y : Trg; - // fast apply - if self.get(_k1).tag() == LAM { - self.rwts.anni += 1; - let got = self.swap(_k1, NULL); - _k1x = Trg::Dir(Ptr::new(VR1, 0, got.loc())); - _k1y = Trg::Dir(Ptr::new(VR2, 0, got.loc())); - } else { - let k2 = self.alloc(); - _k1x = Trg::Ptr(Ptr::new(VR1, 0, k2)); - _k1y = Trg::Ptr(Ptr::new(VR2, 0, k2)); - self.safe_link(Trg::Ptr(Ptr::new(LAM, 0, k2)), _k1); - } - let k3 = self.alloc(); - self.safe_link(Trg::Ptr(Ptr::new(OP2, 0, k3)), _k1y); - let _k4 : Trg = Trg::Ptr(Ptr::big(REF, F__U60.fib)); - let _k4x : Trg; - let _k4y : Trg; - // fast apply - if self.get(_k4).tag() == LAM { - self.rwts.anni += 1; - let got = self.swap(_k4, NULL); - _k4x = Trg::Dir(Ptr::new(VR1, 0, got.loc())); - _k4y = Trg::Dir(Ptr::new(VR2, 0, got.loc())); - } else { - let k5 = self.alloc(); - _k4x = Trg::Ptr(Ptr::new(VR1, 0, k5)); - _k4y = Trg::Ptr(Ptr::new(VR2, 0, k5)); - self.safe_link(Trg::Ptr(Ptr::new(LAM, 0, k5)), _k4); - } - self.safe_link(_k4y, Trg::Ptr(Ptr::new(VR1, 0, k3))); - let trgx : Trg; - let trgy : Trg; - // fast apply - if self.get(trg).tag() == LAM { - self.rwts.anni += 1; - let got = self.swap(trg, NULL); - trgx = Trg::Dir(Ptr::new(VR1, 0, got.loc())); - trgy = Trg::Dir(Ptr::new(VR2, 0, got.loc())); - } else { - let k6 = self.alloc(); - trgx = Trg::Ptr(Ptr::new(VR1, 0, k6)); - trgy = Trg::Ptr(Ptr::new(VR2, 0, k6)); - self.safe_link(Trg::Ptr(Ptr::new(LAM, 0, k6)), trg); - } - self.safe_link(trgy, Trg::Ptr(Ptr::new(VR2, 0, k3))); - let trgxx : Trg; - let trgxy : Trg; - // fast copy - if self.get(trgx).tag() == NUM { - self.rwts.comm += 1; - let got = self.swap(trgx, NULL); - trgxx = Trg::Ptr(got); - trgxy = Trg::Ptr(got); - } else { - let k7 = self.alloc(); - trgxx = Trg::Ptr(Ptr::new(VR1, 0, k7)); - trgxy = Trg::Ptr(Ptr::new(VR2, 0, k7)); - self.safe_link(Trg::Ptr(Ptr::new(DUP, 5, k7)), trgx); - } - let k8 = self.alloc(); - self.safe_link(Trg::Ptr(Ptr::new(VR2, 0, k8)), _k4x); - self.safe_link(Trg::Ptr(Ptr::new(VR1, 0, k8)), Trg::Ptr(Ptr::new(NUM, 0x2, 0x0))); - self.safe_link(Trg::Ptr(Ptr::new(OP2, 1, k8)), trgxy); - let k9 = self.alloc(); - self.safe_link(Trg::Ptr(Ptr::new(VR2, 0, k9)), _k1x); - self.safe_link(Trg::Ptr(Ptr::new(VR1, 0, k9)), Trg::Ptr(Ptr::new(NUM, 0x1, 0x0))); - self.safe_link(Trg::Ptr(Ptr::new(OP2, 1, k9)), trgxx); - return true; - } - -} \ No newline at end of file diff --git a/book/.hvm/src/jit.rs b/book/.hvm/src/jit.rs deleted file mode 100644 index 139a86c70..000000000 --- a/book/.hvm/src/jit.rs +++ /dev/null @@ -1,452 +0,0 @@ -// Despite the file name, this is not actually a JIT (yet). - -use crate::run; -use crate::ast; - -use std::collections::HashMap; - -pub fn compile_book(book: &run::Book) -> String { - let mut code = String::new(); - - code.push_str(&format!("use crate::run::{{*}};\n")); - code.push_str(&format!("\n")); - - for (fid, def) in book.defs.iter() { - if def.node.len() > 0 { - let name = &ast::val_to_name(*fid as run::Val); - code.push_str(&format!("pub const F_{:4} : Val = 0x{:06x};\n", name, fid)); - } - } - - code.push_str(&format!("\n")); - - code.push_str(&format!("impl<'a, const LAZY: bool> NetFields<'a, LAZY> where [(); LAZY as usize]: {{\n")); - code.push_str(&format!("\n")); - - code.push_str(&format!("{}pub fn call_native(&mut self, book: &Book, ptr: Ptr, x: Ptr) -> bool {{\n", ident(1))); - code.push_str(&format!("{}match ptr.val() {{\n", ident(2))); - for (fid, def) in book.defs.iter() { - if def.node.len() > 0 { - let fun = ast::val_to_name(*fid); - code.push_str(&format!("{}F_{} => {{ return self.F_{}(ptr, Trg::Ptr(x)); }}\n", ident(3), fun, fun)); - } - } - code.push_str(&format!("{}_ => {{ return false; }}\n", ident(3))); - code.push_str(&format!("{}}}\n", ident(2))); - code.push_str(&format!("{}}}\n", ident(1))); - code.push_str(&format!("\n")); - - for (fid, def) in book.defs.iter() { - if def.node.len() > 0 { - code.push_str(&compile_term(&book, 1, *fid)); - code.push_str(&format!("\n")); - } - } - - code.push_str(&format!("}}")); - - return code; - -} - -pub fn ident(tab: usize) -> String { - return " ".repeat(tab); -} - -pub fn tag(tag: run::Tag) -> &'static str { - match tag { - run::VR1 => "VR1", - run::VR2 => "VR2", - run::RD1 => "RD1", - run::RD2 => "RD2", - run::REF => "REF", - run::ERA => "ERA", - run::NUM => "NUM", - run::OP2 => "OP2", - run::OP1 => "OP1", - run::MAT => "MAT", - run::LAM => "LAM", - run::TUP => "TUP", - run::DUP => "DUP", - _ => unreachable!(), - } -} - -pub fn atom(ptr: run::Ptr) -> String { - if ptr.is_ref() { - return format!("Ptr::big(REF, F_{})", ast::val_to_name(ptr.val())); - } else { - return format!("Ptr::new({}, 0x{:x}, 0x{:x})", tag(ptr.tag()), ptr.lab(), ptr.loc()); - } -} - -struct Target { - nam: String -} - -impl Target { - fn show(&self) -> String { - format!("{}", self.nam) - } - - fn get(&self) -> String { - format!("self.get({})", self.nam) - } - - fn swap(&self, value: &str) -> String { - format!("self.swap({}, {})", self.nam, value) - } - - fn take(&self) -> String { - self.swap(&"NULL") - } -} - -pub fn compile_term(book: &run::Book, tab: usize, fid: run::Val) -> String { - - // returns a fresh variable: 'v' - fn fresh(newx: &mut usize) -> String { - *newx += 1; - format!("k{}", newx) - } - - fn call_redex( - book : &run::Book, - tab : usize, - newx : &mut usize, - vars : &mut HashMap, - def : &run::Def, - rdex : (run::Ptr, run::Ptr), - ) -> String { - let (rf, rx) = adjust_redex(rdex.0, rdex.1); - let rf_name = format!("_{}", fresh(newx)); - let mut code = String::new(); - code.push_str(&format!("{}let {} : Trg = Trg::Ptr({});\n", ident(tab), rf_name, &atom(rf))); - code.push_str(&burn(book, tab, None, newx, vars, def, rx, &Target { nam: rf_name })); - return code; - } - - fn call( - book : &run::Book, - tab : usize, - tail : Option, - newx : &mut usize, - vars : &mut HashMap, - fid : run::Val, - trg : &Target, - ) -> String { - //let newx = &mut 0; - //let vars = &mut HashMap::new(); - - let def = &book.get(fid).unwrap(); - - // Tail call - // TODO: when I manually edited a file to implement tail call, the single-core performance - // increased a lot, but it resulted in a single thread withholding all redexes and, thus, - // the program went single-core mode again. I believe a smarter redex sharing structure is - // necessary for us to implement tail calls in a way that doesn't sacrify parallelism. - //if tail.is_some() && def.rdex.len() > 0 && def.rdex[0].0.is_ref() && def.rdex[0].0.loc() == tail.unwrap() { - //println!("tco {}", ast::val_to_name(tail.unwrap() as run::Val)); - //let mut code = String::new(); - //for rdex in &def.rdex[1..] { - //code.push_str(&call_redex(book, tab, newx, vars, def, *rdex)); - //} - //code.push_str(&burn(book, tab, Some(fid), newx, vars, def, def.node[0].1, &trg)); - //code.push_str(&call_redex(book, tab, newx, vars, def, def.rdex[0])); - //return code; - //} - - // Normal call - let mut code = String::new(); - for rdex in &def.rdex { - code.push_str(&call_redex(book, tab, newx, vars, def, *rdex)); - } - code.push_str(&burn(book, tab, Some(fid), newx, vars, def, def.node[0].2, &trg)); - return code; - } - - fn burn( - book : &run::Book, - tab : usize, - tail : Option, - newx : &mut usize, - vars : &mut HashMap, - def : &run::Def, - ptr : run::Ptr, - trg : &Target, - ) -> String { - //println!("burn {:08x} {}", ptr.0, x); - let mut code = String::new(); - - // ( ret) ~ (#X R) - // ------------------------------- fast match - // if X == 0: - // ifz ~ R - // ifs ~ * - // else: - // ifz ~ * - // ifs ~ (#(X-1) R) - // When ifs is REF, tail-call optimization is applied. - if ptr.tag() == run::LAM { - let mat = def.node[ptr.loc() as usize].1; - let rty = def.node[ptr.loc() as usize].2; - if mat.tag() == run::MAT { - let cse = def.node[mat.loc() as usize].1; - let rtx = def.node[mat.loc() as usize].2; - let got = def.node[rty.loc() as usize]; - let rtz = if rty.tag() == run::VR1 { got.1 } else { got.2 }; - if cse.tag() == run::LAM && rtx.is_var() && rtx == rtz { - let ifz = def.node[cse.loc() as usize].1; - let ifs = def.node[cse.loc() as usize].2; - let c_z = Target { nam: fresh(newx) }; - let c_s = Target { nam: fresh(newx) }; - let num = Target { nam: format!("{}x", trg.show()) }; - let res = Target { nam: format!("{}y", trg.show()) }; - let lam = fresh(newx); - let mat = fresh(newx); - let cse = fresh(newx); - code.push_str(&format!("{}let {} : Trg;\n", ident(tab), &c_z.show())); - code.push_str(&format!("{}let {} : Trg;\n", ident(tab), &c_s.show())); - code.push_str(&format!("{}// fast match\n", ident(tab))); - code.push_str(&format!("{}if {}.tag() == LAM && self.heap.get({}.loc(), P1).is_num() {{\n", ident(tab), trg.get(), trg.get())); - code.push_str(&format!("{}self.rwts.anni += 2;\n", ident(tab+1))); - code.push_str(&format!("{}self.rwts.oper += 1;\n", ident(tab+1))); - code.push_str(&format!("{}let got = {};\n", ident(tab+1), trg.take())); - code.push_str(&format!("{}let {} = Trg::Dir(Ptr::new(VR1, 0, got.loc()));\n", ident(tab+1), num.show())); - code.push_str(&format!("{}let {} = Trg::Dir(Ptr::new(VR2, 0, got.loc()));\n", ident(tab+1), res.show())); - code.push_str(&format!("{}if {}.val() == 0 {{\n", ident(tab+1), num.get())); - code.push_str(&format!("{}{};\n", ident(tab+2), num.take())); - code.push_str(&format!("{}{} = {};\n", ident(tab+2), &c_z.show(), res.show())); - code.push_str(&format!("{}{} = Trg::Ptr({});\n", ident(tab+2), &c_s.show(), "ERAS")); - code.push_str(&format!("{}}} else {{\n", ident(tab+1))); - code.push_str(&format!("{}{};\n", ident(tab+2), num.swap(&format!("Ptr::big(NUM, {}.val() - 1)", num.get())))); - code.push_str(&format!("{}{} = Trg::Ptr({});\n", ident(tab+2), &c_z.show(), "ERAS")); - code.push_str(&format!("{}{} = {};\n", ident(tab+2), &c_s.show(), trg.show())); - code.push_str(&format!("{}}}\n", ident(tab+1))); - code.push_str(&format!("{}}} else {{\n", ident(tab))); - code.push_str(&format!("{}let {} = self.alloc();\n", ident(tab+1), lam)); - code.push_str(&format!("{}let {} = self.alloc();\n", ident(tab+1), mat)); - code.push_str(&format!("{}let {} = self.alloc();\n", ident(tab+1), cse)); - code.push_str(&format!("{}self.heap.set({}, P1, Ptr::new(MAT, 0, {}));\n", ident(tab+1), lam, mat)); - code.push_str(&format!("{}self.heap.set({}, P2, Ptr::new(VR2, 0, {}));\n", ident(tab+1), lam, mat)); - code.push_str(&format!("{}self.heap.set({}, P1, Ptr::new(LAM, 0, {}));\n", ident(tab+1), mat, cse)); - code.push_str(&format!("{}self.heap.set({}, P2, Ptr::new(VR2, 0, {}));\n", ident(tab+1), mat, lam)); - code.push_str(&format!("{}self.safe_link(Trg::Ptr(Ptr::new(LAM, 0, {})), {});\n", ident(tab+1), lam, trg.show())); - code.push_str(&format!("{}{} = Trg::Ptr(Ptr::new(VR1, 0, {}));\n", ident(tab+1), &c_z.show(), cse)); - code.push_str(&format!("{}{} = Trg::Ptr(Ptr::new(VR2, 0, {}));\n", ident(tab+1), &c_s.show(), cse)); - code.push_str(&format!("{}}}\n", ident(tab))); - code.push_str(&burn(book, tab, None, newx, vars, def, ifz, &c_z)); - code.push_str(&burn(book, tab, tail, newx, vars, def, ifs, &c_s)); - return code; - } - } - } - - // #A ~ <+ #B r> - // ----------------- fast op - // r <~ #(op(+,A,B)) - if ptr.is_op2() { - let val = def.node[ptr.loc() as usize].1; - let ret = def.node[ptr.loc() as usize].2; - if let Some(val) = got(vars, def, val) { - let val = Target { nam: val }; - let nxt = Target { nam: fresh(newx) }; - let op2 = fresh(newx); - code.push_str(&format!("{}let {} : Trg;\n", ident(tab), &nxt.show())); - code.push_str(&format!("{}// fast op\n", ident(tab))); - code.push_str(&format!("{}if {}.is_num() && {}.is_num() {{\n", ident(tab), trg.get(), val.get())); - code.push_str(&format!("{}self.rwts.oper += 2;\n", ident(tab+1))); // OP2 + OP1 - code.push_str(&format!("{}let vx = {};\n", ident(tab+1), trg.take())); - code.push_str(&format!("{}let vy = {};\n", ident(tab+1), val.take())); - code.push_str(&format!("{}{} = Trg::Ptr(Ptr::big(NUM, self.op({},vx.val(),vy.val())));\n", ident(tab+1), &nxt.show(), ptr.lab())); - code.push_str(&format!("{}}} else {{\n", ident(tab))); - code.push_str(&format!("{}let {} = self.alloc();\n", ident(tab+1), op2)); - code.push_str(&format!("{}self.safe_link(Trg::Ptr(Ptr::new(VR1, 0, {})), {});\n", ident(tab+1), op2, val.show())); - code.push_str(&format!("{}self.safe_link(Trg::Ptr(Ptr::new(OP2, {}, {})), {});\n", ident(tab+1), ptr.lab(), op2, trg.show())); - code.push_str(&format!("{}{} = Trg::Ptr(Ptr::new(VR2, 0, {}));\n", ident(tab+1), &nxt.show(), op2)); - code.push_str(&format!("{}}}\n", ident(tab))); - code.push_str(&burn(book, tab, None, newx, vars, def, ret, &nxt)); - return code; - } - } - - // {p1 p2} <~ #N - // ------------- fast copy - // p1 <~ #N - // p2 <~ #N - if ptr.is_dup() { - let x1 = Target { nam: format!("{}x", trg.show()) }; - let x2 = Target { nam: format!("{}y", trg.show()) }; - let p1 = def.node[ptr.loc() as usize].1; - let p2 = def.node[ptr.loc() as usize].2; - let lc = fresh(newx); - code.push_str(&format!("{}let {} : Trg;\n", ident(tab), &x1.show())); - code.push_str(&format!("{}let {} : Trg;\n", ident(tab), &x2.show())); - code.push_str(&format!("{}// fast copy\n", ident(tab))); - code.push_str(&format!("{}if {}.tag() == NUM {{\n", ident(tab), trg.get())); - code.push_str(&format!("{}self.rwts.comm += 1;\n", ident(tab+1))); - code.push_str(&format!("{}let got = {};\n", ident(tab+1), trg.take())); - code.push_str(&format!("{}{} = Trg::Ptr(got);\n", ident(tab+1), &x1.show())); - code.push_str(&format!("{}{} = Trg::Ptr(got);\n", ident(tab+1), &x2.show())); - code.push_str(&format!("{}}} else {{\n", ident(tab))); - code.push_str(&format!("{}let {} = self.alloc();\n", ident(tab+1), lc)); - code.push_str(&format!("{}{} = Trg::Ptr(Ptr::new(VR1, 0, {}));\n", ident(tab+1), &x1.show(), lc)); - code.push_str(&format!("{}{} = Trg::Ptr(Ptr::new(VR2, 0, {}));\n", ident(tab+1), &x2.show(), lc)); - code.push_str(&format!("{}self.safe_link(Trg::Ptr(Ptr::new({}, {}, {})), {});\n", ident(tab+1), tag(ptr.tag()), ptr.lab(), lc, trg.show())); - code.push_str(&format!("{}}}\n", ident(tab))); - code.push_str(&burn(book, tab, None, newx, vars, def, p2, &x2)); - code.push_str(&burn(book, tab, None, newx, vars, def, p1, &x1)); - return code; - } - - // (p1 p2) <~ (x1 x2) - // ------------------ fast apply - // p1 <~ x1 - // p2 <~ x2 - if ptr.is_ctr() && ptr.tag() == run::LAM { - let x1 = Target { nam: format!("{}x", trg.show()) }; - let x2 = Target { nam: format!("{}y", trg.show()) }; - let p1 = def.node[ptr.loc() as usize].1; - let p2 = def.node[ptr.loc() as usize].2; - let lc = fresh(newx); - code.push_str(&format!("{}let {} : Trg;\n", ident(tab), &x1.show())); - code.push_str(&format!("{}let {} : Trg;\n", ident(tab), &x2.show())); - code.push_str(&format!("{}// fast apply\n", ident(tab))); - code.push_str(&format!("{}if {}.tag() == {} {{\n", ident(tab), trg.get(), tag(ptr.tag()))); - code.push_str(&format!("{}self.rwts.anni += 1;\n", ident(tab+1))); - code.push_str(&format!("{}let got = {};\n", ident(tab+1), trg.take())); - code.push_str(&format!("{}{} = Trg::Dir(Ptr::new(VR1, 0, got.loc()));\n", ident(tab+1), &x1.show())); - code.push_str(&format!("{}{} = Trg::Dir(Ptr::new(VR2, 0, got.loc()));\n", ident(tab+1), &x2.show())); - code.push_str(&format!("{}}} else {{\n", ident(tab))); - code.push_str(&format!("{}let {} = self.alloc();\n", ident(tab+1), lc)); - code.push_str(&format!("{}{} = Trg::Ptr(Ptr::new(VR1, 0, {}));\n", ident(tab+1), &x1.show(), lc)); - code.push_str(&format!("{}{} = Trg::Ptr(Ptr::new(VR2, 0, {}));\n", ident(tab+1), &x2.show(), lc)); - code.push_str(&format!("{}self.safe_link(Trg::Ptr(Ptr::new({}, 0, {})), {});\n", ident(tab+1), tag(ptr.tag()), lc, trg.show())); - code.push_str(&format!("{}}}\n", ident(tab))); - code.push_str(&burn(book, tab, None, newx, vars, def, p2, &x2)); - code.push_str(&burn(book, tab, None, newx, vars, def, p1, &x1)); - return code; - } - - //// TODO: implement inlining correctly - //// NOTE: enabling this makes dec_bits_tree hang; investigate - //if ptr.is_ref() && tail.is_some() { - //code.push_str(&format!("{}// inline @{}\n", ident(tab), ast::val_to_name(ptr.loc() as run::Val))); - //code.push_str(&format!("{}if !{}.is_skp() {{\n", ident(tab), trg.get())); - //code.push_str(&format!("{}self.rwts.dref += 1;\n", ident(tab+1))); - //code.push_str(&call(book, tab+1, tail, newx, &mut HashMap::new(), ptr.loc(), trg)); - //code.push_str(&format!("{}}} else {{\n", ident(tab))); - //code.push_str(&make(tab+1, newx, vars, def, ptr, &trg.show())); - //code.push_str(&format!("{}}}\n", ident(tab))); - //return code; - //} - - // ATOM <~ * - // --------- fast erase - // nothing - if ptr.is_num() || ptr.is_era() { - code.push_str(&format!("{}// fast erase\n", ident(tab))); - code.push_str(&format!("{}if {}.is_skp() {{\n", ident(tab), trg.get())); - code.push_str(&format!("{}{};\n", ident(tab+1), trg.take())); - code.push_str(&format!("{}self.rwts.eras += 1;\n", ident(tab+1))); - code.push_str(&format!("{}}} else {{\n", ident(tab))); - code.push_str(&make(tab+1, newx, vars, def, ptr, &trg.show())); - code.push_str(&format!("{}}}\n", ident(tab))); - return code; - } - - code.push_str(&make(tab, newx, vars, def, ptr, &trg.show())); - return code; - } - - fn make( - tab : usize, - newx : &mut usize, - vars : &mut HashMap, - def : &run::Def, - ptr : run::Ptr, - trg : &String, - ) -> String { - //println!("make {:08x} {}", ptr.0, x); - let mut code = String::new(); - if ptr.is_nod() { - let lc = fresh(newx); - let p1 = def.node[ptr.loc() as usize].1; - let p2 = def.node[ptr.loc() as usize].2; - code.push_str(&format!("{}let {} = self.alloc();\n", ident(tab), lc)); - code.push_str(&make(tab, newx, vars, def, p2, &format!("Trg::Ptr(Ptr::new(VR2, 0, {}))", lc))); - code.push_str(&make(tab, newx, vars, def, p1, &format!("Trg::Ptr(Ptr::new(VR1, 0, {}))", lc))); - code.push_str(&format!("{}self.safe_link(Trg::Ptr(Ptr::new({}, {}, {})), {});\n", ident(tab), tag(ptr.tag()), ptr.lab(), lc, trg)); - } else if ptr.is_var() { - match got(vars, def, ptr) { - None => { - //println!("-var fst"); - vars.insert(ptr, trg.clone()); - }, - Some(got) => { - //println!("-var snd"); - code.push_str(&format!("{}self.safe_link({}, {});\n", ident(tab), trg, got)); - } - } - } else { - code.push_str(&format!("{}self.safe_link({}, Trg::Ptr({}));\n", ident(tab), trg, atom(ptr))); - } - return code; - } - - fn got( - vars : &HashMap, - def : &run::Def, - ptr : run::Ptr, - ) -> Option { - if ptr.is_var() { - let got = def.node[ptr.loc() as usize]; - let slf = if ptr.tag() == run::VR1 { got.1 } else { got.2 }; - return vars.get(&slf).cloned(); - } else { - return None; - } - } - - let fun = ast::val_to_name(fid); - let def = &book.get(fid).unwrap(); - - let mut code = String::new(); - // Given a label, returns true if the definition contains that dup label, directly or not - code.push_str(&format!("{}pub fn L_{}(&mut self, lab: Lab) -> bool {{\n", ident(tab), fun)); - for dup in &def.labs { - code.push_str(&format!("{}if lab == 0x{:x} {{ return true; }}\n", ident(tab+1), dup)); - } - code.push_str(&format!("{}return false;\n", ident(tab+1))); - code.push_str(&format!("{}}}\n", ident(tab))); - // Calls the definition, performing inline rewrites when possible, and expanding it when not - code.push_str(&format!("{}pub fn F_{}(&mut self, ptr: Ptr, trg: Trg) -> bool {{\n", ident(tab), fun)); - code.push_str(&format!("{}if self.get(trg).is_dup() && !self.L_{}(self.get(trg).lab()) {{\n", ident(tab+1), fun)); - code.push_str(&format!("{}self.copy(self.swap(trg, NULL), ptr);\n", ident(tab+2))); - code.push_str(&format!("{}return true;\n", ident(tab+2))); - code.push_str(&format!("{}}}\n", ident(tab+1))); - code.push_str(&call(book, tab+1, None, &mut 0, &mut HashMap::new(), fid, &Target { nam: "trg".to_string() })); - code.push_str(&format!("{}return true;\n", ident(tab+1))); - code.push_str(&format!("{}}}\n", ident(tab))); - - return code; -} - -// TODO: HVM-Lang must always output in this form. -fn adjust_redex(rf: run::Ptr, rx: run::Ptr) -> (run::Ptr, run::Ptr) { - if rf.is_skp() && !rx.is_skp() { - return (rf, rx); - } else if !rf.is_skp() && rx.is_skp() { - return (rx, rf); - } else { - println!("Invalid redex. Compiled HVM requires that ALL defs are in the form:"); - println!("@name = ROOT"); - println!(" & ATOM ~ TERM"); - println!(" & ATOM ~ TERM"); - println!(" & ATOM ~ TERM"); - println!(" ..."); - println!("Where ATOM must be either a ref (`@foo`), a num (`#123`), or an era (`*`)."); - println!("If you used HVM-Lang, please report on https://github.com/HigherOrderCO/hvm-lang."); - panic!("Invalid HVMC file."); - } -} diff --git a/book/.hvm/src/lib.rs b/book/.hvm/src/lib.rs deleted file mode 100644 index 6912fad47..000000000 --- a/book/.hvm/src/lib.rs +++ /dev/null @@ -1,14 +0,0 @@ -#![feature(generic_const_exprs)] -#![allow(incomplete_features)] - -#![allow(dead_code)] -#![allow(unused_variables)] -#![allow(unused_imports)] -#![allow(non_snake_case)] -#![allow(non_upper_case_globals)] - -pub mod ast; -pub mod fns; -pub mod jit; -pub mod run; -pub mod u60; diff --git a/book/.hvm/src/main.rs b/book/.hvm/src/main.rs deleted file mode 100644 index 58b8bf196..000000000 --- a/book/.hvm/src/main.rs +++ /dev/null @@ -1,256 +0,0 @@ -#![feature(generic_const_exprs)] -#![allow(incomplete_features)] - -#![allow(dead_code)] -#![allow(non_snake_case)] -#![allow(non_upper_case_globals)] -#![allow(unused_imports)] -#![allow(unused_variables)] - -use std::env; -use std::fs; - -use hvmc::ast; -use hvmc::fns; -use hvmc::jit; -use hvmc::run; -use hvmc::u60; - -use std::collections::HashSet; - -struct Args { - func: String, - argm: String, - opts: HashSet, -} - -fn get_args() -> Args { - let args: Vec = env::args().collect(); - let func = args.get(1).unwrap_or(&"help".to_string()).to_string(); - let argm = args.get(2).unwrap_or(&"".to_string()).to_string(); - let opts = args.iter().skip(3).map(|s| s.to_string()).collect::>(); - return Args { func, argm, opts }; -} - -// Runs 'main' without showing the CLI options -fn run_without_cli(args: Args) { - let lazy = args.opts.contains("-L"); - let seq = lazy || args.opts.contains("-1"); - let file = args.argm; - let book = run::Book::new(); - let mut net = run::Net::new(1 << 28, false); - let begin = std::time::Instant::now(); - if lazy { todo!() } - if seq { - net.normal(&book); - } else { - net.parallel_normal(&book); - } - println!("{}", net.show()); - print_stats(&net, begin); -} - -fn run_with_cli(args: Args) -> Result<(), Box> { - let lazy = args.opts.contains("-L"); - let seq = lazy || args.opts.contains("-1"); - match args.func.as_str() { - "run" => { - if args.argm.len() > 0 { - let file = args.argm; - let book = load_book(&file); - let mut net = run::Net::new(1 << 28, lazy); - let begin = std::time::Instant::now(); - if seq { - net.normal(&book); - } else { - net.parallel_normal(&book); - } - //println!("{}", net.show()); - println!("{}", net.show()); - if args.opts.contains("-s") { - print_stats(&net, begin); - } - } else { - println!("Usage: hvmc run [-s]"); - std::process::exit(1); - } - } - "compile" => { - if args.argm.len() > 0 { - let file = args.argm; - let book = load_book(&file); - let net = run::Net::new(1 << 28, lazy); - let begin = std::time::Instant::now(); - compile_book_to_rust_crate(&file, &book)?; - compile_rust_crate_to_executable(&file)?; - } else { - println!("Usage: hvmc compile "); - std::process::exit(1); - } - } - "gen-cuda-book" => { - if args.argm.len() > 0 { - let file = args.argm; - let book = load_book(&file); - let net = run::Net::new(1 << 28, lazy); - let begin = std::time::Instant::now(); - println!("{}", gen_cuda_book(&book)); - } else { - println!("Usage: hvmc gen-cuda-book "); - std::process::exit(1); - } - } - _ => { - println!("Usage: hvmc [-s]"); - println!("Commands:"); - println!(" run - Run the given file"); - println!(" compile - Compile the given file to an executable"); - println!(" gen-cuda-book - Generate a CUDA book from the given file"); - println!("Options:"); - println!(" [-s] Show stats, including rewrite count"); - println!(" [-1] Single-core mode (no parallelism)"); - } - } - Ok(()) -} - -#[cfg(not(feature = "hvm_cli_options"))] -fn main() { - run_without_cli(get_args()) -} - -#[cfg(feature = "hvm_cli_options")] -fn main() -> Result<(), Box> { - run_with_cli(get_args()) -} - -fn print_stats(net: &run::Net, begin: std::time::Instant) { - let rewrites = net.get_rewrites(); - println!("RWTS : {}", rewrites.total()); - println!("- ANNI : {}", rewrites.anni); - println!("- COMM : {}", rewrites.comm); - println!("- ERAS : {}", rewrites.eras); - println!("- DREF : {}", rewrites.dref); - println!("- OPER : {}", rewrites.oper); - println!("TIME : {:.3} s", (begin.elapsed().as_millis() as f64) / 1000.0); - println!("RPS : {:.3} m", (rewrites.total() as f64) / (begin.elapsed().as_millis() as f64) / 1000.0); -} - -// Load file -fn load_book(file: &str) -> run::Book { - let Ok(file) = fs::read_to_string(file) else { - eprintln!("Input file not found"); - std::process::exit(1); - }; - return ast::book_to_runtime(&ast::do_parse_book(&file)); -} - -pub fn compile_book_to_rust_crate(f_name: &str, book: &run::Book) -> Result<(), std::io::Error> { - let fns_rs = jit::compile_book(book); - let outdir = ".hvm"; - if std::path::Path::new(&outdir).exists() { - fs::remove_dir_all(&outdir)?; - } - let cargo_toml = include_str!("../Cargo.toml"); - let cargo_toml = cargo_toml.split("##--COMPILER-CUTOFF--##").next().unwrap(); - let cargo_toml = cargo_toml.replace("\"hvm_cli_options\"", ""); - fs::create_dir_all(&format!("{}/src", outdir))?; - fs::write(".hvm/Cargo.toml", cargo_toml)?; - fs::write(".hvm/src/ast.rs", include_str!("../src/ast.rs"))?; - fs::write(".hvm/src/jit.rs", include_str!("../src/jit.rs"))?; - fs::write(".hvm/src/lib.rs", include_str!("../src/lib.rs"))?; - fs::write(".hvm/src/main.rs", include_str!("../src/main.rs"))?; - fs::write(".hvm/src/run.rs", include_str!("../src/run.rs"))?; - fs::write(".hvm/src/u60.rs", include_str!("../src/u60.rs"))?; - fs::write(".hvm/src/fns.rs", fns_rs)?; - return Ok(()); -} - -pub fn compile_rust_crate_to_executable(f_name: &str) -> Result<(), std::io::Error> { - let output = std::process::Command::new("cargo").current_dir("./.hvm").arg("build").arg("--release").output()?; - let target = format!("./{}", f_name.replace(".hvmc", "")); - if std::path::Path::new(&target).exists() { - fs::remove_file(&target)?; - } - fs::copy("./.hvm/target/release/hvmc", target)?; - return Ok(()); -} - -// TODO: move to hvm-cuda repo -pub fn gen_cuda_book(book: &run::Book) -> String { - use std::collections::BTreeMap; - - // Sort the book.defs by key - let mut defs = BTreeMap::new(); - for (fid, def) in book.defs.iter() { - if def.node.len() > 0 { - defs.insert(fid, def.clone()); - } - } - - // Initializes code - let mut code = String::new(); - - // Generate function ids - for (i, id) in defs.keys().enumerate() { - code.push_str(&format!("const u32 F_{} = 0x{:x};\n", crate::ast::val_to_name(**id), id)); - } - code.push_str("\n"); - - // Create book - code.push_str("u32 BOOK_DATA[] = {\n"); - - // Generate book data - for (i, (id, net)) in defs.iter().enumerate() { - let node_len = net.node.len(); - let rdex_len = net.rdex.len(); - - code.push_str(&format!(" // @{}\n", crate::ast::val_to_name(**id))); - - // Collect all pointers from root, nodes and rdex into a single buffer - code.push_str(&format!(" // .nlen\n")); - code.push_str(&format!(" 0x{:08X},\n", node_len)); - code.push_str(&format!(" // .rlen\n")); - code.push_str(&format!(" 0x{:08X},\n", rdex_len)); - - // .node - code.push_str(" // .node\n"); - for (i, node) in net.node.iter().enumerate() { - code.push_str(&format!(" 0x{:08X},", node.1.0)); - code.push_str(&format!(" 0x{:08X},", node.2.0)); - if (i + 1) % 4 == 0 { - code.push_str("\n"); - } - } - if node_len % 4 != 0 { - code.push_str("\n"); - } - - // .rdex - code.push_str(" // .rdex\n"); - for (i, (a, b)) in net.rdex.iter().enumerate() { - code.push_str(&format!(" 0x{:08X},", a.0)); - code.push_str(&format!(" 0x{:08X},", b.0)); - if (i + 1) % 4 == 0 { - code.push_str("\n"); - } - } - if rdex_len % 4 != 0 { - code.push_str("\n"); - } - } - - code.push_str("};\n\n"); - - code.push_str("u32 JUMP_DATA[] = {\n"); - - let mut index = 0; - for (i, fid) in defs.keys().enumerate() { - code.push_str(&format!(" 0x{:08X}, 0x{:08X}, // @{}\n", fid, index, crate::ast::val_to_name(**fid))); - index += 2 + 2 * defs[fid].node.len() as u32 + 2 * defs[fid].rdex.len() as u32; - } - - code.push_str("};"); - - return code; -} diff --git a/book/.hvm/src/run.rs b/book/.hvm/src/run.rs deleted file mode 100644 index d390cdcca..000000000 --- a/book/.hvm/src/run.rs +++ /dev/null @@ -1,1389 +0,0 @@ -// An efficient Interaction Combinator runtime -// =========================================== -// This file implements an efficient interaction combinator runtime. Nodes are represented by 2 aux -// ports (P1, P2), with the main port (P1) omitted. A separate vector, 'rdex', holds main ports, -// and, thus, tracks active pairs that can be reduced in parallel. Pointers are unboxed, meaning -// that ERAs, NUMs and REFs don't use any additional space. REFs lazily expand to closed nets when -// they interact with nodes, and are cleared when they interact with ERAs, allowing for constant -// space evaluation of recursive functions on Scott encoded datatypes. - -use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering}; -use std::sync::{Arc, Barrier}; -use std::collections::HashMap; -use std::collections::HashSet; -use crate::u60; - -pub type Tag = u8; -pub type Lab = u32; -pub type Loc = u32; -pub type Val = u64; -pub type AVal = AtomicU64; - -// Core terms. -pub const VR1: Tag = 0x0; // Variable to aux port 1 -pub const VR2: Tag = 0x1; // Variable to aux port 2 -pub const RD1: Tag = 0x2; // Redirect to aux port 1 -pub const RD2: Tag = 0x3; // Redirect to aux port 2 -pub const REF: Tag = 0x4; // Lazy closed net -pub const ERA: Tag = 0x5; // Unboxed eraser -pub const NUM: Tag = 0x6; // Unboxed number -pub const OP2: Tag = 0x7; // Binary numeric operation -pub const OP1: Tag = 0x8; // Unary numeric operation -pub const MAT: Tag = 0x9; // Numeric pattern-matching -pub const LAM: Tag = 0xA; // Main port of lam node -pub const TUP: Tag = 0xB; // Main port of tup node -pub const DUP: Tag = 0xC; // Main port of dup node -pub const END: Tag = 0xE; // Last pointer tag - -// Numeric operations. -pub const ADD: Lab = 0x00; // addition -pub const SUB: Lab = 0x01; // subtraction -pub const MUL: Lab = 0x02; // multiplication -pub const DIV: Lab = 0x03; // division -pub const MOD: Lab = 0x04; // modulus -pub const EQ : Lab = 0x05; // equal-to -pub const NE : Lab = 0x06; // not-equal-to -pub const LT : Lab = 0x07; // less-than -pub const GT : Lab = 0x08; // greater-than -pub const LTE: Lab = 0x09; // less-than-or-equal -pub const GTE: Lab = 0x0A; // greater-than-or-equal -pub const AND: Lab = 0x0B; // logical-and -pub const OR : Lab = 0x0C; // logical-or -pub const XOR: Lab = 0x0D; // logical-xor -pub const LSH: Lab = 0x0E; // left-shift -pub const RSH: Lab = 0x0F; // right-shift -pub const NOT: Lab = 0x10; // logical-not - -pub const ERAS: Ptr = Ptr::new(ERA, 0, 0); -pub const ROOT: Ptr = Ptr::new(VR2, 0, 0); -pub const NULL: Ptr = Ptr(0x0000_0000_0000_0000); -pub const GONE: Ptr = Ptr(0xFFFF_FFFF_FFFF_FFEF); -pub const LOCK: Ptr = Ptr(0xFFFF_FFFF_FFFF_FFFF); // if last digit is F it will be seen as a CTR - -// An auxiliary port. -pub type Port = Val; -pub const P1: Port = 0; -pub const P2: Port = 1; - -// A tagged pointer. -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)] -pub struct Ptr(pub Val); - -// An atomic tagged pointer. -pub struct APtr(pub AVal); - -// FIXME: the 'this' pointer of headers is wasteful, since it is only used once in the lazy -// reducer, and, there, only the tag/lab is needed, because the loc is already known. As such, we -// could actually store only the tag/lab, saving up 32 bits per node. - -// A principal port, used on lazy mode. -pub struct Head { - this: Ptr, // points to this node's port 0 - targ: Ptr, // points to the target port 0 -} - -// An atomic principal port, used on lazy mode. -pub struct AHead { - this: APtr, // points to this node's port 0 - targ: APtr, // points to the target port 0 -} - -// An interaction combinator node. -pub type Node = ([ Head; LAZY as usize], Ptr, Ptr); -pub type ANode = ([AHead; LAZY as usize], APtr, APtr); - -// A target pointer, with implied ownership. -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)] -pub enum Trg { - Dir(Ptr), // we don't own the pointer, so we point to its location - Ptr(Ptr), // we own the pointer, so we store it directly -} - -// The global node buffer. -pub type Nodes = [ANode]; - -// A handy wrapper around Nodes. -pub struct Heap<'a, const LAZY: bool> -where [(); LAZY as usize]: { - pub nodes: &'a Nodes, -} - -// Rewrite counter. -#[derive(Copy, Clone)] -pub struct Rewrites { - pub anni: usize, // anni rewrites - pub comm: usize, // comm rewrites - pub eras: usize, // eras rewrites - pub dref: usize, // dref rewrites - pub oper: usize, // oper rewrites -} - -// Rewrite counter, atomic. -pub struct AtomicRewrites { - pub anni: AtomicUsize, // anni rewrites - pub comm: AtomicUsize, // comm rewrites - pub eras: AtomicUsize, // eras rewrites - pub dref: AtomicUsize, // dref rewrites - pub oper: AtomicUsize, // oper rewrites -} - -// An allocation area delimiter -pub struct Area { - pub init: usize, // first allocation index - pub size: usize, // total nodes in area -} - -// A interaction combinator net. -pub struct NetFields<'a, const LAZY: bool> -where [(); LAZY as usize]: { - pub tid : usize, // thread id - pub tids: usize, // thread count - pub labs: Lab, // dup labels - pub heap: Heap<'a, LAZY>, // nodes - pub rdex: Vec<(Ptr,Ptr)>, // redexes - pub locs: Vec, - pub area: Area, // allocation area - pub next: usize, // next allocation index within area - pub rwts: Rewrites, // rewrite count -} - -// A compact closed net, used for dereferences. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Def { - pub labs: HashSet>, - pub rdex: Vec<(Ptr, Ptr)>, - pub node: Vec<((), Ptr, Ptr)>, -} - -// A map of id to definitions (closed nets). -pub struct Book { - pub defs: HashMap>, -} - -impl Ptr { - #[inline(always)] - pub const fn new(tag: Tag, lab: Lab, loc: Loc) -> Self { - Ptr(((loc as Val) << 32) | ((lab as Val) << 4) | (tag as Val)) - } - - #[inline(always)] - pub const fn big(tag: Tag, val: Val) -> Self { - Ptr((val << 4) | (tag as Val)) - } - - #[inline(always)] - pub const fn tag(&self) -> Tag { - (self.0 & 0xF) as Tag - } - - #[inline(always)] - pub const fn lab(&self) -> Lab { - (self.0 as Lab) >> 4 - } - - #[inline(always)] - pub const fn loc(&self) -> Loc { - (self.0 >> 32) as Loc - } - - #[inline(always)] - pub const fn val(&self) -> Val { - self.0 >> 4 - } - - #[inline(always)] - pub fn is_nil(&self) -> bool { - return self.0 == 0; - } - - #[inline(always)] - pub fn is_var(&self) -> bool { - return matches!(self.tag(), VR1..=VR2) && !self.is_nil(); - } - - #[inline(always)] - pub fn is_red(&self) -> bool { - return matches!(self.tag(), RD1..=RD2) && !self.is_nil(); - } - - #[inline(always)] - pub fn is_era(&self) -> bool { - return matches!(self.tag(), ERA); - } - - #[inline(always)] - pub fn is_ctr(&self) -> bool { - return matches!(self.tag(), LAM..=END); - } - - #[inline(always)] - pub fn is_dup(&self) -> bool { - return matches!(self.tag(), DUP); - } - - #[inline(always)] - pub fn is_ref(&self) -> bool { - return matches!(self.tag(), REF); - } - - #[inline(always)] - pub fn is_pri(&self) -> bool { - return matches!(self.tag(), REF..=END); - } - - #[inline(always)] - pub fn is_num(&self) -> bool { - return matches!(self.tag(), NUM); - } - - #[inline(always)] - pub fn is_op1(&self) -> bool { - return matches!(self.tag(), OP1); - } - - #[inline(always)] - pub fn is_op2(&self) -> bool { - return matches!(self.tag(), OP2); - } - - #[inline(always)] - pub fn is_skp(&self) -> bool { - return matches!(self.tag(), ERA | NUM | REF); - } - - #[inline(always)] - pub fn is_mat(&self) -> bool { - return matches!(self.tag(), MAT); - } - - #[inline(always)] - pub fn is_nod(&self) -> bool { - return matches!(self.tag(), OP2..=END); - } - - #[inline(always)] - pub fn has_loc(&self) -> bool { - return matches!(self.tag(), VR1..=VR2 | OP2..=END); - } - - #[inline(always)] - pub fn redirect(&self) -> Ptr { - return Ptr::new(self.tag() + RD2 - VR2, 0, self.loc()); - } - - #[inline(always)] - pub fn unredirect(&self) -> Ptr { - return Ptr::new(self.tag() + RD2 - VR2, 0, self.loc()); - } - - #[inline(always)] - pub fn can_skip(a: Ptr, b: Ptr) -> bool { - return matches!(a.tag(), ERA | REF) && matches!(b.tag(), ERA | REF); - } - - #[inline(always)] - pub fn view(&self) -> String { - if *self == NULL { - return format!("(NUL)"); - } else { - return match self.tag() { - VR1 => format!("(VR1 {:07x} {:08x})", self.lab(), self.loc()), - VR2 => format!("(VR2 {:07x} {:08x})", self.lab(), self.loc()), - RD1 => format!("(RD1 {:07x} {:08x})", self.lab(), self.loc()), - RD2 => format!("(RD2 {:07x} {:08x})", self.lab(), self.loc()), - REF => format!("(REF \"{}\")", crate::ast::val_to_name(self.val())), - ERA => format!("(ERA)"), - NUM => format!("(NUM {:x})", self.val()), - OP2 => format!("(OP2 {:07x} {:08x})", self.lab(), self.loc()), - OP1 => format!("(OP1 {:07x} {:08x})", self.lab(), self.loc()), - MAT => format!("(MAT {:07x} {:08x})", self.lab(), self.loc()), - LAM => format!("(LAM {:07x} {:08x})", self.lab(), self.loc()), - TUP => format!("(TUP {:07x} {:08x})", self.lab(), self.loc()), - DUP => format!("(DUP {:07x} {:08x})", self.lab(), self.loc()), - END => format!("(END)"), - _ => format!("???"), - }; - }; - } -} - -impl APtr { - pub const fn new(ptr: Ptr) -> Self { - APtr(AtomicU64::new(ptr.0)) - } - - pub fn load(&self) -> Ptr { - Ptr(self.0.load(Ordering::Relaxed)) - } - - pub fn store(&self, ptr: Ptr) { - self.0.store(ptr.0, Ordering::Relaxed); - } -} - - -impl Book { - #[inline(always)] - pub fn new() -> Self { - Book { - defs: HashMap::with_hasher(std::hash::BuildHasherDefault::default()), - } - } - - #[inline(always)] - pub fn def(&mut self, name: Val, def: Def) { - self.defs.insert(name, def); - } - - #[inline(always)] - pub fn get(&self, name: Val) -> Option<&Def> { - self.defs.get(&name) - } -} - -impl Def { - pub fn new() -> Self { - Def { - labs: HashSet::with_hasher(std::hash::BuildHasherDefault::default()), - rdex: vec![], - node: vec![], - } - } -} - -impl<'a, const LAZY: bool> Heap<'a, LAZY> -where [(); LAZY as usize]: { - pub fn new(nodes: &'a Nodes) -> Self { - Heap { nodes } - } - - pub fn init(size: usize) -> Box<[ANode]> { - let mut data = vec![]; - const head : AHead = AHead { - this: APtr::new(NULL), - targ: APtr::new(NULL), - }; - for _ in 0..size { - let p0 = [head; LAZY as usize]; - let p1 = APtr::new(NULL); - let p2 = APtr::new(NULL); - data.push((p0, p1, p2)); - } - return data.into_boxed_slice(); - } - - #[inline(always)] - pub fn get(&self, index: Loc, port: Port) -> Ptr { - unsafe { - let node = self.nodes.get_unchecked(index as usize); - if port == P1 { - return node.1.load(); - } else { - return node.2.load(); - } - } - } - - #[inline(always)] - pub fn set(&self, index: Loc, port: Port, value: Ptr) { - unsafe { - let node = self.nodes.get_unchecked(index as usize); - if port == P1 { - node.1.store(value); - } else { - node.2.store(value); - } - } - } - - #[inline(always)] - pub fn get_pri(&self, index: Loc) -> Head { - unsafe { - //println!("main of: {:016x} = {:016x}", index, self.nodes.get_unchecked(index as usize).0[0].1.load().0); - let this = self.nodes.get_unchecked(index as usize).0[0].this.load(); - let targ = self.nodes.get_unchecked(index as usize).0[0].targ.load(); - return Head { this, targ }; - } - } - - #[inline(always)] - pub fn set_pri(&self, index: Loc, this: Ptr, targ: Ptr) { - //println!("set main {:x} = {:016x} ~ {:016x}", index, this.0, targ.0); - unsafe { - self.nodes.get_unchecked(index as usize).0[0].this.store(this); - self.nodes.get_unchecked(index as usize).0[0].targ.store(targ); - } - } - - #[inline(always)] - pub fn cas(&self, index: Loc, port: Port, expected: Ptr, value: Ptr) -> Result { - unsafe { - let node = self.nodes.get_unchecked(index as usize); - let data = if port == P1 { &node.1.0 } else { &node.2.0 }; - let done = data.compare_exchange_weak(expected.0, value.0, Ordering::Relaxed, Ordering::Relaxed); - return done.map(Ptr).map_err(Ptr); - } - } - - #[inline(always)] - pub fn swap(&self, index: Loc, port: Port, value: Ptr) -> Ptr { - unsafe { - let node = self.nodes.get_unchecked(index as usize); - let data = if port == P1 { &node.1.0 } else { &node.2.0 }; - return Ptr(data.swap(value.0, Ordering::Relaxed)); - } - } - - #[inline(always)] - pub fn get_root(&self) -> Ptr { - return self.get(ROOT.loc(), P2); - } - - #[inline(always)] - pub fn set_root(&self, value: Ptr) { - self.set(ROOT.loc(), P2, value); - } -} - -impl Rewrites { - pub fn new() -> Self { - Rewrites { - anni: 0, - comm: 0, - eras: 0, - dref: 0, - oper: 0, - } - } - - pub fn add_to(&self, target: &AtomicRewrites) { - target.anni.fetch_add(self.anni, Ordering::Relaxed); - target.comm.fetch_add(self.comm, Ordering::Relaxed); - target.eras.fetch_add(self.eras, Ordering::Relaxed); - target.dref.fetch_add(self.dref, Ordering::Relaxed); - target.oper.fetch_add(self.oper, Ordering::Relaxed); - } - - pub fn total(&self) -> usize { - self.anni + self.comm + self.eras + self.dref + self.oper - } - -} - -impl AtomicRewrites { - pub fn new() -> Self { - AtomicRewrites { - anni: AtomicUsize::new(0), - comm: AtomicUsize::new(0), - eras: AtomicUsize::new(0), - dref: AtomicUsize::new(0), - oper: AtomicUsize::new(0), - } - } - - pub fn add_to(&self, target: &mut Rewrites) { - target.anni += self.anni.load(Ordering::Relaxed); - target.comm += self.comm.load(Ordering::Relaxed); - target.eras += self.eras.load(Ordering::Relaxed); - target.dref += self.dref.load(Ordering::Relaxed); - target.oper += self.oper.load(Ordering::Relaxed); - } -} - -impl<'a, const LAZY: bool> NetFields<'a, LAZY> where [(); LAZY as usize]: { - // Creates an empty net with given size. - pub fn new(nodes: &'a Nodes) -> Self { - NetFields { - tid : 0, - tids: 1, - labs: 0x1, - heap: Heap { nodes }, - rdex: vec![], - locs: vec![0; 1 << 16], - area: Area { init: 0, size: nodes.len() }, - next: 0, - rwts: Rewrites::new(), - } - } - - // Creates a net and boots from a REF. - pub fn boot(&self, root_id: Val) { - self.heap.set_root(Ptr::big(REF, root_id)); - } - - // Total rewrite count. - pub fn rewrites(&self) -> usize { - return self.rwts.anni + self.rwts.comm + self.rwts.eras + self.rwts.dref + self.rwts.oper; - } - - #[inline(always)] - pub fn alloc(&mut self) -> Loc { - // On the first pass, just alloc without checking. - // Note: we add 1 to avoid overwritting root. - let index = if self.next < self.area.size - 1 { - self.next += 1; - self.area.init as Loc + self.next as Loc - // On later passes, search for an available slot. - } else { - loop { - self.next += 1; - let index = (self.area.init + self.next % self.area.size) as Loc; - if self.heap.get(index, P1).is_nil() && self.heap.get(index, P2).is_nil() { - break index; - } - } - }; - self.heap.set(index, P1, LOCK); - self.heap.set(index, P2, LOCK); - //println!("ALLOC {}", index); - index - } - - // Gets a pointer's target. - #[inline(always)] - pub fn get_target(&self, ptr: Ptr) -> Ptr { - self.heap.get(ptr.loc(), ptr.0 & 1) - } - - // Sets a pointer's target. - #[inline(always)] - pub fn set_target(&mut self, ptr: Ptr, val: Ptr) { - self.heap.set(ptr.loc(), ptr.0 & 1, val) - } - - // Takes a pointer's target. - #[inline(always)] - pub fn swap_target(&self, ptr: Ptr, value: Ptr) -> Ptr { - self.heap.swap(ptr.loc(), ptr.0 & 1, value) - } - - // Takes a pointer's target. - #[inline(always)] - pub fn take_target(&self, ptr: Ptr) -> Ptr { - loop { - let got = self.heap.swap(ptr.loc(), ptr.0 & 1, LOCK); - if got != LOCK && got != NULL { - return got; - } - } - } - - // Sets a pointer's target, using CAS. - #[inline(always)] - pub fn cas_target(&self, ptr: Ptr, expected: Ptr, value: Ptr) -> Result { - self.heap.cas(ptr.loc(), ptr.0 & 1, expected, value) - } - - // Like get_target, but also for main ports - #[inline(always)] - pub fn get_target_full(&self, ptr: Ptr) -> Ptr { - if ptr.is_var() || ptr.is_red() { - return self.get_target(ptr); - } - if ptr.is_nod() { - return self.heap.get_pri(ptr.loc()).targ; - } - panic!("Can't get target of: {}", ptr.view()); - } - - #[inline(always)] - pub fn redux(&mut self, a: Ptr, b: Ptr) { - if Ptr::can_skip(a, b) { - self.rwts.eras += 1; - } else if !LAZY { - self.rdex.push((a, b)); - } else { - if a.is_nod() { self.heap.set_pri(a.loc(), a, b); } - if b.is_nod() { self.heap.set_pri(b.loc(), b, a); } - } - } - - #[inline(always)] - pub fn get(&self, a: Trg) -> Ptr { - match a { - Trg::Dir(dir) => self.get_target(dir), - Trg::Ptr(ptr) => ptr, - } - } - - #[inline(always)] - pub fn swap(&self, a: Trg, val: Ptr) -> Ptr { - match a { - Trg::Dir(dir) => self.swap_target(dir, val), - Trg::Ptr(ptr) => ptr, - } - } - - // Links two pointers, forming a new wire. Assumes ownership. - #[inline(always)] - pub fn link(&mut self, a_ptr: Ptr, b_ptr: Ptr) { - if a_ptr.is_pri() && b_ptr.is_pri() { - return self.redux(a_ptr, b_ptr); - } else { - self.linker(a_ptr, b_ptr); - self.linker(b_ptr, a_ptr); - } - } - - // Given two locations, links both stored pointers, atomically. - #[inline(always)] - pub fn atomic_link(&mut self, a_dir: Ptr, b_dir: Ptr) { - //println!("link {:016x} {:016x}", a_dir.0, b_dir.0); - let a_ptr = self.take_target(a_dir); - let b_ptr = self.take_target(b_dir); - if a_ptr.is_pri() && b_ptr.is_pri() { - self.set_target(a_dir, NULL); - self.set_target(b_dir, NULL); - return self.redux(a_ptr, b_ptr); - } else { - self.atomic_linker(a_ptr, a_dir, b_ptr); - self.atomic_linker(b_ptr, b_dir, a_ptr); - } - } - - // Given a location, link the pointer stored to another pointer, atomically. - #[inline(always)] - pub fn half_atomic_link(&mut self, a_dir: Ptr, b_ptr: Ptr) { - let a_ptr = self.take_target(a_dir); - if a_ptr.is_pri() && b_ptr.is_pri() { - self.set_target(a_dir, NULL); - return self.redux(a_ptr, b_ptr); - } else { - self.atomic_linker(a_ptr, a_dir, b_ptr); - self.linker(b_ptr, a_ptr); - } - } - - // When two threads interfere, uses the lock-free link algorithm described on the 'paper/'. - #[inline(always)] - pub fn linker(&mut self, a_ptr: Ptr, b_ptr: Ptr) { - if a_ptr.is_var() { - self.set_target(a_ptr, b_ptr); - } else { - if LAZY && a_ptr.is_nod() { - self.heap.set_pri(a_ptr.loc(), a_ptr, b_ptr); - } - } - } - - // When two threads interfere, uses the lock-free link algorithm described on the 'paper/'. - #[inline(always)] - pub fn atomic_linker(&mut self, a_ptr: Ptr, a_dir: Ptr, b_ptr: Ptr) { - // If 'a_ptr' is a var... - if a_ptr.is_var() { - let got = self.cas_target(a_ptr, a_dir, b_ptr); - // Attempts to link using a compare-and-swap. - if got.is_ok() { - self.set_target(a_dir, NULL); - // If the CAS failed, resolve by using redirections. - } else { - //println!("[{:04x}] cas fail {:016x}", self.tid, got.unwrap_err().0); - if b_ptr.is_var() { - self.set_target(a_dir, b_ptr.redirect()); - //self.atomic_linker_var(a_ptr, a_dir, b_ptr); - } else if b_ptr.is_pri() { - self.set_target(a_dir, b_ptr); - self.atomic_linker_pri(a_ptr, a_dir, b_ptr); - } else { - todo!(); - } - } - } else { - self.set_target(a_dir, NULL); - if LAZY && a_ptr.is_nod() { - self.heap.set_pri(a_ptr.loc(), a_ptr, b_ptr); - } - } - } - - // Atomic linker for when 'b_ptr' is a principal port. - pub fn atomic_linker_pri(&mut self, mut a_ptr: Ptr, a_dir: Ptr, b_ptr: Ptr) { - loop { - // Peek the target, which may not be owned by us. - let mut t_dir = a_ptr; - let mut t_ptr = self.get_target(t_dir); - // If target is a redirection, we own it. Clear and move forward. - if t_ptr.is_red() { - self.set_target(t_dir, NULL); - a_ptr = t_ptr; - continue; - } - // If target is a variable, we don't own it. Try replacing it. - if t_ptr.is_var() { - if self.cas_target(t_dir, t_ptr, b_ptr).is_ok() { - //println!("[{:04x}] var", self.tid); - // Clear source location. - self.set_target(a_dir, NULL); - // Collect the orphaned backward path. - t_dir = t_ptr; - t_ptr = self.get_target(t_ptr); - while t_ptr.is_red() { - self.swap_target(t_dir, NULL); - t_dir = t_ptr; - t_ptr = self.get_target(t_dir); - } - return; - } - // If the CAS failed, the var changed, so we try again. - continue; - } - // If it is a node, two threads will reach this branch. - if t_ptr.is_pri() || t_ptr == GONE { - // Sort references, to avoid deadlocks. - let x_dir = if a_dir < t_dir { a_dir } else { t_dir }; - let y_dir = if a_dir < t_dir { t_dir } else { a_dir }; - // Swap first reference by GONE placeholder. - let x_ptr = self.swap_target(x_dir, GONE); - // First to arrive creates a redex. - if x_ptr != GONE { - //println!("[{:04x}] fst {:016x}", self.tid, x_ptr.0); - let y_ptr = self.swap_target(y_dir, GONE); - self.redux(x_ptr, y_ptr); - return; - // Second to arrive clears up the memory. - } else { - //println!("[{:04x}] snd", self.tid); - self.swap_target(x_dir, NULL); - while self.cas_target(y_dir, GONE, NULL).is_err() {}; - return; - } - } - // If it is taken, we wait. - if t_ptr == LOCK { - continue; - } - if t_ptr == NULL { - continue; - } - // Shouldn't be reached. - //println!("[{:04x}] {:016x} | {:016x} {:016x} {:016x}", self.tid, t_ptr.0, a_dir.0, a_ptr.0, b_ptr.0); - unreachable!() - } - } - - // Atomic linker for when 'b_ptr' is an aux port. - pub fn atomic_linker_var(&mut self, a_ptr: Ptr, a_dir: Ptr, b_ptr: Ptr) { - loop { - let ste_dir = b_ptr; - let ste_ptr = self.get_target(ste_dir); - if ste_ptr.is_var() { - let trg_dir = ste_ptr; - let trg_ptr = self.get_target(trg_dir); - if trg_ptr.is_red() { - let neo_ptr = trg_ptr.unredirect(); - if self.cas_target(ste_dir, ste_ptr, neo_ptr).is_ok() { - self.swap_target(trg_dir, NULL); - continue; - } - } - } - break; - } - } - - // Links two targets, using atomics when necessary, based on implied ownership. - #[inline(always)] - pub fn safe_link(&mut self, a: Trg, b: Trg) { - match (a, b) { - (Trg::Dir(a_dir), Trg::Dir(b_dir)) => self.atomic_link(a_dir, b_dir), - (Trg::Dir(a_dir), Trg::Ptr(b_ptr)) => self.half_atomic_link(a_dir, b_ptr), - (Trg::Ptr(a_ptr), Trg::Dir(b_dir)) => self.half_atomic_link(b_dir, a_ptr), - (Trg::Ptr(a_ptr), Trg::Ptr(b_ptr)) => self.link(a_ptr, b_ptr), - } - } - - // Performs an interaction over a redex. - #[inline(always)] - pub fn interact(&mut self, book: &Book, a: Ptr, b: Ptr) { - //println!("inter {} ~ {}", a.view(), b.view()); - match (a.tag(), b.tag()) { - (REF , OP2..) => self.call(book, a, b), - (OP2.. , REF ) => self.call(book, b, a), - (LAM.. , LAM..) if a.lab() == b.lab() => self.anni(a, b), - (LAM.. , LAM..) => self.comm(a, b), - (LAM.. , ERA ) => self.era2(a), - (ERA , LAM..) => self.era2(b), - (REF , ERA ) => self.rwts.eras += 1, - (ERA , REF ) => self.rwts.eras += 1, - (REF , NUM ) => self.rwts.eras += 1, - (NUM , REF ) => self.rwts.eras += 1, - (ERA , ERA ) => self.rwts.eras += 1, - (LAM.. , NUM ) => self.copy(a, b), - (NUM , LAM..) => self.copy(b, a), - (NUM , ERA ) => self.rwts.eras += 1, - (ERA , NUM ) => self.rwts.eras += 1, - (NUM , NUM ) => self.rwts.eras += 1, - (OP2 , NUM ) => self.op2n(a, b), - (NUM , OP2 ) => self.op2n(b, a), - (OP1 , NUM ) => self.op1n(a, b), - (NUM , OP1 ) => self.op1n(b, a), - (OP2 , LAM..) => self.comm(a, b), - (LAM.. , OP2 ) => self.comm(b, a), - (OP1 , LAM..) => self.pass(a, b), - (LAM.. , OP1 ) => self.pass(b, a), - (OP2 , ERA ) => self.era2(a), - (ERA , OP2 ) => self.era2(b), - (OP1 , ERA ) => self.era1(a), - (ERA , OP1 ) => self.era1(b), - (MAT , NUM ) => self.mtch(a, b), - (NUM , MAT ) => self.mtch(b, a), - (MAT , LAM..) => self.comm(a, b), - (LAM.. , MAT ) => self.comm(b, a), - (MAT , ERA ) => self.era2(a), - (ERA , MAT ) => self.era2(b), - _ => { - println!("Invalid interaction: {} ~ {}", a.view(), b.view()); - unreachable!(); - }, - }; - } - - pub fn anni(&mut self, a: Ptr, b: Ptr) { - self.rwts.anni += 1; - let a1 = Ptr::new(VR1, 0, a.loc()); - let b1 = Ptr::new(VR1, 0, b.loc()); - self.atomic_link(a1, b1); - let a2 = Ptr::new(VR2, 0, a.loc()); - let b2 = Ptr::new(VR2, 0, b.loc()); - self.atomic_link(a2, b2); - } - - pub fn comm(&mut self, a: Ptr, b: Ptr) { - self.rwts.comm += 1; - let loc0 = self.alloc(); - let loc1 = self.alloc(); - let loc2 = self.alloc(); - let loc3 = self.alloc(); - self.heap.set(loc0, P1, Ptr::new(VR1, 0, loc2)); - self.heap.set(loc0, P2, Ptr::new(VR1, 0, loc3)); - self.heap.set(loc1, P1, Ptr::new(VR2, 0, loc2)); - self.heap.set(loc1, P2, Ptr::new(VR2, 0, loc3)); - self.heap.set(loc2, P1, Ptr::new(VR1, 0, loc0)); - self.heap.set(loc2, P2, Ptr::new(VR1, 0, loc1)); - self.heap.set(loc3, P1, Ptr::new(VR2, 0, loc0)); - self.heap.set(loc3, P2, Ptr::new(VR2, 0, loc1)); - let a1 = Ptr::new(VR1, 0, a.loc()); - self.half_atomic_link(a1, Ptr::new(b.tag(), b.lab(), loc0)); - let b1 = Ptr::new(VR1, 0, b.loc()); - self.half_atomic_link(b1, Ptr::new(a.tag(), a.lab(), loc2)); - let a2 = Ptr::new(VR2, 0, a.loc()); - self.half_atomic_link(a2, Ptr::new(b.tag(), b.lab(), loc1)); - let b2 = Ptr::new(VR2, 0, b.loc()); - self.half_atomic_link(b2, Ptr::new(a.tag(), a.lab(), loc3)); - } - - pub fn era2(&mut self, a: Ptr) { - self.rwts.eras += 1; - let a1 = Ptr::new(VR1, 0, a.loc()); - self.half_atomic_link(a1, ERAS); - let a2 = Ptr::new(VR2, 0, a.loc()); - self.half_atomic_link(a2, ERAS); - } - - pub fn era1(&mut self, a: Ptr) { - self.rwts.eras += 1; - let a2 = Ptr::new(VR2, 0, a.loc()); - self.half_atomic_link(a2, ERAS); - } - - pub fn pass(&mut self, a: Ptr, b: Ptr) { - self.rwts.comm += 1; - let loc0 = self.alloc(); - let loc1 = self.alloc(); - let loc2 = self.alloc(); - self.heap.set(loc0, P1, Ptr::new(VR2, 0, loc1)); - self.heap.set(loc0, P2, Ptr::new(VR2, 0, loc2)); - self.heap.set(loc1, P1, self.heap.get(a.loc(), P1)); - self.heap.set(loc1, P2, Ptr::new(VR1, 0, loc0)); - self.heap.set(loc2, P1, self.heap.get(a.loc(), P1)); - self.heap.set(loc2, P2, Ptr::new(VR2, 0, loc0)); - let a2 = Ptr::new(VR2, 0, a.loc()); - self.half_atomic_link(a2, Ptr::new(b.tag(), b.lab(), loc0)); - let b1 = Ptr::new(VR1, 0, b.loc()); - self.half_atomic_link(b1, Ptr::new(a.tag(), a.lab(), loc1)); - let b2 = Ptr::new(VR2, 0, b.loc()); - self.half_atomic_link(b2, Ptr::new(a.tag(), a.lab(), loc2)); - } - - pub fn copy(&mut self, a: Ptr, b: Ptr) { - self.rwts.comm += 1; - let a1 = Ptr::new(VR1, 0, a.loc()); - self.half_atomic_link(a1, b); - let a2 = Ptr::new(VR2, 0, a.loc()); - self.half_atomic_link(a2, b); - } - - pub fn mtch(&mut self, a: Ptr, b: Ptr) { - self.rwts.oper += 1; - let a1 = Ptr::new(VR1, 0, a.loc()); // branch - let a2 = Ptr::new(VR2, 0, a.loc()); // return - if b.val() == 0 { - let loc0 = self.alloc(); - //self.heap.set(loc0, P2, ERAS); - self.link(Ptr::new(VR2, 0, loc0), ERAS); - self.half_atomic_link(a1, Ptr::new(LAM, 0, loc0)); - self.half_atomic_link(a2, Ptr::new(VR1, 0, loc0)); - } else { - let loc0 = self.alloc(); - let loc1 = self.alloc(); - self.link(Ptr::new(VR1, 0, loc0), ERAS); - self.link(Ptr::new(VR2, 0, loc0), Ptr::new(LAM, 0, loc1)); - self.link(Ptr::new(VR1, 0, loc1), Ptr::big(NUM, b.val() - 1)); - //self.heap.set(loc0, P1, ERAS); - //self.heap.set(loc0, P2, Ptr::new(LAM, 0, loc1)); - //self.heap.set(loc1, P1, Ptr::big(NUM, b.val() - 1)); - self.half_atomic_link(a1, Ptr::new(LAM, 0, loc0)); - self.half_atomic_link(a2, Ptr::new(VR2, 0, loc1)); - } - } - - pub fn op2n(&mut self, a: Ptr, b: Ptr) { - self.rwts.oper += 1; - let loc0 = self.alloc(); - let a1 = Ptr::new(VR1, 0, a.loc()); - let a2 = Ptr::new(VR2, 0, a.loc()); - self.heap.set(loc0, P1, b); - self.half_atomic_link(a2, Ptr::new(VR2, 0, loc0)); - self.half_atomic_link(a1, Ptr::new(OP1, a.lab(), loc0)); - } - - pub fn op1n(&mut self, a: Ptr, b: Ptr) { - self.rwts.oper += 1; - let op = a.lab(); - let v0 = self.heap.get(a.loc(), P1).val(); - let v1 = b.val(); - let v2 = self.op(op, v0, v1); - let a2 = Ptr::new(VR2, 0, a.loc()); - self.half_atomic_link(a2, Ptr::big(NUM, v2)); - } - - #[inline(always)] - pub fn op(&self, op: Lab, a: Val, b: Val) -> Val { - match op { - ADD => { u60::add(a, b) } - SUB => { u60::sub(a, b) } - MUL => { u60::mul(a, b) } - DIV => { u60::div(a, b) } - MOD => { u60::rem(a, b) } - EQ => { u60::eq(a, b) } - NE => { u60::ne(a, b) } - LT => { u60::lt(a, b) } - GT => { u60::gt(a, b) } - LTE => { u60::lte(a, b) } - GTE => { u60::gte(a, b) } - AND => { u60::and(a, b) } - OR => { u60::or(a, b) } - XOR => { u60::xor(a, b) } - NOT => { u60::not(a) } - LSH => { u60::lsh(a, b) } - RSH => { u60::rsh(a, b) } - _ => { unreachable!() } - } - } - - // Expands a closed net. - #[inline(always)] - pub fn call(&mut self, book: &Book, ptr: Ptr, trg: Ptr) { - //println!("call {} {}", ptr.view(), trg.view()); - self.rwts.dref += 1; - let mut ptr = ptr; - // FIXME: change "while" to "if" once lang prevents refs from returning refs - if ptr.is_ref() { - // Intercepts with a native function, if available. - if !LAZY && self.call_native(book, ptr, trg) { - return; - } - // Load the closed net. - let fid = ptr.val(); - let got = book.get(fid).unwrap(); - if !LAZY && trg.is_dup() && !got.labs.contains(&trg.lab()) { - return self.copy(trg, ptr); - } else if got.node.len() > 0 { - let len = got.node.len() - 1; - // Allocate space. - for i in 0 .. len { - *unsafe { self.locs.get_unchecked_mut(1 + i) } = self.alloc(); - } - // Load nodes, adjusted. - for i in 0 .. len { - let p1 = self.adjust(unsafe { got.node.get_unchecked(1 + i) }.1); - let p2 = self.adjust(unsafe { got.node.get_unchecked(1 + i) }.2); - let lc = *unsafe { self.locs.get_unchecked(1 + i) }; - //println!(":: link loc={} [{} {}]", lc, p1.view(), p2.view()); - if p1 != ROOT { self.link(Ptr::new(VR1, 0, lc), p1); } - if p2 != ROOT { self.link(Ptr::new(VR2, 0, lc), p2); } - } - // Load redexes, adjusted. - for r in &got.rdex { - let p1 = self.adjust(r.0); - let p2 = self.adjust(r.1); - self.redux(p1, p2); - //self.rdex.push((p1, p2)); - } - // Load root, adjusted. - ptr = self.adjust(got.node[0].2); - } - } - self.link(ptr, trg); - } - - // Adjusts dereferenced pointer locations. - #[inline(always)] - fn adjust(&mut self, ptr: Ptr) -> Ptr { - if ptr.has_loc() { - let tag = ptr.tag(); - // FIXME - let lab = if LAZY && ptr.is_dup() && ptr.lab() == 0 { - self.labs += 2; - self.labs - } else { - ptr.lab() - }; - //let lab = ptr.lab(); - let loc = *unsafe { self.locs.get_unchecked(ptr.loc() as usize) }; - return Ptr::new(tag, lab, loc) - } else { - return ptr; - } - } - - pub fn view(&self) -> String { - let mut txt = String::new(); - for i in 0 .. self.heap.nodes.len() as Loc { - let p0 = self.heap.get_pri(i).targ; - let p1 = self.heap.get(i, P1); - let p2 = self.heap.get(i, P2); - if p1 != NULL || p2 != NULL { - txt.push_str(&format!("{:04x} | {:22} {:22} {:22}\n", i, p0.view(), p1.view(), p2.view())); - } - } - return txt; - } - - // Reduces all redexes. - #[inline(always)] - pub fn reduce(&mut self, book: &Book, limit: usize) -> usize { - let mut count = 0; - while let Some((a, b)) = self.rdex.pop() { - //if !a.is_nil() && !b.is_nil() { - self.interact(book, a, b); - count += 1; - if count >= limit { - break; - } - //} - } - return count; - } - - // Expands heads. - #[inline(always)] - pub fn expand(&mut self, book: &Book) { - fn go(net: &mut NetFields, book: &Book, dir: Ptr, len: usize, key: usize) where [(); LAZY as usize]: { - //println!("[{:04x}] expand dir: {:016x}", net.tid, dir.0); - let ptr = net.get_target(dir); - if ptr.is_ctr() { - if len >= net.tids || key % 2 == 0 { - go(net, book, Ptr::new(VR1, 0, ptr.loc()), len * 2, key / 2); - } - if len >= net.tids || key % 2 == 1 { - go(net, book, Ptr::new(VR2, 0, ptr.loc()), len * 2, key / 2); - } - } else if ptr.is_ref() { - let got = net.swap_target(dir, LOCK); - if got != LOCK { - //println!("[{:08x}] expand {:08x}", net.tid, dir.0); - net.call(book, ptr, dir); - } - } - } - return go(self, book, ROOT, 1, self.tid); - } - - // Forks into child threads, returning a NetFields for the (tid/tids)'th thread. - pub fn fork(&self, tid: usize, tids: usize) -> Self { - let mut net = NetFields::new(self.heap.nodes); - net.tid = tid; - net.tids = tids; - net.area = Area { - init: self.heap.nodes.len() * tid / tids, - size: self.heap.nodes.len() / tids, - }; - let from = self.rdex.len() * (tid + 0) / tids; - let upto = self.rdex.len() * (tid + 1) / tids; - for i in from .. upto { - net.rdex.push((self.rdex[i].0, self.rdex[i].1)); - } - if tid == 0 { - net.next = self.next; - } - return net; - } - - // Evaluates a term to normal form in parallel - pub fn parallel_normal(&mut self, book: &Book) { - - const SHARE_LIMIT : usize = 1 << 12; // max share redexes per split - const LOCAL_LIMIT : usize = 1 << 18; // max local rewrites per epoch - - // Local thread context - struct ThreadContext<'a, const LAZY: bool> where [(); LAZY as usize]: { - tid: usize, // thread id - tids: usize, // thread count - tlog2: usize, // log2 of thread count - tick: usize, // current tick - net: NetFields<'a, LAZY>, // thread's own net object - book: &'a Book, // definition book - delta: &'a AtomicRewrites, // global delta rewrites - share: &'a Vec<(APtr, APtr)>, // global share buffer - rlens: &'a Vec, // global redex lengths - total: &'a AtomicUsize, // total redex length - barry: Arc, // synchronization barrier - } - - // Initialize global objects - let cores = std::thread::available_parallelism().unwrap().get() as usize; - let tlog2 = cores.ilog2() as usize; - let tids = 1 << tlog2; - let delta = AtomicRewrites::new(); // delta rewrite counter - let rlens = (0..tids).map(|_| AtomicUsize::new(0)).collect::>(); - let share = (0..SHARE_LIMIT*tids).map(|_| (APtr(AtomicU64::new(0)), APtr(AtomicU64::new(0)))).collect::>(); - let total = AtomicUsize::new(0); // sum of redex bag length - let barry = Arc::new(Barrier::new(tids)); // global barrier - - // Perform parallel reductions - std::thread::scope(|s| { - for tid in 0 .. tids { - let mut ctx = ThreadContext { - tid: tid, - tids: tids, - tick: 0, - net: self.fork(tid, tids), - book: &book, - tlog2: tlog2, - delta: &delta, - share: &share, - rlens: &rlens, - total: &total, - barry: Arc::clone(&barry), - }; - s.spawn(move || { - main(&mut ctx) - }); - } - }); - - // Clear redexes and sum stats - self.rdex.clear(); - delta.add_to(&mut self.rwts); - - // Main reduction loop - #[inline(always)] - fn main(ctx: &mut ThreadContext) where [(); LAZY as usize]: { - loop { - reduce(ctx); - expand(ctx); - if count(ctx) == 0 { break; } - } - ctx.net.rwts.add_to(ctx.delta); - } - - // Reduce redexes locally, then share with target - #[inline(always)] - fn reduce(ctx: &mut ThreadContext) where [(); LAZY as usize]: { - loop { - let reduced = ctx.net.reduce(ctx.book, LOCAL_LIMIT); - if count(ctx) == 0 { - break; - } - let tlog2 = ctx.tlog2; - split(ctx, tlog2); - ctx.tick += 1; - } - } - - // Expand head refs - #[inline(always)] - fn expand(ctx: &mut ThreadContext) where [(); LAZY as usize]: { - ctx.net.expand(ctx.book); - } - - // Count total redexes (and populate 'rlens') - #[inline(always)] - fn count(ctx: &mut ThreadContext) -> usize where [(); LAZY as usize]: { - ctx.barry.wait(); - ctx.total.store(0, Ordering::Relaxed); - ctx.barry.wait(); - ctx.rlens[ctx.tid].store(ctx.net.rdex.len(), Ordering::Relaxed); - ctx.total.fetch_add(ctx.net.rdex.len(), Ordering::Relaxed); - ctx.barry.wait(); - return ctx.total.load(Ordering::Relaxed); - } - - - // Share redexes with target thread - #[inline(always)] - fn split(ctx: &mut ThreadContext, plog2: usize) where [(); LAZY as usize]: { - unsafe { - let side = (ctx.tid >> (plog2 - 1 - (ctx.tick % plog2))) & 1; - let shift = (1 << (plog2 - 1)) >> (ctx.tick % plog2); - let a_tid = ctx.tid; - let b_tid = if side == 1 { a_tid - shift } else { a_tid + shift }; - let a_len = ctx.net.rdex.len(); - let b_len = ctx.rlens[b_tid].load(Ordering::Relaxed); - let send = if a_len > b_len { (a_len - b_len) / 2 } else { 0 }; - let recv = if b_len > a_len { (b_len - a_len) / 2 } else { 0 }; - let send = std::cmp::min(send, SHARE_LIMIT); - let recv = std::cmp::min(recv, SHARE_LIMIT); - for i in 0 .. send { - let init = a_len - send * 2; - let rdx0 = *ctx.net.rdex.get_unchecked(init + i * 2 + 0); - let rdx1 = *ctx.net.rdex.get_unchecked(init + i * 2 + 1); - let targ = ctx.share.get_unchecked(b_tid * SHARE_LIMIT + i); - *ctx.net.rdex.get_unchecked_mut(init + i) = rdx0; - targ.0.store(rdx1.0); - targ.1.store(rdx1.1); - } - ctx.net.rdex.truncate(a_len - send); - ctx.barry.wait(); - for i in 0 .. recv { - let got = ctx.share.get_unchecked(a_tid * SHARE_LIMIT + i); - ctx.net.rdex.push((got.0.load(), got.1.load())); - } - } - } - } - - // Lazy mode weak head normalizer - #[inline(always)] - pub fn weak_normal(&mut self, book: &Book, mut prev: Ptr) -> Ptr { - let mut path : Vec = vec![]; - - loop { - // Load ptrs - let next = self.get_target_full(prev); - - // If next is ref, dereferences - if next.is_ref() { - self.call(book, next, prev); - continue; - } - - // If next is root, stop. - if next == ROOT { - break ; - } - - // If next is a main port... - if next.is_pri() { - // If prev is a main port, reduce the active pair. - if prev.is_pri() { - self.interact(book, prev, next); - prev = path.pop().unwrap(); - continue; - // Otherwise, we're done. - } else { - break; - } - } - - // If next is an aux port, pass through. - let main = self.heap.get_pri(next.loc()); - path.push(prev); - prev = main.this; - } - - return self.get_target_full(prev); - } - - // Reduce a net to normal form. - pub fn normal(&mut self, book: &Book) { - if LAZY { - let mut visit = vec![ROOT]; - while let Some(prev) = visit.pop() { - //println!("normal {} | {}", prev.view(), self.rewrites()); - let next = self.weak_normal(book, prev); - if next.is_nod() { - visit.push(Ptr::new(VR1, 0, next.loc())); - if !next.is_op1() { visit.push(Ptr::new(VR2, 0, next.loc())); } // TODO: improve - } - } - } else { - self.expand(book); - while self.rdex.len() > 0 { - self.reduce(book, usize::MAX); - self.expand(book); - } - } - } - -} - -// A net holding a static nodes buffer. -pub struct StaticNet where [(); LAZY as usize]: { - pub mem: *mut [ANode], - pub net: NetFields<'static, LAZY>, -} - -// A simple Net API. Holds its own nodes buffer, and knows its mode (lazy/eager). -pub enum Net { - Lazy(StaticNet), - Eager(StaticNet), -} - -impl Drop for Net { - fn drop(&mut self) { - match self { - Net::Lazy(this) => { let _ = unsafe { Box::from_raw(this.mem) }; } - Net::Eager(this) => { let _ = unsafe { Box::from_raw(this.mem) }; } - } - } -} - -impl Net { - // Creates a new net with the given size. - pub fn new(size: usize, lazy: bool) -> Self { - if lazy { - let mem = Box::leak(Heap::::init(size)) as *mut _; - let net = NetFields::::new(unsafe { &*mem }); - net.boot(crate::ast::name_to_val("main")); - return Net::Lazy(StaticNet { mem, net }); - } else { - let mem = Box::leak(Heap::::init(size)) as *mut _; - let net = NetFields::::new(unsafe { &*mem }); - net.boot(crate::ast::name_to_val("main")); - return Net::Eager(StaticNet { mem, net }); - } - } - - // Pretty prints. - pub fn show(&self) -> String { - match self { - Net::Lazy(this) => crate::ast::show_runtime_net(&this.net), - Net::Eager(this) => crate::ast::show_runtime_net(&this.net), - } - } - - // Reduces to normal form. - pub fn normal(&mut self, book: &Book) { - match self { - Net::Lazy(this) => this.net.normal(book), - Net::Eager(this) => this.net.normal(book), - } - } - - // Reduces to normal form in parallel. - pub fn parallel_normal(&mut self, book: &Book) { - match self { - Net::Lazy(this) => this.net.parallel_normal(book), - Net::Eager(this) => this.net.parallel_normal(book), - } - } - - pub fn get_rewrites(&self) -> Rewrites { - match self { - Net::Lazy(this) => this.net.rwts, - Net::Eager(this) => this.net.rwts, - } - } -} diff --git a/book/.hvm/src/u60.rs b/book/.hvm/src/u60.rs deleted file mode 100644 index c84f30976..000000000 --- a/book/.hvm/src/u60.rs +++ /dev/null @@ -1,113 +0,0 @@ -// Implements u48: 48-bit unsigned integers using u64 and u128 - -type U60 = u64; - -#[inline(always)] -pub fn new(a: u64) -> U60 { - return a & 0xFFF_FFFF_FFFF_FFFF; -} - -#[inline(always)] -pub fn val(a: u64) -> U60 { - return a; -} - -#[inline(always)] -pub fn add(a: U60, b: U60) -> U60 { - return new(a + b); -} - -#[inline(always)] -pub fn sub(a: U60, b: U60) -> U60 { - return if a >= b { a - b } else { 0x1000000000000000 - (b - a) }; -} - -#[inline(always)] -pub fn mul(a: U60, b: U60) -> U60 { - return new((a as u128 * b as u128) as u64); -} - -#[inline(always)] -pub fn div(a: U60, b: U60) -> U60 { - return a / b; -} - -#[inline(always)] -pub fn rem(a: U60, b: U60) -> U60 { - return a % b; -} - -#[inline(always)] -pub fn and(a: U60, b: U60) -> U60 { - return a & b; -} - -#[inline(always)] -pub fn or(a: U60, b: U60) -> U60 { - return a | b; -} - -#[inline(always)] -pub fn xor(a: U60, b: U60) -> U60 { - return a ^ b; -} - -#[inline(always)] -pub fn lsh(a: U60, b: U60) -> U60 { - return new(a << b); -} - -#[inline(always)] -pub fn rsh(a: U60, b: U60) -> U60 { - return a >> b; -} - -#[inline(always)] -pub fn lt(a: U60, b: U60) -> U60 { - return if a < b { 1 } else { 0 }; -} - -#[inline(always)] -pub fn gt(a: U60, b: U60) -> U60 { - return if a > b { 1 } else { 0 }; -} - -#[inline(always)] -pub fn lte(a: U60, b: U60) -> U60 { - return if a <= b { 1 } else { 0 }; -} - -#[inline(always)] -pub fn gte(a: U60, b: U60) -> U60 { - return if a >= b { 1 } else { 0 }; -} - -#[inline(always)] -pub fn eq(a: U60, b: U60) -> U60 { - return if a == b { 1 } else { 0 }; -} - -#[inline(always)] -pub fn ne(a: U60, b: U60) -> U60 { - return if a != b { 1 } else { 0 }; -} - -#[inline(always)] -pub fn min(a: U60, b: U60) -> U60 { - return if a < b { a } else { b }; -} - -#[inline(always)] -pub fn max(a: U60, b: U60) -> U60 { - return if a > b { a } else { b }; -} - -#[inline(always)] -pub fn not(a: U60) -> U60 { - return !a & 0xFFF_FFFF_FFFF_FFFF; -} - -#[inline(always)] -pub fn show(a: U60) -> String { - return format!("{}", a); -} diff --git a/book/.hvm/target/.rustc_info.json b/book/.hvm/target/.rustc_info.json deleted file mode 100644 index fa4d4a065..000000000 --- a/book/.hvm/target/.rustc_info.json +++ /dev/null @@ -1 +0,0 @@ -{"rustc_fingerprint":4802413464031946296,"outputs":{"15729799797837862367":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n/Users/v/.rustup/toolchains/nightly-aarch64-apple-darwin\noff\npacked\nunpacked\n___\ndebug_assertions\noverflow_checks\npanic=\"unwind\"\nproc_macro\nrelocation_model=\"pic\"\ntarget_abi=\"\"\ntarget_arch=\"aarch64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_family=\"unix\"\ntarget_feature=\"aes\"\ntarget_feature=\"crc\"\ntarget_feature=\"dit\"\ntarget_feature=\"dotprod\"\ntarget_feature=\"dpb\"\ntarget_feature=\"dpb2\"\ntarget_feature=\"fcma\"\ntarget_feature=\"fhm\"\ntarget_feature=\"flagm\"\ntarget_feature=\"fp16\"\ntarget_feature=\"frintts\"\ntarget_feature=\"jsconv\"\ntarget_feature=\"lor\"\ntarget_feature=\"lse\"\ntarget_feature=\"neon\"\ntarget_feature=\"paca\"\ntarget_feature=\"pacg\"\ntarget_feature=\"pan\"\ntarget_feature=\"pmuv3\"\ntarget_feature=\"ras\"\ntarget_feature=\"rcpc\"\ntarget_feature=\"rcpc2\"\ntarget_feature=\"rdm\"\ntarget_feature=\"sb\"\ntarget_feature=\"sha2\"\ntarget_feature=\"sha3\"\ntarget_feature=\"ssbs\"\ntarget_feature=\"v8.1a\"\ntarget_feature=\"v8.2a\"\ntarget_feature=\"v8.3a\"\ntarget_feature=\"v8.4a\"\ntarget_feature=\"vh\"\ntarget_has_atomic\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_equal_alignment=\"128\"\ntarget_has_atomic_equal_alignment=\"16\"\ntarget_has_atomic_equal_alignment=\"32\"\ntarget_has_atomic_equal_alignment=\"64\"\ntarget_has_atomic_equal_alignment=\"8\"\ntarget_has_atomic_equal_alignment=\"ptr\"\ntarget_has_atomic_load_store\ntarget_has_atomic_load_store=\"128\"\ntarget_has_atomic_load_store=\"16\"\ntarget_has_atomic_load_store=\"32\"\ntarget_has_atomic_load_store=\"64\"\ntarget_has_atomic_load_store=\"8\"\ntarget_has_atomic_load_store=\"ptr\"\ntarget_os=\"macos\"\ntarget_pointer_width=\"64\"\ntarget_thread_local\ntarget_vendor=\"apple\"\nunix\n","stderr":""},"4614504638168534921":{"success":true,"status":"","code":0,"stdout":"rustc 1.76.0-nightly (9a66e4471 2023-11-19)\nbinary: rustc\ncommit-hash: 9a66e4471f71283fd54d80ef8147630d34756332\ncommit-date: 2023-11-19\nhost: aarch64-apple-darwin\nrelease: 1.76.0-nightly\nLLVM version: 17.0.5\n","stderr":""}},"successes":{}} \ No newline at end of file diff --git a/book/.hvm/target/CACHEDIR.TAG b/book/.hvm/target/CACHEDIR.TAG deleted file mode 100644 index 20d7c319c..000000000 --- a/book/.hvm/target/CACHEDIR.TAG +++ /dev/null @@ -1,3 +0,0 @@ -Signature: 8a477f597d28d172789f06886806bc55 -# This file is a cache directory tag created by cargo. -# For information about cache directory tags see https://bford.info/cachedir/ diff --git a/book/.hvm/target/release/.cargo-lock b/book/.hvm/target/release/.cargo-lock deleted file mode 100644 index e69de29bb..000000000 diff --git a/book/.hvm/target/release/.fingerprint/hvm-core-5d3b356175a8b321/invoked.timestamp b/book/.hvm/target/release/.fingerprint/hvm-core-5d3b356175a8b321/invoked.timestamp deleted file mode 100644 index e00328da5..000000000 --- a/book/.hvm/target/release/.fingerprint/hvm-core-5d3b356175a8b321/invoked.timestamp +++ /dev/null @@ -1 +0,0 @@ -This file has an mtime of when this was started. \ No newline at end of file diff --git a/book/.hvm/target/release/.fingerprint/hvm-core-5d3b356175a8b321/output-lib-hvmc b/book/.hvm/target/release/.fingerprint/hvm-core-5d3b356175a8b321/output-lib-hvmc deleted file mode 100644 index f7d4eee42..000000000 --- a/book/.hvm/target/release/.fingerprint/hvm-core-5d3b356175a8b321/output-lib-hvmc +++ /dev/null @@ -1,5 +0,0 @@ -{"message":"expected one of `:`, `;`, `<`, `=`, or `where`, found `.`","code":null,"level":"error","spans":[{"file_name":"src/fns.rs","byte_start":186,"byte_end":187,"line_start":7,"line_end":7,"column_start":17,"column_end":18,"is_primary":true,"text":[{"text":"pub const F__U60.fib : Val = 0xf9e180fe9b25;","highlight_start":17,"highlight_end":18}],"label":"expected one of `:`, `;`, `<`, `=`, or `where`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"\u001b[0m\u001b[1m\u001b[38;5;9merror\u001b[0m\u001b[0m\u001b[1m: expected one of `:`, `;`, `<`, `=`, or `where`, found `.`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/fns.rs:7:17\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m7\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0mpub const F__U60.fib : Val = 0xf9e180fe9b25;\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mexpected one of `:`, `;`, `<`, `=`, or `where`\u001b[0m\n\n"} -{"message":"no method named `call_native` found for mutable reference `&mut NetFields<'a, LAZY>` in the current scope","code":{"code":"E0599","explanation":"This error occurs when a method is used on a type which doesn't implement it:\n\nErroneous code example:\n\n```compile_fail,E0599\nstruct Mouth;\n\nlet x = Mouth;\nx.chocolate(); // error: no method named `chocolate` found for type `Mouth`\n // in the current scope\n```\n\nIn this case, you need to implement the `chocolate` method to fix the error:\n\n```\nstruct Mouth;\n\nimpl Mouth {\n fn chocolate(&self) { // We implement the `chocolate` method here.\n println!(\"Hmmm! I love chocolate!\");\n }\n}\n\nlet x = Mouth;\nx.chocolate(); // ok!\n```\n"},"level":"error","spans":[{"file_name":"src/run.rs","byte_start":30344,"byte_end":30355,"line_start":993,"line_end":993,"column_start":24,"column_end":35,"is_primary":true,"text":[{"text":" if !LAZY && self.call_native(book, ptr, trg) {","highlight_start":24,"highlight_end":35}],"label":"method not found in `&mut NetFields<'a, LAZY>`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"\u001b[0m\u001b[1m\u001b[38;5;9merror[E0599]\u001b[0m\u001b[0m\u001b[1m: no method named `call_native` found for mutable reference `&mut NetFields<'a, LAZY>` in the current scope\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/run.rs:993:24\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m993\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m if !LAZY && self.call_native(book, ptr, trg) {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mmethod not found in `&mut NetFields<'a, LAZY>`\u001b[0m\n\n"} -{"message":"missing type for `const` item","code":null,"level":"error","spans":[{"file_name":"src/fns.rs","byte_start":186,"byte_end":186,"line_start":7,"line_end":7,"column_start":17,"column_end":17,"is_primary":true,"text":[{"text":"pub const F__U60.fib : Val = 0xf9e180fe9b25;","highlight_start":17,"highlight_end":17}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"provide a type for the item","code":null,"level":"help","spans":[{"file_name":"src/fns.rs","byte_start":186,"byte_end":186,"line_start":7,"line_end":7,"column_start":17,"column_end":17,"is_primary":true,"text":[{"text":"pub const F__U60.fib : Val = 0xf9e180fe9b25;","highlight_start":17,"highlight_end":17}],"label":null,"suggested_replacement":": ","suggestion_applicability":"HasPlaceholders","expansion":null}],"children":[],"rendered":null}],"rendered":"\u001b[0m\u001b[1m\u001b[38;5;9merror\u001b[0m\u001b[0m\u001b[1m: missing type for `const` item\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/fns.rs:7:17\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m7\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0mpub const F__U60.fib : Val = 0xf9e180fe9b25;\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mhelp: provide a type for the item: `: `\u001b[0m\n\n"} -{"message":"aborting due to 3 previous errors","code":null,"level":"error","spans":[],"children":[],"rendered":"\u001b[0m\u001b[1m\u001b[38;5;9merror\u001b[0m\u001b[0m\u001b[1m: aborting due to 3 previous errors\u001b[0m\n\n"} -{"message":"For more information about this error, try `rustc --explain E0599`.","code":null,"level":"failure-note","spans":[],"children":[],"rendered":"\u001b[0m\u001b[1mFor more information about this error, try `rustc --explain E0599`.\u001b[0m\n"} diff --git a/book/.hvm/target/release/.fingerprint/nohash-hasher-dc8a50f19f38f70e/dep-lib-nohash-hasher b/book/.hvm/target/release/.fingerprint/nohash-hasher-dc8a50f19f38f70e/dep-lib-nohash-hasher deleted file mode 100644 index 1b1cb4d44..000000000 Binary files a/book/.hvm/target/release/.fingerprint/nohash-hasher-dc8a50f19f38f70e/dep-lib-nohash-hasher and /dev/null differ diff --git a/book/.hvm/target/release/.fingerprint/nohash-hasher-dc8a50f19f38f70e/invoked.timestamp b/book/.hvm/target/release/.fingerprint/nohash-hasher-dc8a50f19f38f70e/invoked.timestamp deleted file mode 100644 index e00328da5..000000000 --- a/book/.hvm/target/release/.fingerprint/nohash-hasher-dc8a50f19f38f70e/invoked.timestamp +++ /dev/null @@ -1 +0,0 @@ -This file has an mtime of when this was started. \ No newline at end of file diff --git a/book/.hvm/target/release/.fingerprint/nohash-hasher-dc8a50f19f38f70e/lib-nohash-hasher b/book/.hvm/target/release/.fingerprint/nohash-hasher-dc8a50f19f38f70e/lib-nohash-hasher deleted file mode 100644 index a2544425c..000000000 --- a/book/.hvm/target/release/.fingerprint/nohash-hasher-dc8a50f19f38f70e/lib-nohash-hasher +++ /dev/null @@ -1 +0,0 @@ -7cda99c4629b01d5 \ No newline at end of file diff --git a/book/.hvm/target/release/.fingerprint/nohash-hasher-dc8a50f19f38f70e/lib-nohash-hasher.json b/book/.hvm/target/release/.fingerprint/nohash-hasher-dc8a50f19f38f70e/lib-nohash-hasher.json deleted file mode 100644 index 26841172b..000000000 --- a/book/.hvm/target/release/.fingerprint/nohash-hasher-dc8a50f19f38f70e/lib-nohash-hasher.json +++ /dev/null @@ -1 +0,0 @@ -{"rustc":15865603277963313838,"features":"[\"default\", \"std\"]","target":2117364898282992337,"profile":15536308671813986309,"path":6587989396380489126,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"release/.fingerprint/nohash-hasher-dc8a50f19f38f70e/dep-lib-nohash-hasher"}}],"rustflags":[],"metadata":8492774112348001440,"config":2202906307356721367,"compile_kind":0} \ No newline at end of file diff --git a/book/.hvm/target/release/deps/hvmc-5d3b356175a8b321.d b/book/.hvm/target/release/deps/hvmc-5d3b356175a8b321.d deleted file mode 100644 index 1eebd12de..000000000 --- a/book/.hvm/target/release/deps/hvmc-5d3b356175a8b321.d +++ /dev/null @@ -1,12 +0,0 @@ -/Users/v/vic/dev/kind2/book/.hvm/target/release/deps/libhvmc-5d3b356175a8b321.rmeta: src/lib.rs src/ast.rs src/fns.rs src/jit.rs src/run.rs src/u60.rs - -/Users/v/vic/dev/kind2/book/.hvm/target/release/deps/libhvmc-5d3b356175a8b321.rlib: src/lib.rs src/ast.rs src/fns.rs src/jit.rs src/run.rs src/u60.rs - -/Users/v/vic/dev/kind2/book/.hvm/target/release/deps/hvmc-5d3b356175a8b321.d: src/lib.rs src/ast.rs src/fns.rs src/jit.rs src/run.rs src/u60.rs - -src/lib.rs: -src/ast.rs: -src/fns.rs: -src/jit.rs: -src/run.rs: -src/u60.rs: diff --git a/book/.hvm/target/release/deps/libnohash_hasher-dc8a50f19f38f70e.rlib b/book/.hvm/target/release/deps/libnohash_hasher-dc8a50f19f38f70e.rlib deleted file mode 100644 index b376a6387..000000000 Binary files a/book/.hvm/target/release/deps/libnohash_hasher-dc8a50f19f38f70e.rlib and /dev/null differ diff --git a/book/.hvm/target/release/deps/libnohash_hasher-dc8a50f19f38f70e.rmeta b/book/.hvm/target/release/deps/libnohash_hasher-dc8a50f19f38f70e.rmeta deleted file mode 100644 index 95eb62d54..000000000 Binary files a/book/.hvm/target/release/deps/libnohash_hasher-dc8a50f19f38f70e.rmeta and /dev/null differ diff --git a/book/.hvm/target/release/deps/nohash_hasher-dc8a50f19f38f70e.d b/book/.hvm/target/release/deps/nohash_hasher-dc8a50f19f38f70e.d deleted file mode 100644 index 6503515cc..000000000 --- a/book/.hvm/target/release/deps/nohash_hasher-dc8a50f19f38f70e.d +++ /dev/null @@ -1,7 +0,0 @@ -/Users/v/vic/dev/kind2/book/.hvm/target/release/deps/libnohash_hasher-dc8a50f19f38f70e.rmeta: /Users/v/.cargo/registry/src/index.crates.io-6f17d22bba15001f/nohash-hasher-0.2.0/src/lib.rs - -/Users/v/vic/dev/kind2/book/.hvm/target/release/deps/libnohash_hasher-dc8a50f19f38f70e.rlib: /Users/v/.cargo/registry/src/index.crates.io-6f17d22bba15001f/nohash-hasher-0.2.0/src/lib.rs - -/Users/v/vic/dev/kind2/book/.hvm/target/release/deps/nohash_hasher-dc8a50f19f38f70e.d: /Users/v/.cargo/registry/src/index.crates.io-6f17d22bba15001f/nohash-hasher-0.2.0/src/lib.rs - -/Users/v/.cargo/registry/src/index.crates.io-6f17d22bba15001f/nohash-hasher-0.2.0/src/lib.rs: diff --git a/book/Tree.kind2 b/book/Tree.kind2 new file mode 100644 index 000000000..2422a6287 --- /dev/null +++ b/book/Tree.kind2 @@ -0,0 +1,3 @@ +data Tree A +| node (val: A) (lft: (Tree A)) (rgt: (Tree A)) +| leaf diff --git a/book/Tree/fold.kind2 b/book/Tree/fold.kind2 new file mode 100644 index 000000000..8b460245b --- /dev/null +++ b/book/Tree/fold.kind2 @@ -0,0 +1,7 @@ +use Tree/{node,leaf} + +Tree/fold (bm: (Tree A)) (nd: A -> B -> B -> B) (lf: B) : B = + match bm with (nd: A -> B -> B -> B) (lf: B) { + node: (nd bm.val (Tree/fold bm.lft nd lf) (Tree/fold bm.rgt nd lf)) + leaf: lf + } diff --git a/book/Tree/gen.kind2 b/book/Tree/gen.kind2 new file mode 100644 index 000000000..d5a9310df --- /dev/null +++ b/book/Tree/gen.kind2 @@ -0,0 +1,7 @@ +use Tree/{node,leaf} + +gen (n: U60) (x: U60) : (Tree U60) = + switch n { + 0: leaf + _: (node x (gen n-1 (+ (* x 2) 1)) (gen n-1 (+ (* x 2) 2))) + } diff --git a/book/Tree/leaf.kind2 b/book/Tree/leaf.kind2 new file mode 100644 index 000000000..424d6d2ac --- /dev/null +++ b/book/Tree/leaf.kind2 @@ -0,0 +1,3 @@ +leaf : (Tree A) = + ~λP λnode λleaf + leaf diff --git a/book/Tree/node.kind2 b/book/Tree/node.kind2 new file mode 100644 index 000000000..fa4d00e25 --- /dev/null +++ b/book/Tree/node.kind2 @@ -0,0 +1,3 @@ +node (val: A) (lft: (Tree A)) (rgt: (Tree A)) : (Tree A) = + ~λP λnode λleaf + (node val lft rgt) diff --git a/book/Tree/sum.kind2 b/book/Tree/sum.kind2 new file mode 100644 index 000000000..8d73533fb --- /dev/null +++ b/book/Tree/sum.kind2 @@ -0,0 +1,18 @@ +use Tree/{node,leaf} + +// Sums a binary tree in parallel, using fold +// __(1)__ +// / \ __(1)__ +// (1) (2) => / \ => (14) +// / \ / \ (4) (9) +// (1) (2) (3) (4) + +sum (x: (Tree U60)) : U60 = + fold x { + node: (+ x.val (+ x.lft x.rgt)) + leaf: 0 + } + + + + diff --git a/book/_main b/book/_main new file mode 100755 index 000000000..2e41c4d46 Binary files /dev/null and b/book/_main differ diff --git a/book/_main.hvm2 b/book/_main.hvm2 index 382aeeb4b..4ada7e3ef 100644 --- a/book/_main.hvm2 +++ b/book/_main.hvm2 @@ -1,21 +1,33 @@ +//WARNING: unsolved metas_ +//WARNING: unsolved metas_ _Char = 0 _List = λ_T 0 -_List.Folder = λ_T 0 -_List.cons = λ_T λ_head λ_tail λ_P λ_cons λ_nil ((_cons _head) _tail) -_List.fold = λ_list - let _list.P = 0 - let _list.cons = λ_list.head λ_list.tail λ_P λ_c λ_n ((_c _list.head) (((((_List.fold) _list.tail) _P) _c) _n)) - let _list.nil = λ_P λ_c λ_n _n - (((_list _list.P) _list.cons) _list.nil) -_List.map = λ_A λ_B λ_xs λ_f (((((_List.fold) _xs) 0) λ_h λ_t (((_List.cons 0) (_f _h)) _t)) (_List.nil 0)) -_List.nil = λ_T λ_P λ_cons λ_nil _nil +_List_cons = λ_head λ_tail λ_P λ_cons λ_nil ((_cons _head) _tail) +_List_nil = λ_P λ_cons λ_nil _nil _Nat = 0 -_Nat.succ = λ_n λ_P λ_succ λ_zero (_succ _n) -_Nat.zero = λ_P λ_succ λ_zero _zero +_Nat_succ = λ_n λ_P λ_succ λ_zero (_succ _n) +_Nat_zero = λ_P λ_succ λ_zero _zero _String = (_List _Char) -_String.cons = λ_head λ_tail λ_P λ_cons λ_nil ((_cons _head) _tail) -_String.nil = λ_P λ_cons λ_nil _nil -__main = ((((_List.map 0) 0) (((_List.cons 0) 1) (((_List.cons 0) 2) (((_List.cons 0) 3) (_List.nil 0))))) λ_x (+ _x 1)) +_String_cons = λ_head λ_tail λ_P λ_cons λ_nil ((_cons _head) _tail) +_String_nil = λ_P λ_cons λ_nil _nil +_Tree = λ_A 0 +_Tree_fold = λ_bm λ_nd λ_lf + let _bm_P = 0 + let _bm_node = λ_bm_val λ_bm_lft λ_bm_rgt λ_nd λ_lf (((_nd _bm_val) (((_Tree_fold _bm_lft) _nd) _lf)) (((_Tree_fold _bm_rgt) _nd) _lf)) + let _bm_leaf = λ_nd λ_lf _lf + (((((_bm _bm_P) _bm_node) _bm_leaf) _nd) _lf) +_Tree_gen = λ_n λ_x match _n = _n { + 0: _Tree_leaf + 1+: let _n-1 = _n-1 + (((_Tree_node _x) ((_Tree_gen _n-1) (+ (* _x 2) 1))) ((_Tree_gen _n-1) (+ (* _x 2) 2))) +} +_Tree_leaf = λ_P λ_node λ_leaf _leaf +_Tree_node = λ_val λ_lft λ_rgt λ_P λ_node λ_leaf (((_node _val) _lft) _rgt) +_Tree_sum = λ_x + let _x_P = 0 + let _x_node = λ_x_val λ_x_lft λ_x_rgt (+ _x_val (+ _x_lft _x_rgt)) + let _x_leaf = 0 + (((_Tree_fold _x) _x_node) _x_leaf) +__main = (_Tree_sum ((_Tree_gen 22) 0)) main = __main - diff --git a/book/_main.hvmc b/book/_main.hvmc index b317df7dd..dd77465a1 100644 --- a/book/_main.hvmc +++ b/book/_main.hvmc @@ -1,17 +1,37 @@ -@cons = (a (b ((a (b c)) (* c)))) - -@foo = ({3 (a b) c} (a (d e))) - & @cons ~ (b (f e)) - & @map ~ (c (d f)) - -@main = a - & @map ~ ((<+ #1 b> b) (c a)) - & @cons ~ (#1 (d c)) - & @cons ~ (#2 (e d)) - & @cons ~ (#3 (@nil e)) - -@map = (a ((b (@nil c)) c)) - & @foo ~ (a b) - -@nil = (* (a a)) +@_chr = #0 +@_ls = (* #0) +@_ls_cn = (a (b (* ((a (b c)) (* c))))) +@_ls_nl = (* @_ls_nl_0) +@_ls_nl_0 = (* (a a)) +@_N = #0 +@_N_s = (a (* ((a b) (* b)))) +@_N_z = (* @_N_z_0) +@_N_z_0 = (* (a a)) +@_str = a +& @_ls ~ (@_chr a) +@_str_cn = (a (b (* ((a (b c)) (* c))))) +@_str_nl = (* @_str_nl_0) +@_str_nl_0 = (* (a a)) +@_T = (* #0) +@_T_f = ((#0 (@_T_f_0 (@_T_f_1 (a (b c))))) (a (b c))) +@_T_f_0 = (a (b (c ({7 (a (d (e f))) {7 g h}} ({9 i j} f))))) +& @_T_f ~ (c (h (j e))) +& @_T_f ~ (b (g (i d))) +@_T_f_1 = (* (a a)) +@_T_gn = (?<(@_T_gn_0 @_T_gn_1) (a b)> (a b)) +@_T_gn_0 = (* @_T_lf) +@_T_gn_1 = ({5 a b} ({3 c {3 <* #2 <+ #1 d>> <* #2 <+ #2 e>>}} f)) +& @_T_nd ~ (c (g (h f))) +& @_T_gn ~ (b (e h)) +& @_T_gn ~ (a (d g)) +@_T_lf = (* @_T_lf_0) +@_T_lf_0 = (* (a a)) +@_T_nd = (a (b (c (* ((a (b (c d))) (* d)))))) +@_T_sum = (a b) +& @_T_f ~ (a (@_T_sum_0 (#0 b))) +@_T_sum_0 = (<+ a b> (<+ c a> (c b))) +@__main = a +& @_T_sum ~ (b a) +& @_T_gn ~ (#22 (#0 b)) +@main = @__main diff --git a/book/_main.js b/book/_main.js new file mode 100644 index 000000000..448a2a17a --- /dev/null +++ b/book/_main.js @@ -0,0 +1,36 @@ +let _Char = 0; +let _List = (_T) => 0; +let _List_cons = (_head) => (_tail) => (_P) => (_cons) => (_nil) => ((_cons(_head))(_tail)); +let _List_nil = (_P) => (_cons) => (_nil) => _nil; +let _Nat = 0; +let _Nat_succ = (_n) => (_P) => (_succ) => (_zero) => (_succ(_n)); +let _Nat_zero = (_P) => (_succ) => (_zero) => _zero; +let _String = (_List(_Char)); +let _String_cons = (_head) => (_tail) => (_P) => (_cons) => (_nil) => ((_cons(_head))(_tail)); +let _String_nil = (_P) => (_cons) => (_nil) => _nil; +let _Tree = (_A) => 0; +let _Tree_fold = (_bm) => (_nd) => (_lf) => { + let _bm_P = 0; + let _bm_node = (_bm_val) => (_bm_lft) => (_bm_rgt) => (_nd) => (_lf) => (((_nd(_bm_val))(((_Tree_fold(_bm_lft))(_nd))(_lf)))(((_Tree_fold(_bm_rgt))(_nd))(_lf))); + let _bm_leaf = (_nd) => (_lf) => _lf; + return (((((_bm(_bm_P))(_bm_node))(_bm_leaf))(_nd))(_lf)); +}; +let _Tree_gen = (_n) => (_x) => { + if (_n === 0) { + return _Tree_leaf; + } else { + let _n_1 = _n - 1; + return (((_Tree_node(_x))((_Tree_gen(_n_1))(((_x * 2) + 1))))((_Tree_gen(_n_1))(((_x * 2) + 2)))); + } +}; +let _Tree_leaf = (_P) => (_node) => (_leaf) => _leaf; +let _Tree_node = (_val) => (_lft) => (_rgt) => (_P) => (_node) => (_leaf) => (((_node(_val))(_lft))(_rgt)); +let _Tree_sum = (_x) => { + let _x_P = 0; + let _x_node = (_x_val) => (_x_lft) => (_x_rgt) => (_x_val + (_x_lft + _x_rgt)); + let _x_leaf = 0; + return (((_Tree_fold(_x))(_x_node))(_x_leaf)); +}; +let __main = (_Tree_sum((_Tree_gen(22))(0))); + +console.log(__main) diff --git a/book/_main.kind2 b/book/_main.kind2 index e7191392c..fa84f90fb 100644 --- a/book/_main.kind2 +++ b/book/_main.kind2 @@ -1,11 +1,40 @@ -//data Vector A (len: Nat) -//| cons (len: Nat) (head: A) (tail: (Vector A len)) : (Vector A (Nat.succ len)) -//| nil : (Vector A Nat.zero) + +//// Sums a binary tree in parallel, using fold +//// __(1)__ +//// / \ __(1)__ +//// (1) (2) => / \ => (14) +//// / \ / \ (4) (9) +//// (1) (2) (3) (4) + +//use Tree/{node,leaf} + +//data Tree A +//| node (val: A) (lft: (Tree A)) (rgt: (Tree A)) +//| leaf + +//sum (x: (Tree U60)) : U60 = + //fold x { + //node: (+ x.val (+ x.lft x.rgt)) + //leaf: 0 + //} + +//gen (n: U60) (x: U60) : (Tree U60) = + //switch n { + //0: leaf + //_: (node x (gen n-1 (+ (* x 2) 1)) (gen n-1 (+ (* x 2) 2))) + //} + +//main = (sum (gen 16 0)) + +use Tree/{node,leaf,gen,sum} + +_main: U60 = + (sum (gen 16 0)) //use Nat.{succ,zero} -_main: (List U60) = - (List.map _ _ (List.cons _ 1 (List.cons _ 2 (List.cons _ 3 (List.nil _)))) λx(+ x 1)) +//_main: (List U60) = + //(List.map _ _ (List.cons _ 1 (List.cons _ 2 (List.cons _ 3 (List.nil _)))) λx(+ x 1)) //_main : (Maybe U60) = //(Maybe.bind _ _ (Maybe.some _ 1) λx diff --git a/src/sugar/mod.rs b/src/sugar/mod.rs index a29ef9173..3a389ee59 100644 --- a/src/sugar/mod.rs +++ b/src/sugar/mod.rs @@ -922,9 +922,9 @@ impl Term { // 3. Make `(~x )` or `(Type/fold/ _ x)` if mat.fold { term = Term::App { - era: true, + era: false, fun: Box::new(Term::App { - era: false, + era: true, fun: Box::new(Term::App { era: true, fun: Box::new(Term::Var { nam: format!("{}/fold/", adt.name) }), @@ -936,7 +936,7 @@ impl Term { }; } else { term = Term::App { - era: true, + era: false, fun: Box::new(Term::Ins { val: Box::new(mat.expr.clone()) }), diff --git a/src/term/compile.rs b/src/term/compile.rs index d0457f9f9..0eaa2c8d6 100644 --- a/src/term/compile.rs +++ b/src/term/compile.rs @@ -300,7 +300,7 @@ impl Term { let x = x.to_hvm2(); let z = z.to_hvm2(); let s = s.to_hvm2(); - format!("match {} = {} {{ 0: {} +: {} }}", Term::to_hvm2_name(nam), x, z, s) + format!("match {} = {} {{ 0: {} 1+: {} }}", Term::to_hvm2_name(nam), x, z, s) }, Term::Let { nam, val, bod } => { let val = val.to_hvm2(); @@ -339,7 +339,7 @@ impl Term { } pub fn to_hvm2_name(name: &str) -> String { - format!("_{}", name) + format!("_{}", name.replace("/",".")) }