From fc942b9bc05d87b0267b5a48645d575077f263e7 Mon Sep 17 00:00:00 2001 From: Julius de Jeu Date: Sat, 2 Jul 2022 23:42:15 +0200 Subject: [PATCH] Initial Commit --- .gitignore | 1 + Cargo.lock | 7 ++ Cargo.toml | 8 ++ src/env.rs | 63 ++++++++++++ src/expression.rs | 153 ++++++++++++++++++++++++++++ src/function/function_impl.rs | 67 ++++++++++++ src/function/mod.rs | 112 ++++++++++++++++++++ src/macros.rs | 19 ++++ src/main.rs | 125 +++++++++++++++++++++++ src/statement.rs | 35 +++++++ src/value.rs | 185 ++++++++++++++++++++++++++++++++++ 11 files changed, 775 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/env.rs create mode 100644 src/expression.rs create mode 100644 src/function/function_impl.rs create mode 100644 src/function/mod.rs create mode 100644 src/macros.rs create mode 100644 src/main.rs create mode 100644 src/statement.rs create mode 100644 src/value.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..73c4047 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "sunflower" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..1f06f15 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "sunflower" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/env.rs b/src/env.rs new file mode 100644 index 0000000..fb27045 --- /dev/null +++ b/src/env.rs @@ -0,0 +1,63 @@ +use std::{collections::HashMap, sync::{Arc, RwLock}}; + +use crate::value::Value; + +#[derive(Debug, Clone)] +pub struct Env<'env> { + values: Arc>>, + parent: Option<&'env Env<'env>>, +} + +impl Env<'_> { + pub fn new() -> Self { + Env { + values: Default::default(), + parent: None, + } + } + + pub fn get(&self, name: &str) -> Option { + if let Some(v) = self.values.read().unwrap().get(name) { + Some(v.clone()) + } else if let Some(parent) = &self.parent { + parent.get(name) + } else { + None + } + } + + pub fn set(&self, name: &str, value: impl Into) { + let value = value.into(); + if !self.set_recursive(name, &value) { + self.values.write().unwrap().insert(name.to_string(), value); + } + } + + fn set_recursive(&self, name: &str, value: &Value) -> bool { + if let Some(v) = self.values.write().unwrap().get_mut(name) { + *v = value.clone(); + true + } else if let Some(parent) = self.parent { + parent.set_recursive(name, value) + } else { + false + } + } + + pub fn update(&self, name: &str, f: impl FnOnce(&Value) -> Value) { + if let Some(v) = self.values.write().unwrap().get_mut(name) { + *v = f(v); + } else if let Some(parent) = self.parent { + parent.update(name, f); + } + } +} + +impl<'env> Env<'env> { + pub fn new_with_parent(parent: &'env Env<'env>) -> Self { + Env { + values: Default::default(), + parent: Some(parent), + } + } +} diff --git a/src/expression.rs b/src/expression.rs new file mode 100644 index 0000000..753b1a9 --- /dev/null +++ b/src/expression.rs @@ -0,0 +1,153 @@ +use crate::{env::Env, function::FunctionMap, value::Value}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum BinaryOperation { + Add, + Sub, + Mul, + Div, + LessThan, + GreaterThan, + LessThanOrEqual, + GreaterThanOrEqual, + Equal, + NotEqual, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Expression { + Constant(Value), + Variable(String), + Call(String, Vec), + Binary(Box, BinaryOperation, Box), +} + +impl Expression { + pub fn eval(&self, e: &Env, f: &FunctionMap) -> Value { + match self { + Expression::Constant(v) => v.clone(), + Expression::Variable(name) => e.get(name).unwrap_or_default(), + Expression::Call(name, args) => { + f.call(name, e, &args.iter().map(|ex| ex.eval(e, f)).collect::>()).unwrap() + }, + Expression::Binary(lhs, op, rhs) => { + let lhs = lhs.eval(e, f); + let rhs = rhs.eval(e, f); + match op { + BinaryOperation::Add => lhs + rhs, + BinaryOperation::Sub => lhs - rhs, + BinaryOperation::Mul => lhs * rhs, + BinaryOperation::Div => lhs / rhs, + BinaryOperation::LessThan => (lhs < rhs).into(), + BinaryOperation::GreaterThan => (lhs > rhs).into(), + BinaryOperation::LessThanOrEqual => (lhs <= rhs).into(), + BinaryOperation::GreaterThanOrEqual => (lhs >= rhs).into(), + BinaryOperation::Equal => (lhs == rhs).into(), + BinaryOperation::NotEqual => (lhs != rhs).into(), + } + } + } + } +} + +impl From for Expression { + fn from(v: Value) -> Self { + Expression::Constant(v) + } +} + +macro_rules! from_impl { + ($($type:ty),+) => { + $( + impl From<$type> for Expression { + fn from(v: $type) -> Self { + Expression::Constant(Value::from(v)) + } + } + )+ + }; +} + +from_impl!(i32, f32, String, bool, char); + +impl<'s> From<&'s str> for Expression { + fn from(s: &'s str) -> Self { + Expression::Constant(Value::String(s.to_string())) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::env::Env; + use crate::value::Value; + + #[test] + fn test_eval() { + let f = FunctionMap::new(); + let e = Env::new(); + e.set("test", 10); + assert_eq!( + Expression::Variable("test".to_string()).eval(&e, &f), + Value::Int(10) + ); + assert_eq!( + Expression::Constant(Value::Int(10)).eval(&e, &f), + Value::Int(10) + ); + assert_eq!( + Expression::Binary( + Box::new(Expression::Variable("test".to_string())), + BinaryOperation::Add, + Box::new(Expression::Constant(Value::Int(10))) + ) + .eval(&e, &f), + Value::Int(20) + ); + } + + #[test] + fn test_eval_unset() { + let f = FunctionMap::new(); + + let e = Env::new(); + assert_eq!( + Expression::Variable("test".to_string()).eval(&e, &f), + Value::Nothing + ); + assert_eq!( + Expression::Constant(Value::Error).eval(&e, &f), + Value::Error + ); + assert_eq!( + Expression::Binary( + Box::new(Expression::Variable("test".to_string())), + BinaryOperation::Add, + Box::new(Expression::Constant(Value::Nothing)) + ) + .eval(&e, &f), + Value::Error + ); + } + + #[test] + fn test_eval_int() { + let f = FunctionMap::new(); + + let e = Env::new(); + e.set("test", 10); + assert_eq!( + Expression::Constant(Value::Int(10)).eval(&e, &f), + Value::Int(10) + ); + assert_eq!( + Expression::Binary( + Box::new(Expression::Constant(Value::Int(10))), + BinaryOperation::Add, + Box::new(Expression::Variable("test".to_string())) + ) + .eval(&e, &f), + Value::Int(20) + ); + } +} diff --git a/src/function/function_impl.rs b/src/function/function_impl.rs new file mode 100644 index 0000000..2ebcfbc --- /dev/null +++ b/src/function/function_impl.rs @@ -0,0 +1,67 @@ +use super::{Env, FromValue, NativeFunction, Value}; + +macro_rules! impl_function { + ( $($ty:ident),* $(,)? ) => { + #[allow(non_snake_case, unused_variables, unused_mut, unused_assignments)] + impl NativeFunction<($($ty,)*)> for F + where + F: Fn($(&$ty,)*) -> R, + R: Into, + $($ty: FromValue,)* + { + fn native_execute(&self, _: &Env, args: &[Value]) -> Result { + let mut args = args.iter(); + let total_count = { + let mut count = 0; + $( + let _: Option<$ty> = None; + count += 1; + )* + count + }; + let mut count = 0; + $( + let arg = args.next() + .ok_or_else(|| format!("method expected {} parameters, but parameter {} was missing", total_count, count))?; + let $ty = $ty::from_value(arg) + .ok_or_else(|| format!("Parameter {} was not of type {}, it was {}", count, std::any::type_name::<$ty>(), arg.value_kind()))?; + count += 1; + )* + Ok(self($($ty,)*).into()) + } + } + + + #[allow(non_snake_case, unused_variables, unused_mut, unused_assignments)] + impl NativeFunction<((), $($ty,)*)> for F + where + F: Fn(&Env, $(&$ty,)*) -> R, + R: Into, + $($ty: FromValue,)* + { + fn native_execute(&self, env: &Env, args: &[Value]) -> Result { + let mut args = args.iter(); + let total_count = { + let mut count = 0; + $( + let _: Option<$ty> = None; + count += 1; + )* + count + }; + let mut count = 0; + $( + let arg = args.next() + .ok_or_else(|| format!("method expected {} parameters, but parameter {} was missing", total_count, count))?; + let $ty = $ty::from_value(arg) + .ok_or_else(|| format!("Parameter {} was not of type {}, it was {}", count, std::any::type_name::<$ty>(), arg.value_kind()))?; + count += 1; + )* + Ok(self(env, $($ty,)*).into()) + } + } + + }; +} + +crate::macros::for_each_tuple!(impl_function); diff --git a/src/function/mod.rs b/src/function/mod.rs new file mode 100644 index 0000000..55ca1ec --- /dev/null +++ b/src/function/mod.rs @@ -0,0 +1,112 @@ +use std::{collections::HashMap, marker::PhantomData}; + +use crate::{ + env::Env, + statement::Statement, + value::{FromValue, Value}, +}; + +mod function_impl; + +#[derive(Debug, Clone, Copy)] +enum FunctionType { + Native, + Interpreter, +} + +#[derive(Default)] +pub struct FunctionMap(HashMap, FunctionType)>); + +impl FunctionMap { + pub fn new() -> Self { + Default::default() + } + + pub fn insert_native(&mut self, name: S, f: F) + where + S: ToString, + P: 'static, + F: 'static + NativeFunction

, + { + self.0.insert( + name.to_string(), + (Box::new(f.into_function()), FunctionType::Native), + ); + } + + pub fn insert_interpreter(&mut self, name: &str, params: Vec, body: Vec) { + self.0.insert( + name.to_string(), + ( + Box::new(InterpreterFunction::new(params, body)), + FunctionType::Interpreter, + ), + ); + } + + pub fn call(&self, name: &str, env: &Env, args: &[Value]) -> Result { + let (f, t) = self + .0 + .get(name) + .ok_or_else(|| format!("Function {} not found", name))?; + println!("Calling {:?} function {}", t, name); + + f.execute(env, self, args) + } +} + +pub trait NativeFunction

: Sized { + fn native_execute(&self, env: &Env, args: &[Value]) -> Result; + + fn into_function(self) -> IntoFunction { + IntoFunction::new(self) + } +} + +pub struct IntoFunction { + func: F, + _phantom: PhantomData

, +} + +impl IntoFunction { + fn new(func: F) -> Self { + IntoFunction { + func, + _phantom: PhantomData, + } + } +} + +impl, P> Function for IntoFunction { + fn execute(&self, env: &Env, _: &FunctionMap, args: &[Value]) -> Result { + self.func.native_execute(env, args) + } +} + +pub struct InterpreterFunction { + parameters: Vec, + body: Vec, +} + +impl InterpreterFunction { + pub fn new(parameters: Vec, body: Vec) -> Self { + InterpreterFunction { parameters, body } + } +} + +impl Function for InterpreterFunction { + fn execute(&self, env: &Env, map: &FunctionMap, args: &[Value]) -> Result { + let sub_env = Env::new_with_parent(env); + for (name, value) in self.parameters.iter().zip(args) { + sub_env.set(name, value.clone()); + } + for stmt in self.body.iter() { + stmt.eval(&sub_env, map); + } + Ok(Value::Nothing) + } +} + +pub trait Function { + fn execute(&self, env: &Env, map: &FunctionMap, args: &[Value]) -> Result; +} diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000..43ee970 --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,19 @@ + +macro_rules! for_each_tuple_ { + ($m:ident !!) => { + $m! { } + }; + ($m:ident !! $h:ident, $($t:ident,)*) => { + $m! { $h, $($t,)* } + $crate::macros::for_each_tuple_! { $m !! $($t,)* } + } +} + +macro_rules! for_each_tuple { + ($m:ident) => { + $crate::macros::for_each_tuple_! { $m !! T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, } + } +} + +pub(crate) use for_each_tuple; +pub(crate) use for_each_tuple_; diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..bdfc116 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,125 @@ +use env::Env; +use expression::{BinaryOperation, Expression}; +use function::{Function, FunctionMap, NativeFunction}; +use statement::Statement; +use value::Value; + +mod env; +mod expression; +mod function; +mod macros; +mod statement; +mod value; + +fn run(functions: FunctionMap, statements: &[Statement]) { + let env = Env::new(); + for statement in statements { + statement.eval(&env, &functions); + } +} + +fn main() { + let statements = vec![ + Statement::Assignment("a".to_string(), Expression::from(1)), + Statement::Assignment("b".to_string(), Expression::from(2)), + Statement::Assignment("c".to_string(), Expression::from(3)), + Statement::Print(Expression::Binary( + Box::new(Expression::Variable("a".to_string())), + BinaryOperation::Add, + Box::new(Expression::Variable("b".to_string())), + )), + Statement::Print(Expression::Binary( + Box::new(Expression::Variable("a".to_string())), + BinaryOperation::Add, + Box::new(Expression::Variable("c".to_string())), + )), + Statement::Print(Expression::Binary( + Box::new(Expression::Variable("b".to_string())), + BinaryOperation::Add, + Box::new(Expression::Variable("c".to_string())), + )), + ]; + run(FunctionMap::new(), &statements); + + let while_test = vec![ + Statement::Print("while test".to_string().into()), + Statement::Assignment("a".to_string(), Expression::from(1)), + Statement::While( + Expression::Binary( + Box::new(Expression::Variable("a".to_string())), + BinaryOperation::LessThan, + Box::new(10.into()), + ), + vec![ + Statement::Assignment( + "a".to_string(), + Expression::Binary( + Box::new(Expression::Variable("a".to_string())), + BinaryOperation::Add, + Box::new(1.into()), + ), + ), + Statement::Print(Expression::Variable("a".to_string())), + ], + ), + ]; + run(FunctionMap::new(), &while_test); + + { + let env = Env::new(); + + let res = (|arg: &i32| arg + 1).native_execute(&env, &[Value::Int(1)]); + println!("{:?}", res); + } + + { + let z = || 100; + let env = Env::new(); + let res = z.native_execute(&env, &[]); + println!("{:?}", res); + } + + { + let z = |a: &Value| a.clone(); + let env = Env::new(); + let res = z.into_function().execute(&env, &FunctionMap::new(), &[]); + println!("{:?}", res); + } + + { + let z = |a: &String| a.clone(); + let env = Env::new(); + let res = z.native_execute(&env, &[100.into()]); + println!("{:?}", res); + } + + { + let v: Vec> = vec![ + Box::new((|| 100).into_function()), + Box::new((|a: &i32| a + 1).into_function()), + ]; + for f in v { + let r = f.execute(&Env::new(), &FunctionMap::new(), &[100.into()]); + println!("{:?}", r); + } + } + + let mut m: FunctionMap = Default::default(); + m.insert_native("print", |a: &Value| { + println!("{}", a); + }); + + m.insert_native("waluigi", || "Waluigi"); + + m.insert_interpreter("call_print", vec!["what".to_string()], vec![ + Statement::Expression(Expression::Call("print".to_string(), vec![Expression::Variable("what".to_string())])), + ]); + + run( + m, + &vec![Statement::Expression(Expression::Call( + "call_print".to_string(), + vec![Expression::Call("waluigi".to_string(), vec![])], + ))], + ); +} diff --git a/src/statement.rs b/src/statement.rs new file mode 100644 index 0000000..8c142ef --- /dev/null +++ b/src/statement.rs @@ -0,0 +1,35 @@ +use crate::{env::Env, expression::Expression, function::FunctionMap}; + +#[derive(Debug, Clone)] +pub enum Statement { + Assignment(String, Expression), + Expression(Expression), + Print(Expression), + While(Expression, Vec), +} + +impl Statement { + pub fn eval(&self, env: &Env, f: &FunctionMap) { + match self { + Statement::Assignment(name, expr) => { + let value = expr.eval(env, f); + env.set(name, value); + } + Statement::Expression(expr) => { + expr.eval(env, f); + } + Statement::Print(expr) => { + let value = expr.eval(env, f); + println!("{}", value); + } + Statement::While(expr, body) => { + while expr.eval(env, f).is_truthy() { + let body_env = Env::new_with_parent(env); + for stmt in body.iter() { + stmt.eval(&body_env, f); + } + } + } + } + } +} diff --git a/src/value.rs b/src/value.rs new file mode 100644 index 0000000..ed2a80f --- /dev/null +++ b/src/value.rs @@ -0,0 +1,185 @@ +use std::{ + fmt::Display, + ops::{Add, Div, Mul, Sub}, +}; + +#[derive(Debug, Clone, PartialEq, Default)] +pub enum Value { + Int(i32), + Float(f32), + String(String), + Bool(bool), + Char(char), + #[default] + Nothing, + Error, +} + +impl Eq for Value {} + +// impl Value { +// pub fn as_int(&self) -> Option { +// match self { +// Value::Int(v) => Some(*v), +// _ => None, +// } +// } +// pub fn as_float(&self) -> Option { +// match self { +// Value::Float(v) => Some(*v), +// _ => None, +// } +// } +// pub fn as_string(&self) -> Option { +// match self { +// Value::String(v) => Some(v.clone()), +// _ => None, +// } +// } +// pub fn as_bool(&self) -> Option { +// match self { +// Value::Bool(v) => Some(*v), +// _ => None, +// } +// } +// pub fn as_char(&self) -> Option { +// match self { +// Value::Char(v) => Some(*v), +// _ => None, +// } +// } +// } + +impl Value { + pub fn is_truthy(&self) -> bool { + match self { + Value::Int(v) => *v != 0, + Value::Float(v) => *v != 0.0, + Value::String(v) => !v.is_empty(), + Value::Bool(v) => *v, + Value::Char(v) => *v != '\0', + Value::Nothing => false, + Value::Error => false, + } + } + + pub fn value_kind(&self) -> &'static str { + match self { + Value::Int(_) => "int", + Value::Float(_) => "float", + Value::String(_) => "string", + Value::Bool(_) => "bool", + Value::Char(_) => "char", + Value::Nothing => "nothing", + Value::Error => "error", + } + } +} + +macro_rules! math_trait { + ($trait_name:ty, $function_name:ident) => { + impl $trait_name for Value { + type Output = Value; + fn $function_name(self, other: Value) -> Self::Output { + match (self, other) { + (Value::Int(lhs), Value::Int(rhs)) => Value::Int(lhs.$function_name(rhs)), + (Value::Float(lhs), Value::Float(rhs)) => Value::Float(lhs.$function_name(rhs)), + _ => Value::Error, + } + } + } + }; +} + +math_trait!(Add, add); +math_trait!(Sub, sub); +math_trait!(Mul, mul); +math_trait!(Div, div); + +impl Display for Value { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + Value::Int(v) => v.to_string(), + Value::Float(v) => v.to_string(), + Value::String(v) => v.clone(), + Value::Bool(v) => v.to_string(), + Value::Char(v) => v.to_string(), + Value::Nothing => "()".to_string(), + Value::Error => "Error".to_string(), + } + ) + } +} + +impl From<()> for Value { + fn from(_: ()) -> Self { + Value::Nothing + } +} + +pub trait FromValue { + fn from_value(v: &Value) -> Option<&Self>; +} + +macro_rules! from_impl { + ($($type:ty => $variant:ident),+) => { + $( + impl From<$type> for Value { + fn from(v: $type) -> Self { + Value::$variant(v) + } + } + + impl FromValue for $type{ + fn from_value(v: &Value) -> Option<&Self> { + match v { + Value::$variant(v) => Some(v), + _ => None, + } + } + } + )+ + }; +} + +impl FromValue for Value { + fn from_value(v: &Value) -> Option<&Self> { + Some(v) + } +} + +from_impl!(i32 => Int, f32 => Float, String => String, bool => Bool, char => Char); + +impl From<&'_ str> for Value { + fn from(v: &str) -> Self { + Value::String(v.to_string()) + } +} + +impl FromValue for str { + fn from_value(v: &Value) -> Option<&Self> { + match v { + Value::String(v) => Some(v.as_str()), + _ => None, + } + } +} + +impl PartialOrd for Value { + fn partial_cmp(&self, other: &Self) -> Option { + match (self, other) { + (Value::Int(lhs), Value::Int(rhs)) => lhs.partial_cmp(rhs), + (Value::Float(lhs), Value::Float(rhs)) => lhs.partial_cmp(rhs), + (Value::String(lhs), Value::String(rhs)) => lhs.partial_cmp(rhs), + (Value::Bool(lhs), Value::Bool(rhs)) => lhs.partial_cmp(rhs), + (Value::Char(lhs), Value::Char(rhs)) => lhs.partial_cmp(rhs), + (Value::Nothing, Value::Nothing) => Some(std::cmp::Ordering::Equal), + (Value::Int(lhs), Value::Float(rhs)) => (*lhs as f32).partial_cmp(rhs), + (Value::Float(lhs), Value::Int(rhs)) => lhs.partial_cmp(&(*rhs as f32)), + _ => None, + } + } +}