use std::rc::Rc; use crate::{ environment::Environment, node::{closure::Closure, function::FunctionType}, }; use super::{Error, Node, NodeEnum, Precedence}; #[derive(Debug, Clone, PartialEq, PartialOrd)] pub struct Call { function: Rc, arguments: Vec>, } impl Node for Call { fn evaluate(&self, env: &mut Environment) -> Result, Error> { if env.closures_enabled() { return Ok(Rc::new(self.clone().into())); } // Evaluate callee and error if its not a function let evaluated = self.function.evaluate(env)?; // let func = if let NodeEnum::Function(func) = evaluated.as_ref() { // func // } else { // return Ok(Rc::new(self.clone().into())); // }; env.push_stack()?; let func = match evaluated.as_ref() { NodeEnum::Function(func) => func, NodeEnum::Closure(closure) => { for (k, v) in closure.get_captured_variables() { env.insert(*k, v.clone(), false); } closure.get_function() } _ => { return Ok(Rc::new(self.clone().into())); } }; let arguments = { let mut arguments = vec![]; for value in self.arguments.iter() { arguments.push(value.evaluate(env)?); } arguments }; // Call function body with arguments let ret = match func.get_body() { // Pass arguments to native function FunctionType::Native(_name, native_function) => native_function(&arguments, env), FunctionType::UserFunction(body, fargs) => { if fargs.len() != arguments.len() { return Err(Error::ArgumentCount(fargs.len(), arguments.len())); } // Define variables fargs.iter().zip(&arguments).for_each(|(symbol, value)| { env.insert(symbol.get_value(), value.clone(), false); }); let ev = body.evaluate(env); if ev.is_err() { env.pop_stack()?; return ev; } let ev = ev?; // Return evaluated return value for function Ok(match ev.as_ref() { NodeEnum::Function(hof) => { let captures = fargs.iter().map(|a| a.get_value()).zip(arguments).collect(); Closure::new(hof.clone(), captures) } _ => ev, }) } }; env.pop_stack()?; ret } fn as_string(&self, env: Option<&Environment>) -> String { let arguments = format!( "{}", self.arguments .iter() .map(|x| x.as_string(env)) .reduce(|a, b| a + ", " + &b) .unwrap_or("".to_owned()) ); format!("{}({})", self.function.as_string(env), arguments) } fn precedence(&self) -> Precedence { Precedence::Call } } impl Call { pub fn new(function: Rc, arguments: Vec>) -> Rc { Rc::new( Self { function, arguments, } .into(), ) } }