use crate::ast::HierarchicalId;
use crate::ast_lowering::error::Type::NotAScope;
use crate::ast_lowering::error::*;
#[doc(inline)]
pub use crate::resolve;
#[doc(inline)]
pub use crate::resolve_hierarchical;
use crate::symbol::Ident;
use crate::symbol_table::{SymbolDeclaration, SymbolTable};
use crate::Ast;
#[doc(inline)]
#[macro_export]
macro_rules! resolve {
($fold:expr; $name:ident as $($declaration:ident($id:ident) => $block:block),+) => {
match $fold.resolver.resolve(&$name) {
$(Ok($crate::symbol_table::SymbolDeclaration::$declaration($id)) => $block),+
Err(error) => {
$fold.error(error);
}
Ok(found) => {
use $crate::ast_lowering::error;
$fold.error(Error {
error_type: error::Type::DeclarationTypeMismatch {
found,
expected: vec![$(error::MockSymbolDeclaration::$declaration),+],
},
source: $name.span,
});
}
}
};
}
#[macro_export]
macro_rules! resolve_hierarchical {
($fold:expr; $name:ident as $($declaration:ident($id:ident) => $block:block),+) => {
match $fold.resolver.resolve_hierarchical($name) {
$(Ok($crate::symbol_table::SymbolDeclaration::$declaration($id)) => $block),+
Err(error) => {
$fold.error(error);
}
Ok(found) => {
use $crate::ast_lowering::error::*;
$fold.error(Error {
error_type: Type::DeclarationTypeMismatch {
found,
expected: vec![$(MockSymbolDeclaration::$declaration),+],
},
source: $name.span(),
});
}
}
};
}
pub struct Resolver<'lt> {
pub scope_stack: Vec<&'lt SymbolTable>,
ast: &'lt Ast,
inside_function: bool,
}
impl<'lt> Resolver<'lt> {
pub fn new(ast: &'lt Ast) -> Self {
Self {
scope_stack: Vec::with_capacity(8),
ast,
inside_function: false,
}
}
pub fn enter_function(&mut self, function_symbol_table: &'lt SymbolTable) {
debug_assert!(!self.inside_function, "Already inside a function!");
self.inside_function = true;
self.enter_scope(function_symbol_table);
}
pub fn exit_function(&mut self) -> Option<&'lt SymbolTable> {
debug_assert!(self.inside_function, "Not yet inside a function!");
self.inside_function = false;
self.exit_scope()
}
pub fn resolve(&self, ident: &Ident) -> Result<SymbolDeclaration> {
let mut depth = 0;
for scope in self.scope_stack.iter().rev() {
if let Some(res) = scope.get(&ident.name) {
if self.inside_function
&& depth > 0
&& !matches!(res,SymbolDeclaration::Parameter(_)|SymbolDeclaration::Module(_))
{
return Err(Error {
error_type: Type::NotAllowedInFunction(
NotAllowedInFunction::NonLocalAccess,
),
source: ident.span,
});
}
return Ok(*res);
}
depth += 1;
}
Err(Error {
error_type: Type::NotFound(ident.name),
source: ident.span,
})
}
pub fn resolve_hierarchical(
&self,
hierarchical_ident: &HierarchicalId,
) -> Result<SymbolDeclaration> {
let mut identifiers = hierarchical_ident.names.iter();
let (mut current_span, mut current_declaration) = {
let first_ident = identifiers.next().unwrap();
(first_ident.span, self.resolve(first_ident)?)
};
for ident in identifiers {
let symbol_table = match current_declaration {
SymbolDeclaration::Module(module) => &self.ast[module].contents.symbol_table,
SymbolDeclaration::Block(_) if self.inside_function => {
return Err(Error {
error_type: Type::NotAllowedInFunction(
NotAllowedInFunction::NonLocalAccess,
),
source: current_span,
});
}
SymbolDeclaration::Block(block_id) => {
if let Some(scope) = &self.ast[block_id].contents.scope {
&scope.symbols
} else {
return Err(Error {
error_type: Type::NotFound(ident.name),
source: current_span,
});
}
}
item_without_scope => {
return Err(Error {
error_type: NotAScope {
declaration: item_without_scope.span(self.ast),
name: item_without_scope.name(self.ast),
},
source: current_span,
});
}
};
if let Some(found) = symbol_table.get(&ident.name) {
current_declaration = *found;
current_span = found.span(self.ast);
if self.inside_function
&& !matches!(found,SymbolDeclaration::Parameter(_)|SymbolDeclaration::Module(_))
{
return Err(Error {
error_type: Type::NotAllowedInFunction(
NotAllowedInFunction::NonLocalAccess,
),
source: ident.span,
});
}
} else {
return Err(Error {
error_type: Type::NotFound(ident.name),
source: ident.span,
});
}
}
Ok(current_declaration)
}
pub fn enter_scope(&mut self, scope_symbol_table: &'lt SymbolTable) {
self.scope_stack.push(scope_symbol_table)
}
pub fn exit_scope(&mut self) -> Option<&'lt SymbolTable> {
self.scope_stack.pop()
}
}