use std::collections::HashMap;
use plotnik_core::{Interner, Symbol};
use plotnik_bytecode::StringId;
use super::EmitError;
pub const EASTER_EGG: &str = "Beauty will save the world";
#[derive(Debug)]
pub struct StringTableBuilder {
mapping: HashMap<Symbol, StringId>,
str_lookup: HashMap<String, StringId>,
strings: Vec<String>,
}
impl StringTableBuilder {
pub fn new() -> Self {
let mut builder = Self {
mapping: HashMap::new(),
str_lookup: HashMap::new(),
strings: Vec::new(),
};
builder.strings.push(EASTER_EGG.to_string());
builder
}
pub fn get_or_intern(
&mut self,
sym: Symbol,
interner: &Interner,
) -> Result<StringId, EmitError> {
if let Some(&id) = self.mapping.get(&sym) {
return Ok(id);
}
let text = interner
.try_resolve(sym)
.ok_or(EmitError::StringNotFound(sym))?;
let id = StringId::new(self.strings.len() as u16);
self.strings.push(text.to_string());
self.str_lookup.insert(text.to_string(), id);
self.mapping.insert(sym, id);
Ok(id)
}
pub fn intern_str(&mut self, s: &str) -> StringId {
if let Some(&id) = self.str_lookup.get(s) {
return id;
}
let id = StringId::new(self.strings.len() as u16);
self.strings.push(s.to_string());
self.str_lookup.insert(s.to_string(), id);
id
}
pub fn len(&self) -> usize {
self.strings.len()
}
pub fn is_empty(&self) -> bool {
self.strings.is_empty()
}
pub fn validate(&self) -> Result<(), EmitError> {
if self.strings.len() > 65534 {
return Err(EmitError::TooManyStrings(self.strings.len()));
}
Ok(())
}
pub fn get(&self, sym: Symbol) -> Option<StringId> {
self.mapping.get(&sym).copied()
}
pub fn get_str(&self, id: StringId) -> &str {
&self.strings[id.get() as usize]
}
pub fn emit(&self) -> (Vec<u8>, Vec<u8>) {
let mut blob = Vec::new();
let mut offsets: Vec<u32> = Vec::with_capacity(self.strings.len() + 1);
for s in &self.strings {
offsets.push(blob.len() as u32);
blob.extend_from_slice(s.as_bytes());
}
offsets.push(blob.len() as u32);
let table_bytes: Vec<u8> = offsets.iter().flat_map(|o| o.to_le_bytes()).collect();
(blob, table_bytes)
}
}
impl Default for StringTableBuilder {
fn default() -> Self {
Self::new()
}
}