regulus 0.0.14

A simple, interpreted language with very simple syntax and zero dependencies
Documentation
use crate::prelude::*;

#[derive(Debug, Clone)]
pub enum Argument {
    FunctionCall(FunctionCall, Span),
    Atom(Atom, Span),
    Variable(String, Span),
}

impl Argument {
    pub fn eval(&self, state: &mut State) -> Result<Atom> {
        if state.exit_unwind_value.is_some() {
            return Ok(Atom::Null);
        }
        state.backtrace.push(self.span().clone());
        if let Self::FunctionCall(call, _) = self {
            state.current_doc_comment = Some(call.doc_comment.clone());
            state.current_fn_name = Some(call.name.clone());
        } else {
            state.current_doc_comment = None;
            state.current_fn_name = None;
        }
        let res = match self {
            Self::FunctionCall(call, _) => call.eval(state),
            Self::Atom(atom, _) => Ok(atom.clone()),
            Self::Variable(var, _) => match state.storage.get(var) {
                Some(value) => Ok(value.clone()),
                None => raise!(state, "Name", "no variable named `{var}` found"),
            },
        };
        state.backtrace.pop();
        res
    }

    /// Returns the identifier of this variable.
    /// If it is not a variable, it raises an exception with the given error message.
    pub(crate) fn variable(&self, error_msg: &str, state: &State) -> Result<&str> {
        match self {
            Self::Variable(var, _) => Ok(var),
            _ => raise!(state, "Argument", error_msg),
        }
    }

    /// Returns an approximation of the source code of this argument.
    pub fn stringify(&self) -> String {
        match self {
            Self::Atom(atom, _) => atom.stringify(),
            Self::FunctionCall(call, _) => call.stringify(),
            Self::Variable(name, _) => name.clone(),
        }
    }

    /// Returns the span of this argument.
    pub const fn span(&self) -> &Span {
        match self {
            Self::Atom(_, s) | Self::FunctionCall(_, s) | Self::Variable(_, s) => s,
        }
    }
}

macro_rules! argument_eval_as_methods {
    ($($method_name: ident: $variant:ident -> $ty:ty;)*) => {
        impl Argument {
            $(
                pub fn $method_name(&self, state: &mut State) -> Result<$ty> {
                    match self.eval(state)? {
                        Atom::$variant(v) => Ok(v),
                        val => raise!(state, "Type", "{val} is not a {}", stringify!($variant)),
                    }
                }
            )*
        }
    };
}

impl Argument {
    pub fn eval_as_string(&self, state: &mut State) -> Result<String> {
        let val = self.eval(state)?;
        match val.as_string() {
            Some(s) => Ok(s),
            None => raise!(state, "Type", "{val} is not a list of chars"),
        }
    }

    pub(crate) fn eval_mode(&self, state: &mut State) -> i64 {
        self.eval_int(state)
            .expect("mode argument must be an integer")
    }
}

// method name, atom variant name, rust type
argument_eval_as_methods! {
    eval_int: Int -> i64;
    eval_bool: Bool -> bool;
    eval_char: Char -> char;
    eval_list: List -> List;
    eval_function: Function -> Function;
    eval_object: Object -> Object;
}