Compare commits
No commits in common. "scalar" and "master" have entirely different histories.
51
src/ast.rs
51
src/ast.rs
|
|
@ -1,5 +1,3 @@
|
||||||
use crate::scalar::Scalar;
|
|
||||||
|
|
||||||
pub type Ident = String;
|
pub type Ident = String;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -28,7 +26,7 @@ pub enum Location {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Expression {
|
pub enum Expression {
|
||||||
Literal(Scalar),
|
Constant(Constant),
|
||||||
Variable(Location),
|
Variable(Location),
|
||||||
Call(Call),
|
Call(Call),
|
||||||
Function(Function),
|
Function(Function),
|
||||||
|
|
@ -60,6 +58,15 @@ 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() }
|
||||||
|
|
@ -74,8 +81,8 @@ impl Location {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Expression {
|
impl Expression {
|
||||||
pub fn new_constant(value: impl Into<Scalar>) -> Self {
|
pub fn new_constant(value: impl Into<Constant>) -> Self {
|
||||||
Self::Literal(value.into())
|
Self::Constant(value.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_variable(name: impl Into<String>) -> Self {
|
pub fn new_variable(name: impl Into<String>) -> Self {
|
||||||
|
|
@ -94,12 +101,40 @@ impl Expression {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Into<Scalar>> From<T> for Expression {
|
impl From<()> for Constant {
|
||||||
fn from(value: T) -> Self {
|
fn from(_value: ()) -> Self {
|
||||||
Self::Literal(value.into())
|
Self::Nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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::*;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
pub mod scalar;
|
|
||||||
pub mod twopass;
|
pub mod twopass;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
#[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);
|
|
||||||
128
src/twopass.rs
128
src/twopass.rs
|
|
@ -1,10 +1,6 @@
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
use crate::{
|
use crate::{ast, types};
|
||||||
ast,
|
|
||||||
scalar::Scalar,
|
|
||||||
types::{self, Nil, Value},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct OpenFunction(Function);
|
pub struct OpenFunction(Function);
|
||||||
|
|
@ -48,7 +44,7 @@ enum Location {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
enum Expression {
|
enum Expression {
|
||||||
Constant(Scalar),
|
Constant(ast::Constant),
|
||||||
Variable(Location),
|
Variable(Location),
|
||||||
Call(Call),
|
Call(Call),
|
||||||
Function(Function),
|
Function(Function),
|
||||||
|
|
@ -173,7 +169,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(name.clone().into())),
|
index: Box::new(Expression::Constant(ast::Constant::String(name.clone()))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::Location::Field { table, index } => {
|
ast::Location::Field { table, index } => {
|
||||||
|
|
@ -186,7 +182,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::Literal(c) => Expression::Constant(c.clone()),
|
ast::Expression::Constant(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)),
|
||||||
|
|
@ -259,7 +255,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(Scalar::Nil)
|
Expression::Constant(ast::Constant::Nil)
|
||||||
});
|
});
|
||||||
let scope = scope.scope.into_inner().finish();
|
let scope = scope.scope.into_inner().finish();
|
||||||
Function {
|
Function {
|
||||||
|
|
@ -277,14 +273,14 @@ pub fn build(code: &ast::Function) -> OpenFunction {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Variable(RefCell<Value>);
|
struct Variable(RefCell<types::Value>);
|
||||||
|
|
||||||
impl Variable {
|
impl Variable {
|
||||||
fn get(&self) -> Value {
|
fn get(&self) -> types::Value {
|
||||||
self.0.borrow().clone()
|
self.0.borrow().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set(&self, value: Value) {
|
fn set(&self, value: types::Value) {
|
||||||
*self.0.borrow_mut() = value
|
*self.0.borrow_mut() = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -311,23 +307,28 @@ impl RunContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Eval {
|
trait Eval {
|
||||||
fn eval(&self, ctx: &RunContext) -> Value;
|
fn eval(&self, ctx: &RunContext) -> types::Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for Scalar {
|
impl Eval for ast::Constant {
|
||||||
fn eval(&self, _ctx: &RunContext) -> Value {
|
fn eval(&self, _ctx: &RunContext) -> types::Value {
|
||||||
self.clone().into()
|
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())),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for Location {
|
impl Eval for Location {
|
||||||
fn eval(&self, ctx: &RunContext) -> Value {
|
fn eval(&self, ctx: &RunContext) -> types::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);
|
let table = table.eval(ctx).expect("attempt to index a nil value");
|
||||||
let index = index.eval(ctx);
|
let index = index.eval(ctx).expect("attempt to index with a nil value");
|
||||||
let Value::Table(table) = table else {
|
let types::ValueInner::Table(table) = table else {
|
||||||
panic!("attempt to index a non-table");
|
panic!("attempt to index a non-table");
|
||||||
};
|
};
|
||||||
table.get(index)
|
table.get(index)
|
||||||
|
|
@ -337,10 +338,10 @@ impl Eval for Location {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for Call {
|
impl Eval for Call {
|
||||||
fn eval(&self, ctx: &RunContext) -> Value {
|
fn eval(&self, ctx: &RunContext) -> types::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 Value::Function(f) = callee else {
|
let Some(types::ValueInner::Function(f)) = callee else {
|
||||||
panic!("attempt to call a non-function");
|
panic!("attempt to call a non-function");
|
||||||
};
|
};
|
||||||
f.call(&args)
|
f.call(&args)
|
||||||
|
|
@ -348,31 +349,33 @@ impl Eval for Call {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for Function {
|
impl Eval for Function {
|
||||||
fn eval(&self, ctx: &RunContext) -> Value {
|
fn eval(&self, ctx: &RunContext) -> types::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
|
||||||
types::Function::new(self.name.clone(), move |args| self_.call(&upvalues, args)).into()
|
Some(
|
||||||
|
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) -> Value {
|
fn eval(&self, ctx: &RunContext) -> types::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);
|
let key = field.key.eval(ctx).expect("attempt to set a nil index");
|
||||||
let value = field.value.eval(ctx);
|
let value = field.value.eval(ctx);
|
||||||
table.set(key, value);
|
table.set(key, value);
|
||||||
}
|
}
|
||||||
table.into()
|
Some(table.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for Expression {
|
impl Eval for Expression {
|
||||||
fn eval(&self, ctx: &RunContext) -> Value {
|
fn eval(&self, ctx: &RunContext) -> types::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),
|
||||||
|
|
@ -384,16 +387,16 @@ impl Eval for Expression {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for Statement {
|
impl Eval for Statement {
|
||||||
fn eval(&self, ctx: &RunContext) -> Value {
|
fn eval(&self, ctx: &RunContext) -> types::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);
|
let table = table.eval(ctx).expect("attempt to index a nil value");
|
||||||
let index = index.eval(ctx);
|
let index = index.eval(ctx).expect("attempt to index with a nil value");
|
||||||
let Value::Table(table) = table else {
|
let types::ValueInner::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)
|
||||||
|
|
@ -404,21 +407,21 @@ impl Eval for Statement {
|
||||||
call.eval(ctx);
|
call.eval(ctx);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Nil
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_var(value: &Value) -> VariableRef {
|
fn new_var(value: &types::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: &[Value]) -> Value {
|
fn call(&self, upvalues: &[VariableRef], args: &[types::Value]) -> types::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(&Nil)).collect(),
|
locals: (0..self.locals.len()).map(|_| new_var(&None)).collect(),
|
||||||
upvalues: upvalues.to_vec(),
|
upvalues: upvalues.to_vec(),
|
||||||
};
|
};
|
||||||
for statement in &self.body {
|
for statement in &self.body {
|
||||||
|
|
@ -429,7 +432,7 @@ impl Function {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OpenFunction {
|
impl OpenFunction {
|
||||||
pub fn call(&self, env: Value, args: &[Value]) -> Value {
|
pub fn call(&self, env: types::Value, args: &[types::Value]) -> types::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),
|
||||||
|
|
@ -440,26 +443,29 @@ impl OpenFunction {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{ast::*, types::Nil, types::Value};
|
use crate::{
|
||||||
|
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 Value::Table(table) = table else {
|
let Some(ValueInner::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 Value::Table(table) = table else {
|
let Some(ValueInner::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 Value::Function(func) = func else {
|
let Some(ValueInner::Function(func)) = func else {
|
||||||
panic!("a function expected");
|
panic!("a function expected");
|
||||||
};
|
};
|
||||||
func.call(args)
|
func.call(args)
|
||||||
|
|
@ -473,24 +479,24 @@ mod tests {
|
||||||
body: vec![
|
body: vec![
|
||||||
Statement::Assign {
|
Statement::Assign {
|
||||||
target: Location::new_variable("x"),
|
target: Location::new_variable("x"),
|
||||||
source: Box::new(1.into()),
|
source: Box::new(Expression::new_constant(1)),
|
||||||
},
|
},
|
||||||
Statement::Local {
|
Statement::Local {
|
||||||
name: "x".into(),
|
name: "x".into(),
|
||||||
init: Some(Box::new(2.into())),
|
init: Some(Box::new(Expression::new_constant(2))),
|
||||||
},
|
},
|
||||||
Statement::Assign {
|
Statement::Assign {
|
||||||
target: Location::new_variable("x"),
|
target: Location::new_variable("x"),
|
||||||
source: Box::new(3.into()),
|
source: Box::new(Expression::new_constant(3)),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
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(env.clone().into(), &[]);
|
let ret = testee.call(Some(env.clone().into()), &[]);
|
||||||
assert_eq!(ret, 3.into());
|
assert_eq!(ret, Some(3.into()));
|
||||||
assert_eq!(env.get("x".into()), 1.into());
|
assert_eq!(env.get("x".into()), Some(1.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -501,7 +507,7 @@ mod tests {
|
||||||
body: vec![
|
body: vec![
|
||||||
Statement::Assign {
|
Statement::Assign {
|
||||||
target: Location::new_variable("x"),
|
target: Location::new_variable("x"),
|
||||||
source: Box::new(42.into()),
|
source: Box::new(Expression::new_constant(42)),
|
||||||
},
|
},
|
||||||
Statement::Local {
|
Statement::Local {
|
||||||
name: "x".into(),
|
name: "x".into(),
|
||||||
|
|
@ -509,7 +515,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(666.into()),
|
source: Box::new(Expression::new_constant(666)),
|
||||||
},
|
},
|
||||||
Statement::Local {
|
Statement::Local {
|
||||||
name: "y".into(),
|
name: "y".into(),
|
||||||
|
|
@ -520,9 +526,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(env.clone().into(), &[]);
|
let ret = testee.call(Some(env.clone().into()), &[]);
|
||||||
assert_eq!(ret, 42.into());
|
assert_eq!(ret, Some(42.into()));
|
||||||
assert_eq!(env.get("x".into()), 666.into());
|
assert_eq!(env.get("x".into()), Some(666.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -539,8 +545,8 @@ mod tests {
|
||||||
}))),
|
}))),
|
||||||
};
|
};
|
||||||
let remember = build(&remember);
|
let remember = build(&remember);
|
||||||
let the_answerer = remember.call(Nil, &[42.into()]);
|
let the_answerer = remember.call(None, &[Some(42.into())]);
|
||||||
assert_eq!(call(&the_answerer, &[]), 42.into());
|
assert_eq!(call(&the_answerer, &[]), Some(42.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -580,13 +586,13 @@ mod tests {
|
||||||
))),
|
))),
|
||||||
};
|
};
|
||||||
let make_cell = build(&make_cell);
|
let make_cell = build(&make_cell);
|
||||||
let cell1 = make_cell.call(Nil, &[]);
|
let cell1 = make_cell.call(None, &[]);
|
||||||
let cell2 = make_cell.call(Nil, &[]);
|
let cell2 = make_cell.call(None, &[]);
|
||||||
assert_eq!(call(&get(&cell1, "get"), &[]), Nil);
|
assert_eq!(call(&get(&cell1, "get"), &[]), None);
|
||||||
call(&get(&cell1, "set"), &[42.into()]);
|
call(&get(&cell1, "set"), &[Some(42.into())]);
|
||||||
call(&get(&cell2, "set"), &["foobar".into()]);
|
call(&get(&cell2, "set"), &[Some("foobar".into())]);
|
||||||
assert_eq!(call(&get(&cell1, "get"), &[]), 42.into());
|
assert_eq!(call(&get(&cell1, "get"), &[]), Some(42.into()));
|
||||||
call(&get(&cell1, "set"), &[call(&get(&cell2, "get"), &[])]);
|
call(&get(&cell1, "set"), &[call(&get(&cell2, "get"), &[])]);
|
||||||
assert_eq!(call(&get(&cell1, "get"), &[]), "foobar".into());
|
assert_eq!(call(&get(&cell1, "get"), &[]), Some("foobar".into()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
58
src/types.rs
58
src/types.rs
|
|
@ -1,11 +1,7 @@
|
||||||
use std::{cell::RefCell, collections::HashMap, fmt::Debug, hash::Hash, rc::Rc};
|
use std::{cell::RefCell, collections::HashMap, fmt::Debug, hash::Hash, rc::Rc};
|
||||||
|
|
||||||
use crate::scalar::Scalar;
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
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),
|
||||||
|
|
@ -13,35 +9,41 @@ pub enum Value {
|
||||||
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 Value {
|
impl From<$from> for ValueInner {
|
||||||
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<Value, Value>,
|
content: HashMap<ValueInner, ValueInner>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
|
|
@ -62,19 +64,13 @@ impl Hash for Table {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Table {
|
impl Table {
|
||||||
pub fn get(&self, key: Value) -> Value {
|
pub fn get(&self, key: ValueInner) -> Value {
|
||||||
self.0
|
self.0.borrow().content.get(&key).cloned()
|
||||||
.borrow()
|
|
||||||
.content
|
|
||||||
.get(&key)
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set(&self, key: Value, value: Value) {
|
pub fn set(&self, key: ValueInner, value: Value) {
|
||||||
assert!(key != Nil);
|
|
||||||
let mut this = self.0.borrow_mut();
|
let mut this = self.0.borrow_mut();
|
||||||
if value != Nil {
|
if let Some(value) = value {
|
||||||
this.content.insert(key, value);
|
this.content.insert(key, value);
|
||||||
} else {
|
} else {
|
||||||
this.content.remove(&key);
|
this.content.remove(&key);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user