diff --git a/src/main.rs b/src/main.rs index 94ac853..2a0abd2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,6 +30,10 @@ impl RustFunction for Max { _ => Ok(vec![vm::Value::Nil]), } } + + fn as_indexable(&self) -> String { + "std::max".to_owned() + } } #[derive(Debug, PartialEq, Eq)] pub struct Print; @@ -38,6 +42,10 @@ impl RustFunction for Print { println!("{:?}", parameters); Ok(Vec::new()) } + + fn as_indexable(&self) -> String { + "std::print".to_owned() + } } fn compile( diff --git a/src/vm.rs b/src/vm.rs index 1f37124..afc5a5d 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,6 +1,12 @@ use thiserror::Error; -use std::{cell::RefCell, collections::HashMap, fmt::Debug, hash::Hash, rc::Rc}; +use std::{ + cell::{RefCell, RefMut}, + collections::HashMap, + fmt::Debug, + hash::Hash, + rc::Rc, +}; use crate::{ ast::{BinaryOperator, LuaNumber, UnaryOperator}, @@ -43,6 +49,8 @@ pub enum Instruction { GetUpVal(u16, u16), /// U[B] := R(A) SetUpVal(u16, u16), + /// R(A)[R(B)] := R(C) + SetTable(u16, u16, u16), /// R(A) := {} NewTable(u16), /// R(A) := R(B) + R(C) @@ -83,6 +91,9 @@ impl Debug for Instruction { Instruction::GetGlobal(arg0, arg1) => write!(f, "GETGLOBAL {} {}", arg0, arg1), Instruction::GetUpVal(arg0, arg1) => write!(f, "GETUPVAL {} {}", arg0, arg1), Instruction::SetUpVal(arg0, arg1) => write!(f, "SETUPVAL {} {}", arg0, arg1), + Instruction::SetTable(arg0, arg1, arg2) => { + write!(f, "SETTABLE {} {} {}", arg0, arg1, arg2) + } Instruction::NewTable(arg0) => write!(f, "NEWTABLE {}", arg0), Instruction::Jmp(arg0) => write!(f, "JMP {}", arg0), Instruction::Test(arg0, arg1, arg2) => write!(f, "TEST {} {} {}", arg0, arg1, arg2), @@ -114,6 +125,8 @@ pub enum RuntimeError { TriedCallingNonFunction(Value), #[error("Global not found: {0:?}")] GlobalNotFound(Option), + #[error("Unable to index tables with {0:?}")] + InvalidTableIndex(Value), #[error("{0}")] Custom(String), } @@ -133,6 +146,21 @@ pub enum Value { Table(HashMap), } +impl Value { + pub fn as_indexable(self) -> Result { + match self { + Value::String(value) => Ok(IndexableValue::String(value)), + Value::Number(value) => Ok(IndexableValue::Number(value)), + Value::RustFunction(value) => { + Ok(IndexableValue::RustFunction(value.borrow().as_indexable())) + } + Value::Function(closure) => Ok(IndexableValue::Function(closure.prototype)), + Value::Nil => Err(RuntimeError::InvalidTableIndex(self)), + Value::Table(_) => Err(RuntimeError::InvalidTableIndex(self)), + } + } +} + #[derive(Debug, PartialEq, Eq, Hash, Clone)] pub enum IndexableValue { String(String), @@ -272,6 +300,7 @@ impl Debug for Value { pub trait RustFunction: Debug { fn execute(&self, parameters: Vec) -> Result, RuntimeError>; + fn as_indexable(&self) -> String; } #[derive(Debug, Clone)] @@ -494,6 +523,38 @@ impl ClosureRunner { .map(|v| v.borrow().clone()) .unwrap_or(Value::Nil); } + Instruction::SetTable(tablereg, indexreg, valuereg) => { + let table = self.stack.get(tablereg); + match table { + Some(value) => { + let mut table = value.borrow_mut(); + if let Value::Table(table) = &mut *table { + let index_value = self + .stack + .get(indexreg) + .map(|v| v.borrow().clone()) + .unwrap_or(Value::Nil) + .as_indexable()?; + let value = self + .stack + .get(valuereg) + .map(|v| v.borrow().clone()) + .unwrap_or(Value::Nil); + match value { + Value::Nil => { + table.remove(&index_value); + } + _ => { + table.insert(index_value, value); + } + } + } else { + todo!() + } + } + None => todo!(), + } + } Instruction::NewTable(reg) => { self.set_stack(*reg, Value::Table(HashMap::new())); }