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