├── tests ├── programs │ ├── era_era.hvm │ ├── commutation.hvm │ ├── stress_tests │ │ ├── sum_tail.bend │ │ ├── sum_rec.bend │ │ ├── sum_tail.hvm │ │ ├── fib_rec.bend │ │ ├── apelacion.bend │ │ ├── sum_rec.hvm │ │ ├── tuple_rots.bend │ │ ├── fib_rec.hvm │ │ ├── apelacion.hvm │ │ ├── sum_tree.bend │ │ ├── all_tree.bend │ │ ├── sum_tree.hvm │ │ ├── tuple_rots.hvm │ │ ├── all_tree.hs │ │ ├── all_tree.hvm │ │ ├── sum_tree.js │ │ ├── burn.hvm │ │ └── boom.hvm │ ├── bool_and.hvm │ ├── chained_ops.bend │ ├── church_mul.bend │ ├── church_mul.hvm │ ├── alloc_small_tree.bend │ ├── chained_ops.hvm │ ├── neg_fusion.hvm │ ├── neg_fusion.bend │ ├── queue.hvm │ ├── heavy_pre_reduction.bend │ ├── queue.bend │ ├── alloc_small_tree.hvm │ ├── heavy_pre_reduction.hvm │ ├── u24.hvm │ ├── i24.hvm │ ├── f24.hvm │ ├── numeric_casts.hvm │ ├── alloc_big_tree.hvm │ ├── dec_bits.hvm │ └── dec_bits_tree.hvm ├── compile_files.sh ├── snapshots │ ├── run@tests::programs::era_era.snap │ ├── run@tests::programs::bool_and.snap │ ├── run@tests::programs::commutation.snap │ ├── run@examples::machine_u32::num_add.snap │ ├── run@examples::machine_u32::num_match.snap │ ├── run@tests::programs::chained_ops.snap │ ├── run@tests::programs::dec_bits.snap │ ├── run@tests::programs::neg_fusion.snap │ ├── run@tests::programs::alloc_small_tree.snap │ ├── run@tests::programs::heavy_pre_reduction.snap │ ├── run@examples::sort::radix::radix_sort_ctr.snap │ ├── run@examples::sort::radix::radix_sort_lam.snap │ ├── run@tests::programs::stress_tests::boom.snap │ ├── run@tests::programs::stress_tests::sum_tree.snap │ ├── run@tests::programs::stress_tests::all_tree.snap │ ├── run@tests::programs::stress_tests::fib_rec.snap │ ├── run@tests::programs::stress_tests::sum_rec.snap │ ├── run@tests::programs::stress_tests::sum_tail.snap │ ├── run@examples::arithmetic.snap │ ├── run@examples::sort::bitonic::bitonic_sort_lam.snap │ ├── run@tests::programs::stress_tests::apelacion.snap │ ├── run@examples::lambda_calculus::hoas.snap │ ├── run@tests::programs::stress_tests::tuple_rots.snap │ ├── run@tests::programs::u24.snap │ ├── run@tests::programs::i24.snap │ ├── run@tests::programs::queue.snap │ ├── run@examples::church_encoding::church.snap │ ├── run@examples::sort::merge::merge_sort.snap │ ├── run@tests::programs::f24.snap │ ├── run@tests::programs::stress_tests::burn.snap │ ├── pre_reduce_run@tests::programs::era_era.snap │ ├── pre_reduce_run@examples::machine_u32::num_add.snap │ ├── pre_reduce_run@tests::programs::bool_and.snap │ ├── pre_reduce_run@tests::programs::chained_ops.snap │ ├── pre_reduce_run@tests::programs::commutation.snap │ ├── pre_reduce_run@tests::programs::dec_bits.snap │ ├── pre_reduce_run@tests::programs::neg_fusion.snap │ ├── pre_reduce_run@examples::machine_u32::num_match.snap │ ├── pre_reduce_run@tests::programs::heavy_pre_reduction.snap │ ├── pre_reduce_run@tests::programs::alloc_small_tree.snap │ ├── pre_reduce_run@tests::programs::stress_tests::boom.snap │ ├── pre_reduce_run@tests::programs::stress_tests::sum_tree.snap │ ├── pre_reduce_run@examples::sort::radix::radix_sort_ctr.snap │ ├── pre_reduce_run@examples::sort::radix::radix_sort_lam.snap │ ├── pre_reduce_run@tests::programs::stress_tests::fib_rec.snap │ ├── pre_reduce_run@tests::programs::stress_tests::sum_rec.snap │ ├── pre_reduce_run@examples::sort::bitonic::bitonic_sort_lam.snap │ ├── pre_reduce_run@tests::programs::stress_tests::all_tree.snap │ ├── pre_reduce_run@tests::programs::stress_tests::apelacion.snap │ ├── pre_reduce_run@tests::programs::stress_tests::sum_tail.snap │ ├── pre_reduce_run@examples::arithmetic.snap │ ├── pre_reduce_run@examples::lambda_calculus::hoas.snap │ ├── pre_reduce_run@tests::programs::stress_tests::tuple_rots.snap │ ├── pre_reduce_run@tests::programs::u24.snap │ ├── run@tests::programs::numeric_casts.snap │ ├── pre_reduce_run@tests::programs::i24.snap │ ├── pre_reduce_run@tests::programs::queue.snap │ ├── pre_reduce_run@examples::church_encoding::church.snap │ ├── pre_reduce_run@examples::sort::merge::merge_sort.snap │ ├── pre_reduce_run@tests::programs::f24.snap │ ├── pre_reduce_run@tests::programs::stress_tests::burn.snap │ ├── run@tests::programs::church_mul.snap │ ├── pre_reduce_run@tests::programs::numeric_casts.snap │ ├── pre_reduce_run@tests::programs::church_mul.snap │ ├── run@tests::programs::dec_bits_tree.snap │ ├── pre_reduce_run@tests::programs::dec_bits_tree.snap │ └── run@tests::programs::church_exp.snap ├── loaders.rs ├── tests.rs ├── lists.rs └── transform.rs ├── examples ├── machine_u32 │ ├── num_add.bend │ ├── num_add.hvm │ ├── num_match.bend │ └── num_match.hvm ├── arithmetic.bend ├── arithmetic.hvm ├── church_encoding │ ├── church.bend │ └── church.hvm ├── sort │ ├── merge │ │ ├── merge_sort.bend │ │ └── merge_sort.hvm │ ├── bitonic │ │ ├── bitonic_sort_ctr.hvm1 │ │ ├── bitonic_sort_lam.bend │ │ ├── bitonic_sort_lam.hvm1 │ │ └── bitonic_sort_lam.hvm │ └── radix │ │ ├── radix_sort_ctr.bend │ │ ├── radix_sort_ctr.hvm1 │ │ ├── radix_sort_ctr.hs │ │ ├── radix_sort_ctr.hvm │ │ ├── radix_sort_lam.hvm │ │ ├── radix_sort_lam.bend │ │ ├── radix_sort_lam.js │ │ └── radix_sort_lam.hs └── lambda_calculus │ ├── hoas.bend │ └── hoas.hvm ├── foo.hvm ├── README.md ├── rust-toolchain.toml ├── util ├── src │ ├── pretty_num.rs │ ├── maybe_grow.rs │ ├── lib.rs │ ├── new_uninit_slice.rs │ ├── deref.rs │ ├── prelude.rs │ ├── parse_abbrev_number.rs │ ├── ops │ │ ├── word.rs │ │ └── num.rs │ ├── multi_iterator.rs │ ├── create_var.rs │ └── bi_enum.rs └── Cargo.toml ├── src ├── bare.rs ├── full.rs ├── compile │ └── include_files.rs └── args.rs ├── num └── Cargo.toml ├── .gitignore ├── rustfmt.toml ├── .github └── workflows │ ├── delete-cancelled.yml │ ├── checks.yml │ └── bench.yml ├── runtime ├── Cargo.toml └── src │ ├── addr.rs │ ├── node.rs │ ├── wire.rs │ ├── net.rs │ ├── allocator.rs │ └── runtime.rs ├── ast └── Cargo.toml ├── host ├── Cargo.toml └── src │ ├── host.rs │ └── readback.rs ├── transform ├── Cargo.toml └── src │ ├── prune.rs │ ├── inline.rs │ ├── transform.rs │ └── eta_reduce.rs ├── flake.nix ├── cspell.json ├── Cargo.toml └── flake.lock /tests/programs/era_era.hvm: -------------------------------------------------------------------------------- 1 | @main = * & * ~ * 2 | -------------------------------------------------------------------------------- /tests/programs/commutation.hvm: -------------------------------------------------------------------------------- 1 | @main = root & (x x) ~ {* root} 2 | -------------------------------------------------------------------------------- /examples/machine_u32/num_add.bend: -------------------------------------------------------------------------------- 1 | add = λa λb (+ a b) 2 | main = (add 123 100) 3 | -------------------------------------------------------------------------------- /examples/machine_u32/num_add.hvm: -------------------------------------------------------------------------------- 1 | @add = ($([+] $(a b)) (a b)) 2 | @main = a 3 | & @add ~ (123 (100 a)) 4 | 5 | -------------------------------------------------------------------------------- /foo.hvm: -------------------------------------------------------------------------------- 1 | @main = x & @foo ~ (10 x) 2 | @foo = (?((0 @bar) x) x) 3 | @bar = (x y) & @foo ~ (x $([<<] $(1 y))) 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HVM-64 2 | 3 | This is an experimental 64-bit version of [HVM](https://github.com/HigherOrderCO/HVM). 4 | -------------------------------------------------------------------------------- /examples/machine_u32/num_match.bend: -------------------------------------------------------------------------------- 1 | pred = λx match x { 2 | 0: 0 3 | 1+: x-1 4 | } 5 | 6 | main = (pred 10) 7 | -------------------------------------------------------------------------------- /examples/machine_u32/num_match.hvm: -------------------------------------------------------------------------------- 1 | @main = a 2 | & @pred ~ (10 a) 3 | @pred = (?((a b) d) d) 4 | & (a b) ~ (0 (c c)) 5 | 6 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | profile = "minimal" 3 | channel = "nightly-2024-05-28" 4 | components = ["rustfmt", "clippy"] 5 | -------------------------------------------------------------------------------- /tests/programs/stress_tests/sum_tail.bend: -------------------------------------------------------------------------------- 1 | sum = λa match a { 2 | 0: λs s 3 | 1+: λs (sum a-1 (+ a-1 s)) 4 | } 5 | 6 | main = (sum 10000000 0) 7 | -------------------------------------------------------------------------------- /tests/programs/bool_and.hvm: -------------------------------------------------------------------------------- 1 | @true = (b (* b)) 2 | @false = (* (b b)) 3 | @and = ((b (@false c)) (b c)) 4 | @main = root & @and ~ (@true (@false root)) 5 | -------------------------------------------------------------------------------- /examples/arithmetic.bend: -------------------------------------------------------------------------------- 1 | add = λaλb(+ a b) 2 | sub = λaλb(- a b) 3 | mul = λaλb(* a b) 4 | div = λaλb(/ a b) 5 | mod = λaλb(% a b) 6 | main = λaλb((div a b), (mod a b)) -------------------------------------------------------------------------------- /tests/programs/stress_tests/sum_rec.bend: -------------------------------------------------------------------------------- 1 | add = λa λb (+ a b) 2 | 3 | sum = λn match n { 4 | 0: 1 5 | 1+: (add (sum n-1) (sum n-1)) 6 | } 7 | 8 | main = (sum 23) 9 | -------------------------------------------------------------------------------- /tests/programs/stress_tests/sum_tail.hvm: -------------------------------------------------------------------------------- 1 | @main = a 2 | & @sum ~ (10000000 (0 a)) 3 | @sum = (?((a b) d) d) 4 | & (a b) ~ ((c c) @sum$S0) 5 | @sum$S0 = (#1{a $([+] $(b c))} (b d)) 6 | & @sum ~ (a (c d)) 7 | 8 | -------------------------------------------------------------------------------- /tests/programs/chained_ops.bend: -------------------------------------------------------------------------------- 1 | main = (λa λb (* (- 2 | (* a (+ 178 (- (* a (+ 178 (- (* 10 (+ 80 (* 70 b))) 20))) 20))) 3 | (* 10 (+ 80 (* 70 b)))) (+ (* 10 (+ 80 (* 70 b))) (+ 80 (* 70 a))) 4 | ) 70 50 5 | ) -------------------------------------------------------------------------------- /tests/programs/church_mul.bend: -------------------------------------------------------------------------------- 1 | // size = 1 << 12 2 | 3 | C_20 = λs λz (s (s (s (s (s (s (s (s (s (s (s (s (s (s (s (s (s (s (s (s z)))))))))))))))))))) 4 | 5 | Mult = λm λn λs λz (m (n s) z) 6 | 7 | Main = (Mult C_20 C_20) 8 | -------------------------------------------------------------------------------- /tests/programs/stress_tests/fib_rec.bend: -------------------------------------------------------------------------------- 1 | add = λa λb (+ a b) 2 | 3 | fib = λx match x { 4 | 0: 1 5 | 1+: let p = x-1; match p { 6 | 0: 1 7 | 1+: (+ (fib p) (fib p-1)) 8 | } 9 | } 10 | 11 | main = (fib 30) 12 | -------------------------------------------------------------------------------- /tests/programs/stress_tests/apelacion.bend: -------------------------------------------------------------------------------- 1 | sum = λa match a { 2 | 0: λs s 3 | 1+: λs (sum a-1 (+ a-1 s)) 4 | } 5 | 6 | rec = λa match a { 7 | 0: (sum 1000000 0) 8 | 1+: (+ (rec a-1) (rec a-1)) 9 | } 10 | 11 | main = (rec 6) 12 | -------------------------------------------------------------------------------- /util/src/pretty_num.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | use core::str::from_utf8; 3 | 4 | pub fn pretty_num(n: u64) -> String { 5 | n.to_string().as_bytes().rchunks(3).rev().map(|x| from_utf8(x).unwrap()).flat_map(|x| ["_", x]).skip(1).collect() 6 | } 7 | -------------------------------------------------------------------------------- /tests/compile_files.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "Compiling all .hvm2 files" 4 | 5 | shopt -s globstar 6 | 7 | for hvml in **/*.hvm2; do 8 | hvm64="${hvml%.hvm2}.hvm" 9 | echo "> $hvml" 10 | hvml compile "$hvml" > "$hvm64" 11 | done 12 | -------------------------------------------------------------------------------- /tests/programs/stress_tests/sum_rec.hvm: -------------------------------------------------------------------------------- 1 | @add = ($([+] $(a b)) (a b)) 2 | @main = a 3 | & @sum ~ (23 a) 4 | @sum = (?((a b) c) c) 5 | & (a b) ~ (1 @sum$S0) 6 | @sum$S0 = (#1{a b} c) 7 | & @add ~ (d (e c)) 8 | & @sum ~ (b e) 9 | & @sum ~ (a d) 10 | 11 | -------------------------------------------------------------------------------- /examples/arithmetic.hvm: -------------------------------------------------------------------------------- 1 | @add = ($([+] $(a b)) (a b)) 2 | @div = ($([/] $(a b)) (a b)) 3 | @main = (#1{a b} (#2{c d} {e f})) 4 | & @mod ~ (b (d f)) 5 | & @div ~ (a (c e)) 6 | @mod = ($([%] $(a b)) (a b)) 7 | @mul = ($([*] $(a b)) (a b)) 8 | @sub = ($([-] $(a b)) (a b)) 9 | 10 | -------------------------------------------------------------------------------- /src/bare.rs: -------------------------------------------------------------------------------- 1 | use crate::{RunArgs, RuntimeOpts}; 2 | 3 | use clap::Parser; 4 | 5 | #[derive(Parser, Debug)] 6 | #[command(author, version)] 7 | pub struct BareCli { 8 | #[command(flatten)] 9 | pub opts: RuntimeOpts, 10 | #[command(flatten)] 11 | pub args: RunArgs, 12 | } 13 | -------------------------------------------------------------------------------- /util/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hvm64-util" 3 | version.workspace = true 4 | edition = "2021" 5 | 6 | [dependencies] 7 | stacker = { version = "0.1.15", optional = true } 8 | 9 | [features] 10 | default = ["std"] 11 | std = ["dep:stacker"] 12 | 13 | [lints] 14 | workspace = true 15 | -------------------------------------------------------------------------------- /examples/church_encoding/church.bend: -------------------------------------------------------------------------------- 1 | S = λn λs λz (s (n s z)) 2 | Z = λs λz z 3 | 4 | c2 = λf λx (f (f x)) 5 | c3 = λf λx (f (f (f x))) 6 | c4 = (S (S (S (S Z)))) 7 | 8 | add = λa λb λs λz (a s (b s z)) 9 | mul = λa λb λs λz (a (b s) z) 10 | 11 | // 2 * 3 + 4 12 | main = (add (mul c2 c3) c4) 13 | -------------------------------------------------------------------------------- /tests/programs/stress_tests/tuple_rots.bend: -------------------------------------------------------------------------------- 1 | MkTup8 a b c d e f g h = @t (t a b c d e f g h) 2 | 3 | rot = λx (x λa λb λc λd λe λf λg λh (MkTup8 b c d e f g h a)) 4 | 5 | app = λn match n { 6 | 0: λf λx x 7 | 1+: λf λx (app n-1 f (f x)) 8 | } 9 | 10 | main = (app 2000000 rot (MkTup8 1 2 3 4 5 6 7 8)) 11 | -------------------------------------------------------------------------------- /tests/programs/church_mul.hvm: -------------------------------------------------------------------------------- 1 | @C_20 = (#1{(a b) #1{(c a) #1{(d c) #1{(e d) #1{(f e) #1{(g f) #1{(h g) #1{(i h) #1{(j i) #1{(k j) #1{(l k) #1{(m l) #1{(n m) #1{(o n) #1{(p o) #1{(q p) #1{(r q) #1{(s r) #1{(t s) (u t)}}}}}}}}}}}}}}}}}}} (u b)) 2 | @Mult = ((a (b c)) ((d a) (d (b c)))) 3 | @main = a 4 | & @Mult ~ (@C_20 (@C_20 a)) 5 | 6 | -------------------------------------------------------------------------------- /tests/programs/stress_tests/fib_rec.hvm: -------------------------------------------------------------------------------- 1 | @add = ($([+] $(a b)) (a b)) 2 | @fib = (?((a b) c) c) 3 | & (a b) ~ (1 @fib$S2) 4 | @fib$S0 = (* 1) 5 | @fib$S1 = (a (b c)) 6 | & @fib ~ (b $([+] $(d c))) 7 | & @fib ~ (a d) 8 | @fib$S2 = (#1{?((a b) (c d)) c} d) 9 | & (a b) ~ (@fib$S0 @fib$S1) 10 | @main = a 11 | & @fib ~ (30 a) 12 | 13 | -------------------------------------------------------------------------------- /util/src/maybe_grow.rs: -------------------------------------------------------------------------------- 1 | /// Guard against stack overflows in recursive functions. 2 | #[cfg(feature = "std")] 3 | pub fn maybe_grow(f: impl FnOnce() -> R) -> R { 4 | stacker::maybe_grow(1024 * 32, 1024 * 1024, f) 5 | } 6 | 7 | #[cfg(not(feature = "std"))] 8 | pub fn maybe_grow(f: impl FnOnce() -> R) -> R { 9 | f() 10 | } 11 | -------------------------------------------------------------------------------- /tests/snapshots/run@tests::programs::era_era.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/era_era.hvm 5 | --- 6 | * 7 | RWTS : 3 8 | - ANNI : 0 9 | - COMM : 0 10 | - ERAS : 1 11 | - DREF : 2 12 | - OPER : 0 13 | -------------------------------------------------------------------------------- /num/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hvm64-num" 3 | version.workspace = true 4 | edition = "2021" 5 | 6 | [lib] 7 | path = "src/num.rs" 8 | 9 | [dependencies] 10 | hvm64-util = { path = "../util", default-features = false } 11 | 12 | [features] 13 | default = ["std"] 14 | std = ["hvm64-util/std"] 15 | 16 | [lints] 17 | workspace = true 18 | -------------------------------------------------------------------------------- /tests/snapshots/run@tests::programs::bool_and.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/bool_and.hvm 5 | --- 6 | (* (a a)) 7 | RWTS : 14 8 | - ANNI : 4 9 | - COMM : 0 10 | - ERAS : 1 11 | - DREF : 9 12 | - OPER : 0 13 | -------------------------------------------------------------------------------- /tests/snapshots/run@tests::programs::commutation.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/commutation.hvm 5 | --- 6 | (a a) 7 | RWTS : 7 8 | - ANNI : 1 9 | - COMM : 2 10 | - ERAS : 1 11 | - DREF : 3 12 | - OPER : 0 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !*.* 3 | !*/ 4 | ### Above combination will ignore all files without extension ### 5 | ### https://stackoverflow.com/questions/5711120/gitignore-binary-files-that-have-no-extension ### 6 | 7 | target/ 8 | tmp/ 9 | .hvm/ 10 | .hvmx/ 11 | **/.DS_Store 12 | 13 | flamegraph.svg 14 | .vscode/ 15 | 16 | # nix 17 | .envrc 18 | /.direnv/ 19 | -------------------------------------------------------------------------------- /tests/snapshots/run@examples::machine_u32::num_add.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: examples/machine_u32/num_add.hvm 5 | --- 6 | 223 7 | RWTS : 6 8 | - ANNI : 2 9 | - COMM : 0 10 | - ERAS : 0 11 | - DREF : 3 12 | - OPER : 1 13 | -------------------------------------------------------------------------------- /tests/snapshots/run@examples::machine_u32::num_match.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: examples/machine_u32/num_match.hvm 5 | --- 6 | 9 7 | RWTS : 9 8 | - ANNI : 4 9 | - COMM : 0 10 | - ERAS : 1 11 | - DREF : 3 12 | - OPER : 1 13 | -------------------------------------------------------------------------------- /tests/snapshots/run@tests::programs::chained_ops.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/chained_ops.hvm 5 | --- 6 | 2138224 7 | RWTS : 28 8 | - ANNI : 2 9 | - COMM : 4 10 | - ERAS : 0 11 | - DREF : 2 12 | - OPER : 20 13 | -------------------------------------------------------------------------------- /tests/snapshots/run@tests::programs::dec_bits.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/dec_bits.hvm 5 | --- 6 | (* (* (a a))) 7 | RWTS : 180_043 8 | - ANNI : 98_190 9 | - COMM : 11 10 | - ERAS : 32_738 11 | - DREF : 49_104 12 | - OPER : 0 13 | -------------------------------------------------------------------------------- /tests/snapshots/run@tests::programs::neg_fusion.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/neg_fusion.hvm 5 | --- 6 | (a (* a)) 7 | RWTS : 151 8 | - ANNI : 85 9 | - COMM : 36 10 | - ERAS : 0 11 | - DREF : 30 12 | - OPER : 0 13 | -------------------------------------------------------------------------------- /tests/programs/stress_tests/apelacion.hvm: -------------------------------------------------------------------------------- 1 | @main = a 2 | & @rec ~ (6 a) 3 | @rec = (?((a b) d) d) 4 | & (a b) ~ (c @rec$S0) 5 | & @sum ~ (1000000 (0 c)) 6 | @rec$S0 = (#2{a b} c) 7 | & @rec ~ (a $([+] $(d c))) 8 | & @rec ~ (b d) 9 | @sum = (?((a b) d) d) 10 | & (a b) ~ ((c c) @sum$S0) 11 | @sum$S0 = (#1{a $([+] $(b c))} (b d)) 12 | & @sum ~ (a (c d)) 13 | 14 | -------------------------------------------------------------------------------- /tests/snapshots/run@tests::programs::alloc_small_tree.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/alloc_small_tree.hvm 5 | --- 6 | (a (* a)) 7 | RWTS : 104 8 | - ANNI : 43 9 | - COMM : 13 10 | - ERAS : 17 11 | - DREF : 31 12 | - OPER : 0 13 | -------------------------------------------------------------------------------- /tests/snapshots/run@tests::programs::heavy_pre_reduction.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/heavy_pre_reduction.hvm 5 | --- 6 | * 7 | RWTS : 2 8 | - ANNI : 0 9 | - COMM : 0 10 | - ERAS : 0 11 | - DREF : 2 12 | - OPER : 0 13 | -------------------------------------------------------------------------------- /tests/snapshots/run@examples::sort::radix::radix_sort_ctr.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: examples/sort/radix/radix_sort_ctr.hvm 5 | --- 6 | 16252928 7 | RWTS : 1_376_780_298 8 | - ANNI : 795_869_169 9 | - COMM : 53_477_380 10 | - ERAS : 203_423_749 11 | - DREF : 265_289_735 12 | - OPER : 58_720_265 13 | -------------------------------------------------------------------------------- /tests/snapshots/run@examples::sort::radix::radix_sort_lam.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: examples/sort/radix/radix_sort_lam.hvm 5 | --- 6 | 16252928 7 | RWTS : 1_500_512_246 8 | - ANNI : 846_200_803 9 | - COMM : 28_311_560 10 | - ERAS : 251_658_239 11 | - DREF : 314_572_800 12 | - OPER : 59_768_844 13 | -------------------------------------------------------------------------------- /tests/snapshots/run@tests::programs::stress_tests::boom.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/stress_tests/boom.hvm 5 | --- 6 | (a (* a)) 7 | RWTS : 134_217_907 8 | - ANNI : 33_554_556 9 | - COMM : 67_108_895 10 | - ERAS : 16_777_216 11 | - DREF : 16_777_240 12 | - OPER : 0 13 | -------------------------------------------------------------------------------- /tests/snapshots/run@tests::programs::stress_tests::sum_tree.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/stress_tests/sum_tree.hvm 5 | --- 6 | 0 7 | RWTS : 771_751_906 8 | - ANNI : 369_098_738 9 | - COMM : 83_886_075 10 | - ERAS : 100_663_292 11 | - DREF : 167_772_155 12 | - OPER : 50_331_646 13 | -------------------------------------------------------------------------------- /tests/snapshots/run@tests::programs::stress_tests::all_tree.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/stress_tests/all_tree.hvm 5 | --- 6 | (a (* a)) 7 | RWTS : 822_083_556 8 | - ANNI : 402_653_168 9 | - COMM : 83_886_075 10 | - ERAS : 117_440_507 11 | - DREF : 184_549_375 12 | - OPER : 33_554_431 13 | -------------------------------------------------------------------------------- /tests/snapshots/run@tests::programs::stress_tests::fib_rec.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/stress_tests/fib_rec.hvm 5 | --- 6 | 1346269 7 | RWTS : 39_284_572 8 | - ANNI : 18_137_111 9 | - COMM : 2_178_308 10 | - ERAS : 5_702_885 11 | - DREF : 7_049_155 12 | - OPER : 6_217_113 13 | -------------------------------------------------------------------------------- /tests/snapshots/run@tests::programs::stress_tests::sum_rec.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/stress_tests/sum_rec.hvm 5 | --- 6 | 8388608 7 | RWTS : 159_383_541 8 | - ANNI : 75_497_466 9 | - COMM : 8_388_607 10 | - ERAS : 16_777_215 11 | - DREF : 33_554_431 12 | - OPER : 25_165_822 13 | -------------------------------------------------------------------------------- /tests/snapshots/run@tests::programs::stress_tests::sum_tail.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/stress_tests/sum_tail.hvm 5 | --- 6 | 15783104 7 | RWTS : 120_000_009 8 | - ANNI : 50_000_004 9 | - COMM : 20_000_000 10 | - ERAS : 10_000_001 11 | - DREF : 20_000_003 12 | - OPER : 20_000_001 13 | -------------------------------------------------------------------------------- /tests/snapshots/run@examples::arithmetic.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: examples/arithmetic.hvm 5 | --- 6 | (#1{$([/] $(a b)) $([%] $(c d))} (#2{a c} {b d})) 7 | RWTS : 18 8 | - ANNI : 4 9 | - COMM : 0 10 | - ERAS : 0 11 | - DREF : 14 12 | - OPER : 0 13 | -------------------------------------------------------------------------------- /tests/snapshots/run@examples::sort::bitonic::bitonic_sort_lam.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: examples/sort/bitonic/bitonic_sort_lam.hvm 5 | --- 6 | 523776 7 | RWTS : 2_293_690 8 | - ANNI : 1_375_188 9 | - COMM : 96_766 10 | - ERAS : 222_714 11 | - DREF : 508_403 12 | - OPER : 90_619 13 | -------------------------------------------------------------------------------- /tests/snapshots/run@tests::programs::stress_tests::apelacion.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/stress_tests/apelacion.hvm 5 | --- 6 | 12171264 7 | RWTS : 1_524_001_905 8 | - ANNI : 635_000_952 9 | - COMM : 254_000_063 10 | - ERAS : 127_000_254 11 | - DREF : 254_000_319 12 | - OPER : 254_000_317 13 | -------------------------------------------------------------------------------- /tests/snapshots/run@examples::lambda_calculus::hoas.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: examples/lambda_calculus/hoas.hvm 5 | --- 6 | (((a (((* a) b) (* (* b)))) c) (* (* c))) 7 | RWTS : 886 8 | - ANNI : 484 9 | - COMM : 11 10 | - ERAS : 141 11 | - DREF : 250 12 | - OPER : 0 13 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2021" 2 | version = "Two" 3 | max_width = 120 4 | # won't add \r\n on windows machines, better on diffs 5 | newline_style = "Unix" 6 | use_small_heuristics = "Max" 7 | tab_spaces = 2 8 | imports_granularity = "Crate" 9 | use_field_init_shorthand = true 10 | use_try_shorthand = true 11 | spaces_around_ranges = true 12 | overflow_delimited_expr = true 13 | wrap_comments = true 14 | -------------------------------------------------------------------------------- /tests/programs/stress_tests/sum_tree.bend: -------------------------------------------------------------------------------- 1 | Leaf = @x @Leaf @Node (Leaf x) 2 | Node = @x0 @x1 @Leaf @Node (Node x0 x1) 3 | 4 | add = λa λb (+ a b) 5 | 6 | gen = λn match n { 7 | 0: (Leaf 1) 8 | 1+: (Node (gen n-1) (gen n-1)) 9 | } 10 | 11 | sum = λt 12 | let case_leaf = λx x 13 | let case_node = λx0 λx1 (add (sum x0) (sum x1)) 14 | (t case_leaf case_node) 15 | 16 | main = (sum (gen 24)) 17 | -------------------------------------------------------------------------------- /tests/snapshots/run@tests::programs::stress_tests::tuple_rots.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/stress_tests/tuple_rots.hvm 5 | --- 6 | ((1 (2 (3 (4 (5 (6 (7 (8 a)))))))) a) 7 | RWTS : 64_000_038 8 | - ANNI : 48_000_013 9 | - COMM : 2_000_000 10 | - ERAS : 2_000_002 11 | - DREF : 10_000_022 12 | - OPER : 2_000_001 13 | -------------------------------------------------------------------------------- /tests/snapshots/run@tests::programs::u24.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/u24.hvm 5 | --- 6 | {12 {8 {20 {5 {0 {0 {1 {0 {1 {2 {10 {8 {40 {2 {16777215 {0 {3 *}}}}}}}}}}}}}}}}} 7 | RWTS : 108 8 | - ANNI : 17 9 | - COMM : 1 10 | - ERAS : 18 11 | - DREF : 55 12 | - OPER : 17 13 | -------------------------------------------------------------------------------- /examples/church_encoding/church.hvm: -------------------------------------------------------------------------------- 1 | @S = ((a (b c)) (#1{(c d) a} (b d))) 2 | @Z = (* (a a)) 3 | @add = ((a (b c)) ((d (e b)) (#4{a d} (e c)))) 4 | @c2 = (#2{(a b) (c a)} (c b)) 5 | @c3 = (#3{(a b) #3{(c a) (d c)}} (d b)) 6 | @c4 = a 7 | & @S ~ (b a) 8 | & @S ~ (c b) 9 | & @S ~ (d c) 10 | & @S ~ (@Z d) 11 | @main = a 12 | & @add ~ (b (@c4 a)) 13 | & @mul ~ (@c2 (@c3 b)) 14 | @mul = ((a (b c)) ((d a) (d (b c)))) 15 | 16 | -------------------------------------------------------------------------------- /tests/snapshots/run@tests::programs::i24.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/i24.hvm 5 | --- 6 | {+12 {+8 {+20 {+5 {+0 {0 {1 {0 {1 {+2 {+10 {+8 {+8388607 {-8388608 {+1 {+1 {-1 {-1 *}}}}}}}}}}}}}}}}}} 7 | RWTS : 114 8 | - ANNI : 18 9 | - COMM : 1 10 | - ERAS : 19 11 | - DREF : 58 12 | - OPER : 18 13 | -------------------------------------------------------------------------------- /tests/snapshots/run@tests::programs::queue.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/queue.hvm 5 | --- 6 | (((* (a a)) (((((b c) (b c)) ((((#1{(d e) (f d)} (f e)) ((* (g g)) h)) (* h)) i)) (* i)) j)) (* j)) 7 | RWTS : 102 8 | - ANNI : 39 9 | - COMM : 1 10 | - ERAS : 4 11 | - DREF : 58 12 | - OPER : 0 13 | -------------------------------------------------------------------------------- /util/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "std"), no_std)] 2 | 3 | pub mod prelude; 4 | 5 | mod bi_enum; 6 | mod deref; 7 | mod multi_iterator; 8 | 9 | mod create_var; 10 | mod maybe_grow; 11 | mod new_uninit_slice; 12 | mod parse_abbrev_number; 13 | mod pretty_num; 14 | 15 | pub use create_var::*; 16 | pub use maybe_grow::*; 17 | pub use new_uninit_slice::*; 18 | pub use parse_abbrev_number::*; 19 | pub use pretty_num::*; 20 | -------------------------------------------------------------------------------- /.github/workflows/delete-cancelled.yml: -------------------------------------------------------------------------------- 1 | name: Delete Cancelled Benchmarks 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | run_id: 7 | type: string 8 | description: "" 9 | 10 | jobs: 11 | delete: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - run: gh api "repos/higherorderco/hvm-64/actions/runs/${{ inputs.run_id }}" -X DELETE 15 | env: 16 | GH_TOKEN: ${{ secrets.PAT }} 17 | -------------------------------------------------------------------------------- /tests/snapshots/run@examples::church_encoding::church.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: examples/church_encoding/church.hvm 5 | --- 6 | (#4{#3{(a #2{b c}) #3{(d a) (#2{c e} d)}} #1{(f e) #1{(g f) #1{(h g) #1{(i h) *}}}}} (i b)) 7 | RWTS : 65 8 | - ANNI : 25 9 | - COMM : 1 10 | - ERAS : 0 11 | - DREF : 39 12 | - OPER : 0 13 | -------------------------------------------------------------------------------- /util/src/new_uninit_slice.rs: -------------------------------------------------------------------------------- 1 | use alloc::alloc::alloc; 2 | use core::{alloc::Layout, mem::MaybeUninit, slice}; 3 | 4 | use crate::prelude::*; 5 | 6 | // TODO: use `Box::new_uninit_slice` once stabilized 7 | // https://github.com/rust-lang/rust/issues/63291 8 | pub fn new_uninit_slice(len: usize) -> Box<[MaybeUninit]> { 9 | unsafe { Box::from_raw(slice::from_raw_parts_mut(alloc(Layout::array::(len).unwrap()).cast(), len)) } 10 | } 11 | -------------------------------------------------------------------------------- /runtime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hvm64-runtime" 3 | version.workspace = true 4 | edition = "2021" 5 | 6 | [lib] 7 | path = "src/runtime.rs" 8 | 9 | [dependencies] 10 | hvm64-util = { path = "../util", default-features = false } 11 | hvm64-num = { path = "../num", default-features = false } 12 | 13 | [features] 14 | default = ["std"] 15 | std = ["hvm64-util/std", "hvm64-num/std"] 16 | trace = ["std"] 17 | 18 | [lints] 19 | workspace = true 20 | -------------------------------------------------------------------------------- /tests/programs/alloc_small_tree.bend: -------------------------------------------------------------------------------- 1 | T = λt λf t 2 | F = λt λf f 3 | And = λpλq (p q F) 4 | 5 | Z = λs λz (z) 6 | S = λn λs λz (s n) 7 | 8 | Add = λa λb (a (S b)) 9 | Mul = λa λb λf (a (b f)) 10 | Pow = λa λb (a (Mul b) (S Z)) 11 | 12 | Node = λa λb λn λl (n a b) 13 | Leaf = λn λl l 14 | 15 | Alloc = λn (n (λp (Node (Alloc p) (Alloc p))) Leaf) 16 | Destroy = λt (t (λaλb (And (Destroy a) (Destroy b))) T) 17 | 18 | main = (Destroy (Alloc (Pow (S (S Z)) (S (S (S (S Z))))))) -------------------------------------------------------------------------------- /tests/programs/stress_tests/all_tree.bend: -------------------------------------------------------------------------------- 1 | True = @True @False True 2 | False = @True @False False 3 | 4 | Node = @lft @rgt @Node @Leaf (Node lft rgt) 5 | Leaf = @val @Node @Leaf (Leaf val) 6 | 7 | and = λa (a λb(b) λb(False)) 8 | 9 | gen = λn match n { 10 | 0: (Leaf True) 11 | 1+: (Node (gen n-1) (gen n-1)) 12 | } 13 | 14 | all = λt 15 | let case_node = λa λb (and (all a) (all b)) 16 | let case_leaf = λv v 17 | (t case_node case_leaf) 18 | 19 | main = (all (gen 24)) 20 | -------------------------------------------------------------------------------- /tests/programs/stress_tests/sum_tree.hvm: -------------------------------------------------------------------------------- 1 | @Leaf = (a ((a b) (* b))) 2 | @Node = (a (b (* ((a (b c)) c)))) 3 | @add = ($([+] $(a b)) (a b)) 4 | @gen = (?((a b) d) d) 5 | & (a b) ~ (c @gen$S0) 6 | & @Leaf ~ (1 c) 7 | @gen$S0 = (#1{a b} c) 8 | & @Node ~ (d (e c)) 9 | & @gen ~ (b e) 10 | & @gen ~ (a d) 11 | @main = a 12 | & @sum ~ (b a) 13 | & @gen ~ (24 b) 14 | @sum = (((a a) (@sum$S0 b)) b) 15 | @sum$S0 = (a (b c)) 16 | & @add ~ (d (e c)) 17 | & @sum ~ (b e) 18 | & @sum ~ (a d) 19 | 20 | -------------------------------------------------------------------------------- /util/src/deref.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! deref_to { 3 | ($({$($gen:tt)*})? $ty:ty => self.$field:ident: $trg:ty) => { 4 | impl $($($gen)*)? core::ops::Deref for $ty { 5 | type Target = $trg; 6 | fn deref(&self) -> &Self::Target { 7 | &self.$field 8 | } 9 | } 10 | impl $($($gen)*)? core::ops::DerefMut for $ty { 11 | fn deref_mut(&mut self) -> &mut Self::Target { 12 | &mut self.$field 13 | } 14 | } 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /util/src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub extern crate alloc; 2 | 3 | pub use alloc::{ 4 | borrow::ToOwned, 5 | boxed::Box, 6 | format, 7 | string::{String, ToString}, 8 | vec, 9 | vec::Vec, 10 | }; 11 | 12 | pub use core::{fmt, hint, iter, mem, ptr}; 13 | 14 | #[cfg(feature = "std")] 15 | pub use std::collections::{hash_map::Entry, HashMap as Map, HashSet as Set}; 16 | 17 | #[cfg(not(feature = "std"))] 18 | pub use alloc::collections::{btree_map::Entry, BTreeMap as Map, BTreeSet as Set}; 19 | -------------------------------------------------------------------------------- /tests/snapshots/run@examples::sort::merge::merge_sort.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: examples/sort/merge/merge_sort.hvm 5 | --- 6 | #2(* #2(#2(#2(* #2(#2(#2(#2(3 a) #2(* a)) #2(#2(#2(2 b) #2(* b)) c)) c)) #2(#2(* #2(#2(#2(#2(1 d) #2(* d)) #2(#2(#2(0 e) #2(* e)) f)) f)) g)) g)) 7 | RWTS : 474 8 | - ANNI : 71 9 | - COMM : 242 10 | - ERAS : 79 11 | - DREF : 66 12 | - OPER : 16 13 | -------------------------------------------------------------------------------- /tests/snapshots/run@tests::programs::f24.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/f24.hvm 5 | --- 6 | {+inf {-inf {+NaN {2.5 {-1.5 {1.1499939 {0.25 {0.5 {0 {1 {1 {0 {0 {0 {0 {+NaN {+inf {-inf {1.019989 {0.1000061 {0.1000061 {-0.1000061 {-0.1000061 *}}}}}}}}}}}}}}}}}}}}}}} 7 | RWTS : 174 8 | - ANNI : 23 9 | - COMM : 1 10 | - ERAS : 24 11 | - DREF : 88 12 | - OPER : 38 13 | -------------------------------------------------------------------------------- /tests/programs/stress_tests/tuple_rots.hvm: -------------------------------------------------------------------------------- 1 | @MkTup8 = (a (b (c (d (e (f (g (h ((a (b (c (d (e (f (g (h i)))))))) i))))))))) 2 | @app = (?((a b) c) c) 3 | & (a b) ~ (@app$S0 @app$S1) 4 | @app$S0 = (* (a a)) 5 | @app$S1 = (a (#1{b (c d)} (c e))) 6 | & @app ~ (a (b (d e))) 7 | @main = a 8 | & @app ~ (2000000 (@rot (b a))) 9 | & @MkTup8 ~ (1 (2 (3 (4 (5 (6 (7 (8 b)))))))) 10 | @rot = ((@rot$S0 a) a) 11 | @rot$S0 = (a (b (c (d (e (f (g (h i)))))))) 12 | & @MkTup8 ~ (b (c (d (e (f (g (h (a i)))))))) 13 | 14 | -------------------------------------------------------------------------------- /tests/programs/stress_tests/all_tree.hs: -------------------------------------------------------------------------------- 1 | data Bool = True | False 2 | data Tree = Node Tree Tree | Leaf Bool 3 | 4 | and' :: Bool -> Bool -> Bool 5 | and' a b = case a of 6 | True -> b 7 | False -> False 8 | 9 | gen :: Int -> Tree 10 | gen n = case n of 11 | 0 -> Leaf True 12 | p -> let p' = p - 1 in Node (gen p') (gen p') 13 | 14 | all' :: Tree -> Bool 15 | all' t = case t of 16 | Node a b -> and' (all' a) (all' b) 17 | Leaf v -> v 18 | 19 | main :: Bool 20 | main = all' (gen 22) 21 | 22 | -------------------------------------------------------------------------------- /tests/programs/chained_ops.hvm: -------------------------------------------------------------------------------- 1 | @main = a 2 | & (#1{$([*] $(b $([-] $(c $([*] $(d e)))))) #1{$([*] $(f $([:-20] g))) h}} (#2{i #2{j k}} e)) ~ (70 (50 a)) 3 | & 70 ~ $([*] $(k l)) 4 | & 80 ~ $([+] $(l m)) 5 | & 10 ~ $([*] $(m $([+] $(n d)))) 6 | & 80 ~ $([+] $(o n)) 7 | & 70 ~ $([*] $(h o)) 8 | & 70 ~ $([*] $(j p)) 9 | & 80 ~ $([+] $(p q)) 10 | & 10 ~ $([*] $(q c)) 11 | & 70 ~ $([*] $(i r)) 12 | & 80 ~ $([+] $(r s)) 13 | & 10 ~ $([*] $(s $([:-20] t))) 14 | & 178 ~ $([+] $(t f)) 15 | & 178 ~ $([+] $(g b)) 16 | 17 | -------------------------------------------------------------------------------- /tests/snapshots/run@tests::programs::stress_tests::burn.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/stress_tests/burn.hvm 5 | --- 6 | ((((* (* (* (a a)))) (* (* (* (b b))))) ((* (* (* (c c)))) (* (* (* (d d)))))) (((* (* (* (e e)))) (* (* (* (f f))))) ((* (* (* (g g)))) (* (* (* (h h))))))) 7 | RWTS : 369_096_639 8 | - ANNI : 201_325_229 9 | - COMM : 177 10 | - ERAS : 67_108_511 11 | - DREF : 100_662_722 12 | - OPER : 0 13 | -------------------------------------------------------------------------------- /tests/programs/neg_fusion.hvm: -------------------------------------------------------------------------------- 1 | @C2 = (#1{(a b) (c a)} (c b)) 2 | @False = (* (a a)) 3 | @Mul = ((a b) ((c a) (c b))) 4 | @Neg = ((a (b c)) (b (a c))) 5 | @Not = ((@False (@True a)) a) 6 | @P2 = a 7 | & @Mul ~ (@C2 (@C2 a)) 8 | @P3 = a 9 | & @Mul ~ (@C2 (@P2 a)) 10 | @P4 = a 11 | & @Mul ~ (@C2 (@P3 a)) 12 | @P5 = a 13 | & @Mul ~ (@C2 (@P4 a)) 14 | @P6 = a 15 | & @Mul ~ (@C2 (@P5 a)) 16 | @P7 = a 17 | & @Mul ~ (@C2 (@P6 a)) 18 | @P8 = a 19 | & @Mul ~ (@C2 (@P7 a)) 20 | @True = (a (* a)) 21 | @main = a 22 | & @P8 ~ (@Neg (@True a)) 23 | 24 | -------------------------------------------------------------------------------- /ast/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hvm64-ast" 3 | version.workspace = true 4 | edition = "2021" 5 | 6 | [lib] 7 | path = "src/ast.rs" 8 | 9 | [dependencies] 10 | TSPL = { git = "https://github.com/tjjfvi/TSPL", branch = "no_std", optional = true } 11 | 12 | hvm64-util = { path = "../util", default-features = false } 13 | hvm64-num = { path = "../num", default-features = false } 14 | 15 | [features] 16 | default = ["std", "parser"] 17 | std = ["hvm64-util/std", "hvm64-num/std"] 18 | parser = ["dep:TSPL"] 19 | 20 | [lints] 21 | workspace = true 22 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@tests::programs::era_era.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/era_era.hvm 5 | --- 6 | * 7 | pre-reduce: 8 | RWTS : 0 9 | - ANNI : 0 10 | - COMM : 0 11 | - ERAS : 0 12 | - DREF : 0 13 | - OPER : 0 14 | run: 15 | RWTS : 3 16 | - ANNI : 0 17 | - COMM : 0 18 | - ERAS : 1 19 | - DREF : 2 20 | - OPER : 0 21 | -------------------------------------------------------------------------------- /tests/programs/stress_tests/all_tree.hvm: -------------------------------------------------------------------------------- 1 | @False = (* (a a)) 2 | @Leaf = (a (* ((a b) b))) 3 | @Node = (a (b ((a (b c)) (* c)))) 4 | @True = (a (* a)) 5 | @all = ((@all$S0 ((a a) b)) b) 6 | @all$S0 = (a (b c)) 7 | & @and ~ (d (e c)) 8 | & @all ~ (b e) 9 | & @all ~ (a d) 10 | @and = (((a a) (@and$S0 b)) b) 11 | @and$S0 = (* @False) 12 | @gen = (?((a b) d) d) 13 | & (a b) ~ (c @gen$S0) 14 | & @Leaf ~ (@True c) 15 | @gen$S0 = (#1{a b} c) 16 | & @Node ~ (d (e c)) 17 | & @gen ~ (b e) 18 | & @gen ~ (a d) 19 | @main = a 20 | & @all ~ (b a) 21 | & @gen ~ (24 b) 22 | 23 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@examples::machine_u32::num_add.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: examples/machine_u32/num_add.hvm 5 | --- 6 | 223 7 | pre-reduce: 8 | RWTS : 1 9 | - ANNI : 0 10 | - COMM : 0 11 | - ERAS : 0 12 | - DREF : 1 13 | - OPER : 0 14 | run: 15 | RWTS : 6 16 | - ANNI : 2 17 | - COMM : 0 18 | - ERAS : 0 19 | - DREF : 3 20 | - OPER : 1 21 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@tests::programs::bool_and.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/bool_and.hvm 5 | --- 6 | (* (a a)) 7 | pre-reduce: 8 | RWTS : 3 9 | - ANNI : 0 10 | - COMM : 0 11 | - ERAS : 0 12 | - DREF : 3 13 | - OPER : 0 14 | run: 15 | RWTS : 14 16 | - ANNI : 4 17 | - COMM : 0 18 | - ERAS : 1 19 | - DREF : 9 20 | - OPER : 0 21 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@tests::programs::chained_ops.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/chained_ops.hvm 5 | --- 6 | 2138224 7 | pre-reduce: 8 | RWTS : 0 9 | - ANNI : 0 10 | - COMM : 0 11 | - ERAS : 0 12 | - DREF : 0 13 | - OPER : 0 14 | run: 15 | RWTS : 28 16 | - ANNI : 2 17 | - COMM : 4 18 | - ERAS : 0 19 | - DREF : 2 20 | - OPER : 20 21 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@tests::programs::commutation.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/commutation.hvm 5 | --- 6 | (a a) 7 | pre-reduce: 8 | RWTS : 0 9 | - ANNI : 0 10 | - COMM : 0 11 | - ERAS : 0 12 | - DREF : 0 13 | - OPER : 0 14 | run: 15 | RWTS : 7 16 | - ANNI : 1 17 | - COMM : 2 18 | - ERAS : 1 19 | - DREF : 3 20 | - OPER : 0 21 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@tests::programs::dec_bits.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/dec_bits.hvm 5 | --- 6 | (* (* (a a))) 7 | pre-reduce: 8 | RWTS : 99 9 | - ANNI : 32 10 | - COMM : 0 11 | - ERAS : 8 12 | - DREF : 59 13 | - OPER : 0 14 | run: 15 | RWTS : 71_625 16 | - ANNI : 40_910 17 | - COMM : 11 18 | - ERAS : 20_454 19 | - DREF : 10_250 20 | - OPER : 0 21 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@tests::programs::neg_fusion.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/neg_fusion.hvm 5 | --- 6 | (a (* a)) 7 | pre-reduce: 8 | RWTS : 83 9 | - ANNI : 42 10 | - COMM : 7 11 | - ERAS : 0 12 | - DREF : 34 13 | - OPER : 0 14 | run: 15 | RWTS : 81 16 | - ANNI : 43 17 | - COMM : 29 18 | - ERAS : 0 19 | - DREF : 9 20 | - OPER : 0 21 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@examples::machine_u32::num_match.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: examples/machine_u32/num_match.hvm 5 | --- 6 | 9 7 | pre-reduce: 8 | RWTS : 2 9 | - ANNI : 1 10 | - COMM : 0 11 | - ERAS : 0 12 | - DREF : 1 13 | - OPER : 0 14 | run: 15 | RWTS : 8 16 | - ANNI : 3 17 | - COMM : 0 18 | - ERAS : 1 19 | - DREF : 3 20 | - OPER : 1 21 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@tests::programs::heavy_pre_reduction.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/heavy_pre_reduction.hvm 5 | --- 6 | * 7 | pre-reduce: 8 | RWTS : 53_165 9 | - ANNI : 13_137 10 | - COMM : 35_175 11 | - ERAS : 4_794 12 | - DREF : 59 13 | - OPER : 0 14 | run: 15 | RWTS : 2 16 | - ANNI : 0 17 | - COMM : 0 18 | - ERAS : 0 19 | - DREF : 2 20 | - OPER : 0 21 | -------------------------------------------------------------------------------- /tests/programs/neg_fusion.bend: -------------------------------------------------------------------------------- 1 | // size = 1 << 8 2 | 3 | Mul = λn λm λs (n (m s)) 4 | 5 | // Church nat 6 | C2 = λf λx (f (f x)) 7 | 8 | // Church powers of two 9 | P2 = (Mul C2 C2) // 4 10 | P3 = (Mul C2 P2) // 8 11 | P4 = (Mul C2 P3) // 16 12 | P5 = (Mul C2 P4) // 32 13 | P6 = (Mul C2 P5) // 64 14 | P7 = (Mul C2 P6) // 128 15 | P8 = (Mul C2 P7) // 256 16 | 17 | // Booleans 18 | True = λt λf t 19 | False = λt λf f 20 | Not = λb ((b False) True) 21 | Neg = λb λt λf (b f t) 22 | 23 | // Negates 'true' 256 times: 'neg' is faster than 'not' because it fuses 24 | Main = (P8 Neg True) -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@tests::programs::alloc_small_tree.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/alloc_small_tree.hvm 5 | --- 6 | (a (* a)) 7 | pre-reduce: 8 | RWTS : 34 9 | - ANNI : 11 10 | - COMM : 0 11 | - ERAS : 0 12 | - DREF : 23 13 | - OPER : 0 14 | run: 15 | RWTS : 86 16 | - ANNI : 33 17 | - COMM : 13 18 | - ERAS : 17 19 | - DREF : 23 20 | - OPER : 0 21 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@tests::programs::stress_tests::boom.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/stress_tests/boom.hvm 5 | --- 6 | (a (* a)) 7 | pre-reduce: 8 | RWTS : 24 9 | - ANNI : 0 10 | - COMM : 0 11 | - ERAS : 0 12 | - DREF : 24 13 | - OPER : 0 14 | run: 15 | RWTS : 134_217_907 16 | - ANNI : 33_554_556 17 | - COMM : 67_108_895 18 | - ERAS : 16_777_216 19 | - DREF : 16_777_240 20 | - OPER : 0 21 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@tests::programs::stress_tests::sum_tree.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/stress_tests/sum_tree.hvm 5 | --- 6 | 0 7 | pre-reduce: 8 | RWTS : 30 9 | - ANNI : 14 10 | - COMM : 0 11 | - ERAS : 0 12 | - DREF : 16 13 | - OPER : 0 14 | run: 15 | RWTS : 436_207_603 16 | - ANNI : 167_772_156 17 | - COMM : 83_886_075 18 | - ERAS : 100_663_292 19 | - DREF : 33_554_434 20 | - OPER : 50_331_646 21 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@examples::sort::radix::radix_sort_ctr.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: examples/sort/radix/radix_sort_ctr.hvm 5 | --- 6 | 16252928 7 | pre-reduce: 8 | RWTS : 150 9 | - ANNI : 63 10 | - COMM : 0 11 | - ERAS : 0 12 | - DREF : 87 13 | - OPER : 0 14 | run: 15 | RWTS : 988_282_897 16 | - ANNI : 549_453_814 17 | - COMM : 53_477_380 18 | - ERAS : 203_423_749 19 | - DREF : 123_207_689 20 | - OPER : 58_720_265 21 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@examples::sort::radix::radix_sort_lam.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: examples/sort/radix/radix_sort_lam.hvm 5 | --- 6 | 16252928 7 | pre-reduce: 8 | RWTS : 153 9 | - ANNI : 63 10 | - COMM : 0 11 | - ERAS : 0 12 | - DREF : 90 13 | - OPER : 0 14 | run: 15 | RWTS : 1_134_559_233 16 | - ANNI : 622_854_123 17 | - COMM : 28_311_560 18 | - ERAS : 251_658_239 19 | - DREF : 171_966_467 20 | - OPER : 59_768_844 21 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@tests::programs::stress_tests::fib_rec.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/stress_tests/fib_rec.hvm 5 | --- 6 | 1346269 7 | pre-reduce: 8 | RWTS : 13 9 | - ANNI : 6 10 | - COMM : 0 11 | - ERAS : 0 12 | - DREF : 7 13 | - OPER : 0 14 | run: 15 | RWTS : 29_028_655 16 | - ANNI : 10_573_730 17 | - COMM : 2_178_308 18 | - ERAS : 5_702_885 19 | - DREF : 4_356_619 20 | - OPER : 6_217_113 21 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@tests::programs::stress_tests::sum_rec.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/stress_tests/sum_rec.hvm 5 | --- 6 | 8388608 7 | pre-reduce: 8 | RWTS : 13 9 | - ANNI : 7 10 | - COMM : 0 11 | - ERAS : 0 12 | - DREF : 6 13 | - OPER : 0 14 | run: 15 | RWTS : 83_886_077 16 | - ANNI : 25_165_823 17 | - COMM : 8_388_607 18 | - ERAS : 16_777_215 19 | - DREF : 8_388_610 20 | - OPER : 25_165_822 21 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@examples::sort::bitonic::bitonic_sort_lam.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: examples/sort/bitonic/bitonic_sort_lam.hvm 5 | --- 6 | 523776 7 | pre-reduce: 8 | RWTS : 130 9 | - ANNI : 51 10 | - COMM : 0 11 | - ERAS : 1 12 | - DREF : 78 13 | - OPER : 0 14 | run: 15 | RWTS : 1_563_105 16 | - ANNI : 944_620 17 | - COMM : 96_766 18 | - ERAS : 221_691 19 | - DREF : 209_409 20 | - OPER : 90_619 21 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@tests::programs::stress_tests::all_tree.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/stress_tests/all_tree.hvm 5 | --- 6 | (a (* a)) 7 | pre-reduce: 8 | RWTS : 32 9 | - ANNI : 13 10 | - COMM : 0 11 | - ERAS : 0 12 | - DREF : 19 13 | - OPER : 0 14 | run: 15 | RWTS : 503_316_468 16 | - ANNI : 218_103_801 17 | - COMM : 83_886_075 18 | - ERAS : 117_440_507 19 | - DREF : 50_331_654 20 | - OPER : 33_554_431 21 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@tests::programs::stress_tests::apelacion.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/stress_tests/apelacion.hvm 5 | --- 6 | 12171264 7 | pre-reduce: 8 | RWTS : 33_000_035 9 | - ANNI : 13_000_019 10 | - COMM : 6_000_000 11 | - ERAS : 3_000_003 12 | - DREF : 5_000_010 13 | - OPER : 6_000_003 14 | run: 15 | RWTS : 637 16 | - ANNI : 191 17 | - COMM : 63 18 | - ERAS : 127 19 | - DREF : 66 20 | - OPER : 190 21 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@tests::programs::stress_tests::sum_tail.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/stress_tests/sum_tail.hvm 5 | --- 6 | 15783104 7 | pre-reduce: 8 | RWTS : 6 9 | - ANNI : 3 10 | - COMM : 0 11 | - ERAS : 0 12 | - DREF : 3 13 | - OPER : 0 14 | run: 15 | RWTS : 90_000_008 16 | - ANNI : 30_000_003 17 | - COMM : 20_000_000 18 | - ERAS : 10_000_001 19 | - DREF : 10_000_003 20 | - OPER : 20_000_001 21 | -------------------------------------------------------------------------------- /host/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hvm64-host" 3 | version.workspace = true 4 | edition = "2021" 5 | 6 | [lib] 7 | path = "src/host.rs" 8 | 9 | [dependencies] 10 | hvm64-util = { path = "../util", default-features = false } 11 | hvm64-ast = { path = "../ast", default-features = false } 12 | hvm64-runtime = { path = "../runtime", default-features = false } 13 | hvm64-num = { path = "../num", default-features = false } 14 | 15 | [features] 16 | default = ["std"] 17 | std = ["hvm64-util/std", "hvm64-ast/std", "hvm64-runtime/std", "hvm64-num/std"] 18 | 19 | [lints] 20 | workspace = true 21 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@examples::arithmetic.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: examples/arithmetic.hvm 5 | --- 6 | (#1{$([/] $(a b)) $([%] $(c d))} (#2{a c} {b d})) 7 | pre-reduce: 8 | RWTS : 5 9 | - ANNI : 0 10 | - COMM : 0 11 | - ERAS : 0 12 | - DREF : 5 13 | - OPER : 0 14 | run: 15 | RWTS : 18 16 | - ANNI : 4 17 | - COMM : 0 18 | - ERAS : 0 19 | - DREF : 14 20 | - OPER : 0 21 | -------------------------------------------------------------------------------- /tests/programs/queue.hvm: -------------------------------------------------------------------------------- 1 | @C0 = (* (a a)) 2 | @C1 = ((a b) (a b)) 3 | @C2 = (#1{(a b) (c a)} (c b)) 4 | @Cons = (a (b ((a (b c)) (* c)))) 5 | @Nil = (* (a a)) 6 | @Queue.add = (a ((((a (b c)) c) d) (b d))) 7 | @Queue.new = (a a) 8 | @Queue.rem = ((a ((b (c ((b ((a c) d)) d))) e)) e) 9 | @main = a 10 | & @Queue.rem ~ (b ((* (c d)) a)) 11 | & @Queue.rem ~ (c ((* (e f)) d)) 12 | & @Queue.rem ~ (e ((* (* g)) f)) 13 | & @Cons ~ (@C0 (h g)) 14 | & @Cons ~ (@C1 (i h)) 15 | & @Cons ~ (@C2 (@Nil i)) 16 | & @Queue.add ~ (@C2 (j b)) 17 | & @Queue.add ~ (@C1 (k j)) 18 | & @Queue.add ~ (@C0 (@Queue.new k)) 19 | 20 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@examples::lambda_calculus::hoas.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: examples/lambda_calculus/hoas.hvm 5 | --- 6 | (((a (((* a) b) (* (* b)))) c) (* (* c))) 7 | pre-reduce: 8 | RWTS : 225 9 | - ANNI : 109 10 | - COMM : 0 11 | - ERAS : 0 12 | - DREF : 116 13 | - OPER : 0 14 | run: 15 | RWTS : 598 16 | - ANNI : 331 17 | - COMM : 11 18 | - ERAS : 141 19 | - DREF : 115 20 | - OPER : 0 21 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@tests::programs::stress_tests::tuple_rots.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/stress_tests/tuple_rots.hvm 5 | --- 6 | ((1 (2 (3 (4 (5 (6 (7 (8 a)))))))) a) 7 | pre-reduce: 8 | RWTS : 19 9 | - ANNI : 11 10 | - COMM : 0 11 | - ERAS : 0 12 | - DREF : 8 13 | - OPER : 0 14 | run: 15 | RWTS : 40_000_037 16 | - ANNI : 28_000_012 17 | - COMM : 2_000_000 18 | - ERAS : 2_000_002 19 | - DREF : 6_000_022 20 | - OPER : 2_000_001 21 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@tests::programs::u24.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/u24.hvm 5 | --- 6 | {12 {8 {20 {5 {0 {0 {1 {0 {1 {2 {10 {8 {40 {2 {16777215 {0 {3 *}}}}}}}}}}}}}}}}} 7 | pre-reduce: 8 | RWTS : 85 9 | - ANNI : 16 10 | - COMM : 1 11 | - ERAS : 17 12 | - DREF : 34 13 | - OPER : 17 14 | run: 15 | RWTS : 41 16 | - ANNI : 1 17 | - COMM : 0 18 | - ERAS : 1 19 | - DREF : 39 20 | - OPER : 0 21 | -------------------------------------------------------------------------------- /tests/snapshots/run@tests::programs::numeric_casts.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/numeric_casts.hvm 5 | --- 6 | {0 {1234 {4321 {16771538 {2 {0 {16777215 {16777215 {0 {0 {+0 {+1234 {+4321 {-5678 {+2 {-12 {+8388607 {-8388608 {+8388607 {-8388608 {+0 {+NaN {+inf {-inf {2.1500244 {-2.1500244 {0.15000153 {-1234.0 {1234.0 {123456.0 {16775936.0 {[u24] {[i24] {[f24] *}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} 7 | RWTS : 207 8 | - ANNI : 34 9 | - COMM : 1 10 | - ERAS : 35 11 | - DREF : 106 12 | - OPER : 31 13 | -------------------------------------------------------------------------------- /tests/programs/heavy_pre_reduction.bend: -------------------------------------------------------------------------------- 1 | C2 = λfλx(f (f x)) 2 | C6 = λfλx(f (f (f (f (f (f x)))))) 3 | C1 = λfλx(f x) 4 | C4 = λfλx(f (f (f (f x)))) 5 | C3 = λfλx(f (f (f x))) 6 | plus = λmλnλfλx(m f (n f x)) 7 | fib = 8 | λn 9 | let (*, r) = (n λx(let (a, b) = x; (b, (plus a b))) (C1, C1)) 10 | r 11 | 12 | expensive = (fib (C4 C2)) 13 | 14 | erase = λn λx (n black_box x) 15 | black_plus = (black_box plus) 16 | expensive_1 = (fib (C4 C2)) 17 | expensive_2 = (fib (C4 C2)) 18 | 19 | main_fast = (black_plus expensive expensive ) 20 | main_slow = (black_plus expensive_1 expensive_2 ) 21 | main = * 22 | 23 | black_box = @x x 24 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@tests::programs::i24.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/i24.hvm 5 | --- 6 | {+12 {+8 {+20 {+5 {+0 {0 {1 {0 {1 {+2 {+10 {+8 {+8388607 {-8388608 {+1 {+1 {-1 {-1 *}}}}}}}}}}}}}}}}}} 7 | pre-reduce: 8 | RWTS : 90 9 | - ANNI : 17 10 | - COMM : 1 11 | - ERAS : 18 12 | - DREF : 36 13 | - OPER : 18 14 | run: 15 | RWTS : 43 16 | - ANNI : 1 17 | - COMM : 0 18 | - ERAS : 1 19 | - DREF : 41 20 | - OPER : 0 21 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@tests::programs::queue.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/queue.hvm 5 | --- 6 | (((* (a a)) (((((b c) (b c)) ((((#1{(d e) (f d)} (f e)) ((* (g g)) h)) (* h)) i)) (* i)) j)) (* j)) 7 | pre-reduce: 8 | RWTS : 8 9 | - ANNI : 0 10 | - COMM : 0 11 | - ERAS : 0 12 | - DREF : 8 13 | - OPER : 0 14 | run: 15 | RWTS : 102 16 | - ANNI : 39 17 | - COMM : 1 18 | - ERAS : 4 19 | - DREF : 58 20 | - OPER : 0 21 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@examples::church_encoding::church.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: examples/church_encoding/church.hvm 5 | --- 6 | (#4{#3{(a #2{b c}) #3{(d a) (#2{c e} d)}} #1{(f e) #1{(g f) #1{(h g) #1{(i h) *}}}}} (i b)) 7 | pre-reduce: 8 | RWTS : 24 9 | - ANNI : 12 10 | - COMM : 0 11 | - ERAS : 0 12 | - DREF : 12 13 | - OPER : 0 14 | run: 15 | RWTS : 48 16 | - ANNI : 13 17 | - COMM : 1 18 | - ERAS : 0 19 | - DREF : 34 20 | - OPER : 0 21 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@examples::sort::merge::merge_sort.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: examples/sort/merge/merge_sort.hvm 5 | --- 6 | #2(* #2(#2(#2(* #2(#2(#2(#2(3 a) #2(* a)) #2(#2(#2(2 b) #2(* b)) c)) c)) #2(#2(* #2(#2(#2(#2(1 d) #2(* d)) #2(#2(#2(0 e) #2(* e)) f)) f)) g)) g)) 7 | pre-reduce: 8 | RWTS : 53 9 | - ANNI : 21 10 | - COMM : 0 11 | - ERAS : 0 12 | - DREF : 32 13 | - OPER : 0 14 | run: 15 | RWTS : 438 16 | - ANNI : 48 17 | - COMM : 242 18 | - ERAS : 79 19 | - DREF : 53 20 | - OPER : 16 21 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@tests::programs::f24.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/f24.hvm 5 | --- 6 | {+inf {-inf {+NaN {2.5 {-1.5 {1.1499939 {0.25 {0.5 {0 {1 {1 {0 {0 {0 {0 {+NaN {+inf {-inf {1.019989 {0.1000061 {0.1000061 {-0.1000061 {-0.1000061 *}}}}}}}}}}}}}}}}}}}}}}} 7 | pre-reduce: 8 | RWTS : 134 9 | - ANNI : 22 10 | - COMM : 1 11 | - ERAS : 23 12 | - DREF : 63 13 | - OPER : 25 14 | run: 15 | RWTS : 53 16 | - ANNI : 1 17 | - COMM : 0 18 | - ERAS : 1 19 | - DREF : 51 20 | - OPER : 0 21 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@tests::programs::stress_tests::burn.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/stress_tests/burn.hvm 5 | --- 6 | ((((* (* (* (a a)))) (* (* (* (b b))))) ((* (* (* (c c)))) (* (* (* (d d)))))) (((* (* (* (e e)))) (* (* (* (f f))))) ((* (* (* (g g)))) (* (* (* (h h))))))) 7 | pre-reduce: 8 | RWTS : 18_350_068 9 | - ANNI : 10_485_712 10 | - COMM : 19 11 | - ERAS : 5_242_846 12 | - DREF : 2_621_491 13 | - OPER : 0 14 | run: 15 | RWTS : 211 16 | - ANNI : 47 17 | - COMM : 25 18 | - ERAS : 15 19 | - DREF : 124 20 | - OPER : 0 21 | -------------------------------------------------------------------------------- /tests/programs/queue.bend: -------------------------------------------------------------------------------- 1 | // A cool trick involving HVM's scopeless lambdas is linear queues: 2 | 3 | // Queue.new : Queue a 4 | Queue.new = λx x 5 | 6 | // Queue.add : a -> Queue a -> Queue a 7 | Queue.add = λx λq λk (q λc (c x k)) 8 | 9 | // Queue.rem : Queue a -> Pair a (Queue a) 10 | Queue.rem = λq (q $k λx λxs λp(p x λ$k xs)) 11 | 12 | Nil = λc λn n 13 | Cons = λh λt λc λn (c h t) 14 | 15 | // Church Nats 16 | C0 = λs λz z 17 | C1 = λs λz (s z) 18 | C2 = λs λz (s (s z)) 19 | 20 | // Output: [1, 2, 3] 21 | main = 22 | let q = Queue.new 23 | let q = (Queue.add C0 q) 24 | let q = (Queue.add C1 q) 25 | let q = (Queue.add C2 q) 26 | (Queue.rem q λv0 λq 27 | (Queue.rem q λv1 λq 28 | (Queue.rem q λv2 λq 29 | (Cons C0 (Cons C1 (Cons C2 Nil)))))) -------------------------------------------------------------------------------- /tests/programs/stress_tests/sum_tree.js: -------------------------------------------------------------------------------- 1 | //node = λa λb λnode λleaf (node a b) 2 | //leaf = λv λnode λleaf (leaf v) 3 | 4 | //gen = λn match n { 5 | //0 : (leaf 1) 6 | //1+p : (node (gen p) (gen p)) 7 | //} 8 | 9 | //sum = λt 10 | //let case_node = λa λb (+ (sum a) (sum b)) 11 | //let case_leaf = λv v 12 | //(t case_node case_leaf) 13 | 14 | //main = (sum (gen 24)) 15 | 16 | const node = a => b => nodeFn => leafFn => nodeFn(a)(b); 17 | const leaf = v => nodeFn => leafFn => leafFn(v); 18 | 19 | const gen = n => n == 0 ? leaf(1) : node(gen(n-1))(gen(n-1)); 20 | 21 | const sum = t => { 22 | const case_node = a => b => sum(a) + sum(b); 23 | const case_leaf = v => v; 24 | return t(case_node)(case_leaf); 25 | }; 26 | 27 | console.log(sum(gen(24))); 28 | -------------------------------------------------------------------------------- /tests/snapshots/run@tests::programs::church_mul.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/church_mul.hvm 5 | --- 6 | (#1{(a #1{b #1{c #1{d #1{e #1{f #1{g #1{h #1{i #1{j #1{k #1{l #1{m #1{n #1{o #1{p #1{q #1{r #1{s #1{t u}}}}}}}}}}}}}}}}}}}) #1{(v a) #1{(w v) #1{(x w) #1{(y x) #1{(z y) #1{(aa z) #1{(ab aa) #1{(ac ab) #1{(ad ac) #1{(ae ad) #1{(af ae) #1{(ag af) #1{(ah ag) #1{(ai ah) #1{(aj ai) #1{(ak aj) #1{(al ak) #1{(am al) (#1{c #1{d #1{e #1{f #1{g #1{h #1{i #1{j #1{k #1{l #1{m #1{n #1{o #1{p #1{q #1{r #1{s #1{t #1{u an}}}}}}}}}}}}}}}}}}} am)}}}}}}}}}}}}}}}}}}} (an b)) 7 | RWTS : 167 8 | - ANNI : 25 9 | - COMM : 19 10 | - ERAS : 0 11 | - DREF : 123 12 | - OPER : 0 13 | -------------------------------------------------------------------------------- /transform/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hvm64-transform" 3 | version.workspace = true 4 | edition = "2021" 5 | 6 | [lib] 7 | path = "src/transform.rs" 8 | 9 | [dependencies] 10 | ordered-float = { version = "4.2.0", default-features = false } 11 | 12 | hvm64-util = { path = "../util", default-features = false } 13 | hvm64-runtime = { path = "../runtime", default-features = false } 14 | hvm64-ast = { path = "../ast", default-features = false } 15 | hvm64-host = { path = "../host", default-features = false } 16 | hvm64-num = { path = "../num", default-features = false } 17 | 18 | [features] 19 | default = ["std"] 20 | std = [ 21 | "hvm64-util/std", 22 | "hvm64-runtime/std", 23 | "hvm64-ast/std", 24 | "hvm64-host/std", 25 | "hvm64-num/std", 26 | ] 27 | 28 | [lints] 29 | workspace = true 30 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "HVM-Core"; 3 | inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; 4 | inputs.flake-utils.url = "github:numtide/flake-utils"; 5 | inputs.fenix.url = "github:nix-community/fenix/monthly"; 6 | 7 | outputs = { self, nixpkgs, flake-utils, fenix }: 8 | flake-utils.lib.eachDefaultSystem (system: 9 | let 10 | pkgs = nixpkgs.legacyPackages.${system}; 11 | rust-toolchain = fenix.packages.${system}.fromToolchainFile { 12 | file = ./rust-toolchain.toml; 13 | # to recompute the hash, replace with `pkgs.lib.fakeSha256` 14 | sha256 = "sha256-hBzihtLpwbCyL5AgwKj0sUbJXRir18utWgqUZHGsbFs="; 15 | }; 16 | in { 17 | devShells.default = pkgs.mkShell { packages = [ rust-toolchain ]; }; 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /tests/programs/alloc_small_tree.hvm: -------------------------------------------------------------------------------- 1 | @Add = ((a b) (c b)) 2 | & @S ~ (c a) 3 | @Alloc = ((@Alloc$S0 (@Leaf a)) a) 4 | @Alloc$S0 = (#1{a b} c) 5 | & @Node ~ (d (e c)) 6 | & @Alloc ~ (b e) 7 | & @Alloc ~ (a d) 8 | @And = ((a (@F b)) (a b)) 9 | @Destroy = ((@Destroy$S0 (@T a)) a) 10 | @Destroy$S0 = (a (b c)) 11 | & @And ~ (d (e c)) 12 | & @Destroy ~ (b e) 13 | & @Destroy ~ (a d) 14 | @F = (* (a a)) 15 | @Leaf = (* (a a)) 16 | @Mul = ((a b) ((c a) (c b))) 17 | @Node = (a (b ((a (b c)) (* c)))) 18 | @Pow = ((a (b c)) (d c)) 19 | & @Mul ~ (d a) 20 | & @S ~ (@Z b) 21 | @S = (a ((a b) (* b))) 22 | @T = (a (* a)) 23 | @Z = (* (a a)) 24 | @main = a 25 | & @Destroy ~ (b a) 26 | & @Alloc ~ (c b) 27 | & @Pow ~ (d (e c)) 28 | & @S ~ (f e) 29 | & @S ~ (g f) 30 | & @S ~ (h g) 31 | & @S ~ (@Z h) 32 | & @S ~ (i d) 33 | & @S ~ (@Z i) 34 | 35 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@tests::programs::numeric_casts.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/numeric_casts.hvm 5 | --- 6 | {0 {1234 {4321 {16771538 {2 {0 {16777215 {16777215 {0 {0 {+0 {+1234 {+4321 {-5678 {+2 {-12 {+8388607 {-8388608 {+8388607 {-8388608 {+0 {+NaN {+inf {-inf {2.1500244 {-2.1500244 {0.15000153 {-1234.0 {1234.0 {123456.0 {16775936.0 {[u24] {[i24] {[f24] *}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} 7 | pre-reduce: 8 | RWTS : 167 9 | - ANNI : 33 10 | - COMM : 1 11 | - ERAS : 34 12 | - DREF : 68 13 | - OPER : 31 14 | run: 15 | RWTS : 75 16 | - ANNI : 1 17 | - COMM : 0 18 | - ERAS : 1 19 | - DREF : 73 20 | - OPER : 0 21 | -------------------------------------------------------------------------------- /util/src/parse_abbrev_number.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | 3 | /// Turn a string representation of a number, such as '1G' or '400K', into a 4 | /// number. 5 | pub fn parse_abbrev_number>(arg: &str) -> Result 6 | where 7 | >::Error: fmt::Debug, 8 | { 9 | let (base, scale) = match arg.to_lowercase().chars().last() { 10 | None => return Err("Mem size argument is empty".to_string()), 11 | Some('k') => (&arg[0 .. arg.len() - 1], 1u64 << 10), 12 | Some('m') => (&arg[0 .. arg.len() - 1], 1u64 << 20), 13 | Some('g') => (&arg[0 .. arg.len() - 1], 1u64 << 30), 14 | Some('t') => (&arg[0 .. arg.len() - 1], 1u64 << 40), 15 | Some(_) => (arg, 1), 16 | }; 17 | let base = base.parse::().map_err(|e| e.to_string())?; 18 | (base * scale).try_into().map_err(|e| format!("{:?}", e)) 19 | } 20 | -------------------------------------------------------------------------------- /tests/programs/heavy_pre_reduction.hvm: -------------------------------------------------------------------------------- 1 | @C1 = ((a b) (a b)) 2 | @C2 = (#1{(a b) (c a)} (c b)) 3 | @C3 = (#4{(a b) #4{(c a) (d c)}} (d b)) 4 | @C4 = (#3{(a b) #3{(c a) #3{(d c) (e d)}}} (e b)) 5 | @C6 = (#2{(a b) #2{(c a) #2{(d c) #2{(e d) #2{(f e) (g f)}}}}} (g b)) 6 | @black_plus = a 7 | & @black_box ~ (@plus a) 8 | @erase = ((@black_box (a b)) (a b)) 9 | @expensive = a 10 | & @fib ~ (b a) 11 | & @C4 ~ (@C2 b) 12 | @expensive_1 = a 13 | & @fib ~ (b a) 14 | & @C4 ~ (@C2 b) 15 | @expensive_2 = a 16 | & @fib ~ (b a) 17 | & @C4 ~ (@C2 b) 18 | @fib = ((@fib$S0 ({@C1 @C1} {* a})) a) 19 | @fib$S0 = ({a #6{b c}} {b d}) 20 | & @plus ~ (a (c d)) 21 | @main = * 22 | @main_fast = a 23 | & @black_plus ~ (@expensive (@expensive a)) 24 | @main_slow = a 25 | & @black_plus ~ (@expensive_1 (@expensive_2 a)) 26 | @plus = ((a (b c)) ((d (e b)) (#5{a d} (e c)))) 27 | @black_box = (x x) 28 | 29 | -------------------------------------------------------------------------------- /examples/sort/merge/merge_sort.bend: -------------------------------------------------------------------------------- 1 | data Tree = (Leaf x) | (Node x0 x1) 2 | 3 | sort = λxs 4 | let if_leaf = λv (List.cons v List.nil) 5 | let if_node = λx0 λx1 (merge (sort x0) (sort x1)) 6 | (xs if_leaf if_node) 7 | 8 | merge = λxs 9 | let xs_nil = λys ys 10 | let xs_cons = λx λxs λys 11 | let ys_nil = λx λxs (List.cons x xs) 12 | let ys_cons = λy λys λx λxs 13 | let t = λt (t (List.cons x) λx(x) (List.cons y)) 14 | let t = let k = (< x y); (t (match k { 0: λaλbλcλt(t c a b); 1+: λaλbλcλt(t a b c) })) 15 | (t λa λb λc (a (merge (b xs) (c ys)))) 16 | (ys ys_nil ys_cons x xs) 17 | (xs xs_nil xs_cons) 18 | 19 | sum = λxs 20 | let nil = 0 21 | let cons = λh λt (+ h (sum t)) 22 | (xs nil cons) 23 | 24 | range = λn match n { 25 | 0: λx (Leaf x) 26 | 1+: λx (Node (range n-1 (+ (* x 2) 1)) (range n-1 (* x 2))) 27 | } 28 | 29 | main = (sum (sort (range 2 0))) 30 | -------------------------------------------------------------------------------- /util/src/ops/word.rs: -------------------------------------------------------------------------------- 1 | pub trait FromWord { 2 | fn from_word(bits: u64) -> Self; 3 | } 4 | 5 | pub trait ToWord { 6 | fn to_word(self) -> u64; 7 | } 8 | 9 | macro_rules! impl_word { 10 | ( $($ty:ty),+ ) => { 11 | $( 12 | impl FromWord for $ty { 13 | #[inline(always)] 14 | fn from_word(bits: u64) -> Self { 15 | bits as Self 16 | } 17 | } 18 | 19 | impl ToWord for $ty { 20 | #[inline(always)] 21 | fn to_word(self) -> u64 { 22 | self as u64 23 | } 24 | } 25 | )* 26 | }; 27 | } 28 | 29 | impl_word! { u8, u16, u32, u64, i8, i16, i32 } 30 | 31 | impl FromWord for f32 { 32 | #[inline(always)] 33 | fn from_word(bits: u64) -> Self { 34 | f32::from_bits(bits as u32) 35 | } 36 | } 37 | 38 | impl ToWord for f32 { 39 | #[inline(always)] 40 | fn to_word(self) -> u64 { 41 | self.to_bits() as u64 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@tests::programs::church_mul.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/church_mul.hvm 5 | --- 6 | (#1{(a #1{b #1{c #1{d #1{e #1{f #1{g #1{h #1{i #1{j #1{k #1{l #1{m #1{n #1{o #1{p #1{q #1{r #1{s #1{t u}}}}}}}}}}}}}}}}}}}) #1{(v a) #1{(w v) #1{(x w) #1{(y x) #1{(z y) #1{(aa z) #1{(ab aa) #1{(ac ab) #1{(ad ac) #1{(ae ad) #1{(af ae) #1{(ag af) #1{(ah ag) #1{(ai ah) #1{(aj ai) #1{(ak aj) #1{(al ak) #1{(am al) (#1{c #1{d #1{e #1{f #1{g #1{h #1{i #1{j #1{k #1{l #1{m #1{n #1{o #1{p #1{q #1{r #1{s #1{t #1{u an}}}}}}}}}}}}}}}}}}} am)}}}}}}}}}}}}}}}}}}} (an b)) 7 | pre-reduce: 8 | RWTS : 2 9 | - ANNI : 0 10 | - COMM : 0 11 | - ERAS : 0 12 | - DREF : 2 13 | - OPER : 0 14 | run: 15 | RWTS : 167 16 | - ANNI : 25 17 | - COMM : 19 18 | - ERAS : 0 19 | - DREF : 123 20 | - OPER : 0 21 | -------------------------------------------------------------------------------- /tests/programs/u24.hvm: -------------------------------------------------------------------------------- 1 | @main = x & @t0 ~ (* x) 2 | 3 | // all ops 4 | @t0 = (* {n x}) & @t1 ~ (* x) & 10 ~ $([+] $(2 n)) // 12 5 | @t1 = (* {n x}) & @t2 ~ (* x) & 10 ~ $([-] $(2 n)) // 8 6 | @t2 = (* {n x}) & @t3 ~ (* x) & 10 ~ $([*] $(2 n)) // 20 7 | @t3 = (* {n x}) & @t4 ~ (* x) & 10 ~ $([/] $(2 n)) // 5 8 | @t4 = (* {n x}) & @t5 ~ (* x) & 10 ~ $([%] $(2 n)) // 0 9 | @t5 = (* {n x}) & @t6 ~ (* x) & 10 ~ $([=] $(2 n)) // 0 10 | @t6 = (* {n x}) & @t7 ~ (* x) & 10 ~ $([!] $(2 n)) // 1 11 | @t7 = (* {n x}) & @t8 ~ (* x) & 10 ~ $([<] $(2 n)) // 0 12 | @t8 = (* {n x}) & @t9 ~ (* x) & 10 ~ $([>] $(2 n)) // 1 13 | @t9 = (* {n x}) & @tA ~ (* x) & 10 ~ $([&] $(2 n)) // 2 14 | @tA = (* {n x}) & @tB ~ (* x) & 10 ~ $([|] $(2 n)) // 10 15 | @tB = (* {n x}) & @tC ~ (* x) & 10 ~ $([^] $(2 n)) // 8 16 | @tC = (* {n x}) & @tD ~ (* x) & 10 ~ $([<<] $(2 n)) // 40 17 | @tD = (* {n x}) & @tE ~ (* x) & 10 ~ $([>>] $(2 n)) // 2 18 | 19 | // underflow 20 | @tE = (* {n x}) & @tF ~ (* x) & 0 ~ $([-] $(1 n)) // 16777215 21 | 22 | // overflow 23 | @tF = (* {n x}) & @tG ~ (* x) & 16777215 ~ $([+] $(1 n)) // 0 24 | 25 | // no sign extension 26 | @tG = (* {n x}) & @tH ~ (* x) & 16777215 ~ $([>>] $(22 n)) // 3 27 | 28 | @tH = * 29 | -------------------------------------------------------------------------------- /transform/src/prune.rs: -------------------------------------------------------------------------------- 1 | use hvm64_util::prelude::*; 2 | 3 | use hvm64_ast::{Book, Tree}; 4 | use hvm64_util::maybe_grow; 5 | 6 | pub trait Prune { 7 | fn prune(&mut self, entrypoints: &[String]); 8 | } 9 | 10 | impl Prune for Book { 11 | fn prune(&mut self, entrypoints: &[String]) { 12 | let mut state = PruneState { book: self, unvisited: self.keys().map(|x| x.to_owned()).collect() }; 13 | for name in entrypoints { 14 | state.visit_def(name); 15 | } 16 | let unvisited = state.unvisited; 17 | for name in unvisited { 18 | self.remove(&name); 19 | } 20 | } 21 | } 22 | 23 | #[derive(Debug)] 24 | struct PruneState<'a> { 25 | book: &'a Book, 26 | unvisited: Set, 27 | } 28 | 29 | impl<'a> PruneState<'a> { 30 | fn visit_def(&mut self, name: &str) { 31 | if self.unvisited.remove(name) { 32 | for tree in self.book[name].trees() { 33 | self.visit_tree(tree); 34 | } 35 | } 36 | } 37 | fn visit_tree(&mut self, tree: &Tree) { 38 | maybe_grow(|| { 39 | if let Tree::Ref(name) = tree { 40 | self.visit_def(name); 41 | } else { 42 | tree.children().for_each(|t| self.visit_tree(t)); 43 | } 44 | }) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tests/loaders.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | // use hvm64::{ast::*, run, stdlib::create_host}; 4 | use hvm64_ast::{Book, Net}; 5 | use hvm64_host::Host; 6 | use hvm64_runtime as run; 7 | use std::fs; 8 | 9 | pub fn load_file(file: &str) -> String { 10 | let path = format!("{}/tests/programs/{}", env!("CARGO_MANIFEST_DIR"), file); 11 | fs::read_to_string(path).unwrap() 12 | } 13 | 14 | // Parses code and generate Book from hvm-64 syntax 15 | pub fn parse_core(code: &str) -> Book { 16 | code.parse().unwrap() 17 | } 18 | 19 | // For every pair in the map, replaces all matches of a string with the other 20 | // string 21 | pub fn replace_template(mut code: String, map: &[(&str, &str)]) -> String { 22 | for (from, to) in map { 23 | code = code.replace(from, to); 24 | } 25 | code 26 | } 27 | 28 | pub fn normal_with(book: Book, mem: Option, entry_point: &str) -> (run::Rewrites, Net) { 29 | let area = run::Heap::new(mem).unwrap(); 30 | let host = Host::new(&book); 31 | 32 | let mut rnet = run::Net::new(&area); 33 | rnet.boot(&host.defs[entry_point]); 34 | rnet.normal(); 35 | 36 | let net = host.readback(&rnet); 37 | (rnet.rwts, net) 38 | } 39 | 40 | pub fn normal(book: Book, mem: Option) -> (run::Rewrites, Net) { 41 | normal_with(book, mem, "main") 42 | } 43 | -------------------------------------------------------------------------------- /tests/programs/i24.hvm: -------------------------------------------------------------------------------- 1 | @main = x & @t0 ~ (* x) 2 | 3 | // all ops 4 | @t0 = (* {n x}) & @t1 ~ (* x) & +10 ~ $([+] $(+2 n)) // 12 5 | @t1 = (* {n x}) & @t2 ~ (* x) & +10 ~ $([-] $(+2 n)) // 8 6 | @t2 = (* {n x}) & @t3 ~ (* x) & +10 ~ $([*] $(+2 n)) // 20 7 | @t3 = (* {n x}) & @t4 ~ (* x) & +10 ~ $([/] $(+2 n)) // 5 8 | @t4 = (* {n x}) & @t5 ~ (* x) & +10 ~ $([%] $(+2 n)) // 0 9 | @t5 = (* {n x}) & @t6 ~ (* x) & +10 ~ $([=] $(+2 n)) // 0 10 | @t6 = (* {n x}) & @t7 ~ (* x) & +10 ~ $([!] $(+2 n)) // 1 11 | @t7 = (* {n x}) & @t8 ~ (* x) & +10 ~ $([<] $(+2 n)) // 0 12 | @t8 = (* {n x}) & @t9 ~ (* x) & +10 ~ $([>] $(+2 n)) // 1 13 | @t9 = (* {n x}) & @tA ~ (* x) & +10 ~ $([&] $(+2 n)) // 2 14 | @tA = (* {n x}) & @tB ~ (* x) & +10 ~ $([|] $(+2 n)) // 10 15 | @tB = (* {n x}) & @tC ~ (* x) & +10 ~ $([^] $(+2 n)) // 8 16 | 17 | // underflow 18 | @tC = (* {n x}) & @tD ~ (* x) & -8388608 ~ $([-] $(+1 n)) // 8388607 19 | 20 | // overflow 21 | @tD = (* {n x}) & @tE ~ (* x) & +8388607 ~ $([+] $(+1 n)) // -8388608 22 | 23 | // modulo 24 | @tE = (* {n x}) & @tF ~ (* x) & +3 ~ $([%] $(+2 n)) // +1 25 | @tF = (* {n x}) & @tG ~ (* x) & +3 ~ $([%] $(-2 n)) // +1 26 | @tG = (* {n x}) & @tH ~ (* x) & -3 ~ $([%] $(+2 n)) // -1 27 | @tH = (* {n x}) & @tI ~ (* x) & -3 ~ $([%] $(-2 n)) // -1 28 | 29 | @tI = * 30 | 31 | -------------------------------------------------------------------------------- /examples/sort/bitonic/bitonic_sort_ctr.hvm1: -------------------------------------------------------------------------------- 1 | // Atomic Swapper (HVM builtin) 2 | //(Data.U60.swap 0 a b) = (Both a b) 3 | //(Data.U60.swap n a b) = (Both b a) 4 | 5 | // Swaps distant values in parallel; corresponds to a Red Box 6 | (Warp s (Leaf a) (Leaf b)) = (Data.U60.swap (^ (> a b) s) (Leaf a) (Leaf b)) 7 | (Warp s (Both a b) (Both c d)) = (Join (Warp s a c) (Warp s b d)) 8 | 9 | // Rebuilds the warped tree in the original order 10 | (Join (Both a b) (Both c d)) = (Both (Both a c) (Both b d)) 11 | 12 | // Recursively warps each sub-tree; corresponds to a Blue/Green Box 13 | (Flow s (Leaf a)) = (Leaf a) 14 | (Flow s (Both a b)) = (Down s (Warp s a b)) 15 | 16 | // Propagates Flow downwards 17 | (Down s (Leaf a)) = (Leaf a) 18 | (Down s (Both a b)) = (Both (Flow s a) (Flow s b)) 19 | 20 | // Bitonic Sort 21 | (Sort s (Leaf a)) = (Leaf a) 22 | (Sort s (Both a b)) = (Flow s (Both (Sort 0 a) (Sort 1 b))) 23 | 24 | // Generates a tree of depth `n` 25 | (Gen 0 x) = (Leaf x) 26 | (Gen n x) = let m = (- n 1); (Both (Gen m (* x 2)) (Gen m (+ (* x 2) 1))) 27 | 28 | // Reverses a tree 29 | (Rev (Leaf x)) = (Leaf x) 30 | (Rev (Both a b)) = (Both (Rev b) (Rev a)) 31 | 32 | // Sums a tree 33 | (Sum (Leaf x)) = x 34 | (Sum (Both a b)) = (+ (Sum a) (Sum b)) 35 | 36 | Main = (Sum (Sort 0 (Rev (Gen 18 0)))) 37 | -------------------------------------------------------------------------------- /examples/sort/merge/merge_sort.hvm: -------------------------------------------------------------------------------- 1 | @Leaf = (a #2(#2(a b) #2(* b))) 2 | @List.cons = (a (b #1(#1(a #1(b c)) #1(* c)))) 3 | @List.nil = #1(* #1(a a)) 4 | @Node = (a (b #2(* #2(#2(a #2(b c)) c)))) 5 | @main = a 6 | & @sum ~ (b a) 7 | & @sort ~ (c b) 8 | & @range ~ (2 (0 c)) 9 | @merge = (((a a) (@merge$S5 b)) b) 10 | @merge$S0 = (a (b c)) 11 | & @List.cons ~ (a (b c)) 12 | @merge$S1 = (a (b (c ((c (a (b d))) d)))) 13 | @merge$S2 = (a (b (c ((a (b (c d))) d)))) 14 | @merge$S3 = (* @merge$S2) 15 | @merge$S4 = (#1{a b} (c (#2{$([<] $(a ?((d e) f))) g} (h i)))) 16 | & (d e) ~ (@merge$S1 @merge$S3) 17 | & ((j ((k k) (l m))) m) ~ (f (((n o) ((h p) ((c q) o))) i)) 18 | & @merge ~ (p (q n)) 19 | & @List.cons ~ (b l) 20 | & @List.cons ~ (g j) 21 | @merge$S5 = (a (b ((@merge$S0 (@merge$S4 (a (b c)))) c))) 22 | @range = (?((a b) c) c) 23 | & (a b) ~ (@range$S0 @range$S1) 24 | @range$S0 = (a b) 25 | & @Leaf ~ (a b) 26 | @range$S1 = (#3{a b} (#4{$([*2] $([+1] c)) $([*2] d)} e)) 27 | & @Node ~ (f (g e)) 28 | & @range ~ (b (d g)) 29 | & @range ~ (a (c f)) 30 | @sort = ((@sort$S0 (@sort$S1 a)) a) 31 | @sort$S0 = (a b) 32 | & @List.cons ~ (a (@List.nil b)) 33 | @sort$S1 = (a (b c)) 34 | & @merge ~ (d (e c)) 35 | & @sort ~ (b e) 36 | & @sort ~ (a d) 37 | @sum = ((0 (@sum$S0 a)) a) 38 | @sum$S0 = ($([+] $(a b)) (c b)) 39 | & @sum ~ (c a) 40 | 41 | -------------------------------------------------------------------------------- /tests/snapshots/run@tests::programs::dec_bits_tree.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/dec_bits_tree.hvm 5 | --- 6 | (((((((* (* (a a))) (* (* (b b)))) ((* (* (c c))) (* (* (d d))))) (((* (* (e e))) (* (* (f f)))) ((* (* (g g))) (* (* (h h)))))) ((((* (* (i i))) (* (* (j j)))) ((* (* (k k))) (* (* (l l))))) (((* (* (m m))) (* (* (n n)))) ((* (* (o o))) (* (* (p p))))))) (((((* (* (q q))) (* (* (r r)))) ((* (* (s s))) (* (* (t t))))) (((* (* (u u))) (* (* (v v)))) ((* (* (w w))) (* (* (x x)))))) ((((* (* (y y))) (* (* (z z)))) ((* (* (aa aa))) (* (* (ab ab))))) (((* (* (ac ac))) (* (* (ad ad)))) ((* (* (ae ae))) (* (* (af af)))))))) ((((((* (* (ag ag))) (* (* (ah ah)))) ((* (* (ai ai))) (* (* (aj aj))))) (((* (* (ak ak))) (* (* (al al)))) ((* (* (am am))) (* (* (an an)))))) ((((* (* (ao ao))) (* (* (ap ap)))) ((* (* (aq aq))) (* (* (ar ar))))) (((* (* (as as))) (* (* (at at)))) ((* (* (au au))) (* (* (av av))))))) (((((* (* (aw aw))) (* (* (ax ax)))) ((* (* (ay ay))) (* (* (az az))))) (((* (* (ba ba))) (* (* (bb bb)))) ((* (* (bc bc))) (* (* (bd bd)))))) ((((* (* (be be))) (* (* (bf bf)))) ((* (* (bg bg))) (* (* (bh bh))))) (((* (* (bi bi))) (* (* (bj bj)))) ((* (* (bk bk))) (* (* (bl bl))))))))) 7 | RWTS : 2_874_985 8 | - ANNI : 1_567_101 9 | - COMM : 872 10 | - ERAS : 522_751 11 | - DREF : 784_261 12 | - OPER : 0 13 | -------------------------------------------------------------------------------- /cspell.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2", 3 | "language": "en", 4 | "words": [ 5 | "anni", 6 | "annihilations", 7 | "arrayvec", 8 | "backlinks", 9 | "backoffs", 10 | "combinators", 11 | "condvar", 12 | "ctrs", 13 | "deque", 14 | "dereferencable", 15 | "dref", 16 | "dups", 17 | "dylib", 18 | "dylibs", 19 | "dyntest", 20 | "effectful", 21 | "fmts", 22 | "fuzzer", 23 | "hasher", 24 | "hvml", 25 | "ilog", 26 | "inlinees", 27 | "insta", 28 | "libloading", 29 | "lldb", 30 | "lnet", 31 | "lnum", 32 | "monomorphized", 33 | "newtype", 34 | "nilary", 35 | "nomicon", 36 | "oper", 37 | "outdir", 38 | "plog", 39 | "powf", 40 | "powi", 41 | "ptrs", 42 | "rchunks", 43 | "readback", 44 | "redex", 45 | "redexes", 46 | "redirections", 47 | "repr", 48 | "retraversing", 49 | "rlens", 50 | "rnet", 51 | "rnum", 52 | "rsplit", 53 | "rustc", 54 | "rwts", 55 | "sigabrt", 56 | "skippable", 57 | "struct", 58 | "succ", 59 | "supercombinators", 60 | "targ", 61 | "thiserror", 62 | "tids", 63 | "tlog", 64 | "trgs", 65 | "tspl", 66 | "trit", 67 | "uninit", 68 | "unioned", 69 | "unredirect", 70 | "unsynced", 71 | "vtable" 72 | ], 73 | "files": ["**/*.rs", "**/*.md"], 74 | "ignoreRegExpList": ["HexValues", "(? { 7 | #[derive(Debug, Clone)] 8 | enum $Iter<$($Variant),*> { 9 | $($Variant { iter: $Variant }),* 10 | } 11 | 12 | impl<$($Variant),*> $Iter<$($Variant),*> { 13 | $( 14 | #[allow(non_snake_case)] 15 | fn $Variant(iter: impl IntoIterator) -> Self { 16 | $Iter::$Variant { iter: iter.into_iter() } 17 | } 18 | )* 19 | } 20 | 21 | impl),*> Iterator for $Iter<$($Variant),*> { 22 | type Item = T; 23 | fn next(&mut self) -> Option { 24 | match self { $($Iter::$Variant { iter } => iter.next()),* } 25 | } 26 | 27 | fn size_hint(&self) -> (usize, Option) { 28 | match self { $($Iter::$Variant { iter } => iter.size_hint()),* } 29 | } 30 | } 31 | 32 | impl),*> DoubleEndedIterator for $Iter<$($Variant),*> { 33 | fn next_back(&mut self) -> Option { 34 | match self { $($Iter::$Variant { iter } => iter.next_back()),* } 35 | } 36 | } 37 | 38 | impl),*> ExactSizeIterator for $Iter<$($Variant),*> {} 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hvm64" 3 | version.workspace = true 4 | edition = "2021" 5 | description = "HVM-Core is a massively parallel Interaction Combinator evaluator." 6 | license = "Apache-2.0" 7 | 8 | [workspace.package] 9 | version = "0.3.0" 10 | 11 | [[bin]] 12 | name = "hvm64" 13 | path = "src/main.rs" 14 | bench = false 15 | required-features = ["std"] 16 | 17 | [profile.release] 18 | codegen-units = 1 19 | lto = "fat" 20 | opt-level = 3 21 | panic = "abort" 22 | debug = "full" 23 | 24 | [dependencies] 25 | clap = { version = "4.5.4", features = ["derive"] } 26 | libloading = { version = "0.8.3", default-features = false } 27 | 28 | hvm64-ast = { path = "./ast" } 29 | hvm64-runtime = { path = "./runtime" } 30 | hvm64-transform = { path = "./transform" } 31 | hvm64-util = { path = "./util" } 32 | hvm64-host = { path = "./host" } 33 | hvm64-num = { path = "./num" } 34 | 35 | [dev-dependencies] 36 | insta = { version = "1.34.0", features = ["glob"] } 37 | dyntest = "0.1.2" 38 | 39 | [features] 40 | default = ["std"] 41 | std = [] 42 | trace = ["hvm64-runtime/trace"] 43 | 44 | [patch.crates-io] 45 | highlight_error = { git = "https://github.com/tjjfvi/rust_highlight_error/", branch = "no_std" } 46 | 47 | [[test]] 48 | name = "tests" 49 | harness = false 50 | 51 | [lints] 52 | workspace = true 53 | 54 | [workspace] 55 | resolver = "2" 56 | 57 | [workspace.lints.clippy] 58 | alloc_instead_of_core = "warn" 59 | std_instead_of_core = "warn" 60 | std_instead_of_alloc = "warn" 61 | absolute_paths = "warn" 62 | field_reassign_with_default = "allow" 63 | missing_safety_doc = "allow" 64 | new_ret_no_self = "allow" 65 | -------------------------------------------------------------------------------- /tests/snapshots/pre_reduce_run@tests::programs::dec_bits_tree.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/dec_bits_tree.hvm 5 | --- 6 | (((((((* (* (a a))) (* (* (b b)))) ((* (* (c c))) (* (* (d d))))) (((* (* (e e))) (* (* (f f)))) ((* (* (g g))) (* (* (h h)))))) ((((* (* (i i))) (* (* (j j)))) ((* (* (k k))) (* (* (l l))))) (((* (* (m m))) (* (* (n n)))) ((* (* (o o))) (* (* (p p))))))) (((((* (* (q q))) (* (* (r r)))) ((* (* (s s))) (* (* (t t))))) (((* (* (u u))) (* (* (v v)))) ((* (* (w w))) (* (* (x x)))))) ((((* (* (y y))) (* (* (z z)))) ((* (* (aa aa))) (* (* (ab ab))))) (((* (* (ac ac))) (* (* (ad ad)))) ((* (* (ae ae))) (* (* (af af)))))))) ((((((* (* (ag ag))) (* (* (ah ah)))) ((* (* (ai ai))) (* (* (aj aj))))) (((* (* (ak ak))) (* (* (al al)))) ((* (* (am am))) (* (* (an an)))))) ((((* (* (ao ao))) (* (* (ap ap)))) ((* (* (aq aq))) (* (* (ar ar))))) (((* (* (as as))) (* (* (at at)))) ((* (* (au au))) (* (* (av av))))))) (((((* (* (aw aw))) (* (* (ax ax)))) ((* (* (ay ay))) (* (* (az az))))) (((* (* (ba ba))) (* (* (bb bb)))) ((* (* (bc bc))) (* (* (bd bd)))))) ((((* (* (be be))) (* (* (bf bf)))) ((* (* (bg bg))) (* (* (bh bh))))) (((* (* (bi bi))) (* (* (bj bj)))) ((* (* (bk bk))) (* (* (bl bl))))))))) 7 | pre-reduce: 8 | RWTS : 17_974 9 | - ANNI : 10_232 10 | - COMM : 9 11 | - ERAS : 5_106 12 | - DREF : 2_627 13 | - OPER : 0 14 | run: 15 | RWTS : 1_645 16 | - ANNI : 383 17 | - COMM : 296 18 | - ERAS : 127 19 | - DREF : 839 20 | - OPER : 0 21 | -------------------------------------------------------------------------------- /examples/sort/bitonic/bitonic_sort_lam.bend: -------------------------------------------------------------------------------- 1 | Leaf = @x @Leaf @Node (Leaf x) 2 | Node = @x0 @x1 @Leaf @Node (Node x0 x1) 3 | 4 | swap = λn match n { 5 | 0: λx0 λx1 (Node x0 x1) 6 | 1+: λx0 λx1 (Node x1 x0) 7 | } 8 | 9 | warp = λa 10 | let a_leaf = λax λb 11 | let b_leaf = λbx λax λs (swap (^ (> ax bx) s) (Leaf ax) (Leaf bx)) 12 | let b_node = λa0 λa1 λax λs 0 13 | (b b_leaf b_node ax) 14 | let a_node = λa0 λa1 λb 15 | let b_leaf = λbx λa0 λa1 λs 0 16 | let b_node = λb0 λb1 λa0 λa1 λs (join (warp a0 b0 s) (warp a1 b1 s)) 17 | (b b_leaf b_node a0 a1) 18 | (a a_leaf a_node) 19 | 20 | join = λa 21 | let a_leaf = λax λb 0 22 | let a_node = λa0 λa1 λb 23 | let b_leaf = λbx λa0 λa1 0 24 | let b_node = λb0 λb1 λa0 λa1 (Node (Node a0 b0) (Node a1 b1)) 25 | (b b_leaf b_node a0 a1) 26 | (a a_leaf a_node) 27 | 28 | flow = λa 29 | let a_leaf = λax λs (Leaf ax) 30 | let a_node = λa0 λa1 λs (down (warp a0 a1 s) s) 31 | (a a_leaf a_node) 32 | 33 | down = λa 34 | let a_leaf = λax λs (Leaf ax) 35 | let a_node = λa0 λa1 λs (Node (flow a0 s) (flow a1 s)) 36 | (a a_leaf a_node) 37 | 38 | sort = λa 39 | let a_leaf = λax λs (Leaf ax) 40 | let a_node = λa0 λa1 λs (flow (Node (sort a0 0) (sort a1 1)) s) 41 | (a a_leaf a_node) 42 | 43 | gen = λn match n { 44 | 0: λx (Leaf x) 45 | 1+: λx (Node (gen n-1 (* x 2)) (gen n-1 (+ (* x 2) 1))) 46 | } 47 | 48 | rev = λa 49 | let a_leaf = λax (Leaf ax) 50 | let a_node = λa0 λa1 (Node (rev a1) (rev a0)) 51 | (a a_leaf a_node) 52 | 53 | sum = λa 54 | let a_leaf = λax ax 55 | let a_node = λa0 λa1 (+ (sum a0) (sum a1)) 56 | (a a_leaf a_node) 57 | 58 | main = (sum (sort (rev (gen 10 0)) 0)) 59 | -------------------------------------------------------------------------------- /runtime/src/addr.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | /// A memory address to be used in a [`Port`] or a [`Wire`]. 4 | /// 5 | /// The bottom three bits must be zero; i.e. this address must be at least 6 | /// 8-byte-aligned. 7 | /// 8 | /// Additionally, all bits other than the lowest 48 must be zero. On a 32-bit 9 | /// system, this has no effect, but on a 64-bit system, this means that the top 10 | /// 16 bits much be zero. 11 | #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 12 | #[must_use] 13 | pub struct Addr(pub usize); 14 | 15 | impl fmt::Debug for Addr { 16 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 17 | write!(f, "{:012x?}", self.0) 18 | } 19 | } 20 | 21 | impl Addr { 22 | pub const NULL: Addr = Addr(0); 23 | 24 | /// Casts this address into an `&AtomicU64`, which may or may not be valid. 25 | #[inline(always)] 26 | pub fn val<'a>(&self) -> &'a AtomicU64 { 27 | unsafe { &*(self.0 as *const _) } 28 | } 29 | 30 | /// Casts this address into an `&Def`, which may or may not be valid. 31 | #[inline(always)] 32 | pub fn def<'a>(&self) -> &'a Def { 33 | unsafe { &*(self.0 as *const _) } 34 | } 35 | 36 | const HALF_MASK: usize = 0b1000; 37 | 38 | /// Given an address to one word of a two-word allocation, returns the address 39 | /// of the first word of that allocation. 40 | #[inline(always)] 41 | pub(super) fn left_half(&self) -> Self { 42 | Addr(self.0 & !Addr::HALF_MASK) 43 | } 44 | 45 | /// Given an address to one word of a two-word allocation, returns the address 46 | /// of the other word of that allocation. 47 | #[inline(always)] 48 | pub fn other_half(&self) -> Self { 49 | Addr(self.0 ^ Addr::HALF_MASK) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /util/src/create_var.rs: -------------------------------------------------------------------------------- 1 | #[allow(unused_imports)] 2 | use crate::prelude::*; 3 | 4 | /// Creates a variable uniquely identified by `id`. 5 | pub fn create_var(mut id: usize) -> String { 6 | let mut txt = Vec::new(); 7 | id += 1; 8 | while id > 0 { 9 | id -= 1; 10 | txt.push((id % 26) as u8 + b'a'); 11 | id /= 26; 12 | } 13 | txt.reverse(); 14 | String::from_utf8(txt).unwrap() 15 | } 16 | 17 | /// Inverse function of [`create_var`]. 18 | /// 19 | /// Returns None when the provided string is not an output of 20 | /// `create_var`. 21 | pub fn var_to_num(s: &str) -> Option { 22 | let mut n = 0usize; 23 | for i in s.chars() { 24 | let i = (i as u32).checked_sub('a' as u32)? as usize; 25 | if i > 'z' as usize { 26 | return None; 27 | } 28 | n *= 26; 29 | n += i; 30 | n += 1; 31 | } 32 | n.checked_sub(1) // if it's none, then it means the initial string was '' 33 | } 34 | 35 | #[test] 36 | fn test_create_var() { 37 | assert_eq!(create_var(0), "a"); 38 | assert_eq!(create_var(1), "b"); 39 | assert_eq!(create_var(25), "z"); 40 | assert_eq!(create_var(26), "aa"); 41 | assert_eq!(create_var(27), "ab"); 42 | assert_eq!(create_var(51), "az"); 43 | assert_eq!(create_var(52), "ba"); 44 | assert_eq!(create_var(676), "za"); 45 | assert_eq!(create_var(701), "zz"); 46 | assert_eq!(create_var(702), "aaa"); 47 | assert_eq!(create_var(703), "aab"); 48 | assert_eq!(create_var(728), "aba"); 49 | assert_eq!(create_var(1351), "ayz"); 50 | assert_eq!(create_var(1352), "aza"); 51 | assert_eq!(create_var(1378), "baa"); 52 | } 53 | 54 | #[test] 55 | fn test_var_to_num() { 56 | for i in [0, 1, 2, 3, 10, 26, 27, 30, 50, 70] { 57 | assert_eq!(Some(i), var_to_num(&create_var(i))); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /examples/sort/bitonic/bitonic_sort_lam.hvm1: -------------------------------------------------------------------------------- 1 | Leaf = λx λleaf λnode (leaf x) 2 | Node = λx0 λx1 λleaf λnode (node x0 x1) 3 | 4 | (Match 0 zero succ) = zero 5 | (Match n zero succ) = (succ (- n 1)) 6 | 7 | Swap = λn (Match n λx0λx1((Node) x0 x1) λpλx0λx1((Node) x1 x0)) 8 | 9 | Warp = λa 10 | let a_leaf = λax λb 11 | let b_leaf = λbx λax λs ((Swap) (^ (> ax bx) s) ((Leaf) ax) ((Leaf) bx)) 12 | let b_node = λa0 λa1 λax λs 0 13 | (b b_leaf b_node ax) 14 | let a_node = λa0 λa1 λb 15 | let b_leaf = λbx λa0 λa1 λs 0 16 | let b_node = λb0 λb1 λa0 λa1 λs ((Join) ((Warp) a0 b0 s) ((Warp) a1 b1 s)) 17 | (b b_leaf b_node a0 a1) 18 | (a a_leaf a_node) 19 | 20 | Join = λa 21 | let a_leaf = λax λb 0 22 | let a_node = λa0 λa1 λb 23 | let b_leaf = λbx λa0 λa1 0 24 | let b_node = λb0 λb1 λa0 λa1 ((Node) ((Node) a0 b0) ((Node) a1 b1)) 25 | (b b_leaf b_node a0 a1) 26 | (a a_leaf a_node) 27 | 28 | Flow = λa 29 | let a_leaf = λax λs ((Leaf) ax) 30 | let a_node = λa0 λa1 λs ((Down) ((Warp) a0 a1 s) s) 31 | (a a_leaf a_node) 32 | 33 | Down = λa 34 | let a_leaf = λax λs ((Leaf) ax) 35 | let a_node = λa0 λa1 λs ((Node) ((Flow) a0 s) ((Flow) a1 s)) 36 | (a a_leaf a_node) 37 | 38 | Sort = λa 39 | let a_leaf = λax λs ((Leaf) ax) 40 | let a_node = λa0 λa1 λs ((Flow) ((Node) ((Sort) a0 0) ((Sort) a1 1)) s) 41 | (a a_leaf a_node) 42 | 43 | Gen = λn (Match n λx((Leaf) x) λpλx((Node) ((Gen) p (* x 2)) ((Gen) p (+ (* x 2) 1)))) 44 | 45 | Rev = λa 46 | let a_leaf = λax ((Leaf) ax) 47 | let a_node = λa0 λa1 ((Node) ((Rev) a1) ((Rev) a0)) 48 | (a a_leaf a_node) 49 | 50 | Sum = λa 51 | let a_leaf = λax ax 52 | let a_node = λa0 λa1 (+ ((Sum) a0) ((Sum) a1)) 53 | (a a_leaf a_node) 54 | 55 | Main = ((Sum) ((Sort) ((Rev) ((Gen) 15 0)) 0)) 56 | -------------------------------------------------------------------------------- /tests/programs/f24.hvm: -------------------------------------------------------------------------------- 1 | @half = xN & 1.0 ~ $([/] $(2.0 xN)) 2 | @nan = xN & 0.0 ~ $([/] $(0.0 xN)) 3 | 4 | @main = x & @t0 ~ (* x) 5 | 6 | // nan and inf divisions 7 | @t0 = (* {n x}) & @t1 ~ (* x) & 1.0 ~ $([/] $(0.0 n)) // +inf 8 | @t1 = (* {n x}) & @t2 ~ (* x) & -1.0 ~ $([/] $(0.0 n)) // -inf 9 | @t2 = (* {n x}) & @t3 ~ (* x) & 0.0 ~ $([/] $(0.0 n)) // NaN 10 | 11 | // general operators 12 | @t3 = (* {n x}) & @t4 ~ (* x) & @half ~ $([+] $(2.0 n)) // 2.5 13 | @t4 = (* {n x}) & @t5 ~ (* x) & @half ~ $([-] $(2.0 n)) // -1.5 14 | @t5 = (* {n x}) & @t6 ~ (* x) & @half ~ $([*] $(2.3 n)) // 1.15 15 | @t6 = (* {n x}) & @t7 ~ (* x) & @half ~ $([/] $(2.0 n)) // 0.25 16 | @t7 = (* {n x}) & @t8 ~ (* x) & @half ~ $([%] $(2.0 n)) // 0.5 17 | 18 | // comparisons (returning ints) 19 | @t8 = (* {n x}) & @t9 ~ (* x) & @half ~ $([=] $(2.0 n)) // 0 20 | @t9 = (* {n x}) & @tA ~ (* x) & @half ~ $([!] $(2.0 n)) // 1 21 | @tA = (* {n x}) & @tB ~ (* x) & @half ~ $([<] $(2.0 n)) // 1 22 | @tB = (* {n x}) & @tC ~ (* x) & @half ~ $([>] $(2.0 n)) // 0 23 | 24 | // ieee nan comparisons 25 | @tC = (* {n x}) & @tD ~ (* x) & @nan ~ $([=] $(@nan n)) // 0 26 | @tD = (* {n x}) & @tE ~ (* x) & @nan ~ $([<] $(@nan n)) // 0 27 | @tE = (* {n x}) & @tF ~ (* x) & @nan ~ $([>] $(@nan n)) // 0 28 | 29 | // parsing 30 | @tF = (* {n x}) & @tG ~ (* x) & +NaN ~ $([+] $(0.0 n)) // NaN 31 | @tG = (* {n x}) & @tH ~ (* x) & +inf ~ $([+] $(0.0 n)) // inf 32 | @tH = (* {n x}) & @tI ~ (* x) & -inf ~ $([+] $(0.0 n)) // -inf 33 | @tI = (* {n x}) & @tJ ~ (* x) & 1.02 ~ $([+] $(0.0 n)) // 1.02 34 | 35 | // modulo 36 | @tJ = (* {n x}) & @tK ~ (* x) & +1.2 ~ $([%] $(+1.1 n)) // +0.1 37 | @tK = (* {n x}) & @tL ~ (* x) & +1.2 ~ $([%] $(-1.1 n)) // +0.1 38 | @tL = (* {n x}) & @tM ~ (* x) & -1.2 ~ $([%] $(+1.1 n)) // -0.1 39 | @tM = (* {n x}) & @tN ~ (* x) & -1.2 ~ $([%] $(-1.1 n)) // -0.1 40 | 41 | @tN = * 42 | -------------------------------------------------------------------------------- /examples/lambda_calculus/hoas.bend: -------------------------------------------------------------------------------- 1 | // Example HOAS evaluator for the λ-Calculus on HVM-Lang. 2 | 3 | Lam = λbod λlam λapp λsub (lam bod) 4 | App = λfun λarg λlam λapp λsub (app fun arg) 5 | Sub = λexp λlam λapp λsub (sub exp) 6 | 7 | reduce = λterm 8 | let clam = λbod (Lam bod) 9 | let capp = λfun λarg (reduce.app (reduce fun) arg) 10 | let csub = λexp (Sub exp) 11 | (term clam capp csub) 12 | 13 | reduce.app = λfun 14 | let clam = λfbod λx (reduce (fbod x)) 15 | let capp = λffun λfarg λx (App (App ffun farg) x) 16 | let csub = λfexp λx (App (Sub fexp) x) 17 | (fun clam capp csub) 18 | 19 | normal.go = λterm 20 | let clam = λbod (Lam λx (normal (bod (Sub x)))) 21 | let capp = λfun λarg (App (normal fun) (normal arg)) 22 | let csub = λexp exp 23 | (term clam capp csub) 24 | 25 | normal = λterm 26 | (normal.go (reduce term)) 27 | 28 | ID = (Lam λx x) 29 | C0 = (Lam λf (Lam λx x)) 30 | C1 = (Lam λf (Lam λx (App f x))) 31 | C2 = (Lam λf (Lam λx (App f (App f x)))) 32 | C3 = (Lam λf (Lam λx (App f (App f (App f x))))) 33 | C4 = (Lam λf (Lam λx (App f (App f (App f (App f x)))))) 34 | C6 = (Lam λf (Lam λx (App f (App f (App f (App f (App f (App f x)))))))) 35 | C8 = (Lam λf (Lam λx (App f (App f (App f (App f (App f (App f (App f x))))))))) 36 | CS = (Lam λn (Lam λs (Lam λz (App s (App (App n s) z))))) 37 | FOO = (Lam λx (App x x)) 38 | False = (Lam λt (Lam λf f)) 39 | True = (Lam λt (Lam λf t)) 40 | Not = (Lam λb (App (App b False) True)) 41 | 42 | Main = (normal (App (App C6 Not) True)) 43 | 44 | //λa λ* λ* (a λb λc λ* λ* (c λ* b)) 45 | //λa λ* λ* (a λ* λb λ* λ* (b λc c)) 46 | //λa λ* λ* (a λb λc λ* λ* (c λ* b)) 47 | //λa λ* λ* (a λ* λb λ* λ* (b λc c)) 48 | //λa λ* λ* (a λb λc λ* λ* (c λ* b)) 49 | 50 | 51 | //λa λ* λ* (a λb λc λ* λ* (c λ* b)) 52 | //λa λ* λ* (a λ* λb λ* λ* (b λc c)) 53 | -------------------------------------------------------------------------------- /runtime/src/node.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | /// See [`Port::traverse_node`]. 4 | pub struct TraverseNode { 5 | pub tag: Tag, 6 | pub lab: Lab, 7 | pub p1: Wire, 8 | pub p2: Wire, 9 | } 10 | 11 | impl Port { 12 | #[inline(always)] 13 | pub fn consume_node(self) -> TraverseNode { 14 | self.traverse_node() 15 | } 16 | 17 | #[inline(always)] 18 | pub fn traverse_node(self) -> TraverseNode { 19 | TraverseNode { 20 | tag: self.tag(), 21 | lab: self.lab(), 22 | p1: Wire::new(self.addr()), 23 | p2: Wire::new(self.addr().other_half()), 24 | } 25 | } 26 | } 27 | 28 | pub struct CreatedNode { 29 | pub p0: Port, 30 | pub p1: Port, 31 | pub p2: Port, 32 | } 33 | 34 | impl<'a> Net<'a> { 35 | #[inline(always)] 36 | pub fn create_node(&mut self, tag: Tag, lab: Lab) -> CreatedNode { 37 | let addr = self.alloc(); 38 | CreatedNode { p0: Port::new(tag, lab, addr), p1: Port::new_var(addr), p2: Port::new_var(addr.other_half()) } 39 | } 40 | 41 | /// Creates a wire an aux port pair. 42 | #[inline(always)] 43 | pub fn create_wire(&mut self) -> (Wire, Port) { 44 | let addr = self.alloc(); 45 | self.half_free(addr.other_half()); 46 | (Wire::new(addr), Port::new_var(addr)) 47 | } 48 | 49 | /// Creates a wire pointing to a given port; sometimes necessary to avoid 50 | /// deadlock. 51 | #[inline(always)] 52 | pub fn create_wire_to(&mut self, port: Port) -> Wire { 53 | let addr = self.alloc(); 54 | self.half_free(addr.other_half()); 55 | let wire = Wire::new(addr); 56 | self.link_port_port(port, Port::new_var(wire.addr())); 57 | wire 58 | } 59 | 60 | /// Creates a wire pointing to a trg (this is a no-op if it is already a 61 | /// wire); sometimes necessary to avoid deadlock. 62 | #[inline(always)] 63 | pub fn wire_to_trg(&mut self, trg: Trg) -> Wire { 64 | if trg.is_wire() { trg.as_wire() } else { self.create_wire_to(trg.as_port()) } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /util/src/ops/num.rs: -------------------------------------------------------------------------------- 1 | #[rustfmt::skip] 2 | pub trait Numeric: PartialEq + PartialOrd + Sized { 3 | const ZERO: Self; 4 | 5 | fn add(_: Self, _: Self) -> Self { Self::ZERO } 6 | fn sub(_: Self, _: Self) -> Self { Self::ZERO } 7 | fn mul(_: Self, _: Self) -> Self { Self::ZERO } 8 | fn div(_: Self, _: Self) -> Self { Self::ZERO } 9 | fn rem(_: Self, _: Self) -> Self { Self::ZERO } 10 | fn and(_: Self, _: Self) -> Self { Self::ZERO } 11 | fn or(_: Self, _: Self) -> Self { Self::ZERO } 12 | fn xor(_: Self, _: Self) -> Self { Self::ZERO } 13 | fn shl(_: Self, _: Self) -> Self { Self::ZERO } 14 | fn shr(_: Self, _: Self) -> Self { Self::ZERO } 15 | } 16 | 17 | macro_rules! impl_numeric { 18 | ( $($ty:ty),+ ) => { 19 | $( 20 | impl Numeric for $ty { 21 | const ZERO: Self = 0; 22 | 23 | fn add(a: Self, b: Self) -> Self { a.wrapping_add(b) } 24 | fn sub(a: Self, b: Self) -> Self { a.wrapping_sub(b) } 25 | fn mul(a: Self, b: Self) -> Self { a.wrapping_mul(b) } 26 | fn div(a: Self, b: Self) -> Self { a.checked_div(b).unwrap_or(0) } 27 | fn rem(a: Self, b: Self) -> Self { a.checked_rem(b).unwrap_or(0) } 28 | fn and(a: Self, b: Self) -> Self { a & b } 29 | fn or(a: Self, b: Self) -> Self { a | b } 30 | fn xor(a: Self, b: Self) -> Self { a ^ b } 31 | fn shl(a: Self, b: Self) -> Self { a.wrapping_shl(b as u32) } 32 | fn shr(a: Self, b: Self) -> Self { a.wrapping_shr(b as u32) } 33 | } 34 | )* 35 | } 36 | } 37 | 38 | impl_numeric! { u8, u16, u32, u64, i8, i16, i32 } 39 | 40 | impl Numeric for f32 { 41 | const ZERO: Self = 0.0; 42 | 43 | fn add(a: Self, b: Self) -> Self { 44 | a + b 45 | } 46 | fn sub(a: Self, b: Self) -> Self { 47 | a - b 48 | } 49 | fn mul(a: Self, b: Self) -> Self { 50 | a * b 51 | } 52 | fn div(a: Self, b: Self) -> Self { 53 | a / b 54 | } 55 | fn rem(a: Self, b: Self) -> Self { 56 | a % b 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tests/tests.rs: -------------------------------------------------------------------------------- 1 | use core::str::FromStr; 2 | use dyntest::{dyntest, DynTester}; 3 | use hvm64_transform::pre_reduce::PreReduce; 4 | use std::{fs, path::Path}; 5 | 6 | use hvm64_ast::{self as ast, Book, Net}; 7 | use hvm64_host::Host; 8 | use hvm64_runtime as run; 9 | 10 | use insta::assert_snapshot; 11 | 12 | dyntest!(test); 13 | 14 | fn test(t: &mut DynTester) { 15 | for (name, path) in t.glob("{examples,tests/programs}/**/*.hvm") { 16 | t.test(name.clone(), move || { 17 | let mut settings = insta::Settings::new(); 18 | settings.set_prepend_module_to_snapshot(false); 19 | settings.set_input_file(&path); 20 | settings.set_snapshot_suffix(name); 21 | settings.bind(|| { 22 | test_path(&path); 23 | }) 24 | }); 25 | } 26 | } 27 | 28 | fn execute_host(host: &Host) -> Option<(run::Rewrites, Net)> { 29 | let heap = run::Heap::new(None).unwrap(); 30 | let mut net = run::Net::new(&heap); 31 | let entrypoint = host.defs.get("main").unwrap(); 32 | net.boot(entrypoint); 33 | net.parallel_normal(); 34 | Some((net.rwts, host.readback(&net))) 35 | } 36 | 37 | fn test_run(host: &Host) { 38 | let Some((rwts, net)) = execute_host(host) else { return }; 39 | 40 | let output = format!("{}\n{}", net, &rwts); 41 | assert_snapshot!(output); 42 | } 43 | 44 | fn test_pre_reduce_run(mut book: Book) { 45 | let pre_stats = book.pre_reduce(&|x| x == "main", None, u64::MAX); 46 | 47 | let host = Host::new(&book); 48 | let Some((rwts, net)) = execute_host(&host) else { 49 | assert_snapshot!(&pre_stats.rewrites); 50 | return; 51 | }; 52 | 53 | let output = format!("{}\npre-reduce:\n{}run:\n{}", net, &pre_stats.rewrites, &rwts); 54 | assert_snapshot!(output); 55 | } 56 | 57 | fn test_path(path: &Path) { 58 | let code = fs::read_to_string(path).unwrap(); 59 | let book = ast::Book::from_str(&code).unwrap(); 60 | let host = Host::new(&book); 61 | 62 | test_pre_reduce_run(book.clone()); 63 | test_run(&host); 64 | } 65 | -------------------------------------------------------------------------------- /tests/programs/stress_tests/burn.hvm: -------------------------------------------------------------------------------- 1 | // Decreases a tree of λ-encoded binary counters until they're all 0 (parallel). 2 | // Takes about ~16s on Apple M1, and ~0.5s on RTX 4090 3 | 4 | @c3 = ({{(c b) (b a)} (a R)} (c R)) 5 | @c4 = ({{{(d c) (c b)} (b a)} (a R)} (d R)) 6 | @c6 = ({{{{{(f e) (e d)} (d c)} (c b)} (b a)} (a R)} (f R)) 7 | @c8 = ({{{{{{{(h g) (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (h R)) 8 | @c10 = ({{{{{{{{{(j i) (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (j R)) 9 | @c12 = ({{{{{{{{{{{(l k) (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (l R)) 10 | @c14 = ({{{{{{{{{{{{{(n m) (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (n R)) 11 | @c16 = ({{{{{{{{{{{{{{{(p o) (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (p R)) 12 | @c20 = ({{{{{{{{{{{{{{{{{{{(t s) (s r)} (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (t R)) 13 | @c22 = ({{{{{{{{{{{{{{{{{{{{{(v u) (u t)} (t s)} (s r)} (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (v R)) 14 | @c24 = ({{{{{{{{{{{{{{{{{{{{{{{(x w) (w v)} (v u)} (u t)} (t s)} (s r)} (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (x R)) 15 | 16 | @S = (a ((a b) (* b))) 17 | @Z = (* (a a)) 18 | 19 | @O = (xs ((xs r) (* (* r)))) 20 | @I = (xs (* ((xs r) (* r)))) 21 | @E = (* (* (e e))) 22 | 23 | @decO = (p idecp) & @I ~ (decp idecp) & @dec ~ (p decp) 24 | @decI = (p lowp) & @low ~ (p lowp) 25 | @dec = ((@decO (@decI (@E R))) R) 26 | 27 | @lowO = (p oop) & @O ~ (p op) & @O ~ (op oop) 28 | @lowI = (p oip) & @I ~ (p ip) & @O ~ (ip oip) 29 | @low = ((@lowO (@lowI (@E R))) R) 30 | 31 | @runO = (p R) & @run ~ (decop R) & @dec ~ (op decop) & @O ~ (p op) 32 | @runI = (p R) & @run ~ (decip R) & @dec ~ (ip decip) & @I ~ (p ip) 33 | @run = ((@runO (@runI (@E R))) R) 34 | 35 | @brnZ = (* R) & @run ~ (val R) & @c20 ~ (@I (@E val)) 36 | @brnS = ({p0 p1} (r0 r1)) & @brn ~ (p0 r0) & @brn ~ (p1 r1) 37 | @brn = ((@brnS (@brnZ r)) r) 38 | 39 | @main 40 | = R 41 | & @c3 ~ (@S (@Z dep)) 42 | & @brn ~ (dep R) 43 | -------------------------------------------------------------------------------- /runtime/src/wire.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | /// A wire in the interaction net. 4 | /// 5 | /// More accurately, this is a *directed view* of a wire. If ports `a` and `b` 6 | /// are connected, then the wire leaving `a` and the wire leaving `b` are the 7 | /// same wire, but viewed from opposite directions. 8 | /// 9 | /// This is represented by a pointer to an `AtomicU64` storing the *target* of 10 | /// the wire -- the port on the other side. (The target of the wire leaving `a` 11 | /// is `b`.) 12 | /// 13 | /// Changes to the target are handled by the linker. 14 | #[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] 15 | #[must_use] 16 | pub struct Wire(pub *const AtomicU64); 17 | 18 | impl fmt::Debug for Wire { 19 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 20 | write!(f, "{:012x?}", self.0 as usize) 21 | } 22 | } 23 | 24 | unsafe impl Send for Wire {} 25 | unsafe impl Sync for Wire {} 26 | 27 | impl Wire { 28 | #[inline(always)] 29 | pub fn addr(&self) -> Addr { 30 | Addr(self.0 as _) 31 | } 32 | 33 | #[inline(always)] 34 | pub fn new(addr: Addr) -> Wire { 35 | Wire(addr.0 as _) 36 | } 37 | 38 | #[inline(always)] 39 | fn target<'a>(&self) -> &'a AtomicU64 { 40 | unsafe { &*self.0 } 41 | } 42 | 43 | #[inline(always)] 44 | pub fn load_target(&self) -> Port { 45 | let port = Port(self.target().load(Relaxed)); 46 | port 47 | } 48 | 49 | #[inline(always)] 50 | pub fn set_target(&self, port: Port) { 51 | self.target().store(port.0, Relaxed); 52 | } 53 | 54 | #[inline(always)] 55 | pub fn cas_target(&self, expected: Port, value: Port) -> Result { 56 | self.target().compare_exchange(expected.0, value.0, Relaxed, Relaxed).map(Port).map_err(Port) 57 | } 58 | 59 | #[inline(always)] 60 | pub fn swap_target(&self, value: Port) -> Port { 61 | let port = Port(self.target().swap(value.0, Relaxed)); 62 | port 63 | } 64 | 65 | // Takes a pointer's target. 66 | #[inline(always)] 67 | pub fn lock_target(&self) -> Port { 68 | loop { 69 | let got = self.swap_target(Port::LOCK); 70 | if got != Port::LOCK { 71 | return got; 72 | } 73 | spin_loop(); 74 | } 75 | } 76 | 77 | #[inline(always)] 78 | pub(super) fn as_var(&self) -> Port { 79 | Port::new_var(self.addr()) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /examples/sort/bitonic/bitonic_sort_lam.hvm: -------------------------------------------------------------------------------- 1 | @Leaf = (a ((a b) (* b))) 2 | @Node = (a (b (* ((a (b c)) c)))) 3 | @down = ((@down$S0 (@down$S1 a)) a) 4 | @down$S0 = (a (* b)) 5 | & @Leaf ~ (a b) 6 | @down$S1 = (a (b (#1{c d} e))) 7 | & @Node ~ (f (g e)) 8 | & @flow ~ (b (d g)) 9 | & @flow ~ (a (c f)) 10 | @flow = ((@flow$S0 (@flow$S1 a)) a) 11 | @flow$S0 = (a (* b)) 12 | & @Leaf ~ (a b) 13 | @flow$S1 = (a (b (#2{c d} e))) 14 | & @down ~ (f (d e)) 15 | & @warp ~ (a (b (c f))) 16 | @gen = (?((a b) c) c) 17 | & (a b) ~ (@gen$S0 @gen$S1) 18 | @gen$S0 = (a b) 19 | & @Leaf ~ (a b) 20 | @gen$S1 = (#3{a b} (#4{$([*2] c) $([*2] $([+1] d))} e)) 21 | & @Node ~ (f (g e)) 22 | & @gen ~ (b (d g)) 23 | & @gen ~ (a (c f)) 24 | @join = ((@join$S1 (@join$S6 a)) a) 25 | @join$S0 = (* 0) 26 | @join$S1 = (* @join$S0) 27 | @join$S2 = (* 0) 28 | @join$S3 = (* @join$S2) 29 | @join$S4 = (* @join$S3) 30 | @join$S5 = (a (b (c (d e)))) 31 | & @Node ~ (f (g e)) 32 | & @Node ~ (d (b g)) 33 | & @Node ~ (c (a f)) 34 | @join$S6 = (a (b ((@join$S4 (@join$S5 (a (b c)))) c))) 35 | @main = a 36 | & @sum ~ (b a) 37 | & @sort ~ (c (0 b)) 38 | & @rev ~ (d c) 39 | & @gen ~ (10 (0 d)) 40 | @rev = ((@rev$S0 (@rev$S1 a)) a) 41 | @rev$S0 = (a b) 42 | & @Leaf ~ (a b) 43 | @rev$S1 = (a (b c)) 44 | & @Node ~ (d (e c)) 45 | & @rev ~ (a e) 46 | & @rev ~ (b d) 47 | @sort = ((@sort$S0 (@sort$S1 a)) a) 48 | @sort$S0 = (a (* b)) 49 | & @Leaf ~ (a b) 50 | @sort$S1 = (a (b (c d))) 51 | & @flow ~ (e (c d)) 52 | & @Node ~ (f (g e)) 53 | & @sort ~ (b (1 g)) 54 | & @sort ~ (a (0 f)) 55 | @sum = (((a a) (@sum$S0 b)) b) 56 | @sum$S0 = (a (b c)) 57 | & @sum ~ (a $([+] $(d c))) 58 | & @sum ~ (b d) 59 | @swap = (?((a b) c) c) 60 | & (a b) ~ (@swap$S0 @swap$S2) 61 | @swap$S0 = (a (b c)) 62 | & @Node ~ (a (b c)) 63 | @swap$S1 = (a (b c)) 64 | & @Node ~ (b (a c)) 65 | @swap$S2 = (* @swap$S1) 66 | @warp = ((@warp$S5 (@warp$S11 a)) a) 67 | @warp$S0 = (#5{a b} (#6{$([>] $(a $([^] $(c d)))) e} (c f))) 68 | & @swap ~ (d (g (h f))) 69 | & @Leaf ~ (b h) 70 | & @Leaf ~ (e g) 71 | @warp$S1 = (* 0) 72 | @warp$S10 = (a (b (c (d (#7{e f} g))))) 73 | & @join ~ (h (i g)) 74 | & @warp ~ (d (b (f i))) 75 | & @warp ~ (c (a (e h))) 76 | @warp$S11 = (a (b ((@warp$S9 (@warp$S10 (a (b c)))) c))) 77 | @warp$S2 = (* @warp$S1) 78 | @warp$S3 = (* @warp$S2) 79 | @warp$S4 = (* @warp$S3) 80 | @warp$S5 = (a ((@warp$S0 (@warp$S4 (a b))) b)) 81 | @warp$S6 = (* 0) 82 | @warp$S7 = (* @warp$S6) 83 | @warp$S8 = (* @warp$S7) 84 | @warp$S9 = (* @warp$S8) 85 | 86 | -------------------------------------------------------------------------------- /examples/sort/radix/radix_sort_ctr.bend: -------------------------------------------------------------------------------- 1 | data Arr = Empty | (Single x) | (Concat x0 x1) 2 | 3 | data Map = Free | Used | (Node x0 x1) 4 | 5 | // gen : u32 -> Arr 6 | gen = λn match n { 7 | 0: λx (Single x) 8 | 1+: λx 9 | let x0 = (<< x 1) 10 | let x1 = (| x0 1) 11 | (Concat (gen n-1 x0) (gen n-1 x1)) 12 | } 13 | 14 | // rev : Arr -> Arr 15 | rev Empty = Empty 16 | rev (Single x) = (Single x) 17 | rev (Concat a b) = (Concat (rev b) (rev a)) 18 | 19 | // sum : Arr -> u32 20 | sum Empty = 0 21 | sum (Single x) = x 22 | sum (Concat a b) = (+ (sum a) (sum b)) 23 | 24 | // sort : Arr -> Arr 25 | sort = λt (to_arr (to_map t) 0) 26 | 27 | // to_arr : Map -> u32 -> Arr 28 | to_arr Free * = Empty 29 | to_arr Used k = (Single k) 30 | to_arr (Node a b) k = (Concat (to_arr a (* k 2)) (to_arr b (+ (* k 2) 1))) 31 | 32 | // to_map : Arr -> Map 33 | to_map Empty = Free 34 | to_map (Single x) = (radix x) 35 | to_map (Concat a b) = (merge (to_map a) (to_map b)) 36 | 37 | // merge : Map -> Map -> Map 38 | merge Free x = x 39 | merge Used Free = Used 40 | merge Used Used = Used 41 | merge Used (Node * *) = * 42 | merge (Node a b) Free = (Node a b) 43 | merge (Node * *) Used = * 44 | merge (Node a b) (Node c d) = (Node (merge a c) (merge b d)) 45 | 46 | // radix : u32 -> Map 47 | radix = λn 48 | let r = Used 49 | let r = (swap (& n 1) r Free) 50 | let r = (swap (& n 2) r Free) 51 | let r = (swap (& n 4) r Free) 52 | let r = (swap (& n 8) r Free) 53 | let r = (swap (& n 16) r Free) 54 | let r = (swap (& n 32) r Free) 55 | let r = (swap (& n 64) r Free) 56 | let r = (swap (& n 128) r Free) 57 | let r = (swap (& n 256) r Free) 58 | let r = (swap (& n 512) r Free) 59 | let r = (swap (& n 1024) r Free) 60 | let r = (swap (& n 2048) r Free) 61 | let r = (swap (& n 4096) r Free) 62 | let r = (swap (& n 8192) r Free) 63 | let r = (swap (& n 16384) r Free) 64 | let r = (swap (& n 32768) r Free) 65 | let r = (swap (& n 65536) r Free) 66 | let r = (swap (& n 131072) r Free) 67 | let r = (swap (& n 262144) r Free) 68 | let r = (swap (& n 524288) r Free) 69 | let r = (swap (& n 1048576) r Free) 70 | let r = (swap (& n 2097152) r Free) 71 | let r = (swap (& n 4194304) r Free) 72 | let r = (swap (& n 8388608) r Free) 73 | r 74 | 75 | // swap : u32 -> Map -> Map -> Map 76 | swap = λn match n { 77 | 0: λx0 λx1 (Node x0 x1) 78 | 1+: λx0 λx1 (Node x1 x0) 79 | } 80 | 81 | // main : u32 82 | main = (sum (sort (rev (gen 20 0)))) 83 | -------------------------------------------------------------------------------- /.github/workflows/checks.yml: -------------------------------------------------------------------------------- 1 | name: Checks 2 | 3 | on: 4 | pull_request: 5 | merge_group: 6 | push: 7 | branches: 8 | - main 9 | 10 | jobs: 11 | check: 12 | runs-on: ubuntu-latest 13 | timeout-minutes: 10 14 | steps: 15 | - uses: actions/checkout@v3 16 | - uses: dsherret/rust-toolchain-file@v1 17 | - uses: actions/cache@v2 18 | with: 19 | path: | 20 | ~/.cargo/registry 21 | ~/.cargo/git 22 | target 23 | key: ${{ runner.os }}-check-${{ hashFiles('**/Cargo.lock') }} 24 | - run: RUSTFLAGS="-D warnings" cargo check --all-targets 25 | - run: RUSTFLAGS="-D warnings" cargo check --all-targets --features trace 26 | - run: RUSTFLAGS="-D warnings" cargo check --all-targets --no-default-features 27 | - run: cargo install cargo-no-std-check 28 | - run: cargo no-std-check --workspace --exclude hvm64 --no-default-features 29 | clippy: 30 | runs-on: ubuntu-latest 31 | timeout-minutes: 10 32 | steps: 33 | - uses: actions/checkout@v3 34 | - uses: dsherret/rust-toolchain-file@v1 35 | - uses: actions/cache@v2 36 | with: 37 | path: | 38 | ~/.cargo/registry 39 | ~/.cargo/git 40 | target 41 | key: ${{ runner.os }}-clippy-${{ hashFiles('**/Cargo.lock') }} 42 | - run: RUSTFLAGS="-D warnings" cargo clippy --all-targets 43 | - run: RUSTFLAGS="-D warnings" cargo clippy --all-targets --features trace 44 | - run: RUSTFLAGS="-D warnings" cargo clippy --all-targets --no-default-features 45 | test: 46 | runs-on: ubuntu-latest 47 | timeout-minutes: 10 48 | steps: 49 | - uses: actions/checkout@v3 50 | - uses: dsherret/rust-toolchain-file@v1 51 | - uses: actions/cache@v2 52 | with: 53 | path: | 54 | ~/.cargo/registry 55 | ~/.cargo/git 56 | target 57 | key: ${{ runner.os }}-test-${{ hashFiles('**/Cargo.lock') }} 58 | - run: cargo test --release 59 | fmt: 60 | runs-on: ubuntu-latest 61 | timeout-minutes: 10 62 | steps: 63 | - uses: actions/checkout@v3 64 | - uses: dsherret/rust-toolchain-file@v1 65 | with: 66 | components: rustfmt 67 | - run: cargo fmt --check 68 | cspell: 69 | runs-on: ubuntu-latest 70 | timeout-minutes: 10 71 | steps: 72 | - uses: actions/checkout@v3 73 | - uses: streetsidesoftware/cspell-action@v5 74 | with: 75 | incremental_files_only: false 76 | config: ./cspell.json 77 | files: | 78 | **/*.rs 79 | **/*.md 80 | -------------------------------------------------------------------------------- /tests/programs/numeric_casts.hvm: -------------------------------------------------------------------------------- 1 | @main = x & @tu0 ~ (* x) 2 | 3 | // casting to u24 4 | @tu0 = (* {n x}) & @tu1 ~ (* x) & 0 ~ $([u24] n) // 0 5 | @tu1 = (* {n x}) & @tu2 ~ (* x) & 1234 ~ $([u24] n) // 1234 6 | @tu2 = (* {n x}) & @tu3 ~ (* x) & +4321 ~ $([u24] n) // 4321 7 | @tu3 = (* {n x}) & @tu4 ~ (* x) & -5678 ~ $([u24] n) // 16771538 (reinterprets bits) 8 | @tu4 = (* {n x}) & @tu5 ~ (* x) & 2.8 ~ $([u24] n) // 2 (rounds to zero) 9 | @tu5 = (* {n x}) & @tu6 ~ (* x) & -12.5 ~ $([u24] n) // 0 (saturates) 10 | @tu6 = (* {n x}) & @tu7 ~ (* x) & 16777216.0 ~ $([u24] n) // 16777215 (saturates) 11 | @tu7 = (* {n x}) & @tu8 ~ (* x) & +inf ~ $([u24] n) // 16777215 (saturates) 12 | @tu8 = (* {n x}) & @tu9 ~ (* x) & -inf ~ $([u24] n) // 0 (saturates) 13 | @tu9 = (* {n x}) & @ti0 ~ (* x) & +NaN ~ $([u24] n) // 0 14 | 15 | // casting to i24 16 | @ti0 = (* {n x}) & @ti1 ~ (* x) & 0 ~ $([i24] n) // +0 17 | @ti1 = (* {n x}) & @ti2 ~ (* x) & 1234 ~ $([i24] n) // +1234 18 | @ti2 = (* {n x}) & @ti3 ~ (* x) & +4321 ~ $([i24] n) // +4321 19 | @ti3 = (* {n x}) & @ti4 ~ (* x) & -5678 ~ $([i24] n) // -5678 20 | @ti4 = (* {n x}) & @ti5 ~ (* x) & 2.8 ~ $([i24] n) // +2 (rounds to zero) 21 | @ti5 = (* {n x}) & @ti6 ~ (* x) & -12.7 ~ $([i24] n) // -12 (rounds to zero) 22 | @ti6 = (* {n x}) & @ti7 ~ (* x) & 8388610.0 ~ $([i24] n) // +8388607 (saturates) 23 | @ti7 = (* {n x}) & @ti8 ~ (* x) & -8388610.0 ~ $([i24] n) // -8388608 (saturates) 24 | @ti8 = (* {n x}) & @ti9 ~ (* x) & +inf ~ $([i24] n) // +8388607 (saturates) 25 | @ti9 = (* {n x}) & @ti10 ~ (* x) & -inf ~ $([i24] n) // -8388608 (saturates) 26 | @ti10 = (* {n x}) & @tf0 ~ (* x) & +NaN ~ $([i24] n) // +0 27 | 28 | // casting to f24 29 | @tf0 = (* {n x}) & @tf1 ~ (* x) & +NaN ~ $([f24] n) // +NaN 30 | @tf1 = (* {n x}) & @tf2 ~ (* x) & +inf ~ $([f24] n) // +inf 31 | @tf2 = (* {n x}) & @tf3 ~ (* x) & -inf ~ $([f24] n) // -inf 32 | @tf3 = (* {n x}) & @tf4 ~ (* x) & 2.15 ~ $([f24] n) // 2.15 33 | @tf4 = (* {n x}) & @tf5 ~ (* x) & -2.15 ~ $([f24] n) // -2.15 34 | @tf5 = (* {n x}) & @tf6 ~ (* x) & 0.15 ~ $([f24] n) // 0.15 35 | @tf6 = (* {n x}) & @tf7 ~ (* x) & -1234 ~ $([f24] n) // -1234.0 36 | @tf7 = (* {n x}) & @tf8 ~ (* x) & +1234 ~ $([f24] n) // +1234.0 37 | @tf8 = (* {n x}) & @tf9 ~ (* x) & 123456 ~ $([f24] n) // 123456.0 38 | @tf9 = (* {n x}) & @tp0 ~ (* x) & 16775982 ~ $([f24] n) // 16775936.0 39 | 40 | // printing 41 | @tp0 = (* {n x}) & @tp1 ~ (* x) & n ~ [u24] // [u24] 42 | @tp1 = (* {n x}) & @tp2 ~ (* x) & n ~ [i24] // [i24] 43 | @tp2 = (* {n x}) & @t ~ (* x) & n ~ [f24] // [f24] 44 | 45 | @t = * 46 | -------------------------------------------------------------------------------- /examples/sort/radix/radix_sort_ctr.hvm1: -------------------------------------------------------------------------------- 1 | // Sort : Arr -> Arr 2 | (Sort t) = (ToArr 0 (ToMap t)) 3 | 4 | // ToMap : Arr -> Map 5 | (ToMap Null) = Free 6 | (ToMap (Leaf a)) = (Radix a) 7 | (ToMap (Node a b)) = (Merge (ToMap a) (ToMap b)) 8 | 9 | // ToArr : U60 -> Map -> Arr 10 | (ToArr x Free) = Null 11 | (ToArr x Used) = (Leaf x) 12 | (ToArr x (Both a b)) = 13 | let a = (ToArr (+ (* x 2) 0) a) 14 | let b = (ToArr (+ (* x 2) 1) b) 15 | (Node a b) 16 | 17 | // Merge : Map -> Map -> Map 18 | (Merge Free Free) = Free 19 | (Merge Free Used) = Used 20 | (Merge Used Free) = Used 21 | (Merge Used Used) = Used 22 | (Merge Free (Both c d)) = (Both c d) 23 | (Merge (Both a b) Free) = (Both a b) 24 | (Merge (Both a b) (Both c d)) = (BOTH (Merge a c) (Merge b d)) 25 | 26 | // Radix : U60 -> Map 27 | (Radix n) = 28 | let r = Used 29 | let r = (Data.U60.swap (& n 1) r Free) 30 | let r = (Data.U60.swap (& n 2) r Free) 31 | let r = (Data.U60.swap (& n 4) r Free) 32 | let r = (Data.U60.swap (& n 8) r Free) 33 | let r = (Data.U60.swap (& n 16) r Free) 34 | let r = (Data.U60.swap (& n 32) r Free) 35 | let r = (Data.U60.swap (& n 64) r Free) 36 | let r = (Data.U60.swap (& n 128) r Free) 37 | let r = (Data.U60.swap (& n 256) r Free) 38 | let r = (Data.U60.swap (& n 512) r Free) 39 | let r = (Data.U60.swap (& n 1024) r Free) 40 | let r = (Data.U60.swap (& n 2048) r Free) 41 | let r = (Data.U60.swap (& n 4096) r Free) 42 | let r = (Data.U60.swap (& n 8192) r Free) 43 | let r = (Data.U60.swap (& n 16384) r Free) 44 | let r = (Data.U60.swap (& n 32768) r Free) 45 | let r = (Data.U60.swap (& n 65536) r Free) 46 | let r = (Data.U60.swap (& n 131072) r Free) 47 | let r = (Data.U60.swap (& n 262144) r Free) 48 | let r = (Data.U60.swap (& n 524288) r Free) 49 | let r = (Data.U60.swap (& n 1048576) r Free) 50 | let r = (Data.U60.swap (& n 2097152) r Free) 51 | let r = (Data.U60.swap (& n 4194304) r Free) 52 | let r = (Data.U60.swap (& n 8388608) r Free) 53 | r 54 | 55 | // Reverse : Arr -> Arr 56 | (Reverse Null) = Null 57 | (Reverse (Leaf a)) = (Leaf a) 58 | (Reverse (Node a b)) = (Node (Reverse b) (Reverse a)) 59 | 60 | // Sum : Arr -> U60 61 | (Sum Null) = 0 62 | (Sum (Leaf x)) = x 63 | (Sum (Node a b)) = (+ (Sum a) (Sum b)) 64 | 65 | // Gen : U60 -> Arr 66 | (Gen n) = (Gen.go n 0) 67 | (Gen.go 0 x) = (Leaf x) 68 | (Gen.go n x) = 69 | let x = (<< x 1) 70 | let y = (| x 1) 71 | let n = (- n 1) 72 | (Node (Gen.go n x) (Gen.go n y)) 73 | 74 | // Strict constructors 75 | (BOTH !a !b) = (Both a b) 76 | //(NODE !a !b) = (Node a b) 77 | //(LEAF !a) = (Leaf a) 78 | 79 | Main = (Sum (Sort (Reverse (Gen 22)))) 80 | -------------------------------------------------------------------------------- /transform/src/inline.rs: -------------------------------------------------------------------------------- 1 | use hvm64_util::prelude::*; 2 | 3 | use core::ops::BitOr; 4 | 5 | use hvm64_ast::{Book, Net, Tree}; 6 | use hvm64_util::maybe_grow; 7 | 8 | use super::TransformError; 9 | 10 | pub trait Inline { 11 | fn inline(&mut self) -> Result, TransformError>; 12 | } 13 | 14 | impl Inline for Book { 15 | fn inline(&mut self) -> Result, TransformError> { 16 | let mut state = InlineState::default(); 17 | state.populate_inlinees(self)?; 18 | let mut all_changed = Set::new(); 19 | for (name, net) in &mut self.nets { 20 | let mut inlined = false; 21 | for tree in net.trees_mut() { 22 | inlined |= state.inline_into(tree); 23 | } 24 | if inlined { 25 | all_changed.insert(name.to_owned()); 26 | } 27 | } 28 | Ok(all_changed) 29 | } 30 | } 31 | 32 | #[derive(Debug, Default)] 33 | struct InlineState { 34 | inlinees: Map, 35 | } 36 | 37 | impl InlineState { 38 | fn populate_inlinees(&mut self, book: &Book) -> Result<(), TransformError> { 39 | for (name, net) in &book.nets { 40 | if net.should_inline() { 41 | // Detect cycles with tortoise and hare algorithm 42 | let mut hare = &net.root; 43 | let mut tortoise = &net.root; 44 | // Whether or not the tortoise should take a step 45 | let mut parity = false; 46 | while let Tree::Ref(name) = hare { 47 | let Some(net) = &book.nets.get(name) else { break }; 48 | if net.should_inline() { 49 | hare = &net.root; 50 | } else { 51 | break; 52 | } 53 | if parity { 54 | let Tree::Ref(tortoise_name) = tortoise else { unreachable!() }; 55 | if tortoise_name == name { 56 | Err(TransformError::InfiniteRefCycle(name.to_owned()))?; 57 | } 58 | tortoise = &book.nets[tortoise_name].root; 59 | } 60 | parity = !parity; 61 | } 62 | self.inlinees.insert(name.to_owned(), hare.clone()); 63 | } 64 | } 65 | Ok(()) 66 | } 67 | fn inline_into(&self, tree: &mut Tree) -> bool { 68 | maybe_grow(|| { 69 | let Tree::Ref(name) = &*tree else { 70 | return tree.children_mut().map(|t| self.inline_into(t)).fold(false, bool::bitor); 71 | }; 72 | if let Some(inlined) = self.inlinees.get(name) { 73 | *tree = inlined.clone(); 74 | true 75 | } else { 76 | false 77 | } 78 | }) 79 | } 80 | } 81 | 82 | trait ShouldInline { 83 | fn should_inline(&self) -> bool; 84 | } 85 | 86 | impl ShouldInline for Net { 87 | fn should_inline(&self) -> bool { 88 | self.redexes.is_empty() && self.root.children().next().is_none() 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /tests/lists.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "std")] 2 | 3 | use crate::loaders::*; 4 | use hvm64_ast::{Book, Tree}; 5 | use insta::assert_debug_snapshot; 6 | mod loaders; 7 | 8 | fn list_got(index: u32) -> Book { 9 | let code = load_file("list_put_got.hvm"); 10 | let mut book = parse_core(&code); 11 | let def = book.get_mut("GenGotIndex").unwrap(); 12 | def.apply_tree(Tree::Ref(format!("S{index}"))); 13 | let def = book.get_mut("main").unwrap(); 14 | def.apply_tree(Tree::Ref("GenGotIndex".to_string())); 15 | book 16 | } 17 | 18 | fn list_put(index: u32, value: u32) -> Book { 19 | let code = load_file("list_put_got.hvm"); 20 | let mut book = parse_core(&code); 21 | let def = book.get_mut("GenPutIndexValue").unwrap(); 22 | def.apply_tree(Tree::Ref(format!("S{index}"))); 23 | def.apply_tree(Tree::Ref(format!("S{value}"))); 24 | let def = book.get_mut("main").unwrap(); 25 | def.apply_tree(Tree::Ref("GenPutIndexValue".to_string())); 26 | book 27 | } 28 | 29 | #[test] 30 | fn test_list_got() { 31 | let mut rwts_list = Vec::new(); 32 | 33 | for index in [0, 1, 3, 7, 15, 31] { 34 | let book = list_got(index); 35 | let (rwts, _) = normal(book, None); 36 | rwts_list.push(rwts.total()) 37 | } 38 | 39 | assert_debug_snapshot!(rwts_list[0], @"594"); 40 | assert_debug_snapshot!(rwts_list[1], @"623"); 41 | assert_debug_snapshot!(rwts_list[2], @"681"); 42 | assert_debug_snapshot!(rwts_list[3], @"797"); 43 | assert_debug_snapshot!(rwts_list[4], @"1029"); 44 | assert_debug_snapshot!(rwts_list[5], @"1493"); 45 | 46 | // Tests the linearity of the function 47 | let delta = rwts_list[1] - rwts_list[0]; 48 | assert_eq!(rwts_list[1] + delta * 2, rwts_list[2]); 49 | assert_eq!(rwts_list[2] + delta * 4, rwts_list[3]); 50 | assert_eq!(rwts_list[3] + delta * 8, rwts_list[4]); 51 | assert_eq!(rwts_list[4] + delta * 16, rwts_list[5]); 52 | } 53 | 54 | #[test] 55 | fn test_list_put() { 56 | let mut rwts_list = Vec::new(); 57 | 58 | for (index, value) in [(0, 2), (1, 4), (3, 8), (7, 16), (15, 32), (31, 0)] { 59 | let book = list_put(index, value); 60 | let (rwts, _) = normal(book, None); 61 | rwts_list.push(rwts.total()) 62 | } 63 | 64 | assert_debug_snapshot!(rwts_list[0], @"585"); 65 | assert_debug_snapshot!(rwts_list[1], @"615"); 66 | assert_debug_snapshot!(rwts_list[2], @"675"); 67 | assert_debug_snapshot!(rwts_list[3], @"795"); 68 | assert_debug_snapshot!(rwts_list[4], @"1035"); 69 | assert_debug_snapshot!(rwts_list[5], @"1515"); 70 | 71 | //Tests the linearity of the function 72 | let delta = rwts_list[1] - rwts_list[0]; 73 | assert_eq!(rwts_list[1] + delta * 2, rwts_list[2]); 74 | assert_eq!(rwts_list[2] + delta * 4, rwts_list[3]); 75 | assert_eq!(rwts_list[3] + delta * 8, rwts_list[4]); 76 | assert_eq!(rwts_list[4] + delta * 16, rwts_list[5]); 77 | } 78 | -------------------------------------------------------------------------------- /src/full.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use clap::{Parser, Subcommand}; 4 | 5 | use crate::{args::TransformArgs, RunArgs, RuntimeOpts}; 6 | 7 | #[derive(Parser, Debug)] 8 | #[command( 9 | author, 10 | version, 11 | about = "A massively parallel Interaction Combinator evaluator", 12 | long_about = r##" 13 | A massively parallel Interaction Combinator evaluator 14 | 15 | Examples: 16 | $ hvm64 run examples/church_encoding/church.hvm 17 | $ hvm64 run examples/addition.hvm "#16" "#3" 18 | $ hvm64 compile examples/addition.hvm 19 | $ hvm64 reduce examples/addition.hvm -- "a & @mul ~ (#3 (#4 a))" 20 | $ hvm64 reduce -- "a & #3 ~ <* #4 a>""## 21 | )] 22 | pub struct FullCli { 23 | #[command(subcommand)] 24 | pub mode: CliMode, 25 | } 26 | 27 | #[derive(Subcommand, Clone, Debug)] 28 | #[command(author, version)] 29 | pub enum CliMode { 30 | /// Compile a hvm-64 program into a Rust crate. 31 | Compile { 32 | /// hvm-64 file to compile. 33 | file: PathBuf, 34 | /// Output path; defaults to the input file with `.hvm` stripped. 35 | #[arg(short, long)] 36 | output: Option, 37 | #[command(flatten)] 38 | transform_args: TransformArgs, 39 | }, 40 | /// Run a program, optionally passing a list of arguments to it. 41 | Run { 42 | /// Name of the file to load. 43 | file: PathBuf, 44 | #[command(flatten)] 45 | args: RunArgs, 46 | #[command(flatten)] 47 | run_opts: RuntimeOpts, 48 | #[command(flatten)] 49 | transform_args: TransformArgs, 50 | }, 51 | /// Reduce hvm-64 expressions to their normal form. 52 | /// 53 | /// The expressions are passed as command-line arguments. 54 | /// It is also possible to load files before reducing the expression, 55 | /// which makes it possible to reference definitions from the file 56 | /// in the expression. 57 | Reduce { 58 | /// Files to load before reducing the expressions. 59 | /// 60 | /// Multiple files will act as if they're concatenated together. 61 | #[arg(required = false)] 62 | files: Vec, 63 | /// Expressions to reduce. 64 | /// 65 | /// The normal form of each expression will be 66 | /// printed on a new line. This list must be separated from the file list 67 | /// with a double dash ('--'). 68 | #[arg(required = false, last = true)] 69 | exprs: Vec, 70 | #[command(flatten)] 71 | run_opts: RuntimeOpts, 72 | #[command(flatten)] 73 | transform_args: TransformArgs, 74 | }, 75 | /// Transform a hvm-64 program using one of the optimization passes. 76 | Transform { 77 | /// Files to load before reducing the expressions. 78 | /// 79 | /// Multiple files will act as if they're concatenated together. 80 | #[arg(required = true)] 81 | files: Vec, 82 | #[command(flatten)] 83 | transform_args: TransformArgs, 84 | }, 85 | } 86 | -------------------------------------------------------------------------------- /examples/sort/radix/radix_sort_ctr.hs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Data.Word 4 | import Data.Bits 5 | import System.Environment 6 | 7 | data Arr = Null | Leaf !Word32 | Node Arr Arr deriving Show 8 | data Map = Free | Used | Both !Map !Map deriving Show 9 | 10 | sort :: Arr -> Arr 11 | sort t = toArr 0 (toMap t) 12 | 13 | toMap :: Arr -> Map 14 | toMap Null = Free 15 | toMap (Leaf a) = radix a 16 | toMap (Node a b) = merge (toMap a) (toMap b) 17 | 18 | toArr :: Word32 -> Map -> Arr 19 | toArr x Free = Null 20 | toArr x Used = Leaf x 21 | toArr x (Both a b) = 22 | let a' = toArr (x * 2 + 0) a 23 | b' = toArr (x * 2 + 1) b 24 | in Node a' b' 25 | 26 | merge :: Map -> Map -> Map 27 | merge Free Free = Free 28 | merge Free Used = Used 29 | merge Used Free = Used 30 | merge Used Used = Used 31 | merge Free (Both c d) = (Both c d) 32 | merge (Both a b) Free = (Both a b) 33 | merge (Both a b) (Both c d) = (Both (merge a c) (merge b d)) 34 | 35 | radix :: Word32 -> Map 36 | radix n = 37 | let r0 = Used 38 | r1 = u60_swap (n .&. 1) r0 Free 39 | r2 = u60_swap (n .&. 2) r1 Free 40 | r3 = u60_swap (n .&. 4) r2 Free 41 | r4 = u60_swap (n .&. 8) r3 Free 42 | r5 = u60_swap (n .&. 16) r4 Free 43 | r6 = u60_swap (n .&. 32) r5 Free 44 | r7 = u60_swap (n .&. 64) r6 Free 45 | r8 = u60_swap (n .&. 128) r7 Free 46 | r9 = u60_swap (n .&. 256) r8 Free 47 | rA = u60_swap (n .&. 512) r9 Free 48 | rB = u60_swap (n .&. 1024) rA Free 49 | rC = u60_swap (n .&. 2048) rB Free 50 | rD = u60_swap (n .&. 4096) rC Free 51 | rE = u60_swap (n .&. 8192) rD Free 52 | rF = u60_swap (n .&. 16384) rE Free 53 | rG = u60_swap (n .&. 32768) rF Free 54 | rH = u60_swap (n .&. 65536) rG Free 55 | rI = u60_swap (n .&. 131072) rH Free 56 | rJ = u60_swap (n .&. 262144) rI Free 57 | rK = u60_swap (n .&. 524288) rJ Free 58 | rL = u60_swap (n .&. 1048576) rK Free 59 | rM = u60_swap (n .&. 2097152) rL Free 60 | rN = u60_swap (n .&. 4194304) rM Free 61 | rO = u60_swap (n .&. 8388608) rN Free 62 | in rO 63 | 64 | u60_swap :: Word32 -> Map -> Map -> Map 65 | u60_swap 0 a b = Both a b 66 | u60_swap n a b = Both b a 67 | 68 | reverse' :: Arr -> Arr 69 | reverse' Null = Null 70 | reverse' (Leaf a) = Leaf a 71 | reverse' (Node a b) = Node (reverse' b) (reverse' a) 72 | 73 | sum' :: Arr -> Word32 74 | sum' Null = 0 75 | sum' (Leaf x) = x 76 | sum' (Node a b) = sum' a + sum' b 77 | 78 | gen :: Word32 -> Arr 79 | gen n = gen_go n 0 where 80 | gen_go :: Word32 -> Word32 -> Arr 81 | gen_go 0 x = Leaf x 82 | gen_go n x = 83 | let x' = x * 2 84 | y' = x' + 1 85 | n' = n - 1 86 | in Node (gen_go n' x') (gen_go n' y') 87 | 88 | main :: IO () 89 | main = do 90 | print $ sum' (sort (reverse' (gen 22))) 91 | -------------------------------------------------------------------------------- /examples/lambda_calculus/hoas.hvm: -------------------------------------------------------------------------------- 1 | @App = (a (b (* ((a (b c)) (* c))))) 2 | @C0 = a 3 | & @Lam ~ (@C0$S0 a) 4 | @C0$S0 = (* a) 5 | & @Lam ~ ((b b) a) 6 | @C1 = a 7 | & @Lam ~ (@C1$S0 a) 8 | @C1$S0 = (a b) 9 | & @Lam ~ ((c d) b) 10 | & @App ~ (a (c d)) 11 | @C2 = a 12 | & @Lam ~ (@C2$S0 a) 13 | @C2$S0 = (#1{a b} c) 14 | & @Lam ~ ((d e) c) 15 | & @App ~ (a (f e)) 16 | & @App ~ (b (d f)) 17 | @C3 = a 18 | & @Lam ~ (@C3$S0 a) 19 | @C3$S0 = (#2{a #2{b c}} d) 20 | & @Lam ~ ((e f) d) 21 | & @App ~ (a (g f)) 22 | & @App ~ (b (h g)) 23 | & @App ~ (c (e h)) 24 | @C4 = a 25 | & @Lam ~ (@C4$S0 a) 26 | @C4$S0 = (#3{a #3{b #3{c d}}} e) 27 | & @Lam ~ ((f g) e) 28 | & @App ~ (a (h g)) 29 | & @App ~ (b (i h)) 30 | & @App ~ (c (j i)) 31 | & @App ~ (d (f j)) 32 | @C6 = a 33 | & @Lam ~ (@C6$S0 a) 34 | @C6$S0 = (#4{a #4{b #4{c #4{d #4{e f}}}}} g) 35 | & @Lam ~ ((h i) g) 36 | & @App ~ (a (j i)) 37 | & @App ~ (b (k j)) 38 | & @App ~ (c (l k)) 39 | & @App ~ (d (m l)) 40 | & @App ~ (e (n m)) 41 | & @App ~ (f (h n)) 42 | @C8 = a 43 | & @Lam ~ (@C8$S0 a) 44 | @C8$S0 = (#5{a #5{b #5{c #5{d #5{e #5{f g}}}}}} h) 45 | & @Lam ~ ((i j) h) 46 | & @App ~ (a (k j)) 47 | & @App ~ (b (l k)) 48 | & @App ~ (c (m l)) 49 | & @App ~ (d (n m)) 50 | & @App ~ (e (o n)) 51 | & @App ~ (f (p o)) 52 | & @App ~ (g (i p)) 53 | @CS = a 54 | & @Lam ~ (@CS$S0 a) 55 | @CS$S0 = (a b) 56 | & @Lam ~ ((#6{c d} e) b) 57 | & @Lam ~ ((f g) e) 58 | & @App ~ (c (h g)) 59 | & @App ~ (i (f h)) 60 | & @App ~ (a (d i)) 61 | @FOO = a 62 | & @Lam ~ (@FOO$S0 a) 63 | @FOO$S0 = (#7{a b} c) 64 | & @App ~ (a (b c)) 65 | @False = a 66 | & @Lam ~ (@False$S0 a) 67 | @False$S0 = (* a) 68 | & @Lam ~ ((b b) a) 69 | @ID = a 70 | & @Lam ~ ((b b) a) 71 | @Lam = (a ((a b) (* (* b)))) 72 | @Not = a 73 | & @Lam ~ (@Not$S0 a) 74 | @Not$S0 = (a b) 75 | & @App ~ (c (@True b)) 76 | & @App ~ (a (@False c)) 77 | @Sub = (a (* (* ((a b) b)))) 78 | @True = a 79 | & @Lam ~ (@True$S0 a) 80 | @True$S0 = (a b) 81 | & @Lam ~ ((* a) b) 82 | @main = a 83 | & @normal ~ (b a) 84 | & @App ~ (c (@True b)) 85 | & @App ~ (@C6 (@Not c)) 86 | @normal = (a b) 87 | & @normal.go ~ (c b) 88 | & @reduce ~ (a c) 89 | @normal.go = ((@normal.go$S0 (@normal.go$S1 ((a a) b))) b) 90 | @normal.go$S0 = ((a b) c) 91 | & @Lam ~ ((d e) c) 92 | & @normal ~ (b e) 93 | & @Sub ~ (d a) 94 | @normal.go$S1 = (a (b c)) 95 | & @App ~ (d (e c)) 96 | & @normal ~ (b e) 97 | & @normal ~ (a d) 98 | @reduce = ((@reduce$S0 (@reduce$S1 (@reduce$S2 a))) a) 99 | @reduce$S0 = (a b) 100 | & @Lam ~ (a b) 101 | @reduce$S1 = (a (b c)) 102 | & @reduce.app ~ (d (b c)) 103 | & @reduce ~ (a d) 104 | @reduce$S2 = (a b) 105 | & @Sub ~ (a b) 106 | @reduce.app = ((@reduce.app$S0 (@reduce.app$S1 (@reduce.app$S2 a))) a) 107 | @reduce.app$S0 = ((a b) (a c)) 108 | & @reduce ~ (b c) 109 | @reduce.app$S1 = (a (b (c d))) 110 | & @App ~ (e (c d)) 111 | & @App ~ (a (b e)) 112 | @reduce.app$S2 = (a (b c)) 113 | & @App ~ (d (b c)) 114 | & @Sub ~ (a d) 115 | 116 | -------------------------------------------------------------------------------- /util/src/bi_enum.rs: -------------------------------------------------------------------------------- 1 | /// Defines bi-directional mappings for a numeric enum. 2 | #[macro_export] 3 | macro_rules! bi_enum { 4 | ( 5 | #[repr($uN:ident)] 6 | $(#$attr:tt)* 7 | $vis:vis enum $Ty:ident { 8 | $($(#$var_addr:tt)* $Variant:ident = $value:literal),* $(,)? 9 | } 10 | ) => { 11 | #[repr($uN)] $(#$attr)* $vis enum $Ty { $($(#$var_addr)* $Variant = $value,)* } 12 | 13 | impl TryFrom<$uN> for $Ty { 14 | type Error = (); 15 | fn try_from(value: $uN) -> Result { 16 | Ok(match value { $($value => $Ty::$Variant,)* _ => Err(())?, }) 17 | } 18 | } 19 | 20 | impl $Ty { 21 | #[allow(unused)] 22 | #[inline(always)] 23 | pub unsafe fn from_unchecked(value: $uN) -> $Ty { 24 | Self::try_from(value).unwrap_unchecked() 25 | } 26 | } 27 | 28 | impl From<$Ty> for $uN { 29 | #[inline(always)] 30 | fn from(value: $Ty) -> Self { value as Self } 31 | } 32 | }; 33 | ( 34 | #[repr($uN:ident)] 35 | $(#$attr:tt)* 36 | $vis:vis enum $Ty:ident { 37 | $($(#$var_addr:tt)* $str:literal: $Variant:ident = $value:literal),* $(,)? 38 | } 39 | ) => { 40 | bi_enum! { #[repr($uN)] $(#$attr)* $vis enum $Ty { $($(#$var_addr)* $Variant = $value,)* } } 41 | 42 | #[allow(unused)] 43 | impl $Ty { 44 | #[inline] 45 | pub fn from_str_prefix(str: &str) -> Option { 46 | $(if str.starts_with($str) { Some($Ty::$Variant) } else)* 47 | { None } 48 | } 49 | 50 | #[inline] 51 | pub fn as_str(self) -> &'static str { 52 | match self { $($Ty::$Variant => $str,)* } 53 | } 54 | } 55 | 56 | impl core::fmt::Display for $Ty { 57 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 58 | f.write_str(self.as_str()) 59 | } 60 | } 61 | 62 | impl core::str::FromStr for $Ty { 63 | type Err = (); 64 | fn from_str(s: &str) -> Result { 65 | Ok(match s { $($str => $Ty::$Variant,)* _ => Err(())?, }) 66 | } 67 | } 68 | }; 69 | } 70 | 71 | #[test] 72 | fn test_bi_enum() { 73 | use crate::prelude::*; 74 | use alloc::string::ToString; 75 | use core::str::FromStr; 76 | 77 | bi_enum! { 78 | #[repr(u8)] 79 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 80 | enum Trit { 81 | Nil = 0, 82 | One = 1, 83 | Two = 2, 84 | } 85 | } 86 | assert_eq!(u8::from(Trit::Nil), 0); 87 | assert_eq!(Trit::try_from(1), Ok(Trit::One)); 88 | assert_eq!(Trit::try_from(100), Err(())); 89 | 90 | bi_enum! { 91 | #[repr(u8)] 92 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 93 | enum Op { 94 | "+": Add = 0, 95 | "-": Sub = 1, 96 | "*": Mul = 2, 97 | "/": Div = 3, 98 | } 99 | } 100 | assert_eq!(Op::Add.to_string(), "+"); 101 | assert_eq!(Op::from_str("-"), Ok(Op::Sub)); 102 | assert_eq!(Op::from_str("#"), Err(())); 103 | } 104 | -------------------------------------------------------------------------------- /.github/workflows/bench.yml: -------------------------------------------------------------------------------- 1 | name: Bench 2 | 3 | on: {} # temporarily disabled 4 | 5 | concurrency: 6 | group: bench-${{ github.ref }} 7 | cancel-in-progress: true 8 | 9 | jobs: 10 | bench: 11 | runs-on: ubuntu-latest 12 | timeout-minutes: 30 13 | steps: 14 | - uses: actions/checkout@v3 15 | - uses: dsherret/rust-toolchain-file@v1 16 | - uses: actions/cache@v2 17 | with: 18 | path: | 19 | ~/.cargo/registry 20 | ~/.cargo/git 21 | target 22 | key: ${{ runner.os }}-check-${{ hashFiles('**/Cargo.lock') }} 23 | - name: compare perf 24 | run: | 25 | git fetch origin main 26 | cd .. 27 | git clone https://github.com/higherorderco/hvm-compare-perf 28 | cd hvm-compare-perf 29 | NO_COLOR=1 cargo run bench \ 30 | --core ../hvm-64 \ 31 | -r main -r ${{ github.sha }} \ 32 | -m intr-singl -m intr-multi \ 33 | > ../hvm-64/table 34 | - name: write comment 35 | run: | 36 | echo 'Perf run for [`'`git rev-parse --short ${{ github.sha }}`'`](https://github.com/higherorderco/hvm-64/commit/${{ github.sha }}):' >> comment 37 | echo '```' >> comment 38 | cat table >> comment 39 | echo '```' >> comment 40 | - name: post comment 41 | run: gh pr comment ${{ github.event.number }} -F comment 42 | env: 43 | GH_TOKEN: ${{ secrets.PAT }} 44 | - name: hide old comment 45 | env: 46 | GH_TOKEN: ${{ secrets.PAT }} 47 | run: | 48 | COMMENT_ID=$( 49 | gh api graphql -F pr=${{ github.event.number }} -f query=' 50 | query($pr: Int!) { 51 | organization(login: "higherorderco") { 52 | repository(name: "hvm-64") { 53 | pullRequest(number: $pr) { 54 | comments(last: 100) { 55 | nodes { id author { login } } 56 | } 57 | } 58 | } 59 | } 60 | } 61 | ' \ 62 | | jq -r ' 63 | [ 64 | .data.organization.repository.pullRequest.comments.nodes | .[] 65 | | select(.author.login == "HigherOrderBot") 66 | | .id 67 | ] | .[-2] 68 | ' 69 | ) 70 | 71 | if [ $COMMENT_ID != null ] 72 | then 73 | gh api graphql -F id=$COMMENT_ID -f query=' 74 | mutation($id: ID!) { 75 | minimizeComment(input: { 76 | subjectId: $id, 77 | classifier: OUTDATED, 78 | }) { minimizedComment { ...on Comment { id } } } 79 | } 80 | ' 81 | fi 82 | - name: delete on cancel 83 | if: ${{ cancelled() }} 84 | run: gh workflow run delete-cancelled.yml -f run_id=${{ github.run_id }} 85 | env: 86 | GH_TOKEN: ${{ secrets.PAT }} 87 | -------------------------------------------------------------------------------- /src/compile/include_files.rs: -------------------------------------------------------------------------------- 1 | use std::{fs, io, path::Path}; 2 | 3 | use hvm64_host::Host; 4 | 5 | macro_rules! include_files { 6 | ($([$($prefix:ident)*])? crate $name:ident {$($sub:tt)*} $($rest:tt)*) => { 7 | include_files!([$($($prefix)*)?] $name/ { Cargo.toml src/ { $($sub)* } }); 8 | include_files!([$($($prefix)*)?] $($rest)*); 9 | }; 10 | 11 | ($([$($prefix:ident)*])? $mod:ident/ {$($sub:tt)*} $($rest:tt)*) => { 12 | fs::create_dir_all(concat!(".hvm/", $($(stringify!($prefix), "/",)*)? stringify!($mod)))?; 13 | include_files!([$($($prefix)* $mod)?] $($sub)*); 14 | include_files!([$($($prefix)*)?] $($rest)*); 15 | }; 16 | 17 | ($([$($prefix:ident)*])? $mod:ident {$($sub:tt)*} $($rest:tt)*) => { 18 | include_files!([$($($prefix)*)?] $mod/ {$($sub)*} $($rest)*); 19 | include_files!([$($($prefix)*)?] $mod $($rest)*); 20 | }; 21 | 22 | ($([$($prefix:ident)*])? $file:ident.$ext:ident $($rest:tt)*) => { 23 | fs::write( 24 | concat!(".hvm/", $($(stringify!($prefix), "/",)*)* stringify!($file), ".", stringify!($ext)), 25 | include_str!(concat!("../../", $($(stringify!($prefix), "/",)*)* stringify!($file), ".", stringify!($ext))), 26 | )?; 27 | include_files!([$($($prefix)*)?] $($rest)*); 28 | }; 29 | 30 | ($([$($prefix:ident)*])? $file:ident $($rest:tt)*) => { 31 | include_files!([$($($prefix)*)?] $file.rs $($rest)*); 32 | }; 33 | 34 | ($([$($prefix:ident)*])?) => {}; 35 | } 36 | 37 | /// Copies the `hvm-64` source to a temporary `.hvm` directory. 38 | /// Only a subset of `Cargo.toml` is included. 39 | pub fn create_temp_hvm(host: &Host) -> Result<(), io::Error> { 40 | let lib = super::compile_host(host); 41 | let outdir = ".hvm"; 42 | if Path::new(&outdir).exists() { 43 | fs::remove_dir_all(outdir)?; 44 | } 45 | 46 | fs::create_dir_all(".hvm/gen/src/")?; 47 | fs::write( 48 | ".hvm/Cargo.toml", 49 | r#" 50 | [workspace] 51 | resolver = "2" 52 | package.version = "0.0.0" 53 | members = ["gen"] 54 | 55 | [workspace.lints] 56 | "#, 57 | )?; 58 | fs::write( 59 | ".hvm/gen/Cargo.toml", 60 | r#" 61 | [package] 62 | name = "hvm64-gen" 63 | edition = "2021" 64 | 65 | [lib] 66 | crate-type = ["dylib"] 67 | 68 | [dependencies] 69 | hvm64-runtime = { path = "../runtime" } 70 | hvm64-num = { path = "../num" } 71 | "#, 72 | )?; 73 | fs::write(".hvm/gen/src/lib.rs", lib)?; 74 | 75 | include_files! { 76 | crate num { 77 | num 78 | } 79 | crate util { 80 | lib 81 | bi_enum 82 | create_var 83 | deref 84 | new_uninit_slice 85 | maybe_grow 86 | multi_iterator 87 | parse_abbrev_number 88 | prelude 89 | pretty_num 90 | } 91 | crate runtime { 92 | runtime 93 | addr 94 | allocator 95 | def 96 | instruction 97 | interact 98 | linker 99 | net 100 | node 101 | parallel 102 | port 103 | trace 104 | wire 105 | } 106 | } 107 | 108 | Ok(()) 109 | } 110 | -------------------------------------------------------------------------------- /transform/src/transform.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "std"), no_std)] 2 | 3 | use hvm64_util::prelude::*; 4 | 5 | use hvm64_ast::Book; 6 | 7 | pub mod eta_reduce; 8 | pub mod inline; 9 | pub mod pre_reduce; 10 | pub mod prune; 11 | 12 | use eta_reduce::EtaReduce; 13 | use inline::Inline; 14 | use pre_reduce::PreReduce; 15 | use prune::Prune; 16 | 17 | #[derive(Debug, Clone, PartialEq, Eq)] 18 | #[non_exhaustive] 19 | pub enum TransformError { 20 | InfiniteRefCycle(String), 21 | } 22 | 23 | impl fmt::Display for TransformError { 24 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 25 | match self { 26 | TransformError::InfiniteRefCycle(name) => write!(f, "infinite reference cycle in `@{name}`"), 27 | } 28 | } 29 | } 30 | 31 | pub trait Transform { 32 | fn transform(&mut self, passes: TransformPasses, opts: &TransformOpts) -> Result<(), TransformError>; 33 | } 34 | 35 | impl Transform for Book { 36 | fn transform(&mut self, passes: TransformPasses, opts: &TransformOpts) -> Result<(), TransformError> { 37 | if passes.prune { 38 | self.prune(&opts.prune_entrypoints); 39 | } 40 | if passes.pre_reduce { 41 | if passes.eta_reduce { 42 | for def in self.nets.values_mut() { 43 | def.eta_reduce(); 44 | } 45 | } 46 | self.pre_reduce( 47 | &|x| opts.pre_reduce_skip.iter().any(|y| x == y), 48 | opts.pre_reduce_memory, 49 | opts.pre_reduce_rewrites, 50 | ); 51 | } 52 | for def in &mut self.nets.values_mut() { 53 | if passes.eta_reduce { 54 | def.eta_reduce(); 55 | } 56 | } 57 | if passes.inline { 58 | loop { 59 | let inline_changed = self.inline()?; 60 | if inline_changed.is_empty() { 61 | break; 62 | } 63 | if !passes.eta_reduce { 64 | break; 65 | } 66 | for name in inline_changed { 67 | let def = self.get_mut(&name).unwrap(); 68 | def.eta_reduce(); 69 | } 70 | } 71 | } 72 | if passes.prune { 73 | self.prune(&opts.prune_entrypoints); 74 | } 75 | Ok(()) 76 | } 77 | } 78 | 79 | #[derive(Clone, Debug)] 80 | pub struct TransformOpts { 81 | pub pre_reduce_skip: Vec, 82 | pub pre_reduce_memory: Option, 83 | pub pre_reduce_rewrites: u64, 84 | pub prune_entrypoints: Vec, 85 | } 86 | 87 | impl TransformOpts { 88 | pub fn add_entrypoint(&mut self, entrypoint: &str) { 89 | self.pre_reduce_skip.push(entrypoint.to_owned()); 90 | self.prune_entrypoints.push(entrypoint.to_owned()); 91 | } 92 | } 93 | 94 | macro_rules! transform_passes { 95 | ($($pass:ident),* $(,)?) => { 96 | #[derive(Debug, Default, Clone, Copy)] 97 | #[non_exhaustive] 98 | pub struct TransformPasses { 99 | $(pub $pass: bool),* 100 | } 101 | 102 | impl TransformPasses { 103 | pub const NONE: Self = Self { $($pass: false),* }; 104 | pub const ALL: Self = Self { $($pass: true),* }; 105 | } 106 | } 107 | } 108 | 109 | transform_passes! { 110 | pre_reduce, 111 | eta_reduce, 112 | inline, 113 | prune, 114 | } 115 | -------------------------------------------------------------------------------- /examples/sort/radix/radix_sort_ctr.hvm: -------------------------------------------------------------------------------- 1 | @Concat = (a (b (* (* ((a (b c)) c))))) 2 | @Empty = (a (* (* a))) 3 | @Free = (a (* (* a))) 4 | @Node = (a (b (* (* ((a (b c)) c))))) 5 | @Single = (a (* ((a b) (* b)))) 6 | @Used = (* @Used$C0) 7 | @Used$C0 = (a (* a)) 8 | @gen = (?((@gen$C0 @gen$C1) a) a) 9 | @gen$C0 = (a b) 10 | & @Single ~ (a b) 11 | @gen$C1 = (#3{a b} ($([:<<1] #4{c $([|1] d)}) e)) 12 | & @Concat ~ (f (g e)) 13 | & @gen ~ (b (d g)) 14 | & @gen ~ (a (c f)) 15 | @main = a 16 | & @sum ~ (b a) 17 | & @sort ~ (c b) 18 | & @rev ~ (d c) 19 | & @gen ~ (20 (0 d)) 20 | @merge = (((a a) (@merge$C2 (@merge$C7 (b c)))) (b c)) 21 | @merge$C0 = (* *) 22 | @merge$C1 = (* @merge$C0) 23 | @merge$C2 = ((@Used (@Used (@merge$C1 a))) a) 24 | @merge$C3 = (a (b c)) 25 | & @Node ~ (a (b c)) 26 | @merge$C4 = (* *) 27 | @merge$C5 = (* @merge$C4) 28 | @merge$C6 = (a (b (c (d e)))) 29 | & @Node ~ (f (g e)) 30 | & @merge ~ (d (b g)) 31 | & @merge ~ (c (a f)) 32 | @merge$C7 = (a (b ((@merge$C3 (@merge$C5 (@merge$C6 (a (b c))))) c))) 33 | @radix = (#1{$([&8388608] a) #1{$([&4194304] b) #1{$([&2097152] c) #1{$([&1048576] d) #1{$([&524288] e) #1{$([&262144] f) #1{$([&131072] g) #1{$([&65536] h) #1{$([&32768] i) #1{$([&16384] j) #1{$([&8192] k) #1{$([&4096] l) #1{$([&2048] m) #1{$([&1024] n) #1{$([&512] o) #1{$([&256] p) #1{$([&128] q) #1{$([&64] r) #1{$([&32] s) #1{$([&16] t) #1{$([&8] u) #1{$([&4] v) #1{$([&2] w) $([&1] x)}}}}}}}}}}}}}}}}}}}}}}} y) 34 | & @swap ~ (a (z (@Free y))) 35 | & @swap ~ (b (ab (@Free z))) 36 | & @swap ~ (c (bb (@Free ab))) 37 | & @swap ~ (d (cb (@Free bb))) 38 | & @swap ~ (e (db (@Free cb))) 39 | & @swap ~ (f (eb (@Free db))) 40 | & @swap ~ (g (fb (@Free eb))) 41 | & @swap ~ (h (gb (@Free fb))) 42 | & @swap ~ (i (hb (@Free gb))) 43 | & @swap ~ (j (ib (@Free hb))) 44 | & @swap ~ (k (jb (@Free ib))) 45 | & @swap ~ (l (kb (@Free jb))) 46 | & @swap ~ (m (lb (@Free kb))) 47 | & @swap ~ (n (mb (@Free lb))) 48 | & @swap ~ (o (nb (@Free mb))) 49 | & @swap ~ (p (ob (@Free nb))) 50 | & @swap ~ (q (pb (@Free ob))) 51 | & @swap ~ (r (qb (@Free pb))) 52 | & @swap ~ (s (rb (@Free qb))) 53 | & @swap ~ (t (sb (@Free rb))) 54 | & @swap ~ (u (tb (@Free sb))) 55 | & @swap ~ (v (ub (@Free tb))) 56 | & @swap ~ (w (vb (@Free ub))) 57 | & @swap ~ (x (@Used (@Free vb))) 58 | @rev = ((@Empty (@rev$C0 (@rev$C1 a))) a) 59 | @rev$C0 = (a b) 60 | & @Single ~ (a b) 61 | @rev$C1 = (a (b c)) 62 | & @Concat ~ (d (e c)) 63 | & @rev ~ (a e) 64 | & @rev ~ (b d) 65 | @sort = (a b) 66 | & @to_arr ~ (c (0 b)) 67 | & @to_map ~ (a c) 68 | @sum = ((0 ((a a) (@sum$C0 b))) b) 69 | @sum$C0 = (a (b c)) 70 | & @sum ~ (a $([+] $(d c))) 71 | & @sum ~ (b d) 72 | @swap = (?((@swap$C0 @swap$C2) a) a) 73 | @swap$C0 = (a (b c)) 74 | & @Node ~ (a (b c)) 75 | @swap$C1 = (a (b c)) 76 | & @Node ~ (b (a c)) 77 | @swap$C2 = (* @swap$C1) 78 | @to_arr = ((@to_arr$C0 (@to_arr$C1 (@to_arr$C2 (a b)))) (a b)) 79 | @to_arr$C0 = (* @Empty) 80 | @to_arr$C1 = (a b) 81 | & @Single ~ (a b) 82 | @to_arr$C2 = (a (b (#2{$([*2] c) $([*2] $([+1] d))} e))) 83 | & @Concat ~ (f (g e)) 84 | & @to_arr ~ (b (d g)) 85 | & @to_arr ~ (a (c f)) 86 | @to_map = ((@Free (@to_map$C0 (@to_map$C1 a))) a) 87 | @to_map$C0 = (a b) 88 | & @radix ~ (a b) 89 | @to_map$C1 = (a (b c)) 90 | & @merge ~ (d (e c)) 91 | & @to_map ~ (b e) 92 | & @to_map ~ (a d) 93 | 94 | -------------------------------------------------------------------------------- /tests/programs/stress_tests/boom.hvm: -------------------------------------------------------------------------------- 1 | // Exponentiation of Church encodings. 2 | 3 | @c2 = ({{(c b) (b a)} (a R)} (c R)) 4 | @c4 = ({{{(d c) (c b)} (b a)} (a R)} (d R)) 5 | @c6 = ({{{{{(f e) (e d)} (d c)} (c b)} (b a)} (a R)} (f R)) 6 | @c8 = ({{{{{{{(h g) (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (h R)) 7 | @c11 = ({{{{{{{{{(j i) (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (j R)) 8 | @c12 = ({{{{{{{{{{{(l k) (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (l R)) 9 | @c14 = ({{{{{{{{{{{{{(n m) (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (n R)) 10 | @c16 = ({{{{{{{{{{{{{{{(p o) (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (p R)) 11 | @c21 = ({{{{{{{{{{{{{{{{{{{(t s) (s r)} (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (t R)) 12 | @c22 = ({{{{{{{{{{{{{{{{{{{{{(v u) (u t)} (t s)} (s r)} (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (v R)) 13 | @c24 = ({{{{{{{{{{{{{{{{{{{{{{{(x w) (w v)} (v u)} (u t)} (t s)} (s r)} (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (x R)) 14 | 15 | @k3 = (#1(#1((c b) (b a)) (a R)) (c R)) 16 | @k4 = (#1(#1(#1((d c) (c b)) (b a)) (a R)) (d R)) 17 | @k6 = (#1(#1(#1(#1(#1((f e) (e d)) (d c)) (c b)) (b a)) (a R)) (f R)) 18 | @k8 = (#1(#1(#1(#1(#1(#1(#1((h g) (g f)) (f e)) (e d)) (d c)) (c b)) (b a)) (a R)) (h R)) 19 | @k20 = (#1(#1(#1(#1(#1(#1(#1(#1(#1((j i) (i h)) (h g)) (g f)) (f e)) (e d)) (d c)) (c b)) (b a)) (a R)) (j R)) 20 | @k22 = (#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1((l k) (k j)) (j i)) (i h)) (h g)) (g f)) (f e)) (e d)) (d c)) (c b)) (b a)) (a R)) (l R)) 21 | @k24 = (#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1((n m) (m l)) (l k)) (k j)) (j i)) (i h)) (h g)) (g f)) (f e)) (e d)) (d c)) (c b)) (b a)) (a R)) (n R)) 22 | @k26 = (#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1((p o) (o n)) (n m)) (m l)) (l k)) (k j)) (j i)) (i h)) (h g)) (g f)) (f e)) (e d)) (d c)) (c b)) (b a)) (a R)) (p R)) 23 | @k20 = (#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1((t s) (s r)) (r q)) (q p)) (p o)) (o n)) (n m)) (m l)) (l k)) (k j)) (j i)) (i h)) (h g)) (g f)) (f e)) (e d)) (d c)) (c b)) (b a)) (a R)) (t R)) 24 | @k22 = (#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1((v u) (u t)) (t s)) (s r)) (r q)) (q p)) (p o)) (o n)) (n m)) (m l)) (l k)) (k j)) (j i)) (i h)) (h g)) (g f)) (f e)) (e d)) (d c)) (c b)) (b a)) (a R)) (v R)) 25 | @k24 = (#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1((x w) (w v)) (v u)) (u t)) (t s)) (s r)) (r q)) (q p)) (p o)) (o n)) (n m)) (m l)) (l k)) (k j)) (j i)) (i h)) (h g)) (g f)) (f e)) (e d)) (d c)) (c b)) (b a)) (a R)) (x R)) 26 | 27 | @A = ({(a b) (b c)} (a c)) 28 | @B = (#1((a b) (b c)) (a c)) 29 | @F = (* (x x)) 30 | @N = ((@F (@T a)) a) 31 | @T = (a (* a)) 32 | 33 | @main 34 | = R 35 | & @c8 ~ (@k8 (@N (@T R))) 36 | 37 | //RWTS : 746698 38 | //- ANNI : 186732 39 | //- COMM : 280025 40 | //- ERAS : 279934 41 | //- DREF : 7 42 | //- OPER : 0 43 | //TIME : 0.014 s 44 | //RPS : 53.336 m 45 | -------------------------------------------------------------------------------- /host/src/host.rs: -------------------------------------------------------------------------------- 1 | //! The runtime's host, which acts as a translation layer between the AST and 2 | //! the runtime. 3 | #![cfg_attr(not(feature = "std"), no_std)] 4 | 5 | use hvm64_ast::{Book, Tree}; 6 | use hvm64_runtime::{Addr, Def, DynDef, InterpretedDef, LabSet, Port, Tag, Wire}; 7 | use hvm64_util::prelude::*; 8 | 9 | mod calc_labels; 10 | mod encode; 11 | mod readback; 12 | 13 | use calc_labels::calculate_label_sets; 14 | 15 | /// Stores a bidirectional mapping between names and runtime defs. 16 | #[derive(Default)] 17 | pub struct Host { 18 | /// the forward mapping, from a name to the runtime def 19 | pub defs: Map>, 20 | /// the backward mapping, from the address of a runtime def to the name 21 | pub back: Map, 22 | } 23 | 24 | impl Host { 25 | pub fn new(book: &Book) -> Host { 26 | let mut host = Host::default(); 27 | host.insert_book(book); 28 | host 29 | } 30 | 31 | /// Converts all of the nets from the book into runtime defs, and inserts them 32 | /// into the host. The book must not have refs that are not in the book or the 33 | /// host. 34 | pub fn insert_book(&mut self, book: &Book) { 35 | self.insert_book_with_default(book, &mut |x| panic!("Found reference {x:?}, which is not in the book!")) 36 | } 37 | 38 | /// Like `insert_book`, but allows specifying a function (`default_def`) that 39 | /// will be run when the name of a definition is not found in the book. 40 | /// The return value of the function will be inserted into the host. 41 | pub fn insert_book_with_default(&mut self, book: &Book, default_def: &mut dyn FnMut(&str) -> Box) { 42 | #[cfg(feature = "std")] 43 | { 44 | self.defs.reserve(book.len()); 45 | self.back.reserve(book.len()); 46 | } 47 | 48 | // Because there may be circular dependencies, inserting the definitions 49 | // must be done in two phases: 50 | 51 | // First, we insert empty defs into the host. Even though their instructions 52 | // are not yet set, the address of the def will not change, meaning that 53 | // `net_to_runtime_def` can safely use `Port::new_def` on them. 54 | for (name, labs) in calculate_label_sets(book, |name| match self.defs.get(name) { 55 | Some(x) => x.labs.clone(), 56 | None => { 57 | self.insert_def(name, default_def(name)); 58 | self.defs[name].labs.clone() 59 | } 60 | }) 61 | .into_iter() 62 | { 63 | let def = Box::new(Def::new(labs, InterpretedDef::default())); 64 | self.insert_def(name, def); 65 | } 66 | 67 | // Now that `defs` is fully populated, we can fill in the instructions of 68 | // each of the new defs. 69 | for (name, net) in book.iter() { 70 | let data = self.encode_def(net); 71 | self.get_mut::(name).data = data; 72 | } 73 | } 74 | 75 | /// Inserts a singular def into the mapping. 76 | pub fn insert_def(&mut self, name: &str, def: Box) { 77 | self.back.insert(Port::new_ref(&def).addr(), name.to_owned()); 78 | self.defs.insert(name.to_owned(), def); 79 | } 80 | 81 | /// Returns a mutable [`Def`] named `name`. 82 | pub fn get_mut(&mut self, name: &str) -> &mut Def { 83 | Def::downcast_mut(self.defs.get_mut(name).unwrap()).unwrap() 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /host/src/readback.rs: -------------------------------------------------------------------------------- 1 | use hvm64_util::prelude::*; 2 | 3 | use super::{Addr, Host, Port, Tag, Wire}; 4 | 5 | use core::ops::RangeFrom; 6 | 7 | use hvm64_ast::{Net, Tree}; 8 | use hvm64_num::{Num, NumTag}; 9 | use hvm64_util::{create_var, maybe_grow}; 10 | 11 | impl Host { 12 | /// Creates an ast tree from a wire in a runtime net. 13 | pub fn readback_tree(&self, wire: &Wire) -> Tree { 14 | ReadbackState { host: self, vars: Default::default(), var_id: 0 .. }.read_wire(wire.clone()) 15 | } 16 | 17 | /// Creates an ast net from a runtime net. 18 | /// 19 | /// Note that vicious circles and disconnected subnets will not be in the 20 | /// resulting ast net, as it is impossible to read these back from the runtime 21 | /// net representation. In the case of vicious circles, this may result in 22 | /// unbound variables. 23 | pub fn readback(&self, rt_net: &hvm64_runtime::Net) -> Net { 24 | let mut state = ReadbackState { host: self, vars: Default::default(), var_id: 0 .. }; 25 | let mut net = Net::default(); 26 | 27 | net.root = state.read_wire(rt_net.root.clone()); 28 | for (a, b) in rt_net.redexes.iter() { 29 | net.redexes.push((state.read_port(a.clone(), None), state.read_port(b.clone(), None))) 30 | } 31 | 32 | net 33 | } 34 | } 35 | 36 | /// See [`Host::readback`]. 37 | struct ReadbackState<'a> { 38 | host: &'a Host, 39 | vars: Map, 40 | var_id: RangeFrom, 41 | } 42 | 43 | impl<'a> ReadbackState<'a> { 44 | /// Reads a tree out from a given `wire`. 45 | fn read_wire(&mut self, wire: Wire) -> Tree { 46 | let port = wire.load_target(); 47 | self.read_port(port, Some(wire)) 48 | } 49 | 50 | /// Reads a tree out from a given `port`. If this is a var port, the 51 | /// `wire` this port was reached from must be supplied to key into the 52 | /// `vars` map. 53 | fn read_port(&mut self, port: Port, wire: Option) -> Tree { 54 | maybe_grow(move || match port.tag() { 55 | Tag::Var | Tag::Red => { 56 | // todo: resolve redirects 57 | let key = wire.unwrap().addr().min(port.addr()); 58 | Tree::Var(create_var(match self.vars.entry(key) { 59 | Entry::Occupied(e) => e.remove(), 60 | Entry::Vacant(e) => *e.insert(self.var_id.next().unwrap()), 61 | })) 62 | } 63 | Tag::Ref if port == Port::ERA => Tree::Era, 64 | Tag::Ref => Tree::Ref(self.host.back[&port.addr()].clone()), 65 | Tag::Num => Tree::Num(port.num()), 66 | Tag::Op => { 67 | let op = port.op(); 68 | let node = port.traverse_node(); 69 | let node = Tree::Op { rhs: Box::new(self.read_wire(node.p1)), out: Box::new(self.read_wire(node.p2)) }; 70 | if op == NumTag::Sym { 71 | node 72 | } else { 73 | Tree::Op { rhs: Box::new(Tree::Num(Num::new_sym(op))), out: Box::new(node) } 74 | } 75 | } 76 | Tag::Ctr => { 77 | let node = port.traverse_node(); 78 | Tree::Ctr { lab: node.lab, p1: Box::new(self.read_wire(node.p1)), p2: Box::new(self.read_wire(node.p2)) } 79 | } 80 | Tag::Switch => { 81 | let node = port.traverse_node(); 82 | let arms = self.read_wire(node.p1); 83 | let out = self.read_wire(node.p2); 84 | Tree::Switch { arms: Box::new(arms), out: Box::new(out) } 85 | } 86 | }) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /examples/sort/radix/radix_sort_lam.hvm: -------------------------------------------------------------------------------- 1 | @Concat = (a (b (* (* ((a (b c)) c))))) 2 | @Empty = (a (* (* a))) 3 | @Free = (a (* (* a))) 4 | @Node = (a (b (* (* ((a (b c)) c))))) 5 | @Single = (a (* ((a b) (* b)))) 6 | @Used = (* @Used$S0) 7 | @Used$S0 = (a (* a)) 8 | @gen = (?((a b) c) c) 9 | & (a b) ~ (@gen$S0 @gen$S1) 10 | @gen$S0 = (a b) 11 | & @Single ~ (a b) 12 | @gen$S1 = (#3{a b} ($([:<<1] #4{c $([|1] d)}) e)) 13 | & @Concat ~ (f (g e)) 14 | & @gen ~ (b (d g)) 15 | & @gen ~ (a (c f)) 16 | @main = a 17 | & @sum ~ (b a) 18 | & @sort ~ (c b) 19 | & @rev ~ (d c) 20 | & @gen ~ (20 (0 d)) 21 | @merge = ((@merge$S1 (@merge$S4 (@merge$S9 a))) a) 22 | @merge$S0 = (a (b c)) 23 | & @Node ~ (a (b c)) 24 | @merge$S1 = ((@Free (@Used (@merge$S0 a))) a) 25 | @merge$S2 = (* 0) 26 | @merge$S3 = (* @merge$S2) 27 | @merge$S4 = ((@Used (@Used (@merge$S3 a))) a) 28 | @merge$S5 = (a (b c)) 29 | & @Node ~ (a (b c)) 30 | @merge$S6 = (* 0) 31 | @merge$S7 = (* @merge$S6) 32 | @merge$S8 = (a (b (c (d e)))) 33 | & @Node ~ (f (g e)) 34 | & @merge ~ (d (b g)) 35 | & @merge ~ (c (a f)) 36 | @merge$S9 = (a (b ((@merge$S5 (@merge$S7 (@merge$S8 (a (b c))))) c))) 37 | @radix = (#1{$([&8388608] a) #1{$([&4194304] b) #1{$([&2097152] c) #1{$([&1048576] d) #1{$([&524288] e) #1{$([&262144] f) #1{$([&131072] g) #1{$([&65536] h) #1{$([&32768] i) #1{$([&16384] j) #1{$([&8192] k) #1{$([&4096] l) #1{$([&2048] m) #1{$([&1024] n) #1{$([&512] o) #1{$([&256] p) #1{$([&128] q) #1{$([&64] r) #1{$([&32] s) #1{$([&16] t) #1{$([&8] u) #1{$([&4] v) #1{$([&2] w) $([&1] x)}}}}}}}}}}}}}}}}}}}}}}} y) 38 | & @swap ~ (a (z (@Free y))) 39 | & @swap ~ (b (ab (@Free z))) 40 | & @swap ~ (c (bb (@Free ab))) 41 | & @swap ~ (d (cb (@Free bb))) 42 | & @swap ~ (e (db (@Free cb))) 43 | & @swap ~ (f (eb (@Free db))) 44 | & @swap ~ (g (fb (@Free eb))) 45 | & @swap ~ (h (gb (@Free fb))) 46 | & @swap ~ (i (hb (@Free gb))) 47 | & @swap ~ (j (ib (@Free hb))) 48 | & @swap ~ (k (jb (@Free ib))) 49 | & @swap ~ (l (kb (@Free jb))) 50 | & @swap ~ (m (lb (@Free kb))) 51 | & @swap ~ (n (mb (@Free lb))) 52 | & @swap ~ (o (nb (@Free mb))) 53 | & @swap ~ (p (ob (@Free nb))) 54 | & @swap ~ (q (pb (@Free ob))) 55 | & @swap ~ (r (qb (@Free pb))) 56 | & @swap ~ (s (rb (@Free qb))) 57 | & @swap ~ (t (sb (@Free rb))) 58 | & @swap ~ (u (tb (@Free sb))) 59 | & @swap ~ (v (ub (@Free tb))) 60 | & @swap ~ (w (vb (@Free ub))) 61 | & @swap ~ (x (@Used (@Free vb))) 62 | @rev = ((@Empty (@rev$S0 (@rev$S1 a))) a) 63 | @rev$S0 = (a b) 64 | & @Single ~ (a b) 65 | @rev$S1 = (a (b c)) 66 | & @Concat ~ (d (e c)) 67 | & @rev ~ (a e) 68 | & @rev ~ (b d) 69 | @sort = (a b) 70 | & @to_arr ~ (c (0 b)) 71 | & @to_map ~ (a c) 72 | @sum = ((0 ((a a) (@sum$S0 b))) b) 73 | @sum$S0 = (a (b c)) 74 | & @sum ~ (a $([+] $(d c))) 75 | & @sum ~ (b d) 76 | @swap = (?((a b) c) c) 77 | & (a b) ~ (@swap$S0 @swap$S2) 78 | @swap$S0 = (a (b c)) 79 | & @Node ~ (a (b c)) 80 | @swap$S1 = (a (b c)) 81 | & @Node ~ (b (a c)) 82 | @swap$S2 = (* @swap$S1) 83 | @to_arr = ((@to_arr$S0 (@to_arr$S1 (@to_arr$S2 a))) a) 84 | @to_arr$S0 = (* @Empty) 85 | @to_arr$S1 = (a b) 86 | & @Single ~ (a b) 87 | @to_arr$S2 = (a (b (#2{$([*2] $([+1] c)) $([*2] $([+0] d))} e))) 88 | & @Concat ~ (f (g e)) 89 | & @to_arr ~ (b (c g)) 90 | & @to_arr ~ (a (d f)) 91 | @to_map = ((@Free (@to_map$S0 (@to_map$S1 a))) a) 92 | @to_map$S0 = (a b) 93 | & @radix ~ (a b) 94 | @to_map$S1 = (a (b c)) 95 | & @merge ~ (d (e c)) 96 | & @to_map ~ (b e) 97 | & @to_map ~ (a d) 98 | 99 | -------------------------------------------------------------------------------- /tests/programs/alloc_big_tree.hvm: -------------------------------------------------------------------------------- 1 | // size = 1 << 16 2 | 3 | // Church Nats 4 | @c0 = (* (a a)) 5 | @c1 = ((a R) (a R)) 6 | @c2 = ({(b a) (a R)} (b R)) 7 | @c3 = ({{(c b) (b a)} (a R)} (c R)) 8 | @c4 = ({{{(d c) (c b)} (b a)} (a R)} (d R)) 9 | @c5 = ({{{{(e d) (d c)} (c b)} (b a)} (a R)} (e R)) 10 | @c6 = ({{{{{(f e) (e d)} (d c)} (c b)} (b a)} (a R)} (f R)) 11 | @c7 = ({{{{{{(g f) (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (g R)) 12 | @c8 = ({{{{{{{(h g) (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (h R)) 13 | @c9 = ({{{{{{{{(i h) (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (i R)) 14 | @c10 = ({{{{{{{{{(j i) (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (j R)) 15 | @c11 = ({{{{{{{{{{(k j) (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (k R)) 16 | @c12 = ({{{{{{{{{{{(l k) (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (l R)) 17 | @c13 = ({{{{{{{{{{{{(m l) (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (m R)) 18 | @c14 = ({{{{{{{{{{{{{(n m) (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (n R)) 19 | @c15 = ({{{{{{{{{{{{{{(o n) (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (o R)) 20 | @c16 = ({{{{{{{{{{{{{{{(p o) (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (p R)) 21 | @c17 = ({{{{{{{{{{{{{{{{(q p) (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (q R)) 22 | @c18 = ({{{{{{{{{{{{{{{{{(r q) (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (r R)) 23 | @c19 = ({{{{{{{{{{{{{{{{{{(s r) (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (s R)) 24 | @c20 = ({{{{{{{{{{{{{{{{{{{(t s) (s r)} (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (t R)) 25 | @c21 = ({{{{{{{{{{{{{{{{{{{{(u t) (t s)} (s r)} (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (u R)) 26 | @c22 = ({{{{{{{{{{{{{{{{{{{{{(v u) (u t)} (t s)} (s r)} (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (v R)) 27 | @c23 = ({{{{{{{{{{{{{{{{{{{{{{(w v) (v u)} (u t)} (t s)} (s r)} (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (w R)) 28 | @c24 = ({{{{{{{{{{{{{{{{{{{{{{{(x w) (w v)} (v u)} (u t)} (t s)} (s r)} (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (x R)) 29 | @c25 = ({{{{{{{{{{{{{{{{{{{{{{{{(y x) (x w)} (w v)} (v u)} (u t)} (t s)} (s r)} (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (y R)) 30 | @c26 = ({{{{{{{{{{{{{{{{{{{{{{{{{(z y) (y x)} (x w)} (w v)} (v u)} (u t)} (t s)} (s r)} (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (z R)) 31 | 32 | @g_s = (#1(r0 r1) ((r0 (r1 r)) r)) 33 | @g_z = (x x) 34 | 35 | @main 36 | = R 37 | & @c12 ~ (@g_s (@g_z R)) 38 | -------------------------------------------------------------------------------- /examples/sort/radix/radix_sort_lam.bend: -------------------------------------------------------------------------------- 1 | // data Arr = Empty | (Single x) | (Concat x0 x1) 2 | Empty = λempty λsingle λconcat empty 3 | Single = λx λempty λsingle λconcat (single x) 4 | Concat = λx0 λx1 λempty λsingle λconcat (concat x0 x1) 5 | 6 | // data Map = Free | Used | (Node x0 x1) 7 | Free = λfree λused λnode free 8 | Used = λfree λused λnode used 9 | Node = λx0 λx1 λfree λused λnode (node x0 x1) 10 | 11 | // gen : u32 -> Arr 12 | gen = λn match n { 13 | 0: λx (Single x) 14 | 1+: λx 15 | let x0 = (<< x 1) 16 | let x1 = (| x0 1) 17 | (Concat (gen n-1 x0) (gen n-1 x1)) 18 | } 19 | 20 | // rev : Arr -> Arr 21 | rev = λa 22 | let a_empty = Empty 23 | let a_single = λx (Single x) 24 | let a_concat = λx0 λx1 (Concat (rev x1) (rev x0)) 25 | (a a_empty a_single a_concat) 26 | 27 | // sum : Arr -> u32 28 | sum = λa 29 | let a_empty = 0 30 | let a_single = λx x 31 | let a_concat = λx0 λx1 (+ (sum x0) (sum x1)) 32 | (a a_empty a_single a_concat) 33 | 34 | // sort : Arr -> Arr 35 | sort = λt (to_arr (to_map t) 0) 36 | 37 | // to_arr : Map -> u32 -> Arr 38 | to_arr = λa 39 | let a_free = λk Empty 40 | let a_used = λk (Single k) 41 | let a_node = λx0 λx1 λk 42 | let x0 = (to_arr x0 (+ (* k 2) 0)) 43 | let x1 = (to_arr x1 (+ (* k 2) 1)) 44 | (Concat x0 x1) 45 | (a a_free a_used a_node) 46 | 47 | // to_map : Arr -> Map 48 | to_map = λa 49 | let a_empty = Free 50 | let a_single = λx (radix x) 51 | let a_concat = λx0 λx1 (merge (to_map x0) (to_map x1)) 52 | (a a_empty a_single a_concat) 53 | 54 | // merge : Map -> Map -> Map 55 | merge = λa 56 | let a_free = λb 57 | let b_free = Free 58 | let b_used = Used 59 | let b_node = λb0 λb1 (Node b0 b1) 60 | (b b_free b_used b_node) 61 | let a_used = λb 62 | let b_free = Used 63 | let b_used = Used 64 | let b_node = λb0 λb1 0 65 | (b b_free b_used b_node) 66 | let a_node = λa0 λa1 λb 67 | let b_free = λa0 λa1 (Node a0 a1) 68 | let b_used = λa0 λa1 0 69 | let b_node = λb0 λb1 λa0 λa1 (Node (merge a0 b0) (merge a1 b1)) 70 | (b b_free b_used b_node a0 a1) 71 | (a a_free a_used a_node) 72 | 73 | // radix : u32 -> Map 74 | radix = λn 75 | let r = Used 76 | let r = (swap (& n 1) r Free) 77 | let r = (swap (& n 2) r Free) 78 | let r = (swap (& n 4) r Free) 79 | let r = (swap (& n 8) r Free) 80 | let r = (swap (& n 16) r Free) 81 | let r = (swap (& n 32) r Free) 82 | let r = (swap (& n 64) r Free) 83 | let r = (swap (& n 128) r Free) 84 | let r = (swap (& n 256) r Free) 85 | let r = (swap (& n 512) r Free) 86 | let r = (swap (& n 1024) r Free) 87 | let r = (swap (& n 2048) r Free) 88 | let r = (swap (& n 4096) r Free) 89 | let r = (swap (& n 8192) r Free) 90 | let r = (swap (& n 16384) r Free) 91 | let r = (swap (& n 32768) r Free) 92 | let r = (swap (& n 65536) r Free) 93 | let r = (swap (& n 131072) r Free) 94 | let r = (swap (& n 262144) r Free) 95 | let r = (swap (& n 524288) r Free) 96 | let r = (swap (& n 1048576) r Free) 97 | let r = (swap (& n 2097152) r Free) 98 | let r = (swap (& n 4194304) r Free) 99 | let r = (swap (& n 8388608) r Free) 100 | r 101 | 102 | // swap : u32 -> Map -> Map -> Map 103 | swap = λn match n { 104 | 0: λx0 λx1 (Node x0 x1) 105 | 1+: λx0 λx1 (Node x1 x0) 106 | } 107 | 108 | // main : u32 109 | main = (sum (sort (rev (gen 20 0)))) 110 | -------------------------------------------------------------------------------- /examples/sort/radix/radix_sort_lam.js: -------------------------------------------------------------------------------- 1 | const Empty = empty => single => concat => empty; 2 | const Single = x => empty => single => concat => single(x); 3 | const Concat = x0 => x1 => empty => single => concat => concat(x0)(x1); 4 | 5 | const Free = free => used => node => free; 6 | const Used = free => used => node => used; 7 | const Node = x0 => x1 => free => used => node => node(x0)(x1); 8 | 9 | const gen = n => x => { 10 | if (n === 0) { 11 | return Single(x); 12 | } else { 13 | const p = n - 1; 14 | const x0 = x << 1; 15 | const x1 = x0 | 1; 16 | return Concat(gen(p)(x0))(gen(p)(x1)); 17 | } 18 | }; 19 | 20 | const rev = a => { 21 | const a_empty = Empty; 22 | const a_single = x => Single(x); 23 | const a_concat = x0 => x1 => Concat(rev(x1))(rev(x0)); 24 | return a(a_empty)(a_single)(a_concat); 25 | }; 26 | 27 | const sum = a => { 28 | const a_empty = 0; 29 | const a_single = x => x; 30 | const a_concat = x0 => x1 => sum(x0) + sum(x1); 31 | return a(a_empty)(a_single)(a_concat); 32 | }; 33 | 34 | const sort = t => to_arr(to_map(t))(0); 35 | 36 | const to_arr = a => k => { 37 | const a_free = Empty; 38 | const a_used = Single(k); 39 | const a_node = x0 => x1 => { 40 | const x0Arr = to_arr(x0)(k * 2); 41 | const x1Arr = to_arr(x1)(k * 2 + 1); 42 | return Concat(x0Arr)(x1Arr); 43 | }; 44 | return a(a_free)(a_used)(a_node); 45 | }; 46 | 47 | const to_map = a => { 48 | const a_empty = Free; 49 | const a_single = x => radix(x); 50 | const a_concat = x0 => x1 => merge(to_map(x0))(to_map(x1)); 51 | return a(a_empty)(a_single)(a_concat); 52 | }; 53 | 54 | const merge = a => { 55 | const a_free = b => { 56 | const b_free = Free; 57 | const b_used = Used; 58 | const b_node = b0 => b1 => Node(b0)(b1); 59 | return b(b_free)(b_used)(b_node); 60 | }; 61 | const a_used = b => { 62 | const b_free = Used; 63 | const b_used = Used; 64 | const b_node = b0 => b1 => 0; 65 | return b(b_free)(b_used)(b_node); 66 | }; 67 | const a_node = a0 => a1 => b => { 68 | const b_free = a0 => a1 => Node(a0)(a1); 69 | const b_used = a0 => a1 => 0; 70 | const b_node = b0 => b1 => a0 => a1 => Node(merge(a0)(b0))(merge(a1)(b1)); 71 | return b(b_free)(b_used)(b_node)(a0)(a1); 72 | }; 73 | return a(a_free)(a_used)(a_node); 74 | }; 75 | 76 | const radix = n => { 77 | let r = Used; 78 | r = swap(n & 1)(r)(Free); 79 | r = swap(n & 2)(r)(Free); 80 | r = swap(n & 4)(r)(Free); 81 | r = swap(n & 8)(r)(Free); 82 | r = swap(n & 16)(r)(Free); 83 | r = swap(n & 32)(r)(Free); 84 | r = swap(n & 64)(r)(Free); 85 | r = swap(n & 128)(r)(Free); 86 | r = swap(n & 256)(r)(Free); 87 | r = swap(n & 512)(r)(Free); 88 | r = swap(n & 1024)(r)(Free); 89 | r = swap(n & 2048)(r)(Free); 90 | r = swap(n & 4096)(r)(Free); 91 | r = swap(n & 8192)(r)(Free); 92 | r = swap(n & 16384)(r)(Free); 93 | r = swap(n & 32768)(r)(Free); 94 | r = swap(n & 65536)(r)(Free); 95 | r = swap(n & 131072)(r)(Free); 96 | r = swap(n & 262144)(r)(Free); 97 | r = swap(n & 524288)(r)(Free); 98 | r = swap(n & 1048576)(r)(Free); 99 | r = swap(n & 2097152)(r)(Free); 100 | r = swap(n & 4194304)(r)(Free); 101 | r = swap(n & 8388608)(r)(Free); 102 | return r; 103 | }; 104 | 105 | const swap = n => x0 => x1 => { 106 | if (n === 0) { 107 | return Node(x0)(x1); 108 | } else { 109 | return Node(x1)(x0); 110 | } 111 | }; 112 | 113 | console.log(sum(sort(rev(gen(22)(0))))); 114 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "fenix": { 4 | "inputs": { 5 | "nixpkgs": "nixpkgs", 6 | "rust-analyzer-src": "rust-analyzer-src" 7 | }, 8 | "locked": { 9 | "lastModified": 1709274179, 10 | "narHash": "sha256-O6EC6QELBLHzhdzBOJj0chx8AOcd4nDRECIagfT5Nd0=", 11 | "owner": "nix-community", 12 | "repo": "fenix", 13 | "rev": "4be608f4f81d351aacca01b21ffd91028c23cc22", 14 | "type": "github" 15 | }, 16 | "original": { 17 | "owner": "nix-community", 18 | "ref": "monthly", 19 | "repo": "fenix", 20 | "type": "github" 21 | } 22 | }, 23 | "flake-utils": { 24 | "inputs": { 25 | "systems": "systems" 26 | }, 27 | "locked": { 28 | "lastModified": 1710146030, 29 | "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", 30 | "owner": "numtide", 31 | "repo": "flake-utils", 32 | "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", 33 | "type": "github" 34 | }, 35 | "original": { 36 | "owner": "numtide", 37 | "repo": "flake-utils", 38 | "type": "github" 39 | } 40 | }, 41 | "nixpkgs": { 42 | "locked": { 43 | "lastModified": 1709150264, 44 | "narHash": "sha256-HofykKuisObPUfj0E9CJVfaMhawXkYx3G8UIFR/XQ38=", 45 | "owner": "nixos", 46 | "repo": "nixpkgs", 47 | "rev": "9099616b93301d5cf84274b184a3a5ec69e94e08", 48 | "type": "github" 49 | }, 50 | "original": { 51 | "owner": "nixos", 52 | "ref": "nixos-unstable", 53 | "repo": "nixpkgs", 54 | "type": "github" 55 | } 56 | }, 57 | "nixpkgs_2": { 58 | "locked": { 59 | "lastModified": 1711401922, 60 | "narHash": "sha256-QoQqXoj8ClGo0sqD/qWKFWezgEwUL0SUh37/vY2jNhc=", 61 | "owner": "NixOS", 62 | "repo": "nixpkgs", 63 | "rev": "07262b18b97000d16a4bdb003418bd2fb067a932", 64 | "type": "github" 65 | }, 66 | "original": { 67 | "owner": "NixOS", 68 | "ref": "nixpkgs-unstable", 69 | "repo": "nixpkgs", 70 | "type": "github" 71 | } 72 | }, 73 | "root": { 74 | "inputs": { 75 | "fenix": "fenix", 76 | "flake-utils": "flake-utils", 77 | "nixpkgs": "nixpkgs_2" 78 | } 79 | }, 80 | "rust-analyzer-src": { 81 | "flake": false, 82 | "locked": { 83 | "lastModified": 1709219524, 84 | "narHash": "sha256-8HHRXm4kYQLdUohNDUuCC3Rge7fXrtkjBUf0GERxrkM=", 85 | "owner": "rust-lang", 86 | "repo": "rust-analyzer", 87 | "rev": "9efa23c4dacee88b93540632eb3d88c5dfebfe17", 88 | "type": "github" 89 | }, 90 | "original": { 91 | "owner": "rust-lang", 92 | "ref": "nightly", 93 | "repo": "rust-analyzer", 94 | "type": "github" 95 | } 96 | }, 97 | "systems": { 98 | "locked": { 99 | "lastModified": 1681028828, 100 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 101 | "owner": "nix-systems", 102 | "repo": "default", 103 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 104 | "type": "github" 105 | }, 106 | "original": { 107 | "owner": "nix-systems", 108 | "repo": "default", 109 | "type": "github" 110 | } 111 | } 112 | }, 113 | "root": "root", 114 | "version": 7 115 | } 116 | -------------------------------------------------------------------------------- /runtime/src/net.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | use hvm64_util::new_uninit_slice; 4 | use mem::MaybeUninit; 5 | 6 | /// An interaction combinator net. 7 | pub struct Net<'a> { 8 | pub(super) linker: Linker<'a>, 9 | pub tid: usize, // thread id 10 | pub tids: usize, // thread count 11 | pub trgs: Box<[MaybeUninit]>, 12 | pub root: Wire, 13 | } 14 | 15 | deref_to!({<'a, >} Net<'a> => self.linker: Linker<'a>); 16 | 17 | impl<'h> Net<'h> { 18 | /// Creates an empty net with a given heap. 19 | pub fn new(heap: &'h Heap) -> Self { 20 | let mut net = Net::new_with_root(heap, Wire(ptr::null())); 21 | net.root = Wire::new(net.alloc()); 22 | net 23 | } 24 | 25 | pub(super) fn new_with_root(heap: &'h Heap, root: Wire) -> Self { 26 | Net { linker: Linker::new(heap), tid: 0, tids: 1, trgs: new_uninit_slice(1 << 16), root } 27 | } 28 | 29 | /// Boots a net from a Def. 30 | pub fn boot(&mut self, def: &Def) { 31 | self.call(Port::new_ref(def), self.root.as_var()); 32 | } 33 | } 34 | 35 | impl<'a> Net<'a> { 36 | /// Reduces at most `limit` redexes. 37 | /// 38 | /// If normalized, returns `Some(num_redexes)`. 39 | /// If stopped because the limit was reached, returns `None`. 40 | #[inline(always)] 41 | pub fn reduce(&mut self, limit: usize) -> Option { 42 | let mut count = 0; 43 | 44 | while let Some((a, b)) = self.redexes.pop() { 45 | self.interact(a, b); 46 | count += 1; 47 | if count >= limit { 48 | return None; 49 | } 50 | } 51 | Some(count) 52 | } 53 | 54 | /// Reduces a net to normal form. 55 | pub fn normal(&mut self) { 56 | self.expand(); 57 | while !self.redexes.is_empty() { 58 | self.reduce(usize::MAX); 59 | } 60 | } 61 | } 62 | 63 | impl<'h> Net<'h> { 64 | /// Expands [`Tag::Ref`] nodes in the tree connected to `root`. 65 | pub fn expand(&mut self) { 66 | let (new_root, out_port) = self.create_wire(); 67 | let old_root = mem::replace(&mut self.root, new_root); 68 | self.link_wire_port(old_root, ExpandDef::new(out_port)); 69 | } 70 | } 71 | 72 | struct ExpandDef { 73 | out: Port, 74 | } 75 | 76 | impl ExpandDef { 77 | fn new(out: Port) -> Port { 78 | Port::new_ref(Box::leak(Box::new(Def::new(LabSet::ALL, ExpandDef { out })))) 79 | } 80 | } 81 | 82 | impl AsDef for ExpandDef { 83 | unsafe fn call(def: *const Def, net: &mut Net, port: Port) { 84 | if port.tag() == Tag::Ref && port != Port::ERA { 85 | let other: *const Def = port.addr().def() as *const _; 86 | if let Some(other) = Def::downcast_ptr::(other) { 87 | let def = *Box::from_raw(def as *mut Def); 88 | let other = *Box::from_raw(other as *mut Def); 89 | return net.link_port_port(def.data.out, other.data.out); 90 | } else { 91 | return net.call(port, Port::new_ref(Def::upcast(unsafe { &*def }))); 92 | } 93 | } 94 | let def = *Box::from_raw(def as *mut Def); 95 | match port.tag() { 96 | Tag::Red => { 97 | unreachable!() 98 | } 99 | Tag::Ref | Tag::Num | Tag::Var => net.link_port_port(def.data.out, port), 100 | tag @ (Tag::Op | Tag::Switch | Tag::Ctr) => { 101 | let old = port.consume_node(); 102 | let new = net.create_node(tag, old.lab); 103 | net.link_port_port(def.data.out, new.p0); 104 | net.link_wire_port(old.p1, ExpandDef::new(new.p1)); 105 | net.link_wire_port(old.p2, ExpandDef::new(new.p2)); 106 | } 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /tests/programs/dec_bits.hvm: -------------------------------------------------------------------------------- 1 | // size = 1 << 16 2 | 3 | // Decreases a binary counter until it is 0 4 | 5 | // Church Nats 6 | @c0 = (* (a a)) 7 | @c1 = ((a R) (a R)) 8 | @c2 = ({(b a) (a R)} (b R)) 9 | @c3 = ({{(c b) (b a)} (a R)} (c R)) 10 | @c4 = ({{{(d c) (c b)} (b a)} (a R)} (d R)) 11 | @c5 = ({{{{(e d) (d c)} (c b)} (b a)} (a R)} (e R)) 12 | @c6 = ({{{{{(f e) (e d)} (d c)} (c b)} (b a)} (a R)} (f R)) 13 | @c7 = ({{{{{{(g f) (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (g R)) 14 | @c8 = ({{{{{{{(h g) (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (h R)) 15 | @c9 = ({{{{{{{{(i h) (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (i R)) 16 | @c10 = ({{{{{{{{{(j i) (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (j R)) 17 | @c11 = ({{{{{{{{{{(k j) (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (k R)) 18 | @c12 = ({{{{{{{{{{{(l k) (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (l R)) 19 | @c13 = ({{{{{{{{{{{{(m l) (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (m R)) 20 | @c14 = ({{{{{{{{{{{{{(n m) (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (n R)) 21 | @c15 = ({{{{{{{{{{{{{{(o n) (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (o R)) 22 | @c16 = ({{{{{{{{{{{{{{{(p o) (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (p R)) 23 | @c17 = ({{{{{{{{{{{{{{{{(q p) (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (q R)) 24 | @c18 = ({{{{{{{{{{{{{{{{{(r q) (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (r R)) 25 | @c19 = ({{{{{{{{{{{{{{{{{{(s r) (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (s R)) 26 | @c20 = ({{{{{{{{{{{{{{{{{{{(t s) (s r)} (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (t R)) 27 | @c21 = ({{{{{{{{{{{{{{{{{{{{(u t) (t s)} (s r)} (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (u R)) 28 | @c22 = ({{{{{{{{{{{{{{{{{{{{{(v u) (u t)} (t s)} (s r)} (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (v R)) 29 | @c23 = ({{{{{{{{{{{{{{{{{{{{{{(w v) (v u)} (u t)} (t s)} (s r)} (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (w R)) 30 | @c24 = ({{{{{{{{{{{{{{{{{{{{{{{(x w) (w v)} (v u)} (u t)} (t s)} (s r)} (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (x R)) 31 | @c25 = ({{{{{{{{{{{{{{{{{{{{{{{{(y x) (x w)} (w v)} (v u)} (u t)} (t s)} (s r)} (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (y R)) 32 | @c26 = ({{{{{{{{{{{{{{{{{{{{{{{{{(z y) (y x)} (x w)} (w v)} (v u)} (u t)} (t s)} (s r)} (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (z R)) 33 | 34 | @O = (xs ((xs r) (* (* r)))) 35 | @I = (xs (* ((xs r) (* r)))) 36 | @E = (* (* (e e))) 37 | 38 | @decO = (p idecp) & @I ~ (decp idecp) & @dec ~ (p decp) 39 | @decI = (p lowp) & @low ~ (p lowp) 40 | @dec = ((@decO (@decI (@E R))) R) 41 | 42 | @lowO = (p oop) & @O ~ (p op) & @O ~ (op oop) 43 | @lowI = (p oip) & @I ~ (p ip) & @O ~ (ip oip) 44 | @low = ((@lowO (@lowI (@E R))) R) 45 | 46 | @runO = (p R) & @run ~ (decop R) & @dec ~ (op decop) & @O ~ (p op) 47 | @runI = (p R) & @run ~ (decip R) & @dec ~ (ip decip) & @I ~ (p ip) 48 | @run = ((@runO (@runI (@E R))) R) 49 | 50 | @main 51 | = R 52 | & @c12 ~ (@I (@E nie)) 53 | & @run ~ (nie R) 54 | -------------------------------------------------------------------------------- /examples/sort/radix/radix_sort_lam.hs: -------------------------------------------------------------------------------- 1 | import Prelude hiding (concat) 2 | import Data.Word 3 | import Data.Bits (shiftL, (.&.), (.|.)) 4 | 5 | newtype Arr = Arr { matchArr :: forall r. r -> (Word32 -> r) -> (Arr -> Arr -> r) -> r } 6 | newtype Map = Map { matchApp :: forall r. r -> r -> (Map -> Map -> r) -> r } 7 | 8 | empty :: Arr 9 | empty = Arr $ \empty single concat -> empty 10 | 11 | single :: Word32 -> Arr 12 | single x = Arr $ \empty single concat -> single x 13 | 14 | concat :: Arr -> Arr -> Arr 15 | concat x0 x1 = Arr $ \empty single concat -> concat x0 x1 16 | 17 | free :: Map 18 | free = Map $ \free used node -> free 19 | 20 | used :: Map 21 | used = Map $ \free used node -> used 22 | 23 | node :: Map -> Map -> Map 24 | node x0 x1 = Map $ \free used node -> node x0 x1 25 | 26 | gen :: Word32 -> Word32 -> Arr 27 | gen n x = case n of 28 | 0 -> single x 29 | p -> let x0 = x `shiftL` 1 30 | x1 = x0 .|. 1 31 | in concat (gen (p - 1) x0) (gen (p - 1) x1) 32 | 33 | rev :: Arr -> Arr 34 | rev a = let a_empty = empty 35 | a_single = single 36 | a_concat = \x0 x1 -> concat (rev x1) (rev x0) 37 | in matchArr a a_empty a_single a_concat 38 | 39 | sumArr :: Arr -> Word32 40 | sumArr a = let a_empty = 0 41 | a_single = id 42 | a_concat = \x0 x1 -> sumArr x0 + sumArr x1 43 | in matchArr a a_empty a_single a_concat 44 | 45 | sort :: Arr -> Arr 46 | sort t = to_arr (to_map t) 0 47 | 48 | to_arr :: Map -> Word32 -> Arr 49 | to_arr a = let a_free = \k -> empty 50 | a_used = \k -> single k 51 | a_node = \x0 x1 k -> concat (to_arr x0 (k * 2)) (to_arr x1 (k * 2 + 1)) 52 | in matchApp a a_free a_used a_node 53 | 54 | to_map :: Arr -> Map 55 | to_map a = let a_empty = free 56 | a_single = \x -> radix x 57 | a_concat = \x0 x1 -> merge (to_map x0) (to_map x1) 58 | in matchArr a a_empty a_single a_concat 59 | 60 | merge :: Map -> Map -> Map 61 | merge a = 62 | let a_free = \b -> 63 | let b_free = free 64 | b_used = used 65 | b_node = \b0 b1 -> node b0 b1 66 | in matchApp b b_free b_used b_node 67 | a_used = \b -> 68 | let b_free = used 69 | b_used = used 70 | b_node = \b0 b1 -> undefined 71 | in matchApp b b_free b_used b_node 72 | a_node = \a0 a1 b -> 73 | let b_free = \a0 a1 -> node a0 a1 74 | b_used = \a0 a1 -> undefined 75 | b_node = \b0 b1 a0 a1 -> node (merge a0 b0) (merge a1 b1) 76 | in matchApp b b_free b_used b_node a0 a1 77 | in matchApp a a_free a_used a_node 78 | 79 | radix :: Word32 -> Map 80 | radix n = 81 | let r0 = used 82 | r1 = swap (n .&. 1) r0 free 83 | r2 = swap (n .&. 2) r1 free 84 | r3 = swap (n .&. 4) r2 free 85 | r4 = swap (n .&. 8) r3 free 86 | r5 = swap (n .&. 16) r4 free 87 | r6 = swap (n .&. 32) r5 free 88 | r7 = swap (n .&. 64) r6 free 89 | r8 = swap (n .&. 128) r7 free 90 | r9 = swap (n .&. 256) r8 free 91 | r10 = swap (n .&. 512) r9 free 92 | r11 = swap (n .&. 1024) r10 free 93 | r12 = swap (n .&. 2048) r11 free 94 | r13 = swap (n .&. 4096) r12 free 95 | r14 = swap (n .&. 8192) r13 free 96 | r15 = swap (n .&. 16384) r14 free 97 | r16 = swap (n .&. 32768) r15 free 98 | r17 = swap (n .&. 65536) r16 free 99 | r18 = swap (n .&. 131072) r17 free 100 | r19 = swap (n .&. 262144) r18 free 101 | r20 = swap (n .&. 524288) r19 free 102 | r21 = swap (n .&. 1048576) r20 free 103 | r22 = swap (n .&. 2097152) r21 free 104 | r23 = swap (n .&. 4194304) r22 free 105 | r24 = swap (n .&. 8388608) r23 free 106 | in r24 107 | 108 | swap :: Word32 -> Map -> Map -> Map 109 | swap n x0 x1 = case n of 110 | 0 -> node x0 x1 111 | _ -> node x1 x0 112 | 113 | main :: IO () 114 | main = print $ sumArr (sort (rev (gen 22 0))) 115 | -------------------------------------------------------------------------------- /runtime/src/allocator.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | // Really, rust? 4 | use alloc::alloc::alloc; 5 | 6 | /// The memory behind a two-word allocation. 7 | /// 8 | /// This must be aligned to 16 bytes so that the left word's address always ends 9 | /// with `0b0000` and the right word's address always ends with `0b1000`. 10 | #[repr(C)] 11 | #[repr(align(16))] 12 | #[derive(Default)] 13 | pub(super) struct Node(pub AtomicU64, pub AtomicU64); 14 | 15 | /// The memory buffer backing a [`Net`]. 16 | #[repr(align(16))] 17 | pub struct Heap(pub(super) [Node]); 18 | 19 | impl Heap { 20 | /// Allocates a new heap with the given size in bytes, defaulting to the 21 | /// largest power-of-two allocation the system will allow. 22 | pub fn new(bytes: Option) -> Option> { 23 | if let Some(bytes) = bytes { 24 | return Self::new_exact(bytes / 8); 25 | } 26 | let mut size = if cfg!(target_pointer_width = "64") { 27 | 1 << 40 // 1 TiB 28 | } else { 29 | 1 << 30 // 1 GiB 30 | } / 8; 31 | while size != 0 { 32 | if let Some(heap) = Self::new_exact(size) { 33 | return Some(heap); 34 | } 35 | size /= 2; 36 | } 37 | None 38 | } 39 | /// Allocates a new heap with exactly the given size in words. 40 | #[inline] 41 | pub fn new_exact(words: usize) -> Option> { 42 | let nodes = words / 2; 43 | if nodes == 0 { 44 | return None; 45 | } 46 | unsafe { 47 | let ptr = alloc(Layout::array::(nodes).unwrap()) as *mut Node; 48 | if ptr.is_null() { 49 | return None; 50 | } 51 | Some(Box::from_raw(ptr::slice_from_raw_parts_mut(ptr, nodes) as *mut _)) 52 | } 53 | } 54 | } 55 | 56 | /// Manages allocating and freeing nodes within the net. 57 | pub struct Allocator<'h> { 58 | pub(super) tracer: Tracer, 59 | pub(super) heap: &'h Heap, 60 | pub(super) next: usize, 61 | pub(super) head: Addr, 62 | } 63 | 64 | deref_to!({<'h>} Allocator<'h> => self.tracer: Tracer); 65 | 66 | impl<'h> Allocator<'h> { 67 | pub fn new(heap: &'h Heap) -> Self { 68 | Allocator { tracer: Tracer::default(), heap, next: 0, head: Addr::NULL } 69 | } 70 | 71 | /// Frees one word of a two-word allocation. 72 | #[inline(always)] 73 | pub fn half_free(&mut self, addr: Addr) { 74 | trace!(self.tracer, addr); 75 | const FREE: u64 = Port::FREE.0; 76 | addr.val().store(FREE, Relaxed); 77 | if addr.other_half().val().load(Relaxed) == FREE { 78 | trace!(self.tracer, "other free"); 79 | let addr = addr.left_half(); 80 | if addr.val().compare_exchange(FREE, self.head.0 as u64, Relaxed, Relaxed).is_ok() { 81 | let old_head = &self.head; 82 | let new_head = addr; 83 | trace!(self.tracer, "appended", old_head, new_head); 84 | self.head = new_head; 85 | } else { 86 | trace!(self.tracer, "too slow"); 87 | }; 88 | } 89 | } 90 | 91 | /// Allocates a two-word node. 92 | #[inline(never)] 93 | pub fn alloc(&mut self) -> Addr { 94 | trace!(self.tracer, self.head); 95 | let addr = if self.head != Addr::NULL { 96 | let addr = self.head; 97 | let next = Addr(self.head.val().load(Relaxed) as usize); 98 | trace!(self.tracer, next); 99 | self.head = next; 100 | addr 101 | } else { 102 | let index = self.next; 103 | self.next += 1; 104 | Addr(&self.heap.0.get(index).expect("OOM").0 as *const _ as _) 105 | }; 106 | trace!(self.tracer, addr, self.head); 107 | addr.val().store(Port::LOCK.0, Relaxed); 108 | addr.other_half().val().store(Port::LOCK.0, Relaxed); 109 | addr 110 | } 111 | 112 | #[inline(always)] 113 | pub(crate) fn free_wire(&mut self, wire: Wire) { 114 | self.half_free(wire.addr()); 115 | } 116 | 117 | /// If `trg` is a wire, frees the backing memory. 118 | #[inline(always)] 119 | pub(crate) fn free_trg(&mut self, trg: Trg) { 120 | if trg.is_wire() { 121 | self.free_wire(trg.as_wire()); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /tests/programs/dec_bits_tree.hvm: -------------------------------------------------------------------------------- 1 | // size = 1 << 13 2 | 3 | // Decreases a tree of binary counters until they're all 0 (parallel) 4 | 5 | // Church Nats 6 | @c0 = (* (a a)) 7 | @c1 = ((a R) (a R)) 8 | @c2 = ({(b a) (a R)} (b R)) 9 | @c3 = ({{(c b) (b a)} (a R)} (c R)) 10 | @c4 = ({{{(d c) (c b)} (b a)} (a R)} (d R)) 11 | @c5 = ({{{{(e d) (d c)} (c b)} (b a)} (a R)} (e R)) 12 | @c6 = ({{{{{(f e) (e d)} (d c)} (c b)} (b a)} (a R)} (f R)) 13 | @c7 = ({{{{{{(g f) (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (g R)) 14 | @c8 = ({{{{{{{(h g) (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (h R)) 15 | @c9 = ({{{{{{{{(i h) (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (i R)) 16 | @c10 = ({{{{{{{{{(j i) (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (j R)) 17 | @c11 = ({{{{{{{{{{(k j) (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (k R)) 18 | @c12 = ({{{{{{{{{{{(l k) (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (l R)) 19 | @c13 = ({{{{{{{{{{{{(m l) (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (m R)) 20 | @c14 = ({{{{{{{{{{{{{(n m) (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (n R)) 21 | @c15 = ({{{{{{{{{{{{{{(o n) (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (o R)) 22 | @c16 = ({{{{{{{{{{{{{{{(p o) (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (p R)) 23 | @c17 = ({{{{{{{{{{{{{{{{(q p) (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (q R)) 24 | @c18 = ({{{{{{{{{{{{{{{{{(r q) (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (r R)) 25 | @c19 = ({{{{{{{{{{{{{{{{{{(s r) (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (s R)) 26 | @c20 = ({{{{{{{{{{{{{{{{{{{(t s) (s r)} (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (t R)) 27 | @c21 = ({{{{{{{{{{{{{{{{{{{{(u t) (t s)} (s r)} (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (u R)) 28 | @c22 = ({{{{{{{{{{{{{{{{{{{{{(v u) (u t)} (t s)} (s r)} (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (v R)) 29 | @c23 = ({{{{{{{{{{{{{{{{{{{{{{(w v) (v u)} (u t)} (t s)} (s r)} (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (w R)) 30 | @c24 = ({{{{{{{{{{{{{{{{{{{{{{{(x w) (w v)} (v u)} (u t)} (t s)} (s r)} (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (x R)) 31 | @c25 = ({{{{{{{{{{{{{{{{{{{{{{{{(y x) (x w)} (w v)} (v u)} (u t)} (t s)} (s r)} (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (y R)) 32 | @c26 = ({{{{{{{{{{{{{{{{{{{{{{{{{(z y) (y x)} (x w)} (w v)} (v u)} (u t)} (t s)} (s r)} (r q)} (q p)} (p o)} (o n)} (n m)} (m l)} (l k)} (k j)} (j i)} (i h)} (h g)} (g f)} (f e)} (e d)} (d c)} (c b)} (b a)} (a R)} (z R)) 33 | 34 | @S = (a ((a b) (* b))) 35 | @Z = (* (a a)) 36 | 37 | @O = (xs ((xs r) (* (* r)))) 38 | @I = (xs (* ((xs r) (* r)))) 39 | @E = (* (* (e e))) 40 | 41 | @decO = (p idecp) & @I ~ (decp idecp) & @dec ~ (p decp) 42 | @decI = (p lowp) & @low ~ (p lowp) 43 | @dec = ((@decO (@decI (@E R))) R) 44 | 45 | @lowO = (p oop) & @O ~ (p op) & @O ~ (op oop) 46 | @lowI = (p oip) & @I ~ (p ip) & @O ~ (ip oip) 47 | @low = ((@lowO (@lowI (@E R))) R) 48 | 49 | @runO = (p R) & @run ~ (decop R) & @dec ~ (op decop) & @O ~ (p op) 50 | @runI = (p R) & @run ~ (decip R) & @dec ~ (ip decip) & @I ~ (p ip) 51 | @run = ((@runO (@runI (@E R))) R) 52 | 53 | @brnZ = R & @run ~ (val R) & @c10 ~ (@I (@E val)) 54 | @brnS = ({p0 p1} (r0 r1)) & @brn ~ (p0 r0) & @brn ~ (p1 r1) 55 | @brn = ((@brnS (@brnZ r)) r) 56 | 57 | @main 58 | = R 59 | & @c6 ~ (@S (@Z dep)) 60 | & @brn ~ (dep R) 61 | -------------------------------------------------------------------------------- /tests/transform.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "std")] 2 | 3 | //! Tests for transformation passes 4 | 5 | use hvm64_ast::{Book, Net}; 6 | use hvm64_transform::{eta_reduce::EtaReduce, inline::Inline, pre_reduce::PreReduce, prune::Prune, TransformError}; 7 | 8 | use core::str::FromStr; 9 | use insta::assert_snapshot; 10 | 11 | mod loaders; 12 | use loaders::*; 13 | 14 | #[test] 15 | /// Test that ensures that pre_reduce only reduces repeated refs once. 16 | pub fn test_fast_pre_reduce() { 17 | let mut book = parse_core(&load_file("heavy_pre_reduction.hvm")); 18 | let black_box = book.remove("black_box").unwrap(); 19 | let (mut book_1, mut book_2) = (book.clone(), book); 20 | 21 | let rwts_1 = book_1.pre_reduce(&|x| !["expensive", "main_fast"].contains(&x), None, u64::MAX).rewrites; 22 | let rwts_2 = 23 | book_2.pre_reduce(&|x| !["expensive_1", "expensive_2", "main_slow"].contains(&x), None, u64::MAX).rewrites; 24 | 25 | book_1.insert("black_box".to_owned(), black_box.clone()); 26 | book_2.insert("black_box".to_owned(), black_box); 27 | 28 | let rwts_1 = rwts_1 + normal_with(book_1, None, "main_fast").0; 29 | let rwts_2 = rwts_2 + normal_with(book_2, None, "main_slow").0; 30 | 31 | assert_snapshot!(format!("Fast:\n{rwts_1}Slow:\n{rwts_2}"), @r###" 32 | Fast: 33 | RWTS : 33_236 34 | - ANNI : 4_385 35 | - COMM : 11_725 36 | - ERAS : 1_598 37 | - DREF : 15_528 38 | - OPER : 0 39 | Slow: 40 | RWTS : 50_951 41 | - ANNI : 8_763 42 | - COMM : 23_450 43 | - ERAS : 3_196 44 | - DREF : 15_542 45 | - OPER : 0 46 | "###) 47 | } 48 | 49 | #[test] 50 | pub fn test_eta() { 51 | pub fn parse_and_reduce(net: &str) -> String { 52 | let mut net = Net::from_str(net).unwrap(); 53 | net.eta_reduce(); 54 | format!("{net}") 55 | } 56 | assert_snapshot!(parse_and_reduce("((x y) (x y))"), @"(x x)"); 57 | assert_snapshot!(parse_and_reduce("((a (b (c (d (e f))))) (a (b (c (d (e f))))))"), @"(a a)"); 58 | assert_snapshot!(parse_and_reduce("$((a b) (a b))"), @"$(a a)"); 59 | assert_snapshot!(parse_and_reduce("(a b) & ((a b) (c d)) ~ (c d) "), @r###" 60 | a 61 | & (a c) ~ c 62 | "###); 63 | assert_snapshot!(parse_and_reduce("((a b) {a b})"), @"((a b) {a b})"); 64 | assert_snapshot!(parse_and_reduce("((a (b c)) (b c))"), @"((a b) b)"); 65 | assert_snapshot!(parse_and_reduce("({(a b) (c d)} {(a b) (c d)})"), @"(a a)"); 66 | assert_snapshot!(parse_and_reduce("(* *)"), @"*"); 67 | assert_snapshot!(parse_and_reduce("({(0 0) (12345 12345)} {(* *) (a a)})"), @"({0 12345} {* (a a)})"); 68 | } 69 | 70 | #[test] 71 | pub fn test_inline() { 72 | pub fn parse_and_inline(net: &str) -> Result { 73 | let mut net = Book::from_str(net).unwrap(); 74 | net.inline().map(|_| format!("{net}")) 75 | } 76 | assert_snapshot!(parse_and_inline(" 77 | @era = * 78 | @num = 123 79 | @abab = (a (b (a b))) 80 | @ref = @abab 81 | @def = @ref 82 | @eff = @def 83 | @foo = @bar 84 | @bar = @baz 85 | @baz = @unbound 86 | @into = (@era (@num (@abab (@ref (@def (@eff (@into (@foo (@bar (@baz @unbound)))))))))) 87 | ").unwrap(), @r###" 88 | @abab = (a (b (a b))) 89 | 90 | @bar = @unbound 91 | 92 | @baz = @unbound 93 | 94 | @def = @abab 95 | 96 | @eff = @abab 97 | 98 | @era = * 99 | 100 | @foo = @unbound 101 | 102 | @into = (* (123 (@abab (@abab (@abab (@abab (@into (@unbound (@unbound (@unbound @unbound)))))))))) 103 | 104 | @num = 123 105 | 106 | @ref = @abab 107 | "###); 108 | 109 | for net in ["@a = @a", "@a = @b @b = @c @c = @d @d = @e @e = @f @f = @c"] { 110 | assert!(matches!(parse_and_inline(net), Err(TransformError::InfiniteRefCycle(_)))); 111 | } 112 | } 113 | 114 | #[test] 115 | pub fn test_prune() { 116 | pub fn parse_and_prune(net: &str) -> String { 117 | let mut net = Book::from_str(net).unwrap(); 118 | net.prune(&["main".to_owned()]); 119 | format!("{net}") 120 | } 121 | assert_snapshot!(parse_and_prune(" 122 | @self = (* @self) 123 | @main = (@main (@a @b)) 124 | @a = (@b (@c @d)) 125 | @b = (@c @c) 126 | @c = @d 127 | @d = @at 128 | @idk = (@e @f) 129 | "), @r###" 130 | @a = (@b (@c @d)) 131 | 132 | @b = (@c @c) 133 | 134 | @c = @d 135 | 136 | @d = @at 137 | 138 | @main = (@main (@a @b)) 139 | "###); 140 | } 141 | -------------------------------------------------------------------------------- /runtime/src/runtime.rs: -------------------------------------------------------------------------------- 1 | //! The HVM runtime. 2 | //! 3 | //! The runtime is divided into three major logical components: 4 | //! - the **allocator**, which manages the creation and destruction of nodes in 5 | //! the net 6 | //! - the **linker**, which links ports and wires in the interaction net, in a 7 | //! thread-safe way 8 | //! - the **interactions**, which define the interaction system used in HVM 9 | //! (i.e. the agents and their interaction rules) 10 | //! 11 | //! The memory layout is documented within the code, but at a high level: 12 | //! - references into the net are represented by [`Port`]s and [`Wire`]s, which 13 | //! are often tagged pointers into nodes managed by the allocator 14 | //! - nilary agents are *unboxed* -- they have no backing allocation -- and 15 | //! their data is stored inline in their principal `Port` 16 | //! - other agents are backed by allocated [`Node`]s, which store the targets of 17 | //! the *auxiliary ports* of the net (as managed by the linker); the target of 18 | //! the principal port is left implicit 19 | //! - active pairs are thus stored in a dedicated vector, `net.redexes` 20 | #![cfg_attr(feature = "trace", feature(const_type_name))] 21 | #![cfg_attr(not(feature = "std"), no_std)] 22 | 23 | use hvm64_util::prelude::*; 24 | 25 | use hvm64_util::{bi_enum, deref_to, pretty_num}; 26 | 27 | use self::trace::Tracer; 28 | use alloc::borrow::Cow; 29 | use core::{ 30 | alloc::Layout, 31 | any::{Any, TypeId}, 32 | hint::unreachable_unchecked, 33 | mem::size_of, 34 | ops::{Add, AddAssign, Deref, DerefMut}, 35 | }; 36 | use hvm64_num::{Num, NumTag}; 37 | 38 | use core::sync::atomic; 39 | 40 | fn spin_loop() {} // this could use `std::hint::spin_loop`, but in practice it hurts performance 41 | 42 | use atomic::{AtomicU64, Ordering::Relaxed}; 43 | 44 | use Tag::*; 45 | 46 | mod addr; 47 | mod allocator; 48 | mod def; 49 | mod instruction; 50 | mod interact; 51 | mod linker; 52 | mod net; 53 | mod node; 54 | mod parallel; 55 | mod port; 56 | pub mod trace; 57 | mod wire; 58 | 59 | pub use addr::*; 60 | pub use allocator::*; 61 | pub use def::*; 62 | pub use instruction::*; 63 | pub use linker::*; 64 | pub use net::*; 65 | pub use node::*; 66 | pub use port::*; 67 | pub use wire::*; 68 | 69 | pub type Lab = u16; 70 | 71 | /// Tracks the number of rewrites, categorized by type. 72 | #[derive(Clone, Copy, Debug, Default)] 73 | pub struct Rewrites { 74 | pub anni: T, 75 | pub comm: T, 76 | pub eras: T, 77 | pub dref: T, 78 | pub oper: T, 79 | } 80 | 81 | type AtomicRewrites = Rewrites; 82 | 83 | impl Rewrites { 84 | pub fn add_to(&self, target: &AtomicRewrites) { 85 | target.anni.fetch_add(self.anni, Relaxed); 86 | target.comm.fetch_add(self.comm, Relaxed); 87 | target.eras.fetch_add(self.eras, Relaxed); 88 | target.dref.fetch_add(self.dref, Relaxed); 89 | target.oper.fetch_add(self.oper, Relaxed); 90 | } 91 | 92 | pub fn total(&self) -> u64 { 93 | self.anni + self.comm + self.eras + self.dref + self.oper 94 | } 95 | } 96 | 97 | impl AtomicRewrites { 98 | pub fn add_to(&self, target: &mut Rewrites) { 99 | target.anni += self.anni.load(Relaxed); 100 | target.comm += self.comm.load(Relaxed); 101 | target.eras += self.eras.load(Relaxed); 102 | target.dref += self.dref.load(Relaxed); 103 | target.oper += self.oper.load(Relaxed); 104 | } 105 | } 106 | 107 | impl Add for Rewrites { 108 | type Output = Rewrites; 109 | 110 | fn add(self, rhs: Self) -> Self::Output { 111 | Rewrites { 112 | anni: self.anni + rhs.anni, 113 | comm: self.comm + rhs.comm, 114 | eras: self.eras + rhs.eras, 115 | dref: self.dref + rhs.dref, 116 | oper: self.oper + rhs.oper, 117 | } 118 | } 119 | } 120 | 121 | impl AddAssign for Rewrites { 122 | fn add_assign(&mut self, rhs: Self) { 123 | self.anni += rhs.anni; 124 | self.comm += rhs.comm; 125 | self.eras += rhs.eras; 126 | self.dref += rhs.dref; 127 | self.oper += rhs.oper; 128 | } 129 | } 130 | 131 | impl fmt::Display for Rewrites { 132 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 133 | writeln!(f, "RWTS : {:>15}", pretty_num(self.total()))?; 134 | writeln!(f, "- ANNI : {:>15}", pretty_num(self.anni))?; 135 | writeln!(f, "- COMM : {:>15}", pretty_num(self.comm))?; 136 | writeln!(f, "- ERAS : {:>15}", pretty_num(self.eras))?; 137 | writeln!(f, "- DREF : {:>15}", pretty_num(self.dref))?; 138 | writeln!(f, "- OPER : {:>15}", pretty_num(self.oper))?; 139 | Ok(()) 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/args.rs: -------------------------------------------------------------------------------- 1 | use clap::Args; 2 | use hvm64_transform::TransformPasses; 3 | use std::path::PathBuf; 4 | 5 | #[derive(Args, Clone, Debug)] 6 | pub struct RunArgs { 7 | /// Name of the definition that will get reduced. 8 | #[arg(short, default_value = "main")] 9 | pub entry_point: String, 10 | 11 | /// List of arguments to pass to the program. 12 | /// 13 | /// Arguments are passed using the lambda-calculus interpretation 14 | /// of interaction combinators. So, for example, if the arguments are 15 | /// "#1" "#2" "#3", then the expression that will get reduced is 16 | /// `r & @main ~ (#1 (#2 (#3 r)))`. 17 | pub args: Vec, 18 | } 19 | 20 | #[derive(Args, Clone, Debug)] 21 | pub struct RuntimeOpts { 22 | /// Show performance statistics. 23 | #[arg(short, long = "stats")] 24 | pub show_stats: bool, 25 | 26 | /// Single-core mode (no parallelism). 27 | #[arg(short = '1', long = "single")] 28 | pub single_core: bool, 29 | 30 | /// How much memory to allocate on startup. 31 | /// 32 | /// Supports abbreviations such as '4G' or '400M'. 33 | #[arg(short, long, value_parser = hvm64_util::parse_abbrev_number::)] 34 | pub memory: Option, 35 | 36 | /// Dynamic library hvm-64 files to include. 37 | /// 38 | /// hvm-64 files can be compiled as dylibs with the `--dylib` option. 39 | #[arg(short, long, value_delimiter = ' ', action = clap::ArgAction::Append)] 40 | pub include: Vec, 41 | } 42 | 43 | #[derive(Clone, Debug, Args)] 44 | #[non_exhaustive] 45 | pub struct TransformOpts { 46 | /// Names of the definitions that should not get pre-reduced. 47 | /// 48 | /// For programs that don't take arguments and don't have side effects this is 49 | /// usually the entry point of the program (otherwise, the whole program will 50 | /// get reduced to normal form). 51 | #[arg(long = "pre-reduce-skip", value_delimiter = ' ', action = clap::ArgAction::Append)] 52 | pub pre_reduce_skip: Vec, 53 | 54 | /// How much memory to allocate when pre-reducing. 55 | /// 56 | /// Supports abbreviations such as '4G' or '400M'. 57 | #[arg(long = "pre-reduce-memory", value_parser = hvm64_util::parse_abbrev_number::)] 58 | pub pre_reduce_memory: Option, 59 | 60 | /// Maximum amount of rewrites to do when pre-reducing. 61 | /// 62 | /// Supports abbreviations such as '4G' or '400M'. 63 | #[arg(long = "pre-reduce-rewrites", default_value = "100M", value_parser = hvm64_util::parse_abbrev_number::)] 64 | pub pre_reduce_rewrites: u64, 65 | 66 | /// Names of the definitions that should not get pruned. 67 | #[arg(long = "prune-entrypoints", default_value = "main")] 68 | pub prune_entrypoints: Vec, 69 | } 70 | 71 | #[derive(Args, Clone, Debug)] 72 | pub struct TransformArgs { 73 | /// Enables or disables transformation passes. 74 | #[arg(short = 'O', value_delimiter = ' ', action = clap::ArgAction::Append)] 75 | pub transform_passes: Vec, 76 | 77 | #[command(flatten)] 78 | pub transform_opts: TransformOpts, 79 | } 80 | 81 | macro_rules! transform_passes { 82 | ($($pass:ident: $name:literal $(| $alias:literal)*),* $(,)?) => { 83 | #[derive(Debug, Clone, Copy)] 84 | #[allow(non_camel_case_types)] 85 | pub enum TransformPass { 86 | all(bool), 87 | $($pass(bool),)* 88 | } 89 | 90 | impl clap::ValueEnum for TransformPass { 91 | fn value_variants<'a>() -> &'a [Self] { 92 | &[ 93 | Self::all(true), Self::all(false), 94 | $(Self::$pass(true), Self::$pass(false),)* 95 | ] 96 | } 97 | 98 | fn to_possible_value(&self) -> Option { 99 | use TransformPass::*; 100 | Some(match self { 101 | all(true) => clap::builder::PossibleValue::new("all"), 102 | all(false) => clap::builder::PossibleValue::new("no-all"), 103 | $( 104 | $pass(true) => clap::builder::PossibleValue::new($name)$(.alias($alias))*, 105 | $pass(false) => clap::builder::PossibleValue::new(concat!("no-", $name))$(.alias(concat!("no-", $alias)))*, 106 | )* 107 | }) 108 | } 109 | } 110 | 111 | impl TransformPass { 112 | pub fn to_passes(args: &[TransformPass]) -> TransformPasses { 113 | use TransformPass::*; 114 | let mut opts = TransformPasses::NONE; 115 | for arg in args { 116 | match arg { 117 | all(true) => opts = TransformPasses::ALL, 118 | all(false) => opts = TransformPasses::NONE, 119 | $(&$pass(b) => opts.$pass = b,)* 120 | } 121 | } 122 | opts 123 | } 124 | } 125 | }; 126 | } 127 | 128 | transform_passes! { 129 | pre_reduce: "pre-reduce" | "pre", 130 | eta_reduce: "eta-reduce" | "eta", 131 | inline: "inline", 132 | prune: "prune", 133 | } 134 | -------------------------------------------------------------------------------- /transform/src/eta_reduce.rs: -------------------------------------------------------------------------------- 1 | //! Carries out simple eta-reduction, to reduce the amount of rewrites at 2 | //! runtime. 3 | //! 4 | //! ### Eta-equivalence 5 | //! 6 | //! In interaction combinators, there are some nets that are equivalent and 7 | //! have no observable difference 8 | //! 9 | //! ![Image of eta-equivalence](https://i.postimg.cc/XYVxdMFW/image.png) 10 | //! 11 | //! This module implements the eta-equivalence rule at the top-left of the image 12 | //! above 13 | //! 14 | //! ```txt 15 | //! /|-, ,-|\ eta_reduce 16 | //! ---| | X | |-- ~~~~~~~~~~~~> ------------- 17 | //! \|-' '-|/ 18 | //! ``` 19 | //! 20 | //! In hvm-64's AST representation, this reduction looks like this 21 | //! 22 | //! ```txt 23 | //! {lab x y} ... {lab x y} ~~~~~~~~> x ..... x 24 | //! ``` 25 | //! 26 | //! Essentially, both occurrences of the same constructor are replaced by a 27 | //! variable. 28 | //! 29 | //! ### The algorithm 30 | //! 31 | //! The code uses a two-pass O(n) algorithm, where `n` is the amount of nodes 32 | //! in the AST 33 | //! 34 | //! In the first pass, a node-list is built out of an ordered traversal of the 35 | //! AST. Crucially, the node list stores variable offsets instead of the 36 | //! variable's names Since the AST's order is consistent, the ordering of nodes 37 | //! in the node list can be reproduced with a traversal. 38 | //! 39 | //! This means that each occurrence of a variable is encoded with the offset in 40 | //! the node-list to the _other_ occurrence of the variable. 41 | //! 42 | //! For example, if we start with the net: `[(x y) (x y)]` 43 | //! 44 | //! The resulting node list will look like this: 45 | //! 46 | //! `[Ctr(1), Ctr(0), Var(3), Var(3), Ctr(0), Var(-3), Var(-3)]` 47 | //! 48 | //! The second pass uses the node list to find repeated constructors. If a 49 | //! constructor's children are both variables with the same offset, then we 50 | //! lookup that offset relative to the constructor. If it is equal to the first 51 | //! constructor, it means both of them are equal and they can be replaced with a 52 | //! variable. 53 | //! 54 | //! The pass also reduces subnets such as `(* *) -> *` 55 | 56 | use hvm64_util::prelude::*; 57 | 58 | use core::ops::RangeFrom; 59 | 60 | use hvm64_ast::{Net, Tree}; 61 | use hvm64_num::Num; 62 | 63 | pub trait EtaReduce { 64 | fn eta_reduce(&mut self); 65 | } 66 | 67 | impl EtaReduce for Net { 68 | /// Carries out simple eta-reduction 69 | fn eta_reduce(&mut self) { 70 | let mut phase1 = Phase1::default(); 71 | for tree in self.trees() { 72 | phase1.walk_tree(tree); 73 | } 74 | let mut phase2 = Phase2 { nodes: phase1.nodes, index: 0 .. }; 75 | for tree in self.trees_mut() { 76 | phase2.reduce_tree(tree); 77 | } 78 | } 79 | } 80 | 81 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 82 | enum NodeType { 83 | Ctr(u16), 84 | Var(isize), 85 | Num(Num), 86 | Era, 87 | Other, 88 | Hole, 89 | } 90 | 91 | #[derive(Default)] 92 | struct Phase1<'a> { 93 | vars: Map<&'a str, usize>, 94 | nodes: Vec, 95 | } 96 | 97 | impl<'a> Phase1<'a> { 98 | fn walk_tree(&mut self, tree: &'a Tree) { 99 | match tree { 100 | Tree::Ctr { lab, p1, p2 } => { 101 | self.nodes.push(NodeType::Ctr(*lab)); 102 | self.walk_tree(p1); 103 | self.walk_tree(p2); 104 | } 105 | Tree::Var(name) => { 106 | if let Some(i) = self.vars.get(&**name) { 107 | let j = self.nodes.len() as isize; 108 | self.nodes.push(NodeType::Var(*i as isize - j)); 109 | self.nodes[*i] = NodeType::Var(j - *i as isize); 110 | } else { 111 | self.vars.insert(name, self.nodes.len()); 112 | self.nodes.push(NodeType::Hole); 113 | } 114 | } 115 | Tree::Era => self.nodes.push(NodeType::Era), 116 | Tree::Num(num) => self.nodes.push(NodeType::Num(*num)), 117 | _ => { 118 | self.nodes.push(NodeType::Other); 119 | for i in tree.children() { 120 | self.walk_tree(i); 121 | } 122 | } 123 | } 124 | } 125 | } 126 | 127 | struct Phase2 { 128 | nodes: Vec, 129 | index: RangeFrom, 130 | } 131 | 132 | impl Phase2 { 133 | fn reduce_tree(&mut self, tree: &mut Tree) -> NodeType { 134 | let index = self.index.next().unwrap(); 135 | let ty = self.nodes[index]; 136 | if let Tree::Ctr { p1, p2, .. } = tree { 137 | let a = self.reduce_tree(p1); 138 | let b = self.reduce_tree(p2); 139 | if a == b { 140 | let reducible = match a { 141 | NodeType::Var(delta) => self.nodes[index.wrapping_add_signed(delta)] == ty, 142 | NodeType::Era | NodeType::Num(_) => true, 143 | _ => false, 144 | }; 145 | if reducible { 146 | *tree = mem::take(p1); 147 | return a; 148 | } 149 | } 150 | } else { 151 | for i in tree.children_mut() { 152 | self.reduce_tree(i); 153 | } 154 | } 155 | ty 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /tests/snapshots/run@tests::programs::church_exp.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/tests.rs 3 | expression: output 4 | input_file: tests/programs/church_exp.hvm 5 | --- 6 | (#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1((#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(a b) c) d) e) f) g) h) i) j) k) l) m) n) o) p) q) r) s) t) u) v) w) x) y) z) aa) ab) ac) ad) ae) af) ag) ah) ai) aj) ak) al) am) an) ao) ap) aq) ar) as) at) au) av) aw) ax) ay) az) ba) bb) bc) bd) be) bf) bg) bh) bi) bj) bk) bl) bm) bn) bo) bp) bq) br) bs) bt) bu) bv) bw) bx) by) bz) ca) cb) cc) cd) ce) cf) cg) ch) ci) cj) ck) cl) cm) cn) co) cp) cq) cr) cs) ct) cu) cv) cw) cx) cy) cz) da) db) dc) dd) de) df) dg) dh) di) dj) dk) dl) dm) dn) do) dp) dq) dr) ds) dt) du) dv) dw) dx) dy) dz) ea) eb) ec) ed) ee) ef) eg) eh) ei) ej) ek) el) em) en) eo) ep) eq) er) es) et) eu) ev) ew) ex) ey) ez) fa) fb) fc) fd) fe) ff) fg) fh) fi) fj) fk) fl) fm) fn) fo) fp) fq) fr) fs) ft) fu) fv) fw) fx) fy) fz) ga) gb) gc) gd) ge) gf) gg) gh) gi) gj) gk) gl) gm) gn) go) gp) gq) gr) gs) gt) gu) gv) gw) gx) gy) gz) ha) hb) hc) hd) he) hf) hg) hh) hi) hj) hk) hl) hm) hn) ho) hp) hq) hr) hs) ht) hu) hv) hw) hx) hy) hz) ia) ib) ic) id) ie) if) ig) ih) ii) ij) ik) il) im) in) io) ip) iq) ir) is) it) iu) iv) iw) ix) iy) iz) ja) jb) jc) jd) je) jf) jg) jh) ji) jj) jk) jl) jm) jn) jo) jp) jq) jr) js) jt) ju) jv) jw) jx) jy) jz) ka) kb) kc) kd) ke) kf) kg) kh) ki) kj) kk) kl) km) kn) ko) kp) kq) kr) ks) kt) ku) kv) kw) kx) ky) kz) la) lb) lc) ld) le) lf) lg) lh) li) lj) lk) ll) lm) ln) lo) lp) lq) lr) ls) lt) lu) lv) lw) lx) ly) lz) ma) mb) mc) md) me) mf) mg) mh) mi) mj) mk) ml) mm) mn) mo) mp) mq) mr) ms) mt) mu) mv) mw) mx) my) (my mz)) (mz na)) (na nb)) (nb nc)) (nc nd)) (nd ne)) (ne nf)) (nf ng)) (ng nh)) (nh ni)) (ni nj)) (nj nk)) (nk nl)) (nl nm)) (nm nn)) (nn no)) (no np)) (np nq)) (nq #1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(mf mg) mh) mi) mj) mk) ml) mm) mn) mo) mp) mq) mr) ms) mt) mu) mv) mw) mx) #1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(lm ln) lo) lp) lq) lr) ls) lt) lu) lv) lw) lx) ly) lz) ma) mb) mc) md) me) #1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(kt ku) kv) kw) kx) ky) kz) la) lb) lc) ld) le) lf) lg) lh) li) lj) lk) ll) #1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(ka kb) kc) kd) ke) kf) kg) kh) ki) kj) kk) kl) km) kn) ko) kp) kq) kr) ks) #1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(jh ji) jj) jk) jl) jm) jn) jo) jp) jq) jr) js) jt) ju) jv) jw) jx) jy) jz) #1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(io ip) iq) ir) is) it) iu) iv) iw) ix) iy) iz) ja) jb) jc) jd) je) jf) jg) #1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(hv hw) hx) hy) hz) ia) ib) ic) id) ie) if) ig) ih) ii) ij) ik) il) im) in) #1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(hc hd) he) hf) hg) hh) hi) hj) hk) hl) hm) hn) ho) hp) hq) hr) hs) ht) hu) #1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(gj gk) gl) gm) gn) go) gp) gq) gr) gs) gt) gu) gv) gw) gx) gy) gz) ha) hb) #1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(fq fr) fs) ft) fu) fv) fw) fx) fy) fz) ga) gb) gc) gd) ge) gf) gg) gh) gi) #1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(ex ey) ez) fa) fb) fc) fd) fe) ff) fg) fh) fi) fj) fk) fl) fm) fn) fo) fp) #1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(ee ef) eg) eh) ei) ej) ek) el) em) en) eo) ep) eq) er) es) et) eu) ev) ew) #1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(dl dm) dn) do) dp) dq) dr) ds) dt) du) dv) dw) dx) dy) dz) ea) eb) ec) ed) #1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(cs ct) cu) cv) cw) cx) cy) cz) da) db) dc) dd) de) df) dg) dh) di) dj) dk) #1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(bz ca) cb) cc) cd) ce) cf) cg) ch) ci) cj) ck) cl) cm) cn) co) cp) cq) cr) #1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(bg bh) bi) bj) bk) bl) bm) bn) bo) bp) bq) br) bs) bt) bu) bv) bw) bx) by) #1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(an ao) ap) aq) ar) as) at) au) av) aw) ax) ay) az) ba) bb) bc) bd) be) bf) #1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(u v) w) x) y) z) aa) ab) ac) ad) ae) af) ag) ah) ai) aj) ak) al) am) #1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(#1(b c) d) e) f) g) h) i) j) k) l) m) n) o) p) q) r) s) t) nr))))))))))))))))))))) (a nr)) 7 | RWTS : 1_948 8 | - ANNI : 401 9 | - COMM : 380 10 | - ERAS : 0 11 | - DREF : 1_167 12 | - OPER : 0 13 | --------------------------------------------------------------------------------