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:
Julius 2022-07-03 20:32:31 +02:00
parent c67cbf66e6
commit 25a9a772e6
Signed by: j00lz
GPG key ID: AF241B0AA237BBA2
9 changed files with 298 additions and 20 deletions

7
Cargo.lock generated
View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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();

View file

@ -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,19 +137,24 @@ math_trait!(Rem, rem);
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(),
}
)
if let Value::Custom(c) = self {
c.format(f)
} else {
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(),
_ => 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);
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
View 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()
}
}

View file

@ -9,3 +9,11 @@ let x = 10;
let y = x-5;
print(y);
print( edit_foo(native_test()) );
// Parameter 0 was not of type sunflower::Foo, it was string
// fancy error isn't it?
// edit_foo("hello");
// this doesnt work yet :((((
// print(10.eq(20));