many fixes on compiler

This commit is contained in:
Nilton Constantino 2026-01-21 07:48:06 +00:00
parent 1b091e6ce8
commit 11d9d31c2f
No known key found for this signature in database
9 changed files with 266 additions and 94 deletions

View File

@ -2,21 +2,25 @@ use oxc_ast::ast::*;
use anyhow::{Result, anyhow};
pub fn get_callee_name(expr: &Expression) -> Result<String> {
get_member_expr_name(expr)
}
pub fn get_member_expr_name(expr: &Expression) -> Result<String> {
match expr {
Expression::Identifier(ident) => Ok(ident.name.to_string()),
Expression::Identifier(ident) => {
let name = ident.name.to_string();
// Remove prefix 'P' if it's followed by an uppercase letter (e.g., PGfx -> Gfx)
if name.len() > 1 && name.starts_with('P') && name.chars().nth(1).unwrap().is_uppercase() {
Ok(name[1..].to_string())
} else {
Ok(name)
}
}
Expression::StaticMemberExpression(member) => {
let obj = get_callee_name_from_member_obj(&member.object)?;
let obj = get_member_expr_name(&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"))
_ => Err(anyhow!("Unsupported expression")),
}
}

View File

@ -3,9 +3,11 @@ use oxc_ast::ast::*;
use oxc_span::{Span, GetSpan};
use prometeu_bytecode::opcode::OpCode;
use prometeu_bytecode::asm::{Asm, Operand, assemble};
use prometeu_bytecode::pbc::{ConstantPoolEntry, PbcFile, write_pbc};
use crate::compiler::Symbol;
use crate::syscall_map;
use crate::codegen::ast_util;
use prometeu_core::prometeu_os::Syscall;
use std::collections::HashMap;
use prometeu_bytecode::asm;
@ -15,6 +17,7 @@ pub struct Codegen {
pub symbols: Vec<Symbol>,
instructions: Vec<(Asm, bool)>, // (Asm, has_symbol)
locals: HashMap<String, u32>,
constant_pool: Vec<ConstantPoolEntry>,
next_local: u32,
label_count: u32,
}
@ -27,11 +30,22 @@ impl Codegen {
symbols: Vec::new(),
instructions: Vec::new(),
locals: HashMap::new(),
constant_pool: vec![ConstantPoolEntry::Null], // Index 0 is always Null
next_local: 0,
label_count: 0,
}
}
fn add_constant(&mut self, entry: ConstantPoolEntry) -> u32 {
if let Some(pos) = self.constant_pool.iter().position(|e| e == &entry) {
pos as u32
} else {
let pos = self.constant_pool.len();
self.constant_pool.push(entry);
pos as u32
}
}
pub fn compile_program(&mut self, program: &Program) -> Result<Vec<u8>> {
// Find tick function
let mut tick_fn = None;
@ -63,12 +77,17 @@ impl Codegen {
self.emit_op(OpCode::Ret, vec![], Span::default());
let asm_vec: Vec<Asm> = self.instructions.iter().map(|(a, _)| a.clone()).collect();
let rom = assemble(&asm_vec).map_err(|e| anyhow!("Assemble error: {}", e))?;
let bytecode = assemble(&asm_vec).map_err(|e| anyhow!("Assemble error: {}", e))?;
// Finalize symbols (associate PC)
self.finalize_symbols();
Ok(rom)
let pbc = PbcFile {
cp: self.constant_pool.clone(),
rom: bytecode,
};
write_pbc(&pbc).map_err(|e| anyhow!("PBC Write error: {}", e))
}
fn compile_function(&mut self, f: &Function) -> Result<()> {
@ -150,6 +169,13 @@ impl Codegen {
Expression::BooleanLiteral(b) => {
self.emit_op(OpCode::PushBool, vec![Operand::Bool(b.value)], b.span);
}
Expression::StringLiteral(s) => {
let idx = self.add_constant(ConstantPoolEntry::String(s.value.to_string()));
self.emit_op(OpCode::PushConst, vec![Operand::U32(idx)], s.span);
}
Expression::NullLiteral(n) => {
self.emit_op(OpCode::PushConst, vec![Operand::U32(0)], n.span);
}
Expression::Identifier(ident) => {
let name = ident.name.to_string();
if let Some(&id) = self.locals.get(&name) {
@ -251,12 +277,6 @@ impl Codegen {
self.emit_op(OpCode::BitOr, vec![], call.span);
} else if name == "input.btnA" || name == "Input.btnA" {
self.emit_op(OpCode::PushI32, vec![Operand::I32(syscall_map::BTN_A as i32)], call.span);
self.emit_op(OpCode::Syscall, vec![Operand::U32(syscall_id)], call.span);
} else if name == "input.btnB" || name == "Input.btnB" {
self.emit_op(OpCode::PushI32, vec![Operand::I32(syscall_map::BTN_B as i32)], call.span);
self.emit_op(OpCode::Syscall, vec![Operand::U32(syscall_id)], call.span);
} else {
for arg in &call.arguments {
if let Some(expr) = arg.as_expression() {
@ -270,20 +290,65 @@ impl Codegen {
}
}
Expression::StaticMemberExpression(member) => {
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);
let full_name = ast_util::get_member_expr_name(expr)?;
match full_name.as_str() {
"Color.black" | "color.black" => self.emit_op(OpCode::PushI32, vec![Operand::I32(0x0000)], member.span),
"Color.white" | "color.white" => self.emit_op(OpCode::PushI32, vec![Operand::I32(0xffff)], member.span),
"Color.red" | "color.red" => self.emit_op(OpCode::PushI32, vec![Operand::I32(0xf800)], member.span),
"Color.green" | "color.green" => self.emit_op(OpCode::PushI32, vec![Operand::I32(0x07e0)], member.span),
"Color.blue" | "color.blue" => self.emit_op(OpCode::PushI32, vec![Operand::I32(0x001f)], member.span),
"Color.yellow" | "color.yellow" => self.emit_op(OpCode::PushI32, vec![Operand::I32(0xffe0)], member.span),
"Color.cyan" | "color.cyan" => self.emit_op(OpCode::PushI32, vec![Operand::I32(0x07ff)], member.span),
"Color.magenta" | "color.magenta" => self.emit_op(OpCode::PushI32, vec![Operand::I32(0xf81f)], member.span),
_ => return Err(anyhow!("Member expression outside call not supported: {} at {:?}", full_name, member.span)),
if full_name.to_lowercase().starts_with("color.") {
match full_name.to_lowercase().as_str() {
"color.black" => self.emit_op(OpCode::PushI32, vec![Operand::I32(0x0000)], member.span),
"color.white" => self.emit_op(OpCode::PushI32, vec![Operand::I32(0xffff)], member.span),
"color.red" => self.emit_op(OpCode::PushI32, vec![Operand::I32(0xf800)], member.span),
"color.green" => self.emit_op(OpCode::PushI32, vec![Operand::I32(0x07e0)], member.span),
"color.blue" => self.emit_op(OpCode::PushI32, vec![Operand::I32(0x001f)], member.span),
"color.yellow" => self.emit_op(OpCode::PushI32, vec![Operand::I32(0xffe0)], member.span),
"color.cyan" => self.emit_op(OpCode::PushI32, vec![Operand::I32(0x07ff)], member.span),
"color.magenta" => self.emit_op(OpCode::PushI32, vec![Operand::I32(0xf81f)], member.span),
_ => return Err(anyhow!("Unsupported color constant: {} at {:?}", full_name, member.span)),
}
} else if full_name.to_lowercase().starts_with("pad.") {
let parts: Vec<&str> = full_name.split('.').collect();
if parts.len() == 3 {
let btn_name = parts[1];
let state_name = parts[2];
let btn_id = self.map_btn_name(btn_name)?;
let syscall_id = match state_name {
"down" => Syscall::InputGetPad as u32,
"pressed" => Syscall::InputGetPadPressed as u32,
"released" => Syscall::InputGetPadReleased as u32,
"holdFrames" => Syscall::InputGetPadHold as u32,
_ => return Err(anyhow!("Unsupported button state: {} at {:?}", state_name, member.span)),
};
self.emit_op(OpCode::PushI32, vec![Operand::I32(btn_id as i32)], member.span);
self.emit_op(OpCode::Syscall, vec![Operand::U32(syscall_id)], member.span);
} else {
return Err(anyhow!("Partial Pad access not supported: {} at {:?}", full_name, member.span));
}
} else if full_name.to_lowercase().starts_with("touch.") {
let parts: Vec<&str> = full_name.split('.').collect();
match parts.len() {
2 => {
let prop = parts[1];
let syscall_id = match prop {
"x" => Syscall::TouchGetX as u32,
"y" => Syscall::TouchGetY as u32,
_ => return Err(anyhow!("Unsupported touch property: {} at {:?}", prop, member.span)),
};
self.emit_op(OpCode::Syscall, vec![Operand::U32(syscall_id)], member.span);
}
3 if parts[1] == "button" => {
let state_name = parts[2];
let syscall_id = match state_name {
"down" => Syscall::TouchIsDown as u32,
"pressed" => Syscall::TouchIsPressed as u32,
"released" => Syscall::TouchIsReleased as u32,
"holdFrames" => Syscall::TouchGetHold as u32,
_ => return Err(anyhow!("Unsupported touch button state: {} at {:?}", state_name, member.span)),
};
self.emit_op(OpCode::Syscall, vec![Operand::U32(syscall_id)], member.span);
}
_ => return Err(anyhow!("Unsupported touch access: {} at {:?}", full_name, member.span)),
}
} else {
return Err(anyhow!("Member expression outside call not supported: {} at {:?}", full_name, member.span));
}
}
_ => return Err(anyhow!("Unsupported expression type at {:?}", expr.span())),
@ -291,6 +356,24 @@ impl Codegen {
Ok(())
}
fn map_btn_name(&self, btn_name: &str) -> Result<u32> {
match btn_name.to_lowercase().as_str() {
"up" => Ok(syscall_map::BTN_UP),
"down" => Ok(syscall_map::BTN_DOWN),
"left" => Ok(syscall_map::BTN_LEFT),
"right" => Ok(syscall_map::BTN_RIGHT),
"a" => Ok(syscall_map::BTN_A),
"b" => Ok(syscall_map::BTN_B),
"x" => Ok(syscall_map::BTN_X),
"y" => Ok(syscall_map::BTN_Y),
"l" => Ok(syscall_map::BTN_L),
"r" => Ok(syscall_map::BTN_R),
"start" => Ok(syscall_map::BTN_START),
"select" => Ok(syscall_map::BTN_SELECT),
_ => Err(anyhow!("Unsupported button: {}", btn_name)),
}
}
fn new_label(&mut self, prefix: &str) -> String {
let label = format!("{}_{}", prefix, self.label_count);
self.label_count += 1;

View File

@ -65,6 +65,8 @@ impl<'a> Visit<'a> for Validator {
match expr {
Expression::NumericLiteral(_) |
Expression::BooleanLiteral(_) |
Expression::StringLiteral(_) |
Expression::NullLiteral(_) |
Expression::Identifier(_) |
Expression::AssignmentExpression(_) |
Expression::BinaryExpression(_) |

View File

@ -34,7 +34,15 @@ impl CompilationUnit {
if emit_disasm {
let disasm_path = out.with_extension("disasm.txt");
let instructions = disasm(&self.rom).map_err(|e| anyhow::anyhow!("Disassembly failed: {}", e))?;
// Try to parse as PBC, if fails use raw
let rom_to_disasm = if let Ok(pbc) = prometeu_bytecode::pbc::parse_pbc(&self.rom) {
pbc.rom
} else {
self.rom.clone()
};
let instructions = disasm(&rom_to_disasm).map_err(|e| anyhow::anyhow!("Disassembly failed: {}", e))?;
let mut disasm_text = String::new();
for instr in instructions {

View File

@ -21,7 +21,6 @@ pub fn map_syscall(name: &str) -> Option<u32> {
// Fallback para nomes especiais do compilador
match name {
"input.btnA" | "input.btnB" | "Input.btnA" | "Input.btnB" => Some(Syscall::InputGetPad as u32),
"Color.rgb" | "color.rgb" => Some(0xFFFF_FFFF), // ID especial para Color.rgb (não é um syscall real)
_ => None,
}

View File

@ -331,6 +331,25 @@ impl PrometeuOS {
}
// Helper para syscalls
pub fn get_button<'a>(&self, id: u32, hw: &'a dyn HardwareBridge) -> Option<&'a crate::model::Button> {
let pad = hw.pad();
match id {
0 => Some(&pad.up),
1 => Some(&pad.down),
2 => Some(&pad.left),
3 => Some(&pad.right),
4 => Some(&pad.a),
5 => Some(&pad.b),
6 => Some(&pad.x),
7 => Some(&pad.y),
8 => Some(&pad.l),
9 => Some(&pad.r),
10 => Some(&pad.start),
11 => Some(&pad.select),
_ => None,
}
}
pub fn is_button_down(&self, id: u32, hw: &mut dyn HardwareBridge) -> bool {
match id {
0 => hw.pad().up.down,
@ -650,6 +669,49 @@ impl NativeInterface for PrometeuOS {
vm.push(Value::Boolean(is_down));
Ok(50)
}
Syscall::InputGetPadPressed => {
let button_id = vm.pop_integer()? as u32;
let val = self.get_button(button_id, hw).map(|b| b.pressed).unwrap_or(false);
vm.push(Value::Boolean(val));
Ok(50)
}
Syscall::InputGetPadReleased => {
let button_id = vm.pop_integer()? as u32;
let val = self.get_button(button_id, hw).map(|b| b.released).unwrap_or(false);
vm.push(Value::Boolean(val));
Ok(50)
}
Syscall::InputGetPadHold => {
let button_id = vm.pop_integer()? as u32;
let val = self.get_button(button_id, hw).map(|b| b.hold_frames).unwrap_or(0);
vm.push(Value::Int32(val as i32));
Ok(50)
}
Syscall::TouchGetX => {
vm.push(Value::Int32(hw.touch().x));
Ok(50)
}
Syscall::TouchGetY => {
vm.push(Value::Int32(hw.touch().y));
Ok(50)
}
Syscall::TouchIsDown => {
vm.push(Value::Boolean(hw.touch().f.down));
Ok(50)
}
Syscall::TouchIsPressed => {
vm.push(Value::Boolean(hw.touch().f.pressed));
Ok(50)
}
Syscall::TouchIsReleased => {
vm.push(Value::Boolean(hw.touch().f.released));
Ok(50)
}
Syscall::TouchGetHold => {
vm.push(Value::Int32(hw.touch().f.hold_frames as i32));
Ok(50)
}
// --- Audio Syscalls ---

View File

@ -15,6 +15,16 @@ pub enum Syscall {
// Input
InputGetPad = 0x2001,
InputGetPadPressed = 0x2002,
InputGetPadReleased = 0x2003,
InputGetPadHold = 0x2004,
TouchGetX = 0x2101,
TouchGetY = 0x2102,
TouchIsDown = 0x2103,
TouchIsPressed = 0x2104,
TouchIsReleased = 0x2105,
TouchGetHold = 0x2106,
// Audio
AudioPlaySample = 0x3001,
@ -45,6 +55,15 @@ impl Syscall {
0x1005 => Some(Self::GfxDrawDisc),
0x1006 => Some(Self::GfxDrawSquare),
0x2001 => Some(Self::InputGetPad),
0x2002 => Some(Self::InputGetPadPressed),
0x2003 => Some(Self::InputGetPadReleased),
0x2004 => Some(Self::InputGetPadHold),
0x2101 => Some(Self::TouchGetX),
0x2102 => Some(Self::TouchGetY),
0x2103 => Some(Self::TouchIsDown),
0x2104 => Some(Self::TouchIsPressed),
0x2105 => Some(Self::TouchIsReleased),
0x2106 => Some(Self::TouchGetHold),
0x3001 => Some(Self::AudioPlaySample),
0x4001 => Some(Self::FsOpen),
0x4002 => Some(Self::FsRead),
@ -59,75 +78,70 @@ impl Syscall {
}
}
pub fn as_str(&self) -> &'static str {
match self {
Self::SystemHasCart => "system.has_cart",
Self::SystemRunCart => "system.run_cart",
Self::GfxClear => "gfx.clear",
Self::GfxFillRect => "gfx.fillRect",
Self::GfxDrawLine => "gfx.drawLine",
Self::GfxDrawCircle => "gfx.drawCircle",
Self::GfxDrawDisc => "gfx.drawDisc",
Self::GfxDrawSquare => "gfx.drawSquare",
Self::InputGetPad => "input.get_pad",
Self::AudioPlaySample => "audio.playSample",
Self::FsOpen => "fs.open",
Self::FsRead => "fs.read",
Self::FsWrite => "fs.write",
Self::FsClose => "fs.close",
Self::FsListDir => "fs.listDir",
Self::FsExists => "fs.exists",
Self::FsDelete => "fs.delete",
Self::LogWrite => "log.write",
Self::LogWriteTag => "log.writeTag",
}
}
// pub fn as_str(&self) -> &'static str {
// match self {
// Self::SystemHasCart => "system.has_cart",
// Self::SystemRunCart => "system.run_cart",
// Self::GfxClear => "gfx.clear",
// Self::GfxFillRect => "gfx.fillRect",
// Self::GfxDrawLine => "gfx.drawLine",
// Self::GfxDrawCircle => "gfx.drawCircle",
// Self::GfxDrawDisc => "gfx.drawDisc",
// Self::GfxDrawSquare => "gfx.drawSquare",
// Self::InputGetPad => "input.get_pad",
// Self::InputGetPadPressed => "input.get_pad_pressed",
// Self::InputGetPadReleased => "input.get_pad_released",
// Self::InputGetPadHold => "input.get_pad_hold",
// Self::TouchGetX => "touch.getX",
// Self::TouchGetY => "touch.getY",
// Self::TouchIsDown => "touch.isDown",
// Self::TouchIsPressed => "touch.isPressed",
// Self::TouchIsReleased => "touch.isReleased",
// Self::TouchGetHold => "touch.getHold",
// Self::AudioPlaySample => "audio.playSample",
// Self::FsOpen => "fs.open",
// Self::FsRead => "fs.read",
// Self::FsWrite => "fs.write",
// Self::FsClose => "fs.close",
// Self::FsListDir => "fs.listDir",
// Self::FsExists => "fs.exists",
// Self::FsDelete => "fs.delete",
// Self::LogWrite => "log.write",
// Self::LogWriteTag => "log.writeTag",
// }
// }
pub fn from_name(name: &str) -> Option<Self> {
let name_lower = name.to_lowercase();
match name_lower.as_str() {
"system.has_cart" => Some(Self::SystemHasCart),
"system.run_cart" => Some(Self::SystemRunCart),
match name {
"system.hasCart" => Some(Self::SystemHasCart),
"gfx.clear" => Some(Self::GfxClear),
"gfx.fillrect" | "gfx.draw_rect" => Some(Self::GfxFillRect),
"gfx.drawline" | "gfx.draw_line" => Some(Self::GfxDrawLine),
"gfx.drawcircle" | "gfx.draw_circle" => Some(Self::GfxDrawCircle),
"gfx.drawdisc" | "gfx.draw_disc" => Some(Self::GfxDrawDisc),
"gfx.drawsquare" | "gfx.draw_square" => Some(Self::GfxDrawSquare),
"input.get_pad" => Some(Self::InputGetPad),
"audio.playsample" | "audio.play_sample" => Some(Self::AudioPlaySample),
"gfx.fillRect" | "gfx.draw_rect" => Some(Self::GfxFillRect),
"gfx.drawLine" | "gfx.draw_line" => Some(Self::GfxDrawLine),
"gfx.drawCircle" | "gfx.draw_circle" => Some(Self::GfxDrawCircle),
"fx.drawDisc" | "gfx.draw_disc" => Some(Self::GfxDrawDisc),
"gfx.drawSquare" | "gfx.draw_square" => Some(Self::GfxDrawSquare),
"input.getPad" => Some(Self::InputGetPad),
"input.getPadPressed" | "input.get_pad_pressed" => Some(Self::InputGetPadPressed),
"input.getPadReleased" | "input.get_pad_released" => Some(Self::InputGetPadReleased),
"input.getPadHold" | "input.get_pad_hold" => Some(Self::InputGetPadHold),
"touch.getX" | "touch.get_x" => Some(Self::TouchGetX),
"touch.getY" | "touch.get_y" => Some(Self::TouchGetY),
"touch.isDown" | "touch.is_down" => Some(Self::TouchIsDown),
"touch.isPressed" | "touch.is_pressed" => Some(Self::TouchIsPressed),
"touch.isReleased" | "touch.is_released" => Some(Self::TouchIsReleased),
"touch.getHold" | "touch.get_hold" => Some(Self::TouchGetHold),
"audio.playSample" | "audio.play_sample" => Some(Self::AudioPlaySample),
"fs.open" => Some(Self::FsOpen),
"fs.read" => Some(Self::FsRead),
"fs.write" => Some(Self::FsWrite),
"fs.close" => Some(Self::FsClose),
"fs.listdir" => Some(Self::FsListDir),
"fs.listDir" | "fs.list_dir" => Some(Self::FsListDir),
"fs.exists" => Some(Self::FsExists),
"fs.delete" => Some(Self::FsDelete),
"log.write" => Some(Self::LogWrite),
"log.writetag" | "log.write_tag" => Some(Self::LogWriteTag),
"log.writeTag" | "log.write_tag" => Some(Self::LogWriteTag),
_ => {
// Tenta corresponder exatamente se o lowercase falhar (para camelCase original)
match name {
"gfx.fillRect" => Some(Self::GfxFillRect),
"gfx.drawLine" => Some(Self::GfxDrawLine),
"gfx.drawCircle" => Some(Self::GfxDrawCircle),
"gfx.drawDisc" => Some(Self::GfxDrawDisc),
"gfx.drawSquare" => Some(Self::GfxDrawSquare),
"audio.playSample" => Some(Self::AudioPlaySample),
"fs.listDir" => Some(Self::FsListDir),
"log.writeTag" => Some(Self::LogWriteTag),
"Gfx.fillRect" => Some(Self::GfxFillRect),
"Gfx.drawLine" => Some(Self::GfxDrawLine),
"Gfx.drawCircle" => Some(Self::GfxDrawCircle),
"Gfx.drawDisc" => Some(Self::GfxDrawDisc),
"Gfx.drawSquare" => Some(Self::GfxDrawSquare),
"Audio.playSample" => Some(Self::AudioPlaySample),
"Fs.listDir" => Some(Self::FsListDir),
"Log.writeTag" => Some(Self::LogWriteTag),
"System.hasCart" | "System.has_cart" => Some(Self::SystemHasCart),
"System.runCart" | "System.run_cart" => Some(Self::SystemRunCart),
_ => None,
}
None
}
}
}

View File

@ -1,8 +1,8 @@
export function tick(): void {
let color = 0x07E0; // green
if (Input.btnA()) color = 0xF800; // red
if (Input.btnB()) color = 0x001F; // blue
if (PInput.btnA()) color = 0xF800; // red
if (PInput.btnB()) color = 0x001F; // blue
Gfx.fillRect(60, 60, 40, 40, color);
}