plotnik_compiler/emit/
string_table.rs1use std::collections::HashMap;
6
7use plotnik_core::{Interner, Symbol};
8
9use plotnik_bytecode::StringId;
10
11use super::EmitError;
12
13pub const EASTER_EGG: &str = "Beauty will save the world";
16
17#[derive(Debug)]
26pub struct StringTableBuilder {
27 mapping: HashMap<Symbol, StringId>,
29 str_lookup: HashMap<String, StringId>,
31 strings: Vec<String>,
33}
34
35impl StringTableBuilder {
36 pub fn new() -> Self {
37 let mut builder = Self {
38 mapping: HashMap::new(),
39 str_lookup: HashMap::new(),
40 strings: Vec::new(),
41 };
42 builder.strings.push(EASTER_EGG.to_string());
44 builder
45 }
46
47 pub fn get_or_intern(
49 &mut self,
50 sym: Symbol,
51 interner: &Interner,
52 ) -> Result<StringId, EmitError> {
53 if let Some(&id) = self.mapping.get(&sym) {
54 return Ok(id);
55 }
56
57 let text = interner
58 .try_resolve(sym)
59 .ok_or(EmitError::StringNotFound(sym))?;
60
61 let id = StringId::new(self.strings.len() as u16);
62 self.strings.push(text.to_string());
63 self.str_lookup.insert(text.to_string(), id);
64 self.mapping.insert(sym, id);
65 Ok(id)
66 }
67
68 pub fn intern_str(&mut self, s: &str) -> StringId {
70 if let Some(&id) = self.str_lookup.get(s) {
71 return id;
72 }
73
74 let id = StringId::new(self.strings.len() as u16);
75 self.strings.push(s.to_string());
76 self.str_lookup.insert(s.to_string(), id);
77 id
78 }
79
80 pub fn len(&self) -> usize {
82 self.strings.len()
83 }
84
85 pub fn is_empty(&self) -> bool {
87 self.strings.is_empty()
88 }
89
90 pub fn validate(&self) -> Result<(), EmitError> {
92 if self.strings.len() > 65534 {
95 return Err(EmitError::TooManyStrings(self.strings.len()));
96 }
97 Ok(())
98 }
99
100 pub fn get(&self, sym: Symbol) -> Option<StringId> {
102 self.mapping.get(&sym).copied()
103 }
104
105 pub fn get_str(&self, id: StringId) -> &str {
107 &self.strings[id.get() as usize]
108 }
109
110 pub fn emit(&self) -> (Vec<u8>, Vec<u8>) {
114 let mut blob = Vec::new();
115 let mut offsets: Vec<u32> = Vec::with_capacity(self.strings.len() + 1);
116
117 for s in &self.strings {
118 offsets.push(blob.len() as u32);
119 blob.extend_from_slice(s.as_bytes());
120 }
121 offsets.push(blob.len() as u32); let table_bytes: Vec<u8> = offsets.iter().flat_map(|o| o.to_le_bytes()).collect();
125
126 (blob, table_bytes)
127 }
128}
129
130impl Default for StringTableBuilder {
131 fn default() -> Self {
132 Self::new()
133 }
134}