From 4e66387f4e865af48e4abada98a6c8872ed239d9 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Wed, 4 Feb 2026 13:37:38 +0000 Subject: [PATCH] pr 02 --- Cargo.lock | 1 + crates/prometeu-analysis/src/interner.rs | 54 +++ crates/prometeu-analysis/src/lib.rs | 2 + crates/prometeu-compiler/Cargo.toml | 1 + .../prometeu-compiler/src/building/output.rs | 63 ++-- .../prometeu-compiler/src/common/symbols.rs | 12 +- .../src/frontends/pbs/ast.rs | 37 +- .../src/frontends/pbs/collector.rs | 35 +- .../src/frontends/pbs/lowering.rs | 315 ++++++++++++------ .../src/frontends/pbs/mod.rs | 12 +- .../src/frontends/pbs/parser.rs | 172 ++++++---- .../src/frontends/pbs/resolver.rs | 135 ++++---- .../src/frontends/pbs/symbols.rs | 17 +- .../src/frontends/pbs/typecheck.rs | 135 ++++---- crates/prometeu-compiler/src/sources.rs | 10 +- .../tests/generate_canonical_goldens.rs | 4 +- test-cartridges/canonical/golden/ast.json | 180 +++++----- 17 files changed, 733 insertions(+), 452 deletions(-) create mode 100644 crates/prometeu-analysis/src/interner.rs diff --git a/Cargo.lock b/Cargo.lock index ca679389..8bddab96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1907,6 +1907,7 @@ dependencies = [ "anyhow", "clap", "prometeu-abi", + "prometeu-analysis", "prometeu-bytecode", "serde", "serde_json", diff --git a/crates/prometeu-analysis/src/interner.rs b/crates/prometeu-analysis/src/interner.rs new file mode 100644 index 00000000..135dfcc4 --- /dev/null +++ b/crates/prometeu-analysis/src/interner.rs @@ -0,0 +1,54 @@ +use std::collections::HashMap; + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)] +pub struct NameId(pub u32); + +#[derive(Debug, Default)] +pub struct NameInterner { + names: Vec, + ids: HashMap, +} + +impl NameInterner { + pub fn new() -> Self { + Self { + names: Vec::new(), + ids: HashMap::new(), + } + } + + pub fn intern(&mut self, s: &str) -> NameId { + if let Some(id) = self.ids.get(s) { + return *id; + } + + let id = NameId(self.names.len() as u32); + self.names.push(s.to_string()); + self.ids.insert(self.names[id.0 as usize].clone(), id); + id + } + + pub fn resolve(&self, id: NameId) -> &str { + &self.names[id.0 as usize] + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn interner_intern_resolve_roundtrip() { + let mut interner = NameInterner::new(); + let id = interner.intern("foo"); + assert_eq!(interner.resolve(id), "foo"); + } + + #[test] + fn interner_dedups_strings() { + let mut interner = NameInterner::new(); + let id1 = interner.intern("bar"); + let id2 = interner.intern("bar"); + assert_eq!(id1, id2); + } +} \ No newline at end of file diff --git a/crates/prometeu-analysis/src/lib.rs b/crates/prometeu-analysis/src/lib.rs index a70494c1..6fbd2fdc 100644 --- a/crates/prometeu-analysis/src/lib.rs +++ b/crates/prometeu-analysis/src/lib.rs @@ -1,7 +1,9 @@ pub mod ids; pub mod span; pub mod file_db; +pub mod interner; pub use ids::FileId; pub use span::Span; pub use file_db::{FileDB, LineIndex}; +pub use interner::{NameId, NameInterner}; diff --git a/crates/prometeu-compiler/Cargo.toml b/crates/prometeu-compiler/Cargo.toml index f2ac116f..c0e290e6 100644 --- a/crates/prometeu-compiler/Cargo.toml +++ b/crates/prometeu-compiler/Cargo.toml @@ -16,6 +16,7 @@ include = ["../../VERSION.txt"] [dependencies] prometeu-bytecode = { path = "../prometeu-bytecode" } prometeu-abi = { path = "../prometeu-abi" } +prometeu-analysis = { path = "../prometeu-analysis" } clap = { version = "4.5.54", features = ["derive"] } serde = { version = "1.0.228", features = ["derive"] } serde_json = "1.0.149" diff --git a/crates/prometeu-compiler/src/building/output.rs b/crates/prometeu-compiler/src/building/output.rs index d27c7435..c9e1ea10 100644 --- a/crates/prometeu-compiler/src/building/output.rs +++ b/crates/prometeu-compiler/src/building/output.rs @@ -15,6 +15,7 @@ use crate::frontends::pbs::types::PbsType; use crate::lowering::core_to_vm; use crate::semantics::export_surface::ExportSurfaceKind; use prometeu_bytecode::{ConstantPoolEntry, DebugInfo, FunctionMeta}; +use prometeu_analysis::NameInterner; use serde::{Deserialize, Serialize}; use std::collections::{BTreeMap, HashMap}; @@ -112,6 +113,7 @@ pub fn compile_project( dep_modules: &HashMap, file_manager: &mut FileManager, ) -> Result { + let mut interner = NameInterner::new(); // 1. Parse all files and group symbols by module let mut module_symbols_map: HashMap = HashMap::new(); let mut parsed_files: Vec<(String, FileNode)> = Vec::new(); // (module_path, ast) @@ -121,10 +123,10 @@ pub fn compile_project( let source_code = std::fs::read_to_string(&source_abs)?; let file_id = file_manager.add(source_abs.clone(), source_code.clone()); - let mut parser = Parser::new(&source_code, file_id); + let mut parser = Parser::new(&source_code, file_id, &mut interner); let ast = parser.parse_file()?; - let mut collector = SymbolCollector::new(); + let mut collector = SymbolCollector::new(&interner); let (ts, vs) = collector.collect(&ast)?; let full_path = source_rel.to_string_lossy().replace('\\', "/"); @@ -145,20 +147,30 @@ pub fn compile_project( // Merge symbols for sym in ts.symbols.into_values() { - if let Err(existing) = ms.type_symbols.insert(sym) { + if let Some(existing) = ms.type_symbols.get(sym.name) { return Err(DiagnosticBundle::error( - format!("Duplicate type symbol '{}' in module '{}'", existing.name, module_path), + format!( + "Duplicate type symbol '{}' in module '{}'", + interner.resolve(existing.name), + module_path + ), Some(existing.span) ).into()); } + let _ = ms.type_symbols.insert(sym); } for sym in vs.symbols.into_values() { - if let Err(existing) = ms.value_symbols.insert(sym) { + if let Some(existing) = ms.value_symbols.get(sym.name) { return Err(DiagnosticBundle::error( - format!("Duplicate value symbol '{}' in module '{}'", existing.name, module_path), + format!( + "Duplicate value symbol '{}' in module '{}'", + interner.resolve(existing.name), + module_path + ), Some(existing.span) ).into()); } + let _ = ms.value_symbols.insert(sym); } parsed_files.push((module_path, ast)); @@ -180,7 +192,7 @@ pub fn compile_project( let ms = all_visible_modules.entry(synthetic_module_path.clone()).or_insert_with(ModuleSymbols::new); let sym = Symbol { - name: key.symbol_name.clone(), + name: interner.intern(&key.symbol_name), kind: match key.kind { ExportSurfaceKind::Service => SymbolKind::Service, ExportSurfaceKind::DeclareType => { @@ -200,21 +212,23 @@ pub fn compile_project( }; if sym.namespace == Namespace::Type { - if let Err(existing) = ms.type_symbols.insert(sym.clone()) { + if let Some(existing) = ms.type_symbols.get(sym.name) { return Err(CompileError::DuplicateExport { - symbol: sym.name, - first_dep: existing.origin.unwrap_or_else(|| "unknown".to_string()), + symbol: interner.resolve(sym.name).to_string(), + first_dep: existing.origin.clone().unwrap_or_else(|| "unknown".to_string()), second_dep: sym.origin.unwrap_or_else(|| "unknown".to_string()), }); } + let _ = ms.type_symbols.insert(sym.clone()); } else { - if let Err(existing) = ms.value_symbols.insert(sym.clone()) { + if let Some(existing) = ms.value_symbols.get(sym.name) { return Err(CompileError::DuplicateExport { - symbol: sym.name, - first_dep: existing.origin.unwrap_or_else(|| "unknown".to_string()), + symbol: interner.resolve(sym.name).to_string(), + first_dep: existing.origin.clone().unwrap_or_else(|| "unknown".to_string()), second_dep: sym.origin.unwrap_or_else(|| "unknown".to_string()), }); } + let _ = ms.value_symbols.insert(sym.clone()); } } } @@ -231,7 +245,7 @@ pub fn compile_project( for (module_path, ast) in &parsed_files { let ms = module_symbols_map.get(module_path).unwrap(); - let mut resolver = Resolver::new(ms, &module_provider); + let mut resolver = Resolver::new(ms, &module_provider, &interner); resolver.resolve(ast)?; // Capture imported symbols @@ -240,7 +254,7 @@ pub fn compile_project( // TypeChecker also needs &mut ModuleSymbols let mut ms_mut = module_symbols_map.get_mut(module_path).unwrap(); let imported = file_imported_symbols.get(module_path).unwrap(); - let mut typechecker = TypeChecker::new(&mut ms_mut, imported, &module_provider); + let mut typechecker = TypeChecker::new(&mut ms_mut, imported, &module_provider, &interner); typechecker.check(ast)?; } @@ -255,7 +269,7 @@ pub fn compile_project( for (module_path, ast) in &parsed_files { let ms = module_symbols_map.get(module_path).unwrap(); let imported = file_imported_symbols.get(module_path).unwrap(); - let lowerer = Lowerer::new(ms, imported); + let lowerer = Lowerer::new(ms, imported, &interner); let program = lowerer.lower_file(ast, module_path)?; // Combine program into combined_program @@ -281,7 +295,7 @@ pub fn compile_project( if let Some(surface_kind) = ExportSurfaceKind::from_symbol_kind(sym.kind) { exports.insert(ExportKey { module_path: module_path.clone(), - symbol_name: sym.name.clone(), + symbol_name: interner.resolve(sym.name).to_string(), kind: surface_kind, }, ExportMetadata { func_idx: None, @@ -295,11 +309,15 @@ pub fn compile_project( if sym.visibility == Visibility::Pub { if let Some(surface_kind) = ExportSurfaceKind::from_symbol_kind(sym.kind) { // Find func_idx if it's a function or service - let func_idx = vm_module.functions.iter().position(|f| f.name == sym.name).map(|i| i as u32); + let func_idx = vm_module + .functions + .iter() + .position(|f| f.name == interner.resolve(sym.name)) + .map(|i| i as u32); exports.insert(ExportKey { module_path: module_path.clone(), - symbol_name: sym.name.clone(), + symbol_name: interner.resolve(sym.name).to_string(), kind: surface_kind, }, ExportMetadata { func_idx, @@ -312,7 +330,12 @@ pub fn compile_project( } // 6. Collect symbols - let project_symbols = crate::common::symbols::collect_symbols(&step.project_id.name, &module_symbols_map, file_manager); + let project_symbols = crate::common::symbols::collect_symbols( + &step.project_id.name, + &module_symbols_map, + file_manager, + &interner, + ); // 7. Collect imports from unresolved labels let mut imports = Vec::new(); diff --git a/crates/prometeu-compiler/src/common/symbols.rs b/crates/prometeu-compiler/src/common/symbols.rs index 1617adf0..76c1c18b 100644 --- a/crates/prometeu-compiler/src/common/symbols.rs +++ b/crates/prometeu-compiler/src/common/symbols.rs @@ -1,4 +1,5 @@ use crate::common::spans::Span; +use prometeu_analysis::NameInterner; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -60,19 +61,20 @@ pub fn collect_symbols( project_id: &str, module_symbols: &HashMap, file_manager: &crate::common::files::FileManager, + interner: &NameInterner, ) -> Vec { let mut result = Vec::new(); for (module_path, ms) in module_symbols { // Collect from type_symbols for sym in ms.type_symbols.symbols.values() { - if let Some(s) = convert_symbol(project_id, module_path, sym, file_manager) { + if let Some(s) = convert_symbol(project_id, module_path, sym, file_manager, interner) { result.push(s); } } // Collect from value_symbols for sym in ms.value_symbols.symbols.values() { - if let Some(s) = convert_symbol(project_id, module_path, sym, file_manager) { + if let Some(s) = convert_symbol(project_id, module_path, sym, file_manager, interner) { result.push(s); } } @@ -94,6 +96,7 @@ fn convert_symbol( module_path: &str, sym: &crate::frontends::pbs::symbols::Symbol, file_manager: &crate::common::files::FileManager, + interner: &NameInterner, ) -> Option { use crate::frontends::pbs::symbols::{SymbolKind, Visibility}; @@ -130,11 +133,12 @@ fn convert_symbol( }; let hash = decl_span.compute_hash(); - let id = format!("{}:{}:{}:{}:{:016x}", project_id, kind, module_path, sym.name, hash); + let name = interner.resolve(sym.name).to_string(); + let id = format!("{}:{}:{}:{}:{:016x}", project_id, kind, module_path, name, hash); Some(Symbol { id, - name: sym.name.clone(), + name, kind: kind.to_string(), exported, module_path: module_path.to_string(), diff --git a/crates/prometeu-compiler/src/frontends/pbs/ast.rs b/crates/prometeu-compiler/src/frontends/pbs/ast.rs index 98c5a2dc..14791cda 100644 --- a/crates/prometeu-compiler/src/frontends/pbs/ast.rs +++ b/crates/prometeu-compiler/src/frontends/pbs/ast.rs @@ -1,4 +1,5 @@ use crate::common::spans::Span; +use prometeu_analysis::NameId; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] @@ -56,22 +57,22 @@ pub struct ImportNode { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct ImportSpecNode { pub span: Span, - pub path: Vec, + pub path: Vec, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct ServiceDeclNode { pub span: Span, pub vis: Option, // "pub" | "mod" - pub name: String, - pub extends: Option, + pub name: NameId, + pub extends: Option, pub members: Vec, // ServiceFnSig } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct ServiceFnSigNode { pub span: Span, - pub name: String, + pub name: NameId, pub params: Vec, pub ret: Box, // TypeName or TypeApp } @@ -79,7 +80,7 @@ pub struct ServiceFnSigNode { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct ParamNode { pub span: Span, - pub name: String, + pub name: NameId, pub ty: Box, } @@ -87,7 +88,7 @@ pub struct ParamNode { pub struct FnDeclNode { pub span: Span, pub vis: Option, - pub name: String, + pub name: NameId, pub params: Vec, pub ret: Option>, pub else_fallback: Option>, // Block @@ -99,7 +100,7 @@ pub struct TypeDeclNode { pub span: Span, pub vis: Option, pub type_kind: String, // "struct" | "contract" | "error" - pub name: String, + pub name: NameId, pub is_host: bool, pub params: Vec, // fields for struct/error pub constructors: Vec, // [ ... ] @@ -112,14 +113,14 @@ pub struct ConstructorDeclNode { pub span: Span, pub params: Vec, pub initializers: Vec, - pub name: String, + pub name: NameId, pub body: Box, // BlockNode } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct ConstantDeclNode { pub span: Span, - pub name: String, + pub name: NameId, pub value: Box, } @@ -133,7 +134,7 @@ pub struct TypeBodyNode { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct TypeMemberNode { pub span: Span, - pub name: String, + pub name: NameId, pub ty: Box, } @@ -147,7 +148,7 @@ pub struct BlockNode { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct LetStmtNode { pub span: Span, - pub name: String, + pub name: NameId, pub is_mut: bool, pub ty: Option>, pub init: Box, @@ -192,7 +193,7 @@ pub struct StringLitNode { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct IdentNode { pub span: Span, - pub name: String, + pub name: NameId, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] @@ -248,13 +249,13 @@ pub struct WhenArmNode { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct TypeNameNode { pub span: Span, - pub name: String, + pub name: NameId, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct TypeAppNode { pub span: Span, - pub base: String, + pub base: NameId, pub args: Vec, } @@ -268,7 +269,7 @@ pub struct AllocNode { pub struct MutateNode { pub span: Span, pub target: Box, - pub binding: String, + pub binding: NameId, pub body: Box, // BlockNode } @@ -276,7 +277,7 @@ pub struct MutateNode { pub struct BorrowNode { pub span: Span, pub target: Box, - pub binding: String, + pub binding: NameId, pub body: Box, // BlockNode } @@ -284,7 +285,7 @@ pub struct BorrowNode { pub struct PeekNode { pub span: Span, pub target: Box, - pub binding: String, + pub binding: NameId, pub body: Box, // BlockNode } @@ -292,5 +293,5 @@ pub struct PeekNode { pub struct MemberAccessNode { pub span: Span, pub object: Box, - pub member: String, + pub member: NameId, } diff --git a/crates/prometeu-compiler/src/frontends/pbs/collector.rs b/crates/prometeu-compiler/src/frontends/pbs/collector.rs index d2b4dab9..f8b47754 100644 --- a/crates/prometeu-compiler/src/frontends/pbs/collector.rs +++ b/crates/prometeu-compiler/src/frontends/pbs/collector.rs @@ -2,16 +2,19 @@ use crate::common::diagnostics::{Diagnostic, DiagnosticBundle, DiagnosticLevel}; use crate::frontends::pbs::ast::*; use crate::frontends::pbs::symbols::*; use crate::semantics::export_surface::ExportSurfaceKind; +use prometeu_analysis::NameInterner; -pub struct SymbolCollector { +pub struct SymbolCollector<'a> { + interner: &'a NameInterner, type_symbols: SymbolTable, value_symbols: SymbolTable, diagnostics: Vec, } -impl SymbolCollector { - pub fn new() -> Self { +impl<'a> SymbolCollector<'a> { + pub fn new(interner: &'a NameInterner) -> Self { Self { + interner, type_symbols: SymbolTable::new(), value_symbols: SymbolTable::new(), diagnostics: Vec::new(), @@ -113,27 +116,31 @@ impl SymbolCollector { 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) { + 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); + if let Err(()) = self.type_symbols.insert(symbol.clone()) { + if let Some(existing) = self.type_symbols.get(symbol.name).cloned() { + 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) { + 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); + if let Err(()) = self.value_symbols.insert(symbol.clone()) { + if let Some(existing) = self.value_symbols.get(symbol.name).cloned() { + self.error_duplicate(&symbol, &existing); + } } } @@ -141,7 +148,11 @@ impl SymbolCollector { 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), + message: format!( + "Duplicate symbol '{}' already defined at {:?}", + self.interner.resolve(symbol.name), + existing.span + ), span: Some(symbol.span), }); } @@ -152,7 +163,9 @@ impl SymbolCollector { 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 + self.interner.resolve(symbol.name), + existing.namespace, + existing.span ), span: Some(symbol.span), }); diff --git a/crates/prometeu-compiler/src/frontends/pbs/lowering.rs b/crates/prometeu-compiler/src/frontends/pbs/lowering.rs index a6e1444b..0069eaf4 100644 --- a/crates/prometeu-compiler/src/frontends/pbs/lowering.rs +++ b/crates/prometeu-compiler/src/frontends/pbs/lowering.rs @@ -7,6 +7,7 @@ use crate::frontends::pbs::types::PbsType; use crate::ir_core; use crate::ir_core::ids::{FieldId, FunctionId, TypeId, ValueId}; use crate::ir_core::{Block, Function, Instr, InstrKind, Module, Param, Program, Terminator, Type}; +use prometeu_analysis::NameInterner; use std::collections::HashMap; #[derive(Clone)] @@ -18,6 +19,7 @@ struct LocalInfo { pub struct Lowerer<'a> { module_symbols: &'a ModuleSymbols, imported_symbols: &'a ModuleSymbols, + interner: &'a NameInterner, program: Program, current_function: Option, current_block: Option, @@ -38,7 +40,11 @@ pub struct Lowerer<'a> { } impl<'a> Lowerer<'a> { - pub fn new(module_symbols: &'a ModuleSymbols, imported_symbols: &'a ModuleSymbols) -> Self { + pub fn new( + module_symbols: &'a ModuleSymbols, + imported_symbols: &'a ModuleSymbols, + interner: &'a NameInterner, + ) -> Self { let mut field_offsets = HashMap::new(); field_offsets.insert(FieldId(0), 0); // V0 hardcoded field resolution foundation @@ -51,6 +57,7 @@ impl<'a> Lowerer<'a> { Self { module_symbols, imported_symbols, + interner, program: Program { const_pool: ir_core::ConstPool::new(), modules: Vec::new(), @@ -91,12 +98,14 @@ impl<'a> Lowerer<'a> { if let Node::FnDecl(n) = decl { let id = FunctionId(self.next_func_id); self.next_func_id += 1; - self.function_ids.insert(n.name.clone(), id); + self.function_ids + .insert(self.interner.resolve(n.name).to_string(), id); } if let Node::TypeDecl(n) = decl { let id = TypeId(self.next_type_id); self.next_type_id += 1; - self.type_ids.insert(n.name.clone(), id); + self.type_ids + .insert(self.interner.resolve(n.name).to_string(), id); } } @@ -105,7 +114,7 @@ impl<'a> Lowerer<'a> { for decl in &file.decls { if let Node::TypeDecl(n) = decl { if n.type_kind == "struct" { - struct_nodes.insert(n.name.clone(), n); + struct_nodes.insert(self.interner.resolve(n.name).to_string(), n); } } } @@ -143,11 +152,12 @@ impl<'a> Lowerer<'a> { for decl in &file.decls { if let Node::TypeDecl(n) = decl { + let type_name = self.interner.resolve(n.name).to_string(); let mut constants = HashMap::new(); for c in &n.constants { - constants.insert(c.name.clone(), *c.value.clone()); + constants.insert(self.interner.resolve(c.name).to_string(), *c.value.clone()); } - self.type_constants.insert(n.name.clone(), constants); + self.type_constants.insert(type_name.clone(), constants); let mut ctors = HashMap::new(); @@ -166,20 +176,20 @@ impl<'a> Lowerer<'a> { span: n.span, params, initializers, - name: n.name.clone(), + name: n.name, body: Box::new(Node::Block(BlockNode { span: n.span, stmts: Vec::new(), tail: None, })), }; - ctors.insert(n.name.clone(), default_ctor); + ctors.insert(type_name.clone(), default_ctor); } for ctor in &n.constructors { - ctors.insert(ctor.name.clone(), ctor.clone()); + ctors.insert(self.interner.resolve(ctor.name).to_string(), ctor.clone()); } - self.struct_constructors.insert(n.name.clone(), ctors); + self.struct_constructors.insert(type_name, ctors); } } @@ -205,7 +215,8 @@ impl<'a> Lowerer<'a> { } fn lower_function(&mut self, n: &FnDeclNode) -> Result { - let func_id = *self.function_ids.get(&n.name).unwrap(); + let func_name = self.interner.resolve(n.name).to_string(); + let func_id = *self.function_ids.get(&func_name).unwrap(); self.next_block_id = 0; self.local_vars = vec![HashMap::new()]; self.max_slots_used = 0; @@ -217,10 +228,16 @@ impl<'a> Lowerer<'a> { let ty = self.lower_type_node(¶m.ty); let slots = self.get_type_slots(&ty); params.push(Param { - name: param.name.clone(), + name: self.interner.resolve(param.name).to_string(), ty: ty.clone(), }); - self.local_vars[0].insert(param.name.clone(), LocalInfo { slot: param_slots, ty: ty.clone() }); + self.local_vars[0].insert( + self.interner.resolve(param.name).to_string(), + LocalInfo { + slot: param_slots, + ty: ty.clone(), + }, + ); for i in 0..slots { local_types.insert(param_slots + i, ty.clone()); } @@ -237,7 +254,7 @@ impl<'a> Lowerer<'a> { let func = Function { id: func_id, - name: n.name.clone(), + name: func_name, params, return_type: ret_ty, blocks: Vec::new(), @@ -325,11 +342,12 @@ impl<'a> Lowerer<'a> { fn get_type_id_and_slots(&mut self, node: &Node) -> Result<(TypeId, u32), ()> { match node { Node::TypeName(n) => { - let slots = self.struct_slots.get(&n.name).cloned().unwrap_or(1); - let id = self.get_or_create_type_id(&n.name); + let name = self.interner.resolve(n.name); + let slots = self.struct_slots.get(name).cloned().unwrap_or(1); + let id = self.get_or_create_type_id(name); Ok((id, slots)) } - Node::TypeApp(ta) if ta.base == "array" => { + Node::TypeApp(ta) if self.interner.resolve(ta.base) == "array" => { let size = if ta.args.len() > 1 { if let Node::IntLit(il) = &ta.args[1] { il.value as u32 @@ -376,7 +394,7 @@ impl<'a> Lowerer<'a> { // 4. Bind view to local self.local_vars.push(HashMap::new()); - let view_slot = self.add_local_to_scope(n.binding.to_string(), Type::Int); + let view_slot = self.add_local_to_scope(self.interner.resolve(n.binding).to_string(), Type::Int); self.emit(InstrKind::SetLocal(view_slot)); // 5. Body @@ -403,7 +421,7 @@ impl<'a> Lowerer<'a> { // 4. Bind view to local self.local_vars.push(HashMap::new()); - let view_slot = self.add_local_to_scope(n.binding.to_string(), Type::Int); + let view_slot = self.add_local_to_scope(self.interner.resolve(n.binding).to_string(), Type::Int); self.emit(InstrKind::SetLocal(view_slot)); // 5. Body @@ -430,7 +448,7 @@ impl<'a> Lowerer<'a> { // 4. Bind view to local self.local_vars.push(HashMap::new()); - let view_slot = self.add_local_to_scope(n.binding.to_string(), Type::Int); + let view_slot = self.add_local_to_scope(self.interner.resolve(n.binding).to_string(), Type::Int); self.emit(InstrKind::SetLocal(view_slot)); // 5. Body @@ -465,7 +483,9 @@ impl<'a> Lowerer<'a> { if let Node::Call(call) = &*n.init { if let Node::MemberAccess(ma) = &*call.callee { if let Node::Ident(obj) = &*ma.object { - match (obj.name.as_str(), ma.member.as_str()) { + let obj_name = self.interner.resolve(obj.name); + let member_name = self.interner.resolve(ma.member); + match (obj_name, member_name) { ("Input", "pad") => Type::Struct("Pad".to_string()), ("Input", "touch") => Type::Struct("Touch".to_string()), _ => Type::Int, @@ -476,7 +496,7 @@ impl<'a> Lowerer<'a> { }; let slots = self.get_type_slots(&ty); - let slot = self.add_local_to_scope(n.name.clone(), ty); + let slot = self.add_local_to_scope(self.interner.resolve(n.name).to_string(), ty); for i in (0..slots).rev() { self.emit(InstrKind::SetLocal(slot + i)); @@ -493,7 +513,8 @@ impl<'a> Lowerer<'a> { } fn lower_ident(&mut self, n: &IdentNode) -> Result<(), ()> { - if let Some(info) = self.find_local(&n.name) { + let name_str = self.interner.resolve(n.name); + if let Some(info) = self.find_local(name_str) { let slots = self.get_type_slots(&info.ty); for i in 0..slots { self.emit(InstrKind::GetLocal(info.slot + i)); @@ -501,7 +522,7 @@ impl<'a> Lowerer<'a> { Ok(()) } else { // Check for special identifiers - match n.name.as_str() { + match name_str { "true" => { let id = self.program.const_pool.add_int(1); self.emit(InstrKind::PushConst(id)); @@ -522,12 +543,20 @@ impl<'a> Lowerer<'a> { } // Check if it's a function (for first-class functions if supported) - if let Some(_id) = self.function_ids.get(&n.name) { + if let Some(_id) = self.function_ids.get(name_str) { // Push function reference? Not in v0. - self.error("E_LOWER_UNSUPPORTED", format!("First-class function reference '{}' not supported", n.name), n.span); + self.error( + "E_LOWER_UNSUPPORTED", + format!("First-class function reference '{}' not supported", name_str), + n.span, + ); Err(()) } else { - self.error("E_RESOLVE_UNDEFINED", format!("Undefined identifier '{}'", n.name), n.span); + self.error( + "E_RESOLVE_UNDEFINED", + format!("Undefined identifier '{}'", name_str), + n.span, + ); Err(()) } } @@ -535,17 +564,19 @@ impl<'a> Lowerer<'a> { fn lower_member_access(&mut self, n: &MemberAccessNode) -> Result<(), ()> { if let Node::Ident(id) = &*n.object { - if let Some(constants) = self.type_constants.get(&id.name).cloned() { - if let Some(const_val) = constants.get(&n.member) { - let old_ctx = self.current_type_context.replace(id.name.clone()); + let type_name = self.interner.resolve(id.name); + let member_name = self.interner.resolve(n.member); + if let Some(constants) = self.type_constants.get(type_name).cloned() { + if let Some(const_val) = constants.get(member_name) { + let old_ctx = self.current_type_context.replace(type_name.to_string()); let res = self.lower_node(const_val); self.current_type_context = old_ctx; return res; } } - if id.name == "Color" { - let val = match n.member.as_str() { + if type_name == "Color" { + let val = match member_name { "BLACK" => 0x0000, "WHITE" => 0xFFFF, "RED" => 0xF800, @@ -578,18 +609,21 @@ impl<'a> Lowerer<'a> { fn resolve_member_access(&self, n: &MemberAccessNode) -> Option<(u32, Type)> { match &*n.object { Node::Ident(id) => { - let info = self.find_local(&id.name)?; + let name_str = self.interner.resolve(id.name); + let member_str = self.interner.resolve(n.member); + let info = self.find_local(name_str)?; if let Type::Struct(sname) = &info.ty { - let offset = self.get_field_offset(sname, &n.member); - let ty = self.get_field_type(sname, &n.member); + let offset = self.get_field_offset(sname, member_str); + let ty = self.get_field_type(sname, member_str); Some((info.slot + offset, ty)) } else { None } } Node::MemberAccess(inner) => { + let member_str = self.interner.resolve(n.member); let (base_slot, ty) = self.resolve_member_access(inner)?; if let Type::Struct(sname) = &ty { - let offset = self.get_field_offset(sname, &n.member); - let final_ty = self.get_field_type(sname, &n.member); + let offset = self.get_field_offset(sname, member_str); + let final_ty = self.get_field_type(sname, member_str); Some((base_slot + offset, final_ty)) } else { None } } @@ -649,9 +683,10 @@ impl<'a> Lowerer<'a> { fn lower_call(&mut self, n: &CallNode) -> Result<(), ()> { match &*n.callee { Node::Ident(id_node) => { + let callee_name = self.interner.resolve(id_node.name).to_string(); // 1. Check for constructor call: TypeName(...) - let ctor = self.struct_constructors.get(&id_node.name) - .and_then(|ctors| ctors.get(&id_node.name)) + let ctor = self.struct_constructors.get(&callee_name) + .and_then(|ctors| ctors.get(&callee_name)) .cloned(); if let Some(ctor) = ctor { @@ -660,7 +695,7 @@ impl<'a> Lowerer<'a> { if let Some(ctx) = &self.current_type_context { let ctor = self.struct_constructors.get(ctx) - .and_then(|ctors| ctors.get(&id_node.name)) + .and_then(|ctors| ctors.get(&callee_name)) .cloned(); if let Some(ctor) = ctor { @@ -671,10 +706,10 @@ impl<'a> Lowerer<'a> { for arg in &n.args { self.lower_node(arg)?; } - if let Some(func_id) = self.function_ids.get(&id_node.name) { + if let Some(func_id) = self.function_ids.get(&callee_name) { self.emit(InstrKind::Call(*func_id, n.args.len() as u32)); Ok(()) - } else if let Some(sym) = self.imported_symbols.value_symbols.get(&id_node.name) { + } else if let Some(sym) = self.imported_symbols.value_symbols.get(id_node.name) { if let Some(origin) = &sym.origin { if origin.starts_with('@') { // Format: @dep_alias:module_path @@ -682,32 +717,52 @@ impl<'a> Lowerer<'a> { if parts.len() == 2 { let dep_alias = parts[0].to_string(); let module_path = parts[1].to_string(); - self.emit(InstrKind::ImportCall(dep_alias, module_path, sym.name.clone(), n.args.len() as u32)); + self.emit(InstrKind::ImportCall( + dep_alias, + module_path, + self.interner.resolve(sym.name).to_string(), + n.args.len() as u32, + )); return Ok(()); } } } - self.error("E_LOWER_UNSUPPORTED", format!("Calling symbol '{}' with origin {:?} is not supported yet in v0", id_node.name, sym.origin), id_node.span); + self.error( + "E_LOWER_UNSUPPORTED", + format!( + "Calling symbol '{}' with origin {:?} is not supported yet in v0", + callee_name, + sym.origin + ), + id_node.span, + ); Err(()) } else { // Check for special built-in functions - match id_node.name.as_str() { + match callee_name.as_str() { "some" | "ok" | "err" => { return Ok(()); } _ => {} } - self.error("E_RESOLVE_UNDEFINED", format!("Undefined function '{}'", id_node.name), id_node.span); + self.error( + "E_RESOLVE_UNDEFINED", + format!("Undefined function '{}'", callee_name), + id_node.span, + ); Err(()) } } Node::MemberAccess(ma) => { // Check if it's a constructor alias: TypeName.Alias(...) let ctor = if let Node::Ident(obj_id) = &*ma.object { - self.struct_constructors.get(&obj_id.name) - .and_then(|ctors| ctors.get(&ma.member)) + let obj_name = self.interner.resolve(obj_id.name); + let member_name = self.interner.resolve(ma.member); + self.struct_constructors + .get(obj_name) + .and_then(|ctors| ctors.get(member_name)) .cloned() } else { None @@ -718,9 +773,11 @@ impl<'a> Lowerer<'a> { } // Check for Pad.any() - if ma.member == "any" { + let member_name = self.interner.resolve(ma.member); + if member_name == "any" { if let Node::Ident(obj_id) = &*ma.object { - if let Some(info) = self.find_local(&obj_id.name) { + let obj_name = self.interner.resolve(obj_id.name); + if let Some(info) = self.find_local(obj_name) { if let Type::Struct(sname) = &info.ty { if sname == "Pad" { self.lower_pad_any(info.slot); @@ -733,19 +790,20 @@ impl<'a> Lowerer<'a> { // Host contract static calls: Contract.method(...) if let Node::Ident(obj_id) = &*ma.object { - let is_local = self.find_local(&obj_id.name).is_some(); + let obj_name = self.interner.resolve(obj_id.name); + let is_local = self.find_local(obj_name).is_some(); if !is_local { // Check type symbol (current or imported) for a host contract - let sym_opt = self.module_symbols.type_symbols.get(&obj_id.name) - .or_else(|| self.imported_symbols.type_symbols.get(&obj_id.name)); + let sym_opt = self.module_symbols.type_symbols.get(obj_id.name) + .or_else(|| self.imported_symbols.type_symbols.get(obj_id.name)); if let Some(sym) = sym_opt { if sym.kind == SymbolKind::Contract && sym.is_host { // Lower arguments first to avoid borrowing conflicts for arg in &n.args { self.lower_node(arg)?; } - if let Some(method) = self.contract_registry.get_method(&obj_id.name, &ma.member) { + if let Some(method) = self.contract_registry.get_method(obj_name, member_name) { let id = method.id; let return_slots = if matches!(method.return_type, PbsType::Void) { 0 } else { 1 }; self.emit(InstrKind::HostCall(id, return_slots)); @@ -757,15 +815,15 @@ impl<'a> Lowerer<'a> { } // Check for .raw() - if ma.member == "raw" { + if member_name == "raw" { self.lower_node(&ma.object)?; return Ok(()); } // Check for Color.rgb - if ma.member == "rgb" { + if member_name == "rgb" { if let Node::Ident(obj_id) = &*ma.object { - if obj_id.name == "Color" { + if self.interner.resolve(obj_id.name) == "Color" { if n.args.len() == 3 { // Try to get literal values for r, g, b let mut literals = Vec::new(); @@ -798,20 +856,25 @@ impl<'a> Lowerer<'a> { } if let Node::Ident(obj_id) = &*ma.object { - let is_host_contract = self.module_symbols.type_symbols.get(&obj_id.name) + let obj_name = self.interner.resolve(obj_id.name); + let is_host_contract = self.module_symbols.type_symbols.get(obj_id.name) .map(|sym| sym.kind == SymbolKind::Contract && sym.is_host) .unwrap_or(false); - let is_shadowed = self.find_local(&obj_id.name).is_some(); + let is_shadowed = self.find_local(obj_name).is_some(); if is_host_contract && !is_shadowed { - if let Some(method) = self.contract_registry.get_method(&obj_id.name, &ma.member) { + if let Some(method) = self.contract_registry.get_method(obj_name, member_name) { let ir_ty = self.convert_pbs_type(&method.return_type); let return_slots = self.get_type_slots(&ir_ty); self.emit(InstrKind::HostCall(method.id, return_slots)); return Ok(()); } else { - self.error("E_RESOLVE_UNDEFINED", format!("Undefined contract member '{}.{}'", obj_id.name, ma.member), ma.span); + self.error( + "E_RESOLVE_UNDEFINED", + format!("Undefined contract member '{}.{}'", obj_name, member_name), + ma.span, + ); return Err(()); } } @@ -834,7 +897,7 @@ impl<'a> Lowerer<'a> { let mut param_map = HashMap::new(); for (i, param) in ctor.params.iter().enumerate() { if i < args.len() { - param_map.insert(param.name.clone(), args[i].clone()); + param_map.insert(self.interner.resolve(param.name).to_string(), args[i].clone()); } } @@ -848,7 +911,7 @@ impl<'a> Lowerer<'a> { fn substitute_node(&self, node: &Node, param_map: &HashMap) -> Node { match node { Node::Ident(id) => { - if let Some(arg) = param_map.get(&id.name) { + if let Some(arg) = param_map.get(self.interner.resolve(id.name)) { arg.clone() } else { node.clone() @@ -961,17 +1024,18 @@ impl<'a> Lowerer<'a> { fn lower_type_node(&mut self, node: &Node) -> Type { match node { - Node::TypeName(n) => match n.name.as_str() { + Node::TypeName(n) => match self.interner.resolve(n.name) { "int" => Type::Int, "bounded" => Type::Bounded, "float" => Type::Float, "bool" => Type::Bool, "string" => Type::String, "void" => Type::Void, - _ => Type::Struct(n.name.clone()), + _ => Type::Struct(self.interner.resolve(n.name).to_string()), }, Node::TypeApp(ta) => { - if ta.base == "array" { + let base_name = self.interner.resolve(ta.base); + if base_name == "array" { let elem_ty = self.lower_type_node(&ta.args[0]); let size = if ta.args.len() > 1 { if let Node::IntLit(il) = &ta.args[1] { @@ -983,15 +1047,15 @@ impl<'a> Lowerer<'a> { 0 }; Type::Array(Box::new(elem_ty), size) - } else if ta.base == "optional" { + } else if base_name == "optional" { Type::Optional(Box::new(self.lower_type_node(&ta.args[0]))) - } else if ta.base == "result" { + } else if base_name == "result" { Type::Result( Box::new(self.lower_type_node(&ta.args[0])), Box::new(self.lower_type_node(&ta.args[1])) ) } else { - Type::Struct(format!("{}<{}>", ta.base, ta.args.len())) + Type::Struct(format!("{}<{}>", base_name, ta.args.len())) } } _ => Type::Void, @@ -1126,6 +1190,7 @@ mod tests { use crate::frontends::pbs::parser::Parser; use crate::frontends::pbs::symbols::ModuleSymbols; use crate::ir_core; + use prometeu_analysis::NameInterner; #[test] fn test_basic_lowering() { @@ -1137,14 +1202,16 @@ mod tests { let x = add(10, 20); } "; - let mut parser = Parser::new(code, 0); + let mut interner = NameInterner::new(); + let mut parser = Parser::new(code, 0, &mut interner); let ast = parser.parse_file().expect("Failed to parse"); - let mut collector = SymbolCollector::new(); + let mut collector = SymbolCollector::new(&interner); let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols"); let module_symbols = ModuleSymbols { type_symbols, value_symbols }; - let imported = ModuleSymbols::new(); let lowerer = Lowerer::new(&module_symbols, &imported); + let imported = ModuleSymbols::new(); + let lowerer = Lowerer::new(&module_symbols, &imported, &interner); let program = lowerer.lower_file(&ast, "test").expect("Lowering failed"); // Verify program structure @@ -1174,14 +1241,16 @@ mod tests { } } "; - let mut parser = Parser::new(code, 0); + let mut interner = NameInterner::new(); + let mut parser = Parser::new(code, 0, &mut interner); let ast = parser.parse_file().expect("Failed to parse"); - let mut collector = SymbolCollector::new(); + let mut collector = SymbolCollector::new(&interner); let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols"); let module_symbols = ModuleSymbols { type_symbols, value_symbols }; - let imported = ModuleSymbols::new(); let lowerer = Lowerer::new(&module_symbols, &imported); + let imported = ModuleSymbols::new(); + let lowerer = Lowerer::new(&module_symbols, &imported, &interner); let program = lowerer.lower_file(&ast, "test").expect("Lowering failed"); let max_func = &program.modules[0].functions[0]; @@ -1199,14 +1268,16 @@ mod tests { } } "; - let mut parser = Parser::new(code, 0); + let mut interner = NameInterner::new(); + let mut parser = Parser::new(code, 0, &mut interner); let ast = parser.parse_file().expect("Failed to parse"); - let mut collector = SymbolCollector::new(); + let mut collector = SymbolCollector::new(&interner); let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols"); let module_symbols = ModuleSymbols { type_symbols, value_symbols }; - let imported = ModuleSymbols::new(); let lowerer = Lowerer::new(&module_symbols, &imported); + let imported = ModuleSymbols::new(); + let lowerer = Lowerer::new(&module_symbols, &imported, &interner); let program = lowerer.lower_file(&ast, "test").expect("Lowering failed"); let func = &program.modules[0].functions[0]; @@ -1227,14 +1298,16 @@ mod tests { } } "; - let mut parser = Parser::new(code, 0); + let mut interner = NameInterner::new(); + let mut parser = Parser::new(code, 0, &mut interner); let ast = parser.parse_file().expect("Failed to parse"); - let mut collector = SymbolCollector::new(); + let mut collector = SymbolCollector::new(&interner); let (type_symbols, value_symbols) = collector.collect(&ast).unwrap(); let module_symbols = ModuleSymbols { type_symbols, value_symbols }; - let imported = ModuleSymbols::new(); let lowerer = Lowerer::new(&module_symbols, &imported); + let imported = ModuleSymbols::new(); + let lowerer = Lowerer::new(&module_symbols, &imported, &interner); let program = lowerer.lower_file(&ast, "test").expect("Lowering failed"); let json = serde_json::to_string_pretty(&program).unwrap(); @@ -1268,14 +1341,16 @@ mod tests { } } "; - let mut parser = Parser::new(code, 0); + let mut interner = NameInterner::new(); + let mut parser = Parser::new(code, 0, &mut interner); let ast = parser.parse_file().expect("Failed to parse"); - let mut collector = SymbolCollector::new(); + let mut collector = SymbolCollector::new(&interner); let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols"); let module_symbols = ModuleSymbols { type_symbols, value_symbols }; - let imported = ModuleSymbols::new(); let lowerer = Lowerer::new(&module_symbols, &imported); + let imported = ModuleSymbols::new(); + let lowerer = Lowerer::new(&module_symbols, &imported, &interner); let program = lowerer.lower_file(&ast, "test").expect("Lowering failed"); let func = &program.modules[0].functions[0]; @@ -1302,14 +1377,16 @@ mod tests { Log.write(2, \"Hello\"); } "; - let mut parser = Parser::new(code, 0); + let mut interner = NameInterner::new(); + let mut parser = Parser::new(code, 0, &mut interner); let ast = parser.parse_file().expect("Failed to parse"); - let mut collector = SymbolCollector::new(); + let mut collector = SymbolCollector::new(&interner); let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols"); let module_symbols = ModuleSymbols { type_symbols, value_symbols }; - let imported = ModuleSymbols::new(); let lowerer = Lowerer::new(&module_symbols, &imported); + let imported = ModuleSymbols::new(); + let lowerer = Lowerer::new(&module_symbols, &imported, &interner); let program = lowerer.lower_file(&ast, "test").expect("Lowering failed"); let func = &program.modules[0].functions[0]; @@ -1329,14 +1406,16 @@ mod tests { Gfx.clear(0); } "; - let mut parser = Parser::new(code, 0); + let mut interner = NameInterner::new(); + let mut parser = Parser::new(code, 0, &mut interner); let ast = parser.parse_file().expect("Failed to parse"); - let mut collector = SymbolCollector::new(); + let mut collector = SymbolCollector::new(&interner); let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols"); let module_symbols = ModuleSymbols { type_symbols, value_symbols }; - let imported = ModuleSymbols::new(); let lowerer = Lowerer::new(&module_symbols, &imported); + let imported = ModuleSymbols::new(); + let lowerer = Lowerer::new(&module_symbols, &imported, &interner); let result = lowerer.lower_file(&ast, "test"); assert!(result.is_err()); @@ -1353,14 +1432,16 @@ mod tests { Gfx.clear(0); } "; - let mut parser = Parser::new(code, 0); + let mut interner = NameInterner::new(); + let mut parser = Parser::new(code, 0, &mut interner); let ast = parser.parse_file().expect("Failed to parse"); - let mut collector = SymbolCollector::new(); + let mut collector = SymbolCollector::new(&interner); let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols"); let module_symbols = ModuleSymbols { type_symbols, value_symbols }; - let imported = ModuleSymbols::new(); let lowerer = Lowerer::new(&module_symbols, &imported); + let imported = ModuleSymbols::new(); + let lowerer = Lowerer::new(&module_symbols, &imported, &interner); let result = lowerer.lower_file(&ast, "test"); assert!(result.is_err()); @@ -1376,14 +1457,16 @@ mod tests { Gfx.invalidMethod(0); } "; - let mut parser = Parser::new(code, 0); + let mut interner = NameInterner::new(); + let mut parser = Parser::new(code, 0, &mut interner); let ast = parser.parse_file().expect("Failed to parse"); - let mut collector = SymbolCollector::new(); + let mut collector = SymbolCollector::new(&interner); let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols"); let module_symbols = ModuleSymbols { type_symbols, value_symbols }; - let imported = ModuleSymbols::new(); let lowerer = Lowerer::new(&module_symbols, &imported); + let imported = ModuleSymbols::new(); + let lowerer = Lowerer::new(&module_symbols, &imported, &interner); let result = lowerer.lower_file(&ast, "test"); assert!(result.is_err()); @@ -1399,14 +1482,16 @@ mod tests { let v = alloc Vec3; } "; - let mut parser = Parser::new(code, 0); + let mut interner = NameInterner::new(); + let mut parser = Parser::new(code, 0, &mut interner); let ast = parser.parse_file().expect("Failed to parse"); - let mut collector = SymbolCollector::new(); + let mut collector = SymbolCollector::new(&interner); let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols"); let module_symbols = ModuleSymbols { type_symbols, value_symbols }; - let imported = ModuleSymbols::new(); let lowerer = Lowerer::new(&module_symbols, &imported); + let imported = ModuleSymbols::new(); + let lowerer = Lowerer::new(&module_symbols, &imported, &interner); let program = lowerer.lower_file(&ast, "test").expect("Lowering failed"); let func = &program.modules[0].functions[0]; @@ -1431,14 +1516,16 @@ mod tests { let a = alloc array[10b]; } "; - let mut parser = Parser::new(code, 0); + let mut interner = NameInterner::new(); + let mut parser = Parser::new(code, 0, &mut interner); let ast = parser.parse_file().expect("Failed to parse"); - let mut collector = SymbolCollector::new(); + let mut collector = SymbolCollector::new(&interner); let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols"); let module_symbols = ModuleSymbols { type_symbols, value_symbols }; - let imported = ModuleSymbols::new(); let lowerer = Lowerer::new(&module_symbols, &imported); + let imported = ModuleSymbols::new(); + let lowerer = Lowerer::new(&module_symbols, &imported, &interner); let program = lowerer.lower_file(&ast, "test").expect("Lowering failed"); let func = &program.modules[0].functions[0]; @@ -1463,14 +1550,16 @@ mod tests { let x = alloc int; } "; - let mut parser = Parser::new(code, 0); + let mut interner = NameInterner::new(); + let mut parser = Parser::new(code, 0, &mut interner); let ast = parser.parse_file().expect("Failed to parse"); - let mut collector = SymbolCollector::new(); + let mut collector = SymbolCollector::new(&interner); let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols"); let module_symbols = ModuleSymbols { type_symbols, value_symbols }; - let imported = ModuleSymbols::new(); let lowerer = Lowerer::new(&module_symbols, &imported); + let imported = ModuleSymbols::new(); + let lowerer = Lowerer::new(&module_symbols, &imported, &interner); let program = lowerer.lower_file(&ast, "test").expect("Lowering failed"); let func = &program.modules[0].functions[0]; @@ -1495,14 +1584,16 @@ mod tests { missing_func(); } "; - let mut parser = Parser::new(code, 0); + let mut interner = NameInterner::new(); + let mut parser = Parser::new(code, 0, &mut interner); let ast = parser.parse_file().expect("Failed to parse"); - let mut collector = SymbolCollector::new(); + let mut collector = SymbolCollector::new(&interner); let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols"); let module_symbols = ModuleSymbols { type_symbols, value_symbols }; - let imported = ModuleSymbols::new(); let lowerer = Lowerer::new(&module_symbols, &imported); + let imported = ModuleSymbols::new(); + let lowerer = Lowerer::new(&module_symbols, &imported, &interner); let result = lowerer.lower_file(&ast, "test"); assert!(result.is_err()); @@ -1518,14 +1609,16 @@ mod tests { let x = undefined_var; } "; - let mut parser = Parser::new(code, 0); + let mut interner = NameInterner::new(); + let mut parser = Parser::new(code, 0, &mut interner); let ast = parser.parse_file().expect("Failed to parse"); - let mut collector = SymbolCollector::new(); + let mut collector = SymbolCollector::new(&interner); let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols"); let module_symbols = ModuleSymbols { type_symbols, value_symbols }; - let imported = ModuleSymbols::new(); let lowerer = Lowerer::new(&module_symbols, &imported); + let imported = ModuleSymbols::new(); + let lowerer = Lowerer::new(&module_symbols, &imported, &interner); let result = lowerer.lower_file(&ast, "test"); assert!(result.is_err()); diff --git a/crates/prometeu-compiler/src/frontends/pbs/mod.rs b/crates/prometeu-compiler/src/frontends/pbs/mod.rs index 5c846e90..acbe5b45 100644 --- a/crates/prometeu-compiler/src/frontends/pbs/mod.rs +++ b/crates/prometeu-compiler/src/frontends/pbs/mod.rs @@ -23,6 +23,7 @@ use crate::common::files::FileManager; use crate::frontends::Frontend; use crate::ir_vm; use crate::lowering::core_to_vm; +use prometeu_analysis::NameInterner; use std::path::Path; pub struct PbsFrontend; @@ -42,10 +43,11 @@ impl Frontend for PbsFrontend { })?; let file_id = file_manager.add(entry.to_path_buf(), source.clone()); - let mut parser = parser::Parser::new(&source, file_id); + let mut interner = NameInterner::new(); + let mut parser = parser::Parser::new(&source, file_id, &mut interner); let ast = parser.parse_file()?; - let mut collector = SymbolCollector::new(); + let mut collector = SymbolCollector::new(&interner); let (type_symbols, value_symbols) = collector.collect(&ast)?; let mut module_symbols = ModuleSymbols { type_symbols, value_symbols }; @@ -54,15 +56,15 @@ impl Frontend for PbsFrontend { fn get_module_symbols(&self, _path: &str) -> Option<&ModuleSymbols> { None } } - let mut resolver = Resolver::new(&module_symbols, &EmptyProvider); + let mut resolver = Resolver::new(&module_symbols, &EmptyProvider, &interner); resolver.resolve(&ast)?; let imported_symbols = resolver.imported_symbols; - let mut typechecker = TypeChecker::new(&mut module_symbols, &imported_symbols, &EmptyProvider); + let mut typechecker = TypeChecker::new(&mut module_symbols, &imported_symbols, &EmptyProvider, &interner); typechecker.check(&ast)?; // Lower to Core IR - let lowerer = Lowerer::new(&module_symbols, &imported_symbols); + let lowerer = Lowerer::new(&module_symbols, &imported_symbols, &interner); let module_name = entry.file_stem().unwrap().to_string_lossy(); let core_program = lowerer.lower_file(&ast, &module_name)?; diff --git a/crates/prometeu-compiler/src/frontends/pbs/parser.rs b/crates/prometeu-compiler/src/frontends/pbs/parser.rs index 9fb354ec..962bdae6 100644 --- a/crates/prometeu-compiler/src/frontends/pbs/parser.rs +++ b/crates/prometeu-compiler/src/frontends/pbs/parser.rs @@ -3,16 +3,29 @@ use crate::common::spans::Span; use crate::frontends::pbs::ast::*; use crate::frontends::pbs::lexer::Lexer; use crate::frontends::pbs::token::{Token, TokenKind}; +use prometeu_analysis::{NameId, NameInterner}; -pub struct Parser { +pub struct Parser<'a> { tokens: Vec, pos: usize, file_id: usize, errors: Vec, + interner: &'a mut NameInterner, + builtin_none: NameId, + builtin_some: NameId, + builtin_ok: NameId, + builtin_err: NameId, + builtin_true: NameId, + builtin_false: NameId, + builtin_void: NameId, + builtin_array: NameId, + builtin_optional: NameId, + builtin_result: NameId, + builtin_bounded: NameId, } -impl Parser { - pub fn new(source: &str, file_id: usize) -> Self { +impl<'a> Parser<'a> { + pub fn new(source: &str, file_id: usize, interner: &'a mut NameInterner) -> Self { let mut lexer = Lexer::new(source, file_id); let mut tokens = Vec::new(); loop { @@ -24,11 +37,35 @@ impl Parser { } } + let builtin_none = interner.intern("none"); + let builtin_some = interner.intern("some"); + let builtin_ok = interner.intern("ok"); + let builtin_err = interner.intern("err"); + let builtin_true = interner.intern("true"); + let builtin_false = interner.intern("false"); + let builtin_void = interner.intern("void"); + let builtin_array = interner.intern("array"); + let builtin_optional = interner.intern("optional"); + let builtin_result = interner.intern("result"); + let builtin_bounded = interner.intern("bounded"); + Self { tokens, pos: 0, file_id, errors: Vec::new(), + interner, + builtin_none, + builtin_some, + builtin_ok, + builtin_err, + builtin_true, + builtin_false, + builtin_void, + builtin_array, + builtin_optional, + builtin_result, + builtin_bounded, } } @@ -112,12 +149,12 @@ impl Parser { if self.peek().kind == TokenKind::OpenBrace { self.advance(); // { loop { - if let TokenKind::Identifier(ref name) = self.peek().kind { - path.push(name.clone()); - self.advance(); - } else { - return Err(self.error("Expected identifier in import spec")); - } + let name = match self.peek().kind.clone() { + TokenKind::Identifier(name) => name, + _ => return Err(self.error("Expected identifier in import spec")), + }; + self.advance(); + path.push(self.interner.intern(&name)); if self.peek().kind == TokenKind::Comma { self.advance(); @@ -130,12 +167,12 @@ impl Parser { self.consume(TokenKind::CloseBrace)?; } else { loop { - if let TokenKind::Identifier(ref name) = self.peek().kind { - path.push(name.clone()); - self.advance(); - } else { - return Err(self.error("Expected identifier in import spec")); - } + let name = match self.peek().kind.clone() { + TokenKind::Identifier(name) => name, + _ => return Err(self.error("Expected identifier in import spec")), + }; + self.advance(); + path.push(self.interner.intern(&name)); if self.peek().kind == TokenKind::Dot { self.advance(); @@ -227,7 +264,7 @@ impl Parser { } else { Box::new(Node::TypeName(TypeNameNode { span: Span::new(self.file_id, 0, 0), // Placeholder for void - name: "void".to_string(), + name: self.builtin_void, })) }; @@ -413,35 +450,35 @@ impl Parser { let name = match id_tok.kind { TokenKind::Identifier(ref s) => { self.advance(); - s.clone() + self.interner.intern(s) } TokenKind::Optional => { self.advance(); - "optional".to_string() + self.builtin_optional } TokenKind::Result => { self.advance(); - "result".to_string() + self.builtin_result } TokenKind::Bounded => { self.advance(); - "bounded".to_string() + self.builtin_bounded } TokenKind::None => { self.advance(); - "none".to_string() + self.builtin_none } TokenKind::Some => { self.advance(); - "some".to_string() + self.builtin_some } TokenKind::Ok => { self.advance(); - "ok".to_string() + self.builtin_ok } TokenKind::Err => { self.advance(); - "err".to_string() + self.builtin_err } _ => return Err(self.error_with_code("Expected type name", Some("E_PARSE_EXPECTED_TOKEN"))), }; @@ -491,7 +528,7 @@ impl Parser { // If base was "array", it already has T in args. // We can just add N to args. match &mut node { - Node::TypeApp(ta) if ta.base == "array" => { + Node::TypeApp(ta) if ta.base == self.builtin_array => { ta.args.push(Node::IntLit(IntLitNode { span: size_tok.span, value: size as i64 })); ta.span = span; } @@ -499,7 +536,7 @@ impl Parser { // Fallback for T[N] if we want to support it, but spec says array[N] node = Node::TypeApp(TypeAppNode { span, - base: "array".to_string(), + base: self.builtin_array, args: vec![node, Node::IntLit(IntLitNode { span: size_tok.span, value: size as i64 })], }); } @@ -673,6 +710,7 @@ impl Parser { } TokenKind::Identifier(name) => { self.advance(); + let name = self.interner.intern(&name); let mut node = Node::Ident(IdentNode { span: tok.span, name }); loop { if self.peek().kind == TokenKind::OpenParen { @@ -689,12 +727,12 @@ impl Parser { } TokenKind::None | TokenKind::Some | TokenKind::Ok | TokenKind::Err => { let name = match tok.kind { - TokenKind::None => "none", - TokenKind::Some => "some", - TokenKind::Ok => "ok", - TokenKind::Err => "err", + TokenKind::None => self.builtin_none, + TokenKind::Some => self.builtin_some, + TokenKind::Ok => self.builtin_ok, + TokenKind::Err => self.builtin_err, _ => unreachable!(), - }.to_string(); + }; self.advance(); let mut node = Node::Ident(IdentNode { span: tok.span, name }); loop { @@ -887,40 +925,40 @@ impl Parser { } } - fn expect_identifier(&mut self) -> Result { + fn expect_identifier(&mut self) -> Result { let peeked_kind = self.peek().kind.clone(); match peeked_kind { TokenKind::Identifier(name) => { self.advance(); - Ok(name) + Ok(self.interner.intern(&name)) } TokenKind::Optional => { self.advance(); - Ok("optional".to_string()) + Ok(self.builtin_optional) } TokenKind::Result => { self.advance(); - Ok("result".to_string()) + Ok(self.builtin_result) } TokenKind::None => { self.advance(); - Ok("none".to_string()) + Ok(self.builtin_none) } TokenKind::Some => { self.advance(); - Ok("some".to_string()) + Ok(self.builtin_some) } TokenKind::Ok => { self.advance(); - Ok("ok".to_string()) + Ok(self.builtin_ok) } TokenKind::Err => { self.advance(); - Ok("err".to_string()) + Ok(self.builtin_err) } TokenKind::Bounded => { self.advance(); - Ok("bounded".to_string()) + Ok(self.builtin_bounded) } TokenKind::Invalid(msg) => { let code = if msg.contains("Unterminated string") { @@ -1036,7 +1074,8 @@ mod tests { #[test] fn test_parse_empty_file() { - let mut parser = Parser::new("", 0); + let mut interner = NameInterner::new(); + let mut parser = Parser::new("", 0, &mut interner); let result = parser.parse_file().unwrap(); assert_eq!(result.imports.len(), 0); assert_eq!(result.decls.len(), 0); @@ -1048,14 +1087,16 @@ mod tests { import std.io from "std"; import math from "./math.pbs"; "#; - let mut parser = Parser::new(source, 0); + let mut interner = NameInterner::new(); + let mut parser = Parser::new(source, 0, &mut interner); let result = parser.parse_file().unwrap(); assert_eq!(result.imports.len(), 2); if let Node::Import(ref imp) = result.imports[0] { assert_eq!(imp.from, "std"); if let Node::ImportSpec(ref spec) = *imp.spec { - assert_eq!(spec.path, vec!["std", "io"]); + let path: Vec<&str> = spec.path.iter().map(|id| interner.resolve(*id)).collect(); + assert_eq!(path, vec!["std", "io"]); } else { panic!("Expected ImportSpec"); } } else { panic!("Expected Import"); } } @@ -1067,15 +1108,16 @@ fn add(a: int, b: int): int { return a + b; } "#; - let mut parser = Parser::new(source, 0); + let mut interner = NameInterner::new(); + let mut parser = Parser::new(source, 0, &mut interner); let result = parser.parse_file().unwrap(); assert_eq!(result.decls.len(), 1); if let Node::FnDecl(ref f) = result.decls[0] { - assert_eq!(f.name, "add"); + assert_eq!(interner.resolve(f.name), "add"); assert_eq!(f.params.len(), 2); - assert_eq!(f.params[0].name, "a"); - assert_eq!(f.params[1].name, "b"); + assert_eq!(interner.resolve(f.params[0].name), "a"); + assert_eq!(interner.resolve(f.params[1].name), "b"); } else { panic!("Expected FnDecl"); } } @@ -1087,12 +1129,13 @@ pub declare struct Point { y: int } "#; - let mut parser = Parser::new(source, 0); + let mut interner = NameInterner::new(); + let mut parser = Parser::new(source, 0, &mut interner); let result = parser.parse_file().unwrap(); assert_eq!(result.decls.len(), 1); if let Node::TypeDecl(ref t) = result.decls[0] { - assert_eq!(t.name, "Point"); + assert_eq!(interner.resolve(t.name), "Point"); assert_eq!(t.type_kind, "struct"); assert_eq!(t.vis, Some("pub".to_string())); } else { panic!("Expected TypeDecl"); } @@ -1106,12 +1149,13 @@ pub service Audio { fn stop(): bool; } "#; - let mut parser = Parser::new(source, 0); + let mut interner = NameInterner::new(); + let mut parser = Parser::new(source, 0, &mut interner); let result = parser.parse_file().unwrap(); assert_eq!(result.decls.len(), 1); if let Node::ServiceDecl(ref s) = result.decls[0] { - assert_eq!(s.name, "Audio"); + assert_eq!(interner.resolve(s.name), "Audio"); assert_eq!(s.members.len(), 2); } else { panic!("Expected ServiceDecl"); } } @@ -1125,7 +1169,8 @@ fn main() { foo(x, y); } "#; - let mut parser = Parser::new(source, 0); + let mut interner = NameInterner::new(); + let mut parser = Parser::new(source, 0, &mut interner); let result = parser.parse_file().unwrap(); assert_eq!(result.decls.len(), 1); } @@ -1146,7 +1191,8 @@ fn main(x: int) { }; } "#; - let mut parser = Parser::new(source, 0); + let mut interner = NameInterner::new(); + let mut parser = Parser::new(source, 0, &mut interner); let result = parser.parse_file().unwrap(); assert_eq!(result.decls.len(), 1); } @@ -1161,7 +1207,8 @@ fn bad() { fn good() {} "#; - let mut parser = Parser::new(source, 0); + let mut interner = NameInterner::new(); + let mut parser = Parser::new(source, 0, &mut interner); let result = parser.parse_file(); assert!(result.is_err()); } @@ -1169,7 +1216,8 @@ fn good() {} #[test] fn test_parse_mod_fn() { let source = "mod fn test() {}"; - let mut parser = Parser::new(source, 0); + let mut interner = NameInterner::new(); + let mut parser = Parser::new(source, 0, &mut interner); let result = parser.parse_file().expect("mod fn should be allowed"); if let Node::FnDecl(fn_decl) = &result.decls[0] { assert_eq!(fn_decl.vis, Some("mod".to_string())); @@ -1181,7 +1229,8 @@ fn good() {} #[test] fn test_parse_pub_fn() { let source = "pub fn test() {}"; - let mut parser = Parser::new(source, 0); + let mut interner = NameInterner::new(); + let mut parser = Parser::new(source, 0, &mut interner); let result = parser.parse_file().expect("pub fn should be allowed in parser"); if let Node::FnDecl(fn_decl) = &result.decls[0] { assert_eq!(fn_decl.vis, Some("pub".to_string())); @@ -1197,12 +1246,17 @@ fn main() { return 42; } "#; - let mut parser = Parser::new(source, 0); + let mut interner = NameInterner::new(); + let mut parser = Parser::new(source, 0, &mut interner); let result = parser.parse_file().unwrap(); - let json = serde_json::to_string_pretty(&Node::File(result)).unwrap(); + let json = serde_json::to_string_pretty(&Node::File(result.clone())).unwrap(); assert!(json.contains("\"kind\": \"File\"")); assert!(json.contains("\"kind\": \"FnDecl\"")); - assert!(json.contains("\"name\": \"main\"")); + if let Node::FnDecl(fn_decl) = &result.decls[0] { + assert_eq!(interner.resolve(fn_decl.name), "main"); + } else { + panic!("Expected FnDecl"); + } } } diff --git a/crates/prometeu-compiler/src/frontends/pbs/resolver.rs b/crates/prometeu-compiler/src/frontends/pbs/resolver.rs index 96356269..e1596c39 100644 --- a/crates/prometeu-compiler/src/frontends/pbs/resolver.rs +++ b/crates/prometeu-compiler/src/frontends/pbs/resolver.rs @@ -2,6 +2,7 @@ use crate::common::diagnostics::{Diagnostic, DiagnosticBundle, DiagnosticLevel}; use crate::common::spans::Span; use crate::frontends::pbs::ast::*; use crate::frontends::pbs::symbols::*; +use prometeu_analysis::{NameId, NameInterner}; use std::collections::HashMap; pub trait ModuleProvider { @@ -9,9 +10,10 @@ pub trait ModuleProvider { } pub struct Resolver<'a> { + interner: &'a NameInterner, module_provider: &'a dyn ModuleProvider, current_module: &'a ModuleSymbols, - scopes: Vec>, + scopes: Vec>, pub imported_symbols: ModuleSymbols, diagnostics: Vec, } @@ -20,8 +22,10 @@ impl<'a> Resolver<'a> { pub fn new( current_module: &'a ModuleSymbols, module_provider: &'a dyn ModuleProvider, + interner: &'a NameInterner, ) -> Self { Self { + interner, module_provider, current_module, scopes: Vec::new(), @@ -58,30 +62,30 @@ impl<'a> Resolver<'a> { if let Node::ImportSpec(spec) = &*imp.spec { for name in &spec.path { // Try to find in Type namespace - if let Some(sym) = target_symbols.type_symbols.get(name) { + if let Some(sym) = target_symbols.type_symbols.get(*name) { if sym.visibility == Visibility::Pub { let mut sym = sym.clone(); sym.origin = Some(imp.from.clone()); if let Err(_) = self.imported_symbols.type_symbols.insert(sym) { - self.error_duplicate_import(name, imp.span); + self.error_duplicate_import(*name, imp.span); } } else { self.error_visibility(sym, imp.span); } } // Try to find in Value namespace - else if let Some(sym) = target_symbols.value_symbols.get(name) { + else if let Some(sym) = target_symbols.value_symbols.get(*name) { if sym.visibility == Visibility::Pub { let mut sym = sym.clone(); sym.origin = Some(imp.from.clone()); if let Err(_) = self.imported_symbols.value_symbols.insert(sym) { - self.error_duplicate_import(name, imp.span); + self.error_duplicate_import(*name, imp.span); } } else { self.error_visibility(sym, imp.span); } } else { - self.error_undefined(name, imp.span); + self.error_undefined(*name, imp.span); } } } @@ -139,13 +143,13 @@ impl<'a> Resolver<'a> { } } Node::Ident(n) => { - self.resolve_identifier(&n.name, n.span, Namespace::Value); + self.resolve_identifier(n.name, n.span, Namespace::Value); } Node::TypeName(n) => { - self.resolve_identifier(&n.name, n.span, Namespace::Type); + self.resolve_identifier(n.name, n.span, Namespace::Type); } Node::TypeApp(n) => { - self.resolve_identifier(&n.base, n.span, Namespace::Type); + self.resolve_identifier(n.base, n.span, Namespace::Type); for arg in &n.args { self.resolve_type_ref(arg); } @@ -158,32 +162,32 @@ impl<'a> Resolver<'a> { Node::Mutate(n) => { self.resolve_node(&n.target); self.enter_scope(); - self.define_local(&n.binding, n.span, SymbolKind::Local); + self.define_local(n.binding, n.span, SymbolKind::Local); self.resolve_node(&n.body); self.exit_scope(); } Node::Borrow(n) => { self.resolve_node(&n.target); self.enter_scope(); - self.define_local(&n.binding, n.span, SymbolKind::Local); + self.define_local(n.binding, n.span, SymbolKind::Local); self.resolve_node(&n.body); self.exit_scope(); } Node::Peek(n) => { self.resolve_node(&n.target); self.enter_scope(); - self.define_local(&n.binding, n.span, SymbolKind::Local); + self.define_local(n.binding, n.span, SymbolKind::Local); self.resolve_node(&n.body); self.exit_scope(); } Node::MemberAccess(n) => { if let Node::Ident(id) = &*n.object { - if !self.is_builtin(&id.name, Namespace::Type) { - if self.lookup_identifier(&id.name, Namespace::Value).is_none() { + if !self.is_builtin(id.name, Namespace::Type) { + if self.lookup_identifier(id.name, Namespace::Value).is_none() { // If not found in Value namespace, try Type namespace (for Contracts/Services) - if self.lookup_identifier(&id.name, Namespace::Type).is_none() { + if self.lookup_identifier(id.name, Namespace::Type).is_none() { // Still not found, use resolve_identifier to report error in Value namespace - self.resolve_identifier(&id.name, id.span, Namespace::Value); + self.resolve_identifier(id.name, id.span, Namespace::Value); } } } @@ -199,7 +203,7 @@ impl<'a> Resolver<'a> { self.enter_scope(); for param in &n.params { self.resolve_type_ref(¶m.ty); - self.define_local(¶m.name, param.span, SymbolKind::Local); + self.define_local(param.name, param.span, SymbolKind::Local); } if let Some(ret) = &n.ret { self.resolve_type_ref(ret); @@ -210,7 +214,7 @@ impl<'a> Resolver<'a> { fn resolve_service_decl(&mut self, n: &ServiceDeclNode) { if let Some(ext) = &n.extends { - self.resolve_identifier(ext, n.span, Namespace::Type); + self.resolve_identifier(*ext, n.span, Namespace::Type); } for member in &n.members { if let Node::ServiceFnSig(sig) = member { @@ -231,7 +235,7 @@ impl<'a> Resolver<'a> { } self.enter_scope(); for ctor in &n.constructors { - self.define_local(&ctor.name, ctor.span, SymbolKind::Local); + self.define_local(ctor.name, ctor.span, SymbolKind::Local); } for constant in &n.constants { self.resolve_node(&constant.value); @@ -256,7 +260,7 @@ impl<'a> Resolver<'a> { self.enter_scope(); for param in &n.params { self.resolve_type_ref(¶m.ty); - self.define_local(¶m.name, param.span, SymbolKind::Local); + self.define_local(param.name, param.span, SymbolKind::Local); } for init in &n.initializers { self.resolve_node(init); @@ -278,14 +282,14 @@ impl<'a> Resolver<'a> { self.resolve_type_ref(ty); } self.resolve_node(&n.init); - self.define_local(&n.name, n.span, SymbolKind::Local); + self.define_local(n.name, n.span, SymbolKind::Local); } fn resolve_type_ref(&mut self, node: &Node) { self.resolve_node(node); } - fn resolve_identifier(&mut self, name: &str, span: Span, namespace: Namespace) -> Option { + fn resolve_identifier(&mut self, name: NameId, span: Span, namespace: Namespace) -> Option { if self.is_builtin(name, namespace) { return None; } @@ -297,7 +301,7 @@ impl<'a> Resolver<'a> { self.diagnostics.push(Diagnostic { level: DiagnosticLevel::Error, code: Some("E_TYPE_UNKNOWN_TYPE".to_string()), - message: format!("Unknown type: {}", name), + message: format!("Unknown type: {}", self.interner.resolve(name)), span: Some(span), }); } else { @@ -307,7 +311,8 @@ impl<'a> Resolver<'a> { } } - fn is_builtin(&self, name: &str, namespace: Namespace) -> bool { + fn is_builtin(&self, name: NameId, namespace: Namespace) -> bool { + let name = self.interner.resolve(name); match namespace { Namespace::Type => match name { "int" | "float" | "string" | "bool" | "void" | "optional" | "result" | "bounded" | @@ -321,11 +326,11 @@ impl<'a> Resolver<'a> { } } - fn lookup_identifier(&self, name: &str, namespace: Namespace) -> Option { + fn lookup_identifier(&self, name: NameId, namespace: Namespace) -> Option { // 1. local bindings if namespace == Namespace::Value { for scope in self.scopes.iter().rev() { - if let Some(sym) = scope.get(name) { + if let Some(sym) = scope.get(&name) { return Some(sym.clone()); } } @@ -369,7 +374,7 @@ impl<'a> Resolver<'a> { None } - fn define_local(&mut self, name: &str, span: Span, kind: SymbolKind) { + fn define_local(&mut self, name: NameId, span: Span, kind: SymbolKind) { let scope = self.scopes.last_mut().expect("No scope to define local"); // Check for collision in Type namespace at top-level? @@ -379,22 +384,25 @@ impl<'a> Resolver<'a> { self.diagnostics.push(Diagnostic { level: DiagnosticLevel::Error, code: Some("E_RESOLVE_NAMESPACE_COLLISION".to_string()), - message: format!("Local variable '{}' collides with a type name", name), + message: format!( + "Local variable '{}' collides with a type name", + self.interner.resolve(name) + ), span: Some(span), }); return; } - if scope.contains_key(name) { + if scope.contains_key(&name) { self.diagnostics.push(Diagnostic { level: DiagnosticLevel::Error, code: Some("E_RESOLVE_DUPLICATE_SYMBOL".to_string()), - message: format!("Duplicate local variable '{}'", name), + message: format!("Duplicate local variable '{}'", self.interner.resolve(name)), span: Some(span), }); } else { - scope.insert(name.to_string(), Symbol { - name: name.to_string(), + scope.insert(name, Symbol { + name, kind, namespace: Namespace::Value, visibility: Visibility::FilePrivate, @@ -414,20 +422,20 @@ impl<'a> Resolver<'a> { self.scopes.pop(); } - fn error_undefined(&mut self, name: &str, span: Span) { + fn error_undefined(&mut self, name: NameId, span: Span) { self.diagnostics.push(Diagnostic { level: DiagnosticLevel::Error, code: Some("E_RESOLVE_UNDEFINED".to_string()), - message: format!("Undefined identifier: {}", name), + message: format!("Undefined identifier: {}", self.interner.resolve(name)), span: Some(span), }); } - fn error_duplicate_import(&mut self, name: &str, span: Span) { + fn error_duplicate_import(&mut self, name: NameId, span: Span) { self.diagnostics.push(Diagnostic { level: DiagnosticLevel::Error, code: Some("E_RESOLVE_DUPLICATE_SYMBOL".to_string()), - message: format!("Duplicate import: {}", name), + message: format!("Duplicate import: {}", self.interner.resolve(name)), span: Some(span), }); } @@ -436,7 +444,10 @@ impl<'a> Resolver<'a> { self.diagnostics.push(Diagnostic { level: DiagnosticLevel::Error, code: Some("E_RESOLVE_VISIBILITY".to_string()), - message: format!("DebugSymbol '{}' is not visible here", sym.name), + message: format!( + "DebugSymbol '{}' is not visible here", + self.interner.resolve(sym.name) + ), span: Some(span), }); } @@ -450,11 +461,13 @@ mod tests { use crate::frontends::pbs::*; use std::path::PathBuf; - fn setup_test(source: &str) -> (ast::FileNode, usize) { + fn setup_test(source: &str) -> (ast::FileNode, usize, NameInterner) { let mut fm = FileManager::new(); let file_id = fm.add(PathBuf::from("test.pbs"), source.to_string()); - let mut parser = parser::Parser::new(source, file_id); - (parser.parse_file().expect("Parsing failed"), file_id) + let mut interner = NameInterner::new(); + let mut parser = parser::Parser::new(source, file_id, &mut interner); + let ast = parser.parse_file().expect("Parsing failed"); + (ast, file_id, interner) } #[test] @@ -463,8 +476,8 @@ mod tests { declare struct Foo {} declare struct Foo {} "; - let (ast, _) = setup_test(source); - let mut collector = SymbolCollector::new(); + let (ast, _, mut interner) = setup_test(source); + let mut collector = SymbolCollector::new(&interner); let result = collector.collect(&ast); assert!(result.is_err()); @@ -478,8 +491,8 @@ mod tests { declare struct Foo {} fn Foo() {} "; - let (ast, _) = setup_test(source); - let mut collector = SymbolCollector::new(); + let (ast, _, mut interner) = setup_test(source); + let mut collector = SymbolCollector::new(&interner); let result = collector.collect(&ast); assert!(result.is_err()); @@ -494,8 +507,8 @@ mod tests { let x = y; } "; - let (ast, _) = setup_test(source); - let mut collector = SymbolCollector::new(); + let (ast, _, mut interner) = setup_test(source); + let mut collector = SymbolCollector::new(&interner); let (ts, vs) = collector.collect(&ast).expect("Collection failed"); let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs }; @@ -504,7 +517,7 @@ mod tests { fn get_module_symbols(&self, _path: &str) -> Option<&ModuleSymbols> { None } } - let mut resolver = Resolver::new(&ms, &EmptyProvider); + let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner); let result = resolver.resolve(&ast); assert!(result.is_err()); @@ -520,8 +533,8 @@ mod tests { let y = x; } "; - let (ast, _) = setup_test(source); - let mut collector = SymbolCollector::new(); + let (ast, _, mut interner) = setup_test(source); + let mut collector = SymbolCollector::new(&interner); let (ts, vs) = collector.collect(&ast).expect("Collection failed"); let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs }; @@ -530,7 +543,7 @@ mod tests { fn get_module_symbols(&self, _path: &str) -> Option<&ModuleSymbols> { None } } - let mut resolver = Resolver::new(&ms, &EmptyProvider); + let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner); let result = resolver.resolve(&ast); assert!(result.is_ok()); @@ -542,8 +555,8 @@ mod tests { import PrivateType from \"./other.pbs\" fn main() {} "; - let (ast, _) = setup_test(source); - let mut collector = SymbolCollector::new(); + let (ast, _, mut interner) = setup_test(source); + let mut collector = SymbolCollector::new(&interner); let (ts, vs) = collector.collect(&ast).expect("Collection failed"); let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs }; @@ -558,7 +571,7 @@ mod tests { let mut other_ts = SymbolTable::new(); other_ts.insert(Symbol { - name: "PrivateType".to_string(), + name: interner.intern("PrivateType"), kind: SymbolKind::Struct, namespace: Namespace::Type, visibility: Visibility::FilePrivate, @@ -572,7 +585,7 @@ mod tests { other: ModuleSymbols { type_symbols: other_ts, value_symbols: SymbolTable::new() }, }; - let mut resolver = Resolver::new(&ms, &mock_provider); + let mut resolver = Resolver::new(&ms, &mock_provider, &interner); let result = resolver.resolve(&ast); assert!(result.is_err()); @@ -588,8 +601,8 @@ mod tests { let x: PubType = 10; } "; - let (ast, _) = setup_test(source); - let mut collector = SymbolCollector::new(); + let (ast, _, mut interner) = setup_test(source); + let mut collector = SymbolCollector::new(&interner); let (ts, vs) = collector.collect(&ast).expect("Collection failed"); let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs }; @@ -604,7 +617,7 @@ mod tests { let mut other_ts = SymbolTable::new(); other_ts.insert(Symbol { - name: "PubType".to_string(), + name: interner.intern("PubType"), kind: SymbolKind::Struct, namespace: Namespace::Type, visibility: Visibility::Pub, @@ -618,7 +631,7 @@ mod tests { other: ModuleSymbols { type_symbols: other_ts, value_symbols: SymbolTable::new() }, }; - let mut resolver = Resolver::new(&ms, &mock_provider); + let mut resolver = Resolver::new(&ms, &mock_provider, &interner); let result = resolver.resolve(&ast); assert!(result.is_ok()); @@ -630,8 +643,8 @@ mod tests { import NonExistent from \"./missing.pbs\" fn main() {} "; - let (ast, _) = setup_test(source); - let mut collector = SymbolCollector::new(); + let (ast, _, interner) = setup_test(source); + let mut collector = SymbolCollector::new(&interner); let (ts, vs) = collector.collect(&ast).expect("Collection failed"); let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs }; @@ -640,7 +653,7 @@ mod tests { fn get_module_symbols(&self, _path: &str) -> Option<&ModuleSymbols> { None } } - let mut resolver = Resolver::new(&ms, &EmptyProvider); + let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner); let result = resolver.resolve(&ast); assert!(result.is_err()); diff --git a/crates/prometeu-compiler/src/frontends/pbs/symbols.rs b/crates/prometeu-compiler/src/frontends/pbs/symbols.rs index 3799fe0f..910165ca 100644 --- a/crates/prometeu-compiler/src/frontends/pbs/symbols.rs +++ b/crates/prometeu-compiler/src/frontends/pbs/symbols.rs @@ -1,5 +1,6 @@ use crate::common::spans::Span; use crate::frontends::pbs::types::PbsType; +use prometeu_analysis::NameId; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -28,7 +29,7 @@ pub enum Namespace { #[derive(Debug, Clone)] pub struct Symbol { - pub name: String, + pub name: NameId, pub kind: SymbolKind, pub namespace: Namespace, pub visibility: Visibility, @@ -40,7 +41,7 @@ pub struct Symbol { #[derive(Debug, Clone)] pub struct SymbolTable { - pub symbols: HashMap, + pub symbols: HashMap, } #[derive(Debug, Clone)] @@ -65,15 +66,15 @@ impl SymbolTable { } } - pub fn insert(&mut self, symbol: Symbol) -> Result<(), Symbol> { - if let Some(existing) = self.symbols.get(&symbol.name) { - return Err(existing.clone()); + pub fn insert(&mut self, symbol: Symbol) -> Result<(), ()> { + if self.symbols.contains_key(&symbol.name) { + return Err(()); } - self.symbols.insert(symbol.name.clone(), symbol); + self.symbols.insert(symbol.name, symbol); Ok(()) } - pub fn get(&self, name: &str) -> Option<&Symbol> { - self.symbols.get(name) + pub fn get(&self, name: NameId) -> Option<&Symbol> { + self.symbols.get(&name) } } diff --git a/crates/prometeu-compiler/src/frontends/pbs/typecheck.rs b/crates/prometeu-compiler/src/frontends/pbs/typecheck.rs index c7e75dc1..e41bed2d 100644 --- a/crates/prometeu-compiler/src/frontends/pbs/typecheck.rs +++ b/crates/prometeu-compiler/src/frontends/pbs/typecheck.rs @@ -5,12 +5,14 @@ use crate::frontends::pbs::contracts::ContractRegistry; use crate::frontends::pbs::resolver::ModuleProvider; use crate::frontends::pbs::symbols::*; use crate::frontends::pbs::types::PbsType; +use prometeu_analysis::{NameId, NameInterner}; use std::collections::HashMap; pub struct TypeChecker<'a> { module_symbols: &'a mut ModuleSymbols, imported_symbols: &'a ModuleSymbols, _module_provider: &'a dyn ModuleProvider, + interner: &'a NameInterner, scopes: Vec>, mut_bindings: Vec>, current_return_type: Option, @@ -26,11 +28,13 @@ impl<'a> TypeChecker<'a> { module_symbols: &'a mut ModuleSymbols, imported_symbols: &'a ModuleSymbols, module_provider: &'a dyn ModuleProvider, + interner: &'a NameInterner, ) -> Self { Self { module_symbols, imported_symbols, _module_provider: module_provider, + interner, scopes: Vec::new(), mut_bindings: Vec::new(), current_return_type: None, @@ -84,14 +88,15 @@ impl<'a> TypeChecker<'a> { Node::ServiceDecl(n) => { // For service, the symbol's type is just Service(name) if let Some(sym) = self.module_symbols.type_symbols.symbols.get_mut(&n.name) { - sym.ty = Some(PbsType::Service(n.name.clone())); + sym.ty = Some(PbsType::Service(self.interner.resolve(n.name).to_string())); } } Node::TypeDecl(n) => { + let type_name = self.interner.resolve(n.name).to_string(); let ty = match n.type_kind.as_str() { - "struct" => PbsType::Struct(n.name.clone()), - "contract" => PbsType::Contract(n.name.clone()), - "error" => PbsType::ErrorType(n.name.clone()), + "struct" => PbsType::Struct(type_name.clone()), + "contract" => PbsType::Contract(type_name.clone()), + "error" => PbsType::ErrorType(type_name.clone()), _ => PbsType::Void, }; if let Some(sym) = self.module_symbols.type_symbols.symbols.get_mut(&n.name) { @@ -117,7 +122,7 @@ impl<'a> TypeChecker<'a> { params: params.clone(), return_type: Box::new(ty.clone()), }; - ctors.insert(n.name.clone(), default_ctor_ty); + ctors.insert(type_name.clone(), default_ctor_ty); } for ctor in &n.constructors { @@ -129,9 +134,9 @@ impl<'a> TypeChecker<'a> { params, return_type: Box::new(ty.clone()), }; - ctors.insert(ctor.name.clone(), ctor_ty); + ctors.insert(self.interner.resolve(ctor.name).to_string(), ctor_ty); } - self.struct_constructors.insert(n.name.clone(), ctors); + self.struct_constructors.insert(type_name.clone(), ctors); // Resolve methods let mut methods = HashMap::new(); @@ -146,11 +151,11 @@ impl<'a> TypeChecker<'a> { params, return_type: Box::new(self.resolve_type_node(&m.ret)), }; - methods.insert(m.name.clone(), m_ty); + methods.insert(self.interner.resolve(m.name).to_string(), m_ty); } } } - self.struct_methods.insert(n.name.clone(), methods); + self.struct_methods.insert(type_name, methods); } _ => {} } @@ -206,28 +211,30 @@ impl<'a> TypeChecker<'a> { Node::IfExpr(n) => self.check_if_expr(n), Node::WhenExpr(n) => self.check_when_expr(n), Node::Alloc(n) => self.check_alloc(n), - Node::Mutate(n) => self.check_hip(n.span, &n.target, &n.binding, &n.body, true), - Node::Borrow(n) => self.check_hip(n.span, &n.target, &n.binding, &n.body, false), - Node::Peek(n) => self.check_hip(n.span, &n.target, &n.binding, &n.body, false), + Node::Mutate(n) => self.check_hip(n.span, &n.target, n.binding, &n.body, true), + Node::Borrow(n) => self.check_hip(n.span, &n.target, n.binding, &n.body, false), + Node::Peek(n) => self.check_hip(n.span, &n.target, n.binding, &n.body, false), Node::MemberAccess(n) => self.check_member_access(n), _ => PbsType::Void, } } fn check_member_access(&mut self, n: &MemberAccessNode) -> PbsType { + let member_str = self.interner.resolve(n.member); if let Node::Ident(id) = &*n.object { + let name_str = self.interner.resolve(id.name); // Check if it's a local first - let is_local = self.scopes.iter().any(|s| s.contains_key(&id.name)); + let is_local = self.scopes.iter().any(|s| s.contains_key(name_str)); if !is_local { // Check if it's a known host contract - let sym_opt = self.module_symbols.type_symbols.get(&id.name) - .or_else(|| self.imported_symbols.type_symbols.get(&id.name)); + let sym_opt = self.module_symbols.type_symbols.get(id.name) + .or_else(|| self.imported_symbols.type_symbols.get(id.name)); if let Some(sym) = sym_opt { if sym.kind == SymbolKind::Contract && sym.is_host { // Check if the method exists in registry - if let Some(method) = self.contract_registry.get_method(&id.name, &n.member) { + if let Some(method) = self.contract_registry.get_method(name_str, member_str) { return PbsType::Function { params: method.params.clone(), return_type: Box::new(method.return_type.clone()), @@ -236,7 +243,7 @@ impl<'a> TypeChecker<'a> { self.diagnostics.push(Diagnostic { level: DiagnosticLevel::Error, code: Some("E_RESOLVE_UNDEFINED".to_string()), - message: format!("Method '{}' not found on host contract '{}'", n.member, id.name), + message: format!("Method '{}' not found on host contract '{}'", member_str, name_str), span: Some(n.span), }); } @@ -244,8 +251,8 @@ impl<'a> TypeChecker<'a> { } // v0: Suporte explícito às constantes de Color - if sym.kind == SymbolKind::Struct && id.name == "Color" { - match n.member.as_str() { + if sym.kind == SymbolKind::Struct && name_str == "Color" { + match member_str { "BLACK" | "WHITE" | "RED" | "GREEN" | "BLUE" => { return PbsType::Struct("Color".to_string()); } @@ -255,22 +262,22 @@ impl<'a> TypeChecker<'a> { } // Builtin Struct Associated Members (Static/Constants) - if let Some(constants) = self.struct_constants.get(&id.name) { - if let Some(ty) = constants.get(&n.member) { + if let Some(constants) = self.struct_constants.get(name_str) { + if let Some(ty) = constants.get(member_str) { return ty.clone(); } } // Fallback for constructors if used as Type.alias(...) - if let Some(ctors) = self.struct_constructors.get(&id.name) { - if let Some(ty) = ctors.get(&n.member) { + if let Some(ctors) = self.struct_constructors.get(name_str) { + if let Some(ty) = ctors.get(member_str) { return ty.clone(); } } // Fallback for static methods if used as Type.method(...) - if let Some(methods) = self.struct_methods.get(&id.name) { - if let Some(ty) = methods.get(&n.member) { + if let Some(methods) = self.struct_methods.get(name_str) { + if let Some(ty) = methods.get(member_str) { return ty.clone(); } } @@ -280,7 +287,7 @@ impl<'a> TypeChecker<'a> { let obj_ty = self.check_node(&n.object); if let PbsType::Struct(ref name) = obj_ty { if let Some(methods) = self.struct_methods.get(name) { - if let Some(ty) = methods.get(&n.member) { + if let Some(ty) = methods.get(member_str) { // If it's a method call on an instance, the first parameter (self) is implicit if let PbsType::Function { mut params, return_type } = ty.clone() { if !params.is_empty() { @@ -299,7 +306,7 @@ impl<'a> TypeChecker<'a> { PbsType::Struct(ref name) => { match name.as_str() { "Color" => { - match n.member.as_str() { + match member_str { "value" => return PbsType::Bounded, "raw" => return PbsType::Function { params: vec![], // self is implicit @@ -309,14 +316,14 @@ impl<'a> TypeChecker<'a> { } } "ButtonState" => { - match n.member.as_str() { + match member_str { "pressed" | "released" | "down" => return PbsType::Bool, "hold_frames" => return PbsType::Bounded, _ => {} } } "Pad" => { - match n.member.as_str() { + match member_str { "up" | "down" | "left" | "right" | "a" | "b" | "x" | "y" | "l" | "r" | "start" | "select" => { return PbsType::Struct("ButtonState".to_string()); } @@ -330,7 +337,7 @@ impl<'a> TypeChecker<'a> { } } "Touch" => { - match n.member.as_str() { + match member_str { "f" => return PbsType::Struct("ButtonState".to_string()), "x" | "y" => return PbsType::Int, _ => {} @@ -343,7 +350,7 @@ impl<'a> TypeChecker<'a> { } if obj_ty != PbsType::Void { - let msg = format!("Member '{}' not found on type {:?}", n.member, obj_ty); + let msg = format!("Member '{}' not found on type {:?}", member_str, obj_ty); self.diagnostics.push(Diagnostic { level: DiagnosticLevel::Error, code: Some("E_RESOLVE_UNDEFINED".to_string()), @@ -361,7 +368,7 @@ impl<'a> TypeChecker<'a> { PbsType::Contract(format!("Gate<{}>", ty)) // Approximation for v0 } - fn check_hip(&mut self, _span: Span, target: &Node, binding: &str, body: &Node, is_mut: bool) -> PbsType { + fn check_hip(&mut self, _span: Span, target: &Node, binding: NameId, body: &Node, is_mut: bool) -> PbsType { let target_ty = self.check_node(target); // In v0, we assume target is a gate. We bind the inner type to the binding. let inner_ty = match target_ty { @@ -380,20 +387,21 @@ impl<'a> TypeChecker<'a> { }; self.enter_scope(); - self.define_local(binding, inner_ty, is_mut); + let binding_str = self.interner.resolve(binding); + self.define_local(binding_str, inner_ty, is_mut); let body_ty = self.check_node(body); self.exit_scope(); body_ty } fn check_fn_decl(&mut self, n: &FnDeclNode) { - let sig = self.module_symbols.value_symbols.get(&n.name).and_then(|s| s.ty.clone()); + let sig = self.module_symbols.value_symbols.get(n.name).and_then(|s| s.ty.clone()); if let Some(PbsType::Function { params, return_type }) = sig { self.enter_scope(); self.current_return_type = Some(*return_type.clone()); for (param, ty) in n.params.iter().zip(params.iter()) { - self.define_local(¶m.name, ty.clone(), false); + self.define_local(self.interner.resolve(param.name), ty.clone(), false); } let _body_ty = self.check_node(&n.body); @@ -410,7 +418,11 @@ impl<'a> TypeChecker<'a> { self.diagnostics.push(Diagnostic { level: DiagnosticLevel::Error, code: Some("E_TYPE_RETURN_PATH".to_string()), - message: format!("Function '{}' must return a value of type {}", n.name, return_type), + message: format!( + "Function '{}' must return a value of type {}", + self.interner.resolve(n.name), + return_type + ), span: Some(n.span), }); } @@ -452,44 +464,45 @@ impl<'a> TypeChecker<'a> { init_ty }; - self.define_local(&n.name, final_ty, n.is_mut); + self.define_local(self.interner.resolve(n.name), final_ty, n.is_mut); } fn check_identifier(&mut self, n: &IdentNode) -> PbsType { + let name_str = self.interner.resolve(n.name); // Check locals for scope in self.scopes.iter().rev() { - if let Some(ty) = scope.get(&n.name) { + if let Some(ty) = scope.get(name_str) { return ty.clone(); } } // Check module symbols - if let Some(sym) = self.module_symbols.value_symbols.get(&n.name) { + if let Some(sym) = self.module_symbols.value_symbols.get(n.name) { if let Some(ty) = &sym.ty { return ty.clone(); } } // Check imported symbols - if let Some(sym) = self.imported_symbols.value_symbols.get(&n.name) { + if let Some(sym) = self.imported_symbols.value_symbols.get(n.name) { if let Some(ty) = &sym.ty { return ty.clone(); } } // Fallback for default constructor: check if it's a struct name - if let Some(ctors) = self.struct_constructors.get(&n.name) { - if let Some(ty) = ctors.get(&n.name) { + if let Some(ctors) = self.struct_constructors.get(name_str) { + if let Some(ty) = ctors.get(name_str) { return ty.clone(); } } // Built-ins (some, none, ok, err might be handled as calls or special keywords) // For v0, let's treat none as a special literal or identifier - if n.name == "none" { + if name_str == "none" { return PbsType::None; } - if n.name == "true" || n.name == "false" { + if name_str == "true" || name_str == "false" { return PbsType::Bool; } @@ -502,7 +515,7 @@ impl<'a> TypeChecker<'a> { // Handle special built-in "constructors" if let Node::Ident(id) = &*n.callee { - match id.name.as_str() { + match self.interner.resolve(id.name) { "some" => { if n.args.len() == 1 { let inner_ty = self.check_node(&n.args[0]); @@ -673,9 +686,10 @@ impl<'a> TypeChecker<'a> { self.check_constructor_decl(constructor); } - let struct_ty = PbsType::Struct(n.name.clone()); + let type_name = self.interner.resolve(n.name).to_string(); + let struct_ty = PbsType::Struct(type_name.clone()); let mut constants_scope = HashMap::new(); - if let Some(ctors) = self.struct_constructors.get(&n.name) { + if let Some(ctors) = self.struct_constructors.get(&type_name) { for (name, ty) in ctors { constants_scope.insert(name.clone(), ty.clone()); } @@ -688,10 +702,10 @@ impl<'a> TypeChecker<'a> { if !self.is_assignable(&struct_ty, &val_ty) { self.error_type_mismatch(&struct_ty, &val_ty, constant.span); } - constants_map.insert(constant.name.clone(), struct_ty.clone()); + constants_map.insert(self.interner.resolve(constant.name).to_string(), struct_ty.clone()); } self.scopes.pop(); - self.struct_constants.insert(n.name.clone(), constants_map); + self.struct_constants.insert(type_name, constants_map); if let Some(body) = &n.body { self.check_node(body); @@ -702,7 +716,7 @@ impl<'a> TypeChecker<'a> { self.enter_scope(); for param in &n.params { let ty = self.resolve_type_node(¶m.ty); - self.define_local(¶m.name, ty, false); + self.define_local(self.interner.resolve(param.name), ty, false); } for init in &n.initializers { self.check_node(init); @@ -714,29 +728,30 @@ impl<'a> TypeChecker<'a> { fn resolve_type_node(&mut self, node: &Node) -> PbsType { match node { Node::TypeName(tn) => { - match tn.name.as_str() { + let name_str = self.interner.resolve(tn.name); + match name_str { "int" => PbsType::Int, "float" => PbsType::Float, "bool" => PbsType::Bool, "string" => PbsType::String, "void" => PbsType::Void, "bounded" => PbsType::Bounded, - "Color" | "ButtonState" | "Pad" | "Touch" => PbsType::Struct(tn.name.clone()), + "Color" | "ButtonState" | "Pad" | "Touch" => PbsType::Struct(name_str.to_string()), _ => { // Look up in symbol table - if let Some(sym) = self.lookup_type(&tn.name) { + if let Some(sym) = self.lookup_type(tn.name) { match sym.kind { - SymbolKind::Struct => PbsType::Struct(tn.name.clone()), - SymbolKind::Service => PbsType::Service(tn.name.clone()), - SymbolKind::Contract => PbsType::Contract(tn.name.clone()), - SymbolKind::ErrorType => PbsType::ErrorType(tn.name.clone()), + SymbolKind::Struct => PbsType::Struct(name_str.to_string()), + SymbolKind::Service => PbsType::Service(name_str.to_string()), + SymbolKind::Contract => PbsType::Contract(name_str.to_string()), + SymbolKind::ErrorType => PbsType::ErrorType(name_str.to_string()), _ => PbsType::Void, } } else { self.diagnostics.push(Diagnostic { level: DiagnosticLevel::Error, code: Some("E_TYPE_UNKNOWN_TYPE".to_string()), - message: format!("Unknown type: {}", tn.name), + message: format!("Unknown type: {}", name_str), span: Some(tn.span), }); PbsType::Void @@ -745,7 +760,7 @@ impl<'a> TypeChecker<'a> { } } Node::TypeApp(ta) => { - match ta.base.as_str() { + match self.interner.resolve(ta.base) { "optional" => { if ta.args.len() == 1 { PbsType::Optional(Box::new(self.resolve_type_node(&ta.args[0]))) @@ -770,7 +785,7 @@ impl<'a> TypeChecker<'a> { } } - fn lookup_type(&self, name: &str) -> Option<&Symbol> { + fn lookup_type(&self, name: NameId) -> Option<&Symbol> { if let Some(sym) = self.module_symbols.type_symbols.get(name) { return Some(sym); } diff --git a/crates/prometeu-compiler/src/sources.rs b/crates/prometeu-compiler/src/sources.rs index 43abd7cf..33bdabed 100644 --- a/crates/prometeu-compiler/src/sources.rs +++ b/crates/prometeu-compiler/src/sources.rs @@ -2,6 +2,7 @@ use crate::common::diagnostics::DiagnosticBundle; use crate::common::files::FileManager; use crate::frontends::pbs::{collector::SymbolCollector, parser::Parser, Symbol, Visibility}; use crate::manifest::{load_manifest, ManifestKind}; +use prometeu_analysis::NameInterner; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::fs; @@ -118,6 +119,7 @@ fn discover_recursive(dir: &Path, files: &mut Vec) -> std::io::Result<( pub fn build_exports(module_dir: &Path, file_manager: &mut FileManager) -> Result { let mut symbols = HashMap::new(); let mut files = Vec::new(); + let mut interner = NameInterner::new(); if module_dir.is_dir() { discover_recursive(module_dir, &mut files)?; @@ -129,21 +131,21 @@ pub fn build_exports(module_dir: &Path, file_manager: &mut FileManager) -> Resul let source = fs::read_to_string(&file_path)?; let file_id = file_manager.add(file_path.clone(), source.clone()); - let mut parser = Parser::new(&source, file_id); + let mut parser = Parser::new(&source, file_id, &mut interner); let ast = parser.parse_file()?; - let mut collector = SymbolCollector::new(); + let mut collector = SymbolCollector::new(&interner); let (type_symbols, value_symbols) = collector.collect(&ast)?; // Merge only public symbols for symbol in type_symbols.symbols.into_values() { if symbol.visibility == Visibility::Pub { - symbols.insert(symbol.name.clone(), symbol); + symbols.insert(interner.resolve(symbol.name).to_string(), symbol); } } for symbol in value_symbols.symbols.into_values() { if symbol.visibility == Visibility::Pub { - symbols.insert(symbol.name.clone(), symbol); + symbols.insert(interner.resolve(symbol.name).to_string(), symbol); } } } diff --git a/crates/prometeu-compiler/tests/generate_canonical_goldens.rs b/crates/prometeu-compiler/tests/generate_canonical_goldens.rs index 71cdd2e3..f811d8bf 100644 --- a/crates/prometeu-compiler/tests/generate_canonical_goldens.rs +++ b/crates/prometeu-compiler/tests/generate_canonical_goldens.rs @@ -3,6 +3,7 @@ use prometeu_bytecode::BytecodeLoader; use prometeu_compiler::compiler::compile; use prometeu_compiler::frontends::pbs::ast::Node; use prometeu_compiler::frontends::pbs::parser::Parser; +use prometeu_analysis::NameInterner; use std::fs; use std::path::Path; @@ -56,7 +57,8 @@ fn generate_canonical_goldens() { // 3. AST JSON let source = fs::read_to_string(project_dir.join("src/main/modules/main.pbs")).unwrap(); - let mut parser = Parser::new(&source, 0); + let mut interner = NameInterner::new(); + let mut parser = Parser::new(&source, 0, &mut interner); let ast = parser.parse_file().expect("Failed to parse AST"); let ast_node = Node::File(ast); let ast_json = serde_json::to_string_pretty(&ast_node).unwrap(); diff --git a/test-cartridges/canonical/golden/ast.json b/test-cartridges/canonical/golden/ast.json index 36c3b763..cb78f726 100644 --- a/test-cartridges/canonical/golden/ast.json +++ b/test-cartridges/canonical/golden/ast.json @@ -16,7 +16,7 @@ }, "vis": null, "type_kind": "struct", - "name": "Color", + "name": 11, "is_host": false, "params": [ { @@ -25,7 +25,7 @@ "start": 100, "end": 112 }, - "name": "raw", + "name": 12, "ty": { "kind": "TypeName", "span": { @@ -33,7 +33,7 @@ "start": 105, "end": 112 }, - "name": "bounded" + "name": 10 } } ], @@ -45,7 +45,7 @@ "start": 119, "end": 135 }, - "name": "BLACK", + "name": 13, "value": { "kind": "Call", "span": { @@ -60,7 +60,7 @@ "start": 126, "end": 131 }, - "name": "Color" + "name": 11 }, "args": [ { @@ -81,7 +81,7 @@ "start": 139, "end": 159 }, - "name": "WHITE", + "name": 14, "value": { "kind": "Call", "span": { @@ -96,7 +96,7 @@ "start": 146, "end": 151 }, - "name": "Color" + "name": 11 }, "args": [ { @@ -117,7 +117,7 @@ "start": 163, "end": 181 }, - "name": "RED", + "name": 15, "value": { "kind": "Call", "span": { @@ -132,7 +132,7 @@ "start": 168, "end": 173 }, - "name": "Color" + "name": 11 }, "args": [ { @@ -153,7 +153,7 @@ "start": 185, "end": 204 }, - "name": "GREEN", + "name": 16, "value": { "kind": "Call", "span": { @@ -168,7 +168,7 @@ "start": 192, "end": 197 }, - "name": "Color" + "name": 11 }, "args": [ { @@ -189,7 +189,7 @@ "start": 208, "end": 224 }, - "name": "BLUE", + "name": 17, "value": { "kind": "Call", "span": { @@ -204,7 +204,7 @@ "start": 214, "end": 219 }, - "name": "Color" + "name": 11 }, "args": [ { @@ -231,7 +231,7 @@ }, "vis": null, "type_kind": "struct", - "name": "ButtonState", + "name": 18, "is_host": false, "params": [ { @@ -240,7 +240,7 @@ "start": 261, "end": 274 }, - "name": "pressed", + "name": 19, "ty": { "kind": "TypeName", "span": { @@ -248,7 +248,7 @@ "start": 270, "end": 274 }, - "name": "bool" + "name": 20 } }, { @@ -257,7 +257,7 @@ "start": 280, "end": 294 }, - "name": "released", + "name": 21, "ty": { "kind": "TypeName", "span": { @@ -265,7 +265,7 @@ "start": 290, "end": 294 }, - "name": "bool" + "name": 20 } }, { @@ -274,7 +274,7 @@ "start": 300, "end": 310 }, - "name": "down", + "name": 22, "ty": { "kind": "TypeName", "span": { @@ -282,7 +282,7 @@ "start": 306, "end": 310 }, - "name": "bool" + "name": 20 } }, { @@ -291,7 +291,7 @@ "start": 316, "end": 336 }, - "name": "hold_frames", + "name": 23, "ty": { "kind": "TypeName", "span": { @@ -299,7 +299,7 @@ "start": 329, "end": 336 }, - "name": "bounded" + "name": 10 } } ], @@ -316,7 +316,7 @@ }, "vis": null, "type_kind": "struct", - "name": "Pad", + "name": 24, "is_host": false, "params": [ { @@ -325,7 +325,7 @@ "start": 364, "end": 379 }, - "name": "up", + "name": 25, "ty": { "kind": "TypeName", "span": { @@ -333,7 +333,7 @@ "start": 368, "end": 379 }, - "name": "ButtonState" + "name": 18 } }, { @@ -342,7 +342,7 @@ "start": 385, "end": 402 }, - "name": "down", + "name": 22, "ty": { "kind": "TypeName", "span": { @@ -350,7 +350,7 @@ "start": 391, "end": 402 }, - "name": "ButtonState" + "name": 18 } }, { @@ -359,7 +359,7 @@ "start": 408, "end": 425 }, - "name": "left", + "name": 26, "ty": { "kind": "TypeName", "span": { @@ -367,7 +367,7 @@ "start": 414, "end": 425 }, - "name": "ButtonState" + "name": 18 } }, { @@ -376,7 +376,7 @@ "start": 431, "end": 449 }, - "name": "right", + "name": 27, "ty": { "kind": "TypeName", "span": { @@ -384,7 +384,7 @@ "start": 438, "end": 449 }, - "name": "ButtonState" + "name": 18 } }, { @@ -393,7 +393,7 @@ "start": 455, "end": 469 }, - "name": "a", + "name": 28, "ty": { "kind": "TypeName", "span": { @@ -401,7 +401,7 @@ "start": 458, "end": 469 }, - "name": "ButtonState" + "name": 18 } }, { @@ -410,7 +410,7 @@ "start": 475, "end": 489 }, - "name": "b", + "name": 29, "ty": { "kind": "TypeName", "span": { @@ -418,7 +418,7 @@ "start": 478, "end": 489 }, - "name": "ButtonState" + "name": 18 } }, { @@ -427,7 +427,7 @@ "start": 495, "end": 509 }, - "name": "x", + "name": 30, "ty": { "kind": "TypeName", "span": { @@ -435,7 +435,7 @@ "start": 498, "end": 509 }, - "name": "ButtonState" + "name": 18 } }, { @@ -444,7 +444,7 @@ "start": 515, "end": 529 }, - "name": "y", + "name": 31, "ty": { "kind": "TypeName", "span": { @@ -452,7 +452,7 @@ "start": 518, "end": 529 }, - "name": "ButtonState" + "name": 18 } }, { @@ -461,7 +461,7 @@ "start": 535, "end": 549 }, - "name": "l", + "name": 32, "ty": { "kind": "TypeName", "span": { @@ -469,7 +469,7 @@ "start": 538, "end": 549 }, - "name": "ButtonState" + "name": 18 } }, { @@ -478,7 +478,7 @@ "start": 555, "end": 569 }, - "name": "r", + "name": 33, "ty": { "kind": "TypeName", "span": { @@ -486,7 +486,7 @@ "start": 558, "end": 569 }, - "name": "ButtonState" + "name": 18 } }, { @@ -495,7 +495,7 @@ "start": 575, "end": 593 }, - "name": "start", + "name": 34, "ty": { "kind": "TypeName", "span": { @@ -503,7 +503,7 @@ "start": 582, "end": 593 }, - "name": "ButtonState" + "name": 18 } }, { @@ -512,7 +512,7 @@ "start": 599, "end": 618 }, - "name": "select", + "name": 35, "ty": { "kind": "TypeName", "span": { @@ -520,7 +520,7 @@ "start": 607, "end": 618 }, - "name": "ButtonState" + "name": 18 } } ], @@ -537,7 +537,7 @@ }, "vis": null, "type_kind": "contract", - "name": "Gfx", + "name": 36, "is_host": true, "params": [], "constructors": [], @@ -557,7 +557,7 @@ "start": 654, "end": 682 }, - "name": "clear", + "name": 37, "params": [ { "span": { @@ -565,7 +565,7 @@ "start": 663, "end": 675 }, - "name": "color", + "name": 38, "ty": { "kind": "TypeName", "span": { @@ -573,7 +573,7 @@ "start": 670, "end": 675 }, - "name": "Color" + "name": 11 } } ], @@ -584,7 +584,7 @@ "start": 678, "end": 682 }, - "name": "void" + "name": 6 } } ] @@ -599,7 +599,7 @@ }, "vis": null, "type_kind": "contract", - "name": "Input", + "name": 39, "is_host": true, "params": [], "constructors": [], @@ -619,7 +619,7 @@ "start": 721, "end": 734 }, - "name": "pad", + "name": 40, "params": [], "ret": { "kind": "TypeName", @@ -628,7 +628,7 @@ "start": 731, "end": 734 }, - "name": "Pad" + "name": 24 } } ] @@ -642,7 +642,7 @@ "end": 788 }, "vis": null, - "name": "add", + "name": 41, "params": [ { "span": { @@ -650,7 +650,7 @@ "start": 746, "end": 752 }, - "name": "a", + "name": 28, "ty": { "kind": "TypeName", "span": { @@ -658,7 +658,7 @@ "start": 749, "end": 752 }, - "name": "int" + "name": 42 } }, { @@ -667,7 +667,7 @@ "start": 754, "end": 760 }, - "name": "b", + "name": 29, "ty": { "kind": "TypeName", "span": { @@ -675,7 +675,7 @@ "start": 757, "end": 760 }, - "name": "int" + "name": 42 } } ], @@ -686,7 +686,7 @@ "start": 763, "end": 766 }, - "name": "int" + "name": 42 }, "else_fallback": null, "body": { @@ -719,7 +719,7 @@ "start": 780, "end": 781 }, - "name": "a" + "name": 28 }, "right": { "kind": "Ident", @@ -728,7 +728,7 @@ "start": 784, "end": 785 }, - "name": "b" + "name": 29 } } } @@ -744,7 +744,7 @@ "end": 1180 }, "vis": null, - "name": "frame", + "name": 43, "params": [], "ret": { "kind": "TypeName", @@ -753,7 +753,7 @@ "start": 802, "end": 806 }, - "name": "void" + "name": 6 }, "else_fallback": null, "body": { @@ -771,7 +771,7 @@ "start": 843, "end": 854 }, - "name": "x", + "name": 30, "is_mut": false, "ty": null, "init": { @@ -791,7 +791,7 @@ "start": 859, "end": 870 }, - "name": "y", + "name": 31, "is_mut": false, "ty": null, "init": { @@ -811,7 +811,7 @@ "start": 875, "end": 893 }, - "name": "z", + "name": 44, "is_mut": false, "ty": null, "init": { @@ -828,7 +828,7 @@ "start": 883, "end": 886 }, - "name": "add" + "name": 41 }, "args": [ { @@ -838,7 +838,7 @@ "start": 887, "end": 888 }, - "name": "x" + "name": 30 }, { "kind": "Ident", @@ -847,7 +847,7 @@ "start": 890, "end": 891 }, - "name": "y" + "name": 31 } ] } @@ -881,7 +881,7 @@ "start": 930, "end": 931 }, - "name": "z" + "name": 44 }, "right": { "kind": "IntLit", @@ -929,9 +929,9 @@ "start": 976, "end": 979 }, - "name": "Gfx" + "name": 36 }, - "member": "clear" + "member": 37 }, "args": [ { @@ -948,9 +948,9 @@ "start": 986, "end": 991 }, - "name": "Color" + "name": 11 }, - "member": "GREEN" + "member": 16 } ] } @@ -994,9 +994,9 @@ "start": 1022, "end": 1025 }, - "name": "Gfx" + "name": 36 }, - "member": "clear" + "member": 37 }, "args": [ { @@ -1013,9 +1013,9 @@ "start": 1032, "end": 1037 }, - "name": "Color" + "name": 11 }, - "member": "RED" + "member": 15 } ] } @@ -1032,7 +1032,7 @@ "start": 1103, "end": 1123 }, - "name": "p", + "name": 45, "is_mut": false, "ty": null, "init": { @@ -1056,9 +1056,9 @@ "start": 1111, "end": 1116 }, - "name": "Input" + "name": 39 }, - "member": "pad" + "member": 40 }, "args": [] } @@ -1092,11 +1092,11 @@ "start": 1131, "end": 1132 }, - "name": "p" + "name": 45 }, - "member": "a" + "member": 28 }, - "member": "down" + "member": 22 }, "then_block": { "kind": "Block", @@ -1134,9 +1134,9 @@ "start": 1150, "end": 1153 }, - "name": "Gfx" + "name": 36 }, - "member": "clear" + "member": 37 }, "args": [ { @@ -1153,9 +1153,9 @@ "start": 1160, "end": 1165 }, - "name": "Color" + "name": 11 }, - "member": "BLUE" + "member": 17 } ] }