use crate::prelude::*;
use std::fmt;
use std::rc::Rc;
#[derive(Debug, Clone)]
pub struct FunctionCall {
pub args: Vec<Argument>,
pub name: String,
pub doc_comment: String,
}
impl FunctionCall {
pub fn eval(&self, state: &mut State) -> Result<Atom> {
if state.exit_unwind_value.is_some() {
return Ok(Atom::Null);
}
state.get_function(&self.name)?.call(state, &self.args)
}
pub fn stringify(&self) -> String {
format!(
"{}({})",
self.name,
self.args
.iter()
.map(Argument::stringify)
.collect::<Vec<_>>()
.join(", ")
)
}
}
pub type FunctionBody = dyn Fn(&mut State, &[Argument]) -> Result<Atom>;
struct FunctionInner {
doc: String,
argc: Option<usize>,
body: Box<FunctionBody>,
}
#[derive(Clone)]
pub struct Function(Rc<FunctionInner>);
impl Function {
pub fn new(
doc: impl Into<String>,
argc: Option<usize>,
body: impl Fn(&mut State, &[Argument]) -> Result<Atom> + 'static,
) -> Self {
Self(Rc::new(FunctionInner {
doc: doc.into(),
argc,
body: Box::new(body),
}))
}
pub fn doc(&self) -> &str {
self.0.doc.as_str()
}
pub fn argc(&self) -> Option<usize> {
self.0.argc
}
pub fn body(&self) -> &FunctionBody {
&self.0.body
}
pub fn call(&self, state: &mut State, args: &[Argument]) -> Result<Atom> {
if let Some(argc) = self.argc() {
let arg_len = args.len();
if argc != arg_len {
let current_name = state.current_fn_name.as_ref().unwrap();
raise!(
state,
"Argument",
"expected `{argc}` args, found `{arg_len}` args for `{current_name}`",
);
}
}
(self.body())(state, args)
}
pub(crate) fn wrap_ctor(self, field: &str, value: Atom) -> Self {
let doc = self.doc().to_owned();
let field = field.to_owned();
Self::new(doc, self.argc(), move |state, args| {
let mut obj = self
.clone()
.call(state, args)?
.object()
.ok_or_else(|| state.raise("Type", "expected a type constructor"))?;
obj.data_mut().insert(field.clone(), value.clone());
Ok(Atom::Object(obj))
})
}
}
impl fmt::Debug for Function {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Function")
.field("doc", &self.0.doc)
.field("argc", &self.0.argc)
.field("body", &"..")
.finish()
}
}
impl PartialEq for Function {
fn eq(&self, _other: &Self) -> bool {
false
}
}