sunflower/src/function/mod.rs

122 lines
3.0 KiB
Rust

use std::{collections::HashMap, marker::PhantomData};
use crate::{
env::Env,
pest_parser::FuncHolder,
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 {
FunctionMap::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 insert_holder(&mut self, holder: FuncHolder) {
for f in holder {
self.insert_interpreter(&f.0, f.1, f.2);
}
}
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)
}
}
struct InterpreterFunction {
parameters: Vec<String>,
body: Vec<Statement>,
}
impl InterpreterFunction {
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_here(name, value.clone());
}
for stmt in &self.body {
if let Some(v) = stmt.eval(&sub_env, map){
return Ok(v);
}
}
Ok(Value::Nothing)
}
}
pub trait Function {
fn execute(&self, env: &Env, map: &FunctionMap, args: &[Value]) -> Result<Value, String>;
}