remove obsolete code
This commit is contained in:
parent
843c65ca16
commit
5138ada816
|
|
@ -1,5 +1,3 @@
|
|||
pub mod ast;
|
||||
pub mod run;
|
||||
pub mod scope;
|
||||
pub mod twopass;
|
||||
pub mod types;
|
||||
|
|
|
|||
296
src/run.rs
296
src/run.rs
|
|
@ -1,296 +0,0 @@
|
|||
use std::{collections::HashMap, fmt::Debug, rc::Rc};
|
||||
|
||||
use crate::{
|
||||
ast::{Call, Constant, Expression, Location, Statement},
|
||||
types::{Value, ValueInner},
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Function {
|
||||
name: String,
|
||||
inner: Rc<dyn Fn(&mut Scope, Vec<Value>) -> Value>,
|
||||
}
|
||||
|
||||
impl Debug for Function {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "<function {}>", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Scope {
|
||||
variables: HashMap<String, ValueInner>,
|
||||
functions: HashMap<String, Function>,
|
||||
}
|
||||
|
||||
impl Scope {
|
||||
pub fn get(&self, name: impl AsRef<str>) -> Value {
|
||||
self.variables.get(name.as_ref()).cloned()
|
||||
}
|
||||
|
||||
pub fn rawset(&mut self, name: String, value: Value) {
|
||||
if let Some(value) = value {
|
||||
self.variables.insert(name, value);
|
||||
} else {
|
||||
self.variables.remove(&name);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(&mut self, name: impl AsRef<str>, value: &(impl Clone + Into<ValueInner>)) {
|
||||
self.variables
|
||||
.insert(name.as_ref().to_string(), value.clone().into());
|
||||
}
|
||||
|
||||
pub fn add_fn(
|
||||
&mut self,
|
||||
name: impl Into<String>,
|
||||
f: impl Fn(&mut Scope, Vec<Value>) -> Value + 'static,
|
||||
) {
|
||||
let name = name.into();
|
||||
self.functions.insert(
|
||||
name.clone(),
|
||||
Function {
|
||||
name,
|
||||
inner: Rc::new(f),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Eval {
|
||||
fn eval(&self, scope: &mut Scope) -> Value;
|
||||
}
|
||||
|
||||
impl Eval for Constant {
|
||||
fn eval(&self, _scope: &mut Scope) -> Value {
|
||||
match self {
|
||||
Constant::Nil => None,
|
||||
Constant::Boolean(value) => Some(ValueInner::Boolean(*value)),
|
||||
Constant::Number(value) => Some(ValueInner::Number(*value)),
|
||||
Constant::String(value) => Some(ValueInner::String(value.clone())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eval for Location {
|
||||
fn eval(&self, scope: &mut Scope) -> Value {
|
||||
match self {
|
||||
Location::Variable { name } => scope.get(name),
|
||||
Location::Field { table, index } => {
|
||||
let table = table.eval(scope);
|
||||
let Some(ValueInner::Table(table)) = table else {
|
||||
panic!("attempt to index non-table value {table:?}")
|
||||
};
|
||||
let index = index.eval(scope);
|
||||
let Some(index) = index else {
|
||||
panic!("attempt to index with a nil value")
|
||||
};
|
||||
table.get(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eval for Call {
|
||||
fn eval(&self, scope: &mut Scope) -> Value {
|
||||
let Self { callee, args } = self;
|
||||
let Some(function) = scope.functions.get(callee.as_str()).cloned() else {
|
||||
panic!("attempt to call non-existent function {callee}")
|
||||
};
|
||||
let args = args.iter().map(|arg| arg.eval(scope)).collect();
|
||||
(function.inner)(scope, args)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eval for Expression {
|
||||
fn eval(&self, scope: &mut Scope) -> Value {
|
||||
match self {
|
||||
Expression::Constant(inner) => inner.eval(scope),
|
||||
Expression::Variable(inner) => inner.eval(scope),
|
||||
Expression::Call(inner) => inner.eval(scope),
|
||||
Expression::Function { .. } => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Exec {
|
||||
fn exec(&self, scope: &mut Scope);
|
||||
}
|
||||
|
||||
impl Exec for Statement {
|
||||
fn exec(&self, scope: &mut Scope) {
|
||||
match self {
|
||||
Statement::Assign { target, source } => {
|
||||
let value = source.eval(scope);
|
||||
match target {
|
||||
Location::Variable { name } => {
|
||||
scope.rawset(name.into(), value);
|
||||
}
|
||||
Location::Field { table, index } => {
|
||||
let table = table.eval(scope);
|
||||
let Some(ValueInner::Table(table)) = table else {
|
||||
panic!("attempt to index non-table value {table:?}")
|
||||
};
|
||||
let index = index.eval(scope);
|
||||
let Some(index) = index else {
|
||||
panic!("attempt to index with a nil value")
|
||||
};
|
||||
table.set(index, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
Statement::Call(call) => {
|
||||
call.eval(scope);
|
||||
}
|
||||
Statement::Local { .. } => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{ast, types};
|
||||
|
||||
#[test]
|
||||
fn test_consts() {
|
||||
let mut scope = Scope::default();
|
||||
assert_eq!(ast::Constant::Nil.eval(&mut scope), None);
|
||||
assert_eq!(
|
||||
ast::Constant::Boolean(true).eval(&mut scope),
|
||||
Some(true.into())
|
||||
);
|
||||
assert_eq!(ast::Constant::Number(42).eval(&mut scope), Some(42.into()));
|
||||
assert_eq!(
|
||||
ast::Constant::String("foobar".into()).eval(&mut scope),
|
||||
Some("foobar".into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vars() {
|
||||
let mut scope = Scope::default();
|
||||
let foo = ast::Location::Variable { name: "foo".into() };
|
||||
let bar = ast::Location::Variable { name: "bar".into() };
|
||||
assert_eq!(foo.eval(&mut scope), None);
|
||||
assert_eq!(bar.eval(&mut scope), None);
|
||||
scope.set("bar", &42);
|
||||
assert_eq!(foo.eval(&mut scope), None);
|
||||
assert_eq!(bar.eval(&mut scope), Some(42.into()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fields() {
|
||||
let mut scope = Scope::default();
|
||||
let loc = ast::Location::Field {
|
||||
table: Box::new(ast::Expression::Variable(ast::Location::Variable {
|
||||
name: "foo".into(),
|
||||
})),
|
||||
index: Box::new(ast::Expression::Constant(ast::Constant::String(
|
||||
"bar".into(),
|
||||
))),
|
||||
};
|
||||
let foo = types::Table::default();
|
||||
let bar = types::Table::default();
|
||||
scope.set("foo", &foo);
|
||||
assert_eq!(loc.eval(&mut scope), None);
|
||||
scope.set("bar", &bar);
|
||||
assert_eq!(loc.eval(&mut scope), None);
|
||||
bar.set("foo".into(), Some(666.into()));
|
||||
assert_eq!(loc.eval(&mut scope), None);
|
||||
foo.set("bar".into(), Some(42.into()));
|
||||
assert_eq!(loc.eval(&mut scope), Some(42.into()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_call() {
|
||||
let mut scope = Scope::default();
|
||||
scope.add_fn("sqr", |_, args| {
|
||||
let [arg] = args.as_slice() else {
|
||||
panic!("exactly one argument expected");
|
||||
};
|
||||
let Some(ValueInner::Number(arg)) = arg else {
|
||||
panic!("number expected");
|
||||
};
|
||||
Some(ValueInner::Number(arg * arg))
|
||||
});
|
||||
assert_eq!(
|
||||
ast::Expression::Call(ast::Call {
|
||||
callee: "sqr".into(),
|
||||
args: vec![ast::Expression::Constant(ast::Constant::Number(7))],
|
||||
})
|
||||
.eval(&mut scope),
|
||||
Some(49.into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_var_assign() {
|
||||
let mut scope = Scope::default();
|
||||
assert_eq!(scope.get("foo"), None);
|
||||
assert_eq!(scope.get("bar"), None);
|
||||
ast::Statement::Assign {
|
||||
target: ast::Location::Variable { name: "foo".into() },
|
||||
source: Box::new(ast::Expression::Constant(ast::Constant::Number(42))),
|
||||
}
|
||||
.exec(&mut scope);
|
||||
assert_eq!(scope.get("foo"), Some(42.into()));
|
||||
assert_eq!(scope.get("bar"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_table_assign() {
|
||||
let mut scope = Scope::default();
|
||||
let foo = types::Table::default();
|
||||
let bar = types::Table::default();
|
||||
scope.set("foo", &foo);
|
||||
scope.set("bar", &bar);
|
||||
assert_eq!(foo.get("foo".into()), None);
|
||||
assert_eq!(foo.get("bar".into()), None);
|
||||
assert_eq!(bar.get("foo".into()), None);
|
||||
assert_eq!(bar.get("bar".into()), None);
|
||||
ast::Statement::Assign {
|
||||
target: ast::Location::Field {
|
||||
table: Box::new(ast::Expression::Variable(ast::Location::Variable {
|
||||
name: "foo".into(),
|
||||
})),
|
||||
index: Box::new(ast::Expression::Constant(ast::Constant::String(
|
||||
"bar".into(),
|
||||
))),
|
||||
},
|
||||
source: Box::new(ast::Expression::Constant(ast::Constant::Number(42))),
|
||||
}
|
||||
.exec(&mut scope);
|
||||
assert_eq!(foo.get("foo".into()), None);
|
||||
assert_eq!(foo.get("bar".into()), Some(42.into()));
|
||||
assert_eq!(bar.get("foo".into()), None);
|
||||
assert_eq!(bar.get("bar".into()), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_call_statement() {
|
||||
let mut scope = Scope::default();
|
||||
scope.add_fn("set", |scope, args| {
|
||||
let [key, value] = args.as_slice() else {
|
||||
panic!("exactly two arguments expected");
|
||||
};
|
||||
let Some(ValueInner::String(key)) = key else {
|
||||
panic!("string expected");
|
||||
};
|
||||
scope.rawset(key.clone(), value.clone());
|
||||
None
|
||||
});
|
||||
assert_eq!(scope.get("foo"), None);
|
||||
assert_eq!(scope.get("bar"), None);
|
||||
ast::Statement::Call(ast::Call {
|
||||
callee: "set".into(),
|
||||
args: vec![
|
||||
ast::Expression::Constant(ast::Constant::String("foo".into())),
|
||||
ast::Expression::Constant(ast::Constant::Number(42)),
|
||||
],
|
||||
})
|
||||
.exec(&mut scope);
|
||||
assert_eq!(scope.get("foo"), Some(42.into()));
|
||||
assert_eq!(scope.get("bar"), None);
|
||||
}
|
||||
}
|
||||
63
src/scope.rs
63
src/scope.rs
|
|
@ -1,63 +0,0 @@
|
|||
use std::{
|
||||
cell::RefCell,
|
||||
collections::{hash_map::Entry, HashMap},
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
use crate::types::Value;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Variable(Rc<RefCell<Value>>);
|
||||
|
||||
impl Variable {
|
||||
pub fn get(&self) -> Value {
|
||||
self.0.borrow().clone()
|
||||
}
|
||||
|
||||
pub fn set(&self, value: Value) {
|
||||
*self.0.borrow_mut() = value;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
struct ScopeInner {
|
||||
parent: Option<Scope>,
|
||||
locals: HashMap<String, Variable>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Scope(Rc<RefCell<ScopeInner>>);
|
||||
|
||||
impl Scope {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn nested(&self) -> Self {
|
||||
Self(Rc::new(RefCell::new(ScopeInner {
|
||||
parent: Some(self.clone()),
|
||||
..Default::default()
|
||||
})))
|
||||
}
|
||||
|
||||
pub fn add(&self, name: String) {
|
||||
self.0.borrow_mut().locals.insert(name, Variable::default());
|
||||
}
|
||||
|
||||
pub fn get(&self, name: String) -> Variable {
|
||||
let mut this = self.0.borrow_mut();
|
||||
let parent = this.parent.clone();
|
||||
match this.locals.entry(name) {
|
||||
Entry::Occupied(var) => var.get().clone(),
|
||||
Entry::Vacant(var) => {
|
||||
if let Some(parent) = parent {
|
||||
let name = var.into_key();
|
||||
drop(this);
|
||||
parent.get(name)
|
||||
} else {
|
||||
var.insert(Variable::default()).clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user