dev/prometeuc-improvements #5

Merged
bquarkz merged 11 commits from dev/prometeuc-improvements into master 2026-01-21 10:55:48 +00:00
10 changed files with 325 additions and 70 deletions
Showing only changes of commit 0496afc192 - Show all commits

17
build/program.disasm.txt Normal file
View File

@ -0,0 +1,17 @@
00000000 Call U32(18) U32(0)
0000000A FrameSync
0000000C Jmp U32(0)
00000012 PushI32 U32(1) ; test_supported.ts:2
00000018 SetLocal U32(0) ; test_supported.ts:2
0000001E GetLocal U32(0) ; test_supported.ts:3
00000024 PushI32 U32(10) ; test_supported.ts:3
0000002A Lt ; test_supported.ts:3
0000002C JmpIfFalse U32(80) ; test_supported.ts:3
00000032 GetLocal U32(0) ; test_supported.ts:4
00000038 PushI32 U32(1) ; test_supported.ts:4
0000003E Add ; test_supported.ts:4
00000040 Dup ; test_supported.ts:4
00000042 SetLocal U32(0) ; test_supported.ts:4
00000048 Pop ; test_supported.ts:4
0000004A Jmp U32(80)
00000050 Ret

BIN
build/program.pbc Normal file

Binary file not shown.

74
build/symbols.json Normal file
View File

@ -0,0 +1,74 @@
[
{
"pc": 18,
"file": "test_supported.ts",
"line": 2,
"col": 13
},
{
"pc": 24,
"file": "test_supported.ts",
"line": 2,
"col": 9
},
{
"pc": 30,
"file": "test_supported.ts",
"line": 3,
"col": 9
},
{
"pc": 36,
"file": "test_supported.ts",
"line": 3,
"col": 13
},
{
"pc": 42,
"file": "test_supported.ts",
"line": 3,
"col": 9
},
{
"pc": 44,
"file": "test_supported.ts",
"line": 3,
"col": 5
},
{
"pc": 50,
"file": "test_supported.ts",
"line": 4,
"col": 13
},
{
"pc": 56,
"file": "test_supported.ts",
"line": 4,
"col": 17
},
{
"pc": 62,
"file": "test_supported.ts",
"line": 4,
"col": 13
},
{
"pc": 64,
"file": "test_supported.ts",
"line": 4,
"col": 9
},
{
"pc": 66,
"file": "test_supported.ts",
"line": 4,
"col": 9
},
{
"pc": 72,
"file": "test_supported.ts",
"line": 4,
"col": 9
}
]

View File

@ -18,6 +18,18 @@ pub enum Asm {
Label(String),
}
pub fn update_pc_by_operand(initial_pc: u32, operands: &Vec<Operand>) -> u32 {
let mut pcp: u32 = initial_pc;
for operand in operands {
match operand {
Operand::U32(_) | Operand::I32(_) | Operand::Label(_) => pcp += 4,
Operand::I64(_) | Operand::F64(_) => pcp += 8,
Operand::Bool(_) => pcp += 1,
}
}
pcp
}
pub fn assemble(instructions: &[Asm]) -> Result<Vec<u8>, String> {
let mut labels = HashMap::new();
let mut current_pc = 0u32;
@ -30,13 +42,7 @@ pub fn assemble(instructions: &[Asm]) -> Result<Vec<u8>, String> {
}
Asm::Op(_opcode, operands) => {
current_pc += 2; // OpCode is u16 (2 bytes)
for operand in operands {
match operand {
Operand::U32(_) | Operand::I32(_) | Operand::Label(_) => current_pc += 4,
Operand::I64(_) | Operand::F64(_) => current_pc += 8,
Operand::Bool(_) => current_pc += 1,
}
}
current_pc = update_pc_by_operand(current_pc, operands);
}
}
}

View File

@ -0,0 +1,22 @@
use oxc_ast::ast::*;
use anyhow::{Result, anyhow};
pub fn get_callee_name(expr: &Expression) -> Result<String> {
match expr {
Expression::Identifier(ident) => Ok(ident.name.to_string()),
Expression::StaticMemberExpression(member) => {
let obj = get_callee_name_from_member_obj(&member.object)?;
let prop = member.property.name.to_string();
Ok(format!("{}.{}", obj, prop))
}
_ => Err(anyhow!("Unsupported callee expression")),
}
}
pub fn get_callee_name_from_member_obj(expr: &Expression) -> Result<String> {
if let Expression::Identifier(ident) = expr {
Ok(ident.name.to_string())
} else {
Err(anyhow!("Unsupported member object expression"))
}
}

View File

@ -5,7 +5,9 @@ use prometeu_bytecode::opcode::OpCode;
use prometeu_bytecode::asm::{Asm, Operand, assemble};
use crate::compiler::Symbol;
use crate::syscall_map;
use crate::codegen::ast_util;
use std::collections::HashMap;
use prometeu_bytecode::asm;
pub struct Codegen {
file_name: String,
@ -203,7 +205,7 @@ impl Codegen {
self.emit_op(op, vec![], unary.span);
}
Expression::CallExpression(call) => {
let name = self.get_callee_name(&call.callee)?;
let name = ast_util::get_callee_name(&call.callee)?;
if let Some(syscall_id) = syscall_map::map_syscall(&name) {
if name == "input.btnA" {
self.emit_op(OpCode::PushI32, vec![Operand::I32(syscall_map::BTN_A as i32)], call.span);
@ -222,7 +224,7 @@ impl Codegen {
}
}
Expression::StaticMemberExpression(member) => {
let obj = self.get_callee_name_from_member_obj(&member.object)?;
let obj = ast_util::get_callee_name_from_member_obj(&member.object)?;
let prop = member.property.name.to_string();
let full_name = format!("{}.{}", obj, prop);
// If it's used as a value (GetGlobal/GetLocal?), but for now we only support it in calls
@ -233,26 +235,6 @@ impl Codegen {
Ok(())
}
fn get_callee_name(&self, expr: &Expression) -> Result<String> {
match expr {
Expression::Identifier(ident) => Ok(ident.name.to_string()),
Expression::StaticMemberExpression(member) => {
let obj = self.get_callee_name_from_member_obj(&member.object)?;
let prop = member.property.name.to_string();
Ok(format!("{}.{}", obj, prop))
}
_ => Err(anyhow!("Unsupported callee expression at {:?}", expr.span())),
}
}
fn get_callee_name_from_member_obj(&self, expr: &Expression) -> Result<String> {
if let Expression::Identifier(ident) = expr {
Ok(ident.name.to_string())
} else {
Err(anyhow!("Unsupported member object expression"))
}
}
fn new_label(&mut self, prefix: &str) -> String {
let label = format!("{}_{}", prefix, self.label_count);
self.label_count += 1;
@ -310,13 +292,7 @@ impl Codegen {
}
current_pc += 2;
for operand in operands {
match operand {
Operand::U32(_) | Operand::I32(_) | Operand::Label(_) => current_pc += 4,
Operand::I64(_) | Operand::F64(_) => current_pc += 8,
Operand::Bool(_) => current_pc += 1,
}
}
current_pc = asm::update_pc_by_operand(current_pc, operands);
}
}
}

View File

@ -1,3 +1,5 @@
pub mod codegen;
pub mod validator;
pub mod ast_util;
pub use codegen::Codegen;

View File

@ -0,0 +1,140 @@
use oxc_ast::ast::*;
use oxc_ast::visit::walk;
use oxc_ast::Visit;
use oxc_span::GetSpan;
use anyhow::{Result, anyhow};
use crate::syscall_map;
use crate::codegen::ast_util;
pub struct Validator {
errors: Vec<String>,
}
impl Validator {
pub fn validate(program: &Program) -> Result<()> {
let mut validator = Self { errors: Vec::new() };
// 1. Check for exported tick function
let mut tick_fn_found = false;
for item in &program.body {
if let Statement::ExportNamedDeclaration(decl) = item {
if let Some(Declaration::FunctionDeclaration(f)) = &decl.declaration {
if let Some(ident) = &f.id {
if ident.name == "tick" {
tick_fn_found = true;
break;
}
}
}
}
}
if !tick_fn_found {
validator.errors.push("export function tick() not found".to_string());
}
// 2. Recursive validation of the AST
validator.visit_program(program);
if validator.errors.is_empty() {
Ok(())
} else {
Err(anyhow!("Validation errors:\n{}", validator.errors.join("\n")))
}
}
}
impl<'a> Visit<'a> for Validator {
fn visit_statement(&mut self, stmt: &Statement<'a>) {
match stmt {
Statement::VariableDeclaration(_) |
Statement::ExpressionStatement(_) |
Statement::IfStatement(_) |
Statement::BlockStatement(_) |
Statement::ExportNamedDeclaration(_) |
Statement::FunctionDeclaration(_) => {
// Supported
walk::walk_statement(self, stmt);
}
_ => {
self.errors.push(format!("Unsupported statement type at {:?}", stmt.span()));
}
}
}
fn visit_expression(&mut self, expr: &Expression<'a>) {
match expr {
Expression::NumericLiteral(_) |
Expression::BooleanLiteral(_) |
Expression::Identifier(_) |
Expression::AssignmentExpression(_) |
Expression::BinaryExpression(_) |
Expression::LogicalExpression(_) |
Expression::UnaryExpression(_) |
Expression::CallExpression(_) |
Expression::StaticMemberExpression(_) => {
// Base types supported, detailed checks in specific visit methods if needed
walk::walk_expression(self, expr);
}
_ => {
self.errors.push(format!("Unsupported expression type at {:?}", expr.span()));
}
}
}
fn visit_unary_expression(&mut self, expr: &UnaryExpression<'a>) {
match expr.operator {
UnaryOperator::UnaryNegation |
UnaryOperator::UnaryPlus |
UnaryOperator::LogicalNot => {
walk::walk_unary_expression(self, expr);
}
_ => {
self.errors.push(format!("Unsupported unary operator {:?} at {:?}", expr.operator, expr.span));
}
}
}
fn visit_binary_expression(&mut self, expr: &BinaryExpression<'a>) {
match expr.operator {
BinaryOperator::Addition |
BinaryOperator::Subtraction |
BinaryOperator::Multiplication |
BinaryOperator::Division |
BinaryOperator::Equality |
BinaryOperator::Inequality |
BinaryOperator::LessThan |
BinaryOperator::GreaterThan |
BinaryOperator::LessEqualThan |
BinaryOperator::GreaterEqualThan => {
walk::walk_binary_expression(self, expr);
}
_ => {
self.errors.push(format!("Unsupported binary operator {:?} at {:?}", expr.operator, expr.span));
}
}
}
fn visit_call_expression(&mut self, expr: &CallExpression<'a>) {
if let Ok(name) = ast_util::get_callee_name(&expr.callee) {
if syscall_map::map_syscall(&name).is_none() {
self.errors.push(format!("Unsupported function call: {} at {:?}", name, expr.span));
}
} else {
self.errors.push(format!("Unsupported callee expression at {:?}", expr.callee.span()));
}
walk::walk_call_expression(self, expr);
}
fn visit_logical_expression(&mut self, expr: &LogicalExpression<'a>) {
match expr.operator {
LogicalOperator::And |
LogicalOperator::Or => {
walk::walk_logical_expression(self, expr);
}
_ => {
self.errors.push(format!("Unsupported logical operator {:?} at {:?}", expr.operator, expr.span));
}
}
}
}

View File

@ -4,6 +4,7 @@ use oxc_allocator::Allocator;
use oxc_parser::Parser;
use oxc_span::SourceType;
use crate::codegen::Codegen;
use crate::codegen::validator::Validator;
use std::fs;
use prometeu_bytecode::disasm::disasm;
use serde::Serialize;
@ -16,7 +17,49 @@ pub struct Symbol {
pub col: usize,
}
pub fn compile(entry: &Path, out: &Path, emit_disasm: bool, emit_symbols: bool) -> Result<()> {
pub struct CompilationUnit {
pub rom: Vec<u8>,
pub symbols: Vec<Symbol>,
}
impl CompilationUnit {
pub fn export(&self, out: &Path, emit_disasm: bool, emit_symbols: bool) -> Result<()> {
fs::write(out, &self.rom).with_context(|| format!("Failed to write PBC to {:?}", out))?;
if emit_symbols {
let symbols_path = out.with_file_name("symbols.json");
let symbols_json = serde_json::to_string_pretty(&self.symbols)?;
fs::write(&symbols_path, symbols_json)?;
}
if emit_disasm {
let disasm_path = out.with_extension("disasm.txt");
let instructions = disasm(&self.rom).map_err(|e| anyhow::anyhow!("Disassembly failed: {}", e))?;
let mut disasm_text = String::new();
for instr in instructions {
let symbol = self.symbols.iter().find(|s| s.pc == instr.pc);
let comment = if let Some(s) = symbol {
format!(" ; {}:{}", s.file, s.line)
} else {
"".to_string()
};
let operands_str = instr.operands.iter()
.map(|o| format!("{:?}", o))
.collect::<Vec<_>>()
.join(" ");
disasm_text.push_str(&format!("{:08X} {:?} {}{}\n", instr.pc, instr.opcode, operands_str, comment));
}
fs::write(disasm_path, disasm_text)?;
}
Ok(())
}
}
pub fn compile(entry: &Path) -> Result<CompilationUnit> {
let source_text = fs::read_to_string(entry)
.with_context(|| format!("Failed to read entry file: {:?}", entry))?;
@ -34,39 +77,13 @@ pub fn compile(entry: &Path, out: &Path, emit_disasm: bool, emit_symbols: bool)
return Err(anyhow::anyhow!("Failed to parse module"));
}
Validator::validate(&ret.program)?;
codegen.compile_program(&ret.program)?
};
fs::write(out, &rom).with_context(|| format!("Failed to write PBC to {:?}", out))?;
if emit_symbols {
let symbols_path = out.with_file_name("symbols.json");
let symbols_json = serde_json::to_string_pretty(&codegen.symbols)?;
fs::write(&symbols_path, symbols_json)?;
}
if emit_disasm {
let disasm_path = out.with_extension("disasm.txt");
let instructions = disasm(&rom).map_err(|e| anyhow::anyhow!("Disassembly failed: {}", e))?;
let mut disasm_text = String::new();
for instr in instructions {
let symbol = codegen.symbols.iter().find(|s| s.pc == instr.pc);
let comment = if let Some(s) = symbol {
format!(" ; {}:{}", s.file, s.line)
} else {
"".to_string()
};
let operands_str = instr.operands.iter()
.map(|o| format!("{:?}", o))
.collect::<Vec<_>>()
.join(" ");
disasm_text.push_str(&format!("{:08X} {:?} {}{}\n", instr.pc, instr.opcode, operands_str, comment));
}
fs::write(disasm_path, disasm_text)?;
}
Ok(())
Ok(CompilationUnit {
rom,
symbols: codegen.symbols,
})
}

View File

@ -29,7 +29,8 @@ fn main() -> Result<()> {
println!("Entry: {:?}", entry);
println!("Output: {:?}", out);
compiler::compile(&entry, &out, emit_disasm, emit_symbols)?;
let compilation_unit = compiler::compile(&entry)?;
compilation_unit.export(&out, emit_disasm, emit_symbols)?;
}
}