mirror of
https://github.com/HigherOrderCO/Bend.git
synced 2024-09-11 11:56:54 +03:00
encode terms directly to hvmc nets
This commit is contained in:
parent
d87c0919bd
commit
105633153e
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -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"
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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}");
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
@ -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)]
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user