diff --git a/Cargo.lock b/Cargo.lock index fca2907..1a8baa7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,6 +67,7 @@ dependencies = [ "console_log", "log", "num_enum", + "rustc-hash", "strum_macros", "wasm-bindgen", "web-sys", @@ -157,6 +158,7 @@ name = "native" version = "0.1.0" dependencies = [ "compiler", + "rustc-hash", ] [[package]] @@ -228,6 +230,12 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustversion" version = "1.0.14" diff --git a/compiler/Cargo.toml b/compiler/Cargo.toml index 5cac0ed..27f1e0f 100644 --- a/compiler/Cargo.toml +++ b/compiler/Cargo.toml @@ -13,6 +13,7 @@ anyhow = { version = "1.0.81", features = ["backtrace"] } console_log = "1.0.0" log = "0.4.21" num_enum = "0.7.2" +rustc-hash = "1.1.0" strum_macros = "0.26.2" wasm-bindgen = "0.2.92" diff --git a/compiler/src/interner.rs b/compiler/src/interner.rs new file mode 100644 index 0000000..56bc231 --- /dev/null +++ b/compiler/src/interner.rs @@ -0,0 +1,64 @@ +use rustc_hash::FxHashMap as HashMap; +use std::mem; + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +struct StrId(u32); + +pub struct Interner { + map: HashMap<&'static str, StrId>, + vec: Vec<&'static str>, + buf: String, + full: Vec, +} + +impl Interner { + /// Create a new interner with the given buffer capacity (in bytes) + pub fn with_capacity(cap: usize) -> Interner { + let cap = cap.next_power_of_two(); + Interner { + map: HashMap::default(), + vec: Vec::new(), + buf: String::with_capacity(cap), + full: Vec::new(), + } + } + + /// Intern string, and get it's ID + pub fn intern(&mut self, name: &str) -> StrId { + if let Some(&id) = self.map.get(name) { + return id; + } + let name = unsafe { self.alloc(name) }; + let id = StrId { 0: self.map.len() as u32 }; + self.map.insert(name, id); + self.vec.push(name); + + debug_assert!(self.lookup(id) == name); + debug_assert!(self.intern(name) == id); + + id + } + + /// Get a string, given it's ID + pub fn lookup(&self, id: StrId) -> &str { + self.vec[id.0 as usize] + } + + unsafe fn alloc(&mut self, name: &str) -> &'static str { + let cap = self.buf.capacity(); + if cap < self.buf.len() + name.len() { + let new_cap = (cap.max(name.len()) + 1).next_power_of_two(); + let new_buf = String::with_capacity(new_cap); + let old_buf = mem::replace(&mut self.buf, new_buf); + self.full.push(old_buf); + } + + let interned = { + let start = self.buf.len(); + self.buf.push_str(name); + &self.buf[start..] + }; + + &*(interned as *const str) + } +} diff --git a/compiler/src/lib.rs b/compiler/src/lib.rs index 45d313b..d78200a 100644 --- a/compiler/src/lib.rs +++ b/compiler/src/lib.rs @@ -1,6 +1,7 @@ pub mod chunk; pub mod common; pub mod compiler; +pub mod interner; pub mod scanner; pub mod value; pub mod vm; diff --git a/compiler/src/value.rs b/compiler/src/value.rs index b5fb507..4ca5b6f 100644 --- a/compiler/src/value.rs +++ b/compiler/src/value.rs @@ -25,7 +25,7 @@ pub fn print_value(value: &Value) { } #[cfg(not(feature = "tracing"))] -pub fn print_value(&self, value: Value) { +pub fn print_value(value: Value) { xprint!("Value {value}"); } diff --git a/native/Cargo.toml b/native/Cargo.toml index e67f826..82d1145 100644 --- a/native/Cargo.toml +++ b/native/Cargo.toml @@ -7,3 +7,4 @@ edition = "2021" [dependencies] compiler = {path = "../compiler", features = ["tracing", "print_code"]} +rustc-hash = "1.1.0"