use kabel::{mismatched_types, runtime_error::KabelRuntimeError, wrong_type};
use crate::string::ObjString;
pub struct VM {
ip: usize,
chunk: Vec<u8>,
stack: Vec<Value>,
pool: Vec<Value>,
lines: Vec<(usize, usize)>, // line #, repeats number of instructions
text: Vec<String>
}
impl VM {
pub fn new(bytecode: Vec<u8>, lines: Vec<(usize, usize)>, pool: Vec<Value>, text: String) -> Self {
Self {
ip: 0,
chunk: bytecode,
stack: Vec::new(),
pool,
lines,
text: text.lines().map(|s| s.to_string()).collect(),
}
}
pub fn run(&mut self) -> Result<(), KabelRuntimeError> {
use Value::*;
while self.ip < self.chunk.len() {
match self.read() {
0x00 => { // CONSTANT
let byte = self.read() as usize;
self.stack.push(self.pool[byte])
}
0x01 => { // 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 + v2));
v1.free();
v2.free();
},
(Bool(_v1), Bool(_v2)) => {
return Err(wrong_type!(self, "Cannot add booleans"))
}
(v1, v2) => {
if let Str(v1) = v1 {
v1.free();
}
if let Str(v2) = v2 {
v2.free();
}
return Err(mismatched_types!(self, "Mismatched types: {} and {}", v1.type_str(), v2.type_str()))
}
}
}
0x02 => { // SUB
match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
(Num(v1), Num(v2)) => self.stack.push(Num(v1 - v2)),
(Str(v1), Str(v2)) => {
v1.free(); v2.free();
return Err(wrong_type!(self, "Cannot subtract strings"))
},
(Bool(_v1), Bool(_v2)) => {
return Err(wrong_type!(self, "Cannot subtract booleans"))
}
(v1, v2) => {
if let Str(v1) = v1 {
v1.free();
}
if let Str(v2) = v2 {
v2.free();
}
return Err(mismatched_types!(self, "Mismatched types: {} and {}", v1.type_str(), v2.type_str()))
}
}
}
0xFE => { // PRINT
let value = self.stack.pop().unwrap();
match value {
Num(v) => println!("{}", v),
Str(v) => println!("{}", v.to_string()),
Bool(v) => println!("{}", v),
}
}
_ => {}
}
}
Ok(())
}
pub fn read(&mut self) -> u8 {
let byte = self.chunk[self.ip];
self.ip += 1;
byte
}
pub fn find_line(&mut self) -> usize { // returns line # at ip
let mut line_ip = 0;
for (line, rep) in self.lines.clone() {
if line_ip + rep > self.ip {
return line;
}
line_ip += rep;
}
panic!("Something went wrong in finding line for error")
}
}
impl Drop for VM {
fn drop(&mut self) {
for element in self.stack.clone() {
match element {
Value::Str(v1) => v1.free(),
_ => {}
}
}
for element in self.pool.clone() {
match element {
Value::Str(v1) => v1.free(),
_ => {}
}
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum Value {
Num(f32), Str(ObjString), Bool(bool),
}
impl Value {
pub fn type_str(&self) -> String {
match self {
Value::Num(_) => "number".to_string(),
Value::Str(_) => "string".to_string(),
Value::Bool(_) => "bool".to_string(),
}
}
}