use crate::vm_error; use crate::{ runtime_error::{KabelRuntimeError, RuntimeErrorKind}, vm_boolean_comparison, vm_boolean_equality, }; #[derive(Debug, Clone)] pub struct Unit { pub code: Vec, pub pool: Vec, pub lines: Vec<(usize, usize)>, // line #, repeats number of instructions } impl Unit { pub fn new(code: Vec, pool: Vec, lines: Vec<(usize, usize)>) -> Self { Self { code, pool, lines } } pub fn new_empty() -> Self { Self { code: Vec::new(), pool: Vec::new(), lines: Vec::new(), } } } #[derive(Clone)] pub struct VM { pub ip: usize, pub unit_ptr: usize, pub call_stack: Vec<(usize, usize, usize)>, // (unit_ptr, ip, stack_offset) pub units: Vec, pub stack: Vec, pub variables: Vec, pub variables_offset: usize, text: Vec, } impl VM { pub fn new( bytecode: Vec, lines: Vec<(usize, usize)>, pool: Vec, text: String, ) -> Self { Self { ip: 0, unit_ptr: 0, call_stack: vec![(0, 0, 0)], units: vec![Unit::new(bytecode, pool, lines)], stack: Vec::new(), variables: Vec::new(), variables_offset: 0, text: text.lines().map(|s| s.to_string()).collect::>(), } } pub fn run(&mut self, output: &mut String) -> Result<(), KabelRuntimeError> { use Value::*; while self.ip < self.units[self.unit_ptr].code.len() { match self.read() { 0x00 => { // LOAD let byte = self.read() as usize; println!("ip: {}", self.ip); let value = self.units[self.unit_ptr].pool[byte].clone(); self.stack.push(value); } 0x01 => { // VAR let ptr = self.read() as usize; let value = self.variables[ptr + self.variables_offset].clone(); self.stack.push(value); } 0x02 => { // REF let ptr = self.read() as usize; self.stack.push(Ref(ptr)); } 0x03 => { // ASN let value = self.stack.pop().unwrap(); let ptr = self.read(); let offset = self.call_stack.last().expect("var call stack last").2; self.variables[ptr as usize + offset] = value.clone(); self.stack.push(value); } 0x04 => { // ASNARR let listref = self.stack.pop().unwrap(); let index = self.stack.pop().unwrap(); let value = self.stack.pop().unwrap(); if let Value::Num(index) = index { if index.fract() != 0.0 { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Subscript index must be an integer but found {}", index )); } let index = index as usize; if let Value::Ref(listref) = listref { let list = &mut self.variables[listref]; if let Value::List(ref mut list) = list { let len = list.len(); if index > len { return Err(vm_error!( self, RuntimeErrorKind::ArrayOutOfBounds, "List length is {} but found index {}", len, index )); } println!("index: {}", index); list[index] = value.clone(); self.stack.push(value); } else { let type_str = list.type_str(); return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Tried to subscript non-list type {}", type_str )); } } else { panic!("Something went wrong in kabel, listref was not a Ref"); } } else { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Tried to use non-integer type {} to subscript list", index.type_str() )); } } 0x05 => { // DECL let value = self.stack.pop().unwrap(); self.variables.push(value); } 0x06 => { // ADD match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) { (Num(v1), Num(v2)) => self.stack.push(Num(v1 + v2)), (Str(v1), Str(v2)) => { self.stack.push(Str(v1.clone() + &v2)); } (Bool(_v1), Bool(_v2)) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Cannot add booleans" )) } (v1, v2) => { return Err(vm_error!( self, RuntimeErrorKind::MismatchedTypes, "Mismatched types: {} and {}", v1.type_str(), v2.type_str() )) } } } 0x07 => { // SUB match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) { (Num(v1), Num(v2)) => self.stack.push(Num(v1 - v2)), (Str(_v1), Str(_v2)) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Cannot subtract strings" )) } (Bool(_v1), Bool(_v2)) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Cannot subtract booleans" )) } (v1, v2) => { return Err(vm_error!( self, RuntimeErrorKind::MismatchedTypes, "Mismatched types: {} and {}", v1.type_str(), v2.type_str() )) } } } 0x08 => { // MUL match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) { (Num(v1), Num(v2)) => self.stack.push(Num(v1 * v2)), (Str(v1), Num(v2)) => { if v2.fract() == 0.0 { self.stack.push(Str(v1.repeat(v2 as usize))); } else { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Number must be an integer" )); } } (Str(_v1), Str(_v2)) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Cannot multiply strings" )) } (Bool(_v1), Bool(_v2)) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Cannot multiply booleans" )) } (v1, v2) => { return Err(vm_error!( self, RuntimeErrorKind::MismatchedTypes, "Mismatched types: {} and {}", v1.type_str(), v2.type_str() )) } } } 0x09 => { // DIV match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) { (Num(v1), Num(v2)) => self.stack.push(Num(v1 / v2)), (Str(_v1), Str(_v2)) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Cannot divide strings" )) } (Bool(_v1), Bool(_v2)) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Cannot divide booleans" )) } (v1, v2) => { return Err(vm_error!( self, RuntimeErrorKind::MismatchedTypes, "Mismatched types: {} and {}", v1.type_str(), v2.type_str() )) } } } 0x0A => { // MOD match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) { (Num(v1), Num(v2)) => self.stack.push(Num(v1 % v2)), (Str(_v1), Str(_v2)) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Cannot perform modulus on strings" )) } (Bool(_v1), Bool(_v2)) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Cannot perform modulus on booleans" )) } (v1, v2) => { return Err(vm_error!( self, RuntimeErrorKind::MismatchedTypes, "Mismatched types: {} and {}", v1.type_str(), v2.type_str() )) } } } 0x0B => { // BITAND match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) { (Num(v1), Num(v2)) => { if v1.fract() != 0.0 { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Cannot perform bitwise AND on {}", v1 )); } if v2.fract() != 0.0 { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Cannot perform bitwise AND on {}", v2 )); } self.stack.push(Num((v1 as u32 & v2 as u32) as f32)) } (Str(_v1), Str(_v2)) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Cannot perform bitwise AND on strings" )) } (Bool(_v1), Bool(_v2)) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Cannot perform bitwise AND on booleans" )) } (v1, v2) => { return Err(vm_error!( self, RuntimeErrorKind::MismatchedTypes, "Mismatched types: {} and {}", v1.type_str(), v2.type_str() )) } } } 0x0C => { // BITXOR match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) { (Num(v1), Num(v2)) => { if v1.fract() != 0.0 { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Cannot perform bitwise XOR on {}", v1 )); } if v2.fract() != 0.0 { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Cannot perform bitwise XOR on {}", v2 )); } self.stack.push(Num((v1 as u32 ^ v2 as u32) as f32)) } (Str(_v1), Str(_v2)) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Cannot perform bitwise XOR on strings" )) } (Bool(_v1), Bool(_v2)) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Cannot perform bitwise XOR on booleans" )) } (v1, v2) => { return Err(vm_error!( self, RuntimeErrorKind::MismatchedTypes, "Mismatched types: {} and {}", v1.type_str(), v2.type_str() )) } } } 0x0D => { // BITOR match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) { (Num(v1), Num(v2)) => { if v1.fract() != 0.0 { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Cannot perform bitwise OR on {}", v1 )); } if v2.fract() != 0.0 { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Cannot perform bitwise OR on {}", v2 )); } self.stack.push(Num((v1 as u32 | v2 as u32) as f32)) } (Str(_v1), Str(_v2)) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Cannot perform bitwise OR on strings" )) } (Bool(_v1), Bool(_v2)) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Cannot perform bitwise OR on booleans" )) } (v1, v2) => { return Err(vm_error!( self, RuntimeErrorKind::MismatchedTypes, "Mismatched types: {} and {}", v1.type_str(), v2.type_str() )) } } } // EQ 0x0E => vm_boolean_equality!(self, ==), // NE 0x0F => vm_boolean_equality!(self, !=), // GR 0x10 => vm_boolean_comparison!(self, >), // GE 0x11 => vm_boolean_comparison!(self, >=), // LS 0x12 => vm_boolean_comparison!(self, <), // LE 0x13 => vm_boolean_comparison!(self, <=), // OR 0x14 => match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) { (Num(_v1), Num(_v2)) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Tried to perform \"||\" on non-boolean numbers" )) } (Str(_v1), Str(_v2)) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Tried to perform \"||\" on non-boolean strings" )) } (Bool(v1), Bool(v2)) => self.stack.push(Bool(v1 || v2)), (Null, _) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Tried to perform \"||\" on non-boolean value null" )) } (_, Null) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Tried to perform \"||\" on non-boolean value null" )) } (v1, v2) => { return Err(vm_error!( self, RuntimeErrorKind::MismatchedTypes, "Mismatched types: {} and {}", v1.type_str(), v2.type_str() )) } }, // AND 0x15 => match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) { (Num(_v1), Num(_v2)) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Tried to perform \"&&\" on non-boolean numbers" )) } (Str(_v1), Str(_v2)) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Tried to perform \"&&\" on non-boolean strings" )) } (Bool(v1), Bool(v2)) => self.stack.push(Bool(v1 && v2)), (Null, _) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Tried to perform \"&&\" on non-boolean value null" )) } (_, Null) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Tried to perform \"&&\" on non-boolean value null" )) } (v1, v2) => { return Err(vm_error!( self, RuntimeErrorKind::MismatchedTypes, "Mismatched types: {} and {}", v1.type_str(), v2.type_str() )) } }, // NOT 0x16 => match self.stack.pop().unwrap() { Null => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Tried to perform \"!\" on non-boolean value null" )) } Num(_v1) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Tried to perform \"!\" on non-boolean number" )) } Str(_v1) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Tried to perform \"!\" on non-boolean string" )) } Bool(v1) => self.stack.push(Bool(!v1)), List(_v1) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Tried to perform \"!\" on non-boolean list" )) } Fun(_v1) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Tried to perform \"!\" on non-boolean function" )) } Ref(_v1) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Tried to perform \"!\" on non-boolean reference" )) } }, // NEG 0x17 => match self.stack.pop().unwrap() { Null => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Tried to perform \"-\" on non-number value null" )) } Num(v1) => self.stack.push(Num(-v1)), Str(_v1) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Tried to perform \"-\" on non-number string" )) } Bool(_v1) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Tried to perform \"-\" on non-number boolean" )) } List(_v1) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Tried to perform \"!\" on non-number list" )) } Fun(_v1) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Tried to perform \"-\" on non-number function" )) } Ref(_v1) => { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Tried to perform \"-\" on non-number reference" )) } }, // JMP 0x18 => { let loc = self.read_u16(); self.ip += loc as usize; } // JMP_UP 0x19 => { let loc = self.read_u16(); self.ip -= loc as usize; } // JNE 0x1A => { let condition = self.stack.pop().unwrap(); if let Value::Bool(condition) = condition { if !condition { let loc = self.read_u16(); self.ip += loc as usize; } else { self.read_u16(); } } else { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "if must have condition of type boolean" )); } } // CALL 0x1B => { let num_args = self.read(); let function = self.stack.pop().expect("Stack was empty in call"); if let Value::Fun(function) = function { if num_args as usize != function.arity { return Err(vm_error!( self, RuntimeErrorKind::IncorrectArity, "Function has {} arguments, {} provided", function.arity, num_args )); } self.variables_offset = self.stack.len() - num_args as usize; self.call_stack .push((self.unit_ptr, self.ip, self.variables_offset)); self.stack .insert(self.stack.len() - num_args as usize, Value::Fun(function)); self.ip = 0; self.unit_ptr = function.unit_ptr; } else { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Tried to call non-function type {}", function.type_str() )); } } // RET 0x1C => { let (unit_ptr, ip, variables_offset) = self.call_stack.pop().expect("Call stack empty on RET"); let ret = self.stack.pop().expect("Missing return value"); self.stack = self.stack[..variables_offset].to_vec(); self.variables_offset = self.call_stack.last().expect("call stack empty").2; self.unit_ptr = unit_ptr; self.ip = ip; // returned to code self.stack.push(ret); } // LIST 0x1D => { let len = self.read(); let list = self .stack .drain(self.stack.len() - len as usize..) .collect(); self.stack.push(Value::List(list)); } // SCR 0x1E => { let index = self.stack.pop().expect("stack empty on subscript index"); let list = self.stack.pop().expect("stack empty on subscript list"); if let Value::Num(index) = index { if index.fract() != 0.0 { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Subscript index must be an integer but found {}", index )); } let index = index as usize; if let Value::List(list) = list { if index > list.len() { return Err(vm_error!( self, RuntimeErrorKind::ArrayOutOfBounds, "List length is {} but found index {}", list.len(), index )); } self.stack.push(list[index].clone()); } else { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Tried to subscript non-list type {}", list.type_str() )); } } else { return Err(vm_error!( self, RuntimeErrorKind::WrongType, "Tried to use non-integer type {} to subscript list", index.type_str() )); } } 0xFD => { // POP let times = self.read(); for _ in 0..times { self.stack .pop() .unwrap_or_else(|| panic!("{}: Unable to pop stack", self.ip)); } } 0xFE => { // PRINT let value = self.stack.pop().unwrap(); /*match value { Null => *output += "null", Num(v) => *output += &v.to_string(), Str(v) => *output += &v, Bool(v) => *output += &v.to_string(), List(v) => { *output += "["; for value in v { *output += &format!("", value); } *output += "["; } Fun(v) => *output += &format!("", v.unit_ptr), }*/ *output += &value.to_string(); *output += "\n"; } _ => {} } } Ok(()) } pub fn read(&mut self) -> u8 { let byte = self.units[self.unit_ptr].code[self.ip]; self.ip += 1; byte } pub fn read_u16(&mut self) -> u16 { let byte_one = (self.units[self.unit_ptr].code[self.ip] as u16) << 0x08; self.ip += 1; let byte_two = self.units[self.unit_ptr].code[self.ip] as u16; self.ip += 1; byte_one | byte_two } pub fn find_line(&self) -> usize { // returns line # at ip let mut line_ip = 0; for (line, rep) in self.units[self.unit_ptr].lines.clone() { if line_ip + rep > self.ip { return line; } line_ip += rep; } panic!("Something went wrong in finding line for error") } } #[derive(Debug, Clone)] pub enum Value { Null, Num(f32), Str(String), Bool(bool), List(Vec), Fun(Function), Ref(usize), } impl Value { pub fn type_str(&self) -> String { match self { Value::Null => "null".to_string(), Value::Num(_) => "number".to_string(), Value::Str(_) => "string".to_string(), Value::Bool(_) => "bool".to_string(), Value::List(_) => "list".to_string(), Value::Fun(_) => "function".to_string(), Value::Ref(_) => "reference".to_string(), } } } impl ToString for Value { fn to_string(&self) -> String { use Value::*; match self { Null => "null".to_string(), Num(v) => v.to_string(), Str(v) => v.to_string(), Bool(v) => v.to_string(), List(v) => { let mut output = "".to_string(); output += "["; for value in >::clone(v).into_iter() { output += &value.to_string(); output += ","; } output += "]"; output } Fun(v) => format!("", v.unit_ptr), Ref(v) => format!("", v), } } } #[derive(Debug, Clone, Copy)] pub struct Function { pub unit_ptr: usize, pub arity: usize, }