diff --git a/crates/prometeu-core/src/debugger_protocol.rs b/crates/prometeu-core/src/debugger_protocol.rs index 0eac55dc..0e9b8c89 100644 --- a/crates/prometeu-core/src/debugger_protocol.rs +++ b/crates/prometeu-core/src/debugger_protocol.rs @@ -99,7 +99,7 @@ mod tests { fn test_get_state_serialization() { let resp = DebugResponse::GetState { pc: 42, - stack_top: vec![Value::Integer(10), Value::String("test".into()), Value::Boolean(true)], + stack_top: vec![Value::Int64(10), Value::String("test".into()), Value::Boolean(true)], frame_index: 5, app_id: 1, }; diff --git a/crates/prometeu-core/src/prometeu_os/prometeu_os.rs b/crates/prometeu-core/src/prometeu_os/prometeu_os.rs index f3f5287b..f216b10b 100644 --- a/crates/prometeu-core/src/prometeu_os/prometeu_os.rs +++ b/crates/prometeu-core/src/prometeu_os/prometeu_os.rs @@ -471,7 +471,7 @@ mod tests { os.current_app_id = 123; // 1. Normal log test - vm.push(Value::Integer(2)); // Info + vm.push(Value::Int64(2)); // Info vm.push(Value::String("Hello Log".to_string())); let res = os.syscall(0x5001, &mut vm, &mut hw); assert!(res.is_ok()); @@ -483,7 +483,7 @@ mod tests { // 2. Truncation test let long_msg = "A".repeat(300); - vm.push(Value::Integer(3)); // Warn + vm.push(Value::Int64(3)); // Warn vm.push(Value::String(long_msg)); os.syscall(0x5001, &mut vm, &mut hw).unwrap(); @@ -494,13 +494,13 @@ mod tests { // 3. Rate Limit Test // We already made 2 logs. The limit is 10. for i in 0..8 { - vm.push(Value::Integer(2)); + vm.push(Value::Int64(2)); vm.push(Value::String(format!("Log {}", i))); os.syscall(0x5001, &mut vm, &mut hw).unwrap(); } // The 11th log should be ignored (and generate a system warning) - vm.push(Value::Integer(2)); + vm.push(Value::Int64(2)); vm.push(Value::String("Eleventh log".to_string())); os.syscall(0x5001, &mut vm, &mut hw).unwrap(); @@ -513,7 +513,7 @@ mod tests { // 4. Rate limit reset test in the next frame os.begin_logical_frame(&InputSignals::default(), &mut hw); - vm.push(Value::Integer(2)); + vm.push(Value::Int64(2)); vm.push(Value::String("New frame log".to_string())); os.syscall(0x5001, &mut vm, &mut hw).unwrap(); @@ -521,8 +521,8 @@ mod tests { assert_eq!(recent[0].msg, "New frame log"); // 5. LOG_WRITE_TAG test - vm.push(Value::Integer(2)); // Info - vm.push(Value::Integer(42)); // Tag + vm.push(Value::Int64(2)); // Info + vm.push(Value::Int64(42)); // Tag vm.push(Value::String("Tagged Log".to_string())); os.syscall(0x5002, &mut vm, &mut hw).unwrap(); @@ -670,13 +670,13 @@ impl NativeInterface for PrometeuOS { _ => return Err("Expected string path".into()), }; if self.fs_state != FsState::Mounted { - vm.push(Value::Integer(-1)); + vm.push(Value::Int64(-1)); return Ok(100); } let handle = self.next_handle; self.open_files.insert(handle, path); self.next_handle += 1; - vm.push(Value::Integer(handle as i64)); + vm.push(Value::Int64(handle as i64)); Ok(200) } // FS_READ(handle) -> content diff --git a/crates/prometeu-core/src/virtual_machine/opcode.rs b/crates/prometeu-core/src/virtual_machine/opcode.rs index 7e509732..f71168a1 100644 --- a/crates/prometeu-core/src/virtual_machine/opcode.rs +++ b/crates/prometeu-core/src/virtual_machine/opcode.rs @@ -7,6 +7,7 @@ pub enum OpCode { Halt = 0x01, Jmp = 0x02, JmpIfFalse = 0x03, + JmpIfTrue = 0x04, // 6.2 Stack PushConst = 0x10, @@ -16,6 +17,7 @@ pub enum OpCode { PushI64 = 0x14, PushF64 = 0x15, PushBool = 0x16, + PushI32 = 0x17, // 6.3 Arithmetic Add = 0x20, @@ -31,6 +33,14 @@ pub enum OpCode { And = 0x34, Or = 0x35, Not = 0x36, + BitAnd = 0x37, + BitOr = 0x38, + BitXor = 0x39, + Shl = 0x3A, + Shr = 0x3B, + Lte = 0x3C, + Gte = 0x3D, + Neg = 0x3E, // 6.5 Variables GetGlobal = 0x40, @@ -63,6 +73,7 @@ impl TryFrom for OpCode { 0x01 => Ok(OpCode::Halt), 0x02 => Ok(OpCode::Jmp), 0x03 => Ok(OpCode::JmpIfFalse), + 0x04 => Ok(OpCode::JmpIfTrue), 0x10 => Ok(OpCode::PushConst), 0x11 => Ok(OpCode::Pop), 0x12 => Ok(OpCode::Dup), @@ -70,6 +81,7 @@ impl TryFrom for OpCode { 0x14 => Ok(OpCode::PushI64), 0x15 => Ok(OpCode::PushF64), 0x16 => Ok(OpCode::PushBool), + 0x17 => Ok(OpCode::PushI32), 0x20 => Ok(OpCode::Add), 0x21 => Ok(OpCode::Sub), 0x22 => Ok(OpCode::Mul), @@ -81,6 +93,14 @@ impl TryFrom for OpCode { 0x34 => Ok(OpCode::And), 0x35 => Ok(OpCode::Or), 0x36 => Ok(OpCode::Not), + 0x37 => Ok(OpCode::BitAnd), + 0x38 => Ok(OpCode::BitOr), + 0x39 => Ok(OpCode::BitXor), + 0x3A => Ok(OpCode::Shl), + 0x3B => Ok(OpCode::Shr), + 0x3C => Ok(OpCode::Lte), + 0x3D => Ok(OpCode::Gte), + 0x3E => Ok(OpCode::Neg), 0x40 => Ok(OpCode::GetGlobal), 0x41 => Ok(OpCode::SetGlobal), 0x42 => Ok(OpCode::GetLocal), @@ -106,6 +126,7 @@ impl OpCode { OpCode::Halt => 1, OpCode::Jmp => 2, OpCode::JmpIfFalse => 3, + OpCode::JmpIfTrue => 3, OpCode::PushConst => 2, OpCode::Pop => 1, OpCode::Dup => 1, @@ -113,6 +134,7 @@ impl OpCode { OpCode::PushI64 => 2, OpCode::PushF64 => 2, OpCode::PushBool => 2, + OpCode::PushI32 => 2, OpCode::Add => 2, OpCode::Sub => 2, OpCode::Mul => 4, @@ -124,6 +146,14 @@ impl OpCode { OpCode::And => 2, OpCode::Or => 2, OpCode::Not => 1, + OpCode::BitAnd => 2, + OpCode::BitOr => 2, + OpCode::BitXor => 2, + OpCode::Shl => 2, + OpCode::Shr => 2, + OpCode::Lte => 2, + OpCode::Gte => 2, + OpCode::Neg => 1, OpCode::GetGlobal => 3, OpCode::SetGlobal => 3, OpCode::GetLocal => 2, diff --git a/crates/prometeu-core/src/virtual_machine/value.rs b/crates/prometeu-core/src/virtual_machine/value.rs index 4ecefd85..e1c00655 100644 --- a/crates/prometeu-core/src/virtual_machine/value.rs +++ b/crates/prometeu-core/src/virtual_machine/value.rs @@ -1,9 +1,11 @@ use serde::{Deserialize, Serialize}; +use std::cmp::Ordering; #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] pub enum Value { - Integer(i64), + Int32(i32), + Int64(i64), Float(f64), Boolean(bool), String(String), @@ -14,10 +16,15 @@ pub enum Value { impl PartialEq for Value { fn eq(&self, other: &Self) -> bool { match (self, other) { - (Value::Integer(a), Value::Integer(b)) => a == b, + (Value::Int32(a), Value::Int32(b)) => a == b, + (Value::Int64(a), Value::Int64(b)) => a == b, + (Value::Int32(a), Value::Int64(b)) => *a as i64 == *b, + (Value::Int64(a), Value::Int32(b)) => *a == *b as i64, (Value::Float(a), Value::Float(b)) => a == b, - (Value::Integer(a), Value::Float(b)) => *a as f64 == *b, - (Value::Float(a), Value::Integer(b)) => *a == *b as f64, + (Value::Int32(a), Value::Float(b)) => *a as f64 == *b, + (Value::Float(a), Value::Int32(b)) => *a == *b as f64, + (Value::Int64(a), Value::Float(b)) => *a as f64 == *b, + (Value::Float(a), Value::Int64(b)) => *a == *b as f64, (Value::Boolean(a), Value::Boolean(b)) => a == b, (Value::String(a), Value::String(b)) => a == b, (Value::Ref(a), Value::Ref(b)) => a == b, @@ -27,10 +34,30 @@ impl PartialEq for Value { } } +impl PartialOrd for Value { + fn partial_cmp(&self, other: &Self) -> Option { + match (self, other) { + (Value::Int32(a), Value::Int32(b)) => a.partial_cmp(b), + (Value::Int64(a), Value::Int64(b)) => a.partial_cmp(b), + (Value::Int32(a), Value::Int64(b)) => (*a as i64).partial_cmp(b), + (Value::Int64(a), Value::Int32(b)) => a.partial_cmp(&(*b as i64)), + (Value::Float(a), Value::Float(b)) => a.partial_cmp(b), + (Value::Int32(a), Value::Float(b)) => (*a as f64).partial_cmp(b), + (Value::Float(a), Value::Int32(b)) => a.partial_cmp(&(*b as f64)), + (Value::Int64(a), Value::Float(b)) => (*a as f64).partial_cmp(b), + (Value::Float(a), Value::Int64(b)) => a.partial_cmp(&(*b as f64)), + (Value::Boolean(a), Value::Boolean(b)) => a.partial_cmp(b), + (Value::String(a), Value::String(b)) => a.partial_cmp(b), + _ => None, + } + } +} + impl Value { pub fn as_float(&self) -> Option { match self { - Value::Integer(i) => Some(*i as f64), + Value::Int32(i) => Some(*i as f64), + Value::Int64(i) => Some(*i as f64), Value::Float(f) => Some(*f), _ => None, } @@ -38,7 +65,8 @@ impl Value { pub fn as_integer(&self) -> Option { match self { - Value::Integer(i) => Some(*i), + Value::Int32(i) => Some(*i as i64), + Value::Int64(i) => Some(*i), Value::Float(f) => Some(*f as i64), _ => None, } @@ -51,12 +79,19 @@ mod tests { #[test] fn test_value_equality() { - assert_eq!(Value::Integer(10), Value::Integer(10)); + assert_eq!(Value::Int32(10), Value::Int32(10)); + assert_eq!(Value::Int64(10), Value::Int64(10)); + assert_eq!(Value::Int32(10), Value::Int64(10)); + assert_eq!(Value::Int64(10), Value::Int32(10)); assert_eq!(Value::Float(10.5), Value::Float(10.5)); - assert_eq!(Value::Integer(10), Value::Float(10.0)); - assert_eq!(Value::Float(10.0), Value::Integer(10)); - assert_ne!(Value::Integer(10), Value::Integer(11)); - assert_ne!(Value::Integer(10), Value::Float(10.1)); + assert_eq!(Value::Int32(10), Value::Float(10.0)); + assert_eq!(Value::Float(10.0), Value::Int32(10)); + assert_eq!(Value::Int64(10), Value::Float(10.0)); + assert_eq!(Value::Float(10.0), Value::Int64(10)); + assert_ne!(Value::Int32(10), Value::Int32(11)); + assert_ne!(Value::Int64(10), Value::Int64(11)); + assert_ne!(Value::Int32(10), Value::Int64(11)); + assert_ne!(Value::Int32(10), Value::Float(10.1)); assert_eq!(Value::Boolean(true), Value::Boolean(true)); assert_ne!(Value::Boolean(true), Value::Boolean(false)); assert_eq!(Value::String("oi".into()), Value::String("oi".into())); @@ -65,9 +100,13 @@ mod tests { #[test] fn test_value_conversions() { - let v_int = Value::Integer(42); - assert_eq!(v_int.as_float(), Some(42.0)); - assert_eq!(v_int.as_integer(), Some(42)); + let v_int32 = Value::Int32(42); + assert_eq!(v_int32.as_float(), Some(42.0)); + assert_eq!(v_int32.as_integer(), Some(42)); + + let v_int64 = Value::Int64(42); + assert_eq!(v_int64.as_float(), Some(42.0)); + assert_eq!(v_int64.as_integer(), Some(42)); let v_float = Value::Float(42.7); assert_eq!(v_float.as_float(), Some(42.7)); diff --git a/crates/prometeu-core/src/virtual_machine/virtual_machine.rs b/crates/prometeu-core/src/virtual_machine/virtual_machine.rs index f54c7065..a6e69de3 100644 --- a/crates/prometeu-core/src/virtual_machine/virtual_machine.rs +++ b/crates/prometeu-core/src/virtual_machine/virtual_machine.rs @@ -127,9 +127,9 @@ impl VirtualMachine { cursor += 1; match tag { - 1 => { // Integer (64-bit) + 1 => { // Int64 (64-bit) let val = self.read_i64_at(bytes, &mut cursor)?; - cp.push(Value::Integer(val)); + cp.push(Value::Int64(val)); } 2 => { // Float (64-bit) let val = self.read_f64_at(bytes, &mut cursor)?; @@ -148,6 +148,10 @@ impl VirtualMachine { cursor += len; cp.push(Value::String(s)); } + 5 => { // Int32 (32-bit) + let val = self.read_u32_at(bytes, &mut cursor)? as i32; + cp.push(Value::Int32(val)); + } _ => cp.push(Value::Null), } } @@ -312,6 +316,13 @@ impl VirtualMachine { self.pc = addr; } } + OpCode::JmpIfTrue => { + let addr = self.read_u32()? as usize; + let val = self.pop()?; + if let Value::Boolean(true) = val { + self.pc = addr; + } + } OpCode::PushConst => { let idx = self.read_u32()? as usize; let val = self.program.constant_pool.get(idx).cloned().ok_or("Invalid constant index")?; @@ -319,7 +330,11 @@ impl VirtualMachine { } OpCode::PushI64 => { let val = self.read_i64()?; - self.push(Value::Integer(val)); + self.push(Value::Int64(val)); + } + OpCode::PushI32 => { + let val = self.read_i32()?; + self.push(Value::Int32(val)); } OpCode::PushF64 => { let val = self.read_f64()?; @@ -343,40 +358,75 @@ impl VirtualMachine { self.push(b); } OpCode::Add => self.binary_op(|a, b| match (a, b) { - (Value::Integer(a), Value::Integer(b)) => Ok(Value::Integer(a.wrapping_add(b))), + (Value::Int32(a), Value::Int32(b)) => Ok(Value::Int32(a.wrapping_add(b))), + (Value::Int64(a), Value::Int64(b)) => Ok(Value::Int64(a.wrapping_add(b))), + (Value::Int32(a), Value::Int64(b)) => Ok(Value::Int64((a as i64).wrapping_add(b))), + (Value::Int64(a), Value::Int32(b)) => Ok(Value::Int64(a.wrapping_add(b as i64))), (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a + b)), - (Value::Integer(a), Value::Float(b)) => Ok(Value::Float(a as f64 + b)), - (Value::Float(a), Value::Integer(b)) => Ok(Value::Float(a + b as f64)), + (Value::Int32(a), Value::Float(b)) => Ok(Value::Float(a as f64 + b)), + (Value::Float(a), Value::Int32(b)) => Ok(Value::Float(a + b as f64)), + (Value::Int64(a), Value::Float(b)) => Ok(Value::Float(a as f64 + b)), + (Value::Float(a), Value::Int64(b)) => Ok(Value::Float(a + b as f64)), _ => Err("Invalid types for ADD".into()), })?, OpCode::Sub => self.binary_op(|a, b| match (a, b) { - (Value::Integer(a), Value::Integer(b)) => Ok(Value::Integer(a.wrapping_sub(b))), + (Value::Int32(a), Value::Int32(b)) => Ok(Value::Int32(a.wrapping_sub(b))), + (Value::Int64(a), Value::Int64(b)) => Ok(Value::Int64(a.wrapping_sub(b))), + (Value::Int32(a), Value::Int64(b)) => Ok(Value::Int64((a as i64).wrapping_sub(b))), + (Value::Int64(a), Value::Int32(b)) => Ok(Value::Int64(a.wrapping_sub(b as i64))), (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a - b)), - (Value::Integer(a), Value::Float(b)) => Ok(Value::Float(a as f64 - b)), - (Value::Float(a), Value::Integer(b)) => Ok(Value::Float(a - b as f64)), + (Value::Int32(a), Value::Float(b)) => Ok(Value::Float(a as f64 - b)), + (Value::Float(a), Value::Int32(b)) => Ok(Value::Float(a - b as f64)), + (Value::Int64(a), Value::Float(b)) => Ok(Value::Float(a as f64 - b)), + (Value::Float(a), Value::Int64(b)) => Ok(Value::Float(a - b as f64)), _ => Err("Invalid types for SUB".into()), })?, OpCode::Mul => self.binary_op(|a, b| match (a, b) { - (Value::Integer(a), Value::Integer(b)) => Ok(Value::Integer(a.wrapping_mul(b))), + (Value::Int32(a), Value::Int32(b)) => Ok(Value::Int32(a.wrapping_mul(b))), + (Value::Int64(a), Value::Int64(b)) => Ok(Value::Int64(a.wrapping_mul(b))), + (Value::Int32(a), Value::Int64(b)) => Ok(Value::Int64((a as i64).wrapping_mul(b))), + (Value::Int64(a), Value::Int32(b)) => Ok(Value::Int64(a.wrapping_mul(b as i64))), (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a * b)), - (Value::Integer(a), Value::Float(b)) => Ok(Value::Float(a as f64 * b)), - (Value::Float(a), Value::Integer(b)) => Ok(Value::Float(a * b as f64)), + (Value::Int32(a), Value::Float(b)) => Ok(Value::Float(a as f64 * b)), + (Value::Float(a), Value::Int32(b)) => Ok(Value::Float(a * b as f64)), + (Value::Int64(a), Value::Float(b)) => Ok(Value::Float(a as f64 * b)), + (Value::Float(a), Value::Int64(b)) => Ok(Value::Float(a * b as f64)), _ => Err("Invalid types for MUL".into()), })?, OpCode::Div => self.binary_op(|a, b| match (a, b) { - (Value::Integer(a), Value::Integer(b)) => { + (Value::Int32(a), Value::Int32(b)) => { if b == 0 { return Err("Division by zero".into()); } - Ok(Value::Integer(a / b)) + Ok(Value::Int32(a / b)) + } + (Value::Int64(a), Value::Int64(b)) => { + if b == 0 { return Err("Division by zero".into()); } + Ok(Value::Int64(a / b)) + } + (Value::Int32(a), Value::Int64(b)) => { + if b == 0 { return Err("Division by zero".into()); } + Ok(Value::Int64(a as i64 / b)) + } + (Value::Int64(a), Value::Int32(b)) => { + if b == 0 { return Err("Division by zero".into()); } + Ok(Value::Int64(a / b as i64)) } (Value::Float(a), Value::Float(b)) => { if b == 0.0 { return Err("Division by zero".into()); } Ok(Value::Float(a / b)) } - (Value::Integer(a), Value::Float(b)) => { + (Value::Int32(a), Value::Float(b)) => { if b == 0.0 { return Err("Division by zero".into()); } Ok(Value::Float(a as f64 / b)) } - (Value::Float(a), Value::Integer(b)) => { + (Value::Float(a), Value::Int32(b)) => { + if b == 0 { return Err("Division by zero".into()); } + Ok(Value::Float(a / b as f64)) + } + (Value::Int64(a), Value::Float(b)) => { + if b == 0.0 { return Err("Division by zero".into()); } + Ok(Value::Float(a as f64 / b)) + } + (Value::Float(a), Value::Int64(b)) => { if b == 0 { return Err("Division by zero".into()); } Ok(Value::Float(a / b as f64)) } @@ -385,22 +435,24 @@ impl VirtualMachine { OpCode::Eq => self.binary_op(|a, b| Ok(Value::Boolean(a == b)))?, OpCode::Neq => self.binary_op(|a, b| Ok(Value::Boolean(a != b)))?, OpCode::Lt => self.binary_op(|a, b| { - match (a, b) { - (Value::Integer(a), Value::Integer(b)) => Ok(Value::Boolean(a < b)), - (Value::Float(a), Value::Float(b)) => Ok(Value::Boolean(a < b)), - (Value::Integer(a), Value::Float(b)) => Ok(Value::Boolean((a as f64) < b)), - (Value::Float(a), Value::Integer(b)) => Ok(Value::Boolean(a < (b as f64))), - _ => Err("Invalid types for LT".into()), - } + a.partial_cmp(&b) + .map(|o| Value::Boolean(o == std::cmp::Ordering::Less)) + .ok_or_else(|| "Invalid types for LT".into()) })?, OpCode::Gt => self.binary_op(|a, b| { - match (a, b) { - (Value::Integer(a), Value::Integer(b)) => Ok(Value::Boolean(a > b)), - (Value::Float(a), Value::Float(b)) => Ok(Value::Boolean(a > b)), - (Value::Integer(a), Value::Float(b)) => Ok(Value::Boolean((a as f64) > b)), - (Value::Float(a), Value::Integer(b)) => Ok(Value::Boolean(a > (b as f64))), - _ => Err("Invalid types for GT".into()), - } + a.partial_cmp(&b) + .map(|o| Value::Boolean(o == std::cmp::Ordering::Greater)) + .ok_or_else(|| "Invalid types for GT".into()) + })?, + OpCode::Lte => self.binary_op(|a, b| { + a.partial_cmp(&b) + .map(|o| Value::Boolean(o != std::cmp::Ordering::Greater)) + .ok_or_else(|| "Invalid types for LTE".into()) + })?, + OpCode::Gte => self.binary_op(|a, b| { + a.partial_cmp(&b) + .map(|o| Value::Boolean(o != std::cmp::Ordering::Less)) + .ok_or_else(|| "Invalid types for GTE".into()) })?, OpCode::And => self.binary_op(|a, b| match (a, b) { (Value::Boolean(a), Value::Boolean(b)) => Ok(Value::Boolean(a && b)), @@ -418,6 +470,50 @@ impl VirtualMachine { return Err("Invalid type for NOT".into()); } } + OpCode::BitAnd => self.binary_op(|a, b| match (a, b) { + (Value::Int32(a), Value::Int32(b)) => Ok(Value::Int32(a & b)), + (Value::Int64(a), Value::Int64(b)) => Ok(Value::Int64(a & b)), + (Value::Int32(a), Value::Int64(b)) => Ok(Value::Int64((a as i64) & b)), + (Value::Int64(a), Value::Int32(b)) => Ok(Value::Int64(a & (b as i64))), + _ => Err("Invalid types for BitAnd".into()), + })?, + OpCode::BitOr => self.binary_op(|a, b| match (a, b) { + (Value::Int32(a), Value::Int32(b)) => Ok(Value::Int32(a | b)), + (Value::Int64(a), Value::Int64(b)) => Ok(Value::Int64(a | b)), + (Value::Int32(a), Value::Int64(b)) => Ok(Value::Int64((a as i64) | b)), + (Value::Int64(a), Value::Int32(b)) => Ok(Value::Int64(a | (b as i64))), + _ => Err("Invalid types for BitOr".into()), + })?, + OpCode::BitXor => self.binary_op(|a, b| match (a, b) { + (Value::Int32(a), Value::Int32(b)) => Ok(Value::Int32(a ^ b)), + (Value::Int64(a), Value::Int64(b)) => Ok(Value::Int64(a ^ b)), + (Value::Int32(a), Value::Int64(b)) => Ok(Value::Int64((a as i64) ^ b)), + (Value::Int64(a), Value::Int32(b)) => Ok(Value::Int64(a ^ (b as i64))), + _ => Err("Invalid types for BitXor".into()), + })?, + OpCode::Shl => self.binary_op(|a, b| match (a, b) { + (Value::Int32(a), Value::Int32(b)) => Ok(Value::Int32(a.wrapping_shl(b as u32))), + (Value::Int64(a), Value::Int64(b)) => Ok(Value::Int64(a.wrapping_shl(b as u32))), + (Value::Int32(a), Value::Int64(b)) => Ok(Value::Int64((a as i64).wrapping_shl(b as u32))), + (Value::Int64(a), Value::Int32(b)) => Ok(Value::Int64(a.wrapping_shl(b as u32))), + _ => Err("Invalid types for Shl".into()), + })?, + OpCode::Shr => self.binary_op(|a, b| match (a, b) { + (Value::Int32(a), Value::Int32(b)) => Ok(Value::Int32(a.wrapping_shr(b as u32))), + (Value::Int64(a), Value::Int64(b)) => Ok(Value::Int64(a.wrapping_shr(b as u32))), + (Value::Int32(a), Value::Int64(b)) => Ok(Value::Int64((a as i64).wrapping_shr(b as u32))), + (Value::Int64(a), Value::Int32(b)) => Ok(Value::Int64(a.wrapping_shr(b as u32))), + _ => Err("Invalid types for Shr".into()), + })?, + OpCode::Neg => { + let val = self.pop()?; + match val { + Value::Int32(a) => self.push(Value::Int32(a.wrapping_neg())), + Value::Int64(a) => self.push(Value::Int64(a.wrapping_neg())), + Value::Float(a) => self.push(Value::Float(-a)), + _ => return Err("Invalid type for Neg".into()), + } + } OpCode::GetGlobal => { let idx = self.read_u32()? as usize; let val = self.globals.get(idx).cloned().ok_or("Invalid global index")?; @@ -548,6 +644,20 @@ impl VirtualMachine { Ok(u32::from_le_bytes(bytes)) } + fn read_i32(&mut self) -> Result { + if self.pc + 4 > self.program.rom.len() { + return Err("Unexpected end of ROM".into()); + } + let bytes = [ + self.program.rom[self.pc], + self.program.rom[self.pc + 1], + self.program.rom[self.pc + 2], + self.program.rom[self.pc + 3], + ]; + self.pc += 4; + Ok(i32::from_le_bytes(bytes)) + } + fn read_i64(&mut self) -> Result { if self.pc + 8 > self.program.rom.len() { return Err("Unexpected end of ROM".into()); @@ -661,7 +771,7 @@ mod tests { let mut hw = MockHardware; vm.step(&mut native, &mut hw).unwrap(); - assert_eq!(vm.peek().unwrap(), &Value::Integer(42)); + assert_eq!(vm.peek().unwrap(), &Value::Int64(42)); } #[test] @@ -814,7 +924,7 @@ mod tests { vm2.step(&mut native, &mut hw).unwrap(); // RET assert_eq!(vm2.operand_stack.len(), 1); - assert_eq!(vm2.pop().unwrap(), Value::Integer(123)); + assert_eq!(vm2.pop().unwrap(), Value::Int64(123)); } #[test] @@ -866,16 +976,12 @@ mod tests { vm.step(&mut native, &mut hw).unwrap(); // PopScope 2 assert_eq!(vm.scope_stack.len(), 1); assert_eq!(vm.operand_stack.len(), 2); - assert_eq!(vm.operand_stack.last().unwrap(), &Value::Integer(2)); + assert_eq!(vm.operand_stack.last().unwrap(), &Value::Int64(2)); vm.step(&mut native, &mut hw).unwrap(); // PopScope 1 assert_eq!(vm.scope_stack.len(), 0); assert_eq!(vm.operand_stack.len(), 1); - assert_eq!(vm.operand_stack.last().unwrap(), &Value::Integer(1)); - } - - fn hw_to_mut(hw: &mut MockHardware) -> &mut dyn HardwareBridge { - hw + assert_eq!(vm.operand_stack.last().unwrap(), &Value::Int64(1)); } #[test] @@ -929,12 +1035,149 @@ mod tests { // Then it pushes return value (300). // So the stack should have [100, 300]. assert_eq!(vm.operand_stack.len(), 2); - assert_eq!(vm.operand_stack[0], Value::Integer(100)); - assert_eq!(vm.operand_stack[1], Value::Integer(300)); + assert_eq!(vm.operand_stack[0], Value::Int64(100)); + assert_eq!(vm.operand_stack[1], Value::Int64(300)); // Check if scope_stack was leaked (it currently would be if we don't clear it on RET) // The PR doesn't explicitly say RET should clear scope_stack, but it's good practice. // "Não mexe em scopes intermediários (eles devem já ter sido fechados)" // If they were closed, scope_stack would be empty for this frame. } + + #[test] + fn test_push_i32() { + let mut rom = Vec::new(); + rom.extend_from_slice(&(OpCode::PushI32 as u16).to_le_bytes()); + rom.extend_from_slice(&42i32.to_le_bytes()); + rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); + + let mut vm = VirtualMachine::new(rom, vec![]); + let mut native = MockNative; + let mut hw = MockHardware; + + vm.step(&mut native, &mut hw).unwrap(); + assert_eq!(vm.peek().unwrap(), &Value::Int32(42)); + } + + #[test] + fn test_bitwise_promotion() { + let mut native = MockNative; + let mut hw = MockHardware; + + // i32 & i32 -> i32 + let mut rom = Vec::new(); + rom.extend_from_slice(&(OpCode::PushI32 as u16).to_le_bytes()); + rom.extend_from_slice(&0xF0i32.to_le_bytes()); + rom.extend_from_slice(&(OpCode::PushI32 as u16).to_le_bytes()); + rom.extend_from_slice(&0x0Fi32.to_le_bytes()); + rom.extend_from_slice(&(OpCode::BitAnd as u16).to_le_bytes()); + rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); + + let mut vm = VirtualMachine::new(rom, vec![]); + vm.step(&mut native, &mut hw).unwrap(); + vm.step(&mut native, &mut hw).unwrap(); + vm.step(&mut native, &mut hw).unwrap(); + assert_eq!(vm.pop().unwrap(), Value::Int32(0)); + + // i32 | i64 -> i64 + let mut rom = Vec::new(); + rom.extend_from_slice(&(OpCode::PushI32 as u16).to_le_bytes()); + rom.extend_from_slice(&0xF0i32.to_le_bytes()); + rom.extend_from_slice(&(OpCode::PushI64 as u16).to_le_bytes()); + rom.extend_from_slice(&0x0Fi64.to_le_bytes()); + rom.extend_from_slice(&(OpCode::BitOr as u16).to_le_bytes()); + rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); + + let mut vm = VirtualMachine::new(rom, vec![]); + vm.step(&mut native, &mut hw).unwrap(); + vm.step(&mut native, &mut hw).unwrap(); + vm.step(&mut native, &mut hw).unwrap(); + assert_eq!(vm.pop().unwrap(), Value::Int64(0xFF)); + } + + #[test] + fn test_comparisons_lte_gte() { + let mut native = MockNative; + let mut hw = MockHardware; + + // 10 <= 20 (true) + let mut rom = Vec::new(); + rom.extend_from_slice(&(OpCode::PushI32 as u16).to_le_bytes()); + rom.extend_from_slice(&10i32.to_le_bytes()); + rom.extend_from_slice(&(OpCode::PushI32 as u16).to_le_bytes()); + rom.extend_from_slice(&20i32.to_le_bytes()); + rom.extend_from_slice(&(OpCode::Lte as u16).to_le_bytes()); + rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); + + let mut vm = VirtualMachine::new(rom, vec![]); + vm.step(&mut native, &mut hw).unwrap(); + vm.step(&mut native, &mut hw).unwrap(); + vm.step(&mut native, &mut hw).unwrap(); + assert_eq!(vm.pop().unwrap(), Value::Boolean(true)); + + // 20 >= 20 (true) + let mut rom = Vec::new(); + rom.extend_from_slice(&(OpCode::PushI32 as u16).to_le_bytes()); + rom.extend_from_slice(&20i32.to_le_bytes()); + rom.extend_from_slice(&(OpCode::PushI32 as u16).to_le_bytes()); + rom.extend_from_slice(&20i32.to_le_bytes()); + rom.extend_from_slice(&(OpCode::Gte as u16).to_le_bytes()); + rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); + + let mut vm = VirtualMachine::new(rom, vec![]); + vm.step(&mut native, &mut hw).unwrap(); + vm.step(&mut native, &mut hw).unwrap(); + vm.step(&mut native, &mut hw).unwrap(); + assert_eq!(vm.pop().unwrap(), Value::Boolean(true)); + } + + #[test] + fn test_negation() { + let mut native = MockNative; + let mut hw = MockHardware; + + let mut rom = Vec::new(); + rom.extend_from_slice(&(OpCode::PushI32 as u16).to_le_bytes()); + rom.extend_from_slice(&42i32.to_le_bytes()); + rom.extend_from_slice(&(OpCode::Neg as u16).to_le_bytes()); + rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); + + let mut vm = VirtualMachine::new(rom, vec![]); + vm.step(&mut native, &mut hw).unwrap(); + vm.step(&mut native, &mut hw).unwrap(); + assert_eq!(vm.pop().unwrap(), Value::Int32(-42)); + } + + #[test] + fn test_jmp_if_true() { + let mut native = MockNative; + let mut hw = MockHardware; + + // Corrected Calculations: + // 0-1: PushBool + // 2: 1 (u8) + // 3-4: JmpIfTrue + // 5-8: addr (u32) + // 9-10: Halt (Offset 9) + // 11-12: PushI32 (Offset 11) + // 13-16: 100 (i32) + // 17-18: Halt + + let mut rom = Vec::new(); + rom.extend_from_slice(&(OpCode::PushBool as u16).to_le_bytes()); + rom.push(1); + rom.extend_from_slice(&(OpCode::JmpIfTrue as u16).to_le_bytes()); + rom.extend_from_slice(&(11u32).to_le_bytes()); + rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); // Offset 9 + rom.extend_from_slice(&(OpCode::PushI32 as u16).to_le_bytes()); // Offset 11 + rom.extend_from_slice(&100i32.to_le_bytes()); + rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes()); + + let mut vm = VirtualMachine::new(rom, vec![]); + vm.step(&mut native, &mut hw).unwrap(); // PushBool + vm.step(&mut native, &mut hw).unwrap(); // JmpIfTrue + assert_eq!(vm.pc, 11); + vm.step(&mut native, &mut hw).unwrap(); // PushI32 + assert_eq!(vm.pop().unwrap(), Value::Int32(100)); + } } diff --git a/docs/specs/topics/chapter-2.md b/docs/specs/topics/chapter-2.md index b1226cfc..614fba58 100644 --- a/docs/specs/topics/chapter-2.md +++ b/docs/specs/topics/chapter-2.md @@ -61,13 +61,21 @@ Properties: | Type | Description | | --------- | ------------------------- | -| `integer` | 64-bit signed integer | +| `int32` | 32-bit signed integer | +| `int64` | 64-bit signed integer | | `float` | 64-bit floating point | | `boolean` | true/false | | `string` | immutable UTF-8 | | `null` | absence of value | | `ref` | heap reference | +### 3.1 Numeric Promotion +The VM promotes types automatically during operations: +* `int32` + `int32` → `int32` +* `int32` + `int64` → `int64` +* `int` + `float` → `float` +* Bitwise operations promote `int32` to `int64` if any operand is `int64`. + Do not exist: * magic coercions @@ -123,6 +131,7 @@ State: | `HALT` | 1 | Terminates execution | | `JMP addr` | 2 | Unconditional jump | | `JMP_IF_FALSE addr` | 3 | Jumps if top is false | +| `JMP_IF_TRUE addr` | 3 | Jumps if top is true | --- @@ -134,6 +143,10 @@ State: | `POP` | 1 | Removes top | | `DUP` | 1 | Duplicates top | | `SWAP` | 1 | Swaps two tops | +| `PUSH_I32 v` | 2 | Pushes 32-bit int | +| `PUSH_I64 v` | 2 | Pushes 64-bit int | +| `PUSH_F64 v` | 2 | Pushes 64-bit float | +| `PUSH_BOOL v` | 2 | Pushes boolean | --- @@ -156,9 +169,17 @@ State: | `NEQ` | 2 | | `LT` | 2 | | `GT` | 2 | +| `LTE` | 2 | +| `GTE` | 2 | | `AND` | 2 | | `OR` | 2 | | `NOT` | 1 | +| `BIT_AND` | 2 | +| `BIT_OR` | 2 | +| `BIT_XOR` | 2 | +| `SHL` | 2 | +| `SHR` | 2 | +| `NEG` | 1 | ---