Add custom types. Make an attempt at method calls.
You just need to implement `CustomValue`, `Debug` and `Clone` for your own types, and the type can be used in your code. Next on the list: method calls (e.g. x.y(), instead of y(x))
This commit is contained in:
parent
c67cbf66e6
commit
25a9a772e6
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -44,6 +44,12 @@ dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dyn-clone"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "140206b78fb2bc3edbcfc9b5ccbd0b30699cfe8d348b8b31b330e47df5291a5a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fake-simd"
|
name = "fake-simd"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
@ -148,6 +154,7 @@ dependencies = [
|
||||||
name = "sunflower"
|
name = "sunflower"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"dyn-clone",
|
||||||
"pest",
|
"pest",
|
||||||
"pest_derive",
|
"pest_derive",
|
||||||
]
|
]
|
||||||
|
|
|
@ -6,6 +6,7 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
dyn-clone = "1.0.6"
|
||||||
|
|
||||||
pest = "2.1.3"
|
pest = "2.1.3"
|
||||||
pest_derive = "2.1.0"
|
pest_derive = "2.1.0"
|
||||||
|
|
|
@ -43,6 +43,7 @@ pub enum Expression {
|
||||||
Constant(Value),
|
Constant(Value),
|
||||||
Variable(String),
|
Variable(String),
|
||||||
Call(String, Vec<Expression>),
|
Call(String, Vec<Expression>),
|
||||||
|
CallMethod(Box<Expression>, String, Vec<Expression>),
|
||||||
Binary(Box<Expression>, BinaryOperation, Box<Expression>),
|
Binary(Box<Expression>, BinaryOperation, Box<Expression>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,6 +59,17 @@ impl Expression {
|
||||||
&args.iter().map(|ex| ex.eval(e, f)).collect::<Vec<_>>(),
|
&args.iter().map(|ex| ex.eval(e, f)).collect::<Vec<_>>(),
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
|
Expression::CallMethod(target, name, args) => {
|
||||||
|
let target = target.eval(e, f);
|
||||||
|
let v = vec![target.clone()];
|
||||||
|
let args = v
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.chain(args.iter().map(|ex| ex.eval(e, f)))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
target.call_method(name, e, f, &args)
|
||||||
|
}
|
||||||
Expression::Binary(lhs, op, rhs) => {
|
Expression::Binary(lhs, op, rhs) => {
|
||||||
let lhs = lhs.eval(e, f);
|
let lhs = lhs.eval(e, f);
|
||||||
let rhs = rhs.eval(e, f);
|
let rhs = rhs.eval(e, f);
|
||||||
|
@ -179,4 +191,29 @@ mod tests {
|
||||||
Value::Int(20)
|
Value::Int(20)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eq_method_test() {
|
||||||
|
let f = FunctionMap::new();
|
||||||
|
let e = Env::new();
|
||||||
|
e.set("test", 10);
|
||||||
|
assert_eq!(
|
||||||
|
Expression::CallMethod(
|
||||||
|
Box::new(Expression::Variable("test".to_string())),
|
||||||
|
"eq".to_string(),
|
||||||
|
vec![Expression::Constant(Value::Int(10))]
|
||||||
|
)
|
||||||
|
.eval(&e, &f),
|
||||||
|
Value::Bool(true)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Expression::CallMethod(
|
||||||
|
Box::new(Expression::Variable("test".to_string())),
|
||||||
|
"eq".to_string(),
|
||||||
|
vec![Expression::Constant(Value::Int(11))]
|
||||||
|
)
|
||||||
|
.eval(&e, &f),
|
||||||
|
Value::Bool(false)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
WHITESPACE = _{ " " | "\t" | NEWLINE }
|
WHITESPACE = _{ " " | "\t" | NEWLINE }
|
||||||
|
COMMENT = _{ "/*" ~ (!"*/" ~ ANY)* ~ "*/" | "//" ~ (!NEWLINE ~ ANY)* }
|
||||||
|
|
||||||
number = @{
|
number = @{
|
||||||
"-"?
|
"-"?
|
||||||
|
@ -21,7 +22,7 @@ char = ${ "\'" ~ character ~ "\'" }
|
||||||
|
|
||||||
boolean = { "true" | "false" }
|
boolean = { "true" | "false" }
|
||||||
|
|
||||||
identifier = @{ ASCII_ALPHA ~ ASCII_ALPHANUMERIC* }
|
identifier = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "_" )* }
|
||||||
|
|
||||||
call = ${
|
call = ${
|
||||||
identifier ~ call_params
|
identifier ~ call_params
|
||||||
|
|
33
src/main.rs
33
src/main.rs
|
@ -1,9 +1,9 @@
|
||||||
use std::fs::read_to_string;
|
use std::{any::Any, fs::read_to_string};
|
||||||
|
|
||||||
use env::Env;
|
use env::Env;
|
||||||
use function::FunctionMap;
|
use function::FunctionMap;
|
||||||
use statement::Statement;
|
use statement::Statement;
|
||||||
use value::Value;
|
use value::{custom::CustomValue, Value};
|
||||||
|
|
||||||
mod env;
|
mod env;
|
||||||
mod expression;
|
mod expression;
|
||||||
|
@ -20,6 +20,29 @@ fn run(functions: FunctionMap, statement: Statement) {
|
||||||
statement.eval(&env, &functions);
|
statement.eval(&env, &functions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
struct Foo {
|
||||||
|
x: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CustomValue for Foo {
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "Foo({})", self.x)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eq(&self, other: &Value) -> bool {
|
||||||
|
if let Some(v) = other.as_t() {
|
||||||
|
self == v
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let f = read_to_string("./test.foo").unwrap();
|
let f = read_to_string("./test.foo").unwrap();
|
||||||
let (res, funcs) = pest_parser::parse(f);
|
let (res, funcs) = pest_parser::parse(f);
|
||||||
|
@ -29,5 +52,11 @@ fn main() {
|
||||||
println!("{}", a);
|
println!("{}", a);
|
||||||
});
|
});
|
||||||
m.insert_native("waluigi", || "Waluigi");
|
m.insert_native("waluigi", || "Waluigi");
|
||||||
|
m.insert_native("native_test", || Foo { x: 41 });
|
||||||
|
m.insert_native("edit_foo", |f: &Foo| {
|
||||||
|
let mut f = f.clone();
|
||||||
|
f.x += 1;
|
||||||
|
f
|
||||||
|
});
|
||||||
run(m, res);
|
run(m, res);
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,7 +138,20 @@ fn handle_rules(mut p: pest::iterators::Pairs<Rule>) -> (Statement, FuncHolder)
|
||||||
match pair.as_rule() {
|
match pair.as_rule() {
|
||||||
Rule::expression => {
|
Rule::expression => {
|
||||||
let mut inner = pair.into_inner();
|
let mut inner = pair.into_inner();
|
||||||
parse_expression(inner.next().unwrap())
|
let expr = parse_expression(inner.next().unwrap());
|
||||||
|
if let Some(c) = inner.next() {
|
||||||
|
let mut inner = c.into_inner();
|
||||||
|
let name = inner.next().unwrap();
|
||||||
|
let args = inner
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.into_inner()
|
||||||
|
.map(parse_expression)
|
||||||
|
.collect();
|
||||||
|
Expression::CallMethod(Box::new(expr), name.as_str().to_string(), args)
|
||||||
|
} else {
|
||||||
|
expr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Rule::equality => {
|
Rule::equality => {
|
||||||
let mut inner = pair.into_inner();
|
let mut inner = pair.into_inner();
|
||||||
|
|
142
src/value.rs
142
src/value.rs
|
@ -1,15 +1,24 @@
|
||||||
use std::{
|
use std::{
|
||||||
|
any::Any,
|
||||||
|
cmp::Ordering,
|
||||||
fmt::Display,
|
fmt::Display,
|
||||||
ops::{Add, Div, Mul, Sub, Rem},
|
ops::{Add, Div, Mul, Rem, Sub},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Default)]
|
use crate::{env::Env, function::FunctionMap};
|
||||||
|
|
||||||
|
use self::custom::CustomValue;
|
||||||
|
|
||||||
|
pub mod custom;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
Int(i32),
|
Int(i32),
|
||||||
Float(f32),
|
Float(f32),
|
||||||
String(String),
|
String(String),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
Char(char),
|
Char(char),
|
||||||
|
Custom(Box<dyn CustomValue>),
|
||||||
#[default]
|
#[default]
|
||||||
Nothing,
|
Nothing,
|
||||||
Error,
|
Error,
|
||||||
|
@ -25,22 +34,84 @@ impl Value {
|
||||||
Value::String(v) => !v.is_empty(),
|
Value::String(v) => !v.is_empty(),
|
||||||
Value::Bool(v) => *v,
|
Value::Bool(v) => *v,
|
||||||
Value::Char(v) => *v != '\0',
|
Value::Char(v) => *v != '\0',
|
||||||
|
Value::Custom(_) => false,
|
||||||
Value::Nothing => false,
|
Value::Nothing => false,
|
||||||
Value::Error => false,
|
Value::Error => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn value_kind(&self) -> &'static str {
|
pub fn value_kind(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
Value::Int(_) => "int",
|
Value::Int(_) => "int",
|
||||||
Value::Float(_) => "float",
|
Value::Float(_) => "float",
|
||||||
Value::String(_) => "string",
|
Value::String(_) => "string",
|
||||||
Value::Bool(_) => "bool",
|
Value::Bool(_) => "bool",
|
||||||
Value::Char(_) => "char",
|
Value::Char(_) => "char",
|
||||||
|
Value::Custom(c) => c.value_kind(),
|
||||||
Value::Nothing => "nothing",
|
Value::Nothing => "nothing",
|
||||||
Value::Error => "error",
|
Value::Error => "error",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_any(&self) -> &dyn Any {
|
||||||
|
match self {
|
||||||
|
Value::Int(i) => i,
|
||||||
|
Value::Float(f) => f,
|
||||||
|
Value::String(s) => s,
|
||||||
|
Value::Bool(b) => b,
|
||||||
|
Value::Char(c) => c,
|
||||||
|
Value::Custom(c) => c.as_any(),
|
||||||
|
Value::Nothing => &() as &dyn Any,
|
||||||
|
Value::Error => &() as &dyn Any,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_t<T>(&self) -> Option<&T>
|
||||||
|
where
|
||||||
|
T: Any + 'static,
|
||||||
|
{
|
||||||
|
self.as_any().downcast_ref::<T>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn value_functions() -> FunctionMap {
|
||||||
|
let mut map = FunctionMap::new();
|
||||||
|
map.insert_native("eq", Self::eq);
|
||||||
|
map.insert_native("ne", Self::ne);
|
||||||
|
map.insert_native("lt", Self::lt);
|
||||||
|
map.insert_native("le", Self::le);
|
||||||
|
map.insert_native("gt", Self::gt);
|
||||||
|
map.insert_native("ge", Self::ge);
|
||||||
|
|
||||||
|
map
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inner_functions(&self) -> Option<&FunctionMap> {
|
||||||
|
if let Self::Custom(c) = self {
|
||||||
|
c.functions()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn call_method(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
env: &Env,
|
||||||
|
_global_functions: &FunctionMap,
|
||||||
|
args: &[Value],
|
||||||
|
) -> Value {
|
||||||
|
if let Some(i) = self.inner_functions() {
|
||||||
|
match i.call(name, env, args) {
|
||||||
|
Ok(r) => return r,
|
||||||
|
Err(e) => println!("{}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match Value::value_functions().call(name, env, args) {
|
||||||
|
Ok(r) => return r,
|
||||||
|
Err(e) => println!("{}", e),
|
||||||
|
}
|
||||||
|
Value::Nothing
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! math_trait {
|
macro_rules! math_trait {
|
||||||
|
@ -66,19 +137,24 @@ math_trait!(Rem, rem);
|
||||||
|
|
||||||
impl Display for Value {
|
impl Display for Value {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(
|
if let Value::Custom(c) = self {
|
||||||
f,
|
c.format(f)
|
||||||
"{}",
|
} else {
|
||||||
match self {
|
write!(
|
||||||
Value::Int(v) => v.to_string(),
|
f,
|
||||||
Value::Float(v) => v.to_string(),
|
"{}",
|
||||||
Value::String(v) => v.clone(),
|
match self {
|
||||||
Value::Bool(v) => v.to_string(),
|
Value::Int(v) => v.to_string(),
|
||||||
Value::Char(v) => v.to_string(),
|
Value::Float(v) => v.to_string(),
|
||||||
Value::Nothing => "()".to_string(),
|
Value::String(v) => v.clone(),
|
||||||
Value::Error => "Error".to_string(),
|
Value::Bool(v) => v.to_string(),
|
||||||
}
|
Value::Char(v) => v.to_string(),
|
||||||
)
|
Value::Nothing => "()".to_string(),
|
||||||
|
Value::Error => "Error".to_string(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,6 +195,21 @@ impl FromValue for Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: 'static + CustomValue> FromValue for T {
|
||||||
|
fn from_value(v: &Value) -> Option<&Self> {
|
||||||
|
match v {
|
||||||
|
Value::Custom(v) => v.as_any().downcast_ref(),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: CustomValue> From<T> for Value {
|
||||||
|
fn from(v: T) -> Self {
|
||||||
|
Value::Custom(v.clone_box())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
from_impl!(i32 => Int, f32 => Float, String => String, bool => Bool, char => Char);
|
from_impl!(i32 => Int, f32 => Float, String => String, bool => Bool, char => Char);
|
||||||
|
|
||||||
impl From<&'_ str> for Value {
|
impl From<&'_ str> for Value {
|
||||||
|
@ -136,6 +227,23 @@ impl FromValue for str {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Value {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
match (self, other) {
|
||||||
|
(Value::Int(lhs), Value::Int(rhs)) => lhs == rhs,
|
||||||
|
(Value::Float(lhs), Value::Float(rhs)) => lhs == rhs,
|
||||||
|
(Value::String(lhs), Value::String(rhs)) => lhs == rhs,
|
||||||
|
(Value::Bool(lhs), Value::Bool(rhs)) => lhs == rhs,
|
||||||
|
(Value::Char(lhs), Value::Char(rhs)) => lhs == rhs,
|
||||||
|
(Value::Custom(lhs), other) => lhs.as_ref() == other,
|
||||||
|
(other, Value::Custom(rhs)) => rhs.as_ref() == other,
|
||||||
|
(Value::Nothing, Value::Nothing) => true,
|
||||||
|
(Value::Error, Value::Error) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PartialOrd for Value {
|
impl PartialOrd for Value {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
|
@ -147,6 +255,8 @@ impl PartialOrd for Value {
|
||||||
(Value::Nothing, Value::Nothing) => Some(std::cmp::Ordering::Equal),
|
(Value::Nothing, Value::Nothing) => Some(std::cmp::Ordering::Equal),
|
||||||
(Value::Int(lhs), Value::Float(rhs)) => (*lhs as f32).partial_cmp(rhs),
|
(Value::Int(lhs), Value::Float(rhs)) => (*lhs as f32).partial_cmp(rhs),
|
||||||
(Value::Float(lhs), Value::Int(rhs)) => lhs.partial_cmp(&(*rhs as f32)),
|
(Value::Float(lhs), Value::Int(rhs)) => lhs.partial_cmp(&(*rhs as f32)),
|
||||||
|
(Value::Custom(lhs), other) => lhs.as_ref().partial_cmp(other),
|
||||||
|
(other, Value::Custom(rhs)) => rhs.as_ref().partial_cmp(other).map(Ordering::reverse),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
72
src/value/custom.rs
Normal file
72
src/value/custom.rs
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
use crate::function::FunctionMap;
|
||||||
|
|
||||||
|
use super::Value;
|
||||||
|
|
||||||
|
fn type_name_of_val<T: ?Sized>(_val: &T) -> &'static str {
|
||||||
|
std::any::type_name::<T>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait CustomValue: std::fmt::Debug + BoxClone {
|
||||||
|
/// Cursed workaround. Since every type has to implement this
|
||||||
|
/// method, we can get a handle back to the type.
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any;
|
||||||
|
|
||||||
|
fn value_kind(&self) -> &'static str {
|
||||||
|
type_name_of_val(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{:?}", self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eq(&self, _: &Value) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ne(&self, other: &Value) -> bool {
|
||||||
|
!self.eq(other)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn partial_cmp(&self, _: &Value) -> Option<std::cmp::Ordering> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn functions(&self) -> Option<&FunctionMap> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<Value> for dyn CustomValue {
|
||||||
|
fn eq(&self, other: &Value) -> bool {
|
||||||
|
self.eq(other)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ne(&self, other: &Value) -> bool {
|
||||||
|
self.ne(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd<Value> for dyn CustomValue {
|
||||||
|
fn partial_cmp(&self, other: &Value) -> Option<std::cmp::Ordering> {
|
||||||
|
self.partial_cmp(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait BoxClone {
|
||||||
|
fn clone_box(&self) -> Box<dyn CustomValue>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> BoxClone for T
|
||||||
|
where
|
||||||
|
T: 'static + CustomValue + Clone,
|
||||||
|
{
|
||||||
|
fn clone_box(&self) -> Box<dyn CustomValue> {
|
||||||
|
Box::new(self.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Box<dyn CustomValue> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
self.clone_box()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue