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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dyn-clone"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "140206b78fb2bc3edbcfc9b5ccbd0b30699cfe8d348b8b31b330e47df5291a5a"
|
||||
|
||||
[[package]]
|
||||
name = "fake-simd"
|
||||
version = "0.1.2"
|
||||
|
@ -148,6 +154,7 @@ dependencies = [
|
|||
name = "sunflower"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"dyn-clone",
|
||||
"pest",
|
||||
"pest_derive",
|
||||
]
|
||||
|
|
|
@ -6,6 +6,7 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
dyn-clone = "1.0.6"
|
||||
|
||||
pest = "2.1.3"
|
||||
pest_derive = "2.1.0"
|
||||
|
|
|
@ -43,6 +43,7 @@ pub enum Expression {
|
|||
Constant(Value),
|
||||
Variable(String),
|
||||
Call(String, Vec<Expression>),
|
||||
CallMethod(Box<Expression>, String, Vec<Expression>),
|
||||
Binary(Box<Expression>, BinaryOperation, Box<Expression>),
|
||||
}
|
||||
|
||||
|
@ -58,6 +59,17 @@ impl Expression {
|
|||
&args.iter().map(|ex| ex.eval(e, f)).collect::<Vec<_>>(),
|
||||
)
|
||||
.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) => {
|
||||
let lhs = lhs.eval(e, f);
|
||||
let rhs = rhs.eval(e, f);
|
||||
|
@ -179,4 +191,29 @@ mod tests {
|
|||
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 }
|
||||
COMMENT = _{ "/*" ~ (!"*/" ~ ANY)* ~ "*/" | "//" ~ (!NEWLINE ~ ANY)* }
|
||||
|
||||
number = @{
|
||||
"-"?
|
||||
|
@ -21,7 +22,7 @@ char = ${ "\'" ~ character ~ "\'" }
|
|||
|
||||
boolean = { "true" | "false" }
|
||||
|
||||
identifier = @{ ASCII_ALPHA ~ ASCII_ALPHANUMERIC* }
|
||||
identifier = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "_" )* }
|
||||
|
||||
call = ${
|
||||
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 function::FunctionMap;
|
||||
use statement::Statement;
|
||||
use value::Value;
|
||||
use value::{custom::CustomValue, Value};
|
||||
|
||||
mod env;
|
||||
mod expression;
|
||||
|
@ -20,6 +20,29 @@ fn run(functions: FunctionMap, statement: Statement) {
|
|||
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() {
|
||||
let f = read_to_string("./test.foo").unwrap();
|
||||
let (res, funcs) = pest_parser::parse(f);
|
||||
|
@ -29,5 +52,11 @@ fn main() {
|
|||
println!("{}", a);
|
||||
});
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -138,7 +138,20 @@ fn handle_rules(mut p: pest::iterators::Pairs<Rule>) -> (Statement, FuncHolder)
|
|||
match pair.as_rule() {
|
||||
Rule::expression => {
|
||||
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 => {
|
||||
let mut inner = pair.into_inner();
|
||||
|
|
116
src/value.rs
116
src/value.rs
|
@ -1,15 +1,24 @@
|
|||
use std::{
|
||||
any::Any,
|
||||
cmp::Ordering,
|
||||
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 {
|
||||
Int(i32),
|
||||
Float(f32),
|
||||
String(String),
|
||||
Bool(bool),
|
||||
Char(char),
|
||||
Custom(Box<dyn CustomValue>),
|
||||
#[default]
|
||||
Nothing,
|
||||
Error,
|
||||
|
@ -25,22 +34,84 @@ impl Value {
|
|||
Value::String(v) => !v.is_empty(),
|
||||
Value::Bool(v) => *v,
|
||||
Value::Char(v) => *v != '\0',
|
||||
Value::Custom(_) => false,
|
||||
Value::Nothing => false,
|
||||
Value::Error => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value_kind(&self) -> &'static str {
|
||||
pub fn value_kind(&self) -> &str {
|
||||
match self {
|
||||
Value::Int(_) => "int",
|
||||
Value::Float(_) => "float",
|
||||
Value::String(_) => "string",
|
||||
Value::Bool(_) => "bool",
|
||||
Value::Char(_) => "char",
|
||||
Value::Custom(c) => c.value_kind(),
|
||||
Value::Nothing => "nothing",
|
||||
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 {
|
||||
|
@ -66,6 +137,9 @@ math_trait!(Rem, rem);
|
|||
|
||||
impl Display for Value {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if let Value::Custom(c) = self {
|
||||
c.format(f)
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
|
@ -77,10 +151,12 @@ impl Display for Value {
|
|||
Value::Char(v) => v.to_string(),
|
||||
Value::Nothing => "()".to_string(),
|
||||
Value::Error => "Error".to_string(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<()> for Value {
|
||||
fn from(_: ()) -> Self {
|
||||
|
@ -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);
|
||||
|
||||
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 {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
match (self, other) {
|
||||
|
@ -147,6 +255,8 @@ impl PartialOrd for Value {
|
|||
(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)),
|
||||
(Value::Custom(lhs), other) => lhs.as_ref().partial_cmp(other),
|
||||
(other, Value::Custom(rhs)) => rhs.as_ref().partial_cmp(other).map(Ordering::reverse),
|
||||
_ => 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