use std::sync::Arc;
use gramatika::{Position, Span, Spanned, Token as _};
use super::Scope;
use crate::{
decl::{
legacy::ModuleDecl, Decl, FieldDecl, FunctionDecl, ParamDecl, StructDecl, TypeAliasDecl,
VarDecl,
},
stmt::{BlockStmt, CaseStmt, ContinuingStmt, ElseStmt, ForStmt, IfStmt, LoopStmt, Stmt},
traversal::{FlowControl, Visitor, Walk},
TokenKind,
};
pub(super) struct ScopeBuilder {
root: Arc<Scope>,
current: Arc<Scope>,
}
impl ScopeBuilder {
pub(super) fn new(span: Span) -> Self {
let root = Arc::new(Scope::new(span));
Self {
root: Arc::clone(&root),
current: root,
}
}
pub(super) fn build(self) -> Arc<Scope> {
self.root
}
fn spawn_for_decl(&self, start: Position) -> Arc<Scope> {
let end = self.current.span.end;
let span = Span { start, end };
Scope::with_parent(span, Arc::clone(&self.current))
}
fn spawn_for_block(&self, block: &BlockStmt) -> Arc<Scope> {
let start = block.brace_open.span().end;
let end = block.brace_close.span().start;
let span = Span { start, end };
Scope::with_parent(span, Arc::clone(&self.current))
}
fn pop_scope(&mut self) {
if let Some(parent) = self.current.parent() {
self.current = parent;
} else {
eprintln!("WARNING: No parent scope!");
}
}
}
macro_rules! simple_decl {
($self:ident, $variant:ident($decl:ident)) => {{
let ident = $decl.name.lexeme();
let start = $decl.name.span().start;
let kind = $decl.name.kind();
$self.current = $self.spawn_for_decl(start);
$self
.current
.define((ident, kind), Decl::$variant($decl.clone()));
}};
}
impl Visitor for ScopeBuilder {
fn visit_module_decl(&mut self, decl: &ModuleDecl) {
simple_decl!(self, Module(decl));
}
fn visit_type_alias_decl(&mut self, decl: &TypeAliasDecl) -> FlowControl {
simple_decl!(self, TypeAlias(decl));
FlowControl::Break
}
fn visit_var_decl(&mut self, decl: &VarDecl) -> FlowControl {
match decl.storage.lexeme().as_str() {
"const" | "let" => simple_decl!(self, Const(decl)),
"var" => simple_decl!(self, Var(decl)),
_ => unreachable!(),
}
FlowControl::Break
}
fn visit_struct_decl(&mut self, decl: &StructDecl) -> FlowControl {
let ident = decl.name.lexeme();
let start = decl.name.span().start;
let kind = decl.name.kind();
let starting_scope = self.spawn_for_decl(start);
self.current = Arc::clone(&starting_scope);
self.current
.define((ident, kind), Decl::Struct(decl.clone()));
for field in decl.body.fields.iter() {
field.walk(self);
}
while self.current.span != starting_scope.span {
self.pop_scope();
}
FlowControl::Break
}
fn visit_field_decl(&mut self, decl: &FieldDecl) -> FlowControl {
let ident = decl.name.lexeme();
let kind = decl.name.kind();
self.current
.define((ident, kind), Decl::Field(decl.clone()));
FlowControl::Break
}
fn visit_func_decl(&mut self, decl: &FunctionDecl) -> FlowControl {
let ident = decl.name.lexeme();
let start = decl.name.span().start;
let kind = decl.name.kind();
let starting_scope = self.spawn_for_decl(start);
self.current = Arc::clone(&starting_scope);
self.current
.define((ident, kind), Decl::Function(decl.clone()));
let end = decl.body.brace_close.span().start;
let span = Span { start, end };
self.current = Scope::with_parent(span, Arc::clone(&self.current));
for param in decl.params.iter() {
param.walk(self);
}
decl.body.walk(self);
while self.current.span != starting_scope.span {
self.pop_scope();
}
FlowControl::Break
}
fn visit_param_decl(&mut self, decl: &ParamDecl) -> FlowControl {
let ident = decl.name.lexeme();
let kind = decl.name.kind();
self.current
.define((ident, kind), Decl::Param(decl.clone()));
FlowControl::Break
}
fn visit_if_stmt(&mut self, stmt: &IfStmt) -> FlowControl {
let starting_scope = self.spawn_for_block(&stmt.then_branch);
self.current = Arc::clone(&starting_scope);
stmt.then_branch.walk(self);
while self.current.span != starting_scope.span {
self.pop_scope();
}
self.pop_scope();
FlowControl::Break
}
fn visit_else_stmt(&mut self, stmt: &ElseStmt) -> FlowControl {
let starting_scope = match stmt.body.as_ref() {
Stmt::Block(stmt) => self.spawn_for_block(stmt),
Stmt::If(stmt) => {
return self.visit_if_stmt(stmt);
}
_ => unreachable!(),
};
self.current = Arc::clone(&starting_scope);
stmt.body.walk(self);
while self.current.span != starting_scope.span {
self.pop_scope();
}
self.pop_scope();
FlowControl::Break
}
fn visit_for_stmt(&mut self, stmt: &ForStmt) -> FlowControl {
if let Some(init) = stmt.initializer.as_ref() {
let start = match init.as_ref() {
Stmt::Expr(expr) => expr.semicolon.span().end,
Stmt::Empty(token)
if matches!(token.as_matchable(), (TokenKind::Punct, ";", _)) =>
{
token.span().end
}
_ => return FlowControl::Break,
};
let end = stmt.body.brace_close.span().start;
let span = Span { start, end };
let starting_scope = Scope::with_parent(span, Arc::clone(&self.current));
self.current = Arc::clone(&starting_scope);
stmt.walk(self);
while self.current.span != starting_scope.span {
self.pop_scope();
}
self.pop_scope();
}
let starting_scope = self.spawn_for_block(&stmt.body);
self.current = Arc::clone(&starting_scope);
stmt.body.walk(self);
while self.current.span != starting_scope.span {
self.pop_scope();
}
self.pop_scope();
FlowControl::Break
}
fn visit_loop_stmt(&mut self, stmt: &LoopStmt) -> FlowControl {
let starting_scope = self.spawn_for_block(&stmt.body);
self.current = Arc::clone(&starting_scope);
stmt.body.walk(self);
while self.current.span != starting_scope.span {
self.pop_scope();
}
self.pop_scope();
FlowControl::Break
}
fn visit_continuing_stmt(&mut self, stmt: &ContinuingStmt) -> FlowControl {
let starting_scope = self.spawn_for_block(&stmt.body);
self.current = Arc::clone(&starting_scope);
stmt.body.walk(self);
while self.current.span != starting_scope.span {
self.pop_scope();
}
self.pop_scope();
FlowControl::Break
}
fn visit_case_stmt(&mut self, stmt: &CaseStmt) -> FlowControl {
let starting_scope = self.spawn_for_block(&stmt.body);
self.current = Arc::clone(&starting_scope);
stmt.body.walk(self);
while self.current.span != starting_scope.span {
self.pop_scope();
}
self.pop_scope();
FlowControl::Break
}
}