Initial Commit
This commit is contained in:
commit
fc942b9bc0
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/target
|
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal 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
8
Cargo.toml
Normal 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
63
src/env.rs
Normal 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
153
src/expression.rs
Normal 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)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
67
src/function/function_impl.rs
Normal file
67
src/function/function_impl.rs
Normal 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
112
src/function/mod.rs
Normal 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
19
src/macros.rs
Normal 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
125
src/main.rs
Normal 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
35
src/statement.rs
Normal 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
185
src/value.rs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue