pr 59.1
This commit is contained in:
parent
66a77709f0
commit
f5dafc403f
@ -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");
|
||||
|
||||
|
||||
@ -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(),
|
||||
|
||||
@ -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(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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)?;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,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
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
|
||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user