From 61c42ae790f4103c03fbe2b0ce4de8f05cae338d Mon Sep 17 00:00:00 2001 From: numzero Date: Sun, 20 Apr 2025 13:40:03 +0300 Subject: [PATCH] Merge Value and ValueInner --- src/twopass.rs | 77 ++++++++++++++++++++++++-------------------------- src/types.rs | 30 ++++++++++++-------- 2 files changed, 56 insertions(+), 51 deletions(-) diff --git a/src/twopass.rs b/src/twopass.rs index 4107818..9883886 100644 --- a/src/twopass.rs +++ b/src/twopass.rs @@ -313,10 +313,10 @@ trait Eval { impl Eval for ast::Constant { fn eval(&self, _ctx: &RunContext) -> types::Value { match self { - ast::Constant::Nil => None, - ast::Constant::Boolean(value) => Some(types::ValueInner::Boolean(*value)), - ast::Constant::Number(value) => Some(types::ValueInner::Number(*value)), - ast::Constant::String(value) => Some(types::ValueInner::String(value.clone())), + ast::Constant::Nil => types::Value::Nil, + ast::Constant::Boolean(value) => types::Value::Boolean(*value), + ast::Constant::Number(value) => types::Value::Number(*value), + ast::Constant::String(value) => types::Value::String(value.clone()), } } } @@ -326,9 +326,9 @@ impl Eval for Location { match self { Location::Variable { id } => ctx.var(*id).get(), Location::Field { table, index } => { - let table = table.eval(ctx).expect("attempt to index a nil value"); - let index = index.eval(ctx).expect("attempt to index with a nil value"); - let types::ValueInner::Table(table) = table else { + let table = table.eval(ctx); + let index = index.eval(ctx); + let types::Value::Table(table) = table else { panic!("attempt to index a non-table"); }; table.get(index) @@ -341,7 +341,7 @@ impl Eval for Call { fn eval(&self, ctx: &RunContext) -> types::Value { let callee = self.callee.eval(ctx); let args: Vec<_> = self.args.iter().map(|arg| arg.eval(ctx)).collect(); - let Some(types::ValueInner::Function(f)) = callee else { + let types::Value::Function(f) = callee else { panic!("attempt to call a non-function"); }; f.call(&args) @@ -356,9 +356,7 @@ impl Eval for Function { .map(|(_name, id)| ctx.var(*id)) .collect(); let self_ = self.clone(); // TODO reference, somehow - Some( - types::Function::new(self.name.clone(), move |args| self_.call(&upvalues, args)).into(), - ) + types::Function::new(self.name.clone(), move |args| self_.call(&upvalues, args)).into() } } @@ -366,11 +364,11 @@ impl Eval for Table { fn eval(&self, ctx: &RunContext) -> types::Value { let table = types::Table::default(); for field in &self.fields { - let key = field.key.eval(ctx).expect("attempt to set a nil index"); + let key = field.key.eval(ctx); let value = field.value.eval(ctx); table.set(key, value); } - Some(table.into()) + table.into() } } @@ -394,9 +392,9 @@ impl Eval for Statement { match target { Location::Variable { id } => ctx.var(*id).set(source), Location::Field { table, index } => { - let table = table.eval(ctx).expect("attempt to index a nil value"); - let index = index.eval(ctx).expect("attempt to index with a nil value"); - let types::ValueInner::Table(table) = table else { + let table = table.eval(ctx); + let index = index.eval(ctx); + let types::Value::Table(table) = table else { panic!("attempt to index a non-table"); }; table.set(index, source) @@ -407,7 +405,7 @@ impl Eval for Statement { call.eval(ctx); } }; - None + types::Value::Nil } } @@ -421,7 +419,9 @@ impl Function { assert_eq!(args.len(), self.args.len()); let ctx = RunContext { args: args.iter().map(new_var).collect(), - locals: (0..self.locals.len()).map(|_| new_var(&None)).collect(), + locals: (0..self.locals.len()) + .map(|_| new_var(&types::Value::Nil)) + .collect(), upvalues: upvalues.to_vec(), }; for statement in &self.body { @@ -443,29 +443,26 @@ impl OpenFunction { #[cfg(test)] mod tests { - use crate::{ - ast::*, - types::{Value, ValueInner}, - }; + use crate::{ast::*, types::Nil, types::Value}; use super::build; fn get(table: &Value, index: &str) -> Value { - let Some(ValueInner::Table(table)) = table else { + let Value::Table(table) = table else { panic!("a table expected"); }; table.get(index.into()) } fn set(table: &Value, index: &str, value: Value) { - let Some(ValueInner::Table(table)) = table else { + let Value::Table(table) = table else { panic!("a table expected"); }; table.set(index.into(), value); } fn call(func: &Value, args: &[Value]) -> Value { - let Some(ValueInner::Function(func)) = func else { + let Value::Function(func) = func else { panic!("a function expected"); }; func.call(args) @@ -494,9 +491,9 @@ mod tests { }; let env = crate::types::Table::default(); let testee = build(&testee); - let ret = testee.call(Some(env.clone().into()), &[]); - assert_eq!(ret, Some(3.into())); - assert_eq!(env.get("x".into()), Some(1.into())); + let ret = testee.call(env.clone().into(), &[]); + assert_eq!(ret, 3.into()); + assert_eq!(env.get("x".into()), 1.into()); } #[test] @@ -526,9 +523,9 @@ mod tests { }; let env = crate::types::Table::default(); let testee = build(&testee); - let ret = testee.call(Some(env.clone().into()), &[]); - assert_eq!(ret, Some(42.into())); - assert_eq!(env.get("x".into()), Some(666.into())); + let ret = testee.call(env.clone().into(), &[]); + assert_eq!(ret, 42.into()); + assert_eq!(env.get("x".into()), 666.into()); } #[test] @@ -545,8 +542,8 @@ mod tests { }))), }; let remember = build(&remember); - let the_answerer = remember.call(None, &[Some(42.into())]); - assert_eq!(call(&the_answerer, &[]), Some(42.into())); + let the_answerer = remember.call(Nil, &[42.into()]); + assert_eq!(call(&the_answerer, &[]), 42.into()); } #[test] @@ -586,13 +583,13 @@ mod tests { ))), }; let make_cell = build(&make_cell); - let cell1 = make_cell.call(None, &[]); - let cell2 = make_cell.call(None, &[]); - assert_eq!(call(&get(&cell1, "get"), &[]), None); - call(&get(&cell1, "set"), &[Some(42.into())]); - call(&get(&cell2, "set"), &[Some("foobar".into())]); - assert_eq!(call(&get(&cell1, "get"), &[]), Some(42.into())); + let cell1 = make_cell.call(Nil, &[]); + let cell2 = make_cell.call(Nil, &[]); + assert_eq!(call(&get(&cell1, "get"), &[]), Nil); + call(&get(&cell1, "set"), &[42.into()]); + call(&get(&cell2, "set"), &["foobar".into()]); + assert_eq!(call(&get(&cell1, "get"), &[]), 42.into()); call(&get(&cell1, "set"), &[call(&get(&cell2, "get"), &[])]); - assert_eq!(call(&get(&cell1, "get"), &[]), Some("foobar".into())); + assert_eq!(call(&get(&cell1, "get"), &[]), "foobar".into()); } } diff --git a/src/types.rs b/src/types.rs index 1c1b2dd..e262be9 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,7 +1,9 @@ use std::{cell::RefCell, collections::HashMap, fmt::Debug, hash::Hash, rc::Rc}; -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum ValueInner { +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] +pub enum Value { + #[default] + Nil, Boolean(bool), Number(i64), String(String), @@ -9,16 +11,18 @@ pub enum ValueInner { Function(Function), } +pub use Value::Nil; + macro_rules! impl_from { ($as: ident, $from: ty) => { - impl From<$from> for ValueInner { + impl From<$from> for Value { fn from(value: $from) -> Self { Self::$as(value.into()) } } }; (try $as: ident, $from: ty) => { - impl TryFrom<$from> for ValueInner { + impl TryFrom<$from> for Value { type Error = ::std::num::TryFromIntError; fn try_from(value: $from) -> Result { Ok(Self::$as(value.try_into()?)) @@ -39,11 +43,9 @@ impl_from!(String, &str); impl_from!(Table, Table); impl_from!(Function, Function); -pub type Value = Option; - #[derive(Debug, Clone, Default)] struct TableInner { - content: HashMap, + content: HashMap, } #[derive(Debug, Clone, Default)] @@ -64,13 +66,19 @@ impl Hash for Table { } impl Table { - pub fn get(&self, key: ValueInner) -> Value { - self.0.borrow().content.get(&key).cloned() + pub fn get(&self, key: Value) -> Value { + self.0 + .borrow() + .content + .get(&key) + .cloned() + .unwrap_or_default() } - pub fn set(&self, key: ValueInner, value: Value) { + pub fn set(&self, key: Value, value: Value) { + assert!(key != Nil); let mut this = self.0.borrow_mut(); - if let Some(value) = value { + if value != Nil { this.content.insert(key, value); } else { this.content.remove(&key);