diff --git a/ast/src/ast.rs b/ast/src/ast.rs index 81e9491a..33d2f942 100644 --- a/ast/src/ast.rs +++ b/ast/src/ast.rs @@ -72,16 +72,8 @@ pub enum Tree { /// The label of the combinator. (Combinators with the same label /// annihilate, and combinators with different labels commute.) lab: Lab, - /// The auxiliary ports of this node. - /// - /// - 0 ports: this behaves identically to an eraser node. - /// - 1 port: this behaves identically to a wire. - /// - 2 ports: this is a standard binary combinator node. - /// - 3+ ports: equivalent to right-chained binary nodes; `(a b c)` is - /// equivalent to `(a (b c))`. - /// - /// The length of this vector must be less than [`MAX_ARITY`]. - ports: Vec, + lft: Box, + rgt: Box, }, /// A binary node representing an operation on native integers. /// @@ -98,59 +90,18 @@ pub enum Tree { /// /// The principal port connects to the integer to be matched on. Mat { - /// An auxiliary port; connects to the zero branch. - zero: Box, - /// An auxiliary port; connects to the a CTR with label 0 containing the - /// predecessor and the output of the succ branch. - succ: Box, + /// An auxiliary port; connects to a tree of the following structure: + /// ```text + /// (+value_if_zero (-predecessor_of_number +value_if_succ)) + /// ``` + arms: Box, /// An auxiliary port; connects to the output. out: Box, }, - /// An Scott-encoded ADT node. - /// - /// This is always equivalent to: - /// ```text - /// {$lab - /// * * * ... // one era node per `variant_index` - /// {$lab $f0 $f1 $f2 ... R} // each field, in order, followed by a var node - /// * * * ... // one era node per `variant_count - variant_index - 1` - /// R // a var node - /// } - /// ``` - /// - /// For example: - /// ```text - /// data Option = None | (Some value): - /// None: - /// (0:2) = Adt { lab: 0, variant_index: 0, variant_count: 2, fields: [] } - /// (R * R) = Ctr { lab: 0, ports: [Var { nam: "R" }, Era, Var { nam: "R" }]} - /// (Some 123): - /// (1:2 #123) = Adt { lab: 0, variant_index: 0, variant_count: 2, fields: [Int { val: 123 }] } - /// (* (#123 R) R) = Ctr { lab: 0, ports: [Era, Ctr { lab: 0, ports: [Int { val: 123 }, Var { nam: "R" }] }, Var { nam: "R" }]} - /// ``` - Adt { - lab: Lab, - /// The index of the variant of this ADT node. - /// - /// Must be less than `variant_count`. - variant_index: usize, - /// The number of variants in the data type. - /// - /// Must be greater than `0` and less than `MAX_ADT_VARIANTS`. - variant_count: usize, - /// The fields of this ADT node. - /// - /// Must have a length less than `MAX_ADT_FIELDS`. - fields: Vec, - }, /// One side of a wire; the other side will have the same name. Var { nam: String }, } -pub const MAX_ARITY: usize = 8; -pub const MAX_ADT_VARIANTS: usize = MAX_ARITY - 1; -pub const MAX_ADT_FIELDS: usize = MAX_ARITY - 1; - impl Net { pub fn trees(&self) -> impl Iterator { iter::once(&self.root).chain(self.redexes.iter().flat_map(|(x, y)| [x, y])) @@ -173,7 +124,7 @@ impl Net { let fresh_str = create_var(fresh + 1); let fun = mem::take(&mut self.root); - let app = Tree::Ctr { lab: 0, ports: vec![arg, Tree::Var { nam: fresh_str.clone() }] }; + let app = Tree::Ctr { lab: 0, lft: Box::new(arg), rgt: Box::new(Tree::Var { nam: fresh_str.clone() }) }; self.root = Tree::Var { nam: fresh_str }; self.redexes.push((fun, app)); } @@ -190,45 +141,33 @@ impl Net { impl Tree { #[inline(always)] pub fn children(&self) -> impl ExactSizeIterator + DoubleEndedIterator { - multi_iterator! { Iter { Nil, Two, Three, Vec } } + multi_iterator! { Iter { Nil, Two } } match self { Tree::Era | Tree::Int { .. } | Tree::F32 { .. } | Tree::Ref { .. } | Tree::Var { .. } => Iter::Nil([]), - Tree::Ctr { ports, .. } => Iter::Vec(ports), + Tree::Ctr { lft, rgt, .. } => Iter::Two([&**lft, rgt]), Tree::Op { rhs, out, .. } => Iter::Two([&**rhs, out]), - Tree::Mat { zero, succ, out } => Iter::Three([&**zero, succ, out]), - Tree::Adt { fields, .. } => Iter::Vec(fields), + Tree::Mat { arms, out } => Iter::Two([&**arms, out]), } } #[inline(always)] pub fn children_mut(&mut self) -> impl ExactSizeIterator + DoubleEndedIterator { - multi_iterator! { Iter { Nil, Two, Three, Vec } } + multi_iterator! { Iter { Nil, Two } } match self { Tree::Era | Tree::Int { .. } | Tree::F32 { .. } | Tree::Ref { .. } | Tree::Var { .. } => Iter::Nil([]), - Tree::Ctr { ports, .. } => Iter::Vec(ports), + Tree::Ctr { lft, rgt, .. } => Iter::Two([&mut **lft, rgt]), Tree::Op { rhs, out, .. } => Iter::Two([&mut **rhs, out]), - Tree::Mat { zero, succ, out } => Iter::Three([&mut **zero, succ, out]), - Tree::Adt { fields, .. } => Iter::Vec(fields), + Tree::Mat { arms, out } => Iter::Two([&mut **arms, out]), } } pub fn lab(&self) -> Option { match self { - Tree::Ctr { lab, ports } if ports.len() >= 2 => Some(*lab), - Tree::Adt { lab, .. } => Some(*lab), + Tree::Ctr { lab, .. } => Some(*lab), _ => None, } } - pub fn legacy_mat(mut arms: Tree, out: Tree) -> Option { - let Tree::Ctr { lab: 0, ports } = &mut arms else { None? }; - let ports = mem::take(ports); - let Ok([zero, succ]) = <[_; 2]>::try_from(ports) else { None? }; - let zero = Box::new(zero); - let succ = Box::new(succ); - Some(Tree::Mat { zero, succ, out: Box::new(out) }) - } - /// Increases `fresh` until `create_var(*fresh)` does not conflict /// with a [`Tree::Var`] in `tree` /// @@ -252,12 +191,9 @@ impl Clone for Tree { Tree::Int { val } => Tree::Int { val: *val }, Tree::F32 { val } => Tree::F32 { val: *val }, Tree::Ref { nam } => Tree::Ref { nam: nam.clone() }, - Tree::Ctr { lab, ports } => Tree::Ctr { lab: *lab, ports: ports.clone() }, + Tree::Ctr { lab, lft, rgt } => Tree::Ctr { lab: *lab, lft: lft.clone(), rgt: rgt.clone() }, Tree::Op { op, rhs, out } => Tree::Op { op: *op, rhs: rhs.clone(), out: out.clone() }, - Tree::Mat { zero, succ, out } => Tree::Mat { zero: zero.clone(), succ: succ.clone(), out: out.clone() }, - Tree::Adt { lab, variant_index, variant_count, fields } => { - Tree::Adt { lab: *lab, variant_index: *variant_index, variant_count: *variant_count, fields: fields.clone() } - } + Tree::Mat { arms, out } => Tree::Mat { arms: arms.clone(), out: out.clone() }, Tree::Var { nam } => Tree::Var { nam: nam.clone() }, }) } @@ -319,50 +255,17 @@ impl fmt::Display for Tree { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { maybe_grow(move || match self { Tree::Era => write!(f, "*"), - Tree::Ctr { lab, ports } => { - match lab { - 0 => write!(f, "("), - 1 => write!(f, "["), - _ => write!(f, "{{{lab}"), - }?; - let mut space = *lab > 1; - for port in ports { - if space { - write!(f, " ")?; - } - write!(f, "{port}")?; - space = true; - } - match lab { - 0 => write!(f, ")"), - 1 => write!(f, "]"), - _ => write!(f, "}}"), - }?; - Ok(()) - } - Tree::Adt { lab, variant_index, variant_count, fields } => { - match lab { - 0 => write!(f, "("), - 1 => write!(f, "["), - _ => write!(f, "{{{lab}"), - }?; - write!(f, ":{}:{}", variant_index, variant_count)?; - for field in fields { - write!(f, " {field}")?; - } - match lab { - 0 => write!(f, ")"), - 1 => write!(f, "]"), - _ => write!(f, "}}"), - }?; - Ok(()) - } + Tree::Ctr { lab, lft, rgt } => match lab { + 0 => write!(f, "({lft} {rgt})"), + 1 => write!(f, "[{lft} {rgt}]"), + _ => write!(f, "{{{lab} {lft} {rgt}}}"), + }, Tree::Var { nam } => write!(f, "{nam}"), Tree::Ref { nam } => write!(f, "@{nam}"), Tree::Int { val } => write!(f, "#{val}"), Tree::F32 { val } => write!(f, "#{:?}", val.0), Tree::Op { op, rhs, out } => write!(f, "<{op} {rhs} {out}>"), - Tree::Mat { zero, succ, out } => write!(f, "?<{zero} {succ} {out}>"), + Tree::Mat { arms, out } => write!(f, "?<{arms} {out}>"), }) } } @@ -370,7 +273,6 @@ impl fmt::Display for Tree { #[test] #[cfg(feature = "parser")] fn test_tree_drop() { - use alloc::vec; use core::str::FromStr; drop(Tree::from_str("((* (* *)) (* *))")); @@ -378,15 +280,15 @@ fn test_tree_drop() { let mut long_tree = Tree::Era; let mut cursor = &mut long_tree; for _ in 0 .. 100_000 { - *cursor = Tree::Ctr { lab: 0, ports: vec![Tree::Era, Tree::Era] }; - let Tree::Ctr { ports, .. } = cursor else { unreachable!() }; - cursor = &mut ports[0]; + *cursor = Tree::Ctr { lab: 0, lft: Box::new(Tree::Era), rgt: Box::new(Tree::Era) }; + let Tree::Ctr { lft, .. } = cursor else { unreachable!() }; + cursor = lft; } drop(long_tree); let mut big_tree = Tree::Era; for _ in 0 .. 16 { - big_tree = Tree::Ctr { lab: 0, ports: vec![big_tree.clone(), big_tree] }; + big_tree = Tree::Ctr { lab: 0, lft: Box::new(big_tree.clone()), rgt: Box::new(big_tree) }; } drop(big_tree); } diff --git a/ast/src/parser.rs b/ast/src/parser.rs index 6d0d6792..ac25ee35 100644 --- a/ast/src/parser.rs +++ b/ast/src/parser.rs @@ -3,7 +3,7 @@ use crate::prelude::*; use alloc::collections::BTreeMap; use core::str::FromStr; -use crate::{Book, Lab, Net, Tree, MAX_ADT_FIELDS, MAX_ADT_VARIANTS, MAX_ARITY}; +use crate::{Book, Lab, Net, Tree}; use hvm64_util::{maybe_grow, ops::TypedOp as Op}; use TSPL::{new_parser, Parser}; @@ -57,52 +57,16 @@ impl<'i> Hvm64Parser<'i> { _ => unreachable!(), }; let close = match char { - '(' => ')', - '[' => ']', - '{' => '}', + '(' => ")", + '[' => "]", + '{' => "}", _ => unreachable!(), }; self.skip_trivia(); - if self.peek_one().is_some_and(|x| x == ':') { - self.advance_one(); - let variant_index = self.parse_u64()?; - self.consume(":")?; - let variant_count = self.parse_u64()?; - let mut fields = Vec::new(); - self.skip_trivia(); - while self.peek_one() != Some(close) { - fields.push(self.parse_tree()?); - self.skip_trivia(); - } - self.advance_one(); - if variant_count == 0 { - Err("variant count cannot be zero".to_owned())?; - } - if variant_count > (MAX_ADT_VARIANTS as u64) { - Err("adt has too many variants".to_owned())?; - } - if variant_index >= variant_count { - Err("variant index out of range".to_owned())?; - } - let variant_index = variant_index as usize; - let variant_count = variant_count as usize; - if fields.len() > MAX_ADT_FIELDS { - Err("adt has too many fields".to_owned())?; - } - Ok(Tree::Adt { lab, variant_index, variant_count, fields }) - } else { - let mut ports = Vec::new(); - self.skip_trivia(); - while self.peek_one() != Some(close) { - ports.push(self.parse_tree()?); - self.skip_trivia(); - } - self.advance_one(); - if ports.len() > MAX_ARITY { - Err("ctr has too many ports".to_owned())?; - } - Ok(Tree::Ctr { lab, ports }) - } + let lft = Box::new(self.parse_tree()?); + let rgt = Box::new(self.parse_tree()?); + self.consume(close)?; + Ok(Tree::Ctr { lab, lft, rgt }) } // Ref = "@" Name Some('@') => { @@ -143,21 +107,11 @@ impl<'i> Hvm64Parser<'i> { } // Mat = "?<" Tree Tree ">" Some('?') => { - self.advance_one(); - self.consume("<")?; - let zero = self.parse_tree()?; - let succ = self.parse_tree()?; - self.skip_trivia(); - if self.peek_one() == Some('>') { - self.advance_one(); - Tree::legacy_mat(zero, succ).ok_or_else(|| "invalid legacy match".to_owned()) - } else { - let zero = Box::new(zero); - let succ = Box::new(succ); - let out = Box::new(self.parse_tree()?); - self.consume(">")?; - Ok(Tree::Mat { zero, succ, out }) - } + self.consume("?<")?; + let arms = Box::new(self.parse_tree()?); + let out = Box::new(self.parse_tree()?); + self.consume(">")?; + Ok(Tree::Mat { arms, out }) } // Var = Name _ => Ok(Tree::Var { nam: self.parse_name()? }), diff --git a/examples/sort/radix/radix_sort_ctr.hvm b/examples/sort/radix/radix_sort_ctr.hvm index 05bf998d..036fe31b 100644 --- a/examples/sort/radix/radix_sort_ctr.hvm +++ b/examples/sort/radix/radix_sort_ctr.hvm @@ -5,7 +5,7 @@ @Single = (a (* ((a b) (* b)))) @Used = (* @Used$C0) @Used$C0 = (a (* a)) -@gen = (?<@gen$C0 @gen$C1 a> a) +@gen = (?<(@gen$C0 @gen$C1) a> a) @gen$C0 = (a b) & @Single ~ (a b) @gen$C1 = ({7 a b} (<<< #1 {9 c <| #1 d>}> e)) @@ -69,7 +69,7 @@ @sum$C0 = (a (b c)) & @sum ~ (a <+ d c>) & @sum ~ (b d) -@swap = (?<@swap$C0 @swap$C2 a> a) +@swap = (?<(@swap$C0 @swap$C2) a> a) @swap$C0 = (a (b c)) & @Node ~ (a (b c)) @swap$C1 = (a (b c)) diff --git a/host/src/encode.rs b/host/src/encode.rs index b98b07d2..06878a0d 100644 --- a/host/src/encode.rs +++ b/host/src/encode.rs @@ -65,43 +65,15 @@ impl<'a, E: Encoder> State<'a, E> { self.visit_tree(tree, trg); } fn visit_tree(&mut self, tree: &'a Tree, trg: E::Trg) { - static ERA: Tree = Tree::Era; maybe_grow(move || match tree { Tree::Era => self.encoder.link_const(trg, Port::ERA), Tree::Int { val } => self.encoder.link_const(trg, Port::new_int(*val)), Tree::F32 { val } => self.encoder.link_const(trg, Port::new_float(val.0)), Tree::Ref { nam } => self.encoder.link_const(trg, Port::new_ref(&self.host.defs[nam])), - Tree::Ctr { lab, ports } => { - if ports.is_empty() { - return self.visit_tree(&ERA, trg); - } - let mut trg = trg; - for port in &ports[0 .. ports.len() - 1] { - let (l, r) = self.encoder.ctr(*lab, trg); - self.visit_tree(port, l); - trg = r; - } - self.visit_tree(ports.last().unwrap(), trg); - } - Tree::Adt { lab, variant_index, variant_count, fields } => { - let mut trg = trg; - for _ in 0 .. *variant_index { - let (l, r) = self.encoder.ctr(*lab, trg); - self.visit_tree(&ERA, l); - trg = r; - } - let (mut l, mut r) = self.encoder.ctr(*lab, trg); - for field in fields { - let (x, y) = self.encoder.ctr(*lab, l); - self.visit_tree(field, x); - l = y; - } - for _ in 0 .. (*variant_count - *variant_index - 1) { - let (x, y) = self.encoder.ctr(*lab, r); - self.visit_tree(&ERA, x); - r = y; - } - self.encoder.link(l, r); + Tree::Ctr { lab, lft, rgt } => { + let (l, r) = self.encoder.ctr(*lab, trg); + self.visit_tree(lft, l); + self.visit_tree(rgt, r); } Tree::Op { op, rhs: lft, out: rgt } => match &**lft { Tree::Int { val } => { @@ -118,11 +90,9 @@ impl<'a, E: Encoder> State<'a, E> { self.visit_tree(rgt, r); } }, - Tree::Mat { zero, succ, out } => { + Tree::Mat { arms, out } => { let (a, o) = self.encoder.mat(trg); - let (z, s) = self.encoder.ctr(0, a); - self.visit_tree(zero, z); - self.visit_tree(succ, s); + self.visit_tree(arms, a); self.visit_tree(out, o); } Tree::Var { nam } => match self.scope.entry(nam) { diff --git a/host/src/readback.rs b/host/src/readback.rs index eb9753a6..478da9cc 100644 --- a/host/src/readback.rs +++ b/host/src/readback.rs @@ -72,13 +72,13 @@ impl<'a> ReadbackState<'a> { } Tag::Ctr => { let node = port.traverse_node(); - Tree::Ctr { lab: node.lab, ports: vec![self.read_wire(node.p1), self.read_wire(node.p2)] } + Tree::Ctr { lab: node.lab, lft: Box::new(self.read_wire(node.p1)), rgt: Box::new(self.read_wire(node.p2)) } } Tag::Mat => { let node = port.traverse_node(); let arms = self.read_wire(node.p1); let out = self.read_wire(node.p2); - Tree::legacy_mat(arms, out).expect("invalid mat node") + Tree::Mat { arms: Box::new(arms), out: Box::new(out) } } }) } diff --git a/src/args.rs b/src/args.rs index f7c2bd14..4709f3f1 100644 --- a/src/args.rs +++ b/src/args.rs @@ -127,8 +127,6 @@ macro_rules! transform_passes { transform_passes! { pre_reduce: "pre-reduce" | "pre", - coalesce_ctrs: "coalesce-ctrs" | "coalesce", - encode_adts: "encode-adts" | "adts", eta_reduce: "eta-reduce" | "eta", inline: "inline", prune: "prune", diff --git a/tests/transform.rs b/tests/transform.rs index 89af340d..30794c90 100644 --- a/tests/transform.rs +++ b/tests/transform.rs @@ -2,11 +2,8 @@ //! Tests for transformation passes -use hvm64_ast::{Book, Net, Tree}; -use hvm64_transform::{ - coalesce_ctrs::CoalesceCtrs, encode_adts::EncodeAdts, eta_reduce::EtaReduce, inline::Inline, pre_reduce::PreReduce, - prune::Prune, TransformError, -}; +use hvm64_ast::{Book, Net}; +use hvm64_transform::{eta_reduce::EtaReduce, inline::Inline, pre_reduce::PreReduce, prune::Prune, TransformError}; use insta::assert_snapshot; use std::str::FromStr; @@ -49,34 +46,6 @@ pub fn test_fast_pre_reduce() { "###) } -#[test] -pub fn test_adt_encoding() { - pub fn parse_and_encode(net: &str) -> String { - let mut net = Net::from_str(net).unwrap(); - net.trees_mut().for_each(Tree::coalesce_constructors); - net.trees_mut().for_each(Tree::encode_scott_adts); - format!("{net}") - } - assert_snapshot!(parse_and_encode("(a (b (c d)))"), @"(a b c d)"); - assert_snapshot!(parse_and_encode("(a (b c (d e)))"), @"(a b c d e)"); - assert_snapshot!(parse_and_encode("(a b c d e f g h)"), @"(a b c d e f g h)"); - assert_snapshot!(parse_and_encode("(a b c d (e f g h (i j k l)))"), @"(a b c d (e f g h (i j k l)))"); - - assert_snapshot!(parse_and_encode("(* ((a R) R))"), @"(:1:2 a)"); - assert_snapshot!(parse_and_encode("((a R) (* R))"), @"(:0:2 a)"); - assert_snapshot!(parse_and_encode("(* (* ((a R) R)))"), @"(:2:3 a)"); - assert_snapshot!(parse_and_encode("(* ((a R) (* R)))"), @"(:1:3 a)"); - assert_snapshot!(parse_and_encode("((a (b R)) R)"), @"(:0:1 a b)"); - assert_snapshot!(parse_and_encode("((a (b (c R))) R)"), @"(:0:1 a b c)"); - assert_snapshot!(parse_and_encode("(* ((a (b (c R))) R))"), @"(:1:2 a b c)"); - assert_snapshot!(parse_and_encode("{4 * {4 {4 a {4 b {4 c R}}} R}}"), @"{4:1:2 a b c}"); - assert_snapshot!(parse_and_encode("(* x x)"), @"(:1:2)"); - assert_snapshot!(parse_and_encode("(((((* x x) x) * x) x) * x)"), @"(:0:2 (:0:2 (:1:2)))"); - assert_snapshot!(parse_and_encode("(a b * (a b c) * c)"), @"(a b (:1:3 a b))"); - assert_snapshot!(parse_and_encode("(* (:0:1))"), @"(:1:2)"); - assert_snapshot!(parse_and_encode("(a * (:0:1))"), @"(a (:1:2))"); -} - #[test] pub fn test_eta() { pub fn parse_and_reduce(net: &str) -> String { @@ -85,14 +54,14 @@ pub fn test_eta() { format!("{net}") } assert_snapshot!(parse_and_reduce("((x y) (x y))"), @"(x x)"); - assert_snapshot!(parse_and_reduce("((a b c d e f) (a b c d e f))"), @"(a a)"); + assert_snapshot!(parse_and_reduce("((a (b (c (d (e f))))) (a (b (c (d (e f))))))"), @"(a a)"); assert_snapshot!(parse_and_reduce("<+ (a b) (a b)>"), @"<+ a a>"); assert_snapshot!(parse_and_reduce("(a b) & ((a b) (c d)) ~ (c d) "), @r###" a & (a c) ~ c "###); assert_snapshot!(parse_and_reduce("((a b) [a b])"), @"((a b) [a b])"); - assert_snapshot!(parse_and_reduce("((a b c) b c)"), @"((a b) b)"); + assert_snapshot!(parse_and_reduce("((a (b c)) (b c))"), @"((a b) b)"); assert_snapshot!(parse_and_reduce("([(a b) (c d)] [(a b) (c d)])"), @"(a a)"); assert_snapshot!(parse_and_reduce("(* *)"), @"*"); assert_snapshot!(parse_and_reduce("([(#0 #0) (#12345 #12345)] [(* *) (a a)])"), @"([#0 #12345] [* (a a)])"); @@ -107,25 +76,16 @@ pub fn test_inline() { assert_snapshot!(parse_and_inline(" @era = * @num = #123 - @abab = (a b a b) + @abab = (a (b (a b))) @ref = @abab @def = @ref @eff = @def - @I = (:0:1) - @K = (:0:2) - @1234 = (:1:2 (:3:4)) @foo = @bar @bar = @baz @baz = @unbound - @into = ((@era @num @abab @ref @def @eff @I @K) (@into @1234 @foo @bar @baz @unbound)) + @into = (@era (@num (@abab (@ref (@def (@eff (@into (@foo (@bar (@baz @unbound)))))))))) ").unwrap(), @r###" - @1234 = (:1:2 (:3:4)) - - @I = (:0:1) - - @K = (:0:2) - - @abab = (a b a b) + @abab = (a (b (a b))) @bar = @unbound @@ -139,7 +99,7 @@ pub fn test_inline() { @foo = @unbound - @into = ((* #123 @abab @abab @abab @abab (:0:1) (:0:2)) (@into @1234 @unbound @unbound @unbound @unbound)) + @into = (* (#123 (@abab (@abab (@abab (@abab (@into (@unbound (@unbound (@unbound @unbound)))))))))) @num = #123 @@ -160,14 +120,14 @@ pub fn test_prune() { } assert_snapshot!(parse_and_prune(" @self = (* @self) - @main = (@main @a @b) - @a = (@b @c @d) + @main = (@main (@a @b)) + @a = (@b (@c @d)) @b = (@c @c) @c = @d @d = @at @idk = (@e @f) "), @r###" - @a = (@b @c @d) + @a = (@b (@c @d)) @b = (@c @c) @@ -175,6 +135,6 @@ pub fn test_prune() { @d = @at - @main = (@main @a @b) + @main = (@main (@a @b)) "###); } diff --git a/transform/src/coalesce_ctrs.rs b/transform/src/coalesce_ctrs.rs deleted file mode 100644 index 4d3451fc..00000000 --- a/transform/src/coalesce_ctrs.rs +++ /dev/null @@ -1,29 +0,0 @@ -use crate::prelude::*; -use hvm64_ast::{Tree, MAX_ARITY}; -use hvm64_util::maybe_grow; - -pub trait CoalesceCtrs { - fn coalesce_constructors(&mut self); -} - -impl CoalesceCtrs for Tree { - /// Join chains of CTR nodes, such as `(a (b (c d)))` into n-ary nodes `(a b c - /// d)` - fn coalesce_constructors(&mut self) { - maybe_grow(|| match self { - Tree::Ctr { lab, ports } => { - ports.iter_mut().for_each(Tree::coalesce_constructors); - match &mut ports.pop() { - Some(Tree::Ctr { lab: inner_lab, ports: inner_ports }) - if inner_lab == lab && ports.len() + inner_ports.len() < MAX_ARITY => - { - ports.append(inner_ports); - } - Some(other) => ports.push(mem::take(other)), - None => (), - } - } - other => other.children_mut().for_each(Tree::coalesce_constructors), - }) - } -} diff --git a/transform/src/encode_adts.rs b/transform/src/encode_adts.rs deleted file mode 100644 index 21ebcb2d..00000000 --- a/transform/src/encode_adts.rs +++ /dev/null @@ -1,81 +0,0 @@ -use crate::prelude::*; -use hvm64_ast::{Tree, MAX_ADT_VARIANTS}; -use hvm64_util::maybe_grow; - -pub trait EncodeAdts { - fn encode_scott_adts(&mut self); -} - -impl EncodeAdts for Tree { - /// Encode scott-encoded ADTs into optimized compact ADT nodes - fn encode_scott_adts(&mut self) { - maybe_grow(|| match self { - &mut Tree::Ctr { lab, ref mut ports } => { - fn get_adt_info(lab: u16, ports: &[Tree]) -> Option<(usize, usize)> { - let Some(Tree::Var { nam: ret_var }) = ports.last() else { return None }; - - let mut variant_index = None; - for (idx, i) in ports.iter().enumerate().rev().skip(1) { - match i { - Tree::Era => {} - Tree::Ctr { lab: inner_lab, ports } if *inner_lab == lab && variant_index.is_none() => { - // Ensure that the last port is the return variable - let Some(Tree::Var { nam }) = ports.last() else { return None }; - if nam != ret_var { - return None; - } - variant_index = Some(idx); - } - // Nilary variant - Tree::Var { nam } if nam == ret_var && variant_index.is_none() => { - variant_index = Some(idx); - } - // Does not encode an ADT. - _ => return Some((idx + 1, variant_index?)), - } - } - Some((0, variant_index?)) - } - - if let Some((start, idx)) = get_adt_info(lab, ports) { - let fields = match &mut ports.swap_remove(idx) { - Tree::Ctr { ports: fields, .. } => { - fields.pop(); - mem::take(fields) - } - Tree::Var { .. } => vec![], - _ => unreachable!(), - }; - let adt = Tree::Adt { lab, variant_index: idx - start, variant_count: ports.len() - start, fields }; - if start == 0 { - *self = adt; - } else { - ports.truncate(start); - ports.push(adt); - } - } else { - while let Some(&Tree::Adt { lab: inner_lab, variant_count, .. }) = ports.last() { - if inner_lab == lab - && variant_count < MAX_ADT_VARIANTS - && matches!(ports.get(ports.len() - 2), Some(&Tree::Era)) - { - ports.swap_remove(ports.len() - 2); - let Some(Tree::Adt { variant_index, variant_count, .. }) = ports.last_mut() else { unreachable!() }; - *variant_index += 1; - *variant_count += 1; - if ports.len() == 1 { - *self = ports.pop().unwrap(); - break; - } - } else { - break; - } - } - } - - self.children_mut().for_each(Tree::encode_scott_adts); - } - other => other.children_mut().for_each(Tree::encode_scott_adts), - }) - } -} diff --git a/transform/src/eta_reduce.rs b/transform/src/eta_reduce.rs index 71a6e12e..030d3a46 100644 --- a/transform/src/eta_reduce.rs +++ b/transform/src/eta_reduce.rs @@ -98,14 +98,10 @@ struct Phase1<'a> { impl<'a> Phase1<'a> { fn walk_tree(&mut self, tree: &'a Tree) { match tree { - Tree::Ctr { lab, ports } => { - let last_port = ports.len() - 1; - for (idx, i) in ports.iter().enumerate() { - if idx != last_port { - self.nodes.push(NodeType::Ctr(*lab)); - } - self.walk_tree(i); - } + Tree::Ctr { lab, lft, rgt } => { + self.nodes.push(NodeType::Ctr(*lab)); + self.walk_tree(lft); + self.walk_tree(rgt); } Tree::Var { nam } => { if let Some(i) = self.vars.get(&**nam) { @@ -136,42 +132,28 @@ struct Phase2 { } impl Phase2 { - fn reduce_ctr(&mut self, lab: u16, ports: &mut Vec, skip: usize) -> NodeType { - if skip == ports.len() { - return NodeType::Other; - } - if skip == ports.len() - 1 { - return self.reduce_tree(&mut ports[skip]); - } - let head_index = self.index.next().unwrap(); - let a = self.reduce_tree(&mut ports[skip]); - let b = self.reduce_ctr(lab, ports, skip + 1); - if a == b { - let reducible = match a { - NodeType::Var(delta) => self.nodes[head_index.wrapping_add_signed(delta)] == NodeType::Ctr(lab), - NodeType::Era | NodeType::Int(_) | NodeType::F32(_) => true, - _ => false, - }; - if reducible { - ports.pop(); - return a; - } - } - NodeType::Ctr(lab) - } fn reduce_tree(&mut self, tree: &mut Tree) -> NodeType { - if let Tree::Ctr { lab, ports } = tree { - let ty = self.reduce_ctr(*lab, ports, 0); - if ports.len() == 1 { - *tree = ports.pop().unwrap(); + let index = self.index.next().unwrap(); + let ty = self.nodes[index]; + if let Tree::Ctr { lft, rgt, .. } = tree { + let a = self.reduce_tree(lft); + let b = self.reduce_tree(rgt); + if a == b { + let reducible = match a { + NodeType::Var(delta) => &self.nodes[index.wrapping_add_signed(delta)] == &ty, + NodeType::Era | NodeType::Int(_) | NodeType::F32(_) => true, + _ => false, + }; + if reducible { + *tree = mem::take(lft); + return a; + } } - ty } else { - let index = self.index.next().unwrap(); for i in tree.children_mut() { self.reduce_tree(i); } - self.nodes[index] } + ty } } diff --git a/transform/src/transform.rs b/transform/src/transform.rs index 2054b9ba..4ef6f811 100644 --- a/transform/src/transform.rs +++ b/transform/src/transform.rs @@ -4,15 +4,11 @@ include!("../../prelude.rs"); use hvm64_ast::Book; -pub mod coalesce_ctrs; -pub mod encode_adts; pub mod eta_reduce; pub mod inline; pub mod pre_reduce; pub mod prune; -use coalesce_ctrs::CoalesceCtrs; -use encode_adts::EncodeAdts; use eta_reduce::EtaReduce; use inline::Inline; use pre_reduce::PreReduce; @@ -51,14 +47,6 @@ impl Transform for Book { if passes.eta_reduce { def.eta_reduce(); } - for tree in def.trees_mut() { - if passes.coalesce_ctrs { - tree.coalesce_constructors(); - } - if passes.encode_adts { - tree.encode_scott_adts(); - } - } } if passes.inline { loop { @@ -66,19 +54,12 @@ impl Transform for Book { if inline_changed.is_empty() { break; } - if !(passes.eta_reduce || passes.encode_adts) { + if !passes.eta_reduce { break; } for name in inline_changed { let def = self.get_mut(&name).unwrap(); - if passes.eta_reduce { - def.eta_reduce(); - } - if passes.encode_adts { - for tree in def.trees_mut() { - tree.encode_scott_adts(); - } - } + def.eta_reduce(); } } } @@ -121,8 +102,6 @@ macro_rules! transform_passes { transform_passes! { pre_reduce, - coalesce_ctrs, - encode_adts, eta_reduce, inline, prune,