diff --git a/Cargo.lock b/Cargo.lock index 2f570c1e..ec890d37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -118,6 +118,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + [[package]] name = "clap" version = "4.5.17" @@ -158,6 +164,15 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +[[package]] +name = "clipboard-win" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892" +dependencies = [ + "error-code", +] + [[package]] name = "colorchoice" version = "1.0.2" @@ -174,12 +189,45 @@ dependencies = [ "walkdir", ] +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "error-code" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" + +[[package]] +name = "fd-lock" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" +dependencies = [ + "cfg-if", + "rustix", + "windows-sys", +] + [[package]] name = "fnv" version = "1.0.7" @@ -215,6 +263,15 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys", +] + [[package]] name = "indexmap" version = "2.2.6" @@ -251,6 +308,18 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "libc" +version = "0.2.159" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "loaned" version = "0.1.2" @@ -302,6 +371,27 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "nix" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "once_cell" version = "1.19.0" @@ -326,6 +416,16 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + [[package]] name = "regex-automata" version = "0.4.7" @@ -343,6 +443,41 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +[[package]] +name = "rustix" +version = "0.38.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rustyline" +version = "14.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7803e8936da37efd9b6d4478277f4b2b9bb5cdb37a113e8d63222e58da647e63" +dependencies = [ + "bitflags", + "cfg-if", + "clipboard-win", + "fd-lock", + "home", + "libc", + "log", + "memchr", + "nix", + "radix_trie", + "unicode-segmentation", + "unicode-width", + "utf8parse", + "windows-sys", +] + [[package]] name = "same-file" version = "1.0.6" @@ -381,6 +516,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + [[package]] name = "strsim" version = "0.11.1" @@ -411,6 +552,18 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + [[package]] name = "utf8parse" version = "0.2.2" @@ -444,6 +597,7 @@ dependencies = [ "clap", "ivm", "ivy", + "rustyline", "vine", "vine-util", ] diff --git a/cli/Cargo.toml b/cli/Cargo.toml index d9c3f0ff..b3c9d7a7 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] anyhow = "1.0.87" clap = { version = "4.5.17", features = ["derive", "env"] } +rustyline = "14.0.0" ivm = { path = "../ivm" } ivy = { path = "../ivy" } diff --git a/cli/src/common.rs b/cli/src/common.rs index c57038e8..db68ac91 100644 --- a/cli/src/common.rs +++ b/cli/src/common.rs @@ -1,7 +1,7 @@ use clap::Args; use ivm::{heap::Heap, IVM}; -use ivy::{ast::Nets, optimize::Optimizer}; +use ivy::{ast::Nets, optimize::Optimizer, serialize::Labels}; #[derive(Debug, Default, Args)] pub struct Optimizations { @@ -26,7 +26,7 @@ pub struct RunArgs { impl RunArgs { pub fn run(self, nets: Nets) { let mut globals = Vec::new(); - let globals = nets.serialize(&mut globals); + let globals = nets.serialize(&mut globals, &mut Labels::default()); let main = &globals[nets.get_index_of("::main").expect("missing main")]; let heap = Heap::new(); let mut ivm = IVM::new(&heap); diff --git a/cli/src/ivy_cli.rs b/cli/src/ivy_cli.rs index 2f687dc9..ca5a83ba 100644 --- a/cli/src/ivy_cli.rs +++ b/cli/src/ivy_cli.rs @@ -2,7 +2,10 @@ use std::{fs, path::PathBuf}; use anyhow::Result; use clap::{Args, Parser}; -use ivy::parser::IvyParser; +use rustyline::DefaultEditor; + +use ivm::{heap::Heap, IVM}; +use ivy::{parser::IvyParser, repl::Repl, serialize::Labels}; use crate::{Optimizations, RunArgs}; @@ -13,6 +16,7 @@ pub enum IvyCommand { Run(IvyRunCommand), #[command(about = "Optimize an Ivy program")] Optimize(IvyOptimizeCommand), + Repl(IvyReplCommand), } impl IvyCommand { @@ -20,6 +24,7 @@ impl IvyCommand { match Self::parse() { IvyCommand::Run(run) => run.execute(), IvyCommand::Optimize(optimize) => optimize.execute(), + IvyCommand::Repl(repl) => repl.execute(), } } } @@ -56,3 +61,38 @@ impl IvyOptimizeCommand { unimplemented!() } } + +#[derive(Debug, Args)] +pub struct IvyReplCommand { + #[arg()] + src: Option, + #[command(flatten)] + run_args: RunArgs, +} + +impl IvyReplCommand { + pub fn execute(self) -> Result<()> { + let src = self.src.map(fs::read_to_string).unwrap_or(Ok(String::new()))?; + let nets = IvyParser::parse(&src).unwrap(); + let mut globals = Vec::new(); + let mut labels = Labels::default(); + let globals = nets.serialize(&mut globals, &mut labels); + let heap = Heap::new(); + let mut ivm = IVM::new(&heap); + let mut repl = Repl::new(&mut ivm, &nets, globals, &mut labels); + let mut rl = DefaultEditor::new()?; + loop { + print!("\n{repl}"); + match rl.readline("> ") { + Ok(line) => { + _ = rl.add_history_entry(&line); + if let Err(err) = repl.exec(&line) { + println!("{err:?}"); + } + } + Err(_) => break, + } + } + Ok(()) + } +} diff --git a/cspell.json b/cspell.json index dbb80aec..bca84ac5 100644 --- a/cspell.json +++ b/cspell.json @@ -23,8 +23,10 @@ "nilary", "nonoverlapping", "readback", + "readline", "repr", "rotr", + "rustyline", "scrutinee", "sixel", "strs" diff --git a/ivm/src/addr.rs b/ivm/src/addr.rs index 756fafb9..d30b0f08 100644 --- a/ivm/src/addr.rs +++ b/ivm/src/addr.rs @@ -9,7 +9,7 @@ use crate::{ }; /// The address of a port, a pointer of only-externally-known interpretation. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct Addr(pub *const ()); diff --git a/ivm/src/allocator.rs b/ivm/src/allocator.rs index 5985f7d4..90c9b6d5 100644 --- a/ivm/src/allocator.rs +++ b/ivm/src/allocator.rs @@ -14,15 +14,20 @@ impl<'ivm> IVM<'ivm> { /// `tag` must be the tag of a binary node, and `label` must comply with its /// requirements. #[inline(always)] - pub(crate) unsafe fn new_node( - &mut self, - tag: Tag, - label: u16, - ) -> (Port<'ivm>, Wire<'ivm>, Wire<'ivm>) { + pub unsafe fn new_node(&mut self, tag: Tag, label: u16) -> (Port<'ivm>, Wire<'ivm>, Wire<'ivm>) { let addr = self.alloc_node(); (Port::new(tag, label, addr), Wire::from_addr(addr), Wire::from_addr(addr.other_half())) } + /// Allocates a new wire, returning both of its ends. + pub fn new_wire(&mut self) -> (Wire<'ivm>, Wire<'ivm>) { + unsafe { + let addr = self.alloc_node(); + self.free_wire(Wire::from_addr(addr.other_half())); + (Wire::from_addr(addr), Wire::from_addr(addr)) + } + } + /// Frees the memory backing a wire. #[inline] pub(crate) fn free_wire(&mut self, wire: Wire<'ivm>) { diff --git a/ivm/src/interact.rs b/ivm/src/interact.rs index b3938320..2c1bd39a 100644 --- a/ivm/src/interact.rs +++ b/ivm/src/interact.rs @@ -2,7 +2,7 @@ use std::time::Instant; use crate::{ ivm::IVM, - port::{Port, Tag}, + port::{Port, PortRef, Tag}, wire::Wire, }; @@ -58,6 +58,21 @@ impl<'ivm> IVM<'ivm> { p } + /// Non-destructively follows as many `Wire`s with active targets as currently + /// possible. + #[inline] + pub fn follow_ref<'a>(&self, p: &'a Port<'ivm>) -> PortRef<'a, 'ivm> { + let mut p = PortRef::from(p); + while p.tag() == Tag::Wire { + if let Some(q) = unsafe { p.clone().as_wire() }.load_target() { + p = PortRef::from_port(q); + } else { + break; + } + } + p + } + /// Execute an interaction between two principal ports. pub(crate) fn interact(&mut self, a: Port<'ivm>, b: Port<'ivm>) { use Tag::*; diff --git a/ivm/src/port.rs b/ivm/src/port.rs index 6c0eeb48..fc452caf 100644 --- a/ivm/src/port.rs +++ b/ivm/src/port.rs @@ -2,13 +2,14 @@ use crate::{ addr::Addr, ext::{ExtFn, ExtVal}, global::Global, - wire::Wire, + wire::{Wire, WireRef}, word::{NonZeroWord, Word}, }; use core::{ fmt::{self, Debug}, marker::PhantomData, mem::transmute, + ops::Deref, ptr, }; @@ -208,6 +209,19 @@ impl<'ivm> Port<'ivm> { pub unsafe fn aux(self) -> (Wire<'ivm>, Wire<'ivm>) { (Wire::from_addr(self.addr()), Wire::from_addr(self.addr().other_half())) } + + /// Get the wires leaving the aux ports of this node. + /// + /// ## Safety + /// This port must be the principal port of a binary node: [`Tag::Comb`], + /// [`Tag::ExtVal`], or [`Tag::Branch`]. + #[inline(always)] + pub unsafe fn aux_ref(&self) -> (WireRef<'_, 'ivm>, WireRef<'_, 'ivm>) { + ( + WireRef::from_wire(Wire::from_addr(self.addr())), + WireRef::from_wire(Wire::from_addr(self.addr().other_half())), + ) + } } impl<'ivm> Debug for Port<'ivm> { @@ -223,3 +237,31 @@ impl<'ivm> Debug for Port<'ivm> { } } } + +/// Semantically analogous to `&'a Port<'ivm>`. +pub struct PortRef<'a, 'ivm>(Port<'ivm>, PhantomData<&'a ()>); + +impl<'a, 'ivm> PortRef<'a, 'ivm> { + pub(crate) fn from_port(port: Port<'ivm>) -> Self { + PortRef(port, PhantomData) + } + + #[inline(always)] + pub fn new_wire(wire: &Wire<'ivm>) -> Self { + unsafe { PortRef::from_port(Port::new_wire(wire.clone())) } + } +} + +impl<'a, 'ivm> Deref for PortRef<'a, 'ivm> { + type Target = Port<'ivm>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'a, 'ivm> From<&'a Port<'ivm>> for PortRef<'a, 'ivm> { + fn from(port: &'a Port<'ivm>) -> Self { + Self(unsafe { port.clone() }, PhantomData) + } +} diff --git a/ivm/src/wire.rs b/ivm/src/wire.rs index 4888d3ff..e5aa8e7f 100644 --- a/ivm/src/wire.rs +++ b/ivm/src/wire.rs @@ -2,6 +2,8 @@ use core::{ fmt::{self, Debug}, marker::PhantomData, mem::transmute, + ops::Deref, + ptr, }; use crate::{ @@ -45,6 +47,18 @@ impl<'ivm> Wire<'ivm> { pub(crate) fn addr(&self) -> Addr { self.0 } + + /// Unsafely clones a wire by copying its bits. + /// + /// ## Safety + /// At most one clone may be linked. + /// + /// This can't currently cause UB (just severe logic errors), but may in the + /// future. + #[inline(always)] + pub unsafe fn clone(&self) -> Self { + unsafe { ptr::read(self) } + } } impl<'ivm> Debug for Wire<'ivm> { @@ -52,3 +66,26 @@ impl<'ivm> Debug for Wire<'ivm> { write!(f, "Wire({:?})", self.addr()) } } + +/// Semantically analogous to `&'a Wire<'ivm>`. +pub struct WireRef<'a, 'ivm>(Wire<'ivm>, PhantomData<&'a ()>); + +impl<'a, 'ivm> WireRef<'a, 'ivm> { + pub(crate) fn from_wire(wire: Wire<'ivm>) -> Self { + WireRef(wire, PhantomData) + } +} + +impl<'a, 'ivm> Deref for WireRef<'a, 'ivm> { + type Target = Wire<'ivm>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'a, 'ivm> From<&'a Wire<'ivm>> for WireRef<'a, 'ivm> { + fn from(wire: &'a Wire<'ivm>) -> Self { + Self::from_wire(unsafe { wire.clone() }) + } +} diff --git a/ivy/src/lib.rs b/ivy/src/lib.rs index 2edc4964..12bfb0ee 100644 --- a/ivy/src/lib.rs +++ b/ivy/src/lib.rs @@ -1,6 +1,8 @@ pub mod ast; pub mod optimize; pub mod parser; +pub mod repl; +pub mod serialize; mod lexer; -mod serialize; +mod readback; diff --git a/ivy/src/parser.rs b/ivy/src/parser.rs index 5b4296c4..a0792623 100644 --- a/ivy/src/parser.rs +++ b/ivy/src/parser.rs @@ -77,14 +77,18 @@ impl<'src> IvyParser<'src> { let root = self.parse_tree()?; let mut pairs = Vec::new(); while !self.check(Token::CloseBrace) { - let a = self.parse_tree()?; - self.expect(Token::Eq)?; - let b = self.parse_tree()?; - pairs.push((a, b)); + pairs.push(self.parse_pair()?); } Ok(Net { root, pairs }) } + pub(super) fn parse_pair(&mut self) -> Parse<'src, (Tree, Tree)> { + let a = self.parse_tree()?; + self.expect(Token::Eq)?; + let b = self.parse_tree()?; + Ok((a, b)) + } + fn parse_tree(&mut self) -> Parse<'src, Tree> { if self.check(Token::U32) { return Ok(Tree::U32(self.parse_u32()?)); diff --git a/ivy/src/readback.rs b/ivy/src/readback.rs new file mode 100644 index 00000000..0eff83ff --- /dev/null +++ b/ivy/src/readback.rs @@ -0,0 +1,74 @@ +use std::collections::{hash_map::Entry, HashMap}; + +use ivm::{ + addr::Addr, + ext::ExtTy, + port::{Port, PortRef, Tag}, + wire::Wire, + IVM, +}; + +use crate::{ast::Tree, serialize::Labels}; + +pub struct Reader<'a, 'ivm> { + ivm: &'a IVM<'ivm>, + labels: &'a Labels<'a>, + vars: HashMap, + next_var: usize, +} + +impl<'a, 'ivm> Reader<'a, 'ivm> { + pub fn new(ivm: &'a IVM<'ivm>, labels: &'a Labels<'a>) -> Self { + Reader { ivm, labels, vars: HashMap::new(), next_var: 0 } + } + + pub fn read_port(&mut self, p: &Port<'ivm>) -> Tree { + let p = self.ivm.follow_ref(p); + match p.tag() { + Tag::Wire => { + let n = match self.vars.entry(p.addr()) { + Entry::Occupied(e) => e.remove(), + Entry::Vacant(e) => { + let n = self.next_var; + self.next_var += 1; + e.insert(n); + n + } + }; + Tree::Var(format!("n{n}")) + } + Tag::Global => Tree::Global(unsafe { p.as_global() }.name.clone()), + Tag::Erase => Tree::Erase, + Tag::ExtVal => { + let val = unsafe { p.as_ext_val() }; + match val.ty() { + ExtTy::u32 => Tree::U32(val.as_u32()), + ExtTy::f32 => Tree::F32(val.as_f32()), + ExtTy::IO => Tree::Var("#io".into()), + } + } + Tag::Comb => { + let label = p.label(); + let (p1, p2) = unsafe { p.aux_ref() }; + Tree::Comb(self.labels.from_id(label).to_owned(), self.read_wire(&p1), self.read_wire(&p2)) + } + Tag::ExtFn => { + let f = unsafe { p.as_ext_fn() }; + let (p1, p2) = unsafe { p.aux_ref() }; + Tree::ExtFn(f, self.read_wire(&p1), self.read_wire(&p2)) + } + Tag::Branch => { + let (p1, p2) = unsafe { p.aux_ref() }; + let p1 = PortRef::new_wire(&p1); + let p1 = self.ivm.follow_ref(&p1); + assert_eq!(p1.tag(), Tag::Branch); + let (p11, p12) = unsafe { p1.aux_ref() }; + Tree::Branch(self.read_wire(&p11), self.read_wire(&p12), self.read_wire(&p2)) + } + } + } + + pub fn read_wire(&mut self, w: &Wire<'ivm>) -> Box { + Box::new(self.read_port(&PortRef::new_wire(w))) + } +} diff --git a/ivy/src/repl.rs b/ivy/src/repl.rs new file mode 100644 index 00000000..d341c2a9 --- /dev/null +++ b/ivy/src/repl.rs @@ -0,0 +1,125 @@ +use std::fmt::{self, Display}; + +use indexmap::{map::Entry, IndexMap}; +use ivm::{ + ext::ExtVal, + global::Global, + port::{Port, Tag}, + wire::Wire, + IVM, +}; +use vine_util::parser::{Parser, ParserState}; + +use crate::{ + ast::{Nets, Tree}, + parser::{IvyParser, ParseError}, + readback::Reader, + serialize::Labels, +}; + +pub struct Repl<'a, 'l, 'ivm> { + ivm: &'a mut IVM<'ivm>, + nets: &'a Nets, + globals: &'ivm [Global<'ivm>], + labels: &'a mut Labels<'l>, + + vars: IndexMap>, +} + +impl<'a, 'l, 'ivm> Repl<'a, 'l, 'ivm> { + pub fn new( + ivm: &'a mut IVM<'ivm>, + nets: &'a Nets, + globals: &'ivm [Global<'ivm>], + labels: &'a mut Labels<'l>, + ) -> Self { + let vars = [("io".to_owned(), Port::new_ext_val(ExtVal::IO))].into_iter().collect(); + Self { ivm, nets, globals, labels, vars } + } + + pub fn exec<'s>(&mut self, line: &'s str) -> Result<(), ParseError<'s>> { + let mut parser = IvyParser { state: ParserState::new(line) }; + parser.bump()?; + let mut pairs = Vec::new(); + while parser.state.token.is_some() { + pairs.push(parser.parse_pair()?); + } + for pair in pairs { + self.exec_pair(pair); + } + Ok(()) + } + + pub fn exec_pair(&mut self, pair: (Tree, Tree)) { + let p = self.inject(pair.0); + let q = self.inject(pair.1); + self.ivm.link(p, q); + self.ivm.normalize(); + } + + fn inject_to(&mut self, tree: Tree, to: Wire<'ivm>) { + if let Tree::Var(v) = tree { + match self.vars.entry(v) { + Entry::Occupied(e) => { + let p = e.shift_remove(); + self.ivm.link_wire(to, p); + } + Entry::Vacant(e) => { + e.insert(Port::new_wire(to)); + } + } + } else { + let p = self.inject(tree); + self.ivm.link_wire(to, p); + } + } + + fn inject(&mut self, tree: Tree) -> Port<'ivm> { + match tree { + Tree::Erase => Port::ERASE, + Tree::U32(value) => Port::new_ext_val(ExtVal::new_u32(value)), + Tree::F32(value) => Port::new_ext_val(ExtVal::new_f32(value)), + Tree::Global(name) => Port::new_global(&self.globals[self.nets.get_index_of(&name).unwrap()]), + Tree::Comb(label, a, b) => { + let label = self.labels.to_id(label); + let n = unsafe { self.ivm.new_node(Tag::Comb, label) }; + self.inject_to(*a, n.1); + self.inject_to(*b, n.2); + n.0 + } + Tree::ExtFn(f, a, b) => { + let n = unsafe { self.ivm.new_node(Tag::ExtFn, f.bits()) }; + self.inject_to(*a, n.1); + self.inject_to(*b, n.2); + n.0 + } + Tree::Branch(z, p, o) => { + let n = unsafe { self.ivm.new_node(Tag::Branch, 0) }; + let m = unsafe { self.ivm.new_node(Tag::Branch, 0) }; + self.ivm.link_wire(n.1, m.0); + self.inject_to(*z, m.1); + self.inject_to(*p, m.2); + self.inject_to(*o, n.2); + n.0 + } + Tree::Var(v) => match self.vars.entry(v) { + Entry::Occupied(e) => e.shift_remove(), + Entry::Vacant(e) => { + let (a, b) = self.ivm.new_wire(); + e.insert(Port::new_wire(a)); + Port::new_wire(b) + } + }, + } + } +} + +impl Display for Repl<'_, '_, '_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut reader = Reader::new(self.ivm, self.labels); + for (var, port) in &self.vars { + writeln!(f, "{} = {}", var, reader.read_port(port))?; + } + Ok(()) + } +} diff --git a/ivy/src/serialize.rs b/ivy/src/serialize.rs index 4f81343d..a22d3c8f 100644 --- a/ivy/src/serialize.rs +++ b/ivy/src/serialize.rs @@ -39,6 +39,7 @@ //! `&Global`. use std::{ + borrow::Cow, cell::UnsafeCell, collections::{BTreeMap, HashMap}, marker::PhantomData, @@ -66,7 +67,11 @@ impl Nets { /// /// The indices of the returned global slice correspond exactly to the indices /// of the nets in the `IndexMap`. - pub fn serialize<'ivm>(&self, globals: &'ivm mut Vec>) -> &'ivm [Global<'ivm>] { + pub fn serialize<'ast, 'ivm>( + &'ast self, + globals: &'ivm mut Vec>, + labels: &mut Labels<'ast>, + ) -> &'ivm [Global<'ivm>] { assert!(globals.is_empty()); globals.extend(self.keys().map(|name| Global { name: name.clone(), @@ -85,10 +90,10 @@ impl Nets { let mut serializer = Serializer { globals, nets: self, + labels, current: Default::default(), equivalences: Default::default(), registers: Default::default(), - labels: Default::default(), }; for (i, net) in self.values().enumerate() { @@ -106,16 +111,16 @@ impl Nets { } } -struct Serializer<'ast, 'ivm> { +struct Serializer<'l, 'ast, 'ivm> { globals: &'ivm [UnsafeCell>], nets: &'ast Nets, + labels: &'l mut Labels<'ast>, current: Global<'ivm>, equivalences: BTreeMap<&'ast str, &'ast str>, registers: HashMap<&'ast str, Register>, - labels: IndexSet<&'ast str>, } -impl<'ast, 'ivm> Serializer<'ast, 'ivm> { +impl<'l, 'ast, 'ivm> Serializer<'l, 'ast, 'ivm> { fn push(&mut self, instruction: Instruction<'ivm>) { unsafe { self.current.instructions.push(instruction) } } @@ -176,7 +181,7 @@ impl<'ast, 'ivm> Serializer<'ast, 'ivm> { self.push(Instruction::Nilary(to, Port::new_ext_val(ExtVal::new_f32(*num)))) } Tree::Comb(label, a, b) => { - let label = self.labels.insert_full(label).0 as u16; + let label = self.labels.to_id(label); self.current.labels.add(label); let a = self.serialize_tree(a); let b = self.serialize_tree(b); @@ -245,3 +250,16 @@ impl<'ivm> Bicycle for PropagateLabels<'ivm> { } } } + +#[derive(Debug, Default)] +pub struct Labels<'a>(IndexSet>); + +impl<'a> Labels<'a> { + pub fn to_id(&mut self, label: impl Into>) -> u16 { + self.0.insert_full(label.into()).0 as u16 + } + + pub fn from_id(&self, id: u16) -> &str { + &self.0[id as usize] + } +}