diff --git a/crates/prometeu-compiler/src/backend/emit_bytecode.rs b/crates/prometeu-compiler/src/backend/emit_bytecode.rs index 8c16f4e5..d5b37656 100644 --- a/crates/prometeu-compiler/src/backend/emit_bytecode.rs +++ b/crates/prometeu-compiler/src/backend/emit_bytecode.rs @@ -7,7 +7,6 @@ //! 1. **Instruction Lowering**: Translates `ir_vm::Instruction` into `prometeu_bytecode::asm::Asm` ops. //! 2. **Symbol Mapping**: Associates bytecode offsets (Program Counter) with source code locations. -use crate::common::files::FileManager; use crate::common::symbols::Symbol; use crate::ir_core::ConstantValue; use crate::ir_vm; @@ -35,8 +34,8 @@ pub struct EmitFragments { } /// Entry point for emitting a bytecode module from the IR. -pub fn emit_module(module: &ir_vm::Module, file_manager: &FileManager) -> Result { - let fragments = emit_fragments(module, file_manager)?; +pub fn emit_module(module: &ir_vm::Module) -> Result { + let fragments = emit_fragments(module)?; let exports: Vec<_> = module.functions.iter().enumerate().map(|(i, f)| { prometeu_bytecode::v0::Export { @@ -61,8 +60,8 @@ pub fn emit_module(module: &ir_vm::Module, file_manager: &FileManager) -> Result }) } -pub fn emit_fragments(module: &ir_vm::Module, file_manager: &FileManager) -> Result { - let mut emitter = BytecodeEmitter::new(file_manager); +pub fn emit_fragments(module: &ir_vm::Module) -> Result { + let mut emitter = BytecodeEmitter::new(); let mut mapped_const_ids = Vec::with_capacity(module.const_pool.constants.len()); for val in &module.const_pool.constants { @@ -124,19 +123,16 @@ pub fn emit_fragments(module: &ir_vm::Module, file_manager: &FileManager) -> Res } /// Internal helper for managing the bytecode emission state. -struct BytecodeEmitter<'a> { +struct BytecodeEmitter { /// Stores constant values (like strings) that are referenced by instructions. constant_pool: Vec, - /// Used to look up source code positions for symbol generation. - file_manager: &'a FileManager, } -impl<'a> BytecodeEmitter<'a> { - fn new(file_manager: &'a FileManager) -> Self { +impl BytecodeEmitter { + fn new() -> Self { Self { // Index 0 is traditionally reserved for Null in many VMs constant_pool: vec![ConstantPoolEntry::Null], - file_manager, } } @@ -306,7 +302,6 @@ impl<'a> BytecodeEmitter<'a> { #[cfg(test)] mod tests { use super::*; - use crate::common::files::FileManager; use crate::ir_core::const_pool::ConstantValue; use crate::ir_core::ids::FunctionId; use crate::ir_vm::instr::{InstrKind, Instruction}; @@ -338,8 +333,7 @@ mod tests { module.functions.push(function); - let file_manager = FileManager::new(); - let result = emit_module(&module, &file_manager).expect("Failed to emit module"); + let result = emit_module(&module).expect("Failed to emit module"); let pbc = BytecodeLoader::load(&result.rom).expect("Failed to parse emitted PBC"); diff --git a/crates/prometeu-compiler/src/building/linker.rs b/crates/prometeu-compiler/src/building/linker.rs index b4d6c872..39fe039d 100644 --- a/crates/prometeu-compiler/src/building/linker.rs +++ b/crates/prometeu-compiler/src/building/linker.rs @@ -1,9 +1,9 @@ -use crate::building::output::{CompiledModule, ExportKey, ImportKey}; +use crate::building::output::{CompiledModule}; use crate::building::plan::BuildStep; use prometeu_bytecode::opcode::OpCode; -use prometeu_bytecode::v0::{ConstantPoolEntry, DebugInfo, FunctionMeta}; +use prometeu_bytecode::v0::{ConstantPoolEntry, DebugInfo}; use prometeu_core::virtual_machine::{ProgramImage, Value}; -use std::collections::{BTreeMap, HashMap}; +use std::collections::{HashMap}; #[derive(Debug, PartialEq, Eq, Clone)] pub enum LinkError { @@ -241,6 +241,14 @@ impl Linker { } } } + // v0: Fallback export for entrypoint `src/main/modules:frame` (root module) + if !final_exports.contains_key("src/main/modules:frame") { + if let Some(&root_offset) = module_function_offsets.last() { + if let Some((idx, _)) = combined_function_names.iter().find(|(i, name)| *i >= root_offset && name == "frame") { + final_exports.insert("src/main/modules:frame".to_string(), *idx); + } + } + } let combined_debug_info = if combined_pc_to_span.is_empty() && combined_function_names.is_empty() { None @@ -263,12 +271,14 @@ impl Linker { #[cfg(test)] mod tests { + use std::collections::BTreeMap; use super::*; use crate::building::output::{ExportKey, ExportMetadata, ImportKey, ImportMetadata}; use crate::building::plan::BuildTarget; use crate::deps::resolver::ProjectId; use crate::frontends::pbs::symbols::SymbolKind; use prometeu_bytecode::opcode::OpCode; + use prometeu_bytecode::v0::FunctionMeta; #[test] fn test_link_root_and_lib() { @@ -285,7 +295,7 @@ mod tests { module_path: "math".into(), symbol_name: "add".into(), kind: SymbolKind::Function, - }, ExportMetadata { func_idx: Some(0) }); + }, ExportMetadata { func_idx: Some(0), is_host: false, ty: None }); let lib_module = CompiledModule { project_id: lib_id.clone(), diff --git a/crates/prometeu-compiler/src/building/output.rs b/crates/prometeu-compiler/src/building/output.rs index 5c64049b..c33f5852 100644 --- a/crates/prometeu-compiler/src/building/output.rs +++ b/crates/prometeu-compiler/src/building/output.rs @@ -11,6 +11,7 @@ use crate::frontends::pbs::parser::Parser; use crate::frontends::pbs::resolver::{ModuleProvider, Resolver}; use crate::frontends::pbs::symbols::{ModuleSymbols, Namespace, Symbol, SymbolKind, Visibility}; use crate::frontends::pbs::typecheck::TypeChecker; +use crate::frontends::pbs::types::PbsType; use crate::lowering::core_to_vm; use prometeu_bytecode::v0::{ConstantPoolEntry, DebugInfo, FunctionMeta}; use serde::{Deserialize, Serialize}; @@ -27,7 +28,8 @@ pub struct ExportKey { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ExportMetadata { pub func_idx: Option, - // Add other metadata if needed later (e.g. type info) + pub is_host: bool, + pub ty: Option, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] @@ -150,7 +152,7 @@ pub fn compile_project( let mut all_visible_modules = module_symbols_map.clone(); for (alias, project_id) in &step.deps { if let Some(compiled) = dep_modules.get(project_id) { - for (key, _) in &compiled.exports { + for (key, meta) in &compiled.exports { // Support syntax: "alias/module" and "@alias:module" let key_module_path = key.module_path.replace("src/main/modules/", ""); let synthetic_paths = [ @@ -170,8 +172,8 @@ pub fn compile_project( _ => Namespace::Value, }, visibility: Visibility::Pub, - ty: None, - is_host: false, + ty: meta.ty.clone(), + is_host: meta.is_host, span: Span::new(0, 0, 0), origin: Some(synthetic_module_path.clone()), }; @@ -204,7 +206,8 @@ pub fn compile_project( // TypeChecker also needs &mut ModuleSymbols let mut ms_mut = module_symbols_map.get_mut(module_path).unwrap(); - let mut typechecker = TypeChecker::new(&mut ms_mut, &module_provider); + let imported = file_imported_symbols.get(module_path).unwrap(); + let mut typechecker = TypeChecker::new(&mut ms_mut, imported, &module_provider); typechecker.check(ast)?; } @@ -234,7 +237,7 @@ pub fn compile_project( let vm_module = core_to_vm::lower_program(&combined_program) .map_err(|e| CompileError::Internal(format!("Lowering error: {}", e)))?; - let fragments = emit_fragments(&vm_module, &file_manager) + let fragments = emit_fragments(&vm_module) .map_err(|e| CompileError::Internal(format!("Emission error: {}", e)))?; // 5. Collect exports @@ -246,7 +249,11 @@ pub fn compile_project( module_path: module_path.clone(), symbol_name: sym.name.clone(), kind: sym.kind.clone(), - }, ExportMetadata { func_idx: None }); + }, ExportMetadata { + func_idx: None, + is_host: sym.is_host, + ty: sym.ty.clone(), + }); } } for sym in ms.value_symbols.symbols.values() { @@ -258,7 +265,11 @@ pub fn compile_project( module_path: module_path.clone(), symbol_name: sym.name.clone(), kind: sym.kind.clone(), - }, ExportMetadata { func_idx }); + }, ExportMetadata { + func_idx, + is_host: sym.is_host, + ty: sym.ty.clone(), + }); } } } diff --git a/crates/prometeu-compiler/src/compiler.rs b/crates/prometeu-compiler/src/compiler.rs index 5e35f5c7..9d16b472 100644 --- a/crates/prometeu-compiler/src/compiler.rs +++ b/crates/prometeu-compiler/src/compiler.rs @@ -326,8 +326,7 @@ mod tests { assert!(kinds.contains(&&ir_vm::InstrKind::GateRelease), "Must contain GateRelease (on cleanup or Pop)"); // --- 4. EMIT BYTECODE --- - let file_manager = crate::common::files::FileManager::new(); - let emit_result = backend::emit_module(&vm_module, &file_manager).expect("Emission failed"); + let emit_result = backend::emit_module(&vm_module).expect("Emission failed"); let rom = emit_result.rom; diff --git a/crates/prometeu-compiler/src/frontends/pbs/lowering.rs b/crates/prometeu-compiler/src/frontends/pbs/lowering.rs index ab8ac1fd..a1638a33 100644 --- a/crates/prometeu-compiler/src/frontends/pbs/lowering.rs +++ b/crates/prometeu-compiler/src/frontends/pbs/lowering.rs @@ -722,6 +722,31 @@ 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(); + + 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)); + 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) { + let id = method.id; + let return_slots = if matches!(method.return_type, PbsType::Void) { 0 } else { 1 }; + self.emit(Instr::HostCall(id, return_slots)); + return Ok(()); + } + } + } + } + } + // Check for .raw() if ma.member == "raw" { self.lower_node(&ma.object)?; diff --git a/crates/prometeu-compiler/src/frontends/pbs/mod.rs b/crates/prometeu-compiler/src/frontends/pbs/mod.rs index b8c9c7b4..5c846e90 100644 --- a/crates/prometeu-compiler/src/frontends/pbs/mod.rs +++ b/crates/prometeu-compiler/src/frontends/pbs/mod.rs @@ -58,7 +58,7 @@ impl Frontend for PbsFrontend { resolver.resolve(&ast)?; let imported_symbols = resolver.imported_symbols; - let mut typechecker = TypeChecker::new(&mut module_symbols, &EmptyProvider); + let mut typechecker = TypeChecker::new(&mut module_symbols, &imported_symbols, &EmptyProvider); typechecker.check(&ast)?; // Lower to Core IR diff --git a/crates/prometeu-compiler/src/frontends/pbs/typecheck.rs b/crates/prometeu-compiler/src/frontends/pbs/typecheck.rs index 228c4200..c7e75dc1 100644 --- a/crates/prometeu-compiler/src/frontends/pbs/typecheck.rs +++ b/crates/prometeu-compiler/src/frontends/pbs/typecheck.rs @@ -9,6 +9,7 @@ use std::collections::HashMap; pub struct TypeChecker<'a> { module_symbols: &'a mut ModuleSymbols, + imported_symbols: &'a ModuleSymbols, _module_provider: &'a dyn ModuleProvider, scopes: Vec>, mut_bindings: Vec>, @@ -23,10 +24,12 @@ pub struct TypeChecker<'a> { impl<'a> TypeChecker<'a> { pub fn new( module_symbols: &'a mut ModuleSymbols, + imported_symbols: &'a ModuleSymbols, module_provider: &'a dyn ModuleProvider, ) -> Self { Self { module_symbols, + imported_symbols, _module_provider: module_provider, scopes: Vec::new(), mut_bindings: Vec::new(), @@ -213,45 +216,63 @@ impl<'a> TypeChecker<'a> { fn check_member_access(&mut self, n: &MemberAccessNode) -> PbsType { if let Node::Ident(id) = &*n.object { - // Check if it's a known host contract - if let Some(sym) = self.module_symbols.type_symbols.get(&id.name) { - 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) { - return PbsType::Function { - params: method.params.clone(), - return_type: Box::new(method.return_type.clone()), - }; - } else { - 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), - span: Some(n.span), - }); + // Check if it's a local first + let is_local = self.scopes.iter().any(|s| s.contains_key(&id.name)); + + 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)); + + 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) { + return PbsType::Function { + params: method.params.clone(), + return_type: Box::new(method.return_type.clone()), + }; + } else { + 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), + span: Some(n.span), + }); + } + return PbsType::Void; } - return PbsType::Void; - } - } - // Builtin Struct Associated Members (Static/Constants) - if let Some(constants) = self.struct_constants.get(&id.name) { - if let Some(ty) = constants.get(&n.member) { - return ty.clone(); + // v0: Suporte explícito às constantes de Color + if sym.kind == SymbolKind::Struct && id.name == "Color" { + match n.member.as_str() { + "BLACK" | "WHITE" | "RED" | "GREEN" | "BLUE" => { + return PbsType::Struct("Color".to_string()); + } + _ => {} + } + } } - } - // 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) { - return ty.clone(); + // Builtin Struct Associated Members (Static/Constants) + if let Some(constants) = self.struct_constants.get(&id.name) { + if let Some(ty) = constants.get(&n.member) { + 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) { - 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) { + 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) { + return ty.clone(); + } } } } @@ -449,6 +470,13 @@ impl<'a> TypeChecker<'a> { } } + // Check imported symbols + 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) { @@ -746,6 +774,9 @@ impl<'a> TypeChecker<'a> { if let Some(sym) = self.module_symbols.type_symbols.get(name) { return Some(sym); } + if let Some(sym) = self.imported_symbols.type_symbols.get(name) { + return Some(sym); + } None } diff --git a/crates/prometeu-compiler/src/frontends/pbs/types.rs b/crates/prometeu-compiler/src/frontends/pbs/types.rs index adbd931c..8fa7178c 100644 --- a/crates/prometeu-compiler/src/frontends/pbs/types.rs +++ b/crates/prometeu-compiler/src/frontends/pbs/types.rs @@ -1,6 +1,7 @@ +use serde::{Deserialize, Serialize}; use std::fmt; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum PbsType { Int, Float, diff --git a/crates/prometeu-compiler/tests/hip_conformance.rs b/crates/prometeu-compiler/tests/hip_conformance.rs index 5966a100..ef2c786e 100644 --- a/crates/prometeu-compiler/tests/hip_conformance.rs +++ b/crates/prometeu-compiler/tests/hip_conformance.rs @@ -1,5 +1,4 @@ use prometeu_compiler::backend::emit_bytecode::emit_module; -use prometeu_compiler::common::files::FileManager; use prometeu_compiler::ir_core::ids::{ConstId as CoreConstId, FieldId, FunctionId, TypeId as CoreTypeId, ValueId}; use prometeu_compiler::ir_core::{self, Block, ConstPool, ConstantValue, Instr, Program, Terminator}; use prometeu_compiler::ir_vm::InstrKind; @@ -83,8 +82,7 @@ fn test_hip_conformance_core_to_vm_to_bytecode() { assert!(kinds.contains(&&InstrKind::GateRelease), "Missing GateRelease"); // 3. Emit Bytecode - let file_manager = FileManager::new(); - let emit_result = emit_module(&vm_module, &file_manager).expect("Emission failed"); + let emit_result = emit_module(&vm_module).expect("Emission failed"); let bytecode = emit_result.rom; // 4. Assert industrial PBS\0 format diff --git a/crates/prometeu-core/tests/heartbeat.rs b/crates/prometeu-core/tests/heartbeat.rs index 2e070056..3e9f24fb 100644 --- a/crates/prometeu-core/tests/heartbeat.rs +++ b/crates/prometeu-core/tests/heartbeat.rs @@ -39,8 +39,8 @@ fn test_canonical_cartridge_heartbeat() { let pbc_bytes = fs::read(pbc_path).expect("Failed to read canonical PBC. Did you run the generation test?"); let mut vm = VirtualMachine::new(vec![], vec![]); - vm.initialize(pbc_bytes, "frame").expect("Failed to initialize VM with canonical cartridge"); - vm.prepare_call("frame"); + vm.initialize(pbc_bytes, "src/main/modules:frame").expect("Failed to initialize VM with canonical cartridge"); + vm.prepare_call("src/main/modules:frame"); let mut native = MockNative; let mut hw = Hardware::new(); diff --git a/test-cartridges/canonical/golden/program.pbc b/test-cartridges/canonical/golden/program.pbc index 63d6cc54..807e2d8b 100644 Binary files a/test-cartridges/canonical/golden/program.pbc and b/test-cartridges/canonical/golden/program.pbc differ