use crate::ir::analysis::symbol_trait::SymbolInfo;
use crate::ir::analysis::type_inference::{SymbolType, type_from_name};
use crate::ir::transform::constants::global_builtins;
use indexmap::IndexMap;
use std::collections::HashSet;
#[derive(Debug, Clone, PartialEq)]
pub struct Symbol {
pub local_name: String,
pub qualified_name: String,
pub type_name: String,
pub is_parameter: bool,
}
impl SymbolInfo for Symbol {
fn name(&self) -> &str {
&self.local_name
}
fn qualified_name(&self) -> &str {
&self.qualified_name
}
fn symbol_type(&self) -> SymbolType {
type_from_name(&self.type_name)
}
fn line(&self) -> u32 {
0 }
fn column(&self) -> u32 {
0 }
fn is_parameter(&self) -> bool {
self.is_parameter
}
}
#[derive(Debug, Clone)]
pub struct SymbolTable {
symbols: IndexMap<String, Symbol>,
global_symbols: HashSet<String>,
}
impl Default for SymbolTable {
fn default() -> Self {
Self::new()
}
}
impl SymbolTable {
pub fn new() -> Self {
let global_symbols: HashSet<String> = global_builtins().into_iter().collect();
Self {
symbols: IndexMap::new(),
global_symbols,
}
}
pub fn add_symbol(
&mut self,
local_name: &str,
qualified_name: &str,
type_name: &str,
is_parameter: bool,
) {
self.symbols.insert(
local_name.to_string(),
Symbol {
local_name: local_name.to_string(),
qualified_name: qualified_name.to_string(),
type_name: type_name.to_string(),
is_parameter,
},
);
}
pub fn is_global(&self, name: &str) -> bool {
self.global_symbols.contains(name)
}
pub fn add_global(&mut self, name: &str) {
self.global_symbols.insert(name.to_string());
}
pub fn remove_global(&mut self, name: &str) {
self.global_symbols.remove(name);
}
pub fn remove(&mut self, name: &str) {
self.symbols.shift_remove(name);
self.global_symbols.remove(name);
}
pub fn lookup(&self, name: &str) -> Option<&Symbol> {
self.symbols.get(name)
}
pub fn contains(&self, name: &str) -> bool {
self.symbols.contains_key(name) || self.global_symbols.contains(name)
}
pub fn get_qualified_name(&self, local_name: &str) -> Option<&str> {
self.symbols
.get(local_name)
.map(|s| s.qualified_name.as_str())
}
pub fn symbols(&self) -> &IndexMap<String, Symbol> {
&self.symbols
}
pub fn has_prefix(&self, prefix: &str) -> bool {
let prefix_dot = format!("{}.", prefix);
self.symbols.keys().any(|k| k.starts_with(&prefix_dot))
}
pub fn clear(&mut self) {
self.symbols.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_symbol_table_basics() {
let mut table = SymbolTable::new();
table.add_symbol("x", "e1_x", "Real", false);
assert!(table.contains("x"));
let sym = table.lookup("x").unwrap();
assert_eq!(sym.qualified_name, "e1_x");
assert_eq!(sym.type_name, "Real");
assert!(!sym.is_parameter);
assert!(table.is_global("time"));
assert!(table.is_global("der"));
assert!(!table.is_global("x"));
assert!(table.contains("time"));
assert!(table.contains("x"));
assert!(!table.contains("unknown"));
}
#[test]
fn test_qualified_name_lookup() {
let mut table = SymbolTable::new();
table.add_symbol("x", "comp_x", "Real", false);
table.add_symbol("k", "comp_k", "Real", true);
assert_eq!(table.get_qualified_name("x"), Some("comp_x"));
assert_eq!(table.get_qualified_name("k"), Some("comp_k"));
assert_eq!(table.get_qualified_name("unknown"), None);
}
}