Compare commits

...

3 Commits

Author SHA1 Message Date
4ecafff62f fmt? 2025-04-20 14:27:03 +03:00
8559220aa3 use Value 2025-04-20 13:42:57 +03:00
61c42ae790 Merge Value and ValueInner 2025-04-20 13:40:03 +03:00
2 changed files with 72 additions and 63 deletions

View File

@ -1,6 +1,9 @@
use std::{cell::RefCell, rc::Rc};
use crate::{ast, types};
use crate::{
ast,
types::{self, Nil, Value},
};
#[derive(Debug, Clone)]
pub struct OpenFunction(Function);
@ -273,14 +276,14 @@ pub fn build(code: &ast::Function) -> OpenFunction {
}
#[derive(Debug, Clone)]
struct Variable(RefCell<types::Value>);
struct Variable(RefCell<Value>);
impl Variable {
fn get(&self) -> types::Value {
fn get(&self) -> Value {
self.0.borrow().clone()
}
fn set(&self, value: types::Value) {
fn set(&self, value: Value) {
*self.0.borrow_mut() = value
}
}
@ -307,28 +310,28 @@ impl RunContext {
}
trait Eval {
fn eval(&self, ctx: &RunContext) -> types::Value;
fn eval(&self, ctx: &RunContext) -> Value;
}
impl Eval for ast::Constant {
fn eval(&self, _ctx: &RunContext) -> types::Value {
fn eval(&self, _ctx: &RunContext) -> 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 => Nil,
ast::Constant::Boolean(value) => Value::Boolean(*value),
ast::Constant::Number(value) => Value::Number(*value),
ast::Constant::String(value) => Value::String(value.clone()),
}
}
}
impl Eval for Location {
fn eval(&self, ctx: &RunContext) -> types::Value {
fn eval(&self, ctx: &RunContext) -> Value {
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 Value::Table(table) = table else {
panic!("attempt to index a non-table");
};
table.get(index)
@ -338,10 +341,10 @@ impl Eval for Location {
}
impl Eval for Call {
fn eval(&self, ctx: &RunContext) -> types::Value {
fn eval(&self, ctx: &RunContext) -> 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 Value::Function(f) = callee else {
panic!("attempt to call a non-function");
};
f.call(&args)
@ -349,33 +352,31 @@ impl Eval for Call {
}
impl Eval for Function {
fn eval(&self, ctx: &RunContext) -> types::Value {
fn eval(&self, ctx: &RunContext) -> Value {
let upvalues: Vec<_> = self
.upvalues
.iter()
.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()
}
}
impl Eval for Table {
fn eval(&self, ctx: &RunContext) -> types::Value {
fn eval(&self, ctx: &RunContext) -> 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()
}
}
impl Eval for Expression {
fn eval(&self, ctx: &RunContext) -> types::Value {
fn eval(&self, ctx: &RunContext) -> Value {
match self {
Expression::Constant(inner) => inner.eval(ctx),
Expression::Variable(inner) => inner.eval(ctx),
@ -387,16 +388,16 @@ impl Eval for Expression {
}
impl Eval for Statement {
fn eval(&self, ctx: &RunContext) -> types::Value {
fn eval(&self, ctx: &RunContext) -> Value {
match self {
Statement::Assign { target, source } => {
let source = source.eval(ctx);
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 Value::Table(table) = table else {
panic!("attempt to index a non-table");
};
table.set(index, source)
@ -407,21 +408,21 @@ impl Eval for Statement {
call.eval(ctx);
}
};
None
Nil
}
}
fn new_var(value: &types::Value) -> VariableRef {
fn new_var(value: &Value) -> VariableRef {
Rc::new(Variable(RefCell::new(value.clone())))
}
impl Function {
fn call(&self, upvalues: &[VariableRef], args: &[types::Value]) -> types::Value {
fn call(&self, upvalues: &[VariableRef], args: &[Value]) -> Value {
assert_eq!(upvalues.len(), self.upvalues.len());
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(&Nil)).collect(),
upvalues: upvalues.to_vec(),
};
for statement in &self.body {
@ -432,7 +433,7 @@ impl Function {
}
impl OpenFunction {
pub fn call(&self, env: types::Value, args: &[types::Value]) -> types::Value {
pub fn call(&self, env: Value, args: &[Value]) -> Value {
match self.0.upvalues.as_slice() {
[] => self.0.call(&[], args),
[up] if up.0 == "_ENV" => self.0.call(&[new_var(&env)], args),
@ -445,27 +446,27 @@ impl OpenFunction {
mod tests {
use crate::{
ast::*,
types::{Value, ValueInner},
types::{Nil, 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 +495,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 +527,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 +546,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 +587,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());
}
}

View File

@ -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<Self, Self::Error> {
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<ValueInner>;
#[derive(Debug, Clone, Default)]
struct TableInner {
content: HashMap<ValueInner, ValueInner>,
content: HashMap<Value, Value>,
}
#[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);