mirror of
https://github.com/HigherOrderCO/Bend.git
synced 2024-09-11 03:45:29 +03:00
Merge pull request #501 from HigherOrderCO/475-use-the-hvm-ast-for-representing-inets-inside-the-compiler
Use the hvm ast for representing inets inside the compiler
This commit is contained in:
commit
948eaf0a09
57
Cargo.lock
generated
57
Cargo.lock
generated
@ -60,20 +60,14 @@ dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
|
||||
|
||||
[[package]]
|
||||
name = "bend-lang"
|
||||
version = "0.2.20"
|
||||
version = "0.2.21"
|
||||
dependencies = [
|
||||
"TSPL",
|
||||
"arrayvec",
|
||||
"clap",
|
||||
"highlight_error",
|
||||
"hvm",
|
||||
"indexmap",
|
||||
"insta",
|
||||
"interner",
|
||||
@ -86,9 +80,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.97"
|
||||
version = "1.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4"
|
||||
checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
@ -184,12 +178,31 @@ version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||
|
||||
[[package]]
|
||||
name = "highlight_error"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "809e18805660d7b6b2e2b9f316a5099521b5998d5cba4dda11b5157a21aaef03"
|
||||
|
||||
[[package]]
|
||||
name = "hvm"
|
||||
version = "2.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32cf1c2a5333c940ab1c5b6c64e0f3242739332446b572b07a87f46753c9f69e"
|
||||
dependencies = [
|
||||
"TSPL",
|
||||
"cc",
|
||||
"clap",
|
||||
"highlight_error",
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.2.6"
|
||||
@ -241,9 +254,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.153"
|
||||
version = "0.2.155"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
@ -258,10 +271,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f16fbe026127b8ff026fcc7419ddf201d47807295690de255bb68fced2ba15af"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.82"
|
||||
name = "num_cpus"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
|
||||
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@ -326,9 +349,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.64"
|
||||
version = "2.0.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ad3dee41f36859875573074334c200d1add8e4a87bb37113ebd31d926b7b11f"
|
||||
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -2,7 +2,7 @@
|
||||
name = "bend-lang"
|
||||
description = "A high-level, massively parallel programming language"
|
||||
license = "Apache-2.0"
|
||||
version = "0.2.20"
|
||||
version = "0.2.21"
|
||||
edition = "2021"
|
||||
exclude = ["tests/snapshots/"]
|
||||
|
||||
@ -24,9 +24,9 @@ cli = ["dep:clap"]
|
||||
|
||||
[dependencies]
|
||||
TSPL = "0.0.12"
|
||||
arrayvec = "0.7.4"
|
||||
clap = { version = "4.4.1", features = ["derive"], optional = true }
|
||||
highlight_error = "0.1.1"
|
||||
hvm = "2.0.16"
|
||||
indexmap = "2.2.3"
|
||||
interner = "0.2.1"
|
||||
itertools = "0.11.0"
|
||||
|
@ -83,6 +83,7 @@
|
||||
"Pythonish",
|
||||
"quadtree",
|
||||
"quadtrees",
|
||||
"rbag",
|
||||
"readback",
|
||||
"recursively",
|
||||
"redex",
|
||||
|
@ -59,7 +59,7 @@ If you have a set of mutually recursive functions, you only need to make one of
|
||||
|
||||
### Automatic optimization
|
||||
|
||||
HVM-lang carries out [match linearization](compiler-options.md#linearize-matches) and [combinator floating](compiler-options.md#float-combinators) optimizations, enabled through the CLI, which are active by default in strict mode.
|
||||
Bend carries out [match linearization](compiler-options.md#linearize-matches) and [combinator floating](compiler-options.md#float-combinators) optimizations, enabled through the CLI, which are active by default in strict mode.
|
||||
|
||||
Consider the code below:
|
||||
|
||||
|
@ -94,7 +94,7 @@ Operation | Description | Accepted types | Return type
|
||||
|
||||
### Pattern matching
|
||||
|
||||
HVM-lang also includes a `switch` syntax for pattern-matching U24 numbers.
|
||||
Bend also includes a `switch` syntax for pattern-matching U24 numbers.
|
||||
|
||||
```rs
|
||||
Number.to_church = λn λf λx
|
||||
|
@ -47,7 +47,7 @@ UnwrapOrZero x = (x λx.val x.val 0)
|
||||
|
||||
### Pattern Matching functions
|
||||
|
||||
Besides `match`and `switch` terms, hvm-lang also supports equational-style pattern matching functions.
|
||||
Besides `match`and `switch` terms, Bend also supports equational-style pattern matching functions.
|
||||
|
||||
```py
|
||||
And True b = b
|
||||
|
@ -71,7 +71,7 @@ def radix(n):
|
||||
r = swap(n & 512, r, Map_/Free)
|
||||
return radix2(n, r)
|
||||
|
||||
# At the moment, we need to manually break very large functiions into smaller ones
|
||||
# At the moment, we need to manually break very large functions into smaller ones
|
||||
# if we want to run this program on the GPU.
|
||||
# In a future version of Bend, we will be able to do this automatically.
|
||||
def radix2(n, r):
|
||||
|
@ -1,7 +1,6 @@
|
||||
use crate::{
|
||||
diagnostics::{Diagnostics, DiagnosticsConfig},
|
||||
hvm::{self, ast::get_typ},
|
||||
maybe_grow, ENTRY_POINT,
|
||||
maybe_grow, multi_iterator, ENTRY_POINT,
|
||||
};
|
||||
// use hvmc::ast::get_typ;
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
@ -19,7 +18,7 @@ pub mod term_to_net;
|
||||
pub mod transform;
|
||||
|
||||
pub use net_to_term::{net_to_term, ReadbackError};
|
||||
pub use term_to_net::{book_to_nets, term_to_net};
|
||||
pub use term_to_net::{book_to_hvm, term_to_hvm};
|
||||
|
||||
pub static STRINGS: GlobalPool<String> = GlobalPool::new();
|
||||
#[derive(Debug)]
|
||||
@ -247,45 +246,6 @@ pub struct Name(GlobalString);
|
||||
|
||||
/* Implementations */
|
||||
|
||||
/// A macro for creating iterators that can have statically known
|
||||
/// different types. Useful for iterating over tree children, where
|
||||
/// each tree node variant yields a different iterator type.
|
||||
#[macro_export]
|
||||
macro_rules! multi_iterator {
|
||||
($Iter:ident { $($Variant:ident),* $(,)? }) => {
|
||||
#[derive(Debug, Clone)]
|
||||
enum $Iter<$($Variant),*> {
|
||||
$($Variant { iter: $Variant }),*
|
||||
}
|
||||
|
||||
impl<$($Variant),*> $Iter<$($Variant),*> {
|
||||
$(
|
||||
#[allow(non_snake_case)]
|
||||
fn $Variant(iter: impl IntoIterator<IntoIter = $Variant>) -> Self {
|
||||
$Iter::$Variant { iter: iter.into_iter() }
|
||||
}
|
||||
)*
|
||||
}
|
||||
|
||||
impl<T, $($Variant: Iterator<Item = T>),*> Iterator for $Iter<$($Variant),*> {
|
||||
type Item = T;
|
||||
fn next(&mut self) -> Option<T> {
|
||||
match self { $($Iter::$Variant { iter } => iter.next()),* }
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
match self { $($Iter::$Variant { iter } => iter.size_hint()),* }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, $($Variant: DoubleEndedIterator<Item = T>),*> DoubleEndedIterator for $Iter<$($Variant),*> {
|
||||
fn next_back(&mut self) -> Option<T> {
|
||||
match self { $($Iter::$Variant { iter } => iter.next_back()),* }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl PartialEq<str> for Name {
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
&**self == other
|
||||
@ -901,17 +861,17 @@ impl Num {
|
||||
|
||||
pub fn to_bits(&self) -> u32 {
|
||||
match self {
|
||||
Num::U24(val) => hvm::ast::new_u24(*val),
|
||||
Num::I24(val) => hvm::ast::new_i24(*val),
|
||||
Num::F24(val) => hvm::ast::new_f24(*val),
|
||||
Num::U24(val) => hvm::hvm::Numb::new_u24(*val).0,
|
||||
Num::I24(val) => hvm::hvm::Numb::new_i24(*val).0,
|
||||
Num::F24(val) => hvm::hvm::Numb::new_f24(*val).0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_bits(bits: u32) -> Self {
|
||||
match get_typ(bits) {
|
||||
hvm::ast::TY_U24 => Num::U24(hvm::ast::get_u24(bits)),
|
||||
hvm::ast::TY_I24 => Num::I24(hvm::ast::get_i24(bits)),
|
||||
hvm::ast::TY_F24 => Num::F24(hvm::ast::get_f24(bits)),
|
||||
match hvm::hvm::Numb::get_typ(&hvm::hvm::Numb(bits)) {
|
||||
hvm::hvm::TY_U24 => Num::U24(hvm::hvm::Numb::get_u24(&hvm::hvm::Numb(bits))),
|
||||
hvm::hvm::TY_I24 => Num::I24(hvm::hvm::Numb::get_i24(&hvm::hvm::Numb(bits))),
|
||||
hvm::hvm::TY_F24 => Num::F24(hvm::hvm::Numb::get_f24(&hvm::hvm::Numb(bits))),
|
||||
_ => unreachable!("Invalid Num bits"),
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,12 @@
|
||||
use hvm::ast::{get_f24, get_i24, get_typ, get_u24};
|
||||
|
||||
use crate::{
|
||||
diagnostics::{DiagnosticOrigin, Diagnostics, Severity},
|
||||
fun::{term_to_net::Labels, Book, FanKind, Name, Op, Pattern, Tag, Term},
|
||||
hvm, maybe_grow,
|
||||
fun::{term_to_net::Labels, Book, FanKind, Name, Num, Op, Pattern, Tag, Term},
|
||||
maybe_grow,
|
||||
net::{CtrKind, INet, NodeId, NodeKind, Port, SlotId, ROOT},
|
||||
};
|
||||
use hvm::hvm::Numb;
|
||||
use std::collections::{BTreeSet, HashMap, HashSet};
|
||||
|
||||
use super::Num;
|
||||
|
||||
/// Converts an Interaction-INet to a Lambda Calculus term
|
||||
pub fn net_to_term(
|
||||
net: &INet,
|
||||
@ -224,11 +221,12 @@ impl Reader<'_> {
|
||||
let opr_node = self.net.enter_port(Port(port0_node, 0)).node();
|
||||
let opr_kind = self.net.node(opr_node).kind.clone();
|
||||
let opr = if let NodeKind::Num { val } = opr_kind {
|
||||
if get_typ(val) != hvm::ast::TY_SYM {
|
||||
let typ = hvm::hvm::Numb::get_typ(&Numb(val));
|
||||
if typ != hvm::hvm::TY_SYM {
|
||||
self.error(ReadbackError::InvalidNumericOp);
|
||||
return Term::Err;
|
||||
}
|
||||
if let Some(op) = Op::from_native_tag(val, NumType::U24) {
|
||||
if let Some(op) = Op::from_native_tag(typ, NumType::U24) {
|
||||
op
|
||||
} else {
|
||||
self.error(ReadbackError::InvalidNumericOp);
|
||||
@ -378,32 +376,32 @@ enum NumType {
|
||||
}
|
||||
|
||||
impl Op {
|
||||
fn from_native_tag(val: u32, typ: NumType) -> Option<Op> {
|
||||
fn from_native_tag(val: hvm::hvm::Tag, typ: NumType) -> Option<Op> {
|
||||
let op = match val {
|
||||
0x4 => Op::ADD,
|
||||
0x5 => Op::SUB,
|
||||
0x6 => Op::MUL,
|
||||
0x7 => Op::DIV,
|
||||
0x8 => Op::REM,
|
||||
0x9 => Op::EQ,
|
||||
0xa => Op::NEQ,
|
||||
0xb => Op::LT,
|
||||
0xc => Op::GT,
|
||||
0xd => {
|
||||
hvm::hvm::OP_ADD => Op::ADD,
|
||||
hvm::hvm::OP_SUB => Op::SUB,
|
||||
hvm::hvm::OP_MUL => Op::MUL,
|
||||
hvm::hvm::OP_DIV => Op::DIV,
|
||||
hvm::hvm::OP_REM => Op::REM,
|
||||
hvm::hvm::OP_EQ => Op::EQ,
|
||||
hvm::hvm::OP_NEQ => Op::NEQ,
|
||||
hvm::hvm::OP_LT => Op::LT,
|
||||
hvm::hvm::OP_GT => Op::GT,
|
||||
hvm::hvm::OP_AND => {
|
||||
if typ == NumType::F24 {
|
||||
Op::ATN
|
||||
} else {
|
||||
Op::AND
|
||||
}
|
||||
}
|
||||
0xe => {
|
||||
hvm::hvm::OP_OR => {
|
||||
if typ == NumType::F24 {
|
||||
Op::LOG
|
||||
} else {
|
||||
Op::OR
|
||||
}
|
||||
}
|
||||
0xf => {
|
||||
hvm::hvm::OP_XOR => {
|
||||
if typ == NumType::F24 {
|
||||
Op::POW
|
||||
} else {
|
||||
@ -477,12 +475,12 @@ impl Term {
|
||||
}
|
||||
|
||||
fn num_from_bits_with_type(val: u32, typ: u32) -> Term {
|
||||
match get_typ(typ) {
|
||||
match hvm::hvm::Numb::get_typ(&Numb(typ)) {
|
||||
// No type information, assume u24 by default
|
||||
hvm::ast::TY_SYM => Term::Num { val: Num::U24(get_u24(val)) },
|
||||
hvm::ast::TY_U24 => Term::Num { val: Num::U24(get_u24(val)) },
|
||||
hvm::ast::TY_I24 => Term::Num { val: Num::I24(get_i24(val)) },
|
||||
hvm::ast::TY_F24 => Term::Num { val: Num::F24(get_f24(val)) },
|
||||
hvm::hvm::TY_SYM => Term::Num { val: Num::U24(Numb::get_u24(&Numb(val))) },
|
||||
hvm::hvm::TY_U24 => Term::Num { val: Num::U24(Numb::get_u24(&Numb(val))) },
|
||||
hvm::hvm::TY_I24 => Term::Num { val: Num::I24(Numb::get_i24(&Numb(val))) },
|
||||
hvm::hvm::TY_F24 => Term::Num { val: Num::F24(Numb::get_f24(&Numb(val))) },
|
||||
_ => Term::Err,
|
||||
}
|
||||
}
|
||||
|
@ -1,39 +1,37 @@
|
||||
use crate::{
|
||||
diagnostics::Diagnostics,
|
||||
fun::{Book, Name, Pattern, Tag, Term},
|
||||
hvm, maybe_grow,
|
||||
fun::{num_to_name, Book, FanKind, Name, Op, Pattern, Term},
|
||||
hvm::{net_trees, tree_children},
|
||||
maybe_grow,
|
||||
net::CtrKind::{self, *},
|
||||
};
|
||||
use hvm::ast::{Net, Tree};
|
||||
use loaned::LoanedMut;
|
||||
use std::{
|
||||
collections::{hash_map::Entry, HashMap},
|
||||
ops::{Index, IndexMut},
|
||||
};
|
||||
|
||||
use hvm::ast::{Net, Tree};
|
||||
use loaned::LoanedMut;
|
||||
|
||||
use super::{num_to_name, FanKind, Op};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ViciousCycleErr;
|
||||
|
||||
pub fn book_to_nets(book: &Book, diags: &mut Diagnostics) -> Result<(hvm::ast::Book, Labels), Diagnostics> {
|
||||
pub fn book_to_hvm(book: &Book, diags: &mut Diagnostics) -> Result<(hvm::ast::Book, Labels), Diagnostics> {
|
||||
diags.start_pass();
|
||||
|
||||
let mut hvmc = hvm::ast::Book::default();
|
||||
let mut hvm_book = hvm::ast::Book { defs: Default::default() };
|
||||
let mut labels = Labels::default();
|
||||
|
||||
let main = book.entrypoint.as_ref().unwrap();
|
||||
|
||||
for def in book.defs.values() {
|
||||
for rule in def.rules.iter() {
|
||||
let net = term_to_net(&rule.body, &mut labels);
|
||||
let net = term_to_hvm(&rule.body, &mut labels);
|
||||
|
||||
let name = if def.name == *main { book.hvmc_entrypoint().to_string() } else { def.name.0.to_string() };
|
||||
|
||||
match net {
|
||||
Ok(net) => {
|
||||
hvmc.insert(name, net);
|
||||
hvm_book.defs.insert(name, net);
|
||||
}
|
||||
Err(err) => diags.add_inet_error(err, name),
|
||||
}
|
||||
@ -43,12 +41,12 @@ pub fn book_to_nets(book: &Book, diags: &mut Diagnostics) -> Result<(hvm::ast::B
|
||||
labels.con.finish();
|
||||
labels.dup.finish();
|
||||
|
||||
diags.fatal((hvmc, labels))
|
||||
diags.fatal((hvm_book, labels))
|
||||
}
|
||||
|
||||
/// Converts an LC term into an IC net.
|
||||
pub fn term_to_net(term: &Term, labels: &mut Labels) -> Result<Net, String> {
|
||||
let mut net = Net::default();
|
||||
pub fn term_to_hvm(term: &Term, labels: &mut Labels) -> Result<Net, String> {
|
||||
let mut net = Net { root: Tree::Era, rbag: Default::default() };
|
||||
|
||||
let mut state = EncodeTermState {
|
||||
lets: Default::default(),
|
||||
@ -61,11 +59,11 @@ pub fn term_to_net(term: &Term, labels: &mut Labels) -> Result<Net, String> {
|
||||
};
|
||||
|
||||
state.encode_term(term, Place::Hole(&mut net.root));
|
||||
LoanedMut::from(std::mem::take(&mut state.redexes)).place(&mut net.redexes);
|
||||
LoanedMut::from(std::mem::take(&mut state.redexes)).place(&mut net.rbag);
|
||||
|
||||
let EncodeTermState { created_nodes, .. } = { state };
|
||||
|
||||
let found_nodes = net.trees().map(count_nodes).sum::<usize>();
|
||||
let found_nodes = net_trees(&net).map(count_nodes).sum::<usize>();
|
||||
if created_nodes != found_nodes {
|
||||
return Err("Found term that compiles into an inet with a vicious cycle".into());
|
||||
}
|
||||
@ -86,7 +84,7 @@ struct EncodeTermState<'t, 'l> {
|
||||
|
||||
fn count_nodes(tree: &Tree) -> usize {
|
||||
maybe_grow(|| {
|
||||
usize::from(tree.children().next().is_some()) + tree.children().map(count_nodes).sum::<usize>()
|
||||
usize::from(tree_children(tree).next().is_some()) + tree_children(tree).map(count_nodes).sum::<usize>()
|
||||
})
|
||||
}
|
||||
|
||||
@ -111,7 +109,7 @@ impl<'t, 'l> EncodeTermState<'t, 'l> {
|
||||
Term::Link { nam } => self.link_var(true, nam, up),
|
||||
Term::Ref { nam } => self.link(up, Place::Tree(LoanedMut::new(Tree::Ref { nam: nam.to_string() }))),
|
||||
Term::Num { val } => {
|
||||
let val = val.to_bits();
|
||||
let val = hvm::ast::Numb(val.to_bits());
|
||||
self.link(up, Place::Tree(LoanedMut::new(Tree::Num { val })))
|
||||
}
|
||||
// A lambda becomes to a con node. Ports:
|
||||
@ -144,10 +142,12 @@ impl<'t, 'l> EncodeTermState<'t, 'l> {
|
||||
assert!(with.is_empty());
|
||||
assert!(rules.len() == 2);
|
||||
|
||||
self.created_nodes += 1;
|
||||
self.created_nodes += 2;
|
||||
let loaned = Tree::Swi { fst: Box::new(Tree::Con{fst: Box::new(Tree::Era), snd: Box::new(Tree::Era)}), snd: Box::new(Tree::Era)};
|
||||
let ((zero, succ, out), node) =
|
||||
LoanedMut::loan_with(Tree::Mat { zero: hole(), succ: hole(), out: hole() }, |t, l| {
|
||||
let Tree::Mat { zero, succ, out, .. } = t else { unreachable!() };
|
||||
LoanedMut::loan_with(loaned, |t, l| {
|
||||
let Tree::Swi { fst, snd: out } = t else { unreachable!() };
|
||||
let Tree::Con { fst:zero, snd: succ } = fst.as_mut() else { unreachable!() };
|
||||
(l.loan_mut(zero), l.loan_mut(succ), l.loan_mut(out))
|
||||
});
|
||||
|
||||
@ -172,7 +172,7 @@ impl<'t, 'l> EncodeTermState<'t, 'l> {
|
||||
// Partially apply with fst
|
||||
(Term::Num { val }, snd) => {
|
||||
let val = val.to_bits();
|
||||
let val = (val & !0x1F) | opr.to_native_tag();
|
||||
let val = hvm::ast::Numb((val & !0x1F) | opr.to_native_tag() as u32);
|
||||
let fst = Place::Tree(LoanedMut::new(Tree::Num { val }));
|
||||
let node = self.new_opr();
|
||||
self.link(fst, node.0);
|
||||
@ -183,7 +183,7 @@ impl<'t, 'l> EncodeTermState<'t, 'l> {
|
||||
(fst, Term::Num { val }) => {
|
||||
if [Op::POW, Op::ATN, Op::LOG].contains(opr) {
|
||||
// POW, ATN and LOG share tags with AND, OR and XOR, so don't flip or results will be wrong
|
||||
let opr_val = hvm::ast::new_sym(opr.to_native_tag());
|
||||
let opr_val = hvm::ast::Numb(hvm::hvm::Numb::new_sym(opr.to_native_tag()).0);
|
||||
let oper = Place::Tree(LoanedMut::new(Tree::Num { val: opr_val }));
|
||||
let node1 = self.new_opr();
|
||||
self.encode_term(fst, node1.0);
|
||||
@ -195,7 +195,7 @@ impl<'t, 'l> EncodeTermState<'t, 'l> {
|
||||
} else {
|
||||
// flip
|
||||
let val = val.to_bits();
|
||||
let val = (val & !0x1F) | hvm::ast::flip_sym(opr.to_native_tag());
|
||||
let val = hvm::ast::Numb((val & !0x1F) | flip_sym(opr.to_native_tag()) as u32);
|
||||
let snd = Place::Tree(LoanedMut::new(Tree::Num { val }));
|
||||
let node = self.new_opr();
|
||||
self.encode_term(fst, node.0);
|
||||
@ -205,7 +205,7 @@ impl<'t, 'l> EncodeTermState<'t, 'l> {
|
||||
}
|
||||
// Don't partially apply
|
||||
(fst, snd) => {
|
||||
let opr_val = hvm::ast::new_sym(opr.to_native_tag());
|
||||
let opr_val = hvm::ast::Numb(hvm::hvm::Numb::new_sym(opr.to_native_tag()).0);
|
||||
let oper = Place::Tree(LoanedMut::new(Tree::Num { val: opr_val }));
|
||||
let node1 = self.new_opr();
|
||||
self.encode_term(fst, node1.0);
|
||||
@ -252,10 +252,12 @@ impl<'t, 'l> EncodeTermState<'t, 'l> {
|
||||
|
||||
fn link(&mut self, a: Place<'t>, b: Place<'t>) {
|
||||
match (a, b) {
|
||||
(Place::Tree(a), Place::Tree(b)) => self.redexes.push(LoanedMut::merge(Default::default(), |r, m| {
|
||||
m.place(b, &mut r.1);
|
||||
m.place(a, &mut r.2);
|
||||
})),
|
||||
(Place::Tree(a), Place::Tree(b)) => {
|
||||
self.redexes.push(LoanedMut::merge((false, Tree::Era, Tree::Era), |r, m| {
|
||||
m.place(b, &mut r.1);
|
||||
m.place(a, &mut r.2);
|
||||
}))
|
||||
}
|
||||
(Place::Tree(t), Place::Hole(h)) | (Place::Hole(h), Place::Tree(t)) => {
|
||||
t.place(h);
|
||||
}
|
||||
@ -277,20 +279,25 @@ impl<'t, 'l> EncodeTermState<'t, 'l> {
|
||||
|
||||
fn new_ctr(&mut self, kind: CtrKind) -> (Place<'t>, Place<'t>, Place<'t>) {
|
||||
self.created_nodes += 1;
|
||||
let (ports, node) =
|
||||
LoanedMut::loan_with(Tree::Ctr { lab: kind.to_lab(), ports: vec![Tree::Era, Tree::Era] }, |t, l| {
|
||||
let Tree::Ctr { ports, .. } = t else { unreachable!() };
|
||||
l.loan_mut(ports)
|
||||
});
|
||||
let (a, b) = ports.split_at_mut(1);
|
||||
(Place::Tree(node), Place::Hole(&mut a[0]), Place::Hole(&mut b[0]))
|
||||
let node = match kind {
|
||||
CtrKind::Con(None) => Tree::Con { fst: Box::new(Tree::Era), snd: Box::new(Tree::Era) },
|
||||
CtrKind::Dup(0) => Tree::Dup { fst: Box::new(Tree::Era), snd: Box::new(Tree::Era) },
|
||||
CtrKind::Tup(None) => Tree::Con { fst: Box::new(Tree::Era), snd: Box::new(Tree::Era) },
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let ((a, b), node) = LoanedMut::loan_with(node, |t, l| match t {
|
||||
Tree::Con { fst, snd } => (l.loan_mut(fst), l.loan_mut(snd)),
|
||||
Tree::Dup { fst, snd } => (l.loan_mut(fst), l.loan_mut(snd)),
|
||||
_ => unreachable!(),
|
||||
});
|
||||
(Place::Tree(node), Place::Hole(a), Place::Hole(b))
|
||||
}
|
||||
|
||||
fn new_opr(&mut self) -> (Place<'t>, Place<'t>, Place<'t>) {
|
||||
self.created_nodes += 1;
|
||||
let ((fst, snd), node) =
|
||||
LoanedMut::loan_with(Tree::Op { fst: Box::new(Tree::Era), snd: Box::new(Tree::Era) }, |t, l| {
|
||||
let Tree::Op { fst, snd } = t else { unreachable!() };
|
||||
LoanedMut::loan_with(Tree::Opr { fst: Box::new(Tree::Era), snd: Box::new(Tree::Era) }, |t, l| {
|
||||
let Tree::Opr { fst, snd } = t else { unreachable!() };
|
||||
(l.loan_mut(fst), l.loan_mut(snd))
|
||||
});
|
||||
(Place::Tree(node), Place::Hole(fst), Place::Hole(snd))
|
||||
@ -319,7 +326,7 @@ impl<'t, 'l> EncodeTermState<'t, 'l> {
|
||||
i
|
||||
}
|
||||
|
||||
fn fan_kind(&mut self, fan: &FanKind, tag: &Tag) -> CtrKind {
|
||||
fn fan_kind(&mut self, fan: &FanKind, tag: &crate::fun::Tag) -> CtrKind {
|
||||
let lab = self.labels[*fan].generate(tag);
|
||||
if *fan == FanKind::Tup { Tup(lab) } else { Dup(lab.unwrap()) }
|
||||
}
|
||||
@ -374,7 +381,8 @@ impl IndexMut<FanKind> for Labels {
|
||||
impl LabelGenerator {
|
||||
// If some tag and new generate a new label, otherwise return the generated label.
|
||||
// If none use the implicit label counter.
|
||||
fn generate(&mut self, tag: &Tag) -> Option<u16> {
|
||||
fn generate(&mut self, tag: &crate::fun::Tag) -> Option<u16> {
|
||||
use crate::fun::Tag;
|
||||
match tag {
|
||||
Tag::Named(_name) => {
|
||||
todo!("Named tags not implemented for hvm32");
|
||||
@ -393,7 +401,8 @@ impl LabelGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_tag(&self, label: Option<u16>) -> Tag {
|
||||
pub fn to_tag(&self, label: Option<u16>) -> crate::fun::Tag {
|
||||
use crate::fun::Tag;
|
||||
match label {
|
||||
Some(label) => match self.label_to_name.get(&label) {
|
||||
Some(name) => Tag::Named(name.clone()),
|
||||
@ -415,31 +424,45 @@ impl LabelGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
fn hole<T: Default>() -> T {
|
||||
T::default()
|
||||
}
|
||||
|
||||
impl Op {
|
||||
fn to_native_tag(self) -> u32 {
|
||||
fn to_native_tag(self) -> hvm::hvm::Tag {
|
||||
match self {
|
||||
Op::ADD => hvm::ast::OP_ADD,
|
||||
Op::SUB => hvm::ast::OP_SUB,
|
||||
Op::MUL => hvm::ast::OP_MUL,
|
||||
Op::DIV => hvm::ast::OP_DIV,
|
||||
Op::REM => hvm::ast::OP_REM,
|
||||
Op::EQ => hvm::ast::OP_EQ,
|
||||
Op::NEQ => hvm::ast::OP_NEQ,
|
||||
Op::LT => hvm::ast::OP_LT,
|
||||
Op::GT => hvm::ast::OP_GT,
|
||||
Op::AND => hvm::ast::OP_AND,
|
||||
Op::OR => hvm::ast::OP_OR,
|
||||
Op::XOR => hvm::ast::OP_XOR,
|
||||
Op::SHL => hvm::ast::OP_SHL,
|
||||
Op::SHR => hvm::ast::OP_SHR,
|
||||
Op::ADD => hvm::hvm::OP_ADD,
|
||||
Op::SUB => hvm::hvm::OP_SUB,
|
||||
Op::MUL => hvm::hvm::OP_MUL,
|
||||
Op::DIV => hvm::hvm::OP_DIV,
|
||||
Op::REM => hvm::hvm::OP_REM,
|
||||
Op::EQ => hvm::hvm::OP_EQ,
|
||||
Op::NEQ => hvm::hvm::OP_NEQ,
|
||||
Op::LT => hvm::hvm::OP_LT,
|
||||
Op::GT => hvm::hvm::OP_GT,
|
||||
Op::AND => hvm::hvm::OP_AND,
|
||||
Op::OR => hvm::hvm::OP_OR,
|
||||
Op::XOR => hvm::hvm::OP_XOR,
|
||||
Op::SHL => hvm::hvm::OP_SHL,
|
||||
Op::SHR => hvm::hvm::OP_SHR,
|
||||
|
||||
Op::ATN => hvm::ast::OP_AND,
|
||||
Op::LOG => hvm::ast::OP_OR,
|
||||
Op::POW => hvm::ast::OP_XOR,
|
||||
Op::ATN => hvm::hvm::OP_AND,
|
||||
Op::LOG => hvm::hvm::OP_OR,
|
||||
Op::POW => hvm::hvm::OP_XOR,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn flip_sym(tag: hvm::hvm::Tag) -> hvm::hvm::Tag {
|
||||
match tag {
|
||||
hvm::hvm::OP_SUB => hvm::hvm::FP_SUB,
|
||||
hvm::hvm::FP_SUB => hvm::hvm::OP_SUB,
|
||||
hvm::hvm::OP_DIV => hvm::hvm::FP_DIV,
|
||||
hvm::hvm::FP_DIV => hvm::hvm::OP_DIV,
|
||||
hvm::hvm::OP_REM => hvm::hvm::FP_REM,
|
||||
hvm::hvm::FP_REM => hvm::hvm::OP_REM,
|
||||
hvm::hvm::OP_LT => hvm::hvm::OP_GT,
|
||||
hvm::hvm::OP_GT => hvm::hvm::OP_LT,
|
||||
hvm::hvm::OP_SHL => hvm::hvm::FP_SHL,
|
||||
hvm::hvm::FP_SHL => hvm::hvm::OP_SHL,
|
||||
hvm::hvm::OP_SHR => hvm::hvm::FP_SHR,
|
||||
hvm::hvm::FP_SHR => hvm::hvm::OP_SHR,
|
||||
_ => tag,
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,11 @@
|
||||
use crate::{
|
||||
hvm::ast::{Book, Net, Tree},
|
||||
maybe_grow,
|
||||
};
|
||||
use super::tree_children;
|
||||
use crate::maybe_grow;
|
||||
use hvm::ast::{Book, Net, Tree};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
pub fn add_recursive_priority(book: &mut Book) {
|
||||
// Direct dependencies
|
||||
let deps = book.iter().map(|(nam, net)| (nam.clone(), dependencies(net))).collect::<HashMap<_, _>>();
|
||||
let deps = book.defs.iter().map(|(nam, net)| (nam.clone(), dependencies(net))).collect::<HashMap<_, _>>();
|
||||
// Recursive cycles
|
||||
let cycles = cycles(&deps);
|
||||
|
||||
@ -14,7 +13,7 @@ pub fn add_recursive_priority(book: &mut Book) {
|
||||
// For each function in the cycle, if there are redexes with the
|
||||
// next ref in the cycle, add a priority to one of those redexes.
|
||||
for i in 0 .. cycle.len() {
|
||||
let cur = book.get_mut(&cycle[i]).unwrap();
|
||||
let cur = book.defs.get_mut(&cycle[i]).unwrap();
|
||||
let nxt = &cycle[(i + 1) % cycle.len()];
|
||||
add_priority_next_in_cycle(cur, nxt);
|
||||
}
|
||||
@ -25,7 +24,7 @@ fn add_priority_next_in_cycle(net: &mut Net, nxt: &String) {
|
||||
let mut count = 0;
|
||||
|
||||
// Count the number of recursive refs
|
||||
for (_, a, b) in net.redexes.iter() {
|
||||
for (_, a, b) in net.rbag.iter() {
|
||||
if let Tree::Ref { nam } = a {
|
||||
if nam == nxt {
|
||||
count += 1;
|
||||
@ -40,7 +39,7 @@ fn add_priority_next_in_cycle(net: &mut Net, nxt: &String) {
|
||||
|
||||
// If there are more than one recursive ref, add a priority to them.
|
||||
if count > 1 {
|
||||
for (pri, a, b) in net.redexes.iter_mut().rev() {
|
||||
for (pri, a, b) in net.rbag.iter_mut().rev() {
|
||||
if let Tree::Ref { nam } = a {
|
||||
if nam == nxt {
|
||||
*pri = true;
|
||||
@ -105,7 +104,7 @@ fn find_cycles(
|
||||
fn dependencies(net: &Net) -> HashSet<String> {
|
||||
let mut deps = HashSet::new();
|
||||
dependencies_tree(&net.root, &mut deps);
|
||||
for (_, a, b) in &net.redexes {
|
||||
for (_, a, b) in &net.rbag {
|
||||
dependencies_tree(a, &mut deps);
|
||||
dependencies_tree(b, &mut deps);
|
||||
}
|
||||
@ -116,7 +115,7 @@ fn dependencies_tree(tree: &Tree, deps: &mut HashSet<String>) {
|
||||
if let Tree::Ref { nam, .. } = tree {
|
||||
deps.insert(nam.clone());
|
||||
} else {
|
||||
for subtree in tree.children() {
|
||||
for subtree in tree_children(tree) {
|
||||
dependencies_tree(subtree, deps);
|
||||
}
|
||||
}
|
||||
|
673
src/hvm/ast.rs
673
src/hvm/ast.rs
@ -1,673 +0,0 @@
|
||||
//! The textual language of HVMC.
|
||||
//!
|
||||
//! This file defines an AST for interaction nets, and functions to convert this
|
||||
//! AST to/from the textual syntax.
|
||||
//!
|
||||
//! The grammar is documented in the repo README, as well as within the parser
|
||||
//! methods, for convenience.
|
||||
//!
|
||||
//! The AST is based on the [interaction calculus].
|
||||
//!
|
||||
//! [interaction calculus]: https://en.wikipedia.org/wiki/Interaction_nets#Interaction_calculus
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
use core::fmt;
|
||||
use std::{collections::BTreeMap, iter, mem, str::FromStr};
|
||||
use TSPL::Parser;
|
||||
|
||||
use crate::maybe_grow;
|
||||
|
||||
use super::util::{array_vec, deref};
|
||||
|
||||
/// The top level AST node, representing a collection of named nets.
|
||||
///
|
||||
/// This is a newtype wrapper around a `BTreeMap<String, Net>`, and is
|
||||
/// dereferenceable to such.
|
||||
#[derive(Clone, Hash, PartialEq, Eq, Debug, Default)]
|
||||
pub struct Book {
|
||||
pub nets: BTreeMap<String, Net>,
|
||||
}
|
||||
|
||||
deref!(Book => self.nets: BTreeMap<String, Net>);
|
||||
|
||||
/// An AST node representing an interaction net with one free port.
|
||||
///
|
||||
/// The tree connected to the free port is stored in `root`. The active pairs in
|
||||
/// the net -- trees connected by their roots -- are stored in `redexes`.
|
||||
///
|
||||
/// (The wiring connecting the leaves of all the trees is represented within the
|
||||
/// trees via pairs of [`Tree::Var`] nodes with the same name.)
|
||||
#[derive(Clone, Hash, PartialEq, Eq, Debug, Default)]
|
||||
pub struct Net {
|
||||
pub root: Tree,
|
||||
pub redexes: Vec<(bool, Tree, Tree)>,
|
||||
}
|
||||
|
||||
/// An AST node representing an interaction net tree.
|
||||
///
|
||||
/// Trees in interaction nets are inductively defined as either wires, or an
|
||||
/// agent with all of its auxiliary ports (if any) connected to trees.
|
||||
///
|
||||
/// Here, the wires at the leaves of the tree are represented with
|
||||
/// [`Tree::Var`], where the variable name is shared between both sides of the
|
||||
/// wire.
|
||||
#[derive(Hash, PartialEq, Eq, Debug, Default)]
|
||||
pub enum Tree {
|
||||
#[default]
|
||||
/// A nilary eraser node.
|
||||
Era,
|
||||
/// A native 60-bit integer.
|
||||
Num { val: u32 },
|
||||
/// A nilary node, referencing a named net.
|
||||
Ref { nam: String },
|
||||
/// A n-ary interaction combinator.
|
||||
Ctr {
|
||||
/// The label of the combinator. (Combinators with the same label
|
||||
/// annihilate, and combinators with different labels commute.)
|
||||
lab: u16,
|
||||
/// The auxiliary ports of this node.
|
||||
///
|
||||
/// - 0 ports: this behaves identically to an eraser node.
|
||||
/// - 1 port: this behaves identically to a wire.
|
||||
/// - 2 ports: this is a standard binary combinator node.
|
||||
/// - 3+ ports: equivalent to right-chained binary nodes; `(a b c)` is
|
||||
/// equivalent to `(a (b c))`.
|
||||
///
|
||||
/// The length of this vector must be less than [`MAX_ARITY`].
|
||||
ports: Vec<Tree>,
|
||||
},
|
||||
/// A binary node representing an operation on native integers.
|
||||
///
|
||||
/// The principal port connects to the left operand.
|
||||
Op {
|
||||
/// An auxiliary port; connects to the right operand.
|
||||
fst: Box<Tree>,
|
||||
/// An auxiliary port; connects to the output.
|
||||
snd: Box<Tree>,
|
||||
},
|
||||
/// A binary node representing a match on native integers.
|
||||
///
|
||||
/// The principal port connects to the integer to be matched on.
|
||||
Mat {
|
||||
/// An auxiliary port; connects to the zero branch.
|
||||
zero: Box<Tree>,
|
||||
/// An auxiliary port; connects to the a CTR with label 0 containing the
|
||||
/// predecessor and the output of the succ branch.
|
||||
succ: Box<Tree>,
|
||||
/// An auxiliary port; connects to the output.
|
||||
out: Box<Tree>,
|
||||
},
|
||||
/// One side of a wire; the other side will have the same name.
|
||||
Var { nam: String },
|
||||
}
|
||||
|
||||
pub const MAX_ARITY: usize = 8;
|
||||
pub const MAX_ADT_VARIANTS: usize = MAX_ARITY - 1;
|
||||
pub const MAX_ADT_FIELDS: usize = MAX_ARITY - 1;
|
||||
|
||||
impl Net {
|
||||
pub fn trees(&self) -> impl Iterator<Item = &Tree> {
|
||||
iter::once(&self.root).chain(self.redexes.iter().flat_map(|(_, x, y)| [x, y]))
|
||||
}
|
||||
pub fn trees_mut(&mut self) -> impl Iterator<Item = &mut Tree> {
|
||||
iter::once(&mut self.root).chain(self.redexes.iter_mut().flat_map(|(_, x, y)| [x, y]))
|
||||
}
|
||||
}
|
||||
|
||||
impl Tree {
|
||||
#[inline(always)]
|
||||
pub fn children(&self) -> impl ExactSizeIterator + DoubleEndedIterator<Item = &Tree> {
|
||||
ArrayVec::<_, MAX_ARITY>::into_iter(match self {
|
||||
Tree::Era | Tree::Num { .. } | Tree::Ref { .. } | Tree::Var { .. } => array_vec::from_array([]),
|
||||
Tree::Ctr { ports, .. } => array_vec::from_iter(ports),
|
||||
Tree::Op { fst: rhs, snd: out, .. } => array_vec::from_array([rhs, out]),
|
||||
Tree::Mat { zero, succ, out } => array_vec::from_array([zero, succ, out]),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn children_mut(&mut self) -> impl ExactSizeIterator + DoubleEndedIterator<Item = &mut Tree> {
|
||||
ArrayVec::<_, MAX_ARITY>::into_iter(match self {
|
||||
Tree::Era | Tree::Num { .. } | Tree::Ref { .. } | Tree::Var { .. } => array_vec::from_array([]),
|
||||
Tree::Ctr { ports, .. } => array_vec::from_iter(ports),
|
||||
Tree::Op { fst, snd } => array_vec::from_array([fst, snd]),
|
||||
Tree::Mat { zero, succ, out } => array_vec::from_array([zero, succ, out]),
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn lab(&self) -> Option<u16> {
|
||||
match self {
|
||||
Tree::Ctr { lab, ports } if ports.len() >= 2 => Some(*lab),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn legacy_mat(mut arms: Tree, out: Tree) -> Option<Tree> {
|
||||
let Tree::Ctr { lab: 0, ports } = &mut arms else { None? };
|
||||
let ports = mem::take(ports);
|
||||
let Ok([zero, succ]) = <[_; 2]>::try_from(ports) else { None? };
|
||||
let zero = Box::new(zero);
|
||||
let succ = Box::new(succ);
|
||||
Some(Tree::Mat { zero, succ, out: Box::new(out) })
|
||||
}
|
||||
}
|
||||
|
||||
pub const TY_SYM: u32 = 0x00;
|
||||
pub const TY_U24: u32 = 0x01;
|
||||
pub const TY_I24: u32 = 0x02;
|
||||
pub const TY_F24: u32 = 0x03;
|
||||
pub const OP_ADD: u32 = 0x04;
|
||||
pub const OP_SUB: u32 = 0x05;
|
||||
pub const FP_SUB: u32 = 0x06;
|
||||
pub const OP_MUL: u32 = 0x07;
|
||||
pub const OP_DIV: u32 = 0x08;
|
||||
pub const FP_DIV: u32 = 0x09;
|
||||
pub const OP_REM: u32 = 0x0A;
|
||||
pub const FP_REM: u32 = 0x0B;
|
||||
pub const OP_EQ: u32 = 0x0C;
|
||||
pub const OP_NEQ: u32 = 0x0D;
|
||||
pub const OP_LT: u32 = 0x0E;
|
||||
pub const OP_GT: u32 = 0x0F;
|
||||
pub const OP_AND: u32 = 0x10;
|
||||
pub const OP_OR: u32 = 0x11;
|
||||
pub const OP_XOR: u32 = 0x12;
|
||||
pub const OP_SHL: u32 = 0x13;
|
||||
pub const FP_SHL: u32 = 0x14;
|
||||
pub const OP_SHR: u32 = 0x15;
|
||||
pub const FP_SHR: u32 = 0x16;
|
||||
|
||||
pub fn flip_sym(val: u32) -> u32 {
|
||||
match val {
|
||||
OP_ADD => OP_ADD,
|
||||
OP_SUB => FP_SUB,
|
||||
OP_MUL => OP_MUL,
|
||||
OP_DIV => FP_DIV,
|
||||
OP_REM => FP_REM,
|
||||
OP_EQ => OP_EQ,
|
||||
OP_NEQ => OP_NEQ,
|
||||
OP_LT => OP_GT,
|
||||
OP_GT => OP_LT,
|
||||
OP_AND => OP_AND,
|
||||
OP_OR => OP_OR,
|
||||
OP_XOR => OP_XOR,
|
||||
FP_SUB => OP_SUB,
|
||||
FP_DIV => OP_DIV,
|
||||
FP_REM => OP_REM,
|
||||
OP_SHL => FP_SHL,
|
||||
OP_SHR => FP_SHR,
|
||||
FP_SHL => OP_SHL,
|
||||
FP_SHR => OP_SHR,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_sym(val: u32) -> u32 {
|
||||
((val as u8 as u32) << 5) | TY_SYM
|
||||
}
|
||||
|
||||
pub fn get_sym(val: u32) -> u32 {
|
||||
(val >> 5) as u8 as u32
|
||||
}
|
||||
|
||||
pub fn new_u24(val: u32) -> u32 {
|
||||
(val << 5) | TY_U24
|
||||
}
|
||||
|
||||
pub fn get_u24(val: u32) -> u32 {
|
||||
val >> 5
|
||||
}
|
||||
|
||||
pub fn new_i24(val: i32) -> u32 {
|
||||
((val as u32) << 5) | TY_I24
|
||||
}
|
||||
|
||||
pub fn get_i24(val: u32) -> i32 {
|
||||
((val as i32) << 3) >> 8
|
||||
}
|
||||
|
||||
pub fn new_f24(val: f32) -> u32 {
|
||||
let bits = val.to_bits();
|
||||
let mut shifted_bits = bits >> 8;
|
||||
let lost_bits = bits & 0xFF;
|
||||
shifted_bits += (lost_bits - ((lost_bits >> 7) & !shifted_bits)) >> 7; // round ties to even
|
||||
shifted_bits |= u32::from((bits & 0x7F800000 == 0x7F800000) && (bits << 9 != 0)); // ensure NaNs don't become infinities
|
||||
(shifted_bits << 5) | TY_F24
|
||||
}
|
||||
|
||||
pub fn get_f24(val: u32) -> f32 {
|
||||
f32::from_bits((val << 3) & 0xFFFFFF00)
|
||||
}
|
||||
|
||||
pub fn get_typ(val: u32) -> u32 {
|
||||
(val & 0x1F) as u8 as u32
|
||||
}
|
||||
|
||||
pub fn partial_opr(a: u32, b: u32) -> u32 {
|
||||
(b & !0x1F) | get_sym(a)
|
||||
}
|
||||
|
||||
impl fmt::Display for Book {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
for (i, (name, net)) in self.iter().enumerate() {
|
||||
if i != 0 {
|
||||
f.write_str("\n\n")?;
|
||||
}
|
||||
write!(f, "@{name} = {net}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Net {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", &self.root)?;
|
||||
for (pri, a, b) in &self.redexes {
|
||||
write!(f, "\n &{} {a} ~ {b}", if *pri { "!" } else { "" })?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Tree {
|
||||
#[allow(illegal_floating_point_literal_pattern)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
maybe_grow(move || match self {
|
||||
Tree::Era => write!(f, "*"),
|
||||
Tree::Ctr { lab, ports } => {
|
||||
match lab {
|
||||
0 => write!(f, "("),
|
||||
1 => write!(f, "{{"),
|
||||
_ => unreachable!(),
|
||||
}?;
|
||||
let mut space = *lab > 1;
|
||||
for port in ports {
|
||||
if space {
|
||||
write!(f, " ")?;
|
||||
}
|
||||
write!(f, "{port}")?;
|
||||
space = true;
|
||||
}
|
||||
match lab {
|
||||
0 => write!(f, ")"),
|
||||
1 => write!(f, "}}"),
|
||||
_ => unreachable!(),
|
||||
}?;
|
||||
Ok(())
|
||||
}
|
||||
Tree::Var { nam } => write!(f, "{nam}"),
|
||||
Tree::Ref { nam } => write!(f, "@{nam}"),
|
||||
Tree::Num { val } => {
|
||||
let numb = *val;
|
||||
match get_typ(numb) {
|
||||
TY_SYM => match get_sym(numb) as u8 as u32 {
|
||||
OP_ADD => write!(f, "[+]"),
|
||||
OP_SUB => write!(f, "[-]"),
|
||||
OP_MUL => write!(f, "[*]"),
|
||||
OP_DIV => write!(f, "[/]"),
|
||||
OP_REM => write!(f, "[%]"),
|
||||
OP_EQ => write!(f, "[=]"),
|
||||
OP_LT => write!(f, "[<]"),
|
||||
OP_GT => write!(f, "[>]"),
|
||||
OP_AND => write!(f, "[&]"),
|
||||
OP_OR => write!(f, "[|]"),
|
||||
OP_XOR => write!(f, "[^]"),
|
||||
OP_SHL => write!(f, "[<<]"),
|
||||
OP_SHR => write!(f, "[>>]"),
|
||||
FP_SUB => write!(f, "[:-]"),
|
||||
FP_DIV => write!(f, "[:/]"),
|
||||
FP_REM => write!(f, "[:%]"),
|
||||
FP_SHL => write!(f, "[:<<]"),
|
||||
FP_SHR => write!(f, "[:>>]"),
|
||||
_ => write!(f, "[?]"),
|
||||
},
|
||||
TY_U24 => {
|
||||
let val = get_u24(numb);
|
||||
write!(f, "{}", val)
|
||||
}
|
||||
TY_I24 => {
|
||||
let val = get_i24(numb);
|
||||
write!(f, "{:+}", val)
|
||||
}
|
||||
TY_F24 => {
|
||||
let val = get_f24(numb);
|
||||
match val {
|
||||
f32::INFINITY => write!(f, "+inf"),
|
||||
f32::NEG_INFINITY => write!(f, "-inf"),
|
||||
x if x.is_nan() => write!(f, "+NaN"),
|
||||
_ => write!(f, "{:?}", val),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let typ = get_typ(numb);
|
||||
let val = get_u24(numb);
|
||||
write!(
|
||||
f,
|
||||
"[{}{}]",
|
||||
match typ {
|
||||
OP_ADD => "+",
|
||||
OP_SUB => "-",
|
||||
OP_MUL => "*",
|
||||
OP_DIV => "/",
|
||||
OP_REM => "%",
|
||||
OP_EQ => "=",
|
||||
OP_NEQ => "!",
|
||||
OP_LT => "<",
|
||||
OP_GT => ">",
|
||||
OP_AND => "&",
|
||||
OP_OR => "|",
|
||||
OP_XOR => "^",
|
||||
OP_SHL => "<<",
|
||||
OP_SHR => ">>",
|
||||
FP_SUB => ":-",
|
||||
FP_DIV => ":/",
|
||||
FP_REM => ":%",
|
||||
FP_SHL => ":<<",
|
||||
FP_SHR => ":>>",
|
||||
_ => "?",
|
||||
},
|
||||
val
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Tree::Op { fst, snd } => write!(f, "$({fst} {snd})"),
|
||||
Tree::Mat { zero, succ, out } => write!(f, "?(({zero} {succ}) {out})"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Manually implemented to avoid stack overflows.
|
||||
impl Clone for Tree {
|
||||
fn clone(&self) -> Tree {
|
||||
maybe_grow(|| match self {
|
||||
Tree::Era => Tree::Era,
|
||||
Tree::Num { val } => Tree::Num { val: *val },
|
||||
Tree::Ref { nam } => Tree::Ref { nam: nam.clone() },
|
||||
Tree::Ctr { lab, ports } => Tree::Ctr { lab: *lab, ports: ports.clone() },
|
||||
Tree::Op { fst, snd } => Tree::Op { fst: fst.clone(), snd: snd.clone() },
|
||||
Tree::Mat { zero, succ, out } => Tree::Mat { zero: zero.clone(), succ: succ.clone(), out: out.clone() },
|
||||
Tree::Var { nam } => Tree::Var { nam: nam.clone() },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Drops non-recursively to avoid stack overflows.
|
||||
impl Drop for Tree {
|
||||
fn drop(&mut self) {
|
||||
loop {
|
||||
let mut i = self.children_mut().filter(|x| x.children().len() != 0);
|
||||
let Some(x) = i.next() else { break };
|
||||
if { i }.next().is_none() {
|
||||
// There's only one child; move it up to be the new root.
|
||||
*self = mem::take(x);
|
||||
continue;
|
||||
}
|
||||
// Rotate the tree right:
|
||||
// ```text
|
||||
// a b
|
||||
// / \ / \
|
||||
// b e -> c a
|
||||
// / \ / \
|
||||
// c d d e
|
||||
// ```
|
||||
let d = mem::take(x.children_mut().next_back().unwrap());
|
||||
let b = mem::replace(x, d);
|
||||
let a = mem::replace(self, b);
|
||||
mem::forget(mem::replace(self.children_mut().next_back().unwrap(), a));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// new_parser!(HvmcParser);
|
||||
pub struct HvmcParser<'i> {
|
||||
input: &'i str,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl<'i> Parser<'i> for HvmcParser<'i> {
|
||||
fn input(&mut self) -> &'i str {
|
||||
self.input
|
||||
}
|
||||
|
||||
fn index(&mut self) -> &mut usize {
|
||||
&mut self.index
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> HvmcParser<'i> {
|
||||
pub fn new(input: &'i str) -> Self {
|
||||
Self { input, index: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> HvmcParser<'i> {
|
||||
/// Book = ("@" Name "=" Net)*
|
||||
fn parse_book(&mut self) -> Result<Book, String> {
|
||||
maybe_grow(move || {
|
||||
let mut book = BTreeMap::new();
|
||||
while self.consume("@").is_ok() {
|
||||
let name = self.parse_name()?;
|
||||
self.consume("=")?;
|
||||
let net = self.parse_net()?;
|
||||
book.insert(name, net);
|
||||
}
|
||||
Ok(Book { nets: book })
|
||||
})
|
||||
}
|
||||
|
||||
/// Net = Tree ("&" Tree "~" Tree)*
|
||||
fn parse_net(&mut self) -> Result<Net, String> {
|
||||
let mut redexes = Vec::new();
|
||||
let root = self.parse_tree()?;
|
||||
while self.consume("&").is_ok() {
|
||||
let pri = if self.peek_one() == Some('!') {
|
||||
self.advance_one();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let tree1 = self.parse_tree()?;
|
||||
self.consume("~")?;
|
||||
let tree2 = self.parse_tree()?;
|
||||
redexes.push((pri, tree1, tree2));
|
||||
}
|
||||
Ok(Net { root, redexes })
|
||||
}
|
||||
|
||||
fn parse_tree(&mut self) -> Result<Tree, String> {
|
||||
maybe_grow(move || {
|
||||
self.skip_trivia();
|
||||
match self.peek_one() {
|
||||
Some('*') => {
|
||||
self.advance_one();
|
||||
Ok(Tree::Era)
|
||||
}
|
||||
Some('(') => {
|
||||
self.advance_one();
|
||||
let fst = self.parse_tree()?;
|
||||
self.skip_trivia();
|
||||
let snd = self.parse_tree()?;
|
||||
self.consume(")")?;
|
||||
Ok(Tree::Ctr { lab: 0, ports: vec![fst, snd] })
|
||||
}
|
||||
Some('{') => {
|
||||
self.advance_one();
|
||||
let fst = self.parse_tree()?;
|
||||
self.skip_trivia();
|
||||
let snd = self.parse_tree()?;
|
||||
self.consume("}")?;
|
||||
Ok(Tree::Ctr { lab: 1, ports: vec![fst, snd] })
|
||||
}
|
||||
Some('@') => {
|
||||
self.advance_one();
|
||||
self.skip_trivia();
|
||||
let nam = self.parse_name()?;
|
||||
Ok(Tree::Ref { nam })
|
||||
}
|
||||
Some('$') => {
|
||||
self.advance_one();
|
||||
self.consume("(")?;
|
||||
let fst = Box::new(self.parse_tree()?);
|
||||
self.skip_trivia();
|
||||
let snd = Box::new(self.parse_tree()?);
|
||||
self.consume(")")?;
|
||||
Ok(Tree::Op { fst, snd })
|
||||
}
|
||||
Some('?') => {
|
||||
self.advance_one();
|
||||
self.consume("(")?;
|
||||
let zero = self.parse_tree()?;
|
||||
let succ = self.parse_tree()?;
|
||||
self.skip_trivia();
|
||||
if self.peek_one() == Some(')') {
|
||||
self.advance_one();
|
||||
Tree::legacy_mat(zero, succ).ok_or_else(|| "invalid legacy match".to_owned())
|
||||
} else {
|
||||
let zero = Box::new(zero);
|
||||
let succ = Box::new(succ);
|
||||
let out = Box::new(self.parse_tree()?);
|
||||
self.consume(">")?;
|
||||
Ok(Tree::Mat { zero, succ, out })
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if let Some(c) = self.peek_one() {
|
||||
if "0123456789+-[".contains(c) {
|
||||
return Ok(Tree::Num { val: self.parse_numb()? });
|
||||
}
|
||||
}
|
||||
let nam = self.parse_name()?;
|
||||
Ok(Tree::Var { nam })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_numb_sym(&mut self) -> Result<u32, String> {
|
||||
self.consume("[")?;
|
||||
|
||||
// Parses the symbol
|
||||
let op = new_sym(match () {
|
||||
_ if self.try_consume("+") => OP_ADD,
|
||||
_ if self.try_consume("-") => OP_SUB,
|
||||
_ if self.try_consume("*") => OP_MUL,
|
||||
_ if self.try_consume("/") => OP_DIV,
|
||||
_ if self.try_consume("%") => OP_REM,
|
||||
_ if self.try_consume("=") => OP_EQ,
|
||||
_ if self.try_consume("!") => OP_NEQ,
|
||||
_ if self.try_consume("<") => OP_LT,
|
||||
_ if self.try_consume(">") => OP_GT,
|
||||
_ if self.try_consume("&") => OP_AND,
|
||||
_ if self.try_consume("|") => OP_OR,
|
||||
_ if self.try_consume("^") => OP_XOR,
|
||||
_ if self.try_consume(">>") => OP_SHR,
|
||||
_ if self.try_consume("<<") => OP_SHL,
|
||||
_ if self.try_consume(":-") => FP_SUB,
|
||||
_ if self.try_consume(":/") => FP_DIV,
|
||||
_ if self.try_consume(":%") => FP_REM,
|
||||
_ if self.try_consume(":>>") => FP_SHR,
|
||||
_ if self.try_consume(":<<") => FP_SHL,
|
||||
_ => self.expected("operator symbol")?,
|
||||
});
|
||||
self.skip_trivia();
|
||||
|
||||
// Syntax for partial operations, like `[*2]`
|
||||
let num = if self.peek_one() != Some(']') { partial_opr(op, self.parse_numb_lit()?) } else { op };
|
||||
|
||||
// Closes symbol bracket
|
||||
self.consume("]")?;
|
||||
|
||||
// Returns the symbol
|
||||
Ok(num)
|
||||
}
|
||||
|
||||
pub fn parse_numb_lit(&mut self) -> Result<u32, String> {
|
||||
let num = self.take_while(|x| x.is_alphanumeric() || x == '+' || x == '-' || x == '.');
|
||||
|
||||
Ok(if num.contains('.') || num.contains("inf") || num.contains("NaN") {
|
||||
let val: f32 = num.parse().map_err(|err| format!("invalid number literal: {err}"))?;
|
||||
new_f24(val)
|
||||
} else if num.starts_with('+') || num.starts_with('-') {
|
||||
let val = Self::parse_int(&num[1 ..])? as i32;
|
||||
new_i24(if num.starts_with('-') { -val } else { val })
|
||||
} else {
|
||||
let val = Self::parse_int(num)? as u32;
|
||||
new_u24(val)
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_int(input: &str) -> Result<u64, String> {
|
||||
if let Some(rest) = input.strip_prefix("0x") {
|
||||
u64::from_str_radix(rest, 16).map_err(|err| format!("{err:?}"))
|
||||
} else if let Some(rest) = input.strip_prefix("0b") {
|
||||
u64::from_str_radix(rest, 2).map_err(|err| format!("{err:?}"))
|
||||
} else {
|
||||
input.parse::<u64>().map_err(|err| format!("{err:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_numb(&mut self) -> Result<u32, String> {
|
||||
self.skip_trivia();
|
||||
|
||||
// Parses symbols (SYM)
|
||||
if let Some('[') = self.peek_one() {
|
||||
self.parse_numb_sym()
|
||||
// Parses numbers (U24,I24,F24)
|
||||
} else {
|
||||
self.parse_numb_lit()
|
||||
}
|
||||
}
|
||||
|
||||
/// Name = /[a-zA-Z0-9_.$]+/
|
||||
fn parse_name(&mut self) -> Result<String, String> {
|
||||
let name = self.take_while(|c| c.is_alphanumeric() || c == '_' || c == '.' || c == '-' || c == '/');
|
||||
if name.is_empty() {
|
||||
return self.expected("name");
|
||||
}
|
||||
Ok(name.to_owned())
|
||||
}
|
||||
|
||||
fn try_consume(&mut self, str: &str) -> bool {
|
||||
let matches = self.peek_many(str.len()) == Some(str);
|
||||
if matches {
|
||||
self.advance_many(str.len());
|
||||
}
|
||||
matches
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses the input with the callback, ensuring that the whole input is
|
||||
/// consumed.
|
||||
fn parse_eof<'i, T>(
|
||||
input: &'i str,
|
||||
parse_fn: impl Fn(&mut HvmcParser<'i>) -> Result<T, String>,
|
||||
) -> Result<T, String> {
|
||||
let mut parser = HvmcParser::new(input);
|
||||
let out = parse_fn(&mut parser)?;
|
||||
if parser.index != parser.input.len() {
|
||||
return Err("Unable to parse the whole input. Is this not an hvmc file?".to_owned());
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
impl FromStr for Book {
|
||||
type Err = String;
|
||||
fn from_str(str: &str) -> Result<Self, Self::Err> {
|
||||
parse_eof(str, HvmcParser::parse_book)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Net {
|
||||
type Err = String;
|
||||
fn from_str(str: &str) -> Result<Self, Self::Err> {
|
||||
parse_eof(str, HvmcParser::parse_net)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Tree {
|
||||
type Err = String;
|
||||
fn from_str(str: &str) -> Result<Self, Self::Err> {
|
||||
parse_eof(str, HvmcParser::parse_tree)
|
||||
}
|
||||
}
|
@ -1,12 +1,13 @@
|
||||
use super::ast::Book;
|
||||
use super::tree_children;
|
||||
use crate::{diagnostics::Diagnostics, fun::Name};
|
||||
use hvm::ast::{Book, Net, Tree};
|
||||
|
||||
pub const MAX_NET_SIZE: usize = 64;
|
||||
|
||||
pub fn check_net_sizes(book: &Book, diagnostics: &mut Diagnostics) -> Result<(), Diagnostics> {
|
||||
diagnostics.start_pass();
|
||||
|
||||
for (name, net) in &book.nets {
|
||||
for (name, net) in &book.defs {
|
||||
let nodes = count_nodes(net);
|
||||
if nodes > MAX_NET_SIZE {
|
||||
diagnostics.add_rule_error(
|
||||
@ -20,19 +21,19 @@ pub fn check_net_sizes(book: &Book, diagnostics: &mut Diagnostics) -> Result<(),
|
||||
}
|
||||
|
||||
/// Utility function to count the amount of nodes in an hvm-core AST net
|
||||
pub fn count_nodes<'l>(net: &'l super::ast::Net) -> usize {
|
||||
let mut visit: Vec<&'l super::ast::Tree> = vec![&net.root];
|
||||
pub fn count_nodes(net: &Net) -> usize {
|
||||
let mut visit: Vec<&Tree> = vec![&net.root];
|
||||
let mut count = 0usize;
|
||||
for (_, l, r) in &net.redexes {
|
||||
for (_, l, r) in &net.rbag {
|
||||
visit.push(l);
|
||||
visit.push(r);
|
||||
}
|
||||
while let Some(tree) = visit.pop() {
|
||||
// If it is not 0-ary, then we'll count it as a node.
|
||||
if tree.children().next().is_some() {
|
||||
if tree_children(tree).next().is_some() {
|
||||
count += 1;
|
||||
}
|
||||
for subtree in tree.children() {
|
||||
for subtree in tree_children(tree) {
|
||||
visit.push(subtree);
|
||||
}
|
||||
}
|
||||
|
@ -53,21 +53,22 @@
|
||||
//!
|
||||
//! The pass also reduces subnets such as `(* *) -> *`
|
||||
|
||||
use crate::hvm::ast::{Net, Tree};
|
||||
use crate::hvm::net_trees_mut;
|
||||
|
||||
use super::{tree_children, tree_children_mut};
|
||||
use core::ops::RangeFrom;
|
||||
use hvm::ast::{Net, Tree};
|
||||
use std::collections::HashMap;
|
||||
|
||||
impl Net {
|
||||
/// Carries out simple eta-reduction
|
||||
pub fn eta_reduce(&mut self) {
|
||||
let mut phase1 = Phase1::default();
|
||||
for tree in self.trees() {
|
||||
phase1.walk_tree(tree);
|
||||
}
|
||||
let mut phase2 = Phase2 { nodes: phase1.nodes, index: 0 .. };
|
||||
for tree in self.trees_mut() {
|
||||
phase2.reduce_tree(tree);
|
||||
}
|
||||
/// Carries out simple eta-reduction
|
||||
pub fn eta_reduce_hvm_net(net: &mut Net) {
|
||||
let mut phase1 = Phase1::default();
|
||||
for tree in net_trees_mut(net) {
|
||||
phase1.walk_tree(tree);
|
||||
}
|
||||
let mut phase2 = Phase2 { nodes: phase1.nodes, index: 0 .. };
|
||||
for tree in net_trees_mut(net) {
|
||||
phase2.reduce_tree(tree);
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,13 +76,12 @@ impl Net {
|
||||
enum NodeType {
|
||||
Ctr(u16),
|
||||
Var(isize),
|
||||
Num(u32),
|
||||
Era,
|
||||
Other,
|
||||
Hole,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default, Debug)]
|
||||
struct Phase1<'a> {
|
||||
vars: HashMap<&'a str, usize>,
|
||||
nodes: Vec<NodeType>,
|
||||
@ -90,14 +90,15 @@ struct Phase1<'a> {
|
||||
impl<'a> Phase1<'a> {
|
||||
fn walk_tree(&mut self, tree: &'a Tree) {
|
||||
match tree {
|
||||
Tree::Ctr { lab, ports } => {
|
||||
let last_port = ports.len() - 1;
|
||||
for (idx, i) in ports.iter().enumerate() {
|
||||
if idx != last_port {
|
||||
self.nodes.push(NodeType::Ctr(*lab));
|
||||
}
|
||||
self.walk_tree(i);
|
||||
}
|
||||
Tree::Con { fst, snd } => {
|
||||
self.nodes.push(NodeType::Ctr(0));
|
||||
self.walk_tree(fst);
|
||||
self.walk_tree(snd);
|
||||
}
|
||||
Tree::Dup { fst, snd } => {
|
||||
self.nodes.push(NodeType::Ctr(1));
|
||||
self.walk_tree(fst);
|
||||
self.walk_tree(snd);
|
||||
}
|
||||
Tree::Var { nam } => {
|
||||
if let Some(i) = self.vars.get(&**nam) {
|
||||
@ -110,10 +111,9 @@ impl<'a> Phase1<'a> {
|
||||
}
|
||||
}
|
||||
Tree::Era => self.nodes.push(NodeType::Era),
|
||||
Tree::Num { val } => self.nodes.push(NodeType::Num(*val)),
|
||||
_ => {
|
||||
self.nodes.push(NodeType::Other);
|
||||
for i in tree.children() {
|
||||
for i in tree_children(tree) {
|
||||
self.walk_tree(i);
|
||||
}
|
||||
}
|
||||
@ -127,42 +127,42 @@ struct Phase2 {
|
||||
}
|
||||
|
||||
impl Phase2 {
|
||||
fn reduce_ctr(&mut self, lab: u16, ports: &mut Vec<Tree>, skip: usize) -> NodeType {
|
||||
if skip == ports.len() {
|
||||
return NodeType::Other;
|
||||
}
|
||||
if skip == ports.len() - 1 {
|
||||
return self.reduce_tree(&mut ports[skip]);
|
||||
}
|
||||
let head_index = self.index.next().unwrap();
|
||||
let a = self.reduce_tree(&mut ports[skip]);
|
||||
let b = self.reduce_ctr(lab, ports, skip + 1);
|
||||
if a == b {
|
||||
let reducible = match a {
|
||||
NodeType::Var(delta) => self.nodes[head_index.wrapping_add_signed(delta)] == NodeType::Ctr(lab),
|
||||
NodeType::Era => true,
|
||||
_ => false,
|
||||
};
|
||||
if reducible {
|
||||
ports.pop();
|
||||
return a;
|
||||
fn reduce_ctr(&mut self, tree: &mut Tree, idx: usize) -> NodeType {
|
||||
if let Tree::Con { fst, snd } | Tree::Dup { fst, snd } = tree {
|
||||
let fst_typ = self.reduce_tree(fst);
|
||||
let snd_typ = self.reduce_tree(snd);
|
||||
// If both children are variables with the same offset, and their parent is a ctr of the same label,
|
||||
// then they are eta-reducible and we replace the current node with the first variable.
|
||||
match (fst_typ, snd_typ) {
|
||||
(NodeType::Var(off_lft), NodeType::Var(off_rgt)) => {
|
||||
if off_lft == off_rgt && self.nodes[idx] == self.nodes[(idx as isize + off_lft) as usize] {
|
||||
let Tree::Var { nam } = fst.as_mut() else { unreachable!() };
|
||||
*tree = Tree::Var { nam: std::mem::take(nam) };
|
||||
return NodeType::Var(off_lft);
|
||||
}
|
||||
}
|
||||
(NodeType::Era, NodeType::Era) => {
|
||||
*tree = Tree::Era;
|
||||
return NodeType::Era;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
NodeType::Ctr(lab)
|
||||
}
|
||||
fn reduce_tree(&mut self, tree: &mut Tree) -> NodeType {
|
||||
if let Tree::Ctr { lab, ports } = tree {
|
||||
let ty = self.reduce_ctr(*lab, ports, 0);
|
||||
if ports.len() == 1 {
|
||||
*tree = ports.pop().unwrap();
|
||||
}
|
||||
ty
|
||||
self.nodes[idx]
|
||||
} else {
|
||||
let index = self.index.next().unwrap();
|
||||
for i in tree.children_mut() {
|
||||
self.reduce_tree(i);
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
fn reduce_tree(&mut self, tree: &mut Tree) -> NodeType {
|
||||
let idx = self.index.next().unwrap();
|
||||
match tree {
|
||||
Tree::Con { .. } | Tree::Dup { .. } => self.reduce_ctr(tree, idx),
|
||||
_ => {
|
||||
for child in tree_children_mut(tree) {
|
||||
self.reduce_tree(child);
|
||||
}
|
||||
self.nodes[idx]
|
||||
}
|
||||
self.nodes[index]
|
||||
}
|
||||
}
|
||||
}
|
75
src/hvm/inline.rs
Normal file
75
src/hvm/inline.rs
Normal file
@ -0,0 +1,75 @@
|
||||
use super::{net_trees_mut, tree_children, tree_children_mut};
|
||||
use crate::maybe_grow;
|
||||
use core::ops::BitOr;
|
||||
use hvm::ast::{Book, Net, Tree};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
pub fn inline_hvm_book(book: &mut Book) -> Result<HashSet<String>, String> {
|
||||
let mut state = InlineState::default();
|
||||
state.populate_inlinees(book)?;
|
||||
let mut all_changed = HashSet::new();
|
||||
for (name, net) in &mut book.defs {
|
||||
let mut inlined = false;
|
||||
for tree in net_trees_mut(net) {
|
||||
inlined |= state.inline_into(tree);
|
||||
}
|
||||
if inlined {
|
||||
all_changed.insert(name.to_owned());
|
||||
}
|
||||
}
|
||||
Ok(all_changed)
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct InlineState {
|
||||
inlinees: HashMap<String, Tree>,
|
||||
}
|
||||
|
||||
impl InlineState {
|
||||
fn populate_inlinees(&mut self, book: &Book) -> Result<(), String> {
|
||||
for (name, net) in &book.defs {
|
||||
if should_inline(net) {
|
||||
// Detect cycles with tortoise and hare algorithm
|
||||
let mut hare = &net.root;
|
||||
let mut tortoise = &net.root;
|
||||
// Whether or not the tortoise should take a step
|
||||
let mut parity = false;
|
||||
while let Tree::Ref { nam, .. } = hare {
|
||||
let Some(net) = &book.defs.get(nam) else { break };
|
||||
if should_inline(net) {
|
||||
hare = &net.root;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
if parity {
|
||||
let Tree::Ref { nam: tortoise_nam, .. } = tortoise else { unreachable!() };
|
||||
if tortoise_nam == nam {
|
||||
Err(format!("infinite reference cycle in `@{nam}`"))?;
|
||||
}
|
||||
tortoise = &book.defs[tortoise_nam].root;
|
||||
}
|
||||
parity = !parity;
|
||||
}
|
||||
self.inlinees.insert(name.to_owned(), hare.clone());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn inline_into(&self, tree: &mut Tree) -> bool {
|
||||
maybe_grow(|| {
|
||||
let Tree::Ref { nam, .. } = &*tree else {
|
||||
return tree_children_mut(tree).map(|t| self.inline_into(t)).fold(false, bool::bitor);
|
||||
};
|
||||
if let Some(inlined) = self.inlinees.get(nam) {
|
||||
*tree = inlined.clone();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn should_inline(net: &Net) -> bool {
|
||||
net.rbag.is_empty() && tree_children(&net.root).next().is_none()
|
||||
}
|
154
src/hvm/mod.rs
154
src/hvm/mod.rs
@ -1,7 +1,153 @@
|
||||
use crate::{fun::display::DisplayFn, multi_iterator};
|
||||
use hvm::ast::{Net, Tree};
|
||||
|
||||
pub mod add_recursive_priority;
|
||||
pub mod ast;
|
||||
pub mod check_net_size;
|
||||
pub mod eta_reduce;
|
||||
pub mod inline;
|
||||
pub mod mutual_recursion;
|
||||
pub mod ops;
|
||||
pub mod transform;
|
||||
pub mod util;
|
||||
pub mod prune;
|
||||
|
||||
pub fn tree_children(tree: &Tree) -> impl DoubleEndedIterator<Item = &Tree> + Clone {
|
||||
multi_iterator!(ChildrenIter { Zero, Two });
|
||||
match tree {
|
||||
Tree::Var { .. } | Tree::Ref { .. } | Tree::Era | Tree::Num { .. } => ChildrenIter::Zero([]),
|
||||
Tree::Con { fst, snd } | Tree::Dup { fst, snd } | Tree::Opr { fst, snd } | Tree::Swi { fst, snd } => {
|
||||
ChildrenIter::Two([fst.as_ref(), snd.as_ref()])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tree_children_mut(tree: &mut Tree) -> impl DoubleEndedIterator<Item = &mut Tree> {
|
||||
multi_iterator!(ChildrenIter { Zero, Two });
|
||||
match tree {
|
||||
Tree::Var { .. } | Tree::Ref { .. } | Tree::Era | Tree::Num { .. } => ChildrenIter::Zero([]),
|
||||
Tree::Con { fst, snd } | Tree::Dup { fst, snd } | Tree::Opr { fst, snd } | Tree::Swi { fst, snd } => {
|
||||
ChildrenIter::Two([fst.as_mut(), snd.as_mut()])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn net_trees(net: &Net) -> impl DoubleEndedIterator<Item = &Tree> + Clone {
|
||||
[&net.root].into_iter().chain(net.rbag.iter().flat_map(|(_, fst, snd)| [fst, snd]))
|
||||
}
|
||||
|
||||
pub fn net_trees_mut(net: &mut Net) -> impl DoubleEndedIterator<Item = &mut Tree> {
|
||||
[&mut net.root].into_iter().chain(net.rbag.iter_mut().flat_map(|(_, fst, snd)| [fst, snd]))
|
||||
}
|
||||
|
||||
pub fn display_hvm_book(book: &hvm::ast::Book) -> impl std::fmt::Display + '_ {
|
||||
DisplayFn(|f| {
|
||||
for (nam, def) in book.defs.iter() {
|
||||
writeln!(f, "@{} = {}", nam, display_hvm_tree(&def.root))?;
|
||||
for (pri, a, b) in def.rbag.iter() {
|
||||
writeln!(f, " &{}{} ~ {}", if *pri { "!" } else { " " }, display_hvm_tree(a), display_hvm_tree(b))?;
|
||||
}
|
||||
writeln!(f)?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: We have to reimplement these because hvm prints partially applied numbers incorrectly.
|
||||
// https://github.com/HigherOrderCO/HVM/issues/350
|
||||
pub fn display_hvm_numb(numb: &hvm::ast::Numb) -> impl std::fmt::Display + '_ {
|
||||
let numb = hvm::hvm::Numb(numb.0);
|
||||
match numb.get_typ() {
|
||||
hvm::hvm::TY_SYM => match numb.get_sym() as hvm::hvm::Tag {
|
||||
hvm::hvm::OP_ADD => "[+]".to_string(),
|
||||
hvm::hvm::OP_SUB => "[-]".to_string(),
|
||||
hvm::hvm::FP_SUB => "[:-]".to_string(),
|
||||
hvm::hvm::OP_MUL => "[*]".to_string(),
|
||||
hvm::hvm::OP_DIV => "[/]".to_string(),
|
||||
hvm::hvm::FP_DIV => "[:/]".to_string(),
|
||||
hvm::hvm::OP_REM => "[%]".to_string(),
|
||||
hvm::hvm::FP_REM => "[:%]".to_string(),
|
||||
hvm::hvm::OP_EQ => "[=]".to_string(),
|
||||
hvm::hvm::OP_NEQ => "[!]".to_string(),
|
||||
hvm::hvm::OP_LT => "[<]".to_string(),
|
||||
hvm::hvm::OP_GT => "[>]".to_string(),
|
||||
hvm::hvm::OP_AND => "[&]".to_string(),
|
||||
hvm::hvm::OP_OR => "[|]".to_string(),
|
||||
hvm::hvm::OP_XOR => "[^]".to_string(),
|
||||
hvm::hvm::OP_SHL => "[<<]".to_string(),
|
||||
hvm::hvm::FP_SHL => "[:<<]".to_string(),
|
||||
hvm::hvm::OP_SHR => "[>>]".to_string(),
|
||||
hvm::hvm::FP_SHR => "[:>>]".to_string(),
|
||||
_ => "[?]".to_string(),
|
||||
},
|
||||
hvm::hvm::TY_U24 => {
|
||||
let val = numb.get_u24();
|
||||
format!("{}", val)
|
||||
}
|
||||
hvm::hvm::TY_I24 => {
|
||||
let val = numb.get_i24();
|
||||
format!("{:+}", val)
|
||||
}
|
||||
hvm::hvm::TY_F24 => {
|
||||
let val = numb.get_f24();
|
||||
if val.is_infinite() {
|
||||
if val.is_sign_positive() { "+inf".to_string() } else { "-inf".to_string() }
|
||||
} else if val.is_nan() {
|
||||
"+NaN".to_string()
|
||||
} else {
|
||||
format!("{:?}", val)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let typ = numb.get_typ();
|
||||
let val = numb.get_u24();
|
||||
format!(
|
||||
"[{}{}]",
|
||||
match typ {
|
||||
hvm::hvm::OP_ADD => "+",
|
||||
hvm::hvm::OP_SUB => "-",
|
||||
hvm::hvm::FP_SUB => ":-",
|
||||
hvm::hvm::OP_MUL => "*",
|
||||
hvm::hvm::OP_DIV => "/",
|
||||
hvm::hvm::FP_DIV => ":/",
|
||||
hvm::hvm::OP_REM => "%",
|
||||
hvm::hvm::FP_REM => ":%",
|
||||
hvm::hvm::OP_EQ => "=",
|
||||
hvm::hvm::OP_NEQ => "!",
|
||||
hvm::hvm::OP_LT => "<",
|
||||
hvm::hvm::OP_GT => ">",
|
||||
hvm::hvm::OP_AND => "&",
|
||||
hvm::hvm::OP_OR => "|",
|
||||
hvm::hvm::OP_XOR => "^",
|
||||
hvm::hvm::OP_SHL => "<<",
|
||||
hvm::hvm::FP_SHL => ":<<",
|
||||
hvm::hvm::OP_SHR => ">>",
|
||||
hvm::hvm::FP_SHR => ":>>",
|
||||
_ => "?",
|
||||
},
|
||||
val
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn display_hvm_tree(tree: &hvm::ast::Tree) -> impl std::fmt::Display + '_ {
|
||||
match tree {
|
||||
Tree::Var { nam } => nam.to_string(),
|
||||
Tree::Ref { nam } => format!("@{}", nam),
|
||||
Tree::Era => "*".to_string(),
|
||||
Tree::Num { val } => format!("{}", display_hvm_numb(val)),
|
||||
Tree::Con { fst, snd } => format!("({} {})", display_hvm_tree(fst), display_hvm_tree(snd)),
|
||||
Tree::Dup { fst, snd } => format!("{{{} {}}}", display_hvm_tree(fst), display_hvm_tree(snd)),
|
||||
Tree::Opr { fst, snd } => format!("$({} {})", display_hvm_tree(fst), display_hvm_tree(snd)),
|
||||
Tree::Swi { fst, snd } => format!("?({} {})", display_hvm_tree(fst), display_hvm_tree(snd)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn display_hvm_net(net: &hvm::ast::Net) -> impl std::fmt::Display + '_ {
|
||||
let mut s = display_hvm_tree(&net.root).to_string();
|
||||
for (par, fst, snd) in &net.rbag {
|
||||
s.push_str(" & ");
|
||||
s.push_str(if *par { "!" } else { "" });
|
||||
s.push_str(&display_hvm_tree(fst).to_string());
|
||||
s.push_str(" ~ ");
|
||||
s.push_str(&display_hvm_tree(snd).to_string());
|
||||
}
|
||||
s
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ It is lazy when it's an argument ('(x Foo)') or when it's used linearly ('let x
|
||||
to:
|
||||
'Foo = λa λb (b (λc λa (Foo a c)) (λa a) a)'
|
||||
which is lifted to:
|
||||
'Foo = λa λb (b Foo__C1 Foo_C2 a)'
|
||||
'Foo = λa λb (b Foo__C1 Foo__C2 a)'
|
||||
- Replace non-linear 'let' expressions with 'use' expressions. For example, change:
|
||||
'Foo = λf let x = Foo; (f x x)'
|
||||
to:
|
||||
@ -22,5 +22,5 @@ It is lazy when it's an argument ('(x Foo)') or when it's used linearly ('let x
|
||||
'Foo = λf (f Foo Foo)'
|
||||
- If disabled, re-enable the default 'float-combinators' and 'linearize-matches' compiler options.
|
||||
|
||||
For more information, visit: https://github.com/HigherOrderCO/hvm-lang/blob/main/docs/lazy-definitions.md.
|
||||
For more information, visit: https://github.com/HigherOrderCO/Bend/blob/main/docs/lazy-definitions.md.
|
||||
To disable this check, use the "-Arecursion-cycle" compiler option.
|
||||
|
@ -1,13 +1,13 @@
|
||||
use super::tree_children;
|
||||
use crate::{
|
||||
diagnostics::{Diagnostics, WarningType, ERR_INDENT_SIZE},
|
||||
fun::transform::definition_merge::MERGE_SEPARATOR,
|
||||
maybe_grow,
|
||||
};
|
||||
use hvm::ast::{Book, Tree};
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
use std::fmt::Debug;
|
||||
|
||||
use super::ast::{Book, Tree};
|
||||
|
||||
type Ref = String;
|
||||
type Stack<T> = Vec<T>;
|
||||
type RefSet = IndexSet<Ref>;
|
||||
@ -109,13 +109,9 @@ impl Graph {
|
||||
fn collect_refs(current: Ref, tree: &Tree, graph: &mut Graph) {
|
||||
maybe_grow(|| match tree {
|
||||
Tree::Ref { nam, .. } => graph.add(current, nam.clone()),
|
||||
Tree::Ctr { ports, .. } => {
|
||||
if let Some(last) = ports.last() {
|
||||
collect_refs(current, last, graph);
|
||||
}
|
||||
}
|
||||
Tree::Con { fst: _, snd } => collect_refs(current.clone(), snd, graph),
|
||||
tree => {
|
||||
for subtree in tree.children() {
|
||||
for subtree in tree_children(tree) {
|
||||
collect_refs(current.clone(), subtree, graph);
|
||||
}
|
||||
}
|
||||
@ -126,12 +122,12 @@ impl From<&Book> for Graph {
|
||||
fn from(book: &Book) -> Self {
|
||||
let mut graph = Self::new();
|
||||
|
||||
for (r#ref, net) in book.iter() {
|
||||
for (r#ref, net) in book.defs.iter() {
|
||||
// Collect active refs from the root.
|
||||
collect_refs(r#ref.clone(), &net.root, &mut graph);
|
||||
|
||||
// Collect active refs from redexes.
|
||||
for (_, left, right) in net.redexes.iter() {
|
||||
for (_, left, right) in net.rbag.iter() {
|
||||
if let Tree::Ref { nam, .. } = left {
|
||||
graph.add(r#ref.clone(), nam.clone());
|
||||
}
|
||||
|
222
src/hvm/ops.rs
222
src/hvm/ops.rs
@ -1,222 +0,0 @@
|
||||
mod num;
|
||||
mod word;
|
||||
|
||||
use self::{
|
||||
num::Numeric,
|
||||
word::{FromWord, ToWord},
|
||||
};
|
||||
use core::{
|
||||
cmp::{Eq, Ord},
|
||||
fmt,
|
||||
};
|
||||
use std::mem;
|
||||
|
||||
use super::util::bi_enum;
|
||||
|
||||
bi_enum! {
|
||||
#[repr(u8)]
|
||||
/// The type of a numeric operation.
|
||||
///
|
||||
/// This dictates how the bits of the operands will be interpreted,
|
||||
/// and the return type of the operation.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum Ty {
|
||||
"u8": U8 = 0,
|
||||
"u16": U16 = 1,
|
||||
"u32": U32 = 2,
|
||||
"u60": U60 = 3,
|
||||
"i8": I8 = 4,
|
||||
"i16": I16 = 5,
|
||||
"i32": I32 = 6,
|
||||
"f32": F32 = 7,
|
||||
}
|
||||
}
|
||||
|
||||
impl Ty {
|
||||
#[inline(always)]
|
||||
fn is_int(&self) -> bool {
|
||||
*self < Self::F32
|
||||
}
|
||||
}
|
||||
|
||||
bi_enum! {
|
||||
#[repr(u8)]
|
||||
/// Native operations on numerics (u8, u16, u32, u60, i8, i16, i32, f32).
|
||||
///
|
||||
/// Each operation has a swapped counterpart (accessible with `.swap()`),
|
||||
/// where the order of the operands is swapped.
|
||||
///
|
||||
/// Operations without an already-named counterpart (e.g. `Add <-> Add` and
|
||||
/// `Lt <-> Gt`) are suffixed with `$`/`S`: `(-$ 1 2) = (- 2 1) = 1`.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum Op {
|
||||
"+": Add = 0,
|
||||
"-": Sub = 1,
|
||||
"-$": SubS = 2,
|
||||
"*": Mul = 3,
|
||||
"/": Div = 4,
|
||||
"/$": DivS = 5,
|
||||
"%": Rem = 6,
|
||||
"%$": RemS = 7,
|
||||
"&": And = 8,
|
||||
"|": Or = 9,
|
||||
"^": Xor = 10,
|
||||
"<<": Shl = 11,
|
||||
"<<$": ShlS = 12,
|
||||
">>": Shr = 13,
|
||||
">>$": ShrS = 14,
|
||||
// operators returning ints should go after `Eq`
|
||||
"==": Eq = 15,
|
||||
"!=": Ne = 16,
|
||||
"<": Lt = 17,
|
||||
">": Gt = 18,
|
||||
"<=": Le = 19,
|
||||
">=": Ge = 20,
|
||||
}
|
||||
}
|
||||
|
||||
impl Op {
|
||||
/// Returns this operation's swapped counterpart.
|
||||
///
|
||||
/// For all `op, a, b`, `op.swap().op(a, b) == op.op(b, a)`.
|
||||
#[inline]
|
||||
pub fn swap(self) -> Self {
|
||||
match self {
|
||||
Self::Add => Self::Add,
|
||||
Self::Sub => Self::SubS,
|
||||
Self::SubS => Self::Sub,
|
||||
Self::Mul => Self::Mul,
|
||||
Self::Div => Self::DivS,
|
||||
Self::DivS => Self::Div,
|
||||
Self::Rem => Self::RemS,
|
||||
Self::RemS => Self::Rem,
|
||||
Self::And => Self::And,
|
||||
Self::Or => Self::Or,
|
||||
Self::Xor => Self::Xor,
|
||||
Self::Shl => Self::ShlS,
|
||||
Self::ShlS => Self::Shl,
|
||||
Self::Shr => Self::ShrS,
|
||||
Self::ShrS => Self::Shr,
|
||||
Self::Eq => Self::Eq,
|
||||
Self::Ne => Self::Ne,
|
||||
Self::Lt => Self::Gt,
|
||||
Self::Gt => Self::Lt,
|
||||
Self::Le => Self::Ge,
|
||||
Self::Ge => Self::Le,
|
||||
}
|
||||
}
|
||||
|
||||
fn op<T: Numeric + FromWord + ToWord>(self, a: u64, b: u64) -> u64 {
|
||||
let a = T::from_word(a);
|
||||
let b = T::from_word(b);
|
||||
|
||||
match self {
|
||||
Self::Add => T::add(a, b).to_word(),
|
||||
Self::Sub => T::sub(a, b).to_word(),
|
||||
Self::SubS => T::sub(b, a).to_word(),
|
||||
Self::Mul => T::mul(a, b).to_word(),
|
||||
Self::Div => T::div(a, b).to_word(),
|
||||
Self::DivS => T::div(b, a).to_word(),
|
||||
Self::Rem => T::rem(a, b).to_word(),
|
||||
Self::RemS => T::rem(b, a).to_word(),
|
||||
Self::And => T::and(a, b).to_word(),
|
||||
Self::Or => T::or(a, b).to_word(),
|
||||
Self::Xor => T::xor(a, b).to_word(),
|
||||
Self::Shl => T::shl(a, b).to_word(),
|
||||
Self::ShlS => T::shl(b, a).to_word(),
|
||||
Self::Shr => T::shr(a, b).to_word(),
|
||||
Self::ShrS => T::shr(b, a).to_word(),
|
||||
|
||||
// comparison operators return an integer, which is not necessarily a `T`.
|
||||
Self::Eq => (a == b).into(),
|
||||
Self::Ne => (a != b).into(),
|
||||
Self::Lt => (a < b).into(),
|
||||
Self::Le => (a <= b).into(),
|
||||
Self::Gt => (a > b).into(),
|
||||
Self::Ge => (a >= b).into(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn is_comparison(&self) -> bool {
|
||||
*self >= Self::Eq
|
||||
}
|
||||
}
|
||||
|
||||
/// A numeric operator.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[repr(C, align(2))]
|
||||
pub struct TypedOp {
|
||||
/// The type of the operands.
|
||||
pub ty: Ty,
|
||||
/// The operation. An opaque type whose interpretation depends on `ty`.
|
||||
pub op: Op,
|
||||
}
|
||||
|
||||
impl TypedOp {
|
||||
pub unsafe fn from_unchecked(val: u16) -> Self {
|
||||
mem::transmute(val)
|
||||
}
|
||||
|
||||
/// Whether this operation returns an int.
|
||||
#[inline(always)]
|
||||
pub fn is_int(&self) -> bool {
|
||||
self.ty.is_int() || self.op.is_comparison()
|
||||
}
|
||||
|
||||
pub fn swap(self) -> Self {
|
||||
Self { op: self.op.swap(), ty: self.ty }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn op(self, a: u64, b: u64) -> u64 {
|
||||
const U60: u64 = 0xFFF_FFFF_FFFF_FFFF;
|
||||
|
||||
match self.ty {
|
||||
Ty::I8 => self.op.op::<i8>(a, b),
|
||||
Ty::I16 => self.op.op::<i16>(a, b),
|
||||
Ty::I32 => self.op.op::<i32>(a, b),
|
||||
|
||||
Ty::U8 => self.op.op::<u8>(a, b),
|
||||
Ty::U16 => self.op.op::<u16>(a, b),
|
||||
Ty::U32 => self.op.op::<u32>(a, b),
|
||||
Ty::U60 => self.op.op::<u64>(a, b) & U60,
|
||||
|
||||
Ty::F32 => self.op.op::<f32>(a, b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TypedOp {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self.ty {
|
||||
Ty::U60 => write!(f, "{}", self.op),
|
||||
_ => write!(f, "{}.{}", self.ty, self.op),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u16> for TypedOp {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: u16) -> Result<Self, Self::Error> {
|
||||
let [ty, op] = value.to_ne_bytes();
|
||||
|
||||
Ok(Self { ty: Ty::try_from(ty)?, op: Op::try_from(op)? })
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TypedOp> for u16 {
|
||||
fn from(TypedOp { ty, op }: TypedOp) -> Self {
|
||||
u16::from_ne_bytes([ty as u8, op as u8])
|
||||
}
|
||||
}
|
||||
|
||||
// #[cfg_attr(feature = "std", derive(Error))]
|
||||
// #[derive(Debug)]
|
||||
// pub enum OpParseError {
|
||||
// #[cfg_attr(feature = "std", error("invalid type: {0}"))]
|
||||
// Type(String),
|
||||
// #[cfg_attr(feature = "std", error("invalid operator: {0}"))]
|
||||
// Op(String),
|
||||
// }
|
@ -1,58 +0,0 @@
|
||||
#[rustfmt::skip]
|
||||
pub trait Numeric: PartialEq + PartialOrd + Sized {
|
||||
const ZERO: Self;
|
||||
|
||||
fn add(_: Self, _: Self) -> Self { Self::ZERO }
|
||||
fn sub(_: Self, _: Self) -> Self { Self::ZERO }
|
||||
fn mul(_: Self, _: Self) -> Self { Self::ZERO }
|
||||
fn div(_: Self, _: Self) -> Self { Self::ZERO }
|
||||
fn rem(_: Self, _: Self) -> Self { Self::ZERO }
|
||||
fn and(_: Self, _: Self) -> Self { Self::ZERO }
|
||||
fn or(_: Self, _: Self) -> Self { Self::ZERO }
|
||||
fn xor(_: Self, _: Self) -> Self { Self::ZERO }
|
||||
fn shl(_: Self, _: Self) -> Self { Self::ZERO }
|
||||
fn shr(_: Self, _: Self) -> Self { Self::ZERO }
|
||||
}
|
||||
|
||||
macro_rules! impl_numeric {
|
||||
( $($ty:ty),+ ) => {
|
||||
$(
|
||||
impl Numeric for $ty {
|
||||
const ZERO: Self = 0;
|
||||
|
||||
fn add(a: Self, b: Self) -> Self { a.wrapping_add(b) }
|
||||
fn sub(a: Self, b: Self) -> Self { a.wrapping_sub(b) }
|
||||
fn mul(a: Self, b: Self) -> Self { a.wrapping_mul(b) }
|
||||
fn div(a: Self, b: Self) -> Self { a.checked_div(b).unwrap_or(0) }
|
||||
fn rem(a: Self, b: Self) -> Self { a.checked_rem(b).unwrap_or(0) }
|
||||
fn and(a: Self, b: Self) -> Self { a & b }
|
||||
fn or(a: Self, b: Self) -> Self { a | b }
|
||||
fn xor(a: Self, b: Self) -> Self { a ^ b }
|
||||
fn shl(a: Self, b: Self) -> Self { a.wrapping_shl(b as u32) }
|
||||
fn shr(a: Self, b: Self) -> Self { a.wrapping_shr(b as u32) }
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
impl_numeric! { u8, u16, u32, u64, i8, i16, i32 }
|
||||
|
||||
impl Numeric for f32 {
|
||||
const ZERO: Self = 0.0;
|
||||
|
||||
fn add(a: Self, b: Self) -> Self {
|
||||
a + b
|
||||
}
|
||||
fn sub(a: Self, b: Self) -> Self {
|
||||
a - b
|
||||
}
|
||||
fn mul(a: Self, b: Self) -> Self {
|
||||
a * b
|
||||
}
|
||||
fn div(a: Self, b: Self) -> Self {
|
||||
a / b
|
||||
}
|
||||
fn rem(a: Self, b: Self) -> Self {
|
||||
a % b
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
pub trait FromWord {
|
||||
fn from_word(bits: u64) -> Self;
|
||||
}
|
||||
|
||||
pub trait ToWord {
|
||||
fn to_word(self) -> u64;
|
||||
}
|
||||
|
||||
macro_rules! impl_word {
|
||||
( $($ty:ty),+ ) => {
|
||||
$(
|
||||
impl FromWord for $ty {
|
||||
#[inline(always)]
|
||||
fn from_word(bits: u64) -> Self {
|
||||
bits as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl ToWord for $ty {
|
||||
#[inline(always)]
|
||||
fn to_word(self) -> u64 {
|
||||
self as u64
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
impl_word! { u8, u16, u32, u64, i8, i16, i32 }
|
||||
|
||||
impl FromWord for f32 {
|
||||
#[inline(always)]
|
||||
fn from_word(bits: u64) -> Self {
|
||||
f32::from_bits(bits as u32)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToWord for f32 {
|
||||
#[inline(always)]
|
||||
fn to_word(self) -> u64 {
|
||||
self.to_bits() as u64
|
||||
}
|
||||
}
|
39
src/hvm/prune.rs
Normal file
39
src/hvm/prune.rs
Normal file
@ -0,0 +1,39 @@
|
||||
use super::{net_trees, tree_children};
|
||||
use crate::maybe_grow;
|
||||
use hvm::ast::{Book, Tree};
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub fn prune_hvm_book(book: &mut Book, entrypoints: &[String]) {
|
||||
let mut state = PruneState { book, unvisited: book.defs.keys().map(|x| x.to_owned()).collect() };
|
||||
for name in entrypoints {
|
||||
state.visit_def(name);
|
||||
}
|
||||
let unvisited = state.unvisited;
|
||||
for name in unvisited {
|
||||
book.defs.remove(&name);
|
||||
}
|
||||
}
|
||||
|
||||
struct PruneState<'a> {
|
||||
book: &'a Book,
|
||||
unvisited: HashSet<String>,
|
||||
}
|
||||
|
||||
impl<'a> PruneState<'a> {
|
||||
fn visit_def(&mut self, name: &str) {
|
||||
if self.unvisited.remove(name) {
|
||||
for tree in net_trees(&self.book.defs[name]) {
|
||||
self.visit_tree(tree);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn visit_tree(&mut self, tree: &Tree) {
|
||||
maybe_grow(|| {
|
||||
if let Tree::Ref { nam, .. } = tree {
|
||||
self.visit_def(nam);
|
||||
} else {
|
||||
tree_children(tree).for_each(|t| self.visit_tree(t));
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
use core::fmt;
|
||||
|
||||
pub mod eta_reduce;
|
||||
pub mod inline;
|
||||
pub mod prune;
|
||||
|
||||
pub enum TransformError {
|
||||
InfiniteRefCycle(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for TransformError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
TransformError::InfiniteRefCycle(r) => write!(f, "infinite reference cycle in `@{r}`"),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
use super::TransformError;
|
||||
use crate::{
|
||||
hvm::ast::{Book, Net, Tree},
|
||||
maybe_grow,
|
||||
};
|
||||
use core::ops::BitOr;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
impl Book {
|
||||
pub fn inline(&mut self) -> Result<HashSet<String>, TransformError> {
|
||||
let mut state = InlineState::default();
|
||||
state.populate_inlinees(self)?;
|
||||
let mut all_changed = HashSet::new();
|
||||
for (name, net) in &mut self.nets {
|
||||
let mut inlined = false;
|
||||
for tree in net.trees_mut() {
|
||||
inlined |= state.inline_into(tree);
|
||||
}
|
||||
if inlined {
|
||||
all_changed.insert(name.to_owned());
|
||||
}
|
||||
}
|
||||
Ok(all_changed)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct InlineState {
|
||||
inlinees: HashMap<String, Tree>,
|
||||
}
|
||||
|
||||
impl InlineState {
|
||||
fn populate_inlinees(&mut self, book: &Book) -> Result<(), TransformError> {
|
||||
for (name, net) in &book.nets {
|
||||
if net.should_inline() {
|
||||
// Detect cycles with tortoise and hare algorithm
|
||||
let mut hare = &net.root;
|
||||
let mut tortoise = &net.root;
|
||||
// Whether or not the tortoise should take a step
|
||||
let mut parity = false;
|
||||
while let Tree::Ref { nam, .. } = hare {
|
||||
let Some(net) = &book.nets.get(nam) else { break };
|
||||
if net.should_inline() {
|
||||
hare = &net.root;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
if parity {
|
||||
let Tree::Ref { nam: tortoise_nam, .. } = tortoise else { unreachable!() };
|
||||
if tortoise_nam == nam {
|
||||
Err(TransformError::InfiniteRefCycle(nam.to_owned()))?;
|
||||
}
|
||||
tortoise = &book.nets[tortoise_nam].root;
|
||||
}
|
||||
parity = !parity;
|
||||
}
|
||||
self.inlinees.insert(name.to_owned(), hare.clone());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn inline_into(&self, tree: &mut Tree) -> bool {
|
||||
maybe_grow(|| {
|
||||
let Tree::Ref { nam, .. } = &*tree else {
|
||||
return tree.children_mut().map(|t| self.inline_into(t)).fold(false, bool::bitor);
|
||||
};
|
||||
if let Some(inlined) = self.inlinees.get(nam) {
|
||||
*tree = inlined.clone();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Net {
|
||||
fn should_inline(&self) -> bool {
|
||||
self.redexes.is_empty() && self.root.children().next().is_none()
|
||||
}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::{
|
||||
hvm::ast::{Book, Tree},
|
||||
maybe_grow,
|
||||
};
|
||||
|
||||
impl Book {
|
||||
pub fn prune(&mut self, entrypoints: &[String]) {
|
||||
let mut state = PruneState { book: self, unvisited: self.keys().map(|x| x.to_owned()).collect() };
|
||||
for name in entrypoints {
|
||||
state.visit_def(name);
|
||||
}
|
||||
let unvisited = state.unvisited;
|
||||
for name in unvisited {
|
||||
self.remove(&name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct PruneState<'a> {
|
||||
book: &'a Book,
|
||||
unvisited: HashSet<String>,
|
||||
}
|
||||
|
||||
impl<'a> PruneState<'a> {
|
||||
fn visit_def(&mut self, name: &str) {
|
||||
if self.unvisited.remove(name) {
|
||||
for tree in self.book[name].trees() {
|
||||
self.visit_tree(tree);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn visit_tree(&mut self, tree: &Tree) {
|
||||
maybe_grow(|| {
|
||||
if let Tree::Ref { nam, .. } = tree {
|
||||
self.visit_def(nam);
|
||||
} else {
|
||||
tree.children().for_each(|t| self.visit_tree(t));
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
pub(crate) mod array_vec;
|
||||
pub mod bi_enum;
|
||||
pub mod deref;
|
||||
|
||||
pub(crate) use bi_enum::*;
|
||||
pub(crate) use deref::*;
|
@ -1,29 +0,0 @@
|
||||
use arrayvec::ArrayVec;
|
||||
|
||||
struct Assert<const COND: bool>;
|
||||
|
||||
trait IsTrue {}
|
||||
|
||||
impl IsTrue for Assert<true> {}
|
||||
|
||||
#[allow(private_bounds)]
|
||||
pub(crate) fn from_array<T, const LEN: usize, const CAP: usize>(array: [T; LEN]) -> ArrayVec<T, CAP>
|
||||
where
|
||||
Assert<{ LEN <= CAP }>: IsTrue,
|
||||
{
|
||||
let mut vec = ArrayVec::new();
|
||||
unsafe {
|
||||
for el in array {
|
||||
vec.push_unchecked(el)
|
||||
}
|
||||
}
|
||||
vec
|
||||
}
|
||||
|
||||
pub(crate) fn from_iter<T, const CAP: usize>(iter: impl IntoIterator<Item = T>) -> ArrayVec<T, CAP> {
|
||||
let mut vec = ArrayVec::new();
|
||||
for item in iter {
|
||||
vec.push(item);
|
||||
}
|
||||
vec
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
/// Defines bi-directional mappings for a numeric enum.
|
||||
macro_rules! bi_enum {
|
||||
(
|
||||
#[repr($uN:ident)]
|
||||
$(#$attr:tt)*
|
||||
$vis:vis enum $Ty:ident {
|
||||
$($(#$var_addr:tt)* $Variant:ident = $value:literal),* $(,)?
|
||||
}
|
||||
) => {
|
||||
#[repr($uN)] $(#$attr)* $vis enum $Ty { $($(#$var_addr)* $Variant = $value,)* }
|
||||
|
||||
impl TryFrom<$uN> for $Ty {
|
||||
type Error = ();
|
||||
fn try_from(value: $uN) -> Result<Self, Self::Error> {
|
||||
Ok(match value { $($value => $Ty::$Variant,)* _ => Err(())?, })
|
||||
}
|
||||
}
|
||||
|
||||
impl $Ty {
|
||||
#[allow(unused)]
|
||||
pub unsafe fn from_unchecked(value: $uN) -> $Ty {
|
||||
Self::try_from(value).unwrap_unchecked()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$Ty> for $uN {
|
||||
fn from(value: $Ty) -> Self { value as Self }
|
||||
}
|
||||
};
|
||||
(
|
||||
#[repr($uN:ident)]
|
||||
$(#$attr:tt)*
|
||||
$vis:vis enum $Ty:ident {
|
||||
$($(#$var_addr:tt)* $str:literal: $Variant:ident = $value:literal),* $(,)?
|
||||
}
|
||||
) => {
|
||||
bi_enum! { #[repr($uN)] $(#$attr)* $vis enum $Ty { $($(#$var_addr)* $Variant = $value,)* } }
|
||||
|
||||
impl core::fmt::Display for $Ty {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.write_str(match self { $($Ty::$Variant => $str,)* })
|
||||
}
|
||||
}
|
||||
|
||||
impl core::str::FromStr for $Ty {
|
||||
type Err = ();
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(match s { $($str => $Ty::$Variant,)* _ => Err(())?, })
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use bi_enum;
|
@ -1,17 +0,0 @@
|
||||
macro_rules! deref {
|
||||
($({$($gen:tt)*})? $ty:ty => self.$field:ident: $trg:ty) => {
|
||||
impl $($($gen)*)? core::ops::Deref for $ty {
|
||||
type Target = $trg;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.$field
|
||||
}
|
||||
}
|
||||
impl $($($gen)*)? core::ops::DerefMut for $ty {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.$field
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use deref;
|
56
src/lib.rs
56
src/lib.rs
@ -1,30 +1,32 @@
|
||||
#![feature(box_patterns)]
|
||||
#![feature(let_chains)]
|
||||
#![allow(incomplete_features, clippy::missing_safety_doc, clippy::new_ret_no_self)]
|
||||
#![feature(generic_const_exprs)]
|
||||
|
||||
use crate::fun::{book_to_nets, net_to_term::net_to_term, term_to_net::Labels, Book, Ctx, Term};
|
||||
use diagnostics::{Diagnostics, DiagnosticsConfig, ERR_INDENT_SIZE};
|
||||
use hvm::{
|
||||
add_recursive_priority::add_recursive_priority,
|
||||
ast::Net,
|
||||
check_net_size::{check_net_sizes, MAX_NET_SIZE},
|
||||
mutual_recursion,
|
||||
use crate::{
|
||||
fun::{book_to_hvm, net_to_term::net_to_term, term_to_net::Labels, Book, Ctx, Term},
|
||||
hvm::{
|
||||
add_recursive_priority::add_recursive_priority,
|
||||
check_net_size::{check_net_sizes, MAX_NET_SIZE},
|
||||
display_hvm_book,
|
||||
eta_reduce::eta_reduce_hvm_net,
|
||||
inline::inline_hvm_book,
|
||||
mutual_recursion,
|
||||
prune::prune_hvm_book,
|
||||
},
|
||||
};
|
||||
use net::hvmc_to_net::hvmc_to_net;
|
||||
use std::str::FromStr;
|
||||
use diagnostics::{Diagnostics, DiagnosticsConfig, ERR_INDENT_SIZE};
|
||||
use net::hvm_to_net::hvm_to_net;
|
||||
|
||||
pub mod diagnostics;
|
||||
pub mod fun;
|
||||
pub mod hvm;
|
||||
pub mod imp;
|
||||
pub mod net;
|
||||
mod utils;
|
||||
|
||||
pub use fun::load_book::load_file_to_book;
|
||||
|
||||
pub const ENTRY_POINT: &str = "main";
|
||||
pub const HVM1_ENTRY_POINT: &str = "Main";
|
||||
|
||||
pub const HVM_OUTPUT_END_MARKER: &str = "Result: ";
|
||||
|
||||
pub fn check_book(
|
||||
@ -45,20 +47,21 @@ pub fn compile_book(
|
||||
) -> Result<CompileResult, Diagnostics> {
|
||||
let mut diagnostics = desugar_book(book, opts.clone(), diagnostics_cfg, args)?;
|
||||
|
||||
let (mut hvm_book, labels) = book_to_nets(book, &mut diagnostics)?;
|
||||
let (mut hvm_book, labels) = book_to_hvm(book, &mut diagnostics)?;
|
||||
|
||||
if opts.eta {
|
||||
hvm_book.values_mut().for_each(hvm::ast::Net::eta_reduce);
|
||||
hvm_book.defs.values_mut().for_each(eta_reduce_hvm_net);
|
||||
}
|
||||
|
||||
mutual_recursion::check_cycles(&hvm_book, &mut diagnostics)?;
|
||||
|
||||
if opts.eta {
|
||||
hvm_book.values_mut().for_each(hvm::ast::Net::eta_reduce);
|
||||
hvm_book.defs.values_mut().for_each(eta_reduce_hvm_net);
|
||||
}
|
||||
|
||||
if opts.inline {
|
||||
diagnostics.start_pass();
|
||||
if let Err(e) = hvm_book.inline() {
|
||||
if let Err(e) = inline_hvm_book(&mut hvm_book) {
|
||||
diagnostics.add_book_error(format!("During inlining:\n{:ERR_INDENT_SIZE$}{}", "", e));
|
||||
}
|
||||
diagnostics.fatal(())?;
|
||||
@ -66,7 +69,7 @@ pub fn compile_book(
|
||||
|
||||
if opts.prune {
|
||||
let prune_entrypoints = vec![book.hvmc_entrypoint().to_string()];
|
||||
hvm_book.prune(&prune_entrypoints);
|
||||
prune_hvm_book(&mut hvm_book, &prune_entrypoints);
|
||||
}
|
||||
|
||||
if opts.check_net_size {
|
||||
@ -75,7 +78,7 @@ pub fn compile_book(
|
||||
|
||||
add_recursive_priority(&mut hvm_book);
|
||||
|
||||
Ok(CompileResult { core_book: hvm_book, labels, diagnostics })
|
||||
Ok(CompileResult { hvm_book, labels, diagnostics })
|
||||
}
|
||||
|
||||
pub fn desugar_book(
|
||||
@ -160,7 +163,7 @@ pub fn run_book(
|
||||
args: Option<Vec<Term>>,
|
||||
cmd: &str,
|
||||
) -> Result<Option<(Term, String, Diagnostics)>, Diagnostics> {
|
||||
let CompileResult { core_book, labels, diagnostics } =
|
||||
let CompileResult { hvm_book: core_book, labels, diagnostics } =
|
||||
compile_book(&mut book, compile_opts.clone(), diagnostics_cfg, args)?;
|
||||
|
||||
// TODO: Printing should be taken care by the cli module, but we'd
|
||||
@ -177,14 +180,14 @@ pub fn run_book(
|
||||
}
|
||||
|
||||
pub fn readback_hvm_net(
|
||||
net: &Net,
|
||||
net: &::hvm::ast::Net,
|
||||
book: &Book,
|
||||
labels: &Labels,
|
||||
linear: bool,
|
||||
adt_encoding: AdtEncoding,
|
||||
) -> (Term, Diagnostics) {
|
||||
let mut diags = Diagnostics::default();
|
||||
let net = hvmc_to_net(net);
|
||||
let net = hvm_to_net(net);
|
||||
let mut term = net_to_term(&net, book, labels, linear, &mut diags);
|
||||
term.expand_generated(book);
|
||||
term.resugar_strings(adt_encoding);
|
||||
@ -193,7 +196,7 @@ pub fn readback_hvm_net(
|
||||
}
|
||||
|
||||
/// Runs an HVM book by invoking HVM as a subprocess.
|
||||
fn run_hvm(book: &hvm::ast::Book, cmd: &str) -> Result<String, String> {
|
||||
fn run_hvm(book: &::hvm::ast::Book, cmd: &str) -> Result<String, String> {
|
||||
fn filter_hvm_output(
|
||||
mut stream: impl std::io::Read + Send,
|
||||
mut output: impl std::io::Write + Send,
|
||||
@ -237,7 +240,7 @@ fn run_hvm(book: &hvm::ast::Book, cmd: &str) -> Result<String, String> {
|
||||
}
|
||||
|
||||
let out_path = ".out.hvm";
|
||||
std::fs::write(out_path, book.to_string()).map_err(|x| x.to_string())?;
|
||||
std::fs::write(out_path, display_hvm_book(book).to_string()).map_err(|x| x.to_string())?;
|
||||
let mut process = std::process::Command::new("hvm")
|
||||
.arg(cmd)
|
||||
.arg(out_path)
|
||||
@ -258,14 +261,15 @@ fn run_hvm(book: &hvm::ast::Book, cmd: &str) -> Result<String, String> {
|
||||
}
|
||||
|
||||
/// Reads the final output from HVM and separates the extra information.
|
||||
fn parse_hvm_output(out: &str) -> Result<(Net, String), String> {
|
||||
fn parse_hvm_output(out: &str) -> Result<(::hvm::ast::Net, String), String> {
|
||||
let Some((result, stats)) = out.split_once('\n') else {
|
||||
return Err(format!(
|
||||
"Failed to parse result from HVM (unterminated result).\nOutput from HVM was:\n{:?}",
|
||||
out
|
||||
));
|
||||
};
|
||||
let Ok(net) = hvm::ast::Net::from_str(result) else {
|
||||
let mut p = ::hvm::ast::CoreParser::new(result);
|
||||
let Ok(net) = p.parse_net() else {
|
||||
return Err(format!("Failed to parse result from HVM (invalid net).\nOutput from HVM was:\n{:?}", out));
|
||||
};
|
||||
Ok((net, stats.to_string()))
|
||||
@ -401,7 +405,7 @@ impl std::fmt::Display for AdtEncoding {
|
||||
|
||||
pub struct CompileResult {
|
||||
pub diagnostics: Diagnostics,
|
||||
pub core_book: hvm::ast::Book,
|
||||
pub hvm_book: ::hvm::ast::Book,
|
||||
pub labels: Labels,
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ use bend::{
|
||||
check_book, compile_book, desugar_book,
|
||||
diagnostics::{Diagnostics, DiagnosticsConfig, Severity},
|
||||
fun::{Book, Name},
|
||||
hvm::display_hvm_book,
|
||||
load_file_to_book, run_book, AdtEncoding, CompileOpts, OptLevel, RunOpts,
|
||||
};
|
||||
use clap::{Args, CommandFactory, Parser, Subcommand};
|
||||
@ -230,7 +231,7 @@ pub enum WarningArgs {
|
||||
|
||||
fn main() -> ExitCode {
|
||||
#[cfg(not(feature = "cli"))]
|
||||
compile_error!("The 'cli' feature is needed for the hvm-lang cli");
|
||||
compile_error!("The 'cli' feature is needed for the Bend cli");
|
||||
|
||||
let cli = Cli::parse();
|
||||
|
||||
@ -286,7 +287,7 @@ fn execute_cli_mode(mut cli: Cli) -> Result<(), Diagnostics> {
|
||||
let compile_res = compile_book(&mut book, opts, diagnostics_cfg, None)?;
|
||||
|
||||
eprint!("{}", compile_res.diagnostics);
|
||||
println!("{}", compile_res.core_book);
|
||||
println!("{}", display_hvm_book(&compile_res.hvm_book));
|
||||
}
|
||||
|
||||
Mode::GenC(GenArgs { comp_opts, warn_opts, path })
|
||||
@ -298,7 +299,7 @@ fn execute_cli_mode(mut cli: Cli) -> Result<(), Diagnostics> {
|
||||
let compile_res = compile_book(&mut book, opts, diagnostics_cfg, None)?;
|
||||
|
||||
let out_path = ".out.hvm";
|
||||
std::fs::write(out_path, compile_res.core_book.to_string()).map_err(|x| x.to_string())?;
|
||||
std::fs::write(out_path, compile_res.hvm_book.show()).map_err(|x| x.to_string())?;
|
||||
|
||||
let gen_fn = |out_path: &str| {
|
||||
let mut process = std::process::Command::new("hvm");
|
||||
|
@ -1,17 +1,16 @@
|
||||
use super::{INet, INode, INodes, NodeId, NodeKind::*, Port, SlotId, ROOT};
|
||||
use crate::{
|
||||
fun::Name,
|
||||
hvm,
|
||||
net::{CtrKind, NodeKind},
|
||||
};
|
||||
use hvm::ast::{Net, Tree};
|
||||
|
||||
pub fn hvmc_to_net(net: &Net) -> INet {
|
||||
let inodes = hvmc_to_inodes(net);
|
||||
pub fn hvm_to_net(net: &Net) -> INet {
|
||||
let inodes = hvm_to_inodes(net);
|
||||
inodes_to_inet(&inodes)
|
||||
}
|
||||
|
||||
fn hvmc_to_inodes(net: &Net) -> INodes {
|
||||
fn hvm_to_inodes(net: &Net) -> INodes {
|
||||
let mut inodes = vec![];
|
||||
let mut n_vars = 0;
|
||||
let net_root = if let Tree::Var { nam } = &net.root { nam } else { "" };
|
||||
@ -23,7 +22,7 @@ fn hvmc_to_inodes(net: &Net) -> INodes {
|
||||
}
|
||||
|
||||
// Convert all the trees forming active pairs.
|
||||
for (i, (_, tree1, tree2)) in net.redexes.iter().enumerate() {
|
||||
for (i, (_, tree1, tree2)) in net.rbag.iter().enumerate() {
|
||||
let tree_root = format!("a{i}");
|
||||
let mut tree1 = tree_to_inodes(tree1, tree_root.clone(), net_root, &mut n_vars);
|
||||
inodes.append(&mut tree1);
|
||||
@ -47,55 +46,12 @@ fn tree_to_inodes(tree: &Tree, tree_root: String, net_root: &str, n_vars: &mut N
|
||||
n_vars: &mut NodeId,
|
||||
) -> String {
|
||||
if let Tree::Var { nam } = subtree {
|
||||
return if nam == net_root { "_".to_string() } else { nam.clone() };
|
||||
}
|
||||
if let Tree::Ctr { ports, .. } = subtree {
|
||||
if ports.len() == 1 {
|
||||
return process_node_subtree(&ports[0], net_root, subtrees, n_vars);
|
||||
}
|
||||
}
|
||||
|
||||
let var = new_var(n_vars);
|
||||
subtrees.push((var.clone(), subtree));
|
||||
var
|
||||
}
|
||||
|
||||
fn process_ctr<'a>(
|
||||
inodes: &mut Vec<INode>,
|
||||
lab: u16,
|
||||
ports: &'a [Tree],
|
||||
net_root: &str,
|
||||
principal: String,
|
||||
subtrees: &mut Vec<(String, &'a Tree)>,
|
||||
n_vars: &mut NodeId,
|
||||
) {
|
||||
fn process_sub_ctr<'a>(
|
||||
inodes: &mut Vec<INode>,
|
||||
lab: u16,
|
||||
ports: &'a [Tree],
|
||||
net_root: &str,
|
||||
subtrees: &mut Vec<(String, &'a Tree)>,
|
||||
n_vars: &mut NodeId,
|
||||
) -> String {
|
||||
if ports.len() == 1 {
|
||||
process_node_subtree(&ports[0], net_root, subtrees, n_vars)
|
||||
} else {
|
||||
let principal = new_var(n_vars);
|
||||
process_ctr(inodes, lab, ports, net_root, principal.clone(), subtrees, n_vars);
|
||||
principal
|
||||
}
|
||||
}
|
||||
if ports.is_empty() {
|
||||
let inner = new_var(n_vars);
|
||||
inodes.push(INode { kind: Era, ports: [principal, inner.clone(), inner] });
|
||||
} else if ports.len() == 1 {
|
||||
subtrees.push((principal, &ports[0]));
|
||||
//
|
||||
if nam == net_root { "_".to_string() } else { nam.clone() }
|
||||
} else {
|
||||
// build a 2-ary node
|
||||
let kind = NodeKind::Ctr(CtrKind::from_lab(lab));
|
||||
let rgt = process_sub_ctr(inodes, lab, &ports[1 ..], net_root, subtrees, n_vars);
|
||||
let lft = process_node_subtree(&ports[0], net_root, subtrees, n_vars);
|
||||
inodes.push(INode { kind, ports: [principal.clone(), lft.clone(), rgt.clone()] });
|
||||
let var = new_var(n_vars);
|
||||
subtrees.push((var.clone(), subtree));
|
||||
var
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,8 +63,17 @@ fn tree_to_inodes(tree: &Tree, tree_root: String, net_root: &str, n_vars: &mut N
|
||||
let var = new_var(n_vars);
|
||||
inodes.push(INode { kind: Era, ports: [subtree_root, var.clone(), var] });
|
||||
}
|
||||
Tree::Ctr { lab, ports } => {
|
||||
process_ctr(&mut inodes, *lab, ports, net_root, subtree_root, &mut subtrees, n_vars)
|
||||
Tree::Con { fst, snd } => {
|
||||
let kind = NodeKind::Ctr(CtrKind::Con(None));
|
||||
let fst = process_node_subtree(fst, net_root, &mut subtrees, n_vars);
|
||||
let snd = process_node_subtree(snd, net_root, &mut subtrees, n_vars);
|
||||
inodes.push(INode { kind, ports: [subtree_root, fst, snd] });
|
||||
}
|
||||
Tree::Dup { fst, snd } => {
|
||||
let kind = NodeKind::Ctr(CtrKind::Dup(0));
|
||||
let fst = process_node_subtree(fst, net_root, &mut subtrees, n_vars);
|
||||
let snd = process_node_subtree(snd, net_root, &mut subtrees, n_vars);
|
||||
inodes.push(INode { kind, ports: [subtree_root, fst, snd] });
|
||||
}
|
||||
Tree::Var { .. } => unreachable!(),
|
||||
Tree::Ref { nam } => {
|
||||
@ -117,24 +82,21 @@ fn tree_to_inodes(tree: &Tree, tree_root: String, net_root: &str, n_vars: &mut N
|
||||
inodes.push(INode { kind, ports: [subtree_root, var.clone(), var] });
|
||||
}
|
||||
Tree::Num { val } => {
|
||||
let kind = Num { val: *val };
|
||||
let kind = Num { val: val.0 };
|
||||
let var = new_var(n_vars);
|
||||
inodes.push(INode { kind, ports: [subtree_root, var.clone(), var] });
|
||||
}
|
||||
Tree::Op { fst, snd } => {
|
||||
Tree::Opr { fst, snd } => {
|
||||
let kind = NodeKind::Opr;
|
||||
let fst = process_node_subtree(fst, net_root, &mut subtrees, n_vars);
|
||||
let snd = process_node_subtree(snd, net_root, &mut subtrees, n_vars);
|
||||
inodes.push(INode { kind, ports: [subtree_root, fst, snd] });
|
||||
}
|
||||
Tree::Mat { zero, succ, out } => {
|
||||
let kind = Mat;
|
||||
let zero = process_node_subtree(zero, net_root, &mut subtrees, n_vars);
|
||||
let succ = process_node_subtree(succ, net_root, &mut subtrees, n_vars);
|
||||
let sel_var = new_var(n_vars);
|
||||
inodes.push(INode { kind: NodeKind::Ctr(CtrKind::Con(None)), ports: [sel_var.clone(), zero, succ] });
|
||||
let ret = process_node_subtree(out, net_root, &mut subtrees, n_vars);
|
||||
inodes.push(INode { kind, ports: [subtree_root, sel_var, ret] });
|
||||
Tree::Swi { fst, snd } => {
|
||||
let kind = NodeKind::Mat;
|
||||
let fst = process_node_subtree(fst, net_root, &mut subtrees, n_vars);
|
||||
let snd = process_node_subtree(snd, net_root, &mut subtrees, n_vars);
|
||||
inodes.push(INode { kind, ports: [subtree_root, fst, snd] });
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
pub mod hvmc_to_net;
|
||||
pub mod hvm_to_net;
|
||||
|
||||
use crate::fun::Name;
|
||||
pub type BendLab = u16;
|
||||
@ -58,12 +58,6 @@ impl CtrKind {
|
||||
CtrKind::Dup(_) => todo!("Tagged dups/sups not implemented for hvm32"),
|
||||
}
|
||||
}
|
||||
fn from_lab(lab: u16) -> Self {
|
||||
match lab {
|
||||
0 => CtrKind::Con(None),
|
||||
n => CtrKind::Dup(n - 1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type NodeId = u64;
|
||||
|
38
src/utils.rs
Normal file
38
src/utils.rs
Normal file
@ -0,0 +1,38 @@
|
||||
/// A macro for creating iterators that can have statically known
|
||||
/// different types. Useful for iterating over tree children, where
|
||||
/// each tree node variant yields a different iterator type.
|
||||
#[macro_export]
|
||||
macro_rules! multi_iterator {
|
||||
($Iter:ident { $($Variant:ident),* $(,)? }) => {
|
||||
#[derive(Debug, Clone)]
|
||||
enum $Iter<$($Variant),*> {
|
||||
$($Variant { iter: $Variant }),*
|
||||
}
|
||||
|
||||
impl<$($Variant),*> $Iter<$($Variant),*> {
|
||||
$(
|
||||
#[allow(non_snake_case)]
|
||||
fn $Variant(iter: impl IntoIterator<IntoIter = $Variant>) -> Self {
|
||||
$Iter::$Variant { iter: iter.into_iter() }
|
||||
}
|
||||
)*
|
||||
}
|
||||
|
||||
impl<T, $($Variant: Iterator<Item = T>),*> Iterator for $Iter<$($Variant),*> {
|
||||
type Item = T;
|
||||
fn next(&mut self) -> Option<T> {
|
||||
match self { $($Iter::$Variant { iter } => iter.next()),* }
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
match self { $($Iter::$Variant { iter } => iter.size_hint()),* }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, $($Variant: DoubleEndedIterator<Item = T>),*> DoubleEndedIterator for $Iter<$($Variant),*> {
|
||||
fn next_back(&mut self) -> Option<T> {
|
||||
match self { $($Iter::$Variant { iter } => iter.next_back()),* }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
@ -2,8 +2,8 @@ use bend::{
|
||||
compile_book, desugar_book,
|
||||
diagnostics::{Diagnostics, DiagnosticsConfig, Severity},
|
||||
fun::{load_book::do_parse_book, net_to_term::net_to_term, term_to_net::Labels, Book, Ctx, Name, Term},
|
||||
hvm,
|
||||
net::hvmc_to_net::hvmc_to_net,
|
||||
hvm::display_hvm_book,
|
||||
net::hvm_to_net::hvm_to_net,
|
||||
run_book, AdtEncoding, CompileOpts, RunOpts,
|
||||
};
|
||||
use insta::assert_snapshot;
|
||||
@ -13,7 +13,6 @@ use std::{
|
||||
fmt::Write,
|
||||
io::Read,
|
||||
path::{Path, PathBuf},
|
||||
str::FromStr,
|
||||
};
|
||||
use stdext::function_name;
|
||||
use walkdir::WalkDir;
|
||||
@ -109,7 +108,7 @@ fn compile_file() {
|
||||
let diagnostics_cfg = DiagnosticsConfig { unused_definition: Severity::Allow, ..Default::default() };
|
||||
|
||||
let res = compile_book(&mut book, compile_opts, diagnostics_cfg, None)?;
|
||||
Ok(format!("{}{}", res.diagnostics, res.core_book))
|
||||
Ok(format!("{}{}", res.diagnostics, display_hvm_book(&res.hvm_book)))
|
||||
})
|
||||
}
|
||||
|
||||
@ -125,7 +124,7 @@ fn compile_file_o_all() {
|
||||
};
|
||||
|
||||
let res = compile_book(&mut book, opts, diagnostics_cfg, None)?;
|
||||
Ok(format!("{}{}", res.diagnostics, res.core_book))
|
||||
Ok(format!("{}{}", res.diagnostics, display_hvm_book(&res.hvm_book)))
|
||||
})
|
||||
}
|
||||
|
||||
@ -136,7 +135,7 @@ fn compile_file_o_no_all() {
|
||||
let compile_opts = CompileOpts::default().set_no_all();
|
||||
let diagnostics_cfg = DiagnosticsConfig::default();
|
||||
let res = compile_book(&mut book, compile_opts, diagnostics_cfg, None)?;
|
||||
Ok(format!("{}", res.core_book))
|
||||
Ok(format!("{}", display_hvm_book(&res.hvm_book)))
|
||||
})
|
||||
}
|
||||
|
||||
@ -205,9 +204,10 @@ fn run_lazy() {
|
||||
#[test]
|
||||
fn readback_lnet() {
|
||||
run_golden_test_dir(function_name!(), &|code, _| {
|
||||
let net = hvm::ast::Net::from_str(code)?;
|
||||
let mut p = hvm::ast::CoreParser::new(code);
|
||||
let net = p.parse_net()?;
|
||||
let book = Book::default();
|
||||
let compat_net = hvmc_to_net(&net);
|
||||
let compat_net = hvm_to_net(&net);
|
||||
let mut diags = Diagnostics::default();
|
||||
let term = net_to_term(&compat_net, &book, &Labels::default(), false, &mut diags);
|
||||
Ok(format!("{}{}", diags, term))
|
||||
@ -339,7 +339,7 @@ fn compile_entrypoint() {
|
||||
book.entrypoint = Some(Name::new("foo"));
|
||||
let diagnostics_cfg = DiagnosticsConfig { ..DiagnosticsConfig::new(Severity::Error, true) };
|
||||
let res = compile_book(&mut book, CompileOpts::default(), diagnostics_cfg, None)?;
|
||||
Ok(format!("{}{}", res.diagnostics, res.core_book))
|
||||
Ok(format!("{}{}", res.diagnostics, display_hvm_book(&res.hvm_book)))
|
||||
})
|
||||
}
|
||||
|
||||
@ -386,7 +386,7 @@ fn mutual_recursion() {
|
||||
let mut book = do_parse_book(code, path, Book::builtins())?;
|
||||
let opts = CompileOpts { merge: true, ..CompileOpts::default() };
|
||||
let res = compile_book(&mut book, opts, diagnostics_cfg, None)?;
|
||||
Ok(format!("{}{}", res.diagnostics, res.core_book))
|
||||
Ok(format!("{}{}", res.diagnostics, display_hvm_book(&res.hvm_book)))
|
||||
})
|
||||
}
|
||||
|
||||
@ -460,6 +460,6 @@ fn scott_triggers_unused() {
|
||||
let diagnostics_cfg =
|
||||
DiagnosticsConfig { unused_definition: Severity::Error, ..DiagnosticsConfig::default() };
|
||||
let res = compile_book(&mut book, opts, diagnostics_cfg, None)?;
|
||||
Ok(format!("{}{}", res.diagnostics, res.core_book))
|
||||
Ok(format!("{}{}", res.diagnostics, display_hvm_book(&res.hvm_book)))
|
||||
})
|
||||
}
|
||||
|
@ -18,8 +18,8 @@ input_file: tests/golden_tests/cli/no_check_net_size.bend
|
||||
|
||||
@Gen.go__C1 = ({a d} ({$([*2] $([|1] e)) $([*2] b)} g))
|
||||
& @Arr/Node ~ (c (f g))
|
||||
&! @Gen.go ~ (a (b c))
|
||||
&! @Gen.go ~ (d (e f))
|
||||
&!@Gen.go ~ (a (b c))
|
||||
&!@Gen.go ~ (d (e f))
|
||||
|
||||
@Main__C0 = a
|
||||
& @Gen ~ (4 a)
|
||||
@ -59,8 +59,8 @@ input_file: tests/golden_tests/cli/no_check_net_size.bend
|
||||
|
||||
@Merge__C5 = (* (b (e (a (d g)))))
|
||||
& @Map_/Both ~ (c (f g))
|
||||
&! @Merge ~ (a (b c))
|
||||
&! @Merge ~ (d (e f))
|
||||
&!@Merge ~ (a (b c))
|
||||
&!@Merge ~ (d (e f))
|
||||
|
||||
@Merge__C6 = a
|
||||
& @Map_/Both ~ a
|
||||
@ -104,8 +104,8 @@ input_file: tests/golden_tests/cli/no_check_net_size.bend
|
||||
|
||||
@Reverse__C1 = (* (c (a e)))
|
||||
& @Arr/Node ~ (b (d e))
|
||||
&! @Reverse ~ (a b)
|
||||
&! @Reverse ~ (c d)
|
||||
&!@Reverse ~ (a b)
|
||||
&!@Reverse ~ (c d)
|
||||
|
||||
@Reverse__C2 = (?((@Reverse__C0 @Reverse__C1) a) a)
|
||||
|
||||
@ -118,8 +118,8 @@ input_file: tests/golden_tests/cli/no_check_net_size.bend
|
||||
@Sum = ((@Sum__C2 a) a)
|
||||
|
||||
@Sum__C0 = (* (a (b d)))
|
||||
&! @Sum ~ (a $([+] $(c d)))
|
||||
&! @Sum ~ (b c)
|
||||
&!@Sum ~ (a $([+] $(c d)))
|
||||
&!@Sum ~ (b c)
|
||||
|
||||
@Sum__C1 = (?(((a a) @Sum__C0) b) b)
|
||||
|
||||
@ -140,8 +140,8 @@ input_file: tests/golden_tests/cli/no_check_net_size.bend
|
||||
|
||||
@ToArr__C1 = (* (b (e ({$([*2] $([+1] d)) $([*2] $([+0] a))} g))))
|
||||
& @Arr/Node ~ (c (f g))
|
||||
&! @ToArr ~ (a (b c))
|
||||
&! @ToArr ~ (d (e f))
|
||||
&!@ToArr ~ (a (b c))
|
||||
&!@ToArr ~ (d (e f))
|
||||
|
||||
@ToArr__C2 = (?((@ToArr__C0 @ToArr__C1) a) a)
|
||||
|
||||
@ -154,8 +154,8 @@ input_file: tests/golden_tests/cli/no_check_net_size.bend
|
||||
|
||||
@ToMap__C1 = (* (a (c e)))
|
||||
& @Merge ~ (b (d e))
|
||||
&! @ToMap ~ (a b)
|
||||
&! @ToMap ~ (c d)
|
||||
&!@ToMap ~ (a b)
|
||||
&!@ToMap ~ (c d)
|
||||
|
||||
@ToMap__C2 = (?((@ToMap__C0 @ToMap__C1) a) a)
|
||||
|
||||
|
@ -85,8 +85,8 @@ input_file: tests/golden_tests/compile_file/redex_order_recursive.bend
|
||||
|
||||
@Tree.flip__C0 = (c (a e))
|
||||
& @Tree/node ~ (b (d e))
|
||||
&! @Tree.flip ~ (a b)
|
||||
&! @Tree.flip ~ (c d)
|
||||
&!@Tree.flip ~ (a b)
|
||||
&!@Tree.flip ~ (c d)
|
||||
|
||||
@Tree.flip__C1 = (* a)
|
||||
& @Tree/leaf ~ a
|
||||
@ -98,16 +98,16 @@ input_file: tests/golden_tests/compile_file/redex_order_recursive.bend
|
||||
@Tree.height__C0 = (a (c f))
|
||||
& $(e f) ~ [+1]
|
||||
& @max ~ (b (d e))
|
||||
&! @Tree.height ~ (a b)
|
||||
&! @Tree.height ~ (c d)
|
||||
&!@Tree.height ~ (a b)
|
||||
&!@Tree.height ~ (c d)
|
||||
|
||||
@Tree.height__C1 = (?((@Tree.height__C0 (* (* 0))) a) a)
|
||||
|
||||
@Tree.leaves = ((@Tree.leaves__C1 a) a)
|
||||
|
||||
@Tree.leaves__C0 = (a (b d))
|
||||
&! @Tree.leaves ~ (a $([+] $(c d)))
|
||||
&! @Tree.leaves ~ (b c)
|
||||
&!@Tree.leaves ~ (a $([+] $(c d)))
|
||||
&!@Tree.leaves ~ (b c)
|
||||
|
||||
@Tree.leaves__C1 = (?((@Tree.leaves__C0 (* (* 1))) a) a)
|
||||
|
||||
@ -115,8 +115,8 @@ input_file: tests/golden_tests/compile_file/redex_order_recursive.bend
|
||||
|
||||
@Tree.map__C0 = (a (d ({b e} g)))
|
||||
& @Tree/node ~ (c (f g))
|
||||
&! @Tree.map ~ (a (b c))
|
||||
&! @Tree.map ~ (d (e f))
|
||||
&!@Tree.map ~ (a (b c))
|
||||
&!@Tree.map ~ (d (e f))
|
||||
|
||||
@Tree.map__C1 = (* (a ((a b) c)))
|
||||
& @Tree/leaf ~ (b c)
|
||||
@ -127,8 +127,8 @@ input_file: tests/golden_tests/compile_file/redex_order_recursive.bend
|
||||
|
||||
@Tree.nodes__C0 = (a (b e))
|
||||
& $(d e) ~ [+1]
|
||||
&! @Tree.nodes ~ (a $([+] $(c d)))
|
||||
&! @Tree.nodes ~ (b c)
|
||||
&!@Tree.nodes ~ (a $([+] $(c d)))
|
||||
&!@Tree.nodes ~ (b c)
|
||||
|
||||
@Tree.nodes__C1 = (?((@Tree.nodes__C0 (* (* 0))) a) a)
|
||||
|
||||
@ -151,8 +151,8 @@ input_file: tests/golden_tests/compile_file/redex_order_recursive.bend
|
||||
|
||||
@fold___C1 = (a (c e))
|
||||
& @add ~ (b (d e))
|
||||
&! @fold_ ~ (a b)
|
||||
&! @fold_ ~ (c d)
|
||||
&!@fold_ ~ (a b)
|
||||
&!@fold_ ~ (c d)
|
||||
|
||||
@main = *
|
||||
|
||||
|
@ -43,10 +43,10 @@ input_file: tests/golden_tests/compile_file_o_all/list_merge_sort.bend
|
||||
@Merge__C0 = ({b {g l}} ({h q} ({(a (b c)) {e m}} ({a {d n}} ({f o} t)))))
|
||||
& @If ~ (c (k (s t)))
|
||||
& @List_/Cons ~ (d (j k))
|
||||
&! @Merge ~ (e (f (i j)))
|
||||
&!@Merge ~ (e (f (i j)))
|
||||
& @List_/Cons ~ (g (h i))
|
||||
& @List_/Cons ~ (l (r s))
|
||||
&! @Merge ~ (m (p (q r)))
|
||||
&!@Merge ~ (m (p (q r)))
|
||||
& @List_/Cons ~ (n (o p))
|
||||
|
||||
@Merge__C1 = (* (* a))
|
||||
|
@ -18,7 +18,7 @@ It is lazy when it's an argument ('(x Foo)') or when it's used linearly ('let x
|
||||
to:
|
||||
'Foo = λa λb (b (λc λa (Foo a c)) (λa a) a)'
|
||||
which is lifted to:
|
||||
'Foo = λa λb (b Foo__C1 Foo_C2 a)'
|
||||
'Foo = λa λb (b Foo__C1 Foo__C2 a)'
|
||||
- Replace non-linear 'let' expressions with 'use' expressions. For example, change:
|
||||
'Foo = λf let x = Foo; (f x x)'
|
||||
to:
|
||||
@ -27,7 +27,7 @@ It is lazy when it's an argument ('(x Foo)') or when it's used linearly ('let x
|
||||
'Foo = λf (f Foo Foo)'
|
||||
- If disabled, re-enable the default 'float-combinators' and 'linearize-matches' compiler options.
|
||||
|
||||
For more information, visit: https://github.com/HigherOrderCO/hvm-lang/blob/main/docs/lazy-definitions.md.
|
||||
For more information, visit: https://github.com/HigherOrderCO/Bend/blob/main/docs/lazy-definitions.md.
|
||||
To disable this check, use the "-Arecursion-cycle" compiler option.
|
||||
|
||||
|
||||
|
@ -18,7 +18,7 @@ It is lazy when it's an argument ('(x Foo)') or when it's used linearly ('let x
|
||||
to:
|
||||
'Foo = λa λb (b (λc λa (Foo a c)) (λa a) a)'
|
||||
which is lifted to:
|
||||
'Foo = λa λb (b Foo__C1 Foo_C2 a)'
|
||||
'Foo = λa λb (b Foo__C1 Foo__C2 a)'
|
||||
- Replace non-linear 'let' expressions with 'use' expressions. For example, change:
|
||||
'Foo = λf let x = Foo; (f x x)'
|
||||
to:
|
||||
@ -27,7 +27,7 @@ It is lazy when it's an argument ('(x Foo)') or when it's used linearly ('let x
|
||||
'Foo = λf (f Foo Foo)'
|
||||
- If disabled, re-enable the default 'float-combinators' and 'linearize-matches' compiler options.
|
||||
|
||||
For more information, visit: https://github.com/HigherOrderCO/hvm-lang/blob/main/docs/lazy-definitions.md.
|
||||
For more information, visit: https://github.com/HigherOrderCO/Bend/blob/main/docs/lazy-definitions.md.
|
||||
To disable this check, use the "-Arecursion-cycle" compiler option.
|
||||
|
||||
|
||||
|
@ -23,7 +23,7 @@ It is lazy when it's an argument ('(x Foo)') or when it's used linearly ('let x
|
||||
to:
|
||||
'Foo = λa λb (b (λc λa (Foo a c)) (λa a) a)'
|
||||
which is lifted to:
|
||||
'Foo = λa λb (b Foo__C1 Foo_C2 a)'
|
||||
'Foo = λa λb (b Foo__C1 Foo__C2 a)'
|
||||
- Replace non-linear 'let' expressions with 'use' expressions. For example, change:
|
||||
'Foo = λf let x = Foo; (f x x)'
|
||||
to:
|
||||
@ -32,5 +32,5 @@ It is lazy when it's an argument ('(x Foo)') or when it's used linearly ('let x
|
||||
'Foo = λf (f Foo Foo)'
|
||||
- If disabled, re-enable the default 'float-combinators' and 'linearize-matches' compiler options.
|
||||
|
||||
For more information, visit: https://github.com/HigherOrderCO/hvm-lang/blob/main/docs/lazy-definitions.md.
|
||||
For more information, visit: https://github.com/HigherOrderCO/Bend/blob/main/docs/lazy-definitions.md.
|
||||
To disable this check, use the "-Arecursion-cycle" compiler option.
|
||||
|
@ -19,7 +19,7 @@ It is lazy when it's an argument ('(x Foo)') or when it's used linearly ('let x
|
||||
to:
|
||||
'Foo = λa λb (b (λc λa (Foo a c)) (λa a) a)'
|
||||
which is lifted to:
|
||||
'Foo = λa λb (b Foo__C1 Foo_C2 a)'
|
||||
'Foo = λa λb (b Foo__C1 Foo__C2 a)'
|
||||
- Replace non-linear 'let' expressions with 'use' expressions. For example, change:
|
||||
'Foo = λf let x = Foo; (f x x)'
|
||||
to:
|
||||
@ -28,5 +28,5 @@ It is lazy when it's an argument ('(x Foo)') or when it's used linearly ('let x
|
||||
'Foo = λf (f Foo Foo)'
|
||||
- If disabled, re-enable the default 'float-combinators' and 'linearize-matches' compiler options.
|
||||
|
||||
For more information, visit: https://github.com/HigherOrderCO/hvm-lang/blob/main/docs/lazy-definitions.md.
|
||||
For more information, visit: https://github.com/HigherOrderCO/Bend/blob/main/docs/lazy-definitions.md.
|
||||
To disable this check, use the "-Arecursion-cycle" compiler option.
|
||||
|
@ -19,7 +19,7 @@ It is lazy when it's an argument ('(x Foo)') or when it's used linearly ('let x
|
||||
to:
|
||||
'Foo = λa λb (b (λc λa (Foo a c)) (λa a) a)'
|
||||
which is lifted to:
|
||||
'Foo = λa λb (b Foo__C1 Foo_C2 a)'
|
||||
'Foo = λa λb (b Foo__C1 Foo__C2 a)'
|
||||
- Replace non-linear 'let' expressions with 'use' expressions. For example, change:
|
||||
'Foo = λf let x = Foo; (f x x)'
|
||||
to:
|
||||
@ -28,5 +28,5 @@ It is lazy when it's an argument ('(x Foo)') or when it's used linearly ('let x
|
||||
'Foo = λf (f Foo Foo)'
|
||||
- If disabled, re-enable the default 'float-combinators' and 'linearize-matches' compiler options.
|
||||
|
||||
For more information, visit: https://github.com/HigherOrderCO/hvm-lang/blob/main/docs/lazy-definitions.md.
|
||||
For more information, visit: https://github.com/HigherOrderCO/Bend/blob/main/docs/lazy-definitions.md.
|
||||
To disable this check, use the "-Arecursion-cycle" compiler option.
|
||||
|
@ -18,7 +18,7 @@ It is lazy when it's an argument ('(x Foo)') or when it's used linearly ('let x
|
||||
to:
|
||||
'Foo = λa λb (b (λc λa (Foo a c)) (λa a) a)'
|
||||
which is lifted to:
|
||||
'Foo = λa λb (b Foo__C1 Foo_C2 a)'
|
||||
'Foo = λa λb (b Foo__C1 Foo__C2 a)'
|
||||
- Replace non-linear 'let' expressions with 'use' expressions. For example, change:
|
||||
'Foo = λf let x = Foo; (f x x)'
|
||||
to:
|
||||
@ -27,5 +27,5 @@ It is lazy when it's an argument ('(x Foo)') or when it's used linearly ('let x
|
||||
'Foo = λf (f Foo Foo)'
|
||||
- If disabled, re-enable the default 'float-combinators' and 'linearize-matches' compiler options.
|
||||
|
||||
For more information, visit: https://github.com/HigherOrderCO/hvm-lang/blob/main/docs/lazy-definitions.md.
|
||||
For more information, visit: https://github.com/HigherOrderCO/Bend/blob/main/docs/lazy-definitions.md.
|
||||
To disable this check, use the "-Arecursion-cycle" compiler option.
|
||||
|
@ -21,7 +21,7 @@ It is lazy when it's an argument ('(x Foo)') or when it's used linearly ('let x
|
||||
to:
|
||||
'Foo = λa λb (b (λc λa (Foo a c)) (λa a) a)'
|
||||
which is lifted to:
|
||||
'Foo = λa λb (b Foo__C1 Foo_C2 a)'
|
||||
'Foo = λa λb (b Foo__C1 Foo__C2 a)'
|
||||
- Replace non-linear 'let' expressions with 'use' expressions. For example, change:
|
||||
'Foo = λf let x = Foo; (f x x)'
|
||||
to:
|
||||
@ -30,5 +30,5 @@ It is lazy when it's an argument ('(x Foo)') or when it's used linearly ('let x
|
||||
'Foo = λf (f Foo Foo)'
|
||||
- If disabled, re-enable the default 'float-combinators' and 'linearize-matches' compiler options.
|
||||
|
||||
For more information, visit: https://github.com/HigherOrderCO/hvm-lang/blob/main/docs/lazy-definitions.md.
|
||||
For more information, visit: https://github.com/HigherOrderCO/Bend/blob/main/docs/lazy-definitions.md.
|
||||
To disable this check, use the "-Arecursion-cycle" compiler option.
|
||||
|
@ -21,7 +21,7 @@ It is lazy when it's an argument ('(x Foo)') or when it's used linearly ('let x
|
||||
to:
|
||||
'Foo = λa λb (b (λc λa (Foo a c)) (λa a) a)'
|
||||
which is lifted to:
|
||||
'Foo = λa λb (b Foo__C1 Foo_C2 a)'
|
||||
'Foo = λa λb (b Foo__C1 Foo__C2 a)'
|
||||
- Replace non-linear 'let' expressions with 'use' expressions. For example, change:
|
||||
'Foo = λf let x = Foo; (f x x)'
|
||||
to:
|
||||
@ -30,5 +30,5 @@ It is lazy when it's an argument ('(x Foo)') or when it's used linearly ('let x
|
||||
'Foo = λf (f Foo Foo)'
|
||||
- If disabled, re-enable the default 'float-combinators' and 'linearize-matches' compiler options.
|
||||
|
||||
For more information, visit: https://github.com/HigherOrderCO/hvm-lang/blob/main/docs/lazy-definitions.md.
|
||||
For more information, visit: https://github.com/HigherOrderCO/Bend/blob/main/docs/lazy-definitions.md.
|
||||
To disable this check, use the "-Arecursion-cycle" compiler option.
|
||||
|
@ -18,7 +18,7 @@ It is lazy when it's an argument ('(x Foo)') or when it's used linearly ('let x
|
||||
to:
|
||||
'Foo = λa λb (b (λc λa (Foo a c)) (λa a) a)'
|
||||
which is lifted to:
|
||||
'Foo = λa λb (b Foo__C1 Foo_C2 a)'
|
||||
'Foo = λa λb (b Foo__C1 Foo__C2 a)'
|
||||
- Replace non-linear 'let' expressions with 'use' expressions. For example, change:
|
||||
'Foo = λf let x = Foo; (f x x)'
|
||||
to:
|
||||
@ -27,5 +27,5 @@ It is lazy when it's an argument ('(x Foo)') or when it's used linearly ('let x
|
||||
'Foo = λf (f Foo Foo)'
|
||||
- If disabled, re-enable the default 'float-combinators' and 'linearize-matches' compiler options.
|
||||
|
||||
For more information, visit: https://github.com/HigherOrderCO/hvm-lang/blob/main/docs/lazy-definitions.md.
|
||||
For more information, visit: https://github.com/HigherOrderCO/Bend/blob/main/docs/lazy-definitions.md.
|
||||
To disable this check, use the "-Arecursion-cycle" compiler option.
|
||||
|
@ -18,7 +18,7 @@ It is lazy when it's an argument ('(x Foo)') or when it's used linearly ('let x
|
||||
to:
|
||||
'Foo = λa λb (b (λc λa (Foo a c)) (λa a) a)'
|
||||
which is lifted to:
|
||||
'Foo = λa λb (b Foo__C1 Foo_C2 a)'
|
||||
'Foo = λa λb (b Foo__C1 Foo__C2 a)'
|
||||
- Replace non-linear 'let' expressions with 'use' expressions. For example, change:
|
||||
'Foo = λf let x = Foo; (f x x)'
|
||||
to:
|
||||
@ -27,5 +27,5 @@ It is lazy when it's an argument ('(x Foo)') or when it's used linearly ('let x
|
||||
'Foo = λf (f Foo Foo)'
|
||||
- If disabled, re-enable the default 'float-combinators' and 'linearize-matches' compiler options.
|
||||
|
||||
For more information, visit: https://github.com/HigherOrderCO/hvm-lang/blob/main/docs/lazy-definitions.md.
|
||||
For more information, visit: https://github.com/HigherOrderCO/Bend/blob/main/docs/lazy-definitions.md.
|
||||
To disable this check, use the "-Arecursion-cycle" compiler option.
|
||||
|
Loading…
Reference in New Issue
Block a user