Initial Commit

This commit is contained in:
Julius 2022-07-02 23:42:15 +02:00
commit fc942b9bc0
Signed by: j00lz
GPG key ID: AF241B0AA237BBA2
11 changed files with 775 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

7
Cargo.lock generated Normal file
View file

@ -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"

8
Cargo.toml Normal file
View file

@ -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]

63
src/env.rs Normal file
View file

@ -0,0 +1,63 @@
use std::{collections::HashMap, sync::{Arc, RwLock}};
use crate::value::Value;
#[derive(Debug, Clone)]
pub struct Env<'env> {
values: Arc<RwLock<HashMap<String, Value>>>,
parent: Option<&'env Env<'env>>,
}
impl Env<'_> {
pub fn new() -> Self {
Env {
values: Default::default(),
parent: None,
}
}
pub fn get(&self, name: &str) -> Option<Value> {
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<Value>) {
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),
}
}
}

153
src/expression.rs Normal file
View file

@ -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<Expression>),
Binary(Box<Expression>, BinaryOperation, Box<Expression>),
}
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::<Vec<_>>()).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<Value> 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)
);
}
}

View file

@ -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<F,R,$($ty,)*> NativeFunction<($($ty,)*)> for F
where
F: Fn($(&$ty,)*) -> R,
R: Into<Value>,
$($ty: FromValue,)*
{
fn native_execute(&self, _: &Env, args: &[Value]) -> Result<Value, String> {
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<F,R,$($ty,)*> NativeFunction<((), $($ty,)*)> for F
where
F: Fn(&Env, $(&$ty,)*) -> R,
R: Into<Value>,
$($ty: FromValue,)*
{
fn native_execute(&self, env: &Env, args: &[Value]) -> Result<Value, String> {
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);

112
src/function/mod.rs Normal file
View file

@ -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<String, (Box<dyn Function>, FunctionType)>);
impl FunctionMap {
pub fn new() -> Self {
Default::default()
}
pub fn insert_native<S, P, F>(&mut self, name: S, f: F)
where
S: ToString,
P: 'static,
F: 'static + NativeFunction<P>,
{
self.0.insert(
name.to_string(),
(Box::new(f.into_function()), FunctionType::Native),
);
}
pub fn insert_interpreter(&mut self, name: &str, params: Vec<String>, body: Vec<Statement>) {
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<Value, String> {
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<P>: Sized {
fn native_execute(&self, env: &Env, args: &[Value]) -> Result<Value, String>;
fn into_function(self) -> IntoFunction<Self, P> {
IntoFunction::new(self)
}
}
pub struct IntoFunction<F, P> {
func: F,
_phantom: PhantomData<P>,
}
impl<F, P> IntoFunction<F, P> {
fn new(func: F) -> Self {
IntoFunction {
func,
_phantom: PhantomData,
}
}
}
impl<F: NativeFunction<P>, P> Function for IntoFunction<F, P> {
fn execute(&self, env: &Env, _: &FunctionMap, args: &[Value]) -> Result<Value, String> {
self.func.native_execute(env, args)
}
}
pub struct InterpreterFunction {
parameters: Vec<String>,
body: Vec<Statement>,
}
impl InterpreterFunction {
pub fn new(parameters: Vec<String>, body: Vec<Statement>) -> Self {
InterpreterFunction { parameters, body }
}
}
impl Function for InterpreterFunction {
fn execute(&self, env: &Env, map: &FunctionMap, args: &[Value]) -> Result<Value, String> {
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<Value, String>;
}

19
src/macros.rs Normal file
View file

@ -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_;

125
src/main.rs Normal file
View file

@ -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<Box<dyn Function>> = 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![])],
))],
);
}

35
src/statement.rs Normal file
View file

@ -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<Statement>),
}
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);
}
}
}
}
}
}

185
src/value.rs Normal file
View file

@ -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<i32> {
// match self {
// Value::Int(v) => Some(*v),
// _ => None,
// }
// }
// pub fn as_float(&self) -> Option<f32> {
// match self {
// Value::Float(v) => Some(*v),
// _ => None,
// }
// }
// pub fn as_string(&self) -> Option<String> {
// match self {
// Value::String(v) => Some(v.clone()),
// _ => None,
// }
// }
// pub fn as_bool(&self) -> Option<bool> {
// match self {
// Value::Bool(v) => Some(*v),
// _ => None,
// }
// }
// pub fn as_char(&self) -> Option<char> {
// 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<std::cmp::Ordering> {
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,
}
}
}