bQUARKz f9120e740b
dev/pbs (#8)
Co-authored-by: Nilton Constantino <nilton.constantino@visma.com>
Reviewed-on: #8
2026-03-24 13:40:22 +00:00

172 lines
5.7 KiB
Rust

use crate::common::diagnostics::{Diagnostic, DiagnosticBundle, DiagnosticLevel};
use crate::frontends::pbs::ast::*;
use crate::frontends::pbs::symbols::*;
use crate::semantics::export_surface::ExportSurfaceKind;
pub struct SymbolCollector {
type_symbols: SymbolTable,
value_symbols: SymbolTable,
diagnostics: Vec<Diagnostic>,
}
impl SymbolCollector {
pub fn new() -> Self {
Self {
type_symbols: SymbolTable::new(),
value_symbols: SymbolTable::new(),
diagnostics: Vec::new(),
}
}
pub fn collect(&mut self, file: &FileNode) -> Result<(SymbolTable, SymbolTable), DiagnosticBundle> {
for decl in &file.decls {
match decl {
Node::FnDecl(fn_decl) => self.collect_fn(fn_decl),
Node::ServiceDecl(service_decl) => self.collect_service(service_decl),
Node::TypeDecl(type_decl) => self.collect_type(type_decl),
_ => {}
}
}
if !self.diagnostics.is_empty() {
return Err(DiagnosticBundle {
diagnostics: self.diagnostics.clone(),
});
}
Ok((
std::mem::replace(&mut self.type_symbols, SymbolTable::new()),
std::mem::replace(&mut self.value_symbols, SymbolTable::new()),
))
}
fn collect_fn(&mut self, decl: &FnDeclNode) {
let vis = match decl.vis.as_deref() {
Some("pub") => Visibility::Pub,
Some("mod") => Visibility::Mod,
_ => Visibility::FilePrivate,
};
self.check_export_eligibility(SymbolKind::Function, vis, decl.span);
let symbol = Symbol {
name: decl.name.clone(),
kind: SymbolKind::Function,
namespace: Namespace::Value,
visibility: vis,
ty: None, // Will be resolved later
is_host: false,
span: decl.span,
origin: None,
};
self.insert_value_symbol(symbol);
}
fn collect_service(&mut self, decl: &ServiceDeclNode) {
let vis = match decl.vis.as_deref() {
Some("pub") => Visibility::Pub,
_ => Visibility::Mod, // Defaults to Mod
};
self.check_export_eligibility(SymbolKind::Service, vis, decl.span);
let symbol = Symbol {
name: decl.name.clone(),
kind: SymbolKind::Service,
namespace: Namespace::Type, // Service is a type
visibility: vis,
ty: None,
is_host: false,
span: decl.span,
origin: None,
};
self.insert_type_symbol(symbol);
}
fn collect_type(&mut self, decl: &TypeDeclNode) {
let vis = match decl.vis.as_deref() {
Some("pub") => Visibility::Pub,
Some("mod") => Visibility::Mod,
_ => Visibility::FilePrivate,
};
let kind = match decl.type_kind.as_str() {
"struct" => SymbolKind::Struct,
"contract" => SymbolKind::Contract,
"error" => SymbolKind::ErrorType,
_ => SymbolKind::Struct, // Default
};
self.check_export_eligibility(kind.clone(), vis, decl.span);
let symbol = Symbol {
name: decl.name.clone(),
kind,
namespace: Namespace::Type,
visibility: vis,
ty: None,
is_host: decl.is_host,
span: decl.span,
origin: None,
};
self.insert_type_symbol(symbol);
}
fn insert_type_symbol(&mut self, symbol: Symbol) {
// Check for collision in value namespace first
if let Some(existing) = self.value_symbols.get(&symbol.name) {
let existing = existing.clone();
self.error_collision(&symbol, &existing);
return;
}
if let Err(existing) = self.type_symbols.insert(symbol.clone()) {
self.error_duplicate(&symbol, &existing);
}
}
fn insert_value_symbol(&mut self, symbol: Symbol) {
// Check for collision in type namespace first
if let Some(existing) = self.type_symbols.get(&symbol.name) {
let existing = existing.clone();
self.error_collision(&symbol, &existing);
return;
}
if let Err(existing) = self.value_symbols.insert(symbol.clone()) {
self.error_duplicate(&symbol, &existing);
}
}
fn error_duplicate(&mut self, symbol: &Symbol, existing: &Symbol) {
self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error,
code: Some("E_RESOLVE_DUPLICATE_SYMBOL".to_string()),
message: format!("Duplicate symbol '{}' already defined at {:?}", symbol.name, existing.span),
span: Some(symbol.span),
});
}
fn error_collision(&mut self, symbol: &Symbol, existing: &Symbol) {
self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error,
code: Some("E_RESOLVE_NAMESPACE_COLLISION".to_string()),
message: format!(
"DebugSymbol '{}' collides with another symbol in the {:?} namespace defined at {:?}",
symbol.name, existing.namespace, existing.span
),
span: Some(symbol.span),
});
}
fn check_export_eligibility(&mut self, kind: SymbolKind, vis: Visibility, span: crate::common::spans::Span) {
if let Err(msg) = ExportSurfaceKind::validate_visibility(kind, vis) {
self.diagnostics.push(Diagnostic {
level: DiagnosticLevel::Error,
code: Some("E_SEMANTIC_EXPORT_RESTRICTION".to_string()),
message: msg,
span: Some(span),
});
}
}
}