encode terms directly to hvmc nets

This commit is contained in:
tjjfvi 2024-04-18 11:39:23 -04:00
parent d87c0919bd
commit 105633153e
No known key found for this signature in database
GPG Key ID: 77F042BA4D0E14C8
9 changed files with 179 additions and 347 deletions

6
Cargo.lock generated
View File

@ -209,6 +209,7 @@ dependencies = [
"insta",
"interner",
"itertools",
"loaned",
"parking_lot",
"stacker",
"stdext",
@ -270,6 +271,11 @@ version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "loaned"
version = "0.0.0"
source = "git+https://github.com/tjjfvi/loaned#9b172def001da8ac4e76aa2fe62d27acb38db49b"
[[package]]
name = "lock_api"
version = "0.4.11"

View File

@ -27,6 +27,7 @@ hvm-core = { git = "https://github.com/HigherOrderCO/hvm-core.git" }
indexmap = "2.2.3"
interner = "0.2.1"
itertools = "0.11.0"
loaned = { git = "https://github.com/tjjfvi/loaned", version = "0.0.0" }
parking_lot = "0.12.1"
stacker = "0.1"

View File

@ -1,9 +1,8 @@
use crate::{
builtins::util::{AsDefFunction, FunctionLike, FunctionLikeHosted},
net::net_to_hvmc::net_to_hvmc,
readback_hvmc,
term::{
term_to_net::{term_to_compat_net, Labels},
term_to_net::{term_to_net, Labels},
AdtEncoding, Book, Term,
},
};
@ -94,8 +93,7 @@ pub(crate) fn add_fs_defs(
}
},
};
let result = term_to_compat_net(&result, &mut labels);
match net_to_hvmc(&result) {
match term_to_net(&result, &mut labels) {
Ok(result) => {
// Return λx (x result)
let app = net.create_node(hvmc::run::Tag::Ctr, 0);
@ -131,8 +129,7 @@ pub(crate) fn add_fs_defs(
Err(s) => Term::encode_err(Term::encode_str(&s)),
};
let mut labels = (*self.readback_data.labels).clone();
let result = term_to_compat_net(&result, &mut labels);
if let Ok(result) = net_to_hvmc(&result) {
if let Ok(result) = term_to_net(&result, &mut labels) {
self.readback_data.host.lock().encode_net(net, Trg::port(app.p1), &result);
} else {
eprintln!("{VICIOUS_CIRCLE_MSG}");

View File

@ -24,9 +24,8 @@ pub(crate) fn make_query_def(host: Arc<Mutex<Host>>, labels: Arc<Labels>) -> Def
let buf = buf.strip_suffix('\n').unwrap_or(&buf);
let text = Term::encode_ok(Term::encode_str(buf));
let mut labs = (*self.labels).clone();
let text = crate::term::term_to_net::term_to_compat_net(&text, &mut labs);
net.link_wire_port(output, app_node.p2);
let Ok(text) = crate::net::net_to_hvmc::net_to_hvmc(&text) else {
let Ok(text) = crate::term::term_to_net::term_to_net(&text, &mut labs) else {
net.link_port_port(Port::ERA, app_node.p1);
net.link_wire_port(input, app_node.p0);
return;

View File

@ -13,7 +13,7 @@ use hvmc_net::{
mutual_recursion,
pre_reduce::{pre_reduce, MAX_REWRITES_DEFAULT},
};
use net::{hvmc_to_net::hvmc_to_net, net_to_hvmc::nets_to_hvmc};
use net::hvmc_to_net::hvmc_to_net;
use parking_lot::Mutex;
use std::{sync::Arc, time::Instant};
use term::{book_to_nets, net_to_term::net_to_term, term_to_net::Labels, AdtEncoding, Book, Ctx, Name, Term};
@ -47,9 +47,7 @@ pub fn compile_book(
args: Option<Vec<Term>>,
) -> Result<CompileResult, Diagnostics> {
let mut diagnostics = desugar_book(book, opts.clone(), diagnostics_cfg, args)?;
let (nets, labels) = book_to_nets(book);
let mut core_book = nets_to_hvmc(nets, &mut diagnostics)?;
let (mut core_book, labels) = book_to_nets(book, &mut diagnostics)?;
if opts.eta {
core_book.values_mut().for_each(Net::eta_reduce);

View File

@ -1,5 +1,4 @@
pub mod hvmc_to_net;
pub mod net_to_hvmc;
use crate::term::Name;
pub type HvmlLab = u16;
@ -48,7 +47,7 @@ pub enum CtrKind {
}
impl CtrKind {
fn to_lab(self) -> HvmlLab {
pub fn to_lab(self) -> HvmlLab {
#[allow(clippy::identity_op)]
match self {
CtrKind::Con(None) => 0,

View File

@ -1,211 +0,0 @@
use super::{CtrKind, INet, NodeId, NodeKind, Port, ROOT};
use crate::{
diagnostics::{Diagnostics, ToStringVerbose},
term::num_to_name,
};
use hvmc::ast::{Book, Net, Tree};
use std::collections::{HashMap, HashSet};
#[derive(Debug, Clone)]
pub struct ViciousCycleErr;
/// Converts the inet-encoded definitions into an hvmc AST Book.
pub fn nets_to_hvmc(nets: HashMap<String, INet>, info: &mut Diagnostics) -> Result<Book, Diagnostics> {
info.start_pass();
let mut book = Book::default();
for (name, inet) in nets {
let res = net_to_hvmc(&inet);
if let Some(net) = info.take_inet_err(res, name.clone()) {
book.insert(name, net);
}
}
info.fatal(book)
}
/// Convert an inet-encoded definition into an hvmc AST inet.
pub fn net_to_hvmc(inet: &INet) -> Result<Net, ViciousCycleErr> {
let (net_root, net_redexes) = get_tree_roots(inet)?;
let mut port_to_var_id: HashMap<Port, VarId> = HashMap::new();
let root = if let Some(net_root) = net_root {
// If there is a root tree connected to the root node
net_tree_to_hvmc_tree(inet, net_root, &mut port_to_var_id)
} else {
// If the root node points to some aux port (application)
port_to_var_id.insert(inet.enter_port(ROOT), 0);
Tree::Var { nam: num_to_name(0) }
};
let mut redexes = vec![];
for [root0, root1] in net_redexes {
let root0 = net_tree_to_hvmc_tree(inet, root0, &mut port_to_var_id);
let root1 = net_tree_to_hvmc_tree(inet, root1, &mut port_to_var_id);
redexes.push((root0, root1));
}
Ok(Net { root, redexes })
}
fn net_tree_to_hvmc_tree(inet: &INet, tree_root: NodeId, port_to_var_id: &mut HashMap<Port, VarId>) -> Tree {
match &inet.node(tree_root).kind {
NodeKind::Era => Tree::Era,
NodeKind::Ctr(kind) => Tree::Ctr {
lab: kind.to_lab(),
ports: vec![
var_or_subtree(inet, Port(tree_root, 1), port_to_var_id),
var_or_subtree(inet, Port(tree_root, 2), port_to_var_id),
],
},
NodeKind::Ref { def_name } => Tree::Ref { nam: def_name.to_string() },
NodeKind::Num { val } => Tree::Num { val: *val as i64 },
NodeKind::Op2 { opr } => Tree::Op {
op: *opr,
rhs: Box::new(var_or_subtree(inet, Port(tree_root, 1), port_to_var_id)),
out: Box::new(var_or_subtree(inet, Port(tree_root, 2), port_to_var_id)),
},
NodeKind::Mat => {
let node = inet.node(inet.enter_port(Port(tree_root, 1)).node());
if node.kind != NodeKind::Ctr(CtrKind::Con(None)) {
panic!("hvm-lang produced an INet where the first port is not a Con node. This is not supported.");
}
Tree::Mat {
zero: Box::new(var_or_subtree(inet, inet.enter_port(node.aux1), port_to_var_id)),
succ: Box::new(var_or_subtree(inet, inet.enter_port(node.aux2), port_to_var_id)),
out: Box::new(var_or_subtree(inet, Port(tree_root, 2), port_to_var_id)),
}
}
NodeKind::Rot => unreachable!(),
}
}
fn var_or_subtree(inet: &INet, src_port: Port, port_to_var_id: &mut HashMap<Port, VarId>) -> Tree {
let dst_port = inet.enter_port(src_port);
if dst_port.slot() == 0 {
// Subtree
net_tree_to_hvmc_tree(inet, dst_port.node(), port_to_var_id)
} else {
// Var
if let Some(&var_id) = port_to_var_id.get(&src_port) {
// Previously found var
Tree::Var { nam: num_to_name(var_id) }
} else {
// New var
let var_id = port_to_var_id.len() as VarId;
port_to_var_id.insert(dst_port, var_id);
Tree::Var { nam: num_to_name(var_id) }
}
}
}
type VarId = NodeId;
/// Finds the roots of all the trees in the inet.
/// Returns them as the root of the root tree and the active pairs of the net.
/// Active pairs are found by a right-to-left, depth-first search.
fn get_tree_roots(inet: &INet) -> Result<(Option<NodeId>, Vec<[NodeId; 2]>), ViciousCycleErr> {
let mut redex_roots: Vec<[NodeId; 2]> = vec![];
let mut movements: Vec<Movement> = vec![];
let mut root_set = HashSet::from([ROOT.node()]);
let mut explored_nodes = vec![false; inet.nodes.len()];
// Start by checking the root tree (if any)
explored_nodes[ROOT.node() as usize] = true;
let root_link = inet.enter_port(ROOT);
let root_node = root_link.node();
let root_tree_root = if root_link.slot() == 0 {
// If the root node is connected to a main port, we have a root tree
movements.push(Movement::Down(root_node));
root_set.insert(root_node);
Some(root_node)
} else {
// Otherwise, root node connected to an aux port, no root tree.
movements.push(Movement::Side(root_node));
None
};
// Traverse the net
while let Some(movement) = movements.pop() {
match movement {
Movement::Down(node_id) => explore_down_link(inet, node_id, &mut explored_nodes, &mut movements),
Movement::Side(node_id) => {
explore_side_link(inet, node_id, &mut movements, &mut redex_roots, &mut root_set)?;
}
}
}
Ok((root_tree_root, redex_roots))
}
enum Movement {
Down(NodeId),
Side(NodeId),
}
fn explore_down_link(
inet: &INet,
node_id: NodeId,
explored_nodes: &mut [bool],
movements: &mut Vec<Movement>,
) {
// Don't go down already explored nodes.
if !explored_nodes[node_id as usize] {
explored_nodes[node_id as usize] = true;
for down_slot in [1, 2] {
let down_port = inet.enter_port(Port(node_id, down_slot));
let movement = if down_port.slot() == 0 || down_port == ROOT {
// If this down-link is to a main port, this is a node of the same tree
Movement::Down(down_port.node())
} else {
// Otherwise it's a side-link
Movement::Side(down_port.node())
};
movements.push(movement);
}
}
}
fn explore_side_link(
inet: &INet,
node_id: NodeId,
movements: &mut Vec<Movement>,
redex_roots: &mut Vec<[NodeId; 2]>,
root_set: &mut HashSet<NodeId>,
) -> Result<(), ViciousCycleErr> {
let new_roots = go_up_tree(inet, node_id)?;
// If this is a new tree, explore it downwards
if !root_set.contains(&new_roots[0]) && !root_set.contains(&new_roots[1]) {
movements.push(Movement::Down(new_roots[0]));
movements.push(Movement::Down(new_roots[1]));
redex_roots.push(new_roots);
root_set.insert(new_roots[0]);
root_set.insert(new_roots[1]);
}
Ok(())
}
/// Goes up a node tree, starting from some given node.
/// Returns the active pair at the root of this tree.
fn go_up_tree(inet: &INet, start_node: NodeId) -> Result<[NodeId; 2], ViciousCycleErr> {
let mut explored_nodes = HashSet::new();
let mut cur_node = start_node;
loop {
if !explored_nodes.insert(cur_node) {
return Err(ViciousCycleErr);
}
let up = inet.enter_port(Port(cur_node, 0));
if up.slot() == 0 || up == ROOT {
return Ok([up.node(), cur_node]);
}
cur_node = up.node();
}
}
impl ToStringVerbose for ViciousCycleErr {
fn to_string_verbose(&self, _verbose: bool) -> String {
"Found term that compiles into an inet with a vicious cycle".into()
}
}

View File

@ -20,7 +20,7 @@ pub mod transform;
pub use hvmc::ops::{IntOp, Op, Ty as OpType};
pub use net_to_term::{net_to_term, ReadbackError};
pub use term_to_net::{book_to_nets, term_to_compat_net};
pub use term_to_net::{book_to_nets, term_to_net};
pub static STRINGS: GlobalPool<String> = GlobalPool::new();
#[derive(Debug)]

View File

@ -1,11 +1,7 @@
use crate::{
diagnostics::{Diagnostics, ToStringVerbose},
maybe_grow,
net::{
CtrKind::*,
INet,
NodeKind::{self, *},
Port, ROOT,
},
net::CtrKind::{self, *},
term::{Book, Name, Pattern, Tag, Term},
};
use std::{
@ -13,79 +9,117 @@ use std::{
ops::{Index, IndexMut},
};
use super::FanKind;
use hvmc::ast::{Net, Tree};
use loaned::LoanedMut;
pub fn book_to_nets(book: &Book) -> (HashMap<String, INet>, Labels) {
let mut nets = HashMap::new();
use super::{num_to_name, FanKind};
#[derive(Debug, Clone)]
pub struct ViciousCycleErr;
pub fn book_to_nets(book: &Book, info: &mut Diagnostics) -> Result<(hvmc::ast::Book, Labels), Diagnostics> {
info.start_pass();
let mut hvmc = hvmc::ast::Book::default();
let mut labels = Labels::default();
let main = book.entrypoint.as_ref().unwrap();
for def in book.defs.values() {
for rule in def.rules.iter() {
let net = term_to_compat_net(&rule.body, &mut labels);
let net = term_to_net(&rule.body, &mut labels);
let name = if def.name == *main { book.hvmc_entrypoint().to_string() } else { def.name.0.to_string() };
nets.insert(name, net);
if let Some(net) = info.take_inet_err(net, name.clone()) {
hvmc.insert(name, net);
}
}
}
labels.con.finish();
labels.dup.finish();
(nets, labels)
Ok((hvmc, labels))
}
/// Converts an IC term into an IC net.
pub fn term_to_compat_net(term: &Term, labels: &mut Labels) -> INet {
/// Converts an LC term into an IC net.
pub fn term_to_net(term: &Term, labels: &mut Labels) -> Result<Net, ViciousCycleErr> {
let mut net = Net::default();
let mut state = EncodeTermState {
inet: Default::default(),
scope: Default::default(),
vars: Default::default(),
global_vars: Default::default(),
wires: Default::default(),
redexes: Default::default(),
name_idx: 0,
created_nodes: 0,
labels,
};
state.encode_term(term, Trg::Port(ROOT));
state.encode_term(term, Place::Hole(&mut net.root));
LoanedMut::from(std::mem::take(&mut state.redexes)).place(&mut net.redexes);
state.inet
let EncodeTermState { created_nodes, .. } = { state };
let found_nodes = net.trees().map(count_nodes).sum::<usize>();
if created_nodes != found_nodes {
Err(ViciousCycleErr)?
}
Ok(net)
}
#[derive(Debug)]
struct EncodeTermState<'a> {
inet: INet,
scope: HashMap<Name, Vec<Trg>>,
global_vars: HashMap<Name, Trg>,
labels: &'a mut Labels,
vars: Vec<Option<Trg>>,
struct EncodeTermState<'t, 'l> {
// inet: INet,
scope: HashMap<Name, Vec<Place<'t>>>,
global_vars: HashMap<Name, Place<'t>>,
labels: &'l mut Labels,
wires: Vec<Option<Place<'t>>>,
name_idx: u64,
redexes: Vec<LoanedMut<'t, (Tree, Tree)>>,
created_nodes: usize,
}
#[derive(Debug, Clone, Copy)]
enum Trg {
Port(Port),
Var(usize),
fn count_nodes(tree: &Tree) -> usize {
maybe_grow(|| {
usize::from(tree.children().next().is_some()) + tree.children().map(count_nodes).sum::<usize>()
})
}
impl EncodeTermState<'_> {
#[derive(Debug)]
enum Place<'t> {
Tree(LoanedMut<'t, Tree>),
Hole(&'t mut Tree),
Wire(usize),
}
impl<'t, 'l> EncodeTermState<'t, 'l> {
/// Adds a subterm connected to `up` to the `inet`.
/// `scope` has the current variable scope.
/// `vars` has the information of which ports the variables are declared and used in.
/// `global_vars` has the same information for global lambdas. Must be linked outside this function.
/// Expects variables to be linear, refs to be stored as Refs and all names to be bound.
fn encode_term(&mut self, term: &Term, up: Trg) {
fn encode_term(&mut self, term: &Term, up: Place<'t>) {
maybe_grow(|| {
match term {
Term::Lnk { nam } => self.link_global(nam, up),
Term::Ref { nam } => self.link(up, Place::Tree(LoanedMut::new(Tree::Ref { nam: nam.to_string() }))),
Term::Era => self.link(up, Place::Tree(LoanedMut::new(Tree::Era))),
Term::Num { val } => self.link(up, Place::Tree(LoanedMut::new(Tree::Num { val: *val as i64 }))),
// A lambda becomes to a con node. Ports:
// - 0: points to where the lambda occurs.
// - 1: points to the lambda variable.
// - 2: points to the lambda body.
// core: (var_use bod)
Term::Lam { tag, pat, bod } => {
let fun = self.inet.new_node(Ctr(Con(self.labels.con.generate(tag))));
self.link(up, Trg::Port(Port(fun, 0)));
self.encode_pat(pat, Trg::Port(Port(fun, 1)));
self.encode_term(bod, Trg::Port(Port(fun, 2)));
let kind = Con(self.labels.con.generate(tag));
let node = self.new_ctr(kind);
self.link(up, node.0);
self.encode_pat(pat, node.1);
self.encode_term(bod, node.2);
}
// An application becomes to a con node too. Ports:
// - 0: points to the function being applied.
@ -93,10 +127,11 @@ impl EncodeTermState<'_> {
// - 2: points to where the application occurs.
// core: & fun ~ (arg ret) (fun not necessarily main port)
Term::App { tag, fun, arg } => {
let app = self.inet.new_node(Ctr(Con(self.labels.con.generate(tag))));
self.encode_term(fun, Trg::Port(Port(app, 0)));
self.encode_term(arg, Trg::Port(Port(app, 1)));
self.link(up, Trg::Port(Port(app, 2)));
let kind = Con(self.labels.con.generate(tag));
let node = self.new_ctr(kind);
self.encode_term(fun, node.0);
self.encode_term(arg, node.1);
self.link(up, node.2);
}
// core: & arg ~ ?<(zero succ) ret>
Term::Swt { arg, bnd: _, with, pred: _, arms: rules } => {
@ -104,20 +139,17 @@ impl EncodeTermState<'_> {
assert!(with.is_empty());
assert!(rules.len() == 2);
let mat = self.inet.new_node(Mat);
self.created_nodes += 1;
let ((zero, succ, out), node) =
LoanedMut::loan_with(Tree::Mat { zero: hole(), succ: hole(), out: hole() }, |t, l| {
let Tree::Mat { zero, succ, out, .. } = t else { unreachable!() };
(l.loan_mut(zero), l.loan_mut(succ), l.loan_mut(out))
});
self.encode_term(arg, Trg::Port(Port(mat, 0)));
let zero = &rules[0];
let succ = &rules[1];
let sel = self.inet.new_node(Ctr(Con(None)));
self.inet.link(Port(sel, 0), Port(mat, 1));
self.encode_term(zero, Trg::Port(Port(sel, 1)));
self.encode_term(succ, Trg::Port(Port(sel, 2)));
self.link(up, Trg::Port(Port(mat, 2)));
self.encode_term(arg, Place::Tree(node));
self.encode_term(&rules[0], Place::Hole(zero));
self.encode_term(&rules[1], Place::Hole(succ));
self.link(up, Place::Hole(out));
}
Term::Var { nam } => {
// We assume this variable to be valid, bound and correctly scoped.
@ -129,125 +161,126 @@ impl EncodeTermState<'_> {
let down = self.scope.get_mut(nam).unwrap().pop().unwrap();
self.link(up, down);
}
Term::Lnk { nam } => {
self.link_global(nam, up);
}
// core: @def_id
Term::Ref { nam: def_name } => {
let node = self.inet.new_node(Ref { def_name: def_name.clone() });
self.inet.link(Port(node, 1), Port(node, 2));
self.link(up, Trg::Port(Port(node, 0)));
}
Term::Let { pat, val, nxt } => {
let var = self.new_var();
self.encode_term(val, Trg::Var(var));
self.encode_pat(pat, Trg::Var(var));
let wire = self.new_wire();
self.encode_term(val, Place::Wire(wire));
self.encode_pat(pat, Place::Wire(wire));
self.encode_term(nxt, up);
}
Term::Fan { fan, tag, els } => {
let kind = self.fan_kind(fan, tag);
self.make_node_list(
kind,
up,
els.iter().map(|el| |slf: &mut Self, up| slf.encode_term(el, up)),
);
}
Term::Era => {
let era = self.inet.new_node(Era);
self.inet.link(Port(era, 1), Port(era, 2));
self.link(up, Trg::Port(Port(era, 0)));
}
// core: #val
Term::Num { val } => {
let node = self.inet.new_node(Num { val: *val });
// This representation only has nodes of arity 2, so we connect the two aux ports that are not used.
self.inet.link(Port(node, 1), Port(node, 2));
self.link(up, Trg::Port(Port(node, 0)));
self.make_node_list(kind, up, els.iter().map(|el| |slf: &mut Self, up| slf.encode_term(el, up)));
}
// core: & fst ~ <op snd ret>
Term::Opx { opr, fst, snd } => {
let opx = self.inet.new_node(Op2 { opr: *opr });
self.encode_term(fst, Trg::Port(Port(opx, 0)));
self.encode_term(snd, Trg::Port(Port(opx, 1)));
self.link(up, Trg::Port(Port(opx, 2)));
self.created_nodes += 1;
let ((rhs, out), lhs) =
LoanedMut::loan_with(Tree::Op { op: *opr, rhs: hole(), out: hole() }, |t, l| {
let Tree::Op { rhs, out, .. } = t else { unreachable!() };
(l.loan_mut(rhs), l.loan_mut(out))
});
self.encode_term(fst, Place::Tree(lhs));
self.encode_term(snd, Place::Hole(rhs));
self.link(up, Place::Hole(out));
}
Term::Use { .. } // Removed in earlier pass
| Term::Mat { .. } // Removed in earlier pass
| Term::Nat { .. } // Removed in encode_nat
| Term::Str { .. } // Removed in encode_str
| Term::Lst { .. } // Removed in encode_list
// Removed in earlier passes:
Term::Use { .. }
| Term::Mat { .. }
| Term::Nat { .. }
| Term::Str { .. }
| Term::Lst { .. }
| Term::Err => unreachable!(),
}
})
}
fn encode_pat(&mut self, pat: &Pattern, up: Trg) {
fn encode_pat(&mut self, pat: &Pattern, up: Place<'t>) {
maybe_grow(|| match pat {
Pattern::Var(None) => {
let era = self.inet.new_node(Era);
self.inet.link(Port(era, 1), Port(era, 2));
self.link(up, Trg::Port(Port(era, 0)));
}
Pattern::Var(None) => self.link(up, Place::Tree(LoanedMut::new(Tree::Era))),
Pattern::Var(Some(name)) => self.scope.entry(name.clone()).or_default().push(up),
Pattern::Chn(name) => self.link_global(name, up),
Pattern::Fan(is_tup, tag, els) => {
let kind = self.fan_kind(is_tup, tag);
Pattern::Fan(fan, tag, els) => {
let kind = self.fan_kind(fan, tag);
self.make_node_list(kind, up, els.iter().map(|el| |slf: &mut Self, up| slf.encode_pat(el, up)));
}
Pattern::Ctr(_, _) | Pattern::Num(_) | Pattern::Lst(_) | Pattern::Str(_) => unreachable!(),
})
}
fn link(&mut self, x: Trg, y: Trg) {
match (x, y) {
(Trg::Port(p), Trg::Port(q)) => self.inet.link(p, q),
(Trg::Var(v), t) | (t, Trg::Var(v)) => match &mut self.vars[v] {
x @ None => *x = Some(t),
x => {
let u = x.take().unwrap();
self.link(t, u);
fn link(&mut self, a: Place<'t>, b: Place<'t>) {
match (a, b) {
(Place::Tree(a), Place::Tree(b)) => self.redexes.push(LoanedMut::merge(Default::default(), |r, m| {
m.place(b, &mut r.0);
m.place(a, &mut r.1);
})),
(Place::Tree(t), Place::Hole(h)) | (Place::Hole(h), Place::Tree(t)) => {
t.place(h);
}
(Place::Hole(a), Place::Hole(b)) => {
let var = Tree::Var { nam: num_to_name(self.name_idx) };
self.name_idx += 1;
*a = var.clone();
*b = var;
}
(Place::Wire(v), p) | (p, Place::Wire(v)) => {
let v = &mut self.wires[v];
match v.take() {
Some(q) => self.link(p, q),
None => *v = Some(p),
}
},
}
}
}
fn new_ctr(&mut self, kind: CtrKind) -> (Place<'t>, Place<'t>, Place<'t>) {
self.created_nodes += 1;
let (ports, node) =
LoanedMut::loan_with(Tree::Ctr { lab: kind.to_lab(), ports: vec![Tree::Era, Tree::Era] }, |t, l| {
let Tree::Ctr { ports, .. } = t else { unreachable!() };
l.loan_mut(ports)
});
let (a, b) = ports.split_at_mut(1);
(Place::Tree(node), Place::Hole(&mut a[0]), Place::Hole(&mut b[0]))
}
/// Adds a list-like tree of nodes of the same kind to the inet.
fn make_node_list(
&mut self,
kind: NodeKind,
mut up: Trg,
mut els: impl DoubleEndedIterator<Item = impl FnOnce(&mut Self, Trg)>,
kind: CtrKind,
mut up: Place<'t>,
mut els: impl DoubleEndedIterator<Item = impl FnOnce(&mut Self, Place<'t>)>,
) {
let last = els.next_back().unwrap();
for item in els {
let node = self.inet.new_node(kind.clone());
self.link(up, Trg::Port(Port(node, 0)));
item(self, Trg::Port(Port(node, 1)));
up = Trg::Port(Port(node, 2));
let node = self.new_ctr(kind);
self.link(up, node.0);
item(self, node.1);
up = node.2;
}
last(self, up);
}
fn new_var(&mut self) -> usize {
let i = self.vars.len();
self.vars.push(None);
fn new_wire(&mut self) -> usize {
let i = self.wires.len();
self.wires.push(None);
i
}
fn fan_kind(&mut self, fan: &FanKind, tag: &Tag) -> NodeKind {
fn fan_kind(&mut self, fan: &FanKind, tag: &Tag) -> CtrKind {
let lab = self.labels[*fan].generate(tag);
Ctr(if *fan == FanKind::Tup { Tup(lab) } else { Dup(lab.unwrap()) })
if *fan == FanKind::Tup { Tup(lab) } else { Dup(lab.unwrap()) }
}
fn link_global(&mut self, name: &Name, trg: Trg) {
fn link_global(&mut self, name: &Name, place: Place<'t>) {
match self.global_vars.entry(name.clone()) {
Entry::Occupied(e) => {
let other = e.remove();
self.link(trg, other);
self.link(place, other);
}
Entry::Vacant(e) => {
e.insert(trg);
e.insert(place);
}
}
}
@ -326,3 +359,13 @@ impl LabelGenerator {
self.name_to_label.clear();
}
}
fn hole<T: Default>() -> T {
T::default()
}
impl ToStringVerbose for ViciousCycleErr {
fn to_string_verbose(&self, _verbose: bool) -> String {
"Found term that compiles into an inet with a vicious cycle".into()
}
}