From 6cbcddfd99cf2b1bcebbf15ed0733342f941e674 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Wed, 4 Feb 2026 19:27:06 +0000 Subject: [PATCH] pr 04.g --- .../src/frontends/pbs/resolve.rs | 260 +++++++++++++++++- test-cartridges/canonical/golden/program.pbc | Bin 3727 -> 3727 bytes 2 files changed, 245 insertions(+), 15 deletions(-) diff --git a/crates/prometeu-compiler/src/frontends/pbs/resolve.rs b/crates/prometeu-compiler/src/frontends/pbs/resolve.rs index cc01988d..2bd80433 100644 --- a/crates/prometeu-compiler/src/frontends/pbs/resolve.rs +++ b/crates/prometeu-compiler/src/frontends/pbs/resolve.rs @@ -1,20 +1,20 @@ -use crate::common::diagnostics::Diagnostic; -use crate::frontends::pbs::ast::{AstArena, NodeKind}; -use crate::analysis::symbols::{Symbol, SymbolArena, SymbolKind, DefIndex, DefKey, Namespace, NodeToSymbol}; +use crate::common::diagnostics::{Diagnostic, DiagnosticLevel}; +use crate::frontends::pbs::ast::{AstArena, NodeKind, NodeId}; +use crate::analysis::symbols::{Symbol, SymbolArena, SymbolKind, DefIndex, DefKey, Namespace, NodeToSymbol, RefIndex}; use prometeu_analysis::NameInterner; pub fn build_def_index( arena: &AstArena, module: u32, _interner: &NameInterner, -) -> (SymbolArena, DefIndex, NodeToSymbol, Vec) { +) -> (SymbolArena, DefIndex, RefIndex, NodeToSymbol, Vec) { let mut symbols = SymbolArena::new(); let mut index = DefIndex::new(); + let mut ref_index = RefIndex::new(); let mut node_to_symbol = NodeToSymbol::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`. + // Passo 1: Coletar definições top-level for &root_id in &arena.roots { let root_kind = arena.kind(root_id); if let NodeKind::File(file_node) = root_kind { @@ -67,13 +67,152 @@ pub fn build_def_index( } } - (symbols, index, node_to_symbol, diagnostics) + // Passo 2: Resolver referências (Identifiers) + for &root_id in &arena.roots { + walk_node(root_id, arena, module, &index, &mut ref_index, &mut node_to_symbol, &mut diagnostics); + } + + (symbols, index, ref_index, node_to_symbol, diagnostics) +} + +fn walk_node( + node_id: NodeId, + arena: &AstArena, + module: u32, + index: &DefIndex, + ref_index: &mut RefIndex, + node_to_symbol: &mut NodeToSymbol, + diagnostics: &mut Vec, +) { + let kind = arena.kind(node_id); + let span = arena.span(node_id); + + match kind { + NodeKind::File(file) => { + for &decl in &file.decls { + walk_node(decl, arena, module, index, ref_index, node_to_symbol, diagnostics); + } + } + NodeKind::FnDecl(fn_decl) => { + walk_node(fn_decl.body, arena, module, index, ref_index, node_to_symbol, diagnostics); + } + NodeKind::Block(block) => { + for &stmt in &block.stmts { + walk_node(stmt, arena, module, index, ref_index, node_to_symbol, diagnostics); + } + if let Some(tail) = block.tail { + walk_node(tail, arena, module, index, ref_index, node_to_symbol, diagnostics); + } + } + NodeKind::LetStmt(let_stmt) => { + walk_node(let_stmt.init, arena, module, index, ref_index, node_to_symbol, diagnostics); + } + NodeKind::ExprStmt(expr_stmt) => { + walk_node(expr_stmt.expr, arena, module, index, ref_index, node_to_symbol, diagnostics); + } + NodeKind::ReturnStmt(ret) => { + if let Some(expr) = ret.expr { + walk_node(expr, arena, module, index, ref_index, node_to_symbol, diagnostics); + } + } + NodeKind::Ident(ident) => { + let key = DefKey { + module, + name: ident.name, + namespace: Namespace::Value, + }; + + if let Some(symbol_id) = index.get(key) { + ref_index.record_ref(symbol_id, span); + node_to_symbol.bind_node(node_id, symbol_id); + } else { + diagnostics.push(Diagnostic { + level: DiagnosticLevel::Error, + code: Some("E_RESOLVE_UNDEFINED_IDENTIFIER".to_string()), + message: format!("Undefined identifier"), + span: Some(span), + }); + } + } + NodeKind::Call(call) => { + walk_node(call.callee, arena, module, index, ref_index, node_to_symbol, diagnostics); + for &arg in &call.args { + walk_node(arg, arena, module, index, ref_index, node_to_symbol, diagnostics); + } + } + NodeKind::Unary(unary) => { + walk_node(unary.expr, arena, module, index, ref_index, node_to_symbol, diagnostics); + } + NodeKind::Binary(binary) => { + walk_node(binary.left, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(binary.right, arena, module, index, ref_index, node_to_symbol, diagnostics); + } + NodeKind::Cast(cast) => { + walk_node(cast.expr, arena, module, index, ref_index, node_to_symbol, diagnostics); + } + NodeKind::IfExpr(if_expr) => { + walk_node(if_expr.cond, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(if_expr.then_block, arena, module, index, ref_index, node_to_symbol, diagnostics); + if let Some(else_block) = if_expr.else_block { + walk_node(else_block, arena, module, index, ref_index, node_to_symbol, diagnostics); + } + } + NodeKind::WhenExpr(when_expr) => { + for &arm in &when_expr.arms { + walk_node(arm, arena, module, index, ref_index, node_to_symbol, diagnostics); + } + } + NodeKind::WhenArm(when_arm) => { + walk_node(when_arm.cond, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(when_arm.body, arena, module, index, ref_index, node_to_symbol, diagnostics); + } + NodeKind::Alloc(alloc) => { + walk_node(alloc.ty, arena, module, index, ref_index, node_to_symbol, diagnostics); + } + NodeKind::Mutate(mutate) => { + walk_node(mutate.target, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(mutate.body, arena, module, index, ref_index, node_to_symbol, diagnostics); + } + NodeKind::Borrow(borrow) => { + walk_node(borrow.target, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(borrow.body, arena, module, index, ref_index, node_to_symbol, diagnostics); + } + NodeKind::Peek(peek) => { + walk_node(peek.target, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(peek.body, arena, module, index, ref_index, node_to_symbol, diagnostics); + } + NodeKind::MemberAccess(member) => { + walk_node(member.object, arena, module, index, ref_index, node_to_symbol, diagnostics); + } + NodeKind::TypeDecl(type_decl) => { + if let Some(body) = type_decl.body { + walk_node(body, arena, module, index, ref_index, node_to_symbol, diagnostics); + } + } + NodeKind::TypeBody(body) => { + for &method in &body.methods { + walk_node(method, arena, module, index, ref_index, node_to_symbol, diagnostics); + } + } + NodeKind::ServiceDecl(service) => { + for &member in &service.members { + walk_node(member, arena, module, index, ref_index, node_to_symbol, diagnostics); + } + } + // Nós literais ou que não contém outros nós para percorrer (neste nível) + NodeKind::IntLit(_) | NodeKind::FloatLit(_) | NodeKind::BoundedLit(_) | NodeKind::StringLit(_) | + NodeKind::TypeName(_) | NodeKind::TypeApp(_) | NodeKind::Import(_) | NodeKind::ImportSpec(_) | + NodeKind::ServiceFnSig(_) | NodeKind::ConstructorDecl(_) | NodeKind::ConstantDecl(_) => {} + } } #[cfg(test)] mod tests { use super::*; - use crate::frontends::pbs::ast::{FileNodeArena, FnDeclNodeArena, TypeDeclNodeArena, NodeId}; + use crate::frontends::pbs::ast::{ + AstArena, BlockNodeArena, CallNodeArena, ExprStmtNodeArena, FileNodeArena, FnDeclNodeArena, + IdentNodeArena, NodeId, TypeDeclNodeArena, + }; use crate::common::spans::Span; #[test] @@ -81,6 +220,9 @@ mod tests { let mut arena = AstArena::default(); let mut interner = NameInterner::new(); + // Create a dummy body for the function to avoid panic/invalid access + let body_id = arena.push(NodeKind::Block(BlockNodeArena { stmts: vec![], tail: None }), Span::new(0, 0, 0)); + let fn_name = interner.intern("my_func"); let fn_id = arena.push(NodeKind::FnDecl(FnDeclNodeArena { vis: Some("pub".to_string()), @@ -88,7 +230,7 @@ mod tests { params: vec![], ret: None, else_fallback: None, - body: NodeId(0), // Dummy + body: body_id, }), Span::new(0, 10, 20)); let type_name = interner.intern("MyStruct"); @@ -110,7 +252,7 @@ mod tests { arena.roots.push(file_id); - let (symbols, index, _node_to_symbol, diagnostics) = build_def_index(&arena, 1, &interner); + let (symbols, index, _ref_index, _node_to_symbol, diagnostics) = build_def_index(&arena, 1, &interner); assert!(diagnostics.is_empty()); assert_eq!(symbols.symbols.len(), 2); @@ -135,13 +277,15 @@ mod tests { let name = interner.intern("conflict"); + let body_id = arena.push(NodeKind::Block(BlockNodeArena { stmts: vec![], tail: None }), Span::new(0, 0, 0)); + let fn1_id = arena.push(NodeKind::FnDecl(FnDeclNodeArena { vis: None, name, params: vec![], ret: None, else_fallback: None, - body: NodeId(0), + body: body_id, }), Span::new(0, 10, 20)); let fn2_id = arena.push(NodeKind::FnDecl(FnDeclNodeArena { @@ -150,7 +294,7 @@ mod tests { params: vec![], ret: None, else_fallback: None, - body: NodeId(0), + body: body_id, }), Span::new(0, 30, 40)); let file_id = arena.push(NodeKind::File(FileNodeArena { @@ -160,7 +304,7 @@ mod tests { arena.roots.push(file_id); - let (_symbols, _index, _node_to_symbol, diagnostics) = build_def_index(&arena, 1, &interner); + let (_symbols, _index, _ref_index, _node_to_symbol, diagnostics) = build_def_index(&arena, 1, &interner); assert_eq!(diagnostics.len(), 1); assert_eq!(diagnostics[0].code, Some("E_RESOLVE_DUPLICATE_SYMBOL".to_string())); @@ -173,13 +317,14 @@ mod tests { let mut interner = NameInterner::new(); let name = interner.intern("bound_func"); + let body_id = arena.push(NodeKind::Block(BlockNodeArena { stmts: vec![], tail: None }), Span::new(0, 0, 0)); let decl_id = arena.push(NodeKind::FnDecl(FnDeclNodeArena { vis: None, name, params: vec![], ret: None, else_fallback: None, - body: NodeId(0), + body: body_id, }), Span::new(0, 10, 20)); let file_id = arena.push(NodeKind::File(FileNodeArena { @@ -189,10 +334,95 @@ mod tests { arena.roots.push(file_id); - let (symbols, _index, node_to_symbol, _diagnostics) = build_def_index(&arena, 1, &interner); + let (symbols, _index, _ref_index, node_to_symbol, _diagnostics) = build_def_index(&arena, 1, &interner); let symbol_id = node_to_symbol.get(decl_id).expect("Node should be bound to a symbol"); let symbol = symbols.get(symbol_id); assert_eq!(symbol.name, name); } + + #[test] + fn test_resolve_undefined_identifier() { + let mut arena = AstArena::default(); + let mut interner = NameInterner::new(); + + let ident_name = interner.intern("unknown"); + let ident_id = arena.push(NodeKind::Ident(IdentNodeArena { name: ident_name }), Span::new(0, 50, 60)); + let expr_stmt = arena.push(NodeKind::ExprStmt(ExprStmtNodeArena { expr: ident_id }), Span::new(0, 50, 60)); + let body_id = arena.push(NodeKind::Block(BlockNodeArena { stmts: vec![expr_stmt], tail: None }), Span::new(0, 40, 70)); + + let fn_id = arena.push(NodeKind::FnDecl(FnDeclNodeArena { + vis: None, + name: interner.intern("main"), + params: vec![], + ret: None, + else_fallback: None, + body: body_id, + }), Span::new(0, 10, 80)); + + let file_id = arena.push(NodeKind::File(FileNodeArena { + imports: vec![], + decls: vec![fn_id], + }), Span::new(0, 0, 100)); + + arena.roots.push(file_id); + + let (_symbols, _index, _ref_index, _node_to_symbol, diagnostics) = build_def_index(&arena, 1, &interner); + + assert_eq!(diagnostics.len(), 1); + assert_eq!(diagnostics[0].code, Some("E_RESOLVE_UNDEFINED_IDENTIFIER".to_string())); + assert_eq!(diagnostics[0].span, Some(Span::new(0, 50, 60))); + } + + #[test] + fn test_resolve_reference_success() { + let mut arena = AstArena::default(); + let mut interner = NameInterner::new(); + + // fn target() {} + let target_name = interner.intern("target"); + let target_body = arena.push(NodeKind::Block(BlockNodeArena { stmts: vec![], tail: None }), Span::new(0, 5, 5)); + let target_id = arena.push(NodeKind::FnDecl(FnDeclNodeArena { + vis: None, + name: target_name, + params: vec![], + ret: None, + else_fallback: None, + body: target_body, + }), Span::new(0, 0, 10)); + + // fn caller() { target(); } + let ident_id = arena.push(NodeKind::Ident(IdentNodeArena { name: target_name }), Span::new(0, 50, 56)); + let call_id = arena.push(NodeKind::Call(CallNodeArena { callee: ident_id, args: vec![] }), Span::new(0, 50, 58)); + let expr_stmt = arena.push(NodeKind::ExprStmt(ExprStmtNodeArena { expr: call_id }), Span::new(0, 50, 58)); + let body_id = arena.push(NodeKind::Block(BlockNodeArena { stmts: vec![expr_stmt], tail: None }), Span::new(0, 40, 70)); + + let caller_id = arena.push(NodeKind::FnDecl(FnDeclNodeArena { + vis: None, + name: interner.intern("caller"), + params: vec![], + ret: None, + else_fallback: None, + body: body_id, + }), Span::new(0, 30, 80)); + + let file_id = arena.push(NodeKind::File(FileNodeArena { + imports: vec![], + decls: vec![target_id, caller_id], + }), Span::new(0, 0, 100)); + + arena.roots.push(file_id); + + let (symbols, index, ref_index, node_to_symbol, diagnostics) = build_def_index(&arena, 1, &interner); + + assert!(diagnostics.is_empty(), "Diagnostics should be empty: {:?}", diagnostics); + + let target_sym_id = index.get(DefKey { module: 1, name: target_name, namespace: Namespace::Value }).expect("target should be in index"); + let refs = ref_index.refs_of(target_sym_id); + assert_eq!(refs.len(), 1); + assert_eq!(refs[0], Span::new(0, 50, 56)); + + let bound_id = node_to_symbol.get(ident_id).expect("ident should be bound to symbol"); + assert_eq!(bound_id, target_sym_id); + } } diff --git a/test-cartridges/canonical/golden/program.pbc b/test-cartridges/canonical/golden/program.pbc index 666448e9c82fd9759bc1514ce6c916eaa9193fac..b06b91caaacaf4416129e782584fa51dc847ec21 100644 GIT binary patch delta 47 wcmeB|?U&tadA