This commit is contained in:
Nilton Constantino 2026-02-02 19:20:21 +00:00
parent 66a77709f0
commit f5dafc403f
No known key found for this signature in database
11 changed files with 137 additions and 68 deletions

View File

@ -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<EmitResult> {
let fragments = emit_fragments(module, file_manager)?;
pub fn emit_module(module: &ir_vm::Module) -> Result<EmitResult> {
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<EmitFragments> {
let mut emitter = BytecodeEmitter::new(file_manager);
pub fn emit_fragments(module: &ir_vm::Module) -> Result<EmitFragments> {
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<ConstantPoolEntry>,
/// 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");

View File

@ -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(),

View File

@ -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<u32>,
// Add other metadata if needed later (e.g. type info)
pub is_host: bool,
pub ty: Option<PbsType>,
}
#[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(),
});
}
}
}

View File

@ -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;

View File

@ -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)?;

View File

@ -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

View File

@ -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<HashMap<String, PbsType>>,
mut_bindings: Vec<HashMap<String, bool>>,
@ -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,8 +216,15 @@ 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 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
if let Some(sym) = self.module_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) {
@ -232,6 +242,16 @@ impl<'a> TypeChecker<'a> {
}
return PbsType::Void;
}
// 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());
}
_ => {}
}
}
}
// Builtin Struct Associated Members (Static/Constants)
@ -255,6 +275,7 @@ impl<'a> TypeChecker<'a> {
}
}
}
}
let obj_ty = self.check_node(&n.object);
if let PbsType::Struct(ref name) = obj_ty {
@ -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
}

View File

@ -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,

View File

@ -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

View File

@ -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();