Compare commits

..

4 Commits

Author SHA1 Message Date
006a366fb0 Merge some more 2025-04-20 14:02:28 +03:00
c42623223c Extract ast::Constant as scalar::Scalar 2025-04-20 13:49:06 +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
5 changed files with 142 additions and 137 deletions

View File

@ -1,3 +1,5 @@
use crate::scalar::Scalar;
pub type Ident = String; pub type Ident = String;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -26,7 +28,7 @@ pub enum Location {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Expression { pub enum Expression {
Constant(Constant), Literal(Scalar),
Variable(Location), Variable(Location),
Call(Call), Call(Call),
Function(Function), Function(Function),
@ -58,15 +60,6 @@ pub struct Function {
pub ret: Option<Box<Expression>>, pub ret: Option<Box<Expression>>,
} }
#[derive(Debug, Clone, Default)]
pub enum Constant {
#[default]
Nil,
Boolean(bool),
Number(i64),
String(String),
}
impl Location { impl Location {
pub fn new_variable(name: impl Into<String>) -> Self { pub fn new_variable(name: impl Into<String>) -> Self {
Self::Variable { name: name.into() } Self::Variable { name: name.into() }
@ -81,8 +74,8 @@ impl Location {
} }
impl Expression { impl Expression {
pub fn new_constant(value: impl Into<Constant>) -> Self { pub fn new_constant(value: impl Into<Scalar>) -> Self {
Self::Constant(value.into()) Self::Literal(value.into())
} }
pub fn new_variable(name: impl Into<String>) -> Self { pub fn new_variable(name: impl Into<String>) -> Self {
@ -101,40 +94,12 @@ impl Expression {
} }
} }
impl From<()> for Constant { impl<T: Into<Scalar>> From<T> for Expression {
fn from(_value: ()) -> Self { fn from(value: T) -> Self {
Self::Nil Self::Literal(value.into())
} }
} }
macro_rules! impl_from {
($as: ident, $from: ty) => {
impl From<$from> for Constant {
fn from(value: $from) -> Self {
Self::$as(value.into())
}
}
};
(try $as: ident, $from: ty) => {
impl TryFrom<$from> for Constant {
type Error = ::std::num::TryFromIntError;
fn try_from(value: $from) -> Result<Self, Self::Error> {
Ok(Self::$as(value.try_into()?))
}
}
};
}
impl_from!(Boolean, bool);
impl_from!(try Number, isize);
impl_from!(try Number, usize);
impl_from!(try Number, i64);
impl_from!(try Number, u64);
impl_from!(Number, u32);
impl_from!(Number, i32);
impl_from!(String, String);
impl_from!(String, &str);
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -1,3 +1,4 @@
pub mod ast; pub mod ast;
pub mod scalar;
pub mod twopass; pub mod twopass;
pub mod types; pub mod types;

41
src/scalar.rs Normal file
View File

@ -0,0 +1,41 @@
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum Scalar {
#[default]
Nil,
Boolean(bool),
Number(i64),
String(String),
}
macro_rules! impl_from {
($as: ident, $from: ty) => {
impl From<$from> for Scalar {
fn from(value: $from) -> Self {
Self::$as(value.into())
}
}
};
(try $as: ident, $from: ty) => {
impl TryFrom<$from> for Scalar {
type Error = ::std::num::TryFromIntError;
fn try_from(value: $from) -> Result<Self, Self::Error> {
Ok(Self::$as(value.try_into()?))
}
}
};
}
impl From<()> for Scalar {
fn from(_value: ()) -> Self {
Self::Nil
}
}
impl_from!(Boolean, bool);
impl_from!(try Number, isize);
impl_from!(try Number, usize);
impl_from!(try Number, i64);
impl_from!(try Number, u64);
impl_from!(Number, u32);
impl_from!(Number, i32);
impl_from!(String, String);
impl_from!(String, &str);

View File

@ -1,6 +1,10 @@
use std::{cell::RefCell, rc::Rc}; use std::{cell::RefCell, rc::Rc};
use crate::{ast, types}; use crate::{
ast,
scalar::Scalar,
types::{self, Nil, Value},
};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct OpenFunction(Function); pub struct OpenFunction(Function);
@ -44,7 +48,7 @@ enum Location {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
enum Expression { enum Expression {
Constant(ast::Constant), Constant(Scalar),
Variable(Location), Variable(Location),
Call(Call), Call(Call),
Function(Function), Function(Function),
@ -169,7 +173,7 @@ fn lookup(scope: &BuildContext<'_>, loc: &ast::Location) -> Location {
.expect("_ENV must be always available"); .expect("_ENV must be always available");
Location::Field { Location::Field {
table: Box::new(Expression::Variable(Location::Variable { id: env })), table: Box::new(Expression::Variable(Location::Variable { id: env })),
index: Box::new(Expression::Constant(ast::Constant::String(name.clone()))), index: Box::new(Expression::Constant(name.clone().into())),
} }
} }
ast::Location::Field { table, index } => { ast::Location::Field { table, index } => {
@ -182,7 +186,7 @@ fn lookup(scope: &BuildContext<'_>, loc: &ast::Location) -> Location {
fn build_expression(scope: &BuildContext<'_>, code: &ast::Expression) -> Expression { fn build_expression(scope: &BuildContext<'_>, code: &ast::Expression) -> Expression {
match code { match code {
ast::Expression::Constant(c) => Expression::Constant(c.clone()), ast::Expression::Literal(c) => Expression::Constant(c.clone()),
ast::Expression::Variable(loc) => Expression::Variable(lookup(scope, loc)), ast::Expression::Variable(loc) => Expression::Variable(lookup(scope, loc)),
ast::Expression::Call(call) => Expression::Call(build_call(scope, call)), ast::Expression::Call(call) => Expression::Call(build_call(scope, call)),
ast::Expression::Function(f) => Expression::Function(build_function(scope, f)), ast::Expression::Function(f) => Expression::Function(build_function(scope, f)),
@ -255,7 +259,7 @@ fn build_function(parent: &BuildContext<'_>, code: &ast::Function) -> Function {
let ret = Box::new(if let Some(ret) = &code.ret { let ret = Box::new(if let Some(ret) = &code.ret {
build_expression(&scope, &ret) build_expression(&scope, &ret)
} else { } else {
Expression::Constant(ast::Constant::Nil) Expression::Constant(Scalar::Nil)
}); });
let scope = scope.scope.into_inner().finish(); let scope = scope.scope.into_inner().finish();
Function { Function {
@ -273,14 +277,14 @@ pub fn build(code: &ast::Function) -> OpenFunction {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct Variable(RefCell<types::Value>); struct Variable(RefCell<Value>);
impl Variable { impl Variable {
fn get(&self) -> types::Value { fn get(&self) -> Value {
self.0.borrow().clone() self.0.borrow().clone()
} }
fn set(&self, value: types::Value) { fn set(&self, value: Value) {
*self.0.borrow_mut() = value *self.0.borrow_mut() = value
} }
} }
@ -307,28 +311,23 @@ impl RunContext {
} }
trait Eval { trait Eval {
fn eval(&self, ctx: &RunContext) -> types::Value; fn eval(&self, ctx: &RunContext) -> Value;
} }
impl Eval for ast::Constant { impl Eval for Scalar {
fn eval(&self, _ctx: &RunContext) -> types::Value { fn eval(&self, _ctx: &RunContext) -> Value {
match self { self.clone().into()
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())),
}
} }
} }
impl Eval for Location { impl Eval for Location {
fn eval(&self, ctx: &RunContext) -> types::Value { fn eval(&self, ctx: &RunContext) -> Value {
match self { match self {
Location::Variable { id } => ctx.var(*id).get(), Location::Variable { id } => ctx.var(*id).get(),
Location::Field { table, index } => { Location::Field { table, index } => {
let table = table.eval(ctx).expect("attempt to index a nil value"); let table = table.eval(ctx);
let index = index.eval(ctx).expect("attempt to index with a nil value"); let index = index.eval(ctx);
let types::ValueInner::Table(table) = table else { let Value::Table(table) = table else {
panic!("attempt to index a non-table"); panic!("attempt to index a non-table");
}; };
table.get(index) table.get(index)
@ -338,10 +337,10 @@ impl Eval for Location {
} }
impl Eval for Call { impl Eval for Call {
fn eval(&self, ctx: &RunContext) -> types::Value { fn eval(&self, ctx: &RunContext) -> Value {
let callee = self.callee.eval(ctx); let callee = self.callee.eval(ctx);
let args: Vec<_> = self.args.iter().map(|arg| arg.eval(ctx)).collect(); 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"); panic!("attempt to call a non-function");
}; };
f.call(&args) f.call(&args)
@ -349,33 +348,31 @@ impl Eval for Call {
} }
impl Eval for Function { impl Eval for Function {
fn eval(&self, ctx: &RunContext) -> types::Value { fn eval(&self, ctx: &RunContext) -> Value {
let upvalues: Vec<_> = self let upvalues: Vec<_> = self
.upvalues .upvalues
.iter() .iter()
.map(|(_name, id)| ctx.var(*id)) .map(|(_name, id)| ctx.var(*id))
.collect(); .collect();
let self_ = self.clone(); // TODO reference, somehow 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 { impl Eval for Table {
fn eval(&self, ctx: &RunContext) -> types::Value { fn eval(&self, ctx: &RunContext) -> Value {
let table = types::Table::default(); let table = types::Table::default();
for field in &self.fields { 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); let value = field.value.eval(ctx);
table.set(key, value); table.set(key, value);
} }
Some(table.into()) table.into()
} }
} }
impl Eval for Expression { impl Eval for Expression {
fn eval(&self, ctx: &RunContext) -> types::Value { fn eval(&self, ctx: &RunContext) -> Value {
match self { match self {
Expression::Constant(inner) => inner.eval(ctx), Expression::Constant(inner) => inner.eval(ctx),
Expression::Variable(inner) => inner.eval(ctx), Expression::Variable(inner) => inner.eval(ctx),
@ -387,16 +384,16 @@ impl Eval for Expression {
} }
impl Eval for Statement { impl Eval for Statement {
fn eval(&self, ctx: &RunContext) -> types::Value { fn eval(&self, ctx: &RunContext) -> Value {
match self { match self {
Statement::Assign { target, source } => { Statement::Assign { target, source } => {
let source = source.eval(ctx); let source = source.eval(ctx);
match target { match target {
Location::Variable { id } => ctx.var(*id).set(source), Location::Variable { id } => ctx.var(*id).set(source),
Location::Field { table, index } => { Location::Field { table, index } => {
let table = table.eval(ctx).expect("attempt to index a nil value"); let table = table.eval(ctx);
let index = index.eval(ctx).expect("attempt to index with a nil value"); let index = index.eval(ctx);
let types::ValueInner::Table(table) = table else { let Value::Table(table) = table else {
panic!("attempt to index a non-table"); panic!("attempt to index a non-table");
}; };
table.set(index, source) table.set(index, source)
@ -407,21 +404,21 @@ impl Eval for Statement {
call.eval(ctx); 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()))) Rc::new(Variable(RefCell::new(value.clone())))
} }
impl Function { 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!(upvalues.len(), self.upvalues.len());
assert_eq!(args.len(), self.args.len()); assert_eq!(args.len(), self.args.len());
let ctx = RunContext { let ctx = RunContext {
args: args.iter().map(new_var).collect(), 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(), upvalues: upvalues.to_vec(),
}; };
for statement in &self.body { for statement in &self.body {
@ -432,7 +429,7 @@ impl Function {
} }
impl OpenFunction { 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() { match self.0.upvalues.as_slice() {
[] => self.0.call(&[], args), [] => self.0.call(&[], args),
[up] if up.0 == "_ENV" => self.0.call(&[new_var(&env)], args), [up] if up.0 == "_ENV" => self.0.call(&[new_var(&env)], args),
@ -443,29 +440,26 @@ impl OpenFunction {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{ast::*, types::Nil, types::Value};
ast::*,
types::{Value, ValueInner},
};
use super::build; use super::build;
fn get(table: &Value, index: &str) -> Value { fn get(table: &Value, index: &str) -> Value {
let Some(ValueInner::Table(table)) = table else { let Value::Table(table) = table else {
panic!("a table expected"); panic!("a table expected");
}; };
table.get(index.into()) table.get(index.into())
} }
fn set(table: &Value, index: &str, value: Value) { 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"); panic!("a table expected");
}; };
table.set(index.into(), value); table.set(index.into(), value);
} }
fn call(func: &Value, args: &[Value]) -> 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"); panic!("a function expected");
}; };
func.call(args) func.call(args)
@ -479,24 +473,24 @@ mod tests {
body: vec![ body: vec![
Statement::Assign { Statement::Assign {
target: Location::new_variable("x"), target: Location::new_variable("x"),
source: Box::new(Expression::new_constant(1)), source: Box::new(1.into()),
}, },
Statement::Local { Statement::Local {
name: "x".into(), name: "x".into(),
init: Some(Box::new(Expression::new_constant(2))), init: Some(Box::new(2.into())),
}, },
Statement::Assign { Statement::Assign {
target: Location::new_variable("x"), target: Location::new_variable("x"),
source: Box::new(Expression::new_constant(3)), source: Box::new(3.into()),
}, },
], ],
ret: Some(Box::new(Expression::new_variable("x"))), ret: Some(Box::new(Expression::new_variable("x"))),
}; };
let env = crate::types::Table::default(); let env = crate::types::Table::default();
let testee = build(&testee); let testee = build(&testee);
let ret = testee.call(Some(env.clone().into()), &[]); let ret = testee.call(env.clone().into(), &[]);
assert_eq!(ret, Some(3.into())); assert_eq!(ret, 3.into());
assert_eq!(env.get("x".into()), Some(1.into())); assert_eq!(env.get("x".into()), 1.into());
} }
#[test] #[test]
@ -507,7 +501,7 @@ mod tests {
body: vec![ body: vec![
Statement::Assign { Statement::Assign {
target: Location::new_variable("x"), target: Location::new_variable("x"),
source: Box::new(Expression::new_constant(42)), source: Box::new(42.into()),
}, },
Statement::Local { Statement::Local {
name: "x".into(), name: "x".into(),
@ -515,7 +509,7 @@ mod tests {
}, },
Statement::Assign { Statement::Assign {
target: Location::new_field(Expression::new_variable("_ENV"), "x"), target: Location::new_field(Expression::new_variable("_ENV"), "x"),
source: Box::new(Expression::new_constant(666)), source: Box::new(666.into()),
}, },
Statement::Local { Statement::Local {
name: "y".into(), name: "y".into(),
@ -526,9 +520,9 @@ mod tests {
}; };
let env = crate::types::Table::default(); let env = crate::types::Table::default();
let testee = build(&testee); let testee = build(&testee);
let ret = testee.call(Some(env.clone().into()), &[]); let ret = testee.call(env.clone().into(), &[]);
assert_eq!(ret, Some(42.into())); assert_eq!(ret, 42.into());
assert_eq!(env.get("x".into()), Some(666.into())); assert_eq!(env.get("x".into()), 666.into());
} }
#[test] #[test]
@ -545,8 +539,8 @@ mod tests {
}))), }))),
}; };
let remember = build(&remember); let remember = build(&remember);
let the_answerer = remember.call(None, &[Some(42.into())]); let the_answerer = remember.call(Nil, &[42.into()]);
assert_eq!(call(&the_answerer, &[]), Some(42.into())); assert_eq!(call(&the_answerer, &[]), 42.into());
} }
#[test] #[test]
@ -586,13 +580,13 @@ mod tests {
))), ))),
}; };
let make_cell = build(&make_cell); let make_cell = build(&make_cell);
let cell1 = make_cell.call(None, &[]); let cell1 = make_cell.call(Nil, &[]);
let cell2 = make_cell.call(None, &[]); let cell2 = make_cell.call(Nil, &[]);
assert_eq!(call(&get(&cell1, "get"), &[]), None); assert_eq!(call(&get(&cell1, "get"), &[]), Nil);
call(&get(&cell1, "set"), &[Some(42.into())]); call(&get(&cell1, "set"), &[42.into()]);
call(&get(&cell2, "set"), &[Some("foobar".into())]); call(&get(&cell2, "set"), &["foobar".into()]);
assert_eq!(call(&get(&cell1, "get"), &[]), Some(42.into())); assert_eq!(call(&get(&cell1, "get"), &[]), 42.into());
call(&get(&cell1, "set"), &[call(&get(&cell2, "get"), &[])]); 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,11 @@
use std::{cell::RefCell, collections::HashMap, fmt::Debug, hash::Hash, rc::Rc}; use std::{cell::RefCell, collections::HashMap, fmt::Debug, hash::Hash, rc::Rc};
#[derive(Debug, Clone, PartialEq, Eq, Hash)] use crate::scalar::Scalar;
pub enum ValueInner {
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub enum Value {
#[default]
Nil,
Boolean(bool), Boolean(bool),
Number(i64), Number(i64),
String(String), String(String),
@ -9,41 +13,35 @@ pub enum ValueInner {
Function(Function), Function(Function),
} }
pub use Value::Nil;
impl<T: Into<Scalar>> From<T> for Value {
fn from(value: T) -> Self {
match value.into() {
Scalar::Nil => Nil,
Scalar::Boolean(value) => Value::Boolean(value),
Scalar::Number(value) => Value::Number(value),
Scalar::String(value) => Value::String(value),
}
}
}
macro_rules! impl_from { macro_rules! impl_from {
($as: ident, $from: ty) => { ($as: ident, $from: ty) => {
impl From<$from> for ValueInner { impl From<$from> for Value {
fn from(value: $from) -> Self { fn from(value: $from) -> Self {
Self::$as(value.into()) Self::$as(value.into())
} }
} }
}; };
(try $as: ident, $from: ty) => {
impl TryFrom<$from> for ValueInner {
type Error = ::std::num::TryFromIntError;
fn try_from(value: $from) -> Result<Self, Self::Error> {
Ok(Self::$as(value.try_into()?))
}
}
};
} }
impl_from!(Boolean, bool);
impl_from!(try Number, isize);
impl_from!(try Number, usize);
impl_from!(try Number, i64);
impl_from!(try Number, u64);
impl_from!(Number, u32);
impl_from!(Number, i32);
impl_from!(String, String);
impl_from!(String, &str);
impl_from!(Table, Table); impl_from!(Table, Table);
impl_from!(Function, Function); impl_from!(Function, Function);
pub type Value = Option<ValueInner>;
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
struct TableInner { struct TableInner {
content: HashMap<ValueInner, ValueInner>, content: HashMap<Value, Value>,
} }
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
@ -64,13 +62,19 @@ impl Hash for Table {
} }
impl Table { impl Table {
pub fn get(&self, key: ValueInner) -> Value { pub fn get(&self, key: Value) -> Value {
self.0.borrow().content.get(&key).cloned() 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(); let mut this = self.0.borrow_mut();
if let Some(value) = value { if value != Nil {
this.content.insert(key, value); this.content.insert(key, value);
} else { } else {
this.content.remove(&key); this.content.remove(&key);