pr 04.e
This commit is contained in:
parent
5fff614708
commit
737780d7d5
@ -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;
|
||||
|
||||
206
crates/prometeu-compiler/src/frontends/pbs/resolve.rs
Normal file
206
crates/prometeu-compiler/src/frontends/pbs/resolve.rs
Normal file
@ -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<Diagnostic>) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user