1use serde::{Deserialize, Serialize};
2use num_derive::{FromPrimitive, ToPrimitive};
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, FromPrimitive, ToPrimitive)]
5#[repr(u8)]
6pub enum OpCode {
7 Push = 0x01,
9 Pop = 0x02,
10 Dup = 0x03,
11 Swap = 0x04,
12
13 Add = 0x10,
15 Sub = 0x11,
16 Mul = 0x12,
17 Div = 0x13,
18 Mod = 0x14,
19 Neg = 0x15,
20
21 Eq = 0x20,
23 Ne = 0x21,
24 Lt = 0x22,
25 Le = 0x23,
26 Gt = 0x24,
27 Ge = 0x25,
28
29 And = 0x30,
31 Or = 0x31,
32 Not = 0x32,
33
34 Jump = 0x40,
36 JumpIf = 0x41,
37 JumpIfNot = 0x42,
38 Call = 0x43,
39 Return = 0x44,
40
41 LoadLocal = 0x50,
43 StoreLocal = 0x51,
44 LoadGlobal = 0x52,
45 StoreGlobal = 0x53,
46
47 BuildList = 0x60,
49 BuildMap = 0x61,
50 Index = 0x62,
51 SetIndex = 0x63,
52
53 Print = 0x70,
55 Halt = 0xFF,
56}
57
58#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
59pub enum Value {
60 Null,
61 Bool(bool),
62 Int(i64),
63 Float(f64),
64 String(String),
65 List(Vec<Value>),
66 Map(Vec<(String, Value)>),
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct Instruction {
71 pub opcode: OpCode,
72 pub operand: Option<Value>,
73}
74
75impl Instruction {
76 pub fn new(opcode: OpCode) -> Self {
77 Self { opcode, operand: None }
78 }
79
80 pub fn with_operand(opcode: OpCode, operand: Value) -> Self {
81 Self { opcode, operand: Some(operand) }
82 }
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct BytecodeModule {
87 pub instructions: Vec<Instruction>,
88 pub constants: Vec<Value>,
89 pub entry_point: usize,
90}
91
92impl BytecodeModule {
93 pub fn new() -> Self {
94 Self {
95 instructions: Vec::new(),
96 constants: Vec::new(),
97 entry_point: 0,
98 }
99 }
100
101 pub fn add_instruction(&mut self, instruction: Instruction) -> usize {
102 let index = self.instructions.len();
103 self.instructions.push(instruction);
104 index
105 }
106
107 pub fn add_constant(&mut self, value: Value) -> usize {
108 let index = self.constants.len();
109 self.constants.push(value);
110 index
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 #[test]
119 fn test_opcode_encoding() {
120 assert_eq!(OpCode::Push as u8, 0x01);
121 assert_eq!(OpCode::Add as u8, 0x10);
122 assert_eq!(OpCode::Halt as u8, 0xFF);
123 }
124
125 #[test]
126 fn test_instruction_creation() {
127 let inst = Instruction::new(OpCode::Add);
128 assert_eq!(inst.opcode, OpCode::Add);
129 assert_eq!(inst.operand, None);
130
131 let inst_with_val = Instruction::with_operand(OpCode::Push, Value::Int(42));
132 assert_eq!(inst_with_val.opcode, OpCode::Push);
133 assert_eq!(inst_with_val.operand, Some(Value::Int(42)));
134 }
135
136 #[test]
137 fn test_bytecode_module() {
138 let mut module = BytecodeModule::new();
139
140 let const_idx = module.add_constant(Value::Int(100));
141 assert_eq!(const_idx, 0);
142
143 let inst_idx = module.add_instruction(Instruction::new(OpCode::Push));
144 assert_eq!(inst_idx, 0);
145
146 assert_eq!(module.instructions.len(), 1);
147 assert_eq!(module.constants.len(), 1);
148 }
149}