val 0.6.0

An arbitrary precision calculator language
Documentation
use super::*;

#[derive(Debug)]
pub struct UserFunction {
  pub body: Vec<Spanned<Statement>>,
  pub environment: Environment,
  pub name: Option<String>,
  pub parameters: Vec<String>,
}

impl UserFunction {
  pub(crate) fn call(
    function: &Gc<Self>,
    arguments: Vec<Value>,
  ) -> Result<Value> {
    let environment = Environment::with_parent(function.environment.clone());

    if let Some(name) = &function.name {
      environment.add_symbol(
        name,
        Value::Function(Function::UserDefined(function.clone())),
      );
    }

    for (parameter, argument) in function.parameters.iter().zip(arguments) {
      environment.add_symbol(parameter, argument);
    }

    match Evaluator::for_function(environment)
      .evaluate_statements(&function.body)?
    {
      Completion::Return(value) | Completion::Value(value) => Ok(value),
      Completion::Break | Completion::Continue => Ok(Value::Null),
    }
  }

  pub(crate) fn check_arity(&self, len: usize, span: &Span) -> Result<()> {
    BuiltinArity::Exact(self.parameters.len()).check(self.name(), len, span)
  }

  pub(crate) fn name(&self) -> &str {
    self.name.as_deref().unwrap_or("<anonymous>")
  }
}

impl Finalize for UserFunction {}

unsafe impl Trace for UserFunction {
  gc::custom_trace!(this, {
    let Self {
      body: _,
      environment,
      name: _,
      parameters: _,
    } = this;

    unsafe { mark(environment) };
  });
}