Skip to main content

prolog2/heap/
symbol_db.rs

1//! Global symbol database.
2//!
3//! All constant and variable symbols are interned here. Heap cells store
4//! integer IDs rather than strings; this module provides the mapping in
5//! both directions.
6
7use std::{
8    collections::HashMap,
9    sync::{Arc, RwLock},
10};
11
12use lazy_static::lazy_static;
13
14const KNOWN_SYMBOLS: &[&str] = &[
15    "false",
16    "true", // indices 0, 1
17    "+",
18    "-",
19    "*",
20    "/",
21    "**", // indices 2-6
22    "cos",
23    "sin",
24    "tan",
25    "acos",
26    "asin",
27    "atan", // indices 7-12
28    "log",
29    "abs",
30    "round",
31    "sqrt",
32    "to_degrees",
33    "to_radians", // indices 13-18
34];
35
36/// Compute the symbol ID for a known symbol at compile time
37pub const fn known_symbol_id(index: usize) -> usize {
38    isize::MAX as usize + index
39}
40
41lazy_static! {
42    static ref SYMBOLS: RwLock<SymbolDB> = RwLock::new(SymbolDB {
43        const_symbols: KNOWN_SYMBOLS
44            .iter()
45            .map(|&symbol| symbol.to_string().into())
46            .collect(),
47        var_symbol_map: HashMap::new(),
48        strings: Vec::new(),
49    });
50}
51
52/// Global symbol table.
53///
54/// Maps between string representations and numeric IDs so the heap can
55/// work entirely with `usize` values. Constants, variables, and string
56/// literals are stored separately.
57pub struct SymbolDB {
58    const_symbols: Vec<Arc<str>>,
59    var_symbol_map: HashMap<(usize, usize), Arc<str>>,
60    strings: Vec<Arc<str>>,
61}
62
63impl SymbolDB {
64    pub fn set_const(symbol: String) -> usize {
65        let mut symbols = SYMBOLS.write().unwrap();
66        let symbol: Arc<str> = symbol.into();
67        match symbols.const_symbols.iter().position(|e| *e == symbol) {
68            Some(i) => i + isize::MAX as usize,
69            None => {
70                symbols.const_symbols.push(symbol);
71                symbols.const_symbols.len() - 1 + isize::MAX as usize
72            }
73        }
74    }
75
76    pub fn set_var(symbol: String, addr: usize, heap_id: usize) {
77        SYMBOLS
78            .write()
79            .unwrap()
80            .var_symbol_map
81            .insert((addr, heap_id), symbol.into());
82    }
83
84    pub fn get_const(id: usize) -> Arc<str> {
85        SYMBOLS.read().unwrap().const_symbols[id - isize::MAX as usize].clone()
86    }
87
88    pub fn get_var(addr: usize, heap_id: usize) -> Option<Arc<str>> {
89        let vars = &SYMBOLS.read().unwrap().var_symbol_map;
90        if let Some(symbol) = vars.get(&(addr, heap_id)) {
91            Some(symbol.clone())
92        } else {
93            None
94        }
95    }
96
97    pub fn get_string(index: usize) -> Arc<str> {
98        // TODO: make this more efficient
99        SYMBOLS.read().unwrap().strings.get(index).unwrap().clone()
100    }
101
102    pub fn set_string(value: String) -> usize {
103        let mut write_gaurd = SYMBOLS.write().unwrap();
104        write_gaurd.strings.push(value.into());
105        write_gaurd.strings.len() - 1
106    }
107
108    pub fn _see_var_map() {
109        let symbols = SYMBOLS.read().unwrap();
110        for (k, v) in &symbols.var_symbol_map {
111            println!("{k:?}:\t{v}")
112        }
113    }
114}
115
116#[cfg(test)]
117mod tests {
118    use super::super::{heap::_CON_PTR, symbol_db::SymbolDB};
119
120    #[test]
121    //Check required symbols are preloaded
122    fn known_symbols() {
123        assert_eq!(&*SymbolDB::get_const(_CON_PTR), "false");
124        assert_eq!(&*SymbolDB::get_const(_CON_PTR + 1), "true");
125    }
126
127    #[test]
128    fn insert_constant_symbol() {
129        let id = SymbolDB::set_const("a".into());
130
131        assert_eq!(&*SymbolDB::get_const(id), "a");
132        assert_eq!(SymbolDB::set_const("a".into()), id);
133    }
134
135    #[test]
136    fn insert_variable_symbol() {
137        SymbolDB::set_var("X".into(), 100, 0);
138        SymbolDB::set_var("Y".into(), 200, 1);
139        SymbolDB::set_var("Z".into(), 200, 2);
140
141        assert_eq!(*SymbolDB::get_var(100, 0).unwrap(), *"X");
142        assert_eq!(*SymbolDB::get_var(200, 1).unwrap(), *"Y");
143        assert_eq!(*SymbolDB::get_var(200, 2).unwrap(), *"Z");
144    }
145
146    #[test]
147    fn insert_string() {
148        let idx = SymbolDB::set_string("some string".into());
149        assert_eq!(*SymbolDB::get_string(idx), *"some string");
150    }
151}