dev/pbs #8
@ -41,7 +41,7 @@ pub fn disasm(rom: &[u8]) -> Result<Vec<Instr>, String> {
|
||||
match opcode {
|
||||
OpCode::PushConst | OpCode::PushI32 | OpCode::Jmp | OpCode::JmpIfFalse | OpCode::JmpIfTrue
|
||||
| OpCode::GetGlobal | OpCode::SetGlobal | OpCode::GetLocal | OpCode::SetLocal
|
||||
| OpCode::PopN | OpCode::Syscall => {
|
||||
| OpCode::PopN | OpCode::Syscall | OpCode::LoadRef | OpCode::StoreRef => {
|
||||
let v = read_u32_le(&mut cursor).map_err(|e| e.to_string())?;
|
||||
operands.push(DisasmOperand::U32(v));
|
||||
}
|
||||
|
||||
@ -54,7 +54,7 @@ pub enum OpCode {
|
||||
/// Operand: value (i32)
|
||||
PushI32 = 0x17,
|
||||
/// Removes `n` values from the stack.
|
||||
/// Operand: n (u16)
|
||||
/// Operand: n (u32)
|
||||
PopN = 0x18,
|
||||
|
||||
// --- 6.3 Arithmetic ---
|
||||
|
||||
@ -161,6 +161,13 @@ impl<'a> BytecodeEmitter<'a> {
|
||||
asm_instrs.push(Asm::Op(OpCode::Syscall, vec![Operand::U32(*id)]));
|
||||
}
|
||||
InstrKind::FrameSync => asm_instrs.push(Asm::Op(OpCode::FrameSync, vec![])),
|
||||
InstrKind::Alloc => asm_instrs.push(Asm::Op(OpCode::Alloc, vec![])),
|
||||
InstrKind::LoadRef(offset) => {
|
||||
asm_instrs.push(Asm::Op(OpCode::LoadRef, vec![Operand::U32(*offset)]));
|
||||
}
|
||||
InstrKind::StoreRef(offset) => {
|
||||
asm_instrs.push(Asm::Op(OpCode::StoreRef, vec![Operand::U32(*offset)]));
|
||||
}
|
||||
}
|
||||
|
||||
let end_idx = asm_instrs.len();
|
||||
|
||||
@ -179,8 +179,15 @@ impl<'a> TypeChecker<'a> {
|
||||
// In v0, we assume target is a gate. We bind the inner type to the binding.
|
||||
let inner_ty = match target_ty {
|
||||
PbsType::Contract(name) if name.starts_with("Gate<") => {
|
||||
// Try to extract type name from Gate<TypeName>
|
||||
PbsType::Void // Simplified
|
||||
// Extract type name from Gate<TypeName>
|
||||
let inner_name = &name[5..name.len()-1];
|
||||
match inner_name {
|
||||
"int" => PbsType::Int,
|
||||
"float" => PbsType::Float,
|
||||
"bool" => PbsType::Bool,
|
||||
"string" => PbsType::String,
|
||||
_ => PbsType::Void, // Should be PbsType::Struct(inner_name) if we had better info
|
||||
}
|
||||
}
|
||||
_ => PbsType::Void
|
||||
};
|
||||
|
||||
@ -136,4 +136,13 @@ pub enum InstrKind {
|
||||
Syscall(u32),
|
||||
/// Special instruction to synchronize with the hardware frame clock.
|
||||
FrameSync,
|
||||
|
||||
// --- HIP / Memory ---
|
||||
|
||||
/// Allocates memory on the heap. Pops size from stack.
|
||||
Alloc,
|
||||
/// Reads from heap at reference + offset. Pops reference, pushes value.
|
||||
LoadRef(u32),
|
||||
/// Writes to heap at reference + offset. Pops reference and value.
|
||||
StoreRef(u32),
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
//! # Prometeu Compiler
|
||||
//!
|
||||
//! This crate provides the official compiler for the Prometeu ecosystem.
|
||||
//! It translates high-level source code (primarily TypeScript/JavaScript) into
|
||||
//! It translates high-level source code (primarily Prometeu Base Script - PBS) into
|
||||
//! Prometeu ByteCode (.pbc), which runs on the Prometeu Virtual Machine.
|
||||
//!
|
||||
//! ## Architecture Overview:
|
||||
@ -9,8 +9,8 @@
|
||||
//! The compiler follows a multi-stage pipeline:
|
||||
//!
|
||||
//! 1. **Frontend (Parsing & Analysis)**:
|
||||
//! - Uses the `oxc` parser to generate an Abstract Syntax Tree (AST).
|
||||
//! - Performs semantic analysis and validation (e.g., ensuring only supported TS features are used).
|
||||
//! - Uses the PBS parser to generate an Abstract Syntax Tree (AST).
|
||||
//! - Performs semantic analysis and validation.
|
||||
//! - Lowers the AST into the **Intermediate Representation (IR)**.
|
||||
//! - *Example*: Converting a `a + b` expression into IR instructions like `Push(a)`, `Push(b)`, `Add`.
|
||||
//!
|
||||
@ -30,7 +30,7 @@
|
||||
//!
|
||||
//! ```bash
|
||||
//! # Build a project from a directory
|
||||
//! prometeu-compiler build ./my-game --entry ./src/main.ts --out ./game.pbc
|
||||
//! prometeu-compiler build ./my-game --entry ./src/main.pbs --out ./game.pbc
|
||||
//! ```
|
||||
//!
|
||||
//! ## Programmatic Entry Point:
|
||||
@ -67,7 +67,7 @@ pub enum Commands {
|
||||
/// Path to the project root directory.
|
||||
project_dir: PathBuf,
|
||||
|
||||
/// Explicit path to the entry file (defaults to src/main.ts).
|
||||
/// Explicit path to the entry file (defaults to src/main.pbs).
|
||||
#[arg(short, long)]
|
||||
entry: Option<PathBuf>,
|
||||
|
||||
|
||||
@ -71,11 +71,10 @@ pub fn lower_function(core_func: &ir_core::Function) -> Result<ir::Function> {
|
||||
ir_core::Instr::And => ir::InstrKind::And,
|
||||
ir_core::Instr::Or => ir::InstrKind::Or,
|
||||
ir_core::Instr::Not => ir::InstrKind::Not,
|
||||
ir_core::Instr::Alloc | ir_core::Instr::Free | ir_core::Instr::ReadGate | ir_core::Instr::WriteGate => {
|
||||
// HIP effects are not yet supported in VM IR, so we emit Nop or similar.
|
||||
// For now, let's use Nop.
|
||||
ir::InstrKind::Nop
|
||||
}
|
||||
ir_core::Instr::Alloc => ir::InstrKind::Alloc,
|
||||
ir_core::Instr::ReadGate => ir::InstrKind::LoadRef(0),
|
||||
ir_core::Instr::WriteGate => ir::InstrKind::StoreRef(0),
|
||||
ir_core::Instr::Free => ir::InstrKind::Nop,
|
||||
};
|
||||
vm_func.body.push(ir::Instruction::new(kind, None));
|
||||
}
|
||||
|
||||
50
crates/prometeu-compiler/tests/pbs_end_to_end_tests.rs
Normal file
50
crates/prometeu-compiler/tests/pbs_end_to_end_tests.rs
Normal file
@ -0,0 +1,50 @@
|
||||
use prometeu_compiler::compiler;
|
||||
use prometeu_bytecode::pbc::parse_pbc;
|
||||
use prometeu_bytecode::disasm::disasm;
|
||||
use prometeu_bytecode::opcode::OpCode;
|
||||
use std::fs;
|
||||
use tempfile::tempdir;
|
||||
|
||||
#[test]
|
||||
fn test_compile_hip_program() {
|
||||
let dir = tempdir().unwrap();
|
||||
let project_dir = dir.path();
|
||||
|
||||
// Create prometeu.json
|
||||
fs::write(
|
||||
project_dir.join("prometeu.json"),
|
||||
r#"{
|
||||
"script_fe": "pbs",
|
||||
"entry": "main.pbs"
|
||||
}"#,
|
||||
).unwrap();
|
||||
|
||||
// Create main.pbs with HIP effects
|
||||
let code = "
|
||||
fn main() {
|
||||
let x = alloc int;
|
||||
mutate x as v {
|
||||
let y = v + 1;
|
||||
}
|
||||
}
|
||||
";
|
||||
fs::write(project_dir.join("main.pbs"), code).unwrap();
|
||||
|
||||
// Compile
|
||||
let unit = compiler::compile(project_dir).expect("Failed to compile");
|
||||
|
||||
// Parse PBC
|
||||
let pbc = parse_pbc(&unit.rom).expect("Failed to parse PBC");
|
||||
|
||||
// Disassemble
|
||||
let instrs = disasm(&pbc.rom).expect("Failed to disassemble");
|
||||
|
||||
// Verify opcodes exist in bytecode
|
||||
let opcodes: Vec<_> = instrs.iter().map(|i| i.opcode).collect();
|
||||
|
||||
assert!(opcodes.contains(&OpCode::Alloc));
|
||||
assert!(opcodes.contains(&OpCode::LoadRef)); // From ReadGate
|
||||
assert!(opcodes.contains(&OpCode::StoreRef)); // From WriteGate
|
||||
assert!(opcodes.contains(&OpCode::Add));
|
||||
assert!(opcodes.contains(&OpCode::Ret));
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user