From 31f75d412990331d939c0301f7401a60717fcd6e Mon Sep 17 00:00:00 2001 From: tjjfvi Date: Tue, 5 Mar 2024 10:07:30 -0500 Subject: [PATCH] initial n-ary prep --- Cargo.lock | 7 ++ Cargo.toml | 1 + src/ast.rs | 233 +++++++++++++++++++++++++++++++----- src/host.rs | 8 +- src/host/calc_labels.rs | 24 ++-- src/host/encode.rs | 216 +++++++++++++++++++++++++++++++++ src/host/encode_def.rs | 107 ----------------- src/host/encode_net.rs | 67 ----------- src/host/readback.rs | 6 +- src/lib.rs | 4 +- src/main.rs | 4 +- src/transform/pre_reduce.rs | 14 +-- src/util.rs | 1 + src/util/apply_tree.rs | 17 +-- src/util/array_vec.rs | 29 +++++ 15 files changed, 486 insertions(+), 252 deletions(-) create mode 100644 src/host/encode.rs delete mode 100644 src/host/encode_def.rs delete mode 100644 src/host/encode_net.rs create mode 100644 src/util/array_vec.rs diff --git a/Cargo.lock b/Cargo.lock index 9aeca9db..a385c4ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -65,6 +65,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "autocfg" version = "1.1.0" @@ -410,6 +416,7 @@ checksum = "379dada1584ad501b383485dd706b8afb7a70fcbc7f4da7d780638a5a6124a60" name = "hvm-core" version = "0.2.19" dependencies = [ + "arrayvec", "clap", "criterion", "insta", diff --git a/Cargo.toml b/Cargo.toml index 529cb961..231c7982 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ panic = "abort" debug = "full" [dependencies] +arrayvec = "0.7.4" clap = { version = "4.5.1", features = ["derive"] } nohash-hasher = { version = "0.2.0" } stacker = "0.1.15" diff --git a/src/ast.rs b/src/ast.rs index 46502e60..75976966 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -10,10 +10,12 @@ //! //! [interaction calculus]: https://en.wikipedia.org/wiki/Interaction_nets#Interaction_calculus +use arrayvec::ArrayVec; + use crate::{ ops::Op, run::Lab, - util::{deref, maybe_grow}, + util::{array_vec, deref, maybe_grow}, }; use std::{collections::BTreeMap, fmt, str::FromStr}; @@ -58,13 +60,21 @@ pub enum Tree { Num { val: u64 }, /// A nilary node, referencing a named net. Ref { nam: String }, - /// A binary interaction combinator. + /// A n-ary interaction combinator. Ctr { /// The label of the combinator. (Combinators with the same label /// annihilate, and combinators with different labels commute.) lab: Lab, - lft: Box, - rgt: Box, + /// 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, }, /// A binary node representing an operation on native integers. /// @@ -81,20 +91,103 @@ pub enum Tree { /// /// The principal port connects to the integer to be matched on. Mat { - /// An auxiliary port; connects to the branches of this match. - /// - /// This should be connected to something of the form: - /// ```text - /// (+value_if_zero (-predecessor_of_number +value_if_non_zero)) - /// ``` - sel: Box, + /// An auxiliary port; connects to the zero branch. + zero: Box, + /// An auxiliary port; connects to the predecessor input of the succ branch. + pred: Box, + /// An auxiliary port; connects to the output of the succ branch. + succ: Box, /// An auxiliary port; connects to the output. - ret: Box, + 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: [Num { val: 123 }] } + /// (* (#123 R) R) = Ctr { lab: 0, ports: [Era, Ctr { lab: 0, ports: [Num { 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 Tree { + #[inline(always)] + pub fn children(&self) -> impl Iterator { + ArrayVec::<_, MAX_ARITY>::into_iter(match self { + Tree::Era | Tree::Num { .. } | Tree::Ref { .. } | Tree::Var { .. } => array_vec::from_array([]), + Tree::Ctr { ports, .. } => array_vec::from_iter(ports), + Tree::Op { rhs, out, .. } => array_vec::from_array([rhs, out]), + Tree::Mat { zero, pred, succ, out } => array_vec::from_array([zero, pred, succ, out]), + Tree::Adt { fields, .. } => array_vec::from_iter(fields), + }) + } + + #[inline(always)] + pub fn children_mut(&mut self) -> impl Iterator { + ArrayVec::<_, MAX_ARITY>::into_iter(match self { + Tree::Era | Tree::Num { .. } | Tree::Ref { .. } | Tree::Var { .. } => array_vec::from_array([]), + Tree::Ctr { ports, .. } => array_vec::from_iter(ports), + Tree::Op { rhs, out, .. } => array_vec::from_array([rhs, out]), + Tree::Mat { zero, pred, succ, out } => array_vec::from_array([zero, pred, succ, out]), + Tree::Adt { fields, .. } => array_vec::from_iter(fields), + }) + } + + pub(crate) fn lab(&self) -> Option { + match self { + Tree::Ctr { lab, ports } if ports.len() >= 2 => Some(*lab), + Tree::Adt { lab, .. } => Some(*lab), + _ => None, + } + } + + pub fn legacy_mat(arms: Tree, out: Tree) -> Option { + let Tree::Ctr { lab: 0, ports } = arms else { None? }; + let Ok([zero, succ]) = <[_; 2]>::try_from(ports) else { None? }; + let Tree::Ctr { lab: 0, ports } = succ else { None? }; + let Ok([pred, succ]) = <[_; 2]>::try_from(ports) else { None? }; + let zero = Box::new(zero); + let pred = Box::new(pred); + let succ = Box::new(succ); + Some(Tree::Mat { zero, pred, succ, out: Box::new(out) }) + } +} + /// The state of the HVMC parser. struct Parser<'i> { /// The remaining characters in the input. An empty string indicates EOF. @@ -147,15 +240,52 @@ impl<'i> Parser<'i> { '{' => self.parse_int()? as Lab, _ => unreachable!(), }; - let lft = Box::new(self.parse_tree()?); - let rgt = Box::new(self.parse_tree()?); - self.consume(match char { - '(' => ")", - '[' => "]", - '{' => "}", + let close = match char { + '(' => ')', + '[' => ']', + '{' => '}', _ => unreachable!(), - })?; - Ok(Tree::Ctr { lab, lft, rgt }) + }; + self.skip_trivia(); + if self.peek_char().is_some_and(|x| x.is_ascii_digit()) { + let variant_index = self.parse_int()?; + self.consume(":")?; + let variant_count = self.parse_int()?; + let mut fields = Vec::new(); + self.skip_trivia(); + while self.peek_char() != Some(close) { + fields.push(self.parse_tree()?); + self.skip_trivia(); + } + self.advance_char(); + 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_char() != Some(close) { + ports.push(self.parse_tree()?); + self.skip_trivia(); + } + self.advance_char(); + if ports.len() > MAX_ARITY { + Err("ctr has too many ports".to_owned())?; + } + Ok(Tree::Ctr { lab, ports }) + } } // Ref = "@" Name Some('@') => { @@ -182,10 +312,20 @@ impl<'i> Parser<'i> { Some('?') => { self.advance_char(); self.consume("<")?; - let sel = Box::new(self.parse_tree()?); - let ret = Box::new(self.parse_tree()?); - self.consume(">")?; - Ok(Tree::Mat { sel, ret }) + let zero = self.parse_tree()?; + let pred = self.parse_tree()?; + self.skip_trivia(); + if self.peek_char() == Some('>') { + self.advance_char(); + Tree::legacy_mat(zero, pred).ok_or_else(|| "invalid legacy match".to_owned()) + } else { + let zero = Box::new(zero); + let pred = Box::new(pred); + let succ = Box::new(self.parse_tree()?); + let out = Box::new(self.parse_tree()?); + self.consume(">")?; + Ok(Tree::Mat { zero, pred, succ, out }) + } } // Var = Name _ => Ok(Tree::Var { nam: self.parse_name()? }), @@ -335,16 +475,49 @@ 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, lft, rgt } => match lab { - 0 => write!(f, "({lft} {rgt})"), - 1 => write!(f, "[{lft} {rgt}]"), - _ => write!(f, "{{{lab} {lft} {rgt}}}"), - }, + 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::Var { nam } => write!(f, "{nam}"), Tree::Ref { nam } => write!(f, "@{nam}"), Tree::Num { val } => write!(f, "#{val}"), Tree::Op { op, rhs, out } => write!(f, "<{op} {rhs} {out}>"), - Tree::Mat { sel, ret } => write!(f, "?<{sel} {ret}>"), + Tree::Mat { zero, pred, succ, out } => write!(f, "?<{zero} {pred} {succ} {out}>"), }) } } diff --git a/src/host.rs b/src/host.rs index ec5bfba5..de52bc29 100644 --- a/src/host.rs +++ b/src/host.rs @@ -12,12 +12,10 @@ use std::{ }; mod calc_labels; -mod encode_def; -mod encode_net; +mod encode; mod readback; use calc_labels::calculate_label_sets; -pub(crate) use encode_def::encode_def; /// Stores a bidirectional mapping between names and runtime defs. #[derive(Default)] @@ -88,9 +86,7 @@ impl Host { // Now that `defs` is fully populated, we can fill in the instructions of // each of the new defs. for (nam, net) in book.iter() { - let data = encode_def(net, |nam| { - Port::new_ref(&self.defs[nam] /* calculate_label_sets already ensures all ref names are in self.defs */) - }); + let data = self.encode_def(net); match self.defs.get_mut(nam).unwrap() { DefRef::Owned(def) => def.downcast_mut::().unwrap().data = data, DefRef::Static(_) => unreachable!(), diff --git a/src/host/calc_labels.rs b/src/host/calc_labels.rs index 13026e5b..79e57ced 100644 --- a/src/host/calc_labels.rs +++ b/src/host/calc_labels.rs @@ -167,27 +167,21 @@ impl<'b, F: FnMut(&'b str) -> LabSet> State<'b, F> { } fn visit_tree(&mut self, tree: &'b Tree, depth: Option, mut out: Option<&mut LabSet>) -> usize { - maybe_grow(move || match tree { - Tree::Era | Tree::Var { .. } | Tree::Num { .. } => usize::MAX, - Tree::Ctr { lab, lft, rgt } => { + maybe_grow(move || { + if let Some(lab) = tree.lab() { if let Some(out) = out.as_deref_mut() { - out.add(*lab); + out.add(lab); } - usize::min(self.visit_tree(lft, depth, out.as_deref_mut()), self.visit_tree(rgt, depth, out.as_deref_mut())) } - Tree::Ref { nam } => { + if let Tree::Ref { nam } = tree { if self.book.contains_key(nam) { - self.visit_def(nam, depth.map(|x| x + 1), out) - } else { - if let Some(out) = out { - out.union(&(self.lookup)(nam)); - } - usize::MAX + return self.visit_def(nam, depth.map(|x| x + 1), out); + } + if let Some(out) = &mut out { + out.union(&(self.lookup)(nam)); } } - Tree::Op { rhs: lft, out: rgt, .. } | Tree::Mat { sel: lft, ret: rgt } => { - usize::min(self.visit_tree(lft, depth, out.as_deref_mut()), self.visit_tree(rgt, depth, out.as_deref_mut())) - } + tree.children().map(|child| self.visit_tree(child, depth, out.as_deref_mut())).fold(usize::MAX, usize::min) }) } } diff --git a/src/host/encode.rs b/src/host/encode.rs new file mode 100644 index 00000000..dae458e6 --- /dev/null +++ b/src/host/encode.rs @@ -0,0 +1,216 @@ +use super::*; +use crate::{ops::Op, run::Lab, util::maybe_grow}; + +impl Host { + /// Converts an ast net to a list of instructions to create the net. + /// + /// `get_def` must return the `Port` corresponding to a given `Ref` name. + pub(crate) fn encode_def(&self, net: &Net) -> InterpretedDef { + let mut def = InterpretedDef { instr: Vec::new(), trgs: 1 }; + let mut state = State { host: self, encoder: &mut def, scope: Default::default() }; + state.visit_net(net, TrgId::new(0)); + state.finish(); + def + } + + /// Encode `tree` directly into `trg`, skipping the intermediate `Def` + /// representation. + pub fn encode_tree(&self, net: &mut run::Net, trg: run::Trg, tree: &Tree) { + let mut state = State { host: self, encoder: net, scope: Default::default() }; + state.visit_tree(tree, trg); + state.finish(); + } + + /// Encode the root of `ast_net` directly into `trg` and encode its redexes + /// into `net` redex list. + pub fn encode_net(&self, net: &mut run::Net, trg: run::Trg, ast_net: &Net) { + let mut state = State { host: self, encoder: net, scope: Default::default() }; + state.visit_net(ast_net, trg); + state.finish(); + } +} + +struct State<'a, E: Encoder> { + host: &'a Host, + encoder: &'a mut E, + scope: HashMap<&'a str, E::Trg>, +} + +impl<'a, E: Encoder> State<'a, E> { + fn finish(self) { + assert!(self.scope.is_empty(), "unbound variables: {:?}", self.scope.keys()); + } + fn visit_net(&mut self, net: &'a Net, trg: E::Trg) { + self.visit_tree(&net.root, trg); + net.redexes.iter().for_each(|(a, b)| self.visit_redex(a, b)); + } + fn visit_redex(&mut self, a: &'a Tree, b: &'a Tree) { + let (port, tree) = match (a, b) { + (Tree::Era, t) | (t, Tree::Era) => (Port::ERA, t), + (Tree::Ref { nam }, t) | (t, Tree::Ref { nam }) => (Port::new_ref(&self.host.defs[nam]), t), + (Tree::Num { val }, t) | (t, Tree::Num { val }) => (Port::new_num(*val), t), + (t, u) => { + let (av, aw, bv, bw) = self.encoder.wires(); + self.visit_tree(t, av); + self.visit_tree(u, bv); + self.encoder.link(aw, bw); + return; + } + }; + let trg = self.encoder.make_const(port); + self.visit_tree(tree, trg); + } + fn visit_tree(&mut self, tree: &'a Tree, trg: E::Trg) { + maybe_grow(move || match tree { + Tree::Era => self.encoder.link_const(trg, Port::ERA), + Tree::Num { val } => self.encoder.link_const(trg, Port::new_num(*val)), + 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(&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(&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(&Tree::Era, x); + r = y; + } + self.encoder.link(l, r); + } + Tree::Op { op, rhs: lft, out: rgt } => { + let (l, r) = self.encoder.op(*op, trg); + self.visit_tree(lft, l); + self.visit_tree(rgt, r); + } + Tree::Mat { zero, pred, succ, out } => { + let (a, o) = self.encoder.mat(trg); + let (z, b) = self.encoder.ctr(0, a); + self.visit_tree(zero, z); + let (p, s) = self.encoder.ctr(0, b); + self.visit_tree(pred, p); + self.visit_tree(succ, s); + self.visit_tree(out, o); + } + Tree::Var { nam } => match self.scope.entry(nam) { + Entry::Occupied(e) => self.encoder.link(e.remove(), trg), + Entry::Vacant(e) => { + e.insert(trg); + } + }, + }) + } +} + +trait Encoder { + type Trg; + fn link_const(&mut self, trg: Self::Trg, port: Port); + fn link(&mut self, a: Self::Trg, b: Self::Trg); + fn make_const(&mut self, port: Port) -> Self::Trg; + fn ctr(&mut self, lab: Lab, trg: Self::Trg) -> (Self::Trg, Self::Trg); + fn op(&mut self, op: Op, trg: Self::Trg) -> (Self::Trg, Self::Trg); + fn op_num(&mut self, op: Op, trg: Self::Trg, rhs: u64) -> Self::Trg; + fn mat(&mut self, trg: Self::Trg) -> (Self::Trg, Self::Trg); + fn wires(&mut self) -> (Self::Trg, Self::Trg, Self::Trg, Self::Trg); +} + +impl InterpretedDef { + fn new_trg_id(&mut self) -> TrgId { + let index = self.trgs; + self.trgs += 1; + TrgId::new(index) + } +} + +impl Encoder for InterpretedDef { + type Trg = TrgId; + fn link_const(&mut self, trg: Self::Trg, port: Port) { + self.instr.push(Instruction::LinkConst { trg, port }); + } + fn link(&mut self, a: Self::Trg, b: Self::Trg) { + self.instr.push(Instruction::Link { a, b }); + } + fn make_const(&mut self, port: Port) -> Self::Trg { + let trg = self.new_trg_id(); + self.instr.push(Instruction::Const { trg, port }); + trg + } + fn ctr(&mut self, lab: Lab, trg: Self::Trg) -> (Self::Trg, Self::Trg) { + let lft = self.new_trg_id(); + let rgt = self.new_trg_id(); + self.instr.push(Instruction::Ctr { lab, trg, lft, rgt }); + (lft, rgt) + } + fn op(&mut self, op: Op, trg: Self::Trg) -> (Self::Trg, Self::Trg) { + let rhs = self.new_trg_id(); + let out = self.new_trg_id(); + self.instr.push(Instruction::Op { op, trg, rhs, out }); + (rhs, out) + } + fn op_num(&mut self, op: Op, trg: Self::Trg, rhs: u64) -> Self::Trg { + let out = self.new_trg_id(); + self.instr.push(Instruction::OpNum { op, trg, rhs, out }); + out + } + fn mat(&mut self, trg: Self::Trg) -> (Self::Trg, Self::Trg) { + let lft = self.new_trg_id(); + let rgt = self.new_trg_id(); + self.instr.push(Instruction::Mat { trg, lft, rgt }); + (lft, rgt) + } + fn wires(&mut self) -> (Self::Trg, Self::Trg, Self::Trg, Self::Trg) { + let av = self.new_trg_id(); + let aw = self.new_trg_id(); + let bv = self.new_trg_id(); + let bw = self.new_trg_id(); + self.instr.push(Instruction::Wires { av, aw, bv, bw }); + (av, aw, bv, bw) + } +} + +impl<'a, M: Mode> Encoder for run::Net<'a, M> { + type Trg = run::Trg; + fn link_const(&mut self, trg: Self::Trg, port: Port) { + self.link_trg_port(trg, port) + } + fn link(&mut self, a: Self::Trg, b: Self::Trg) { + self.link_trg(a, b) + } + fn make_const(&mut self, port: Port) -> Self::Trg { + run::Trg::port(port) + } + fn ctr(&mut self, lab: Lab, trg: Self::Trg) -> (Self::Trg, Self::Trg) { + self.do_ctr(lab, trg) + } + fn op(&mut self, op: Op, trg: Self::Trg) -> (Self::Trg, Self::Trg) { + self.do_op(op, trg) + } + fn op_num(&mut self, op: Op, trg: Self::Trg, rhs: u64) -> Self::Trg { + self.do_op_num(op, trg, rhs) + } + fn mat(&mut self, trg: Self::Trg) -> (Self::Trg, Self::Trg) { + self.do_mat(trg) + } + fn wires(&mut self) -> (Self::Trg, Self::Trg, Self::Trg, Self::Trg) { + self.do_wires() + } +} diff --git a/src/host/encode_def.rs b/src/host/encode_def.rs deleted file mode 100644 index a6748e1c..00000000 --- a/src/host/encode_def.rs +++ /dev/null @@ -1,107 +0,0 @@ -use crate::util::maybe_grow; - -use super::*; - -/// Converts an ast net to a list of instructions to create the net. -/// -/// `get_def` must return the `Port` corresponding to a given `Ref` name. -pub(crate) fn encode_def Port>(net: &Net, get_def: F) -> InterpretedDef { - let mut state = - State { get_def, scope: Default::default(), instr: Default::default(), end: Default::default(), next_index: 1 }; - - state.visit_tree(&net.root, TrgId::new(0)); - - net.redexes.iter().for_each(|(a, b)| state.visit_redex(a, b)); - - assert!(state.scope.is_empty(), "unbound variables: {:?}", state.scope.keys()); - - state.instr.append(&mut state.end); - - InterpretedDef { instr: state.instr, trgs: state.next_index } -} - -struct State<'a, F: FnMut(&str) -> Port> { - get_def: F, - scope: HashMap<&'a str, TrgId>, - instr: Vec, - end: Vec, - next_index: usize, -} - -impl<'a, F: FnMut(&str) -> Port> State<'a, F> { - fn id(&mut self) -> TrgId { - let i = self.next_index; - self.next_index += 1; - TrgId::new(i) - } - fn visit_redex(&mut self, a: &'a Tree, b: &'a Tree) { - let (port, tree) = match (a, b) { - (Tree::Era, t) | (t, Tree::Era) => (Port::ERA, t), - (Tree::Ref { nam }, t) | (t, Tree::Ref { nam }) => ((self.get_def)(&nam), t), - (Tree::Num { val }, t) | (t, Tree::Num { val }) => (Port::new_num(*val), t), - (t, u) => { - let av = self.id(); - let aw = self.id(); - let bv = self.id(); - let bw = self.id(); - self.instr.push(Instruction::Wires { av, aw, bv, bw }); - self.end.push(Instruction::Link { a: aw, b: bw }); - self.visit_tree(t, av); - self.visit_tree(u, bv); - return; - } - }; - let trg = self.id(); - self.instr.push(Instruction::Const { port, trg }); - self.visit_tree(tree, trg); - } - fn visit_tree(&mut self, tree: &'a Tree, trg: TrgId) { - maybe_grow(move || match tree { - Tree::Era => { - self.instr.push(Instruction::LinkConst { trg, port: Port::ERA }); - } - Tree::Ref { nam } => { - self.instr.push(Instruction::LinkConst { trg, port: (self.get_def)(&nam) }); - } - Tree::Num { val } => { - self.instr.push(Instruction::LinkConst { trg, port: Port::new_num(*val) }); - } - Tree::Var { nam } => match self.scope.entry(nam) { - Entry::Occupied(e) => { - let other = e.remove(); - self.instr.push(Instruction::Link { a: other, b: trg }); - } - Entry::Vacant(e) => { - e.insert(trg); - } - }, - Tree::Ctr { lab, lft, rgt } => { - let l = self.id(); - let r = self.id(); - self.instr.push(Instruction::Ctr { lab: *lab, trg, lft: l, rgt: r }); - self.visit_tree(lft, l); - self.visit_tree(rgt, r); - } - Tree::Op { op, rhs, out } => { - if let Tree::Num { val } = &**rhs { - let o = self.id(); - self.instr.push(Instruction::OpNum { op: *op, rhs: *val, trg, out: o }); - self.visit_tree(out, o); - } else { - let r = self.id(); - let o = self.id(); - self.instr.push(Instruction::Op { op: *op, trg, rhs: r, out: o }); - self.visit_tree(rhs, r); - self.visit_tree(out, o); - } - } - Tree::Mat { sel, ret } => { - let l = self.id(); - let r = self.id(); - self.instr.push(Instruction::Mat { trg, lft: l, rgt: r }); - self.visit_tree(sel, l); - self.visit_tree(ret, r); - } - }) - } -} diff --git a/src/host/encode_net.rs b/src/host/encode_net.rs deleted file mode 100644 index 9a6c7054..00000000 --- a/src/host/encode_net.rs +++ /dev/null @@ -1,67 +0,0 @@ -//! Code for directly encoding a [`hvmc::ast::Net`] or a [`hvmc::ast::Tree`] -//! into a [`hvmc::run::Net`] - -use std::collections::{hash_map::Entry, HashMap}; - -use crate::{ - ast::{Net, Tree}, - host::Host, - run::{self, Mode, Port, Trg}, -}; - -impl Host { - /// Encode `tree` directly into `trg`, skipping the intermediate `Def` - /// representation. - pub fn encode_tree(&self, net: &mut run::Net, trg: Trg, tree: &Tree) { - EncodeState { host: self, net, vars: Default::default() }.encode(trg, tree); - } - /// Encode the root of `ast_net` directly into `trg` and encode its redexes - /// into `net` redex list. - pub fn encode_net(&self, net: &mut run::Net, trg: Trg, ast_net: &Net) { - let mut state = EncodeState { host: self, net, vars: Default::default() }; - for (l, r) in &ast_net.redexes { - let (ap, a, bp, b) = state.net.do_wires(); - state.encode(ap, l); - state.encode(bp, r); - state.net.link_trg(a, b); - } - state.encode(trg, &ast_net.root); - } -} - -struct EncodeState<'c, 'n, M: Mode> { - host: &'c Host, - net: &'c mut run::Net<'n, M>, - vars: HashMap<&'c str, Trg>, -} - -impl<'c, 'n, M: Mode> EncodeState<'c, 'n, M> { - fn encode(&mut self, trg: Trg, tree: &'c Tree) { - match tree { - Tree::Era => self.net.link_trg_port(trg, Port::ERA), - Tree::Num { val } => self.net.link_trg_port(trg, Port::new_num(*val)), - Tree::Ref { nam } => self.net.link_trg_port(trg, Port::new_ref(&self.host.defs[nam])), - Tree::Ctr { lab, lft, rgt } => { - let (l, r) = self.net.do_ctr(*lab, trg); - self.encode(l, lft); - self.encode(r, rgt); - } - Tree::Op { op, rhs: lft, out: rgt } => { - let (l, r) = self.net.do_op(*op, trg); - self.encode(l, lft); - self.encode(r, rgt); - } - Tree::Mat { sel, ret } => { - let (s, r) = self.net.do_mat(trg); - self.encode(s, sel); - self.encode(r, ret); - } - Tree::Var { nam } => match self.vars.entry(nam) { - Entry::Occupied(e) => self.net.link_trg(e.remove(), trg), - Entry::Vacant(e) => { - e.insert(trg); - } - }, - } - } -} diff --git a/src/host/readback.rs b/src/host/readback.rs index 08a5c6db..72126391 100644 --- a/src/host/readback.rs +++ b/src/host/readback.rs @@ -64,11 +64,13 @@ impl<'a> ReadbackState<'a> { } Tag::Ctr => { let node = port.traverse_node(); - Tree::Ctr { lab: node.lab, lft: Box::new(self.read_wire(node.p1)), rgt: Box::new(self.read_wire(node.p2)) } + Tree::Ctr { lab: node.lab, ports: vec![self.read_wire(node.p1), self.read_wire(node.p2)] } } Tag::Mat => { let node = port.traverse_node(); - Tree::Mat { sel: Box::new(self.read_wire(node.p1)), ret: Box::new(self.read_wire(node.p2)) } + let arms = self.read_wire(node.p1); + let out = self.read_wire(node.p2); + Tree::legacy_mat(arms, out).expect("invalid mat node") } }) } diff --git a/src/lib.rs b/src/lib.rs index 3084efd7..b38219eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ -#![feature(const_type_id, extern_types, inline_const, new_uninit)] +#![feature(const_type_id, extern_types, inline_const, generic_const_exprs, new_uninit)] #![cfg_attr(feature = "trace", feature(const_type_name))] -#![allow(non_snake_case)] +#![allow(non_snake_case, incomplete_features)] pub mod ast; pub mod compile; diff --git a/src/main.rs b/src/main.rs index 1acba438..eb4936eb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -374,8 +374,7 @@ fn compile_executable(target: &str, host: &host::Host) -> Result<(), io::Error> fuzz host { calc_labels - encode_def - encode_net + encode readback } lib @@ -401,6 +400,7 @@ fn compile_executable(target: &str, host: &host::Host) -> Result<(), io::Error> } util { apply_tree + array_vec bi_enum create_var deref diff --git a/src/transform/pre_reduce.rs b/src/transform/pre_reduce.rs index d1e9261b..d1f11833 100644 --- a/src/transform/pre_reduce.rs +++ b/src/transform/pre_reduce.rs @@ -21,7 +21,7 @@ use std::{ use crate::{ ast::{Book, Net, Tree}, - host::{encode_def, DefRef, Host}, + host::{DefRef, Host}, run::{self, Def, Heap, InterpretedDef, LabSet, Rewrites}, util::maybe_grow, }; @@ -108,15 +108,11 @@ struct State<'a> { impl<'a> State<'a> { fn visit_tree(&mut self, tree: &Tree) { - maybe_grow(move || match tree { - Tree::Era | Tree::Num { .. } | Tree::Var { .. } => (), - Tree::Ref { nam } => { + maybe_grow(move || { + if let Tree::Ref { nam } = tree { self.pre_reduce(nam); } - Tree::Ctr { lft, rgt, .. } | Tree::Op { rhs: lft, out: rgt, .. } | Tree::Mat { sel: lft, ret: rgt } => { - self.visit_tree(lft); - self.visit_tree(rgt); - } + tree.children().for_each(|child| self.visit_tree(child)) }) } fn visit_net(&mut self, net: &Net) { @@ -147,7 +143,7 @@ impl<'a> State<'a> { let net = self.host.readback(&mut rt); // Mutate the host in-place with the pre-reduced net. - let instr = encode_def(&net, |nam| run::Port::new_ref(&self.host.defs[nam])); + let instr = self.host.encode_def(&net); if let DefRef::Owned(def_box) = self.host.defs.get_mut(nam).unwrap() { let interpreted_def: &mut crate::run::Def = def_box.downcast_mut().unwrap(); interpreted_def.data = instr; diff --git a/src/util.rs b/src/util.rs index 1e8e81cc..7090843d 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,4 +1,5 @@ mod apply_tree; +pub(crate) mod array_vec; mod bi_enum; mod create_var; mod deref; diff --git a/src/util/apply_tree.rs b/src/util/apply_tree.rs index bb7a68ab..61dd221e 100644 --- a/src/util/apply_tree.rs +++ b/src/util/apply_tree.rs @@ -16,7 +16,7 @@ impl Net { let fresh_str = create_var(fresh + 1); let fun = core::mem::take(&mut self.root); - let app = Tree::Ctr { lab: 0, lft: Box::new(arg), rgt: Box::new(Tree::Var { nam: fresh_str.clone() }) }; + let app = Tree::Ctr { lab: 0, ports: vec![arg, Tree::Var { nam: fresh_str.clone() }] }; self.root = Tree::Var { nam: fresh_str }; self.redexes.push((fun, app)); } @@ -37,18 +37,11 @@ impl Tree { /// This function can be called multiple times with many trees to /// ensure that `fresh` does not conflict with any of them. pub(crate) fn ensure_no_conflicts(&self, fresh: &mut usize) { - match self { - Tree::Var { nam } => { - if let Some(var_num) = var_to_num(nam) { - *fresh = (*fresh).max(var_num); - } + if let Tree::Var { nam } = self { + if let Some(var_num) = var_to_num(nam) { + *fresh = (*fresh).max(var_num); } - // Recurse on children - Tree::Ctr { lft, rgt, .. } | Tree::Op { rhs: lft, out: rgt, .. } | Tree::Mat { sel: lft, ret: rgt } => { - lft.ensure_no_conflicts(fresh); - rgt.ensure_no_conflicts(fresh); - } - Tree::Era | Tree::Num { .. } | Tree::Ref { .. } => {} } + self.children().for_each(|child| child.ensure_no_conflicts(fresh)); } } diff --git a/src/util/array_vec.rs b/src/util/array_vec.rs new file mode 100644 index 00000000..824f5212 --- /dev/null +++ b/src/util/array_vec.rs @@ -0,0 +1,29 @@ +use arrayvec::ArrayVec; + +struct Assert; + +trait IsTrue {} + +impl IsTrue for Assert {} + +#[allow(private_bounds)] +pub(crate) fn from_array(array: [T; LEN]) -> ArrayVec +where + Assert<{ LEN <= CAP }>: IsTrue, +{ + let mut vec = ArrayVec::new(); + unsafe { + for el in array { + vec.push_unchecked(el) + } + } + vec +} + +pub(crate) fn from_iter(iter: impl IntoIterator) -> ArrayVec { + let mut vec = ArrayVec::new(); + for item in iter { + vec.push(item); + } + vec +}