implicits args and stuff

This commit is contained in:
Victor Taelin 2024-03-15 18:51:25 -03:00
parent dd6706a7d5
commit 0647ea9bc7
54 changed files with 4079 additions and 684 deletions

1
.gitignore vendored
View File

@ -8,3 +8,4 @@ demo/
.fill.tmp
.check.hs
guide.txt
.hvm

16
book/.hvm/Cargo.lock generated Normal file
View File

@ -0,0 +1,16 @@
# 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"

31
book/.hvm/Cargo.toml Normal file
View File

@ -0,0 +1,31 @@
[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"

799
book/.hvm/src/ast.rs Normal file
View File

@ -0,0 +1,799 @@
// 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<Tree>, rgt: Box<Tree> },
Tup { lft: Box<Tree>, rgt: Box<Tree> },
Dup { lab: run::Lab, lft: Box<Tree>, rgt: Box<Tree> },
Var { nam: String },
Ref { nam: run::Val },
Num { val: run::Val },
Op1 { opr: run::Lab, lft: run::Val, rgt: Box<Tree> },
Op2 { opr: run::Lab, lft: Box<Tree>, rgt: Box<Tree> },
Mat { sel: Box<Tree>, ret: Box<Tree> },
}
type Redex = Vec<(Tree, Tree)>;
#[derive(Clone, Hash, PartialEq, Eq, Debug)]
pub struct Net {
pub root: Tree,
pub rdex: Redex,
}
pub type Book = BTreeMap<String, Net>;
// Parser
// ------
// FIXME: remove after skip is fixed
fn skip_spaces(chars: &mut Peekable<Chars>) {
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<Chars>) {
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<Chars>, 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<Chars>) -> Result<u64, String> {
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<Chars>) -> Result<String, String> {
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<Chars>) -> Result<String, String> {
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<Chars>) -> Result<run::Lab, String> {
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<Chars>) -> Result<Tree, String> {
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<Chars>) -> Result<Net, String> {
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<Chars>) -> Result<Book, String> {
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<T>(code: &str, parse_fn: impl Fn(&mut Peekable<Chars>) -> Result<T, String>) -> 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<const LAZY: bool>(rt_net: &run::NetFields<LAZY>, 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<const LAZY: bool>(rt_net: &run::NetFields<LAZY>) -> 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<u8> {
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<u8>) -> 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<u8> {
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<u8>) -> 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<const LAZY: bool>(rt_net: &mut run::NetFields<LAZY>, tree: &Tree, vars: &mut HashMap<String, Parent>, 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<const LAZY: bool>(rt_net: &mut run::NetFields<LAZY>, 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<const LAZY: bool>(rt_net: &mut run::NetFields<LAZY>, 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<run::Lab, nohash_hasher::BuildNoHashHasher<run::Lab>>;
type InsideRefs = HashSet<run::Val>;
#[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<run::Val, Inside>, fid: run::Val, seen: &mut HashSet<run::Val>) {
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::<false>::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<const LAZY: bool>(net: &run::NetFields<LAZY>) -> 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<LAZY>, 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<const LAZY: bool>(rt_net: &run::NetFields<LAZY>, ptr: run::Ptr, parent: Parent, vars: &mut HashMap<Parent, String>, 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<const LAZY: bool>(rt_net: &run::NetFields<LAZY>, 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<const LAZY: bool>(rt_net: &run::NetFields<LAZY>) -> 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::<false>::init(def.node.len());
let net = net_from_runtime(&runtime_def_to_runtime_net(&nodes, &def));
book.insert(name, net);
}
}
book
}

355
book/.hvm/src/fns.rs Normal file
View File

@ -0,0 +1,355 @@
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;
}
}

452
book/.hvm/src/jit.rs Normal file
View File

@ -0,0 +1,452 @@
// 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<NUM>'
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<run::Ptr, String>,
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<run::Val>,
newx : &mut usize,
vars : &mut HashMap<run::Ptr, String>,
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<run::Val>,
newx : &mut usize,
vars : &mut HashMap<run::Ptr, String>,
def : &run::Def,
ptr : run::Ptr,
trg : &Target,
) -> String {
//println!("burn {:08x} {}", ptr.0, x);
let mut code = String::new();
// (<?(ifz ifs) ret> 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<run::Ptr, String>,
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<run::Ptr, String>,
def : &run::Def,
ptr : run::Ptr,
) -> Option<String> {
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.");
}
}

14
book/.hvm/src/lib.rs Normal file
View File

@ -0,0 +1,14 @@
#![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;

256
book/.hvm/src/main.rs Normal file
View File

@ -0,0 +1,256 @@
#![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<String>,
}
fn get_args() -> Args {
let args: Vec<String> = 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::<HashSet<_>>();
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<dyn std::error::Error>> {
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 <file.hvmc> [-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 <file.hvmc>");
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 <file.hvmc>");
std::process::exit(1);
}
}
_ => {
println!("Usage: hvmc <cmd> <file.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<dyn std::error::Error>> {
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;
}

1389
book/.hvm/src/run.rs Normal file

File diff suppressed because it is too large Load Diff

113
book/.hvm/src/u60.rs Normal file
View File

@ -0,0 +1,113 @@
// 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);
}

View File

@ -0,0 +1 @@
{"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":{}}

View File

@ -0,0 +1,3 @@
Signature: 8a477f597d28d172789f06886806bc55
# This file is a cache directory tag created by cargo.
# For information about cache directory tags see https://bford.info/cachedir/

View File

View File

@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@ -0,0 +1,5 @@
{"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":": <type>","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: `: <type>`\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"}

View File

@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@ -0,0 +1 @@
{"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}

View File

@ -0,0 +1,12 @@
/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:

View File

@ -0,0 +1,7 @@
/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:

View File

@ -1,5 +1,7 @@
Bool.and (a: Bool) (b: Bool) : Bool =
use Bool.{true,false,and}
and (a: Bool) (b: Bool) : Bool =
match b {
Bool.true: b
Bool.false: Bool.false
true: b
false: false
}

View File

@ -1,2 +1,2 @@
Bool.false : Bool =
false : Bool =
~λP λt λf f

View File

@ -1,6 +1,6 @@
use Bool.{true,false,not}
notnot (x: Bool) : {(not (not x)) = x} =
notnot (x: Bool) : (not (not x)) == x =
match x {
true: {=}
false: {=}

View File

@ -1,10 +1,11 @@
use Equal.{refl}
apply A B (f: ∀(x:A) B) (a: A) (b: A) (e: {a = b}) : {(f a) = (f b)} =
apply <A> <B> <a> <b> (f: A -> B) (e: a == b) : (f a) == (f b) =
match e {
refl: {=}
}
//Equal.apply
//: ∀(A: *)
//∀(B: *)

View File

@ -0,0 +1,3 @@
cons <T> (head: T) (tail: (List.Church T)) : (List.Church T)
= λP λcons λnil
(cons head (tail P cons nil))

5
book/List.Church.kind2 Normal file
View File

@ -0,0 +1,5 @@
List.Church (T) : * =
∀(P: *)
∀(cons: T -> P -> P)
∀(nil: P)
P

View File

@ -0,0 +1,3 @@
List.Church.nil (T) : (List.Church T)
= λP λcons λnil
nil

View File

@ -1,5 +0,0 @@
List.Folder.cons
: ∀(T: *) ∀(head: T) ∀(tail: (List.Folder T))
(List.Folder T)
= λT λhead λtail λP λcons λnil
(cons head (tail P cons nil))

View File

@ -1,7 +0,0 @@
List.Folder
: ∀(T: *) *
= λT
∀(P: *)
∀(cons: ∀(head: T) ∀(tail: P) P)
∀(nil: P)
P

View File

@ -1,3 +0,0 @@
List.Folder.nil
: ∀(T: *) (List.Folder T)
= λT λP λcons λnil nil

View File

@ -1,3 +1,2 @@
List.cons
: ∀(T: *) ∀(head: T) ∀(tail: (List T)) (List T)
= λT λhead λtail ~λP λcons λnil (cons head tail)
List.cons <T> (head: T) (tail: (List T)) : (List T) =
~λP λcons λnil (cons head tail)

View File

@ -1,7 +1,7 @@
List.fold
: ∀(T: *) ∀(list: (List T)) (List.Folder T)
= λT λlist λP λcons λnil
use fold_P = λxs P
use fold_cons = λhead λtail (cons head (List.fold T tail P cons nil))
use fold_nil = nil
(~list fold_P fold_cons fold_nil)
use List.{cons,nil}
List.fold <A> <B> (xs: (List A)) (c: A -> B -> B) (n: B) : B =
match xs {
cons: (c xs.head (List.fold xs.tail c n))
nil: n
}

16
book/List.map.kind2 Normal file
View File

@ -0,0 +1,16 @@
use List.{cons,nil}
map <A> <B> (xs: (List A)) (f: A -> B) : (List B) =
fold xs {
cons: (cons (f xs.head) xs.tail)
nil: []
}
//map A B (xs: (List A)) (f: ∀(x: A) B) : (List B) =
//(List.fold _ xs _ λhλt(cons _ (f h) t) [])
//match xs {
//cons: (cons _ (f xs.head) (map _ _ xs.tail f))
//nil: []
//}

View File

@ -1,3 +1,2 @@
List.nil
: ∀(T: *) (List T)
= λT ~λP λcons λnil nil
List.nil <T> : (List T) =
~λP λcons λnil nil

View File

@ -2,6 +2,6 @@ use Nat.{succ,zero,half,double}
bft (n: Nat) : {(half (double n)) = n} =
match n {
succ: (Equal.apply _ _ succ _ _ (bft n.pred))
succ: (Equal.apply _ _ _ _ succ (bft n.pred))
zero: {=}
}

21
book/_main.hvm2 Normal file
View File

@ -0,0 +1,21 @@
_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
_Nat = 0
_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))
main = __main

17
book/_main.hvmc Normal file
View File

@ -0,0 +1,17 @@
@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))

View File

@ -4,8 +4,42 @@
//use Nat.{succ,zero}
_main : U60 =
(U60.fib 6)
_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
//(Maybe.bind _ _ (Maybe.some _ 2) λy
//(Maybe.some _ (+ x y))))
//main : (Maybe U60) = {
//x = !bar
//y = 80
//for i in [1,2,!(foo y)]:
//for j in [10,20,30]:
//x += i
//if x > j:
//return x
//return x + y
//}
//fold xs:
//cons:
//xs.head + xs.tail
//nil:
//0
//for x in xs:
//_main (a: Nat) (b: Nat) (e: (Equal A a b)) : (Equal A a b) =
//switch a {

14
book/_tmp.hvm2 Normal file
View File

@ -0,0 +1,14 @@
_Char = 0
_List = λ_T 0
_List.cons = λ_T λ_head λ_tail λ_P λ_cons λ_nil ((_cons _head) _tail)
_List.map = λ_A λ_B λ_xs let _xs.P = 0 let _xs.cons = λ_xs.head λ_xs.tail λ_f (((_List.cons 0) (_f _xs.head)) ((((_List.map 0) 0) _xs.tail) _f)) let _xs.nil = λ_f (_List.nil 0) (((_xs _xs.P) _xs.cons) _xs.nil)
_List.nil = λ_T λ_P λ_cons λ_nil _nil
_Nat = 0
_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 (_List.cons 0 1 (_List.cons 0 2 (_List.nil 0))) λx(+ x 10))

19
book/_tmp.kind2 Normal file
View File

@ -0,0 +1,19 @@
WARNING: unsolved metas.
WARNING: unsolved metas.
WARNING: unsolved metas.
WARNING: unsolved metas.
WARNING: unsolved metas.
_Char = 0
_List = λ_T 0
_List.cons = λ_T λ_head λ_tail λ_P λ_cons λ_nil ((_cons _head) _tail)
_List.map = λ_A λ_B λ_xs let _xs.P = 0 let _xs.cons = λ_xs.head λ_xs.tail λ_f (((_List.cons 0) (_f _xs.head)) ((((_List.map 0) 0) _xs.tail) _f)) let _xs.nil = λ_f (_List.nil 0) (((_xs _xs.P) _xs.cons) _xs.nil)
_List.nil = λ_T λ_P λ_cons λ_nil _nil
_Nat = 0
_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

436
kind2.ts
View File

@ -1,436 +0,0 @@
#!/usr/bin/env ts-node
// TODO: move the parser to HVM
// ICC: Parser, Stringifier and CLI
// ================================
// List
// ----
type List<A> =
| { tag: "Cons"; head: A; tail: List<A> }
| { tag: "Nil"; };
// Constructors
const Cons = <A>(head: A, tail: List<A>): List<A> => ({ tag: "Cons", head, tail });
const Nil = <A>(): List<A> => ({ tag: "Nil" });
// Term
// ----
type Term =
| { $: "All"; nam: string; inp: Term; bod: (x:Term)=> Term } // ∀(x: inp) bod
| { $: "Lam"; nam: string; bod: (x:Term)=> Term } // λx bod
| { $: "App"; fun: Term; arg: Term } // (fun arg)
| { $: "Ann"; val: Term; typ: Term } // {val:typ}
| { $: "Slf"; nam: string; bod: (x:Term)=> Term } // $x bod
| { $: "Ins"; val: Term } // ~val
| { $: "Set" } // *
| { $: "U60" } // #U60
| { $: "Num"; val: BigInt } // #num
| { $: "Op2"; opr: string; fst: Term; snd: Term } // #(<opr> fst snd)
| { $: "Mat"; nam: string; x: Term; z: Term; s: (x:Term) => Term; p: (x: Term) => Term } // #match k = x { zero: z; succ: s }: p
| { $: "Txt"; txt: string } // "text"
| { $: "Ref"; nam: string; val?: Term }
| { $: "Let"; nam: string; val: Term; bod: (x:Term)=> Term }
| { $: "Hol"; nam: string }
| { $: "Var"; nam: string; idx: number };
// Constructors
export const All = (nam: string, inp: Term, bod: (x:Term)=> Term): Term => ({ $: "All", nam, inp, bod });
export const Lam = (nam: string, bod: (x:Term)=> Term): Term => ({ $: "Lam", nam, bod });
export const App = (fun: Term, arg: Term): Term => ({ $: "App", fun, arg });
export const Ann = (val: Term, typ: Term): Term => ({ $: "Ann", val, typ });
export const Slf = (nam: string, bod: (x:Term)=> Term): Term => ({ $: "Slf", nam, bod });
export const Ins = (val: Term): Term => ({ $: "Ins", val });
export const Set = (): Term => ({ $: "Set" });
export const U60 = (): Term => ({ $: "U60" });
export const Num = (val: BigInt): Term => ({ $: "Num", val });
export const Op2 = (opr: string, fst: Term, snd: Term): Term => ({ $: "Op2", opr, fst, snd });
export const Mat = (nam: string, x: Term, z: Term, s: (x:Term) => Term, p: (x:Term) => Term): Term => ({ $: "Mat", nam, x, z, s, p });
export const Txt = (txt: string): Term => ({ $: "Txt", txt });
export const Ref = (nam: string, val?: Term): Term => ({ $: "Ref", nam, val });
export const Let = (nam: string, val: Term, bod: (x:Term)=> Term): Term => ({ $: "Let", nam, val, bod });
export const Hol = (nam: string): Term => ({ $: "Hol", nam });
export const Var = (nam: string, idx: number): Term => ({ $: "Var", nam, idx });
// Book
// ----
type Book = {[name: string]: Term};
// Stringifier
// -----------
export const name = (numb: number): string => {
let name = '';
do {
name = String.fromCharCode(97 + (numb % 26)) + name;
numb = Math.floor(numb / 26) - 1;
} while (numb >= 0);
return name;
}
export const context = (numb: number): string => {
var names = [];
for (var i = 0; i < numb; ++i) {
names.push(name(i));
}
return "["+names.join(",")+"]";
}
const compile_oper = (opr: string): string => {
switch (opr) {
case "+" : return "ADD";
case "-" : return "SUB";
case "*" : return "MUL";
case "/" : return "DIV";
case "%" : return "MOD";
case "==" : return "EQ";
case "!=" : return "NE";
case "<" : return "LT";
case ">" : return "GT";
case "<=" : return "LTE";
case ">=" : return "GTE";
case "&" : return "AND";
case "|" : return "OR";
case "^" : return "XOR";
case "<<" : return "LSH";
case ">>" : return "RSH";
default: throw new Error("Unknown operator: " + opr);
}
};
export const compile = (term: Term, used_refs: any, dep: number = 0): string => {
switch (term.$) {
case "All": return `(All "${term.nam}" ${compile(term.inp, used_refs, dep)} λ${name(dep)} ${compile(term.bod(Var(term.nam,dep)), used_refs, dep + 1)})`;
case "Lam": return `(Lam "${term.nam}" λ${name(dep)} ${compile(term.bod(Var(term.nam,dep)), used_refs, dep + 1)})`;
case "App": return `(App ${compile(term.fun, used_refs, dep)} ${compile(term.arg, used_refs, dep)})`;
case "Ann": return `(Ann ${compile(term.val, used_refs, dep)} ${compile(term.typ, used_refs, dep)})`;
case "Slf": return `(Slf "${term.nam}" λ${name(dep)} ${compile(term.bod(Var(term.nam,dep)), used_refs, dep + 1)})`;
case "Ins": return `(Ins ${compile(term.val, used_refs, dep)})`;
case "Set": return `(Set)`;
case "U60": return `(U60)`;
case "Num": return `(Num ${term.val.toString()})`;
case "Op2": return `(Op2 ${compile_oper(term.opr)} ${compile(term.fst, used_refs, dep)} ${compile(term.snd, used_refs, dep)})`;
case "Mat": return `(Mat "${term.nam}" ${compile(term.x, used_refs, dep)} ${compile(term.z, used_refs, dep)} λ${name(dep)} ${compile(term.s(Var(term.nam,dep)), used_refs, dep + 1)} λ${name(dep)} ${compile(term.p(Var(term.nam,dep)), used_refs, dep + 1)})`;
case "Txt": return `(Txt \`${term.txt}\`)`;
case "Hol": return `(Hol "${term.nam}" ${context(dep)})`;
case "Var": return name(term.idx);
case "Ref": return (used_refs[term.nam] = 1), ("Book." + term.nam);
case "Let": return `(Let "${term.nam}" ${compile(term.val, used_refs, dep)} λ${name(dep)} ${compile(term.bod(Var(term.nam,dep)), used_refs, dep + 1)})`;
}
};
// Parser
// ------
export type Scope = List<[string, Term]>;
export function find<A>(list: Scope, nam: string): Term {
switch (list.tag) {
case "Cons": return list.head[0] === nam ? list.head[1] : find(list.tail, nam);
case "Nil" : return Ref(nam);
}
}
export function skip(code: string): string {
while (true) {
if (code.slice(0, 2) === "//") {
do { code = code.slice(1); } while (code.length != 0 && code[0] != "\n");
continue;
}
if (code[0] === "\n" || code[0] === " ") {
code = code.slice(1);
continue;
}
break;
}
return code;
}
export function is_name_char(c: string): boolean {
return /[a-zA-Z0-9_.-]/.test(c);
}
export function is_oper_char(c: string): boolean {
return /[+\-*/%<>=&|^!~]/.test(c);
}
export function parse_word(is_letter: (c: string) => boolean, code: string): [string, string] {
code = skip(code);
var name = "";
while (is_letter(code[0]||"")) {
name = name + code[0];
code = code.slice(1);
}
return [code, name];
}
export function parse_name(code: string): [string, string] {
return parse_word(is_name_char, code);
}
export function parse_oper(code: string): [string, string] {
return parse_word(is_oper_char, code);
}
export function parse_text(code: string, text: string): [string, null] {
code = skip(code);
if (text === "") {
return [code, null];
} else if (code[0] === text[0]) {
return parse_text(code.slice(1), text.slice(1));
} else {
throw "parse error";
}
}
export function parse_term(code: string): [string, (ctx: Scope) => Term] {
code = skip(code);
// ALL: `∀(x: A) B[x]` -SUGAR
if (code[0] === "∀") {
var [code, nam] = parse_name(code.slice(2));
var [code, _ ] = parse_text(code, ":");
var [code, inp] = parse_term(code);
var [code, _ ] = parse_text(code, ")");
var [code, bod] = parse_term(code);
return [code, ctx => All(nam, inp(ctx), x => bod(Cons([nam, x], ctx)))];
}
// LAM: `λx f`
if (code[0] === "λ") {
var [code, nam] = parse_name(code.slice(1));
var [code, bod] = parse_term(code);
return [code, ctx => Lam(nam, x => bod(Cons([nam, x], ctx)))];
}
// APP: `(f x y z ...)`
if (code[0] === "(") {
var [code, fun] = parse_term(code.slice(1));
var args: ((ctx: Scope) => Term)[] = [];
while (code[0] !== ")") {
var [code, arg] = parse_term(code);
args.push(arg);
code = skip(code);
}
var [code, _] = parse_text(code, ")");
return [code, ctx => args.reduce((f, x) => App(f, x(ctx)), fun(ctx))];
}
// ANN: `{x : T}`
if (code[0] === "{") {
var [code, val] = parse_term(code.slice(1));
var [code, _ ] = parse_text(code, ":");
var [code, typ] = parse_term(code);
var [code, _ ] = parse_text(code, "}");
return [code, ctx => Ann(val(ctx), typ(ctx))];
}
// SLF: `$x T`
if (code[0] === "$") {
var [code, nam] = parse_name(code.slice(1));
var [code, bod] = parse_term(code);
return [code, ctx => Slf(nam, x => bod(Cons([nam, x], ctx)))];
}
// INS: `~x`
if (code[0] === "~") {
var [code, val] = parse_term(code.slice(1));
return [code, ctx => Ins(val(ctx))];
}
// SET: `*`
if (code[0] === "*") {
return [code.slice(1), ctx => Set()];
}
// LET: `let x = A in B`
if (code.slice(0,4) === "let ") {
var [code, nam] = parse_name(code.slice(4));
var [code, _ ] = parse_text(code, "=");
var [code, val] = parse_term(code);
var [code, bod] = parse_term(code);
return [code, ctx => Let(nam, val(ctx), x => bod(Cons([nam, x], ctx)))];
}
// U60: `#U60`
if (code.slice(0,4) === "#U60") {
return [code.slice(4), ctx => U60()];
}
// OP2: `#(<opr> fst snd)`
if (code.slice(0,2) === "#(") {
var [code, opr] = parse_oper(code.slice(2));
var [code, fst] = parse_term(code);
var [code, snd] = parse_term(code);
var [code, _] = parse_text(code, ")");
return [code, ctx => Op2(opr, fst(ctx), snd(ctx))];
}
// MAT: `#match x = val { #0: z; #+: s }: p`
if (code.slice(0,7) === "#match ") {
var [code, nam] = parse_name(code.slice(7));
var [code, _ ] = parse_text(code, "=");
var [code, x] = parse_term(code);
var [code, _ ] = parse_text(code, "{");
var [code, _ ] = parse_text(code, "#0:");
var [code, z] = parse_term(code);
var [code, _ ] = parse_text(code, "#+:");
var [code, s] = parse_term(code);
var [code, _ ] = parse_text(code, "}");
var [code, _ ] = parse_text(code, ":");
var [code, p] = parse_term(code);
return [code, ctx => Mat(nam, x(ctx), z(ctx), k => s(Cons([nam+"-1", k], ctx)), k => p(Cons([nam, k], ctx)))];
}
// NUM: `#num`
if (code[0] === "#") {
var [code, num] = parse_name(code.slice(1));
return [code, ctx => Num(BigInt(num))];
}
// CHR: `'a'` -- char syntax sugar
if (code[0] === "'") {
var [code, chr] = [code.slice(2), code[1]];
var [code, _] = parse_text(code, "'");
return [code, ctx => Num(BigInt(chr.charCodeAt(0)))];
}
// STR: `"text"` -- string syntax sugar
if (code[0] === "\"" || code[0] === "`") {
var str = "";
var end = code[0];
code = code.slice(1);
while (code[0] !== end) {
str += code[0];
code = code.slice(1);
}
code = code.slice(1);
return [code, ctx => Txt(str)];
}
// HOL: `?name`
if (code[0] === "?") {
var [code, nam] = parse_name(code.slice(1));
return [code, ctx => Hol(nam)];
}
// VAR: `name`
var [code, nam] = parse_name(code);
if (nam.length > 0) {
return [code, ctx => find(ctx, nam)];
}
throw "parse error";
}
export function do_parse_term(code: string): Term {
return parse_term(code)[1](Nil());
}
export function parse_def(code: string): [string, {nam: string, val: Term}] {
var [code, nam] = parse_name(skip(code));
if (skip(code)[0] === ":") {
var [code, _ ] = parse_text(code, ":");
var [code, typ] = parse_term(code);
var [code, _ ] = parse_text(code, "=");
var [code, val] = parse_term(code);
return [code, {nam: nam, val: Ann(val(Nil()), typ(Nil()))}];
} else {
var [code, _ ] = parse_text(code, "=");
var [code, val] = parse_term(code);
return [code, {nam: nam, val: val(Nil())}];
}
}
export function parse_book(code: string): Book {
var book : Book = {};
while (code.length > 0) {
var [code, def] = parse_def(code);
book[def.nam] = def.val;
code = skip(code);
}
return book;
}
export function do_parse_book(code: string): Book {
return parse_book(code);
}
// CLI
// ---
import * as fs from "fs";
import { execSync } from "child_process";
export function main() {
// Loads Kind's HVM checker.
var kind2_hvm1 = fs.readFileSync(__dirname + "/kind2.hvm1", "utf8");
// Loads all local ".kind2" files.
const code = fs.readdirSync(".")
.filter(file => file.endsWith(".kind2"))
.map(file => fs.readFileSync("./"+file, "utf8"))
.join("\n");
// Parses into book.
const book = do_parse_book(code);
// Compiles book to hvm1.
//var book_hvm1 = "Names = [" + Object.keys(book).map(x => '"'+x+'"').join(",") + "]\n";
//var ref_count = 0;
var used_refs = {};
var book_hvm1 = "";
for (let name in book) {
book_hvm1 += "Book." + name + " = (Ref \"" + name + "\" " + compile(book[name], used_refs) + ")\n";
}
// Checks for unbound definitions
for (var ref_name in used_refs) {
if (!book[ref_name]) {
throw "Unbound definition: " + ref_name;
}
}
// Gets arguments.
const args = process.argv.slice(2);
const func = args[0];
const name = args[1];
// Creates main.
var main_hvm1 = "";
switch (func) {
case "check": {
main_hvm1 = "Main = (Checker Book." + name + ")\n";
break;
}
case "run": {
main_hvm1 = "Main = (Normalizer Book." + name + ")\n";
break;
}
default: {
console.log("Usage: kind2 [check|run] <name>");
}
}
// Generates the 'kind2.hvm1' file.
var checker_hvm1 = [kind2_hvm1, book_hvm1, main_hvm1].join("\n\n");
// Saves locally.
fs.writeFileSync("./.kind2.hvm1", checker_hvm1);
// Runs 'hvm1 kind2.hvm1 -s -L -1'
//const output = execSync("hvm1 run .kind2.hvm1 -s -L -1").toString();
//for (let name in book) {
//console.log("Checking: " + name);
const output = execSync("hvm1 run -t 1 -c -f .kind2.hvm1 \"(Main)\"").toString();
//const output = execSync(`hvm1 run -t 1 -c -f .kind2.hvm1 "(Checker Book.${name})"`).toString();
try {
var check_text = output.slice(output.indexOf("[["), output.indexOf("RWTS")).trim();
var stats_text = output.slice(output.indexOf("RWTS"));
var [logs, check] = JSON.parse(check_text);
logs.reverse();
for (var log of logs) {
console.log(log);
}
console.log(check ? "Check!" : "Error.");
console.log("");
console.log(stats_text);
} catch (e) {
console.log(output);
}
//}
};
main();

View File

@ -1,22 +0,0 @@
{
"name": "kind2",
"version": "0.1.0",
"description": "",
"main": "kind2.ts",
"bin": {
"kind2": "./kind2.ts"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/victortaelin/kind2.git"
},
"author": "VictorTaelin",
"license": "MIT",
"bugs": {
"url": "https://github.com/victortaelin/kind2/issues"
},
"homepage": "https://github.com/victortaelin/kind2#readme"
}

View File

@ -40,7 +40,7 @@ impl Book {
pub fn to_hvm2(&self) -> String {
let mut code = String::new();
for (name, term) in &self.defs {
code.push_str(&format!("{} = {}\n", name, term.to_hvm2()));
code.push_str(&format!("{} = {}\n", Term::to_hvm2_name(name), term.to_hvm2()));
}
code
}

View File

@ -49,6 +49,7 @@ impl Book {
book.load(base, "Nat")?;
book.load(base, "Nat.succ")?;
book.load(base, "Nat.zero")?;
book.expand_implicits();
return Ok(book);
}
@ -83,6 +84,19 @@ impl Book {
return Ok(());
}
// Desugars all definitions
pub fn expand_implicits(&mut self) {
// Creates a map with the implicit count of each top-level definition
let mut implicit_count = BTreeMap::new();
for (name, term) in self.defs.iter() {
implicit_count.insert(name.clone(), term.count_implicits());
}
// Expands implicit calls of each top-level definition in the book
for def in self.defs.iter_mut() {
def.1.expand_implicits(im::Vector::new(), &implicit_count);
}
}
// Gets a file id from its name
pub fn get_file_id(&mut self, name: &str) -> u64 {
if let Some(id) = self.fids.get(name) {

View File

@ -49,12 +49,12 @@ impl<'i> KindParser<'i> {
let mut typ = Term::Set;
let mut val = Term::new_adt(&adt);
for (idx_nam, idx_typ) in adt.idxs.iter().rev() {
typ = Term::All { nam: idx_nam.clone(), inp: Box::new(idx_typ.clone()), bod: Box::new(typ) };
val = Term::Lam { nam: idx_nam.clone(), bod: Box::new(val) };
typ = Term::All { era: false, nam: idx_nam.clone(), inp: Box::new(idx_typ.clone()), bod: Box::new(typ) };
val = Term::Lam { era: false, nam: idx_nam.clone(), bod: Box::new(val) };
}
for par_nam in adt.pars.iter().rev() {
typ = Term::All { nam: par_nam.clone(), inp: Box::new(Term::Set), bod: Box::new(typ) };
val = Term::Lam { nam: par_nam.clone(), bod: Box::new(val) };
typ = Term::All { era: false, nam: par_nam.clone(), inp: Box::new(Term::Set), bod: Box::new(typ) };
val = Term::Lam { era: false, nam: par_nam.clone(), bod: Box::new(val) };
}
//println!("NAM: {}", adt.name);
//println!("TYP: {}", typ.show());
@ -68,22 +68,23 @@ impl<'i> KindParser<'i> {
let nam = uses.get(&nam).unwrap_or(&nam).to_string();
self.skip_trivia();
// Untyped Arguments (optional)
// Arguments (optional)
let mut args = im::Vector::new();
while self.peek_one().map_or(false, |c| c.is_ascii_alphabetic()) {
let par = self.parse_name()?;
self.skip_trivia();
args.push_back((par, Term::Met{}));
}
// Typed Arguments (optional)
while self.peek_one() == Some('(') {
self.consume("(")?;
let mut uses = uses.clone();
while self.peek_one() == Some('<') || self.peek_one() == Some('(') {
let implicit = self.peek_one() == Some('<');
self.consume(if implicit { "<" } else { "(" })?;
let arg_name = self.parse_name()?;
self.consume(":")?;
let arg_type = self.parse_term(fid, uses)?;
self.consume(")")?;
args.push_back((arg_name, arg_type));
self.skip_trivia();
let arg_type = if self.peek_one() == Some(':') {
self.consume(":")?;
self.parse_term(fid, &uses)?
} else {
Term::Met {}
};
self.consume(if implicit { ">" } else { ")" })?;
uses = shadow(&arg_name, &uses);
args.push_back((implicit, arg_name, arg_type));
self.skip_trivia();
}
@ -92,7 +93,7 @@ impl<'i> KindParser<'i> {
let ann;
if self.peek_one() == Some(':') {
self.consume(":")?;
typ = self.parse_term(fid, uses)?;
typ = self.parse_term(fid, &uses)?;
ann = true;
} else {
typ = Term::Met {};
@ -101,7 +102,9 @@ impl<'i> KindParser<'i> {
// Value (mandatory)
self.consume("=")?;
let mut val = self.parse_term(fid, uses)?;
let mut val = self.parse_term(fid, &uses)?;
//println!("PARSING {}", nam);
// Adds lambdas/foralls for each argument
typ.add_alls(args.clone());
@ -140,15 +143,16 @@ impl<'i> KindParser<'i> {
impl Term {
// Wraps a Lam around this term.
fn add_lam(&mut self, arg: &str) {
fn add_lam(&mut self, era: bool, arg: &str) {
*self = Term::Lam {
era: era,
nam: arg.to_string(),
bod: Box::new(std::mem::replace(self, Term::Met {})),
};
}
// Wraps many lams around this term. Linearizes when possible.
fn add_lams(&mut self, args: im::Vector<(String,Term)>) {
fn add_lams(&mut self, args: im::Vector<(bool,String,Term)>) {
// Passes through Src
if let Term::Src { val, .. } = self {
val.add_lams(args);
@ -161,35 +165,44 @@ impl Term {
}
// Linearizes a numeric pattern match
if let Term::Swi { nam, z, s, .. } = self {
if args.len() >= 1 && args[0].0 == *nam {
if args.len() >= 1 && args[0].1 == *nam {
let (head, tail) = args.split_at(1);
z.add_lams(tail.clone());
s.add_lams(tail.clone());
self.add_lam(&head[0].0);
self.add_lam(head[0].0, &head[0].1);
return;
}
}
// Linearizes a user-defined ADT match
if let Term::Mch { mch } = self {
let Match { name, cses, .. } = &mut **mch;
if args.len() >= 1 && args[0].0 == *name {
let (head, tail) = args.split_at(1);
for (_, cse) in cses {
cse.add_lams(tail.clone());
if !mch.fold {
let Match { name, cses, .. } = &mut **mch;
if args.len() >= 1 && args[0].1 == *name {
let (head, tail) = args.split_at(1);
for (_, cse) in cses {
cse.add_lams(tail.clone());
}
self.add_lam(head[0].0, &head[0].1);
return;
}
self.add_lam(&head[0].0);
return;
}
}
// Prepend remaining lambdas
for (nam, _) in args.iter().rev() {
self.add_lam(&nam);
if args.len() > 0 {
let (head, tail) = args.split_at(1);
self.add_lam(head[0].0, &head[0].1);
if let Term::Lam { era: _, nam: _, bod } = self {
bod.add_lams(tail.clone());
} else {
unreachable!();
}
}
}
// Wraps an All around this term.
fn add_all(&mut self, arg: &str, typ: &Term) {
fn add_all(&mut self, era: bool, arg: &str, typ: &Term) {
*self = Term::All {
era: era,
nam: arg.to_string(),
inp: Box::new(typ.clone()),
bod: Box::new(std::mem::replace(self, Term::Met {})),
@ -197,9 +210,9 @@ impl Term {
}
// Wraps many Lams around this term.
fn add_alls(&mut self, args: im::Vector<(String,Term)>) {
for (nam, typ) in args.iter().rev() {
self.add_all(&nam, typ);
fn add_alls(&mut self, args: im::Vector<(bool,String,Term)>) {
for (era, nam, typ) in args.iter().rev() {
self.add_all(*era, &nam, typ);
}
}

View File

@ -1,3 +1,20 @@
// PROJECT FILES:
//./term/mod.rs//
//./term/show.rs//
//./term/parse.rs//
//./term/compile.rs//
//./sugar/mod.rs//
//./show/mod.rs//
//./info/mod.rs//
//./info/parse.rs//
//./info/show.rs//
//./book/mod.rs//
//./book/compile.rs//
//./book/parse.rs//
//./book/show.rs//
// PROJECT CLI (main.rs):
use clap::{Arg, ArgAction, Command};
use std::fs;
use std::io::Write;
@ -49,7 +66,7 @@ fn generate_check_hs(book: &Book, command: &str, arg: &str) -> String {
fn generate_hvm2(book: &Book, arg: &str) -> String {
let book_hvm2 = book.to_hvm2();
let main_hvm2 = format!("main = {}\n", arg);
let main_hvm2 = format!("main = {}\n", Term::to_hvm2_name(arg));
format!("{}\n{}", book_hvm2, main_hvm2)
}
@ -102,13 +119,15 @@ fn normal(name: &str, _level: u32, runtime: &str) {
eprintln!("{stderr}");
}
fn format_def(name: &str) {
let book = load_book(name);
let defn = book.defs.get(name).expect("definition exists");
let form = book.format_def(name, defn).flatten(Some(40));
let path = PathBuf::from(format!("./{name}.kind2"));
fs::write(&path, form).expect(&format!("failed to write to '{:?}'", path));
fn auto_format(file_name: &str) {
let base = std::env::current_dir().expect("failed to get current directory");
let file = base.join(format!("{file_name}.kind2"));
let text = std::fs::read_to_string(&file).expect("failed to read file");
let fid = Book::new().get_file_id(&file.to_str().unwrap().to_string());
let book = KindParser::new(&text).parse_book(file_name, fid).expect("failed to parse book");
let form = book.defs.iter().map(|(name, term)| book.format_def(name, term)).collect();
let form = Show::pile("\n\n", form).flatten(Some(60));
std::fs::write(&file, form).expect(&format!("failed to write to file '{}'", file_name));
}
fn compile(name: &str) {
@ -185,7 +204,7 @@ fn main() {
.value_parser(["hs", "hvm2"])
.default_value("hs")))
.subcommand(Command::new("format")
.about("Pretty-formats a definition")
.about("Auto-formats a file")
.arg(Arg::new("name").required(true)))
.subcommand(Command::new("compile")
.about("Compiles to HVM2")
@ -207,7 +226,7 @@ fn main() {
}
Some(("format", sub_matches)) => {
let name = sub_matches.get_one::<String>("name").expect("required");
format_def(name);
auto_format(name);
}
Some(("compile", sub_matches)) => {
let name = sub_matches.get_one::<String>("name").expect("required");

View File

@ -34,6 +34,7 @@ pub struct Constructor {
#[derive(Debug, Clone)]
pub struct Match {
pub adt: String, // datatype
pub fold: bool, // is this a fold?
pub name: String, // scrutinee name
pub expr: Term, // structinee expression
pub with: Vec<(String,Term)>, // terms to move in
@ -199,7 +200,7 @@ impl Term {
let mut term = self;
loop {
match term {
Term::App { fun, arg } => {
Term::App { era: _, fun, arg } => {
if let Term::Var { nam } = &**fun {
if nam == "Nat.succ" {
nat += 1;
@ -241,9 +242,9 @@ impl Term {
let mut term = self;
loop {
match term {
Term::App { fun, arg } => {
if let Term::App { fun: cons, arg: head } = &**fun {
if let Term::App { fun: cons_fun, arg: _ } = &**cons {
Term::App { era: _, fun, arg } => {
if let Term::App { era: _, fun: cons, arg: head } = &**fun {
if let Term::App { era: _, fun: cons_fun, arg: _ } = &**cons {
if let Term::Var { nam } = &**cons_fun {
if nam == "List.cons" {
vals.push(head.clone());
@ -268,13 +269,17 @@ impl Term {
// Builds a chain of applications of List.cons and List.nil from a Vec<Box<Term>>
pub fn new_list(list: &List) -> Term {
let mut term = Term::App {
era: true,
fun: Box::new(Term::Var { nam: "List.nil".to_string() }),
arg: Box::new(Term::Met {}),
};
for item in (&list.vals).into_iter().rev() {
term = Term::App {
era: false,
fun: Box::new(Term::App {
era: false,
fun: Box::new(Term::App {
era: true,
fun: Box::new(Term::Var { nam: "List.cons".to_string() }),
arg: Box::new(Term::Met {}),
}),
@ -333,9 +338,9 @@ impl Term {
// - (EQUAL a b) ::= (App (App (App (Var "Equal") _) a) b)
pub fn as_equal(&self) -> Option<Equal> {
match self {
Term::App { fun, arg: rhs } => {
if let Term::App { fun: eq_fun, arg: lhs } = &**fun {
if let Term::App { fun: eq_fun, arg: _ } = &**eq_fun {
Term::App { era: _, fun, arg: rhs } => {
if let Term::App { era: _, fun: eq_fun, arg: lhs } = &**fun {
if let Term::App { era: _, fun: eq_fun, arg: _ } = &**eq_fun {
if let Term::Var { nam } = &**eq_fun {
if nam == "Equal" {
return Some(Equal { a: (**lhs).clone(), b: (**rhs).clone() });
@ -352,8 +357,11 @@ impl Term {
// Builds an equal chain
pub fn new_equal(eq: &Equal) -> Term {
Term::App {
era: false,
fun: Box::new(Term::App {
era: false,
fun: Box::new(Term::App {
era: true,
fun: Box::new(Term::Var { nam: "Equal".to_string() }),
arg: Box::new(Term::Met {}),
}),
@ -419,7 +427,7 @@ impl Term {
}
// Reads the motive: `∀(P: ∀(I0: I0.TY) ∀(I1: I1.TY) ... ∀(x: (TY P0 P1 ... I0 I1 ...)) *).`
if let Term::All { nam, inp, bod } = term {
if let Term::All { era: _, nam, inp, bod } = term {
// Gets the motive name
pvar = nam.clone();
@ -427,7 +435,7 @@ impl Term {
let mut moti = &**inp;
// Reads each motive index
while let Term::All { nam, inp: idx_inp, bod: idx_bod } = moti {
while let Term::All { era: _, nam, inp: idx_inp, bod: idx_bod } = moti {
if let Term::All { .. } = &**idx_bod {
idxs.push((nam.clone(), *idx_inp.clone()));
moti = idx_bod;
@ -437,14 +445,14 @@ impl Term {
}
// Skips the witness
if let Term::All { nam: _, inp: wit_inp, bod: wit_bod } = moti {
if let Term::All { era: _, nam: _, inp: wit_inp, bod: wit_bod } = moti {
// Here, the witness has form '(TY P0 P1 ... I0 I1 ...)'
let mut wit_inp = wit_inp;
// Skips the wit's indices (outermost Apps)
for _ in 0 .. idxs.len() {
if let Term::App { fun: wit_inp_fun, arg: _ } = &**wit_inp {
if let Term::App { era: _, fun: wit_inp_fun, arg: _ } = &**wit_inp {
wit_inp = wit_inp_fun;
} else {
return None;
@ -452,7 +460,7 @@ impl Term {
}
// Collects the wit's parameters (remaining Apps)
while let Term::App { fun: wit_inp_fun, arg: wit_inp_arg } = &**wit_inp {
while let Term::App { era: _, fun: wit_inp_fun, arg: wit_inp_arg } = &**wit_inp {
if let Term::Var { nam: parameter } = &**wit_inp_arg {
pars.push(parameter.to_string());
wit_inp = wit_inp_fun;
@ -485,12 +493,12 @@ impl Term {
}
// Reads each constructor: `∀(C0: ∀(C0_F0: C0_F0.TY) ∀(C0_F1: C0_F1.TY) ... (P I0 I1 ... (TY.C0 P0 P1 ... C0_F0 C0_F1 ...)))`
while let Term::All { nam, inp, bod } = term {
while let Term::All { era: _, nam, inp, bod } = term {
let mut flds: Vec<(String,Term)> = Vec::new();
let mut ctyp: &Term = &**inp;
// Reads each field
while let Term::All { nam, inp, bod } = ctyp {
while let Term::All { era: _, nam, inp, bod } = ctyp {
flds.push((nam.clone(), *inp.clone()));
ctyp = bod;
}
@ -498,7 +506,7 @@ impl Term {
// Now, the ctyp will be in the form (P I0 I1 ... (Ctr P0 P1 ... F0 F1 ...))
// Skips the outermost application
if let Term::App { fun: ctyp_fun, arg: _ } = ctyp {
if let Term::App { era: _, fun: ctyp_fun, arg: _ } = ctyp {
ctyp = ctyp_fun;
} else {
return None;
@ -506,7 +514,7 @@ impl Term {
// Collects constructor indices until we reach the pattern head P
let mut ctr_idxs: Vec<Term> = Vec::new();
while let Term::App { fun: fun_app, arg: arg_app } = ctyp {
while let Term::App { era: _, fun: fun_app, arg: arg_app } = ctyp {
ctr_idxs.push(*arg_app.clone());
ctyp = fun_app;
}
@ -538,18 +546,19 @@ impl Term {
// Then appends each type parameter
for par in adt.pars.iter() {
self_type = Term::App { fun: Box::new(self_type), arg: Box::new(Term::Var { nam: par.clone() }) };
self_type = Term::App { era: false, fun: Box::new(self_type), arg: Box::new(Term::Var { nam: par.clone() }) };
}
// And finally appends each index
for (idx_name, _) in adt.idxs.iter() {
self_type = Term::App { fun: Box::new(self_type), arg: Box::new(Term::Var { nam: idx_name.clone() }) };
self_type = Term::App { era: false, fun: Box::new(self_type), arg: Box::new(Term::Var { nam: idx_name.clone() }) };
}
// 2. Builds the motive type: ∀(I0: I0.TY) ∀(I1: I1.TY) ... ∀(x: (Type P0 P1 ... I0 I1 ...)) *
// Starts with the witness type: ∀(x: (Type P0 P1 ... I0 I1 ...)) *
let mut motive_type = Term::All {
era: false,
nam: "x".to_string(),
inp: Box::new(self_type.clone()),
bod: Box::new(Term::Set),
@ -558,6 +567,7 @@ impl Term {
// Then prepends each index type
for (idx_name, idx_type) in adt.idxs.iter().rev() {
motive_type = Term::All {
era: false,
nam: idx_name.clone(),
inp: Box::new(idx_type.clone()),
bod: Box::new(motive_type),
@ -570,6 +580,7 @@ impl Term {
// Applies the motive to each index
for (idx_name, _) in adt.idxs.iter() {
term = Term::App {
era: false,
fun: Box::new(term),
arg: Box::new(Term::Var { nam: idx_name.clone() }),
};
@ -577,6 +588,7 @@ impl Term {
// And applies it to the witness (self)
term = Term::App {
era: false,
fun: Box::new(term),
arg: Box::new(Term::Var { nam: "self".to_string() }),
};
@ -589,6 +601,7 @@ impl Term {
// Applies the motive to each constructor index
for idx in ctr.idxs.iter().rev() {
ctr_term = Term::App {
era: false,
fun: Box::new(ctr_term),
arg: Box::new(idx.clone()),
};
@ -598,16 +611,19 @@ impl Term {
let mut ctr_type = Term::Var { nam: format!("{}.{}", adt.name, ctr.name) };
// For each type parameter
for par in adt.pars.iter() {
ctr_type = Term::App {
fun: Box::new(ctr_type),
arg: Box::new(Term::Var { nam: par.clone() }),
};
}
// NOTE: Not necessary anymore due to auto implicit arguments
//for par in adt.pars.iter() {
//ctr_type = Term::App {
//era: true,
//fun: Box::new(ctr_type),
//arg: Box::new(Term::Var { nam: par.clone() }),
//};
//}
// And for each field
for (fld_name, _fld_type) in ctr.flds.iter() {
ctr_type = Term::App {
era: false,
fun: Box::new(ctr_type),
arg: Box::new(Term::Var { nam: fld_name.clone() }),
};
@ -615,6 +631,7 @@ impl Term {
// Wraps the constructor type with the application
ctr_term = Term::App {
era: false,
fun: Box::new(ctr_term),
arg: Box::new(ctr_type),
};
@ -622,6 +639,7 @@ impl Term {
// Finally, quantifies each field
for (fld_name, fld_type) in ctr.flds.iter().rev() {
ctr_term = Term::All {
era: false,
nam: fld_name.clone(),
inp: Box::new(fld_type.clone()),
bod: Box::new(ctr_term),
@ -630,6 +648,7 @@ impl Term {
// And quantifies the constructor
term = Term::All {
era: false,
nam: ctr.name.clone(),
inp: Box::new(ctr_term),
bod: Box::new(term),
@ -638,6 +657,7 @@ impl Term {
// 5 Quantifies the motive
term = Term::All {
era: false,
nam: "P".to_string(),
inp: Box::new(motive_type),
bod: Box::new(term),
@ -680,11 +700,8 @@ impl ADT {
Err(format!("Cannot find definition for type '{}'.", name))
}
}
}
impl ADT {
// Formats an ADT
pub fn format(&self) -> Box<Show> {
// ADT head: `data Name <params> <indices>`
@ -856,12 +873,14 @@ impl Term {
if let Some(moti) = &mat.moti {
// Creates the first lambda: 'λx <motive>'
motive = Term::Lam {
era: false,
nam: mat.name.clone(),
bod: Box::new(moti.clone()),
};
// Creates a lambda for each index: 'λindices ... λx <motive>'
for (idx_name, _) in adt.idxs.iter().rev() {
motive = Term::Lam {
era: false,
nam: idx_name.clone(),
bod: Box::new(motive),
};
@ -869,6 +888,7 @@ impl Term {
// Creates a forall for each moved value: 'λindices ... λx ∀(a: A) ... <motive>'
for (nam, typ) in mat.with.iter().rev() {
motive = Term::All {
era: false,
nam: nam.clone(),
inp: Box::new(typ.clone()),
bod: Box::new(motive),
@ -892,6 +912,7 @@ impl Term {
// Adds moved value lambdas
for (nam, _) in mat.with.iter().rev() {
ctr_term = Term::Lam {
era: false,
nam: nam.clone(),
bod: Box::new(ctr_term),
};
@ -899,6 +920,7 @@ impl Term {
// Adds field lambdas
for (fld_name, _) in ctr.flds.iter().rev() {
ctr_term = Term::Lam {
era: false,
nam: format!("{}.{}", mat.name, fld_name.clone()),
bod: Box::new(ctr_term),
};
@ -911,13 +933,26 @@ impl Term {
}
}
// 3. Wrap Ins around term
term = Term::Ins {
val: Box::new(mat.expr.clone())
};
// 3. Wrap Ins or Type.fold around term
if mat.fold {
term = Term::App {
era: false,
fun: Box::new(Term::App {
era: true,
fun: Box::new(Term::Var { nam: format!("{}.fold", adt.name) }),
arg: Box::new(Term::Met {}),
}),
arg: Box::new(mat.expr.clone()),
};
} else {
term = Term::Ins {
val: Box::new(mat.expr.clone())
};
}
// 4. Apply the motive, making a Var for it
term = Term::App {
era: true,
fun: Box::new(term),
arg: Box::new(Term::Var { nam: format!("{}.P", mat.name) }),
};
@ -925,6 +960,7 @@ impl Term {
// 5. Apply each constructor (by name)
for ctr in &adt.ctrs {
term = Term::App {
era: false,
fun: Box::new(term),
arg: Box::new(Term::Var { nam: format!("{}.{}", mat.name, ctr.name) }),
};
@ -935,6 +971,7 @@ impl Term {
let mut ann_type = Term::Met {};
for (nam, typ) in mat.with.iter().rev() {
ann_type = Term::All {
era: false,
nam: nam.clone(),
inp: Box::new(typ.clone()),
bod: Box::new(ann_type),
@ -950,6 +987,7 @@ impl Term {
// 7. Applies each moved var
for (nam, _) in mat.with.iter() {
term = Term::App {
era: false,
fun: Box::new(term),
arg: Box::new(Term::Var { nam: nam.clone() }),
};
@ -971,8 +1009,6 @@ impl Term {
bod: Box::new(term)
};
//println!("PARSED:\n{}", term.show());
// 10. Return 'term'
return term;
}
@ -984,7 +1020,7 @@ impl Match {
pub fn format(&self) -> Box<Show> {
Show::pile(" ", vec![
Show::glue(" ", vec![
Show::text("match"),
Show::text(if self.fold { "fold" } else { "match" }),
Show::text(&self.name),
Show::text("="),
self.expr.format_go(),
@ -1015,9 +1051,9 @@ impl Match {
impl<'i> KindParser<'i> {
// MAT ::= match <name> = <term> { <name> : <term> <...> }: <term>
pub fn parse_match(&mut self, fid: u64, uses: &Uses) -> Result<Match, String> {
pub fn parse_match(&mut self, fid: u64, uses: &Uses, fold: bool) -> Result<Match, String> {
// Parses the header: 'match <name> = <expr>'
self.consume("match ")?;
self.consume(if fold { "fold " } else { "match " })?;
let name = self.parse_name()?;
self.skip_trivia();
let expr = if self.peek_one() == Some('=') {
@ -1090,7 +1126,7 @@ impl<'i> KindParser<'i> {
} else {
None
};
return Ok(Match { adt, name, expr, with, cses, moti });
return Ok(Match { adt, fold, name, expr, with, cses, moti });
}
}

View File

@ -69,16 +69,16 @@ impl Term {
format!("x{}", name.replace("-", "._."))
}
match self {
Term::All { nam, inp, bod } => {
Term::All { era: _, nam, inp, bod } => {
let inp = inp.to_hvm2_checker(env.clone(), met);
let bod = bod.to_hvm2_checker(cons(&env, nam.clone()), met);
format!("(All \"{}\" {} λ{} {})", nam, inp, binder(nam), bod)
}
Term::Lam { nam, bod } => {
Term::Lam { era: _, nam, bod } => {
let bod = bod.to_hvm2_checker(cons(&env, nam.clone()), met);
format!("(Lam \"{}\" λ{} {})", nam, binder(nam), bod)
}
Term::App { fun, arg } => {
Term::App { era: _, fun, arg } => {
let fun = fun.to_hvm2_checker(env.clone(), met);
let arg = arg.to_hvm2_checker(env.clone(), met);
format!("(App {} {})", fun, arg)
@ -162,16 +162,16 @@ impl Term {
pub fn to_hs_checker(&self, env: im::Vector<String>, met: &mut usize) -> String {
match self {
Term::All { nam, inp, bod } => {
Term::All { era: _, nam, inp, bod } => {
let inp = inp.to_hs_checker(env.clone(), met);
let bod = bod.to_hs_checker(cons(&env, nam.clone()), met);
format!("(All \"{}\" {} $ \\{} -> {})", nam, inp, Term::to_hs_name(nam), bod)
},
Term::Lam { nam, bod } => {
Term::Lam { era: _, nam, bod } => {
let bod = bod.to_hs_checker(cons(&env, nam.clone()), met);
format!("(Lam \"{}\" $ \\{} -> {})", nam, Term::to_hs_name(nam), bod)
},
Term::App { fun, arg } => {
Term::App { era: _, fun, arg } => {
let fun = fun.to_hs_checker(env.clone(), met);
let arg = arg.to_hs_checker(env.clone(), met);
format!("(App {} {})", fun, arg)
@ -252,17 +252,26 @@ impl Term {
pub fn to_hvm2(&self) -> String {
match self {
Term::All { nam: _, inp: _, bod: _ } => {
Term::All { era: _, nam: _, inp: _, bod: _ } => {
format!("0")
},
Term::Lam { nam, bod } => {
Term::Lam { era, nam, bod } => {
let bod = bod.to_hvm2();
format!("λ{} {}", nam, bod)
if *era {
format!("{}", bod)
} else {
format!("λ{} {}", Term::to_hvm2_name(nam), bod)
}
},
Term::App { fun, arg } => {
let fun = fun.to_hvm2();
let arg = arg.to_hvm2();
format!("(App {} {})", fun, arg)
Term::App { era, fun, arg } => {
if *era {
let fun = fun.to_hvm2();
format!("{}", fun)
} else {
let fun = fun.to_hvm2();
let arg = arg.to_hvm2();
format!("({} {})", fun, arg)
}
},
Term::Ann { chk: _, val, typ: _ } => {
val.to_hvm2()
@ -291,18 +300,18 @@ impl Term {
let x = x.to_hvm2();
let z = z.to_hvm2();
let s = s.to_hvm2();
format!("switch {} = {} {{ 0: {} _: {} }}", nam, x, z, s)
format!("match {} = {} {{ 0: {} +: {} }}", Term::to_hvm2_name(nam), x, z, s)
},
Term::Let { nam, val, bod } => {
let val = val.to_hvm2();
let bod = bod.to_hvm2();
format!("let {} = {} {}", nam, val, bod)
format!("let {} = {} {}", Term::to_hvm2_name(nam), val, bod)
},
// FIXME: change to "use" once hvm-lang supports it
Term::Use { nam, val, bod } => {
let val = val.to_hvm2();
let bod = bod.to_hvm2();
format!("use {} = {} {}", nam, val, bod)
format!("let {} = {} {}", Term::to_hvm2_name(nam), val, bod)
},
Term::Hol { nam: _ } => {
format!("0")
@ -312,7 +321,7 @@ impl Term {
format!("0")
},
Term::Var { nam } => {
format!("{}", nam)
format!("{}", Term::to_hvm2_name(nam))
},
Term::Src { src: _, val } => {
val.to_hvm2()
@ -329,4 +338,9 @@ impl Term {
}
}
pub fn to_hvm2_name(name: &str) -> String {
format!("_{}", name)
}
}

View File

@ -1,5 +1,10 @@
//./../sugar/mod.rs//
//./parse.rs//
//./../book/parse.rs//
use crate::{*};
use std::collections::BTreeSet;
use std::collections::BTreeMap;
pub mod compile;
pub mod parse;
@ -40,9 +45,9 @@ pub struct Src {
// VAR | <name>
#[derive(Clone, Debug)]
pub enum Term {
All { nam: String, inp: Box<Term>, bod: Box<Term> },
Lam { nam: String, bod: Box<Term> },
App { fun: Box<Term>, arg: Box<Term> },
All { era: bool, nam: String, inp: Box<Term>, bod: Box<Term> },
Lam { era: bool, nam: String, bod: Box<Term> },
App { era: bool, fun: Box<Term>, arg: Box<Term> },
Ann { chk: bool, val: Box<Term>, typ: Box<Term> },
Slf { nam: String, typ: Box<Term>, bod: Box<Term> },
Ins { val: Box<Term> },
@ -102,14 +107,14 @@ impl Term {
pub fn get_free_vars(&self, env: im::Vector<String>, free_vars: &mut BTreeSet<String>) {
match self {
Term::All { nam, inp, bod } => {
Term::All { era: _, nam, inp, bod } => {
inp.get_free_vars(env.clone(), free_vars);
bod.get_free_vars(cons(&env, nam.clone()), free_vars);
},
Term::Lam { nam, bod } => {
Term::Lam { era: _, nam, bod } => {
bod.get_free_vars(cons(&env, nam.clone()), free_vars);
},
Term::App { fun, arg } => {
Term::App { era: _, fun, arg } => {
fun.get_free_vars(env.clone(), free_vars);
arg.get_free_vars(env.clone(), free_vars);
},
@ -163,88 +168,27 @@ impl Term {
}
}
// Recurses through the term, desugaring Mch constructors
pub fn desugar(&mut self) {
match self {
// Desugars the Mch constructor using sugar::new_match
Term::Mch { mch } => {
*self = Term::new_match(&mch);
self.desugar();
},
// Recurses on subterms for all other constructors
Term::All { nam: _, inp, bod } => {
inp.desugar();
bod.desugar();
},
Term::Lam { nam: _, bod } => {
bod.desugar();
},
Term::App { fun, arg } => {
fun.desugar();
arg.desugar();
},
Term::Ann { chk: _, val, typ } => {
val.desugar();
typ.desugar();
},
Term::Slf { nam: _, typ, bod } => {
typ.desugar();
bod.desugar();
},
Term::Ins { val } => {
val.desugar();
},
Term::Op2 { opr: _, fst, snd } => {
fst.desugar();
snd.desugar();
},
Term::Swi { nam: _, x, z, s, p } => {
x.desugar();
z.desugar();
s.desugar();
p.desugar();
},
Term::Let { nam: _, val, bod } => {
val.desugar();
bod.desugar();
},
Term::Use { nam: _, val, bod } => {
val.desugar();
bod.desugar();
},
Term::Src { src: _, val } => {
val.desugar();
},
// Base cases, do nothing
Term::Set => {},
Term::U60 => {},
Term::Num { val: _ } => {},
Term::Nat { nat: _ } => {},
Term::Txt { txt: _ } => {},
Term::Var { nam: _ } => {},
Term::Hol { nam: _ } => {},
Term::Met {} => {},
}
}
// Removes Src's
pub fn clean(&self) -> Term {
match self {
Term::All { nam, inp, bod } => {
Term::All { era, nam, inp, bod } => {
Term::All {
era: *era,
nam: nam.clone(),
inp: Box::new(inp.clean()),
bod: Box::new(bod.clean()),
}
},
Term::Lam { nam, bod } => {
Term::Lam { era, nam, bod } => {
Term::Lam {
era: *era,
nam: nam.clone(),
bod: Box::new(bod.clean()),
}
},
Term::App { fun, arg } => {
Term::App { era, fun, arg } => {
Term::App {
era: *era,
fun: Box::new(fun.clean()),
arg: Box::new(arg.clean()),
}
@ -330,5 +274,158 @@ impl Term {
},
}
}
}
// Expands syntax sugars like Mch to proper λ-encodings.
pub fn desugar(&mut self) {
match self {
// Desugars the Mch constructor using sugar::new_match
Term::Mch { mch } => {
*self = Term::new_match(&mch);
self.desugar();
},
// Recurses on subterms for all other constructors
Term::All { era: _, nam: _, inp, bod } => {
inp.desugar();
bod.desugar();
},
Term::Lam { era: _, nam: _, bod } => {
bod.desugar();
},
Term::App { era: _, fun, arg } => {
fun.desugar();
arg.desugar();
},
Term::Ann { chk: _, val, typ } => {
val.desugar();
typ.desugar();
},
Term::Slf { nam: _, typ, bod } => {
typ.desugar();
bod.desugar();
},
Term::Ins { val } => {
val.desugar();
},
Term::Op2 { opr: _, fst, snd } => {
fst.desugar();
snd.desugar();
},
Term::Swi { nam: _, x, z, s, p } => {
x.desugar();
z.desugar();
s.desugar();
p.desugar();
},
Term::Let { nam: _, val, bod } => {
val.desugar();
bod.desugar();
},
Term::Use { nam: _, val, bod } => {
val.desugar();
bod.desugar();
},
Term::Src { src: _, val } => {
val.desugar();
},
Term::Set => {},
Term::U60 => {},
Term::Num { .. } => {},
Term::Nat { .. } => {},
Term::Txt { .. } => {},
Term::Var { .. } => {},
Term::Hol { .. } => {},
Term::Met {} => {},
}
}
// Expands implicit calls, applying them to the right number of metavars.
pub fn expand_implicits(&mut self, env: im::Vector<String>, implicit_count: &BTreeMap<String, u64>) {
match self {
Term::All { era: _, nam, inp, bod } => {
inp.expand_implicits(env.clone(), implicit_count);
bod.expand_implicits(cons(&env, nam.clone()), implicit_count);
},
Term::Lam { era: _, nam, bod } => {
bod.expand_implicits(cons(&env, nam.clone()), implicit_count);
},
Term::App { era: _, fun, arg } => {
fun.expand_implicits(env.clone(), implicit_count);
arg.expand_implicits(env.clone(), implicit_count);
},
Term::Ann { chk: _, val, typ } => {
val.expand_implicits(env.clone(), implicit_count);
typ.expand_implicits(env.clone(), implicit_count);
},
Term::Slf { nam, typ, bod } => {
typ.expand_implicits(env.clone(), implicit_count);
bod.expand_implicits(cons(&env, nam.clone()), implicit_count);
},
Term::Ins { val } => {
val.expand_implicits(env.clone(), implicit_count);
},
Term::Set => {},
Term::U60 => {},
Term::Num { val: _ } => {},
Term::Op2 { opr: _, fst, snd } => {
fst.expand_implicits(env.clone(), implicit_count);
snd.expand_implicits(env.clone(), implicit_count);
},
Term::Swi { nam, x, z, s, p } => {
x.expand_implicits(env.clone(), implicit_count);
z.expand_implicits(env.clone(), implicit_count);
s.expand_implicits(cons(&env, format!("{}-1",nam)), implicit_count);
p.expand_implicits(cons(&env, nam.clone()), implicit_count);
},
Term::Nat { nat: _ } => {},
Term::Txt { txt: _ } => {},
Term::Let { nam, val, bod } => {
val.expand_implicits(env.clone(), implicit_count);
bod.expand_implicits(cons(&env, nam.clone()), implicit_count);
},
Term::Use { nam, val, bod } => {
val.expand_implicits(env.clone(), implicit_count);
bod.expand_implicits(cons(&env, nam.clone()), implicit_count);
},
Term::Hol { nam: _ } => {},
Term::Met {} => {},
Term::Src { src: _, val } => {
val.expand_implicits(env, implicit_count);
},
Term::Var { nam } => {
if !env.contains(nam) {
if let Some(implicits) = implicit_count.get(nam) {
for _ in 0..*implicits {
*self = Term::App {
era: true,
fun: Box::new(std::mem::replace(self, Term::Met {})),
arg: Box::new(Term::Met {}),
};
}
}
}
},
Term::Mch { .. } => {
unreachable!()
},
}
}
// Counts the number of implicit arguments of a term.
pub fn count_implicits(&self) -> u64 {
match self {
Term::All { era: true, nam: _, inp: _, bod } => {
return 1 + bod.count_implicits();
}
Term::Src { src: _, val } => {
return val.count_implicits();
}
Term::Ann { chk: _, val: _, typ } => {
return typ.count_implicits();
}
_ => {
return 0;
}
}
}
}

View File

@ -72,6 +72,12 @@ impl<'i> KindParser<'i> {
}
pub fn parse_term(&mut self, fid: u64, uses: &Uses) -> Result<Term, String> {
let init = self.parse_term_ini(fid, uses)?;
let term = self.parse_term_end(fid, uses, init)?;
return Ok(term);
}
pub fn parse_term_ini(&mut self, fid: u64, uses: &Uses) -> Result<Term, String> {
self.skip_trivia();
//println!("parsing ||{}", self.input[self.index..].replace("\n",""));
@ -87,7 +93,7 @@ impl<'i> KindParser<'i> {
let bod = Box::new(self.parse_term(fid, &shadow(&nam, uses))?);
let end = *self.index() as u64;
let src = Src::new(fid, ini, end);
return Ok(Term::Src { src, val: Box::new(Term::All { nam, inp, bod }) });
return Ok(Term::Src { src, val: Box::new(Term::All { era: false, nam, inp, bod }) });
}
// LAM ::= λ<name> <term>
@ -104,20 +110,18 @@ impl<'i> KindParser<'i> {
let bod = Box::new(self.parse_term(fid, &shadow(&nam, uses))?);
let end = *self.index() as u64;
let src = Src::new(fid, ini, end);
let typ = Box::new(Term::All { nam: nam.clone(), inp: typ, bod: Box::new(Term::Met {}) });
let val = Box::new(Term::Lam { nam: nam.clone(), bod });
let typ = Box::new(Term::All { era: false, nam: nam.clone(), inp: typ, bod: Box::new(Term::Met {}) });
let val = Box::new(Term::Lam { era: false, nam: nam.clone(), bod });
let val = Box::new(Term::Ann { chk: true, typ, val });
return Ok(Term::Src { src, val });
}
//λ(x: A) B
//-----------------
//{λx B: ∀(x: A) _}
// Untyped
let nam = self.parse_name()?;
let era = false;
let bod = Box::new(self.parse_term(fid, &shadow(&nam, uses))?);
let end = *self.index() as u64;
let src = Src::new(fid, ini, end);
return Ok(Term::Src { src, val: Box::new(Term::Lam { nam, bod }) });
return Ok(Term::Src { src, val: Box::new(Term::Lam { era, nam, bod }) });
}
// RFL ::= (=)
@ -129,7 +133,9 @@ impl<'i> KindParser<'i> {
return Ok(Term::Src {
src,
val: Box::new(Term::App {
era: false,
fun: Box::new(Term::App {
era: false,
fun: Box::new(Term::Var { nam: "Equal.refl".to_string() }),
arg: Box::new(Term::Met {}),
}),
@ -167,7 +173,7 @@ impl<'i> KindParser<'i> {
let src = Src::new(fid, ini, end);
let mut app = fun;
for arg in args {
app = Box::new(Term::App { fun: app, arg });
app = Box::new(Term::App { era: false, fun: app, arg });
}
return Ok(Term::Src { src, val: app });
}
@ -417,7 +423,16 @@ impl<'i> KindParser<'i> {
// MCH ::= match <name> = <term> { <name> : <term> <...> }: <term>
if self.starts_with("match ") {
let ini = *self.index() as u64;
let mch = self.parse_match(fid, uses)?;
let mch = self.parse_match(fid, uses, false)?;
let end = *self.index() as u64;
let src = Src::new(fid, ini, end);
return Ok(Term::Src { src, val: Box::new(Term::Mch { mch: Box::new(mch) }) });
}
// FLD ::= fold <name> = <term> { <name> : <term> <...> }: <term>
if self.starts_with("fold ") {
let ini = *self.index() as u64;
let mch = self.parse_match(fid, uses, true)?;
let end = *self.index() as u64;
let src = Src::new(fid, ini, end);
return Ok(Term::Src { src, val: Box::new(Term::Mch { mch: Box::new(mch) }) });
@ -438,4 +453,73 @@ impl<'i> KindParser<'i> {
}
pub fn parse_term_end(&mut self, fid: u64, uses: &Uses, term: Term) -> Result<Term, String> {
self.skip_trivia();
// Simple Function
if self.starts_with("->") {
self.consume("->")?;
let ini = *self.index() as u64;
let nam = "x".to_string();
let inp = Box::new(term);
let bod = Box::new(self.parse_term(fid, &shadow(&nam, uses))?);
let end = *self.index() as u64;
let src = Src::new(fid, ini, end);
return Ok(Term::Src { src, val: Box::new(Term::All { era: false, nam, inp, bod }) });
}
// Equality
if self.starts_with("==") {
self.consume("==")?;
let ini = *self.index() as u64;
let v_0 = term;
let v_1 = self.parse_term(fid, uses)?;
let end = *self.index() as u64;
let src = Src::new(fid, ini, end);
let eql = Equal { a: v_0, b: v_1 };
return Ok(Term::Src { src, val: Box::new(Term::new_equal(&eql)) });
}
// Annotation
if self.starts_with("::") {
self.consume("::")?;
let ini = *self.index() as u64;
let val = Box::new(term);
let typ = Box::new(self.parse_term(fid, uses)?);
let end = *self.index() as u64;
let src = Src::new(fid, ini, end);
return Ok(Term::Src { src, val: Box::new(Term::Ann { chk: true, typ, val }) });
}
// List.cons
if self.starts_with(",") {
self.consume(",")?;
let ini = *self.index() as u64;
let hd = Box::new(term);
let tl = Box::new(self.parse_term(fid, uses)?);
let end = *self.index() as u64;
let src = Src::new(fid, ini, end);
return Ok(Term::Src {
src,
val: Box::new(Term::App {
era: false,
fun: Box::new(Term::App {
era: false,
fun: Box::new(Term::App {
era: true,
fun: Box::new(Term::Var { nam: "cons".to_string() }),
arg: Box::new(Term::Met {}),
}),
arg: hd,
}),
arg: tl,
}),
});
}
return Ok(term);
}
}

View File

@ -65,7 +65,7 @@ impl Term {
Term::All { .. } => {
let mut bnd = vec![];
let mut bod = self;
while let Term::All { nam, inp, bod: in_bod } = bod {
while let Term::All { era: _, nam, inp, bod: in_bod } = bod {
bnd.push(Show::call("", vec![
Show::glue("", vec![
Show::text("∀("),
@ -85,7 +85,7 @@ impl Term {
Term::Lam { .. } => {
let mut bnd = vec![];
let mut bod = self;
while let Term::Lam { nam, bod: in_bod } = bod {
while let Term::Lam { era: _, nam, bod: in_bod } = bod {
bnd.push(Show::text(&format!("λ{}",nam)));
bod = in_bod;
}
@ -97,7 +97,7 @@ impl Term {
Term::App { .. } => {
let mut fun = self;
let mut spn = vec![];
while let Term::App { fun: in_fun, arg } = fun {
while let Term::App { era: _, fun: in_fun, arg } = fun {
spn.push(arg);
fun = in_fun;
}