ion_binary_rs/
symbol_table.rs

1use crate::binary_parser_types::SYSTEM_SYMBOL_TABLE;
2use log::trace;
3use std::collections::HashMap;
4
5/// A table symbol. It can b used together with the "with_shared_table" method
6/// in order to define new shared tables.
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub enum Symbol {
9    Symbol(String),
10    Dummy,
11}
12
13#[derive(Eq, PartialEq, Debug)]
14pub struct LocalSymbolTable(Vec<Symbol>);
15
16impl LocalSymbolTable {
17    pub fn new() -> LocalSymbolTable {
18        LocalSymbolTable(
19            SYSTEM_SYMBOL_TABLE
20                .to_vec()
21                .iter()
22                .map(|s| Symbol::Symbol(s.to_string()))
23                .collect(),
24        )
25    }
26
27    pub fn add_symbol(&mut self, symbol: Symbol) -> usize {
28        let id = self.0.len();
29        self.0.push(symbol);
30        id
31    }
32
33    pub fn add_symbols(&mut self, slice: &[Symbol]) {
34        for symbol in slice {
35            self.add_symbol(symbol.clone());
36        }
37    }
38
39    pub fn get_symbol_by_id(&self, id: usize) -> Option<&Symbol> {
40        self.0.get(id)
41    }
42
43    pub fn get_id_by_symbol(&self, symbol: &str) -> Option<usize> {
44        self.0
45            .iter()
46            .enumerate()
47            .find(|(_, value)| {
48                if let Symbol::Symbol(value) = value {
49                    *value == symbol
50                } else {
51                    false
52                }
53            })
54            .map(|value| value.0)
55    }
56
57    pub fn insert_dummy_symbols(&mut self, max_len: usize) {
58        for _ in 0..max_len {
59            self.add_symbol(Symbol::Dummy);
60        }
61    }
62
63    pub fn list_all_symbols(&self) -> &[Symbol] {
64        &self.0
65    }
66}
67
68#[derive(Debug)]
69pub struct SharedSymbolTable {
70    _name: String,
71    _version: u32,
72    symbols: Vec<Symbol>,
73}
74
75impl SharedSymbolTable {
76    pub fn is_superset(&self, table: &SharedSymbolTable) -> bool {
77        for (index, symbol) in table.symbols.iter().enumerate() {
78            match self.symbols.get(index) {
79                Some(value) if value == symbol => {}
80                _ => {
81                    return false;
82                }
83            }
84        }
85
86        true
87    }
88
89    pub fn get_symbols_max_len(&self, max_len: usize) -> &[Symbol] {
90        if max_len > self.symbols.len() {
91            return &self.symbols;
92        }
93
94        self.symbols.split_at(max_len).0
95    }
96
97    pub fn get_all_symbols(&self) -> &[Symbol] {
98        &self.symbols
99    }
100}
101
102#[derive(Debug)]
103pub struct Import {
104    pub(crate) name: String,
105    pub(crate) version: Option<u32>,
106    pub(crate) max_len: Option<usize>,
107}
108
109/// Errors that can happen related with the Symbol Table.
110#[derive(Eq, PartialEq, Debug)]
111pub enum SymbolContextError {
112    TableVersionAlreadyThere,
113    MaxIdNeededWhenImportingASharedTableWhereVersionIsNotAvailable,
114    MaxIdNeededWhenImportingANotFoundSharedTable,
115    InternalParserErrorThisIsABug,
116    NewTableIsNotSuperSetOfPrevious,
117}
118
119#[derive(Debug)]
120pub struct SymbolContext {
121    current_table: LocalSymbolTable,
122    shared_tables: HashMap<String, (u32, HashMap<u32, SharedSymbolTable>)>,
123}
124
125impl SymbolContext {
126    pub fn new() -> SymbolContext {
127        SymbolContext {
128            current_table: LocalSymbolTable::new(),
129            shared_tables: HashMap::new(),
130        }
131    }
132
133    pub fn set_new_table_from_current(&mut self, symbols: Vec<Symbol>) {
134        for symbol in symbols.into_iter() {
135            self.current_table.add_symbol(symbol);
136        }
137    }
138
139    pub fn add_shared_table(
140        &mut self,
141        name: String,
142        version: u32,
143        symbols: &[Symbol],
144    ) -> Result<(), SymbolContextError> {
145        let new_table = SharedSymbolTable {
146            _name: name.clone(),
147            _version: version,
148            symbols: symbols.to_vec(),
149        };
150
151        match self.shared_tables.get_mut(&name) {
152            Some(table_collection) => match table_collection.1.get_mut(&version) {
153                Some(_) => Err(SymbolContextError::TableVersionAlreadyThere),
154                None => {
155                    SymbolContext::assert_new_table_is_superset(
156                        &new_table,
157                        &version,
158                        &table_collection.1,
159                    )?;
160
161                    if table_collection.0 < version {
162                        table_collection.0 = version;
163                    }
164
165                    trace!("New shared table imported {:?}", new_table);
166
167                    table_collection.1.insert(version, new_table);
168                    Ok(())
169                }
170            },
171            None => {
172                trace!("New shared table imported {:?}", new_table);
173
174                let mut new_hashmap = HashMap::new();
175                new_hashmap.insert(version, new_table);
176                let new_tuple = (version, new_hashmap);
177                self.shared_tables.insert(name, new_tuple);
178
179                Ok(())
180            }
181        }
182    }
183
184    pub fn assert_new_table_is_superset(
185        table: &SharedSymbolTable,
186        version: &u32,
187        tables: &HashMap<u32, SharedSymbolTable>,
188    ) -> Result<(), SymbolContextError> {
189        for index in (*version - 1)..=0 {
190            if let Some(existing_table) = tables.get(&index) {
191                if !table.is_superset(existing_table) {
192                    return Err(SymbolContextError::NewTableIsNotSuperSetOfPrevious);
193                } else {
194                    return Ok(());
195                }
196            }
197        }
198
199        Ok(())
200    }
201
202    pub fn set_new_table(
203        &mut self,
204        imports: &[Import],
205        symbols: &[Symbol],
206    ) -> Result<(), SymbolContextError> {
207        let mut new_table = LocalSymbolTable::new();
208
209        let symbols: Vec<Symbol> = symbols.to_vec();
210
211        for import in imports {
212            if import.name == "$ion" {
213                continue;
214            }
215
216            let version = if let Some(version) = import.version {
217                std::cmp::max(1, version)
218            } else {
219                1
220            };
221
222            match self.shared_tables.get(&import.name) {
223                Some(table_collection) => match table_collection.1.get(&version) {
224                    Some(table) => {
225                        let symbols = match import.max_len {
226                            Some(len) => table.get_symbols_max_len(len),
227                            None => table.get_all_symbols(),
228                        };
229
230                        new_table.add_symbols(symbols);
231                    }
232                    None => {
233                        if let Some(max_len) = import.max_len {
234                            let table = match table_collection.1.get(&table_collection.0) {
235                                Some(table) => table,
236                                None => {
237                                    return Err(SymbolContextError::InternalParserErrorThisIsABug)
238                                }
239                            };
240
241                            let symbols = table.get_symbols_max_len(max_len);
242                            new_table.add_symbols(symbols);
243                        } else {
244                            return Err(SymbolContextError::MaxIdNeededWhenImportingASharedTableWhereVersionIsNotAvailable);
245                        }
246                    }
247                },
248                None => {
249                    if let Some(len) = import.max_len {
250                        new_table.insert_dummy_symbols(len);
251                    } else {
252                        return Err(
253                            SymbolContextError::MaxIdNeededWhenImportingANotFoundSharedTable,
254                        );
255                    }
256                }
257            }
258        }
259
260        new_table.add_symbols(&symbols);
261
262        trace!(
263            "New local table importing {:?} resulting in: {:?}",
264            imports,
265            new_table
266        );
267
268        self.current_table = new_table;
269
270        Ok(())
271    }
272
273    pub fn get_symbol_by_id(&self, id: usize) -> Option<&Symbol> {
274        self.current_table.get_symbol_by_id(id)
275    }
276
277    pub fn insert_symbol(&mut self, symbol: &str) -> usize {
278        match self.current_table.get_id_by_symbol(symbol) {
279            Some(id) => id,
280            None => self
281                .current_table
282                .add_symbol(Symbol::Symbol(symbol.to_string())),
283        }
284    }
285
286    pub fn dump_all_local_symbols(&self) -> Vec<String> {
287        self.current_table.list_all_symbols()[10..]
288            .iter()
289            .map(|s| match s {
290                Symbol::Symbol(name) => name.clone(),
291                _ => "".to_string(),
292            })
293            .collect()
294    }
295}
296
297impl Default for SymbolContext {
298    fn default() -> Self {
299        Self::new()
300    }
301}