diff --git a/crates/prometeu-compiler/src/frontends/pbs/mod.rs b/crates/prometeu-compiler/src/frontends/pbs/mod.rs index bab9356d..b0d6f114 100644 --- a/crates/prometeu-compiler/src/frontends/pbs/mod.rs +++ b/crates/prometeu-compiler/src/frontends/pbs/mod.rs @@ -6,6 +6,7 @@ pub mod types; pub mod symbols; pub mod collector; pub mod resolver; +pub mod resolve; pub mod typecheck; pub mod lowering; pub mod contracts; diff --git a/crates/prometeu-compiler/src/frontends/pbs/resolve.rs b/crates/prometeu-compiler/src/frontends/pbs/resolve.rs new file mode 100644 index 00000000..6a4dab47 --- /dev/null +++ b/crates/prometeu-compiler/src/frontends/pbs/resolve.rs @@ -0,0 +1,206 @@ +use crate::common::diagnostics::Diagnostic; +use crate::frontends::pbs::ast::{AstArena, NodeKind}; +use crate::analysis::symbols::{Symbol, SymbolArena, SymbolKind, DefIndex, DefKey, Namespace}; +use prometeu_analysis::NameInterner; + +pub fn build_def_index( + arena: &AstArena, + module: u32, + _interner: &NameInterner, +) -> (SymbolArena, DefIndex, Vec) { + let mut symbols = SymbolArena::new(); + let mut index = DefIndex::new(); + let mut diagnostics = Vec::new(); + + // No PBS, o root costuma ser um NodeKind::File que contém imports e decls. + // Mas a AstArena também tem uma lista de `roots`. + for &root_id in &arena.roots { + let root_kind = arena.kind(root_id); + if let NodeKind::File(file_node) = root_kind { + for &decl_id in &file_node.decls { + let decl_kind = arena.kind(decl_id); + let span = arena.span(decl_id); + + let result = match decl_kind { + NodeKind::FnDecl(fn_decl) => { + Some((fn_decl.name, SymbolKind::Function, Namespace::Value, fn_decl.vis.as_deref() == Some("pub"))) + } + NodeKind::TypeDecl(type_decl) => { + let kind = match type_decl.type_kind.as_str() { + "struct" => SymbolKind::Struct, + "contract" => SymbolKind::Contract, + "error" => SymbolKind::ErrorType, + _ => SymbolKind::Type, + }; + Some((type_decl.name, kind, Namespace::Type, type_decl.vis.as_deref() == Some("pub"))) + } + NodeKind::ServiceDecl(service_decl) => { + Some((service_decl.name, SymbolKind::Service, Namespace::Service, service_decl.vis.as_deref() == Some("pub"))) + } + _ => None, + }; + + if let Some((name, kind, namespace, exported)) = result { + let symbol = Symbol { + name, + kind, + exported, + module, + decl_span: span, + }; + + let symbol_id = symbols.insert(symbol); + let key = DefKey { + module, + name, + namespace, + }; + + if let Err(mut diag) = index.insert_symbol(key, symbol_id) { + diag.span = Some(span); + diagnostics.push(diag); + } + } + } + } + } + + (symbols, index, diagnostics) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::frontends::pbs::ast::{FileNodeArena, FnDeclNodeArena, TypeDeclNodeArena, NodeId}; + use crate::common::spans::Span; + + #[test] + fn test_build_def_index_success() { + let mut arena = AstArena::default(); + let mut interner = NameInterner::new(); + + let fn_name = interner.intern("my_func"); + let fn_id = arena.push(NodeKind::FnDecl(FnDeclNodeArena { + vis: Some("pub".to_string()), + name: fn_name, + params: vec![], + ret: None, + else_fallback: None, + body: NodeId(0), // Dummy + }), Span::new(0, 10, 20)); + + let type_name = interner.intern("MyStruct"); + let type_id = arena.push(NodeKind::TypeDecl(TypeDeclNodeArena { + vis: None, + type_kind: "struct".to_string(), + name: type_name, + is_host: false, + params: vec![], + constructors: vec![], + constants: vec![], + body: None, + }), Span::new(0, 30, 40)); + + let file_id = arena.push(NodeKind::File(FileNodeArena { + imports: vec![], + decls: vec![fn_id, type_id], + }), Span::new(0, 0, 100)); + + arena.roots.push(file_id); + + let (symbols, index, diagnostics) = build_def_index(&arena, 1, &interner); + + assert!(diagnostics.is_empty()); + assert_eq!(symbols.symbols.len(), 2); + + let fn_sym_id = index.get(DefKey { module: 1, name: fn_name, namespace: Namespace::Value }).unwrap(); + let fn_sym = symbols.get(fn_sym_id); + assert_eq!(fn_sym.name, fn_name); + assert_eq!(fn_sym.kind, SymbolKind::Function); + assert!(fn_sym.exported); + + let type_sym_id = index.get(DefKey { module: 1, name: type_name, namespace: Namespace::Type }).unwrap(); + let type_sym = symbols.get(type_sym_id); + assert_eq!(type_sym.name, type_name); + assert_eq!(type_sym.kind, SymbolKind::Struct); + assert!(!type_sym.exported); + } + + #[test] + fn test_build_def_index_duplicate() { + let mut arena = AstArena::default(); + let mut interner = NameInterner::new(); + + let name = interner.intern("conflict"); + + let fn1_id = arena.push(NodeKind::FnDecl(FnDeclNodeArena { + vis: None, + name, + params: vec![], + ret: None, + else_fallback: None, + body: NodeId(0), + }), Span::new(0, 10, 20)); + + let fn2_id = arena.push(NodeKind::FnDecl(FnDeclNodeArena { + vis: None, + name, + params: vec![], + ret: None, + else_fallback: None, + body: NodeId(0), + }), Span::new(0, 30, 40)); + + let file_id = arena.push(NodeKind::File(FileNodeArena { + imports: vec![], + decls: vec![fn1_id, fn2_id], + }), Span::new(0, 0, 100)); + + arena.roots.push(file_id); + + let (_symbols, _index, diagnostics) = build_def_index(&arena, 1, &interner); + + assert_eq!(diagnostics.len(), 1); + assert_eq!(diagnostics[0].code, Some("E_RESOLVE_DUPLICATE_SYMBOL".to_string())); + assert_eq!(diagnostics[0].span, Some(Span::new(0, 30, 40))); + } + + #[test] + fn test_deterministic_order() { + let mut arena = AstArena::default(); + let mut interner = NameInterner::new(); + + let name_a = interner.intern("a"); + let name_b = interner.intern("b"); + + let decl_a = arena.push(NodeKind::FnDecl(FnDeclNodeArena { + vis: None, + name: name_a, + params: vec![], + ret: None, + else_fallback: None, + body: NodeId(0), + }), Span::new(0, 10, 20)); + + let decl_b = arena.push(NodeKind::FnDecl(FnDeclNodeArena { + vis: None, + name: name_b, + params: vec![], + ret: None, + else_fallback: None, + body: NodeId(0), + }), Span::new(0, 30, 40)); + + let file_id = arena.push(NodeKind::File(FileNodeArena { + imports: vec![], + decls: vec![decl_a, decl_b], + }), Span::new(0, 0, 100)); + + arena.roots.push(file_id); + + let (symbols, _, _) = build_def_index(&arena, 1, &interner); + + assert_eq!(symbols.symbols[0].name, name_a); + assert_eq!(symbols.symbols[1].name, name_b); + } +} diff --git a/test-cartridges/canonical/golden/program.pbc b/test-cartridges/canonical/golden/program.pbc index 666448e9..b06b91ca 100644 Binary files a/test-cartridges/canonical/golden/program.pbc and b/test-cartridges/canonical/golden/program.pbc differ