pub mod function_symbol;
pub use function_symbol::*;
pub mod variable_symbol;
pub use variable_symbol::*;
use std::cell::RefCell;
use leo_ast::{Function, Struct};
use leo_errors::{AstError, Result};
use leo_span::{Span, Symbol};
use indexmap::IndexMap;
#[derive(Clone, Debug, Default)]
pub struct SymbolTable {
pub(crate) parent: Option<Box<SymbolTable>>,
pub functions: IndexMap<Symbol, FunctionSymbol>,
pub structs: IndexMap<Symbol, Struct>,
pub(crate) variables: IndexMap<Symbol, VariableSymbol>,
pub(crate) scope_index: usize,
pub(crate) scopes: Vec<RefCell<SymbolTable>>,
}
impl SymbolTable {
pub fn check_shadowing(&self, symbol: Symbol, span: Span) -> Result<()> {
if self.variables.contains_key(&symbol) {
Err(AstError::shadowed_variable(symbol, span).into())
} else if self.functions.contains_key(&symbol) {
Err(AstError::shadowed_function(symbol, span).into())
} else if let Some(existing) = self.structs.get(&symbol) {
match existing.is_record {
true => Err(AstError::shadowed_record(symbol, span).into()),
false => Err(AstError::shadowed_struct(symbol, span).into()),
}
} else if let Some(parent) = self.parent.as_ref() {
parent.check_shadowing(symbol, span)
} else {
Ok(())
}
}
pub fn scope_index(&mut self) -> usize {
let index = self.scope_index;
self.scope_index += 1;
index
}
pub fn insert_fn(&mut self, symbol: Symbol, insert: &Function) -> Result<()> {
self.check_shadowing(symbol, insert.span)?;
let id = self.scope_index();
self.functions.insert(symbol, Self::new_function_symbol(id, insert));
self.scopes.push(Default::default());
Ok(())
}
pub fn insert_struct(&mut self, symbol: Symbol, insert: &Struct) -> Result<()> {
self.check_shadowing(symbol, insert.span)?;
self.structs.insert(symbol, insert.clone());
Ok(())
}
pub fn insert_variable(&mut self, symbol: Symbol, insert: VariableSymbol) -> Result<()> {
self.check_shadowing(symbol, insert.span)?;
self.variables.insert(symbol, insert);
Ok(())
}
pub fn insert_block(&mut self) -> usize {
self.scopes.push(RefCell::new(Default::default()));
self.scope_index()
}
pub fn lookup_fn_symbol(&self, symbol: Symbol) -> Option<&FunctionSymbol> {
if let Some(func) = self.functions.get(&symbol) {
Some(func)
} else if let Some(parent) = self.parent.as_ref() {
parent.lookup_fn_symbol(symbol)
} else {
None
}
}
pub fn lookup_struct(&self, symbol: Symbol) -> Option<&Struct> {
if let Some(struct_) = self.structs.get(&symbol) {
Some(struct_)
} else if let Some(parent) = self.parent.as_ref() {
parent.lookup_struct(symbol)
} else {
None
}
}
pub fn lookup_variable(&self, symbol: Symbol) -> Option<&VariableSymbol> {
if let Some(var) = self.variables.get(&symbol) {
Some(var)
} else if let Some(parent) = self.parent.as_ref() {
parent.lookup_variable(symbol)
} else {
None
}
}
pub fn variable_in_local_scope(&self, symbol: Symbol) -> bool {
self.variables.contains_key(&symbol)
}
pub fn variable_in_parent_scope(&self, symbol: Symbol) -> bool {
if let Some(parent) = self.parent.as_ref() {
if parent.variables.contains_key(&symbol) { true } else { parent.variable_in_parent_scope(symbol) }
} else {
false
}
}
pub fn lookup_variable_mut(&mut self, symbol: Symbol) -> Option<&mut VariableSymbol> {
if let Some(var) = self.variables.get_mut(&symbol) {
Some(var)
} else if let Some(parent) = self.parent.as_mut() {
parent.lookup_variable_mut(symbol)
} else {
None
}
}
pub fn lookup_fn_scope(&self, symbol: Symbol) -> Option<&RefCell<Self>> {
self.lookup_fn_symbol(symbol).and_then(|func| self.scopes.get(func.id))
}
pub fn lookup_scope_by_index(&self, index: usize) -> Option<&RefCell<Self>> {
self.scopes.get(index)
}
}