use crate::error::{Result, SourceLocation};
use crate::types::TypeInfo;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Symbol {
pub name: String,
pub kind: SymbolKind,
pub type_info: TypeInfo,
pub visibility: Visibility,
pub location: SourceLocation,
pub scope_id: usize,
pub attributes: HashMap<String, String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum SymbolKind {
Variable,
Constant,
SubProcedure,
Function,
PropertyGet,
PropertyLet,
PropertySet,
Class,
Module,
Form,
Control,
Enum,
EnumMember,
UserType,
TypeMember,
Parameter,
Label,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Default, Deserialize)]
pub enum Visibility {
#[default]
Public,
Private,
Friend,
Global,
}
#[derive(Debug, Clone)]
pub struct SymbolTable {
symbols: HashMap<usize, HashMap<String, Symbol>>,
next_scope_id: usize,
}
impl SymbolTable {
pub fn new() -> Self {
Self {
symbols: HashMap::new(),
next_scope_id: 0,
}
}
pub fn create_scope(&mut self) -> usize {
let scope_id = self.next_scope_id;
self.next_scope_id += 1;
self.symbols.insert(scope_id, HashMap::new());
scope_id
}
pub fn add_symbol(&mut self, scope_id: usize, symbol: Symbol) -> Result<()> {
let scope = self.symbols.get_mut(&scope_id).ok_or_else(|| {
crate::error::SemanticError::InvalidScope {
message: format!("Scope {} does not exist", scope_id),
}
})?;
if let Some(existing) = scope.get(&symbol.name) {
return Err(crate::error::SemanticError::DuplicateSymbol {
name: symbol.name.clone(),
location: symbol.location.clone(),
previous_location: existing.location.clone(),
});
}
scope.insert(symbol.name.clone(), symbol);
Ok(())
}
pub fn lookup_in_scope(&self, scope_id: usize, name: &str) -> Option<&Symbol> {
self.symbols.get(&scope_id)?.get(name)
}
pub fn get_scope_symbols(&self, scope_id: usize) -> Option<&HashMap<String, Symbol>> {
self.symbols.get(&scope_id)
}
pub fn get_symbols_by_kind(&self, kind: SymbolKind) -> Vec<&Symbol> {
self.symbols
.values()
.flat_map(|scope| scope.values())
.filter(|s| s.kind == kind)
.collect()
}
pub fn symbol_exists(&self, name: &str) -> bool {
self.symbols.values().any(|scope| scope.contains_key(name))
}
}
impl Default for SymbolTable {
fn default() -> Self {
Self::new()
}
}