regulus 0.0.14

A simple, interpreted language with very simple syntax and zero dependencies
Documentation
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)
    }

    /// Returns an approximation of the source code of this function call.
    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)
    }

    /// Assuming that this is a constructor function, this creates a new constructor that
    /// sets `field` to `value` for all newly constructed objects.
    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))
        })
    }
}

// the callback cannot be debugged
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
    }
}