use std::borrow::Cow;
use std::collections::BTreeMap;
use std::sync::LazyLock;

#[cfg(feature = "debugger")]
use crate::DebuggerHandler;
use crate::Module;
#[cfg(feature = "debugger")]
use crate::ast::constants;
#[cfg(feature = "debugger")]
use crate::eval::debugger::DefaultDebuggerHandler;
#[cfg(feature = "debugger")]
use crate::eval::debugger::Source;
use crate::module::resolver::DefaultModuleResolver;
use crate::{
    Ident, Program, Shared, SharedCell, Token, TokenKind,
    arena::Arena,
    ast::{
        TokenId,
        node::{self as ast, Branches, MatchArms, Pattern},
    },
    error::InnerError,
    get_token,
    macro_expand::{Macro, MacroEvaluator},
};
use crate::{
    IdentWithToken, ModuleResolver,
    error::runtime::RuntimeError,
    eval::{env::EnvError, runtime_value::ModuleEnv},
    module::{self, error::ModuleError},
    selector::Selector,
};

#[cfg(feature = "debugger")]
use debugger::{Breakpoint, DebugContext, Debugger};

pub mod builtin;
#[cfg(feature = "debugger")]
pub mod debugger;
pub mod env;
pub mod runtime_value;

use env::Env;
use runtime_value::RuntimeValue;

static TYPE_IDENT: LazyLock<Ident> = LazyLock::new(|| Ident::new("type"));
static DYNAMIC_IDENT: LazyLock<Ident> = LazyLock::new(|| Ident::new("<dynamic>"));

/// Control flow signals for internal evaluation.
///
/// These are not errors, but signals used within the evaluator
/// to handle `break` and `continue` statements in loops.
#[derive(Debug, Clone, PartialEq)]
pub(crate) enum ControlFlow {
    /// Signal to break out of a loop, with the token for error reporting and an optional value.
    /// The value is boxed to keep the size of the enum small.
    Break(Token, Option<Box<RuntimeValue>>),
    /// Signal to continue to the next iteration, with the token for error reporting.
    Continue(Token),
}

/// Internal evaluation error that combines control flow and runtime errors.
///
/// This type is used internally by the evaluator and should not be exposed
/// to external consumers. External APIs convert this to `RuntimeError`.
#[derive(Debug)]
pub(crate) enum EvalError {
    /// Control flow signal (break/continue).
    Flow(ControlFlow),
    /// Runtime error.
    Runtime(RuntimeError),
}

impl From<RuntimeError> for EvalError {
    fn from(err: RuntimeError) -> Self {
        EvalError::Runtime(err)
    }
}

impl EvalError {
    /// Converts to RuntimeError, treating control flow as unexpected.
    pub(crate) fn into_runtime_error(self) -> RuntimeError {
        match self {
            EvalError::Flow(ControlFlow::Break(token, _)) => RuntimeError::UnexpectedBreak(token),
            EvalError::Flow(ControlFlow::Continue(token)) => RuntimeError::UnexpectedContinue(token),
            EvalError::Runtime(err) => err,
        }
    }

    /// Converts to InnerError for external API.
    pub(crate) fn into_inner_error(self) -> InnerError {
        InnerError::from(self.into_runtime_error())
    }
}

/// Result type for internal evaluation functions.
pub(crate) type EvalResult = Result<RuntimeValue, EvalError>;

/// Configuration options for the evaluator.
#[derive(Debug, Clone)]
pub struct Options {
    /// Maximum depth of the call stack to prevent infinite recursion.
    pub max_call_stack_depth: u32,
}

#[cfg(debug_assertions)]
impl Default for Options {
    fn default() -> Self {
        Self {
            max_call_stack_depth: 40,
        }
    }
}

#[cfg(not(debug_assertions))]
impl Default for Options {
    fn default() -> Self {
        Self {
            max_call_stack_depth: 192,
        }
    }
}

/// The AST evaluator for executing mq programs.
///
/// Evaluates abstract syntax trees and manages the runtime environment,
/// including variable bindings, function calls, and module loading.
#[derive(Debug)]
pub struct Evaluator<T: ModuleResolver = DefaultModuleResolver> {
    env: Shared<SharedCell<Env>>,
    token_arena: Shared<SharedCell<Arena<Shared<Token>>>>,

    call_stack_depth: u32,
    pub(crate) options: Options,
    pub(crate) module_loader: module::ModuleLoader<T>,
    pub(crate) macro_expander: Macro,

    #[cfg(feature = "debugger")]
    debugger: Shared<SharedCell<Debugger>>,
    #[cfg(feature = "debugger")]
    pub(crate) debugger_handler: Shared<SharedCell<Box<dyn DebuggerHandler>>>,
}

impl<T: ModuleResolver> Default for Evaluator<T> {
    fn default() -> Self {
        Self {
            env: Shared::new(SharedCell::new(Env::default())),
            token_arena: Shared::new(SharedCell::new(Arena::new(10))),
            call_stack_depth: 0,
            options: Options::default(),
            module_loader: module::ModuleLoader::new(T::default()),
            macro_expander: Macro::new(),
            #[cfg_attr(feature = "sync", allow(clippy::arc_with_non_send_sync))]
            #[cfg(feature = "debugger")]
            debugger: Shared::new(SharedCell::new(Debugger::new())),
            #[cfg(feature = "debugger")]
            debugger_handler: Shared::new(SharedCell::new(Box::new(DefaultDebuggerHandler))),
        }
    }
}

impl<T: ModuleResolver> Clone for Evaluator<T> {
    fn clone(&self) -> Self {
        Self {
            env: Shared::clone(&self.env),
            token_arena: Shared::clone(&self.token_arena),
            call_stack_depth: self.call_stack_depth,
            options: self.options.clone(),
            module_loader: self.module_loader.clone(),
            macro_expander: self.macro_expander.clone(),
            #[cfg(feature = "debugger")]
            debugger: Shared::clone(&self.debugger),
            #[cfg(feature = "debugger")]
            debugger_handler: Shared::clone(&self.debugger_handler),
        }
    }
}

impl<T: ModuleResolver> Evaluator<T> {
    pub(crate) fn new(
        module_loader: module::ModuleLoader<T>,
        token_arena: Shared<SharedCell<Arena<Shared<Token>>>>,
    ) -> Self {
        Self {
            module_loader,
            token_arena,
            ..Default::default()
        }
    }

    #[allow(unused)]
    pub(crate) fn with_env(
        token_arena: Shared<SharedCell<Arena<Shared<Token>>>>,
        env: Shared<SharedCell<Env>>,
    ) -> Self {
        Self {
            token_arena,
            env: Shared::clone(&env),
            ..Default::default()
        }
    }

    /// Helper method to temporarily take the macro_expander to avoid borrow conflicts.
    /// This pattern is needed when the macro_expander needs to call methods that require
    /// mutable access to the evaluator (self).
    ///
    /// # Arguments
    /// * `f` - A closure that operates on the taken macro_expander and returns a result
    ///
    /// # Example
    /// ```ignore
    /// self.with_macro_expander(|expander, evaluator| {
    ///     expander.expand(&program, evaluator)
    /// })?;
    /// ```
    fn with_macro_expander<F, R>(&mut self, f: F) -> Result<R, RuntimeError>
    where
        F: FnOnce(&mut Macro, &mut Self) -> Result<R, RuntimeError>,
    {
        let mut macro_expander = std::mem::take(&mut self.macro_expander);
        let result = f(&mut macro_expander, self);
        self.macro_expander = macro_expander;
        result
    }

    pub(crate) fn eval<I>(&mut self, program: &Program, input: I) -> Result<Vec<RuntimeValue>, InnerError>
    where
        I: Iterator<Item = RuntimeValue>,
    {
        // First pass: handle includes and imports, collect other nodes
        let program = program.iter().try_fold(
            Vec::with_capacity(program.len()),
            |mut nodes: Vec<Shared<ast::Node>>, node: &Shared<ast::Node>| -> Result<_, InnerError> {
                match &*node.expr {
                    ast::Expr::Include(module_id) => {
                        self.eval_include(module_id.to_owned(), &Shared::clone(&self.env))
                            .map_err(InnerError::from)?;
                    }
                    ast::Expr::Import(module_path) => {
                        self.eval_import(module_path.to_owned(), &Shared::clone(&self.env))
                            .map_err(|e| e.into_inner_error())?;
                    }
                    ast::Expr::Module(ident, program) => {
                        self.eval_module(&RuntimeValue::NONE, ident, program, &Shared::clone(&self.env))
                            .map_err(|e| e.into_inner_error())?;
                    }
                    _ => nodes.push(Shared::clone(node)),
                };

                Ok(nodes)
            },
        )?;

        // Expand macros in all nodes, including function definitions
        let program = self
            .with_macro_expander(|expander, evaluator| expander.expand(&program, evaluator))
            .map_err(InnerError::from)?;

        // Register expanded function definitions
        let mut program = program.iter().try_fold(
            Vec::with_capacity(program.len()),
            |mut nodes: Vec<Shared<ast::Node>>, node: &Shared<ast::Node>| -> Result<_, InnerError> {
                match &*node.expr {
                    ast::Expr::Def(ident, params, program) => {
                        define(
                            &self.env,
                            ident.name,
                            RuntimeValue::Function(Box::new(params.clone()), program.clone(), Shared::clone(&self.env)),
                        );
                    }
                    _ => nodes.push(Shared::clone(node)),
                };

                Ok(nodes)
            },
        )?;
        let nodes_index = &program.iter().position(|node| node.is_nodes());

        if let Some(index) = nodes_index {
            let (program, nodes_program) = program.split_at_mut(*index);
            let program = program.to_vec();
            let nodes_program = nodes_program.to_vec();

            let values: Result<Vec<RuntimeValue>, InnerError> = input
                .map(|runtime_value| match &runtime_value {
                    RuntimeValue::Markdown(node, _) => self.eval_markdown_node(&program, node),
                    _ => self
                        .eval_program(&program, runtime_value, &Shared::clone(&self.env))
                        .map_err(|e| e.into_inner_error()),
                })
                .collect();

            if nodes_program.is_empty() {
                values
            } else {
                self.eval_program(&nodes_program, values?.into(), &Shared::clone(&self.env))
                    .map(|value| {
                        if let RuntimeValue::Array(values) = value {
                            values
                        } else {
                            vec![value]
                        }
                    })
                    .map_err(|e| e.into_inner_error())
            }
        } else {
            input
                .map(|runtime_value| match &runtime_value {
                    RuntimeValue::Markdown(node, _) => self.eval_markdown_node(&program, node),
                    _ => self
                        .eval_program(&program, runtime_value, &Shared::clone(&self.env))
                        .map_err(|e| e.into_inner_error()),
                })
                .collect()
        }
    }

    #[inline(always)]
    fn eval_markdown_node(&mut self, program: &Program, node: &mq_markdown::Node) -> Result<RuntimeValue, InnerError> {
        node.map_values(&mut |child_node| {
            let value = self
                .eval_program(
                    program,
                    RuntimeValue::new_markdown(child_node.clone()),
                    &Shared::clone(&self.env),
                )
                .map_err(|e| e.into_inner_error())?;

            Ok(match value {
                RuntimeValue::None => child_node.to_fragment(),
                RuntimeValue::Function(_, _, _)
                | RuntimeValue::NativeFunction(_)
                | RuntimeValue::Module(_)
                | RuntimeValue::Ast(_) => mq_markdown::Node::Empty,
                RuntimeValue::Array(arr) => arr
                    .iter()
                    .filter_map(|v| if v.is_none() { None } else { Some(v.to_string()) })
                    .collect::<Vec<_>>()
                    .join("\n")
                    .into(),
                RuntimeValue::Dict(_)
                | RuntimeValue::Boolean(_)
                | RuntimeValue::Number(_)
                | RuntimeValue::String(_)
                | RuntimeValue::Bytes(_) => value.to_string().into(),
                RuntimeValue::Symbol(i) => i.as_str().into(),
                RuntimeValue::Markdown(node, _) => *node,
            })
        })
        .map(RuntimeValue::new_markdown)
    }

    /// Defines a new string variable in the current environment.
    pub fn define_string_value(&self, name: &str, value: &str) {
        define(&self.env, Ident::new(name), RuntimeValue::String(value.to_string()));
    }

    /// Defines an arbitrary runtime value in the current environment.
    pub fn define_value(&self, name: &str, value: RuntimeValue) {
        define(&self.env, Ident::new(name), value);
    }

    pub(crate) fn load_builtin_module(&mut self) -> Result<(), RuntimeError> {
        match self.module_loader.load_builtin(Shared::clone(&self.token_arena)) {
            Ok(module) => self.load_module(module),
            Err(ModuleError::AlreadyLoaded(_)) => Ok(()),
            Err(e) => Err(e.into()),
        }
    }

    pub(crate) fn load_module(&mut self, module: module::Module) -> Result<(), RuntimeError> {
        self.load_module_with_env(module, &Shared::clone(&self.env))
    }

    pub(crate) fn import_module(&mut self, module: Module) -> Result<RuntimeValue, RuntimeError> {
        self.import_module_with_env(module, &Shared::clone(&self.env))
            .map_err(|e| e.into_runtime_error())
    }

    pub(crate) fn load_module_with_env(
        &mut self,
        module: module::Module,
        env: &Shared<SharedCell<Env>>,
    ) -> Result<(), RuntimeError> {
        let (functions, vars) = self.with_macro_expander(|expander, evaluator| {
            expander.collect_macros(&module.macros, evaluator)?;
            let functions = expander.expand(&module.functions, evaluator)?;
            let vars = expander.expand(&module.vars, evaluator)?;
            Ok((functions, vars))
        })?;

        for node in &module.modules {
            let _ = match &*node.expr {
                ast::Expr::Include(_) => self
                    .eval_expr(&RuntimeValue::NONE, node, env)
                    .map_err(|e| e.into_runtime_error())?,
                ast::Expr::Module(ident, program) => self
                    .eval_module(&RuntimeValue::NONE, ident, program, env)
                    .map_err(|e| e.into_runtime_error())?,
                ast::Expr::Import(module_path) => self
                    .eval_import(module_path.to_owned(), env)
                    .map_err(|e| e.into_runtime_error())?,
                _ => {
                    return Err(RuntimeError::InternalError(
                        (*get_token(Shared::clone(&self.token_arena), node.token_id)).clone(),
                    ));
                }
            };
        }

        for node in functions {
            if let ast::Expr::Def(ident, params, program) = &*node.expr {
                define(
                    env,
                    ident.name,
                    RuntimeValue::Function(Box::new(params.clone()), program.clone(), Shared::clone(env)),
                );
            }
        }

        for node in vars {
            if let ast::Expr::Let(pattern, rhs) = &*node.expr {
                let val = self
                    .eval_expr(&RuntimeValue::NONE, rhs, env)
                    .map_err(|e| e.into_runtime_error())?;
                if let Pattern::Ident(ident) = pattern {
                    define(env, ident.name, val);
                } else {
                    match self.match_pattern(&val, pattern)? {
                        Some(bindings) => {
                            for (name, bound_val) in bindings {
                                define(env, name, bound_val);
                            }
                        }
                        None => {
                            let token = (*get_token(Shared::clone(&self.token_arena), node.token_id)).clone();
                            return Err(RuntimeError::DestructuringFailed(token));
                        }
                    }
                }
            } else {
                return Err(RuntimeError::InternalError(
                    (*get_token(Shared::clone(&self.token_arena), node.token_id)).clone(),
                ));
            }
        }

        Ok(())
    }

    #[inline(always)]
    fn eval_program(
        &mut self,
        program: &Program,
        runtime_value: RuntimeValue,
        env: &Shared<SharedCell<Env>>,
    ) -> EvalResult {
        let mut value = runtime_value;
        for expr in program {
            match self.eval_expr(&value, expr, env) {
                Ok(new_value) => {
                    value = self.maybe_auto_call_pipeline_ident(new_value, &value, expr, env)?;
                }
                Err(e) => return Err(e),
            }
        }
        Ok(value)
    }

    #[inline(always)]
    fn auto_call_ident(expr: &Shared<ast::Node>) -> Option<Ident> {
        match &*expr.expr {
            ast::Expr::Ident(ident) => Some(ident.name),
            ast::Expr::QualifiedAccess(_, ast::AccessTarget::Ident(ident)) => Some(ident.name),
            _ => None,
        }
    }

    #[inline]
    fn maybe_auto_call_pipeline_ident(
        &mut self,
        value: RuntimeValue,
        runtime_value: &RuntimeValue,
        expr: &Shared<ast::Node>,
        env: &Shared<SharedCell<Env>>,
    ) -> EvalResult {
        match &value {
            RuntimeValue::Function(params, _, _) => {
                let Some(ident) = Self::auto_call_ident(expr) else {
                    return Ok(value);
                };

                let required_params = params
                    .iter()
                    .filter(|p| p.default.is_none() && !p.is_variadic)
                    .take(2)
                    .count();
                if required_params > 1 {
                    return Ok(value);
                }

                self.call_fn(
                    &value,
                    Shared::clone(expr),
                    ident,
                    &ast::Args::new(),
                    runtime_value,
                    env,
                )
            }
            RuntimeValue::NativeFunction(native_ident) => {
                let can_auto_call = builtin::get_builtin_functions(native_ident)
                    .is_some_and(|f| f.num_params.is_valid(0) || f.num_params.is_missing_one_params(0));
                if !can_auto_call || Self::auto_call_ident(expr).is_none() {
                    return Ok(value);
                }

                self.eval_builtin(runtime_value, Shared::clone(expr), native_ident, &ast::Args::new(), env)
            }
            _ => Ok(value),
        }
    }

    #[inline(always)]
    fn eval_ident(&self, ident: Ident, token_id: TokenId, env: &Shared<SharedCell<Env>>) -> EvalResult {
        #[cfg(not(feature = "sync"))]
        {
            env.borrow()
                .resolve(ident)
                .map_err(|e| EvalError::from(e.to_runtime_error(token_id, Shared::clone(&self.token_arena))))
        }
        #[cfg(feature = "sync")]
        {
            env.read()
                .unwrap()
                .resolve(ident)
                .map_err(|e| EvalError::from(e.to_runtime_error(token_id, Shared::clone(&self.token_arena))))
        }
    }

    #[inline(never)]
    #[cfg(feature = "debugger")]
    fn eval_debugger(&self, runtime_value: &RuntimeValue, node: Shared<ast::Node>, env: Shared<SharedCell<Env>>) {
        let current_call_stack = self.debugger.read().unwrap().current_call_stack();
        let token = get_token(Shared::clone(&self.token_arena), node.token_id);

        let debug_context = DebugContext {
            current_value: runtime_value.clone(),
            current_node: Shared::clone(&node),
            token: Shared::clone(&token),
            call_stack: current_call_stack,
            env: Shared::clone(&env),
            source: Source {
                name: if token.module_id == Module::TOP_LEVEL_MODULE_ID {
                    None
                } else {
                    Some(self.module_loader.module_name(token.module_id).to_string())
                },
                code: self
                    .module_loader
                    .get_source_code_for_debug(token.module_id)
                    .unwrap_or_default(),
            },
        };
        let breakpoint = Breakpoint {
            id: 0,
            line: token.range.start.line as usize,
            column: Some(token.range.start.column),
            enabled: true,
            source: None,
        };

        let next_action = self
            .debugger_handler
            .read()
            .unwrap()
            .on_breakpoint_hit(&breakpoint, &debug_context);
        self.debugger.write().unwrap().next(next_action);
    }

    #[inline(always)]
    fn eval_include(&mut self, module: ast::Literal, env: &Shared<SharedCell<Env>>) -> Result<(), RuntimeError> {
        match module {
            ast::Literal::String(module_name) => {
                let module = self
                    .module_loader
                    .load_from_file(&module_name, Shared::clone(&self.token_arena))?;
                #[cfg(feature = "http-import")]
                self.module_loader.push_http_boundary();
                let result = self.load_module_with_env(module, env);
                #[cfg(feature = "http-import")]
                self.module_loader.pop_http_boundary();
                result
            }
            _ => Err(RuntimeError::ModuleLoadError(ModuleError::InvalidModule)),
        }
    }

    fn eval_module(
        &mut self,
        runtime_value: &RuntimeValue,
        ident: &IdentWithToken,
        program: &Program,
        env: &Shared<SharedCell<Env>>,
    ) -> EvalResult {
        let module_name_to_use = &ident.name.as_str();

        let module_env = if let Ok(RuntimeValue::Module(module_env)) = resolve(module_name_to_use, env) {
            Shared::clone(module_env.exports())
        } else {
            // Create a new environment for the module exports
            Shared::new(SharedCell::new(Env::with_parent(Shared::downgrade(env))))
        };

        for node in program {
            match &*node.expr {
                ast::Expr::Include(_) => {
                    self.eval_expr(&RuntimeValue::NONE, node, &Shared::clone(&module_env))?;
                }
                ast::Expr::Def(ident, params, program) => {
                    define(
                        &module_env,
                        ident.name,
                        RuntimeValue::Function(Box::new(params.clone()), program.clone(), Shared::clone(&module_env)),
                    );
                }
                ast::Expr::Let(pattern, rhs) => {
                    let val = self.eval_expr(&RuntimeValue::NONE, rhs, &Shared::clone(&module_env))?;
                    if let Pattern::Ident(ident) = pattern {
                        define(&module_env, ident.name, val);
                    } else {
                        match self.match_pattern(&val, pattern)? {
                            Some(bindings) => {
                                for (name, bound_val) in bindings {
                                    define(&module_env, name, bound_val);
                                }
                            }
                            None => {
                                let token = (*get_token(Shared::clone(&self.token_arena), node.token_id)).clone();
                                return Err(RuntimeError::DestructuringFailed(token).into());
                            }
                        }
                    }
                }
                ast::Expr::Import(module_path) => {
                    self.eval_import(module_path.to_owned(), &Shared::clone(&module_env))?;
                }
                ast::Expr::Module(ident, program) => {
                    // Collect macros from the module
                    self.with_macro_expander(|expander, evaluator| expander.collect_macros(program, evaluator))?;
                    let _ = self.eval_module(&RuntimeValue::NONE, ident, program, &module_env)?;
                }
                ast::Expr::Macro(..) => {
                    // Macros are not loaded into runtime environment
                }
                _ => {}
            }
        }

        // Register the module in the environment
        let module_runtime_value = RuntimeValue::Module(runtime_value::ModuleEnv::new(
            module_name_to_use,
            Shared::clone(&module_env),
        ));

        define(&self.env, Ident::new(module_name_to_use), module_runtime_value);

        Ok(runtime_value.clone())
    }

    fn import_module_with_env(&mut self, module: Module, env: &Shared<SharedCell<Env>>) -> EvalResult {
        // Collect macros from the module
        // Create a new environment for the module exports
        let module_env = Shared::new(SharedCell::new(Env::with_parent(Shared::downgrade(env))));
        let module_name_to_use = module.name.to_string();

        self.load_module_with_env(module, &Shared::clone(&module_env))?;

        // Register the module in the environment
        let module_runtime_value = RuntimeValue::Module(runtime_value::ModuleEnv::new(
            &module_name_to_use,
            Shared::clone(&module_env),
        ));

        define(&self.env, Ident::new(&module_name_to_use), module_runtime_value);

        Ok(RuntimeValue::Module(ModuleEnv::new(&module_name_to_use, module_env)))
    }

    fn eval_import(&mut self, module_path: ast::Literal, env: &Shared<SharedCell<Env>>) -> EvalResult {
        match module_path {
            ast::Literal::String(module_name) => {
                let module = self
                    .module_loader
                    .load_from_file(&module_name, Shared::clone(&self.token_arena));
                match module {
                    Ok(module) => {
                        #[cfg(feature = "http-import")]
                        self.module_loader.push_http_boundary();
                        let result = self.import_module_with_env(module, env);
                        #[cfg(feature = "http-import")]
                        self.module_loader.pop_http_boundary();
                        result
                    }
                    Err(ModuleError::AlreadyLoaded(_)) => {
                        let canonical = self.module_loader.canonical_name(&module_name).to_owned();
                        match resolve(&canonical, env) {
                            Ok(value) => Ok(value),
                            Err(_) => {
                                Err(RuntimeError::ModuleLoadError(ModuleError::NotFound(Cow::Owned(canonical))).into())
                            }
                        }
                    }
                    Err(e) => Err(RuntimeError::ModuleLoadError(e).into()),
                }
            }
            _ => Err(RuntimeError::ModuleLoadError(ModuleError::InvalidModule).into()),
        }
    }

    fn eval_qualified_access(
        &mut self,
        runtime_value: &RuntimeValue,
        module_path: &[ast::IdentWithToken],
        access_target: &ast::AccessTarget,
        token_id: TokenId,
        env: &Shared<SharedCell<Env>>,
    ) -> EvalResult {
        // Traverse the module path to get to the final module
        let mut current_value = if let Some(first_module) = module_path.first() {
            self.eval_ident(first_module.name, token_id, env)?
        } else {
            let token = get_token(Shared::clone(&self.token_arena), token_id);
            return Err(RuntimeError::InternalError((*token).clone()).into());
        };

        // Traverse nested modules
        for module_ident in &module_path[1..] {
            match current_value {
                RuntimeValue::Module(module_env) => {
                    let module_exports = Shared::clone(module_env.exports());

                    #[cfg(not(feature = "sync"))]
                    let resolved = module_exports.borrow().resolve(module_ident.name);
                    #[cfg(feature = "sync")]
                    let resolved = module_exports.read().unwrap().resolve(module_ident.name);

                    current_value = resolved
                        .map_err(|e| EvalError::from(e.to_runtime_error(token_id, Shared::clone(&self.token_arena))))?;
                }
                _ => {
                    let token = get_token(Shared::clone(&self.token_arena), token_id);
                    return Err(RuntimeError::NotDefined((*token).clone(), module_ident.name.to_string()).into());
                }
            }
        }

        // Now access the target from the final module
        match current_value {
            RuntimeValue::Module(module_env) => {
                let module_exports = Shared::clone(module_env.exports());

                match access_target {
                    ast::AccessTarget::Call(func_name, args) => {
                        // Resolve function from module exports and call it
                        #[cfg(not(feature = "sync"))]
                        let resolved = module_exports.borrow().resolve(func_name.name);
                        #[cfg(feature = "sync")]
                        let resolved = module_exports.read().unwrap().resolve(func_name.name);

                        match resolved {
                            Ok(fn_value) => {
                                // Create a dummy node for the function call
                                let call_node = Shared::new(ast::Node {
                                    token_id,
                                    expr: Shared::new(ast::Expr::Call(func_name.clone(), args.clone())),
                                });
                                self.call_fn(&fn_value, call_node, func_name.name, args, runtime_value, env)
                            }
                            Err(_) => {
                                let token = func_name
                                    .token
                                    .as_ref()
                                    .cloned()
                                    .unwrap_or(get_token(Shared::clone(&self.token_arena), token_id));
                                Err(RuntimeError::NotDefined((*token).clone(), func_name.name.to_string()).into())
                            }
                        }
                    }
                    ast::AccessTarget::Ident(ident) => {
                        // Resolve value from module exports
                        #[cfg(not(feature = "sync"))]
                        let resolved = module_exports.borrow().resolve(ident.name);
                        #[cfg(feature = "sync")]
                        let resolved = module_exports.read().unwrap().resolve(ident.name);

                        resolved.map_err(|e| {
                            EvalError::from(e.to_runtime_error(token_id, Shared::clone(&self.token_arena)))
                        })
                    }
                }
            }
            _ => {
                let (token, last_module) = module_path
                    .last()
                    .map(|m| (m.token.clone(), m.name.to_string()))
                    .unwrap_or_default();
                let token = token.unwrap_or(get_token(Shared::clone(&self.token_arena), token_id));
                Err(RuntimeError::NotDefined((*token).clone(), last_module).into())
            }
        }
    }

    #[inline(always)]
    fn eval_selector_expr_with_args(
        runtime_value: &RuntimeValue,
        selector: &Selector,
        args: &[RuntimeValue],
    ) -> RuntimeValue {
        match runtime_value {
            RuntimeValue::Markdown(node_value, _) => builtin::eval_selector_with_args(node_value, selector, args),
            RuntimeValue::Array(values) => {
                if let Selector::List(Some(idx), None) = selector {
                    return values.get(*idx).cloned().unwrap_or(RuntimeValue::NONE);
                }
                let values = values
                    .iter()
                    .flat_map(|value| match value {
                        RuntimeValue::Markdown(node_value, _) => {
                            match builtin::eval_selector_with_args(node_value, selector, args) {
                                RuntimeValue::Array(arr) => arr,
                                other => vec![other],
                            }
                        }
                        _ if matches!(selector, Selector::List(None, None)) && args.is_empty() => {
                            vec![value.clone()]
                        }
                        RuntimeValue::Dict(_) => {
                            vec![Self::eval_selector_expr_with_args(value, selector, args)]
                        }
                        _ => vec![RuntimeValue::NONE],
                    })
                    .collect::<Vec<_>>();
                RuntimeValue::Array(values)
            }
            RuntimeValue::Dict(map) => {
                let new_map: BTreeMap<_, _> = map
                    .iter()
                    .map(|(k, v)| {
                        let new_v = if *k == *TYPE_IDENT {
                            v.clone()
                        } else {
                            Self::eval_selector_expr_with_args(v, selector, args)
                        };
                        (*k, new_v)
                    })
                    .collect();
                if new_map.is_empty() {
                    RuntimeValue::NONE
                } else {
                    RuntimeValue::Dict(new_map)
                }
            }
            _ => RuntimeValue::NONE,
        }
    }

    fn eval_property_selector_expr(runtime_value: &RuntimeValue, property_name: &Ident) -> RuntimeValue {
        match runtime_value {
            RuntimeValue::Array(values) => {
                let values = values
                    .iter()
                    .map(|value| match value {
                        RuntimeValue::Dict(_) => Self::eval_property_selector_expr(value, property_name),
                        _ => RuntimeValue::NONE,
                    })
                    .collect::<Vec<_>>();
                RuntimeValue::Array(values)
            }
            RuntimeValue::Dict(map) => map.get(property_name).cloned().unwrap_or(RuntimeValue::NONE),
            _ => RuntimeValue::NONE,
        }
    }

    /// Collects the value itself and all nested values recursively (depth-first).
    fn collect_recursive(value: &RuntimeValue) -> Vec<RuntimeValue> {
        let mut result = vec![value.clone()];
        match value {
            RuntimeValue::Array(items) => {
                for item in items {
                    result.extend(Self::collect_recursive(item));
                }
            }
            RuntimeValue::Dict(map) => {
                for v in map.values() {
                    result.extend(Self::collect_recursive(v));
                }
            }
            _ => {}
        }
        result
    }

    fn eval_selector_expr(runtime_value: &RuntimeValue, selector: &Selector) -> RuntimeValue {
        if let Selector::Property(property_name) = selector {
            return Self::eval_property_selector_expr(runtime_value, property_name);
        }

        match runtime_value {
            RuntimeValue::Markdown(node_value, _) => builtin::eval_selector(node_value, selector),
            RuntimeValue::Array(values) => {
                if let Selector::List(Some(idx), None) = selector {
                    return values.get(*idx).cloned().unwrap_or(RuntimeValue::NONE);
                }
                let values = values
                    .iter()
                    .flat_map(|value| match value {
                        RuntimeValue::Markdown(node_value, _) => match builtin::eval_selector(node_value, selector) {
                            RuntimeValue::Array(arr) => arr,
                            other => vec![other],
                        },
                        _ if matches!(selector, Selector::List(None, None)) => {
                            vec![value.clone()]
                        }
                        RuntimeValue::Dict(_) => match Self::eval_selector_expr(value, selector) {
                            RuntimeValue::Array(arr) if matches!(selector, Selector::Recursive) => arr,
                            other => vec![other],
                        },
                        _ => vec![RuntimeValue::NONE],
                    })
                    .collect::<Vec<_>>();

                RuntimeValue::Array(values)
            }
            RuntimeValue::Dict(map) => {
                if matches!(selector, Selector::List(None, None)) {
                    return RuntimeValue::Array(map.values().cloned().collect());
                }
                if matches!(selector, Selector::Recursive) {
                    return RuntimeValue::Array(Self::collect_recursive(runtime_value));
                }
                let new_map: BTreeMap<_, _> = map
                    .iter()
                    .map(|(k, v)| {
                        let new_v = if *k == *TYPE_IDENT {
                            v.clone()
                        } else {
                            Self::eval_selector_expr(v, selector)
                        };
                        (*k, new_v)
                    })
                    .collect();

                if new_map.is_empty() {
                    RuntimeValue::NONE
                } else {
                    RuntimeValue::Dict(new_map)
                }
            }
            _ => RuntimeValue::NONE,
        }
    }

    fn eval_interpolated_string(
        &mut self,
        runtime_value: &RuntimeValue,
        segments: &[ast::StringSegment],
        token_id: TokenId,
        env: &Shared<SharedCell<Env>>,
    ) -> EvalResult {
        // Calculate estimated capacity based on segment content
        let estimated_capacity = segments
            .iter()
            .map(|segment| match segment {
                ast::StringSegment::Text(s) => s.len(),
                ast::StringSegment::Expr(_) => 32, // Estimated size for expression result
                ast::StringSegment::Env(_) => 32,  // Estimated size for environment variable
                ast::StringSegment::Self_ => 64,   // Estimated size for self reference
            })
            .sum();

        segments
            .iter()
            .try_fold(String::with_capacity(estimated_capacity), |mut acc, segment| {
                match segment {
                    ast::StringSegment::Text(s) => acc.push_str(s),
                    ast::StringSegment::Expr(expr_node) => {
                        let value = self.eval_expr(runtime_value, expr_node, env)?;
                        acc.push_str(&value.to_string());
                    }
                    ast::StringSegment::Env(env_var) => {
                        acc.push_str(&std::env::var(env_var).map_err(|_| {
                            RuntimeError::EnvNotFound(
                                (*get_token(Shared::clone(&self.token_arena), token_id)).clone(),
                                env_var.clone(),
                            )
                        })?);
                    }
                    ast::StringSegment::Self_ => {
                        acc.push_str(&runtime_value.to_string());
                    }
                }

                Ok(acc)
            })
            .map(|acc| acc.into())
    }

    fn eval_expr(
        &mut self,
        runtime_value: &RuntimeValue,
        node: &Shared<ast::Node>,
        env: &Shared<SharedCell<Env>>,
    ) -> EvalResult {
        #[cfg(feature = "debugger")]
        {
            let token = &get_token(Shared::clone(&self.token_arena), node.token_id);
            let call_stack = self.debugger.read().unwrap().current_call_stack();
            let debug_context = DebugContext {
                current_value: runtime_value.clone(),
                current_node: Shared::clone(node),
                token: Shared::clone(token),
                call_stack,
                env: Shared::clone(env),
                source: Source {
                    name: if token.module_id == Module::TOP_LEVEL_MODULE_ID {
                        None
                    } else {
                        Some(self.module_loader.module_name(token.module_id).to_string())
                    },
                    code: self
                        .module_loader
                        .get_source_code_for_debug(token.module_id)
                        .unwrap_or_default(),
                },
            };

            let breakpoint = self
                .debugger
                .read()
                .unwrap()
                .get_hit_breakpoint(&debug_context, Shared::clone(token));

            if let Some(breakpoint) = breakpoint {
                let next_action = self
                    .debugger_handler
                    .read()
                    .unwrap()
                    .on_breakpoint_hit(&breakpoint, &debug_context);
                self.debugger.write().unwrap().next(next_action);
            } else if self.debugger.write().unwrap().should_break(&debug_context) {
                let next_action = self.debugger_handler.read().unwrap().on_step(&debug_context);
                self.debugger.write().unwrap().next(next_action);
            }
        }

        match &*node.expr {
            ast::Expr::Selector(ident) => Ok(Self::eval_selector_expr(runtime_value, ident)),
            ast::Expr::SelectorChain(selectors) => Ok(selectors
                .iter()
                .fold(runtime_value.clone(), |v, sel| Self::eval_selector_expr(&v, sel))),
            ast::Expr::SelectorCall(selector, args) => {
                let evaluated_args = args
                    .iter()
                    .map(|arg| self.eval_expr(runtime_value, arg, env))
                    .collect::<Result<Vec<_>, _>>()?;
                Ok(Self::eval_selector_expr_with_args(
                    runtime_value,
                    selector,
                    &evaluated_args,
                ))
            }
            ast::Expr::Call(ident, args) => {
                #[cfg(feature = "debugger")]
                if ident.name == constants::builtins::BREAKPOINT.into() {
                    self.eval_debugger(runtime_value, Shared::clone(node), Shared::clone(env));
                    return Ok(runtime_value.clone());
                }

                self.eval_fn(runtime_value, Shared::clone(node), ident.name, args, env)
            }
            ast::Expr::Ident(ident) => self.eval_ident(ident.name, node.token_id, env),
            ast::Expr::Literal(literal) => Ok(self.eval_literal(literal)),
            ast::Expr::Self_ | ast::Expr::Nodes => Ok(runtime_value.clone()),
            ast::Expr::QualifiedAccess(module_name, access_target) => {
                self.eval_qualified_access(runtime_value, module_name, access_target, node.token_id, env)
            }
            ast::Expr::Block(program) => {
                let block_env = Shared::new(SharedCell::new(Env::with_parent(Shared::downgrade(env))));
                self.eval_program(program, runtime_value.clone(), &block_env)
            }
            ast::Expr::CallDynamic(callable, args) => self.eval_call_dynamic(runtime_value, callable, args, env),
            ast::Expr::If(condition) => self.eval_if(runtime_value, condition, env),
            ast::Expr::Def(ident, params, program) => {
                let function = RuntimeValue::Function(Box::new(params.clone()), program.clone(), Shared::clone(env));
                define(env, ident.name, function.clone());
                Ok(function)
            }
            ast::Expr::Macro(_, _, _) => self.eval_macro(node),
            ast::Expr::Fn(params, program) => Ok(RuntimeValue::Function(
                Box::new(params.clone()),
                program.clone(),
                Shared::clone(env),
            )),
            ast::Expr::As(ident, node) => {
                let val = self.eval_expr(runtime_value, node, env)?;
                define(env, ident.name, val);
                Ok(runtime_value.clone())
            }
            ast::Expr::Let(pattern, node) => {
                let val = self.eval_expr(runtime_value, node, env)?;
                if let Pattern::Ident(ident) = pattern {
                    define(env, ident.name, val);
                } else {
                    match self.match_pattern(&val, pattern)? {
                        Some(bindings) => {
                            for (name, bound_val) in bindings {
                                define(env, name, bound_val);
                            }
                        }
                        None => {
                            let token = (*get_token(Shared::clone(&self.token_arena), node.token_id)).clone();
                            return Err(RuntimeError::DestructuringFailed(token).into());
                        }
                    }
                }
                Ok(runtime_value.clone())
            }
            ast::Expr::Var(pattern, node) => {
                let val = self.eval_expr(runtime_value, node, env)?;
                if let Pattern::Ident(ident) = pattern {
                    define_mutable(env, ident.name, val);
                } else {
                    match self.match_pattern(&val, pattern)? {
                        Some(bindings) => {
                            for (name, bound_val) in bindings {
                                define_mutable(env, name, bound_val);
                            }
                        }
                        None => {
                            let token = (*get_token(Shared::clone(&self.token_arena), node.token_id)).clone();
                            return Err(RuntimeError::DestructuringFailed(token).into());
                        }
                    }
                }
                Ok(runtime_value.clone())
            }
            ast::Expr::Assign(ident, node) => {
                let val = self.eval_expr(runtime_value, node, env)?;
                #[cfg(not(feature = "sync"))]
                {
                    env.borrow_mut().assign(ident.name, val).map_err(|e| {
                        e.to_runtime_error_with_token(
                            ident
                                .token
                                .as_ref()
                                .map(|t| (**t).clone())
                                .unwrap_or((*get_token(Shared::clone(&self.token_arena), node.token_id)).clone()),
                        )
                    })?;
                }

                #[cfg(feature = "sync")]
                {
                    env.write().unwrap().assign(ident.name, val).map_err(|e| {
                        e.to_runtime_error_with_token(
                            ident
                                .token
                                .as_ref()
                                .map(|t| (**t).clone())
                                .unwrap_or((*get_token(Shared::clone(&self.token_arena), node.token_id)).clone()),
                        )
                    })?;
                }
                Ok(runtime_value.clone())
            }
            ast::Expr::And(operands) => self.eval_and(runtime_value, operands, env),
            ast::Expr::Or(operands) => self.eval_or(runtime_value, operands, env),
            ast::Expr::While(cond, program) => self.eval_while(runtime_value, cond, program, env),
            ast::Expr::Loop(program) => self.eval_loop(runtime_value, program, env),
            ast::Expr::Try(try_expr, catch_expr) => self.eval_try(runtime_value, try_expr, catch_expr, env),
            ast::Expr::Foreach(ident, values, body) => {
                self.eval_foreach(runtime_value, ident.name, values, body, node.token_id, env)
            }
            ast::Expr::InterpolatedString(segments) => {
                self.eval_interpolated_string(runtime_value, segments, node.token_id, env)
            }
            ast::Expr::Include(module_id) => {
                self.eval_include(module_id.to_owned(), env)?;
                Ok(runtime_value.clone())
            }
            ast::Expr::Import(module_path) => self.eval_import(module_path.to_owned(), env),
            ast::Expr::Module(ident, program) => self.eval_module(runtime_value, ident, program, env),

            ast::Expr::Match(value_node, arms) => self.eval_match(runtime_value, value_node, arms, env),
            ast::Expr::Break(value_node) => {
                let token = get_token(Shared::clone(&self.token_arena), node.token_id);
                let value = match value_node {
                    Some(node) => Some(Box::new(self.eval_expr(runtime_value, node, env)?)),
                    None => None,
                };
                Err(EvalError::Flow(ControlFlow::Break((*token).clone(), value)))
            }
            ast::Expr::Continue => {
                let token = get_token(Shared::clone(&self.token_arena), node.token_id);
                Err(EvalError::Flow(ControlFlow::Continue((*token).clone())))
            }
            ast::Expr::Paren(expr) => self.eval_expr(runtime_value, expr, env),
            ast::Expr::Quote(inner) => self.eval_quote(inner, env),
            ast::Expr::Unquote(_) => {
                let token = get_token(Shared::clone(&self.token_arena), node.token_id);
                Err(RuntimeError::UnquoteNotAllowedOutsideQuote((*token).clone()).into())
            }
        }
    }

    #[inline(always)]
    fn eval_literal(&self, literal: &ast::Literal) -> RuntimeValue {
        match literal {
            ast::Literal::None => RuntimeValue::None,
            ast::Literal::Bool(b) => RuntimeValue::Boolean(*b),
            ast::Literal::String(s) => RuntimeValue::String(s.clone()),
            ast::Literal::Bytes(b) => RuntimeValue::Bytes(b.clone()),
            ast::Literal::Symbol(i) => RuntimeValue::Symbol(*i),
            ast::Literal::Number(n) => RuntimeValue::Number(*n),
        }
    }

    #[inline(always)]
    fn eval_and(
        &mut self,
        runtime_value: &RuntimeValue,
        operands: &[Shared<ast::Node>],
        env: &Shared<SharedCell<Env>>,
    ) -> EvalResult {
        let mut last_value = RuntimeValue::Boolean(true);
        for operand in operands {
            last_value = self.eval_expr(runtime_value, operand, env)?;
            if !last_value.is_truthy() {
                return Ok(RuntimeValue::Boolean(false));
            }
        }
        Ok(last_value)
    }

    #[inline(always)]
    fn eval_or(
        &mut self,
        runtime_value: &RuntimeValue,
        operands: &[Shared<ast::Node>],
        env: &Shared<SharedCell<Env>>,
    ) -> EvalResult {
        for operand in operands {
            let value = self.eval_expr(runtime_value, operand, env)?;
            if value.is_truthy() {
                return Ok(value);
            }
        }
        Ok(RuntimeValue::Boolean(false))
    }

    fn eval_foreach(
        &mut self,
        runtime_value: &RuntimeValue,
        ident: Ident,
        values: &Shared<ast::Node>,
        body: &Program,
        token_id: TokenId,
        env: &Shared<SharedCell<Env>>,
    ) -> EvalResult {
        let values = self.eval_expr(runtime_value, values, env)?;
        let values = match values {
            RuntimeValue::Array(values) => {
                let env = Shared::new(SharedCell::new(Env::with_parent(Shared::downgrade(env))));
                let mut results = Vec::with_capacity(values.len());

                for value in values {
                    define(&env, ident, value.clone());
                    match self.eval_program(body, value, &env) {
                        Ok(result) => results.push(result),
                        Err(EvalError::Flow(ControlFlow::Break(_, Some(v)))) => return Ok(*v),
                        Err(EvalError::Flow(ControlFlow::Break(_, None))) => break,
                        Err(EvalError::Flow(ControlFlow::Continue(_))) => continue,
                        Err(e) => return Err(e),
                    }
                }

                results
            }
            RuntimeValue::String(s) => {
                let env = Shared::new(SharedCell::new(Env::with_parent(Shared::downgrade(env))));
                let mut results = Vec::with_capacity(s.len());

                for c in s.chars() {
                    define(&env, ident, RuntimeValue::String(c.to_string()));
                    match self.eval_program(body, RuntimeValue::String(c.to_string()), &env) {
                        Ok(result) => results.push(result),
                        Err(EvalError::Flow(ControlFlow::Break(_, Some(v)))) => return Ok(*v),
                        Err(EvalError::Flow(ControlFlow::Break(_, None))) => break,
                        Err(EvalError::Flow(ControlFlow::Continue(_))) => continue,
                        Err(e) => return Err(e),
                    }
                }

                results
            }
            _ => {
                return Err(RuntimeError::InvalidTypes {
                    token: (*get_token(Shared::clone(&self.token_arena), token_id)).clone(),
                    name: TokenKind::Foreach.to_string(),
                    args: vec![values.to_string().into()],
                }
                .into());
            }
        };

        Ok(RuntimeValue::Array(values))
    }

    fn eval_while(
        &mut self,
        runtime_value: &RuntimeValue,
        cond: &Shared<ast::Node>,
        body: &Program,
        env: &Shared<SharedCell<Env>>,
    ) -> EvalResult {
        let mut runtime_value = runtime_value.clone();
        let env = Shared::new(SharedCell::new(Env::with_parent(Shared::downgrade(env))));
        let mut cond_value = self.eval_expr(&runtime_value, cond, &env)?;

        if !cond_value.is_truthy() {
            return Ok(RuntimeValue::NONE);
        }
        let mut first = true;

        while cond_value.is_truthy() {
            match self.eval_program(body, runtime_value.clone(), &env) {
                Ok(mut new_runtime_value) => {
                    std::mem::swap(&mut runtime_value, &mut new_runtime_value);
                    cond_value = self.eval_expr(&runtime_value, cond, &env)?;
                }
                Err(EvalError::Flow(ControlFlow::Break(_, Some(v)))) => {
                    runtime_value = *v;
                    break;
                }
                Err(EvalError::Flow(ControlFlow::Break(_, None))) if first => {
                    runtime_value = RuntimeValue::NONE;
                    break;
                }
                Err(EvalError::Flow(ControlFlow::Break(_, None))) => break,
                Err(EvalError::Flow(ControlFlow::Continue(_))) if first => {
                    runtime_value = RuntimeValue::NONE;
                    continue;
                }
                Err(EvalError::Flow(ControlFlow::Continue(_))) => continue,
                Err(e) => return Err(e),
            }

            first = false;
        }

        Ok(runtime_value)
    }

    fn eval_loop(&mut self, runtime_value: &RuntimeValue, body: &Program, env: &Shared<SharedCell<Env>>) -> EvalResult {
        let mut runtime_value = runtime_value.clone();
        let env = Shared::new(SharedCell::new(Env::with_parent(Shared::downgrade(env))));

        loop {
            match self.eval_program(body, runtime_value.clone(), &env) {
                Ok(mut new_runtime_value) => {
                    std::mem::swap(&mut runtime_value, &mut new_runtime_value);
                }
                Err(EvalError::Flow(ControlFlow::Break(_, Some(v)))) => {
                    runtime_value = *v;
                    break;
                }
                Err(EvalError::Flow(ControlFlow::Break(_, None))) => break,
                Err(EvalError::Flow(ControlFlow::Continue(_))) => continue,
                Err(e) => return Err(e),
            }
        }

        Ok(runtime_value)
    }

    #[inline(always)]
    fn eval_try(
        &mut self,
        runtime_value: &RuntimeValue,
        try_expr: &Shared<ast::Node>,
        catch_expr: &Shared<ast::Node>,
        env: &Shared<SharedCell<Env>>,
    ) -> EvalResult {
        match self.eval_expr(runtime_value, try_expr, env) {
            Ok(result) => Ok(result),
            Err(_) => self.eval_expr(runtime_value, catch_expr, env),
        }
    }

    #[inline(always)]
    fn eval_if(
        &mut self,
        runtime_value: &RuntimeValue,
        conditions: &Branches,
        env: &Shared<SharedCell<Env>>,
    ) -> EvalResult {
        for (cond_node, body) in conditions {
            match cond_node {
                Some(cond_node) => {
                    let cond = self.eval_expr(runtime_value, cond_node, env)?;

                    if cond.is_truthy() {
                        return self.eval_expr(runtime_value, body, env);
                    }
                }
                None => return self.eval_expr(runtime_value, body, env),
            }
        }

        Ok(RuntimeValue::NONE)
    }

    fn eval_match(
        &mut self,
        runtime_value: &RuntimeValue,
        value_node: &Shared<ast::Node>,
        arms: &MatchArms,
        env: &Shared<SharedCell<Env>>,
    ) -> EvalResult {
        let match_value = self.eval_expr(runtime_value, value_node, env)?;

        // Try each arm in order
        for arm in arms {
            // Check if the pattern matches
            if let Some(bindings) = self.match_pattern(&match_value, &arm.pattern)? {
                // If there's a guard, evaluate it
                if let Some(guard_node) = &arm.guard {
                    // Create a new environment with pattern bindings
                    let guard_env = Shared::new(SharedCell::new(Env::with_parent(Shared::downgrade(env))));
                    for (name, value) in bindings.iter() {
                        define(&guard_env, *name, value.clone());
                    }

                    let guard_result = self.eval_expr(runtime_value, guard_node, &guard_env)?;
                    if !guard_result.is_truthy() {
                        // Guard failed, try next arm
                        continue;
                    }
                }

                // Pattern matched (and guard passed if present), evaluate body
                let body_env = Shared::new(SharedCell::new(Env::with_parent(Shared::downgrade(env))));
                for (name, value) in bindings {
                    define(&body_env, name, value);
                }

                return self.eval_expr(runtime_value, &arm.body, &body_env);
            }
        }

        Ok(RuntimeValue::NONE)
    }

    #[inline(always)]
    fn eval_quote(&mut self, node: &Shared<ast::Node>, env: &Shared<SharedCell<Env>>) -> EvalResult {
        let processed_node = self.process_quote_node(node, env)?;
        Ok(RuntimeValue::Ast(processed_node))
    }

    fn process_quote_node(
        &mut self,
        node: &Shared<ast::Node>,
        env: &Shared<SharedCell<Env>>,
    ) -> Result<Shared<ast::Node>, RuntimeError> {
        match &*node.expr {
            ast::Expr::Unquote(inner) => {
                let value = self
                    .eval_expr(&RuntimeValue::None, inner, env)
                    .map_err(|e| e.into_runtime_error())?;

                match value {
                    RuntimeValue::Ast(ast_node) => Ok(ast_node),
                    RuntimeValue::None => Ok(Shared::new(ast::Node {
                        token_id: node.token_id,
                        expr: Shared::new(ast::Expr::Block(vec![])),
                    })),
                    other_value => {
                        let literal_expr = self.value_to_literal_expr(&other_value)?;
                        Ok(Shared::new(ast::Node {
                            token_id: node.token_id,
                            expr: Shared::new(literal_expr),
                        }))
                    }
                }
            }
            ast::Expr::Block(nodes) => {
                let processed_nodes: Result<Vec<_>, _> =
                    nodes.iter().map(|n| self.process_quote_node(n, env)).collect();
                let processed = processed_nodes?;
                let filtered_nodes: Vec<_> = processed
                    .into_iter()
                    .filter(|n| !matches!(&*n.expr, ast::Expr::Block(nodes) if nodes.is_empty()))
                    .collect();

                Ok(Shared::new(ast::Node {
                    token_id: node.token_id,
                    expr: Shared::new(ast::Expr::Block(filtered_nodes)),
                }))
            }
            ast::Expr::Paren(inner) => {
                let processed = self.process_quote_node(inner, env)?;
                Ok(Shared::new(ast::Node {
                    token_id: node.token_id,
                    expr: Shared::new(ast::Expr::Paren(processed)),
                }))
            }
            ast::Expr::Quote(inner) => Ok(Shared::new(ast::Node {
                token_id: node.token_id,
                expr: Shared::new(ast::Expr::Quote(Shared::clone(inner))),
            })),
            _ => Ok(Shared::clone(node)),
        }
    }

    #[inline(always)]
    fn value_to_literal_expr(&self, value: &RuntimeValue) -> Result<ast::Expr, RuntimeError> {
        match value {
            RuntimeValue::String(s) => Ok(ast::Expr::Literal(ast::Literal::String(s.clone()))),
            RuntimeValue::Number(n) => Ok(ast::Expr::Literal(ast::Literal::Number(*n))),
            RuntimeValue::Boolean(b) => Ok(ast::Expr::Literal(ast::Literal::Bool(*b))),
            RuntimeValue::None => Ok(ast::Expr::Literal(ast::Literal::None)),
            _ => {
                let s = format!("{}", value);
                Ok(ast::Expr::Literal(ast::Literal::String(s)))
            }
        }
    }

    #[inline(always)]
    fn eval_macro(&mut self, node: &Shared<ast::Node>) -> EvalResult {
        let macro_env = Shared::new(SharedCell::new(Env::with_parent(Shared::downgrade(&self.env))));
        let value = self.eval_expr(&RuntimeValue::None, node, &macro_env)?;

        if let RuntimeValue::Ast(_) = value {
            Ok(value)
        } else {
            let token = get_token(Shared::clone(&self.token_arena), node.token_id);
            Err(RuntimeError::InvalidMacroResultAst((*token).clone()).into())
        }
    }

    fn match_pattern(
        &self,
        value: &RuntimeValue,
        pattern: &Pattern,
    ) -> Result<Option<Vec<(Ident, RuntimeValue)>>, RuntimeError> {
        match pattern {
            Pattern::Wildcard => {
                // Wildcard always matches, no bindings
                Ok(Some(Vec::new()))
            }
            Pattern::Ident(ident) => {
                // Identifier matches and binds the value
                Ok(Some(vec![(ident.name, value.clone())]))
            }
            Pattern::Literal(lit) => {
                // Literal pattern: check equality
                let pattern_value = self.eval_literal(lit);
                if *value == pattern_value {
                    Ok(Some(Vec::new()))
                } else {
                    Ok(None)
                }
            }
            Pattern::Type(type_name) => {
                // Type pattern: check runtime type
                let type_str = type_name.as_str();
                let matches = match type_str.as_str() {
                    "string" => matches!(value, RuntimeValue::String(_)),
                    "number" => matches!(value, RuntimeValue::Number(_)),
                    "bool" => matches!(value, RuntimeValue::Boolean(_)),
                    "array" => matches!(value, RuntimeValue::Array(_)),
                    "dict" => matches!(value, RuntimeValue::Dict(_)),
                    "bytes" => matches!(value, RuntimeValue::Bytes(_)),
                    "markdown" => matches!(value, RuntimeValue::Markdown(_, _)),
                    "function" => matches!(value, RuntimeValue::Function(_, _, _)),
                    "symbol" => matches!(value, RuntimeValue::Symbol(_)),
                    "none" => matches!(value, RuntimeValue::None),
                    _ => false,
                };

                if matches { Ok(Some(Vec::new())) } else { Ok(None) }
            }
            Pattern::Array(patterns) => {
                // Array pattern: match array elements
                if let RuntimeValue::Array(values) = value {
                    if values.len() != patterns.len() {
                        return Ok(None);
                    }

                    let mut all_bindings = Vec::new();
                    for (pattern, value) in patterns.iter().zip(values.iter()) {
                        if let Some(bindings) = self.match_pattern(value, pattern)? {
                            all_bindings.extend(bindings);
                        } else {
                            return Ok(None);
                        }
                    }
                    Ok(Some(all_bindings))
                } else {
                    Ok(None)
                }
            }
            Pattern::ArrayRest(patterns, rest_binding) => {
                // Array rest pattern: match prefix and bind rest
                if let RuntimeValue::Array(values) = value {
                    if values.len() < patterns.len() {
                        return Ok(None);
                    }

                    let mut all_bindings = Vec::new();

                    // Match the prefix patterns
                    for (pattern, value) in patterns.iter().zip(values.iter()) {
                        if let Some(bindings) = self.match_pattern(value, pattern)? {
                            all_bindings.extend(bindings);
                        } else {
                            return Ok(None);
                        }
                    }

                    // Bind the rest of the array
                    let rest_values = values[patterns.len()..].to_vec();
                    all_bindings.push((rest_binding.name, RuntimeValue::Array(rest_values)));

                    Ok(Some(all_bindings))
                } else {
                    Ok(None)
                }
            }
            Pattern::Dict(field_patterns) => {
                // Dict pattern: match dictionary fields
                if let RuntimeValue::Dict(dict) = value {
                    let mut all_bindings = Vec::new();

                    for (key, pattern) in field_patterns {
                        if let Some(field_value) = dict.get(&key.name) {
                            if let Some(bindings) = self.match_pattern(field_value, pattern)? {
                                all_bindings.extend(bindings);
                            } else {
                                return Ok(None);
                            }
                        } else {
                            // Required field is missing
                            return Ok(None);
                        }
                    }

                    Ok(Some(all_bindings))
                } else {
                    Ok(None)
                }
            }
            Pattern::Or(patterns) => {
                // Or pattern: match if any alternative matches
                for pattern in patterns {
                    if let Some(bindings) = self.match_pattern(value, pattern)? {
                        return Ok(Some(bindings));
                    }
                }
                Ok(None)
            }
        }
    }

    #[inline(always)]
    fn eval_fn(
        &mut self,
        runtime_value: &RuntimeValue,
        node: Shared<ast::Node>,
        ident: Ident,
        args: &ast::Args,
        env: &Shared<SharedCell<Env>>,
    ) -> EvalResult {
        #[cfg(not(feature = "sync"))]
        let resolved = env.borrow().resolve(ident);
        #[cfg(feature = "sync")]
        let resolved = env.read().unwrap().resolve(ident);

        match resolved {
            Ok(fn_value) => self.call_fn(&fn_value, node, ident, args, runtime_value, env),
            Err(_) => self.eval_builtin(runtime_value, node, &ident, args, env),
        }
    }

    #[inline(always)]
    fn eval_builtin(
        &mut self,
        runtime_value: &RuntimeValue,
        node: Shared<ast::Node>,
        ident: &Ident,
        args: &ast::Args,
        env: &Shared<SharedCell<Env>>,
    ) -> EvalResult {
        let args: Result<builtin::Args, EvalError> =
            args.iter().map(|arg| self.eval_expr(runtime_value, arg, env)).collect();
        builtin::eval_builtin(runtime_value, ident, args?, env)
            .map_err(|e| EvalError::from(e.to_runtime_error((*node).clone(), Shared::clone(&self.token_arena))))
    }

    #[inline(always)]
    fn eval_call_dynamic(
        &mut self,
        runtime_value: &RuntimeValue,
        callable: &Shared<ast::Node>,
        args: &ast::Args,
        env: &Shared<SharedCell<Env>>,
    ) -> EvalResult {
        let fn_value = self.eval_expr(runtime_value, callable, env)?;

        self.call_fn(
            &fn_value,
            Shared::clone(callable),
            *DYNAMIC_IDENT,
            args,
            runtime_value,
            env,
        )
    }

    #[inline(always)]
    fn enter_scope(&mut self) -> Result<(), EvalError> {
        if self.call_stack_depth >= self.options.max_call_stack_depth {
            return Err(RuntimeError::RecursionError(self.options.max_call_stack_depth).into());
        }
        self.call_stack_depth += 1;
        Ok(())
    }

    #[inline(always)]
    fn exit_scope(&mut self) {
        if self.call_stack_depth > 0 {
            self.call_stack_depth -= 1;
        }
    }

    fn call_fn(
        &mut self,
        fn_value: &RuntimeValue,
        node: Shared<ast::Node>,
        ident: Ident,
        args: &ast::Args,
        runtime_value: &RuntimeValue,
        env: &Shared<SharedCell<Env>>,
    ) -> EvalResult {
        if let RuntimeValue::Function(params, program, fn_env) = fn_value {
            self.enter_scope()?;
            #[cfg(feature = "debugger")]
            self.debugger.write().unwrap().push_call_stack(Shared::clone(&node));

            let new_env = Shared::new(SharedCell::new(Env::with_parent(Shared::downgrade(fn_env))));

            // If the function name matches a built-in, expose the native builtin in the
            // body's scope so calls to the same name invoke the builtin rather than
            // recursing back into this user-defined function.
            if builtin::get_builtin_functions(&ident).is_some() {
                define(&new_env, ident, RuntimeValue::NativeFunction(ident));
            }

            let has_variadic = params.iter().any(|p| p.is_variadic);
            let required_params = params.iter().filter(|p| p.default.is_none() && !p.is_variadic).count();
            let arg_count = args.len();
            let param_count = params.len();

            let use_self_param = if has_variadic {
                if arg_count >= required_params {
                    false
                } else if arg_count + 1 >= required_params {
                    true
                } else {
                    return Err(RuntimeError::InvalidNumberOfArguments {
                        token: (*get_token(Shared::clone(&self.token_arena), node.token_id)).clone(),
                        name: ident.to_string(),
                        expected: required_params as u8,
                        actual: args.len() as u8,
                    }
                    .into());
                }
            } else if arg_count >= required_params && arg_count <= param_count {
                false
            } else if arg_count + 1 >= required_params && arg_count < param_count {
                true
            } else {
                // arg_count > param_count: too many arguments
                return Err(RuntimeError::InvalidNumberOfArguments {
                    token: (*get_token(Shared::clone(&self.token_arena), node.token_id)).clone(),
                    name: ident.to_string(),
                    expected: params.len() as u8,
                    actual: args.len() as u8,
                }
                .into());
            };

            let mut param_iter = params.iter();
            let mut arg_iter = args.iter();

            if use_self_param && let Some(param) = param_iter.next() {
                define(&new_env, param.ident.name, runtime_value.clone());
            }

            for param in param_iter {
                if param.is_variadic {
                    // Collect all remaining arguments into an array
                    let mut variadic_args = Vec::new();
                    for arg in arg_iter.by_ref() {
                        variadic_args.push(self.eval_expr(runtime_value, arg, env)?);
                    }
                    define(&new_env, param.ident.name, RuntimeValue::Array(variadic_args));
                } else if let Some(arg) = arg_iter.next() {
                    let val = self.eval_expr(runtime_value, arg, env)?;
                    define(&new_env, param.ident.name, val);
                } else if let Some(default_expr) = &param.default {
                    let val = self.eval_expr(runtime_value, default_expr, &new_env)?;
                    define(&new_env, param.ident.name, val);
                } else {
                    return Err(RuntimeError::InvalidNumberOfArguments {
                        token: (*get_token(Shared::clone(&self.token_arena), node.token_id)).clone(),
                        name: ident.to_string(),
                        expected: params.len() as u8,
                        actual: args.len() as u8,
                    }
                    .into());
                }
            }

            let result = self.eval_program(program, runtime_value.clone(), &new_env);
            self.exit_scope();
            #[cfg(feature = "debugger")]
            self.debugger.write().unwrap().pop_call_stack();

            result
        } else if let RuntimeValue::NativeFunction(ident) = fn_value {
            self.eval_builtin(runtime_value, node, ident, args, env)
        } else {
            Err(RuntimeError::InvalidDefinition(
                (*get_token(Shared::clone(&self.token_arena), node.token_id)).clone(),
                ident.to_string(),
            )
            .into())
        }
    }
}

#[inline(always)]
fn define(env: &Shared<SharedCell<Env>>, ident: Ident, runtime_value: RuntimeValue) {
    #[cfg(not(feature = "sync"))]
    {
        env.borrow_mut().define(ident, runtime_value);
    }
    #[cfg(feature = "sync")]
    {
        env.write().unwrap().define(ident, runtime_value);
    }
}

#[inline(always)]
fn define_mutable(env: &Shared<SharedCell<Env>>, ident: Ident, runtime_value: RuntimeValue) {
    #[cfg(not(feature = "sync"))]
    {
        env.borrow_mut().define_mutable(ident, runtime_value);
    }
    #[cfg(feature = "sync")]
    {
        env.write().unwrap().define_mutable(ident, runtime_value);
    }
}

#[inline(always)]
fn resolve(ident: &str, env: &Shared<SharedCell<Env>>) -> Result<RuntimeValue, EnvError> {
    #[cfg(not(feature = "sync"))]
    {
        env.borrow().resolve(ident.into())
    }
    #[cfg(feature = "sync")]
    {
        env.read().unwrap().resolve(ident.into())
    }
}

#[cfg(test)]
mod tests {
    use std::f64::consts::PI;
    use std::vec;

    use crate::ast::node::{Args, IdentWithToken, MatchArm, Param};
    use crate::error::runtime::RuntimeError;
    use crate::eval::module::error::ModuleError;
    use crate::number::{INFINITE, NAN, Number};
    use crate::range::Range;
    use crate::{AstExpr, AstNode, DefaultModuleLoader, ModuleLoader, token_alloc};
    use crate::{Token, TokenKind};

    use super::*;
    use rstest::{fixture, rstest};
    use scopeguard::defer;
    use smallvec::{SmallVec, smallvec};
    use std::io::Write;
    use std::{fs::File, path::PathBuf};

    type TempDir = PathBuf;
    type TempFile = PathBuf;

    fn create_file(name: &str, content: &str) -> (TempDir, TempFile) {
        let temp_dir = std::env::temp_dir();
        let temp_file_path = temp_dir.join(name);
        let mut file = File::create(&temp_file_path).expect("Failed to create temp file");
        file.write_all(content.as_bytes())
            .expect("Failed to write to temp file");

        (temp_dir, temp_file_path)
    }

    #[fixture]
    fn token_arena() -> Shared<SharedCell<Arena<Shared<Token>>>> {
        let token_arena = Shared::new(SharedCell::new(Arena::new(10)));

        token_alloc(
            &token_arena,
            &Shared::new(Token {
                kind: TokenKind::Eof,
                range: Range::default(),
                module_id: 1.into(),
            }),
        );

        token_arena
    }

    fn ast_node(expr: AstExpr) -> Shared<AstNode> {
        Shared::new(AstNode {
            token_id: 0.into(),
            expr: Shared::new(expr),
        })
    }

    fn ast_call(name: &str, args: Args) -> Shared<AstNode> {
        Shared::new(AstNode {
            token_id: 0.into(),
            expr: Shared::new(ast::Expr::Call(IdentWithToken::new(name), args)),
        })
    }

    #[rstest]
    #[case::starts_with(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("starts_with", smallvec![ast_node(ast::Expr::Literal(ast::Literal::String("te".to_string())))])
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::starts_with(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "test".to_string(), position: None}))],
       vec![
            ast_call("starts_with", smallvec![ast_node(ast::Expr::Literal(ast::Literal::String("te".to_string())))])
       ],
       Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "true".to_string(), position: None}))]))]
    #[case::starts_with(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("starts_with", smallvec![ast_node(ast::Expr::Literal(ast::Literal::String("st".to_string())))])
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::starts_with(vec![RuntimeValue::Array(vec!["start".to_string().into(), "end".to_string().into()])],
       vec![
            ast_call("starts_with", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("start".to_string())))
            ])
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::starts_with(vec![RuntimeValue::Array(vec!["start".to_string().into(), "end".to_string().into()])],
       vec![
            ast_call("starts_with", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("end".to_string())))
            ])
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::starts_with(vec![RuntimeValue::Number(1.into())],
       vec![
            ast_call("starts_with", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("end".to_string())))
            ])
       ],
       Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                    name: "starts_with".to_string(),
                                                    args: vec!["number".into(), "string".into()]})))]
    #[case::ends_with(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("ends_with", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("st".to_string())))
            ])
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::ends_with(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "test".to_string(), position: None}))],
       vec![
            ast_call("ends_with", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("st".to_string())))
            ])
       ],
       Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "true".to_string(), position: None}))]))]
    #[case::ends_with(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("ends_with", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("te".to_string())))
            ])
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::ends_with(vec![RuntimeValue::Array(vec!["start".to_string().into(), "end".to_string().into()])],
       vec![
            ast_call("ends_with", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("end".to_string())))
            ])
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::ends_with(vec![RuntimeValue::Array(vec!["start".to_string().into(), "end".to_string().into()])],
       vec![
            ast_call("ends_with", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("start".to_string())))
            ])
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::ends_with(vec![RuntimeValue::Number(1.into())],
       vec![
            ast_call("ends_with", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("te".to_string())))
            ])
       ],
       Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                    name: "ends_with".to_string(),
                                                    args: vec!["number".into(), "string".into()]})))]
    #[case::downcase(vec![RuntimeValue::String("TEST".to_string())],
       vec![ast_call("downcase", SmallVec::new())],
       Ok(vec![RuntimeValue::String("test".to_string())]))]
    #[case::downcase(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "TEST".to_string(), position: None}))],
       vec![ast_call("downcase", SmallVec::new())],
       Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "test".to_string(), position: None}))]))]
    #[case::upcase(vec![RuntimeValue::String("test".to_string())],
       vec![ast_call("upcase", SmallVec::new())],
       Ok(vec![RuntimeValue::String("TEST".to_string())]))]
    #[case::upcase(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "test".to_string(), position: None}))],
       vec![ast_call("upcase", SmallVec::new())],
       Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "TEST".to_string(), position: None}))]))]
    #[case::upcase(vec![RuntimeValue::NONE],
       vec![ast_call("upcase", SmallVec::new())],
       Ok(vec![RuntimeValue::NONE]))]
    #[case::upcase(vec![RuntimeValue::Number(123.into())],
       vec![ast_call("upcase", SmallVec::new())],
       Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                    name: "upcase".to_string(),
                                                    args: vec!["number".into()]})))]
    #[case::replace(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("replace", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("test".to_string()))),
                ast_node(ast::Expr::Literal(ast::Literal::String("exam".to_string())))
            ])
       ],
       Ok(vec![RuntimeValue::String("examString".to_string())]))]
    #[case::replace(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "testString".to_string(), position: None}))],
       vec![
            ast_call("replace", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("test".to_string()))),
                ast_node(ast::Expr::Literal(ast::Literal::String("exam".to_string())))
            ])
       ],
       Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "examString".to_string(), position: None}))]))]
    #[case::replace(vec![RuntimeValue::NONE],
       vec![
            ast_call("replace", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("test".to_string()))),
                ast_node(ast::Expr::Literal(ast::Literal::String("exam".to_string())))
            ])
       ],
       Ok(vec![RuntimeValue::NONE]))]
    #[case::replace(vec![RuntimeValue::Number(123.into())],
       vec![
            ast_call("replace", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("test".to_string()))),
                ast_node(ast::Expr::Literal(ast::Literal::String("exam".to_string())))
            ])
       ],
       Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                    name: "replace".to_string(),
                                                    args: vec!["number".into(), "string".into(), "string".into()]})))]
    #[case::gsub_regex(vec![RuntimeValue::String("test123".to_string())],
       vec![
            ast_call("gsub", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String(r"\d+".to_string()))),
                ast_node(ast::Expr::Literal(ast::Literal::String("456".to_string())))
            ])
       ],
       Ok(vec![RuntimeValue::String("test456".to_string())]))]
    #[case::gsub_regex(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "test123".to_string(), position: None}))],
       vec![
            ast_call("gsub", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String(r"\d+".to_string()))),
                ast_node(ast::Expr::Literal(ast::Literal::String("456".to_string())))
            ])
       ],
       Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "test456".to_string(), position: None}))]))]
    #[case::gsub_regex(vec![RuntimeValue::Number(123.into())],
       vec![
            ast_call("gsub", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("test".to_string()))),
                ast_node(ast::Expr::Literal(ast::Literal::String(r"\d+".to_string()))),
            ])
       ],
       Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                    name: "gsub".to_string(),
                                                    args: vec!["number".into(), "string".into(), "string".into()]})))]
    #[case::len(vec![RuntimeValue::String("testString".to_string())],
       vec![ast_call("len", SmallVec::new())],
       Ok(vec![RuntimeValue::Number(10.into())]))]
    #[case::len(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "testString".to_string(), position: None}))],
       vec![ast_call("len", SmallVec::new())],
       Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "10".to_string(), position: None}))]))]
    #[case::len(vec![RuntimeValue::TRUE],
       vec![ast_call("len", SmallVec::new())],
       Ok(vec![RuntimeValue::Number(1.into())]))]
    #[case::len(vec![RuntimeValue::String("テスト".to_string())],
       vec![ast_call("len", SmallVec::new())],
       Ok(vec![RuntimeValue::Number(3.into())]))]
    #[case::len(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "テスト".to_string(), position: None}))],
       vec![ast_call("len", SmallVec::new())],
       Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "3".to_string(), position: None}))]))]
    #[case::utf8bytelen(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("utf8bytelen", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::Number(4.into())]))]
    #[case::utf8bytelen(vec![RuntimeValue::String("テスト".to_string())],
       vec![
            ast_call("utf8bytelen", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::Number(9.into())]))]
    #[case::utf8bytelen(vec![RuntimeValue::String("😊".to_string())],
       vec![
            ast_call("utf8bytelen", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::Number(4.into())]))]
    #[case::utf8bytelen(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "test".to_string(), position: None}))],
       vec![
            ast_call("utf8bytelen", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "4".to_string(), position: None}))]))]
    #[case::utf8bytelen(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "テスト".to_string(), position: None}))],
       vec![
            ast_call("utf8bytelen", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "9".to_string(), position: None}))]))]
    #[case::utf8bytelen(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "😊".to_string(), position: None}))],
       vec![
            ast_call("utf8bytelen", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "4".to_string(), position: None}))]))]
    #[case::utf8bytelen(vec![RuntimeValue::Array(vec![RuntimeValue::String("test".to_string())])],
       vec![
            ast_call("utf8bytelen", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::Number(1.into())]))]
    #[case::utf8bytelen(vec![RuntimeValue::TRUE],
       vec![
            ast_call("utf8bytelen", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::Number(1.into())]))]
    #[case::index(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("index", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("test".to_string())))
            ])
       ],
       Ok(vec![RuntimeValue::Number(0.into())]))]
    #[case::index(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "testString".to_string(), position: None}))],
       vec![
            ast_call("index", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("test".to_string())))
            ])
       ],
       Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "0".to_string(), position: None}))]))]
    #[case::index(vec![RuntimeValue::Number(1.into())],
       vec![
            ast_call("index", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("test".to_string())))
            ])
       ],
       Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                    name: "index".to_string(),
                                                    args: vec!["number".into(), "string".into()]})))]
    #[case::array_index(vec![RuntimeValue::Array(vec![RuntimeValue::String("test1".to_string()), RuntimeValue::String("test2".to_string()), RuntimeValue::String("test3".to_string())])],
        vec![
              ast_call("index", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("test2".to_string())))
              ])
        ],
        Ok(vec![RuntimeValue::Number(1.into())]))]
    #[case::array_index_not_found(vec![RuntimeValue::Array(vec![RuntimeValue::String("test1".to_string()), RuntimeValue::String("test2".to_string()), RuntimeValue::String("test3".to_string())])],
        vec![
              ast_call("index", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("test4".to_string())))
              ])
        ],
        Ok(vec![RuntimeValue::Number((-1).into())]))]
    #[case::rindex(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("rindex", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("String".to_string())))
            ])
       ],
       Ok(vec![RuntimeValue::Number(4.into())]))]
    #[case::rindex(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "testString".to_string(), position: None}))],
       vec![
            ast_call("rindex", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("String".to_string())))
            ])
       ],
       Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "4".to_string(), position: None}))]))]
    #[case::rindex(vec![RuntimeValue::Number(123.into())],
       vec![
            ast_call("rindex", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("String".to_string())))
            ])
       ],
       Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                    name: "rindex".to_string(),
                                                    args: vec!["number".into(), "string".into()]})))]
    #[case::array_rindex(vec![RuntimeValue::Array(vec![RuntimeValue::String("test1".to_string()), RuntimeValue::String("test2".to_string()), RuntimeValue::String("test1".to_string())])],
        vec![
              ast_call("rindex", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("test1".to_string())))
              ])
        ],
        Ok(vec![RuntimeValue::Number(2.into())]))]
    #[case::array_rindex(vec![RuntimeValue::Array(vec![RuntimeValue::String("test1".to_string()), RuntimeValue::String("test2".to_string()), RuntimeValue::String("test3".to_string())])],
        vec![
              ast_call("rindex", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("test4".to_string())))
              ])
        ],
        Ok(vec![RuntimeValue::Number((-1).into())]))]
    #[case::array_rindex_empty(vec![RuntimeValue::Array(Vec::new())],
        vec![
              ast_call("rindex", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("test".to_string())))
              ])
        ],
        Ok(vec![RuntimeValue::Number((-1).into())]))]
    #[case::eq(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("eq", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("eq".to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::String("eq".to_string())))
                ])
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::eq(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("eq", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("eq".to_string()))),
                ast_node(ast::Expr::Literal(ast::Literal::String("eq1".to_string())))
            ])
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::ne(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("ne", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("eq".to_string()))),
                ast_node(ast::Expr::Literal(ast::Literal::String("eq1".to_string())))
            ])
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::ne(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("ne", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("eq".to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::String("eq".to_string())))
                ])
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::ne(vec![RuntimeValue::Number(1.3.into())],
       vec![
            ast_call("ne", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.3.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.3.into()))),
                ]),
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::ne(vec![RuntimeValue::Number(1.3.into())],
       vec![
            ast_call("ne", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.3.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.2.into()))),
                ]),
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::gt(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("gt", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                ]),
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::gt(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("gt", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
                ]),
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::gt(vec![RuntimeValue::Number(1.3.into())],
       vec![
            ast_call("gt", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.4.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.3.into()))),
                ]),
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::gt(vec![RuntimeValue::Number(1.3.into())],
       vec![
            ast_call("gt", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.3.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.4.into()))),
                ]),
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::gt(vec![RuntimeValue::Number(1.3.into())],
       vec![
            ast_call("gt", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String(1.to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::String(2.to_string()))),
                ]),
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::gt(vec![RuntimeValue::Number(1.3.into())],
       vec![
            ast_call("gt", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String(2.to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::String(1.to_string()))),
                ]),
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::gt(vec![RuntimeValue::FALSE],
       vec![
            ast_call("gt", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                    ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                ]),
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::gt(vec![RuntimeValue::FALSE],
       vec![
            ast_call("gt", smallvec![
                ast_call("to_code", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("1".into()))),
                ]),
                ast_call("to_code", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("2".into()))),
                ]),
            ]),
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::gt(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("gt", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::String("1".to_string()))),
                ]),
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::gte(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("gte", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                ]),
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::gte(vec![RuntimeValue::String("test".to_string())],
       vec! [
            ast_call("gte", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                ]),
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::gte(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("gte", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
                ]),
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::gte(vec![RuntimeValue::Number(1.3.into())],
       vec![
            ast_call("gte", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.3.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.3.into()))),
                ]),
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::gte(vec![RuntimeValue::Number(1.3.into())],
       vec![
            ast_call("gte", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.2.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.3.into()))),
                ]),
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::gte(vec![RuntimeValue::String("test".to_string())],
       vec! [
            ast_call("gte", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String(2.to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::String(1.to_string()))),
                ]),
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::gte(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("gte", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String(1.to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::String(2.to_string()))),
                ]),
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::gte(vec![RuntimeValue::TRUE],
       vec![
            ast_call("gte", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                    ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                ]),
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::gte(vec![RuntimeValue::TRUE],
       vec![
            ast_call("gte", smallvec![
                ast_call("to_code", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("1".into()))),
                ]),
                ast_call("to_code", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("1".into()))),
                ]),
            ]),
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::lt(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("lt", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
                ]),
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::lt(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("lt", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                ]),
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::lt(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("lt", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.2.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.3.into()))),
                ])
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::lt(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("lt", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.3.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.2.into()))),
                ])
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::lt(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("lt", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String(1.to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::String(2.to_string()))),
                ]),
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::lt(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("lt", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String(2.to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::String(1.to_string()))),
                ]),
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::lt(vec![RuntimeValue::TRUE],
       vec![
            ast_call("lt", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                    ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                ]),
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::lt(vec![RuntimeValue::TRUE],
       vec![
            ast_call("lt", smallvec![
                ast_call("to_code", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("2".into()))),
                ]),
                ast_call("to_code", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("1".into()))),
                ]),
            ]),
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::lt(vec![RuntimeValue::TRUE],
       vec![
            ast_call("lt", smallvec![
                ast_call("to_code", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("1".into()))),
                ]),
                ast_call("to_code", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("2".into()))),
                ]),
            ]),
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::lt(vec![RuntimeValue::TRUE],
       vec![
            ast_call("lt", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::String("2".into()))),
                ]),
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::lte(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("lte", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                ])
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::lte(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("lte", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
                ])
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::lte(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("lte", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                ])
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::lte(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("lte", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.3.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.3.into()))),
                ])
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::lte(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("lte", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.2.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.3.into()))),
                ])
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::lte(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("lte", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.4.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.3.into()))),
                ]),
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::lte(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("lte", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
                ])
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::lte(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("lte", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String(2.to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::String(1.to_string()))),
                ])
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::lte(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("lte", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::String("2".to_string()))),
                ])
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::lte(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("lte", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                    ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                ])
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::add(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("add", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                ]),
       ],
       Ok(vec![RuntimeValue::Number(2.into())]))]
    #[case::add(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("add", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("te".to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::String("st".to_string()))),
                ]),
       ],
       Ok(vec![RuntimeValue::String("test".to_string())]))]
    #[case::add(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("add", smallvec![
                    ast_call("array", smallvec![
                        ast_node(ast::Expr::Literal(ast::Literal::String("te".to_string())))
                    ]),
                    ast_call("array", smallvec![
                        ast_node(ast::Expr::Literal(ast::Literal::String("te".to_string())))
                    ])
                ]),
       ],
       Ok(vec![RuntimeValue::Array(vec!["te".to_string().into(), "te".to_string().into()])]))]
    #[case::add(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("add", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                ]),
       ],
       Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                         name: "add".to_string(),
                                                         args: vec!["bool".into(), "number".into()]})))]
    #[case::add(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("add", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.3.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.3.into()))),
                ]),
       ],
       Ok(vec![RuntimeValue::Number(2.6.into())]))]
    #[case::add(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("add", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.3.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.3.into()))),
                ]),
       ],
       Ok(vec![RuntimeValue::Number(2.6.into())]))]
    #[case::add(vec![RuntimeValue::TRUE],
       vec![
            ast_call("add", smallvec![
                ast_call("to_code", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("2".into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::None)),
                ]),
                ast_call("to_code", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("1".into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::None)),
                ]),
            ]),
       ],
       Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Code(mq_markdown::Code{value: "21".to_string(), lang: None, fence: true, meta: None, position: None}))]))]
    #[case::add(vec![RuntimeValue::TRUE],
       vec![
            ast_call("add", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("2".into()))),
                ast_call("to_code", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("1".into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::None)),
                ]),
            ]),
       ],
       Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Code(mq_markdown::Code{value: "21".to_string(), lang: None, fence: true, meta: None, position: None}))]))]
    #[case::add(vec![RuntimeValue::TRUE],
       vec![
            ast_call("add", smallvec![
                ast_call("to_code", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("2".into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::None)),
                ]),
                ast_node(ast::Expr::Literal(ast::Literal::String("1".into()))),
            ]),
       ],
       Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Code(mq_markdown::Code{value: "21".to_string(), lang: None, fence: true, meta: None, position: None}))]))]
    #[case::sub(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("sub", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                ]),
       ],
       Ok(vec![RuntimeValue::Number(0.into())]))]
    #[case::sub(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("sub", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("te".to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                ]),
       ],
       Err(InnerError::Runtime(RuntimeError::Runtime(Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()}, "invalid float literal".to_string()))))]
    #[case::sub(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("sub", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.3.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.2.into()))),
                ])
       ],
       Ok(vec![RuntimeValue::Number(0.10000000000000009.into())]))]
    #[case::div(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("div", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                ])
       ],
       Ok(vec![RuntimeValue::Number(1.into())]))]
    #[case::div(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("div", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("te".to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                ])
       ],
       Err(InnerError::Runtime(RuntimeError::Runtime(Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()}, "invalid float literal".to_string()))))]
    #[case::div(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("div", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(0.into()))),
                ])
       ],
       Err(InnerError::Runtime(RuntimeError::ZeroDivision(Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()}))))]
    #[case::div(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("div", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.3.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.1.into()))),
                ])
       ],
       Ok(vec![RuntimeValue::Number(1.1818181818181817.into())]))]
    #[case::mul(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("mul", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                ]),
       ],
       Ok(vec![RuntimeValue::Number(2.into())]))]
    #[case::mul(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("mul", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(2.0.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.3.into()))),
                ]),
       ],
       Ok(vec![RuntimeValue::Number(2.6.into())]))]
    #[case::mul(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("mul", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("te".to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
                ]),
       ],
       Ok(vec![RuntimeValue::String("tete".to_string())]))]
    #[case::mod_(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("mod", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
                ]),
       ],
       Ok(vec![RuntimeValue::Number(1.into())]))]
    #[case::mod_(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("mod", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.1.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(2.0.into()))),
                ]),
       ],
       Ok(vec![RuntimeValue::Number(1.1.into())]))]
    #[case::mod_(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("mod", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("te".to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                ]),
       ],
       Err(InnerError::Runtime(RuntimeError::Runtime(Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()}, "invalid float literal".to_string()))))]
    #[case::pow(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("pow", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(3.into()))),
                ]),
       ],
       Ok(vec![RuntimeValue::Number(8.into())]))]
    #[case::pow(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("pow", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("te".to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                ]),
       ],
       Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                    name: "pow".to_string(),
                                                    args: vec!["string".into(), "number".into()]})))]
    #[case::and(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("and", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                    ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                ]),
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::and(vec![RuntimeValue::TRUE],
       vec![
            ast_call("and", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                    ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                ]),
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::and(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("and", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                    ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                ]),
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::and(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("and", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                    ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                ]),
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::or(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("or", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                    ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                ]),
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::or(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("or", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                    ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                ]),
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::or(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("or", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                    ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                ]),
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::or(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("or", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                    ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                ]),
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::not(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("not", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                ]),
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::not(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("not", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                ]),
       ],
       Ok(vec![RuntimeValue::TRUE]))]
    #[case::to_string(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("to_string", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::String("test".to_string())]))]
    #[case::to_string(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "test".to_string(), position: None}))],
       vec![
            ast_call("to_string", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "test".to_string(), position: None}))]))]
    #[case::to_string_symbol(vec![RuntimeValue::Symbol("test".to_string().into())],
       vec![
            ast_call("to_string", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::String("test".to_string())]))]
    #[case::split1(vec![RuntimeValue::String("test1,test2".to_string())],
       vec![
            ast_call("split", smallvec![
                        ast_node(ast::Expr::Literal(ast::Literal::String(",".to_string())))]
                        )
       ],
       Ok(vec![RuntimeValue::Array(vec![RuntimeValue::String("test1".to_string()), RuntimeValue::String("test2".to_string())])]))]
    #[case::split2(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "test1,test2".to_string(), position: None}))],
       vec![
            ast_call("split", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String(",".to_string())))
            ])
       ],
       Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "test1\ntest2".to_string(), position: None}))]))]
    #[case::split(vec![RuntimeValue::Number(1.into())],
       vec![
            ast_call("split", smallvec![ast_node(ast::Expr::Literal(ast::Literal::String(",".to_string())))])
       ],
       Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                    name: "split".to_string(),
                                                    args: vec!["number".into(), "string".into()]})))]
    #[case::split_array(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("value1".to_string()),
            RuntimeValue::String("separator".to_string()),
            RuntimeValue::String("value2".to_string()),
        ])],
        vec![
            ast_call("split", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("separator".to_string())))
            ])
        ],
        Ok(vec![RuntimeValue::Array(vec![
            RuntimeValue::Array(vec![RuntimeValue::String("value1".to_string())]),
            RuntimeValue::Array(vec![RuntimeValue::String("value2".to_string())])
        ])]))]
    #[case::split_array_multiple_separators(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("value1".to_string()),
            RuntimeValue::String("separator".to_string()),
            RuntimeValue::String("value2".to_string()),
            RuntimeValue::String("separator".to_string()),
            RuntimeValue::String("value3".to_string()),
        ])],
        vec![
            ast_call("split", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("separator".to_string())))
            ])
        ],
        Ok(vec![RuntimeValue::Array(vec![
            RuntimeValue::Array(vec![RuntimeValue::String("value1".to_string())]),
            RuntimeValue::Array(vec![RuntimeValue::String("value2".to_string())]),
            RuntimeValue::Array(vec![RuntimeValue::String("value3".to_string())])
        ])]))]
    #[case::split_array_no_separator(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("value1".to_string()),
            RuntimeValue::String("value2".to_string()),
            RuntimeValue::String("value3".to_string()),
        ])],
        vec![
            ast_call("split", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("separator".to_string())))
            ])
        ],
        Ok(vec![RuntimeValue::Array(vec![
        RuntimeValue::Array(vec![
            RuntimeValue::String("value1".to_string()),
            RuntimeValue::String("value2".to_string()),
            RuntimeValue::String("value3".to_string())
        ])
        ])]))]
    #[case::split_array_empty(vec![RuntimeValue::Array(Vec::new())],
        vec![
            ast_call("split", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("separator".to_string())))
            ])
        ],
        Ok(vec![RuntimeValue::Array(vec![RuntimeValue::Array(Vec::new())])]))]
    #[case::split_array_mixed_types(vec![RuntimeValue::Array(vec![
            RuntimeValue::Number(1.into()),
            RuntimeValue::String("separator".to_string()),
            RuntimeValue::Boolean(true),
        ])],
        vec![
            ast_call("split", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("separator".to_string())))
            ])
        ],
        Ok(vec![RuntimeValue::Array(vec![
            RuntimeValue::Array(vec![RuntimeValue::Number(1.into())]),
            RuntimeValue::Array(vec![RuntimeValue::Boolean(true)])
        ])]))]
    #[case::join1(vec![RuntimeValue::String("test1,test2".to_string())],
       vec![
            ast_call("join", smallvec![
                ast_call("split", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String(",".to_string())))
                ]),
                ast_node(ast::Expr::Literal(ast::Literal::String("#".to_string())))
            ])
       ],
       Ok(vec![RuntimeValue::String("test1#test2".to_string())]))]
    #[case::join_error(vec![RuntimeValue::Number(1.into())],
       vec![
            ast_call("join", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("#".to_string())))
            ])
       ],
       Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                    name: "join".to_string(),
                                                    args: vec!["number".into(), "string".into()]})))]
    #[case::reverse_string(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("reverse", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::String("tset".to_string())]))]
    #[case::reverse_string_empty(vec![RuntimeValue::String("".to_string())],
       vec![
            ast_call("reverse", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::String("".to_string())]))]
    #[case::reverse_array(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("a".to_string()),
            RuntimeValue::String("b".to_string()),
            RuntimeValue::String("c".to_string()),
        ])],
        vec![
            ast_call("reverse", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("c".to_string()),
            RuntimeValue::String("b".to_string()),
            RuntimeValue::String("a".to_string()),
        ])]))]
    #[case::reverse_array_empty(vec![RuntimeValue::Array(Vec::new())],
        vec![
            ast_call("reverse", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Array(Vec::new())]))]
    #[case::reverse_number(vec![RuntimeValue::Number(123.into())],
       vec![
            ast_call("reverse", SmallVec::new())
       ],
       Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                    name: "reverse".to_string(),
                                                    args: vec!["number".into()]})))]
    #[case::base64(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("base64", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("test".to_string())))
            ])
       ],
       Ok(vec![RuntimeValue::String("dGVzdA==".to_string())]))]
    #[case::base64(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value:"test".to_string(), position: None}))],
       vec![
            ast_call("base64", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "dGVzdA==".to_string(), position: None}))]))]
    #[case::base64(vec![RuntimeValue::Number(1.into())],
       vec![
            ast_call("base64", SmallVec::new())
       ],
       Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                    name: "base64".to_string(),
                                                    args: vec!["number".into()]})))]
    #[case::base64d(vec![RuntimeValue::String("dGVzdA==".to_string())],
       vec![
            ast_call("base64d", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("dGVzdA==".to_string())))
            ])
       ],
       Ok(vec![RuntimeValue::String("test".to_string())]))]
    #[case::base64d(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value:"dGVzdA==".to_string(), position: None}))],
       vec![
            ast_call("base64d", smallvec![
            ])
       ],
       Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "test".to_string(), position: None}))]))]
    #[case::base64d(vec![RuntimeValue::Number(1.into())],
       vec![
            ast_call("base64d", smallvec![
            ])
       ],
       Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                    name: "base64d".to_string(),
                                                    args: vec!["number".into()]})))]
    #[case::base64url_encode(
        vec![RuntimeValue::String("hello".into())],
        vec![
            ast_call("base64url", smallvec![
            ])
        ],
        Ok(vec![RuntimeValue::String("aGVsbG8".into())])
    )]
    #[case::base64url_decode(
        vec![RuntimeValue::String("aGVsbG8".into())],
        vec![
            ast_call("base64urld", smallvec![
            ])
        ],
        Ok(vec![RuntimeValue::String("hello".into())])
    )]
    #[case::base64url(vec![RuntimeValue::Number(1.into())],
       vec![
            ast_call("base64url", smallvec![
            ])
       ],
       Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                    name: "base64url".to_string(),
                                                    args: vec!["number".into()]})))]
    #[case::base64urld(vec![RuntimeValue::Number(1.into())],
       vec![
            ast_call("base64urld", smallvec![
            ])
       ],
       Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                    name: "base64urld".to_string(),
                                                    args: vec!["number".into()]})))]
    #[case::def(vec![RuntimeValue::String("test1,test2".to_string())],
       vec![
            ast_node(ast::Expr::Def(
                IdentWithToken::new("split2"),
                smallvec![
                    Param::new(IdentWithToken::new("str")),
                ],
                vec![ast_call("split",
                    smallvec![
                        ast_node(ast::Expr::Ident(IdentWithToken::new("str"))),
                        ast_node(ast::Expr::Literal(ast::Literal::String(",".to_string()))),
                    ])
                ]
            )),
            ast_call("split2", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("test1,test2".to_string()))),
            ]),
       ],
       Ok(vec![RuntimeValue::Array(vec![RuntimeValue::String("test1".to_string()), RuntimeValue::String("test2".to_string())])]))]
    #[case::def2(vec![RuntimeValue::String("Hello".to_string())],
       vec![
            ast_node(ast::Expr::Def(
                IdentWithToken::new("concat_self"),
                smallvec![
                    Param::new(IdentWithToken::new("str1")),
                    Param::new(IdentWithToken::new("str2")),
                ],
                vec![ast_call("add",
                    smallvec![
                        ast_node(ast::Expr::Ident(IdentWithToken::new("str1"))),
                        ast_node(ast::Expr::Ident(IdentWithToken::new("str2"))),
                    ])
                ]
            )),
            ast_call("concat_self", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("Hello".to_string()))),
                ast_node(ast::Expr::Literal(ast::Literal::String("World".to_string()))),
            ])
       ],
       Ok(vec![RuntimeValue::String("HelloWorld".to_string())]))]
    #[case::def3(vec![RuntimeValue::String("Test".to_string())],
       vec![
            ast_node(ast::Expr::Def(
                IdentWithToken::new("prepend_self"),
                smallvec![
                    Param::new(IdentWithToken::new("str1")),
                    Param::new(IdentWithToken::new("str2")),
                ],
                vec![ast_call("add",
                    smallvec![
                        ast_node(ast::Expr::Ident(IdentWithToken::new("str1"))),
                        ast_node(ast::Expr::Ident(IdentWithToken::new("str2"))),
                    ])
                ]
            )),
            ast_call("prepend_self", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("test".to_string())))
            ])
       ],
       Ok(vec![RuntimeValue::String("Testtest".to_string())]))]
    #[case::type_string(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("type", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::String("string".to_string())]))]
    #[case::type_int(vec![RuntimeValue::Number(42.into())],
       vec![
            ast_call("type", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::String("number".to_string())]))]
    #[case::type_bool(vec![RuntimeValue::TRUE],
       vec![
            ast_call("type", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::String("bool".to_string())]))]
    #[case::type_array(vec![RuntimeValue::Array(vec![RuntimeValue::String("test".to_string())])],
       vec![
            ast_call("type", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::String("array".to_string())]))]
    #[case::min(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("min", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
                ])
        ],
       Ok(vec![RuntimeValue::Number(1.into())]))]
    #[case::min(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("min", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("1".into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::String("2".into()))),
                ])
        ],
       Ok(vec![RuntimeValue::String("1".into())]))]
    #[case::min(vec![RuntimeValue::Number(1.into())],
       vec![
            ast_call("min", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
            ])
        ],
       Ok(vec![RuntimeValue::Number(1.into())]))]
    #[case::min(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("min", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("te".to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                ])
            ],
       Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                    name: "min".to_string(),
                                                    args: vec!["string".into(), "number".into()]})))]
    #[case::max(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("max", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
                ])
            ],
       Ok(vec![RuntimeValue::Number(2.into())]))]
    #[case::max(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("max", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("1".into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::String("2".into()))),
                ])
            ],
       Ok(vec![RuntimeValue::String("2".into())]))]
    #[case::max(vec![RuntimeValue::Number(3.into())],
       vec![
            ast_call("max", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                ])
            ],
       Ok(vec![RuntimeValue::Number(3.into())]))]
    #[case::max(vec![RuntimeValue::String("test".to_string())],
       vec![
            ast_call("max", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("te".to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                ])
            ],
       Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                    name: "max".to_string(),
                                                    args: vec!["string".into(), "number".into()]})))]
    #[case::trim(vec![RuntimeValue::String("  test  ".to_string())],
       vec![
            ast_call("trim", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::String("test".to_string())]))]
    #[case::trim(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "  test  ".to_string(), position: None}))],
       vec![
            ast_call("trim", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "test".to_string(), position: None}))]))]
    #[case::trim(vec![RuntimeValue::Number(1.into())],
       vec![
            ast_call("trim", SmallVec::new())
       ],
       Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                    name: "trim".to_string(),
                                                    args: vec!["number".into()]})))]
    #[case::slice(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "testString".to_string(), position: None}))],
       vec![
            ast_call("slice", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Number(0.into()))),
                ast_node(ast::Expr::Literal(ast::Literal::Number(4.into()))),
            ])
       ],
       Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "test".to_string(), position: None}))]))]
    #[case::slice(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("slice", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Number(0.into()))),
                ast_node(ast::Expr::Literal(ast::Literal::Number(4.into()))),
            ])
       ],
       Ok(vec![RuntimeValue::String("test".to_string())]))]
    #[case::slice(vec![RuntimeValue::String("testString".to_string())],
       vec![
            ast_call("slice", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Number(4.into()))),
                ast_node(ast::Expr::Literal(ast::Literal::Number(10.into()))),
            ])
       ],
       Ok(vec![RuntimeValue::String("String".to_string())]))]
    #[case::slice(vec![RuntimeValue::NONE],
       vec![
            ast_call("slice", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Number(0.into()))),
                ast_node(ast::Expr::Literal(ast::Literal::Number(4.into()))),
            ])
       ],
       Ok(vec![RuntimeValue::NONE]))]
    #[case::slice_array(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("item1".to_string()),
            RuntimeValue::String("item2".to_string()),
            RuntimeValue::String("item3".to_string()),
            RuntimeValue::String("item4".to_string()),
            RuntimeValue::String("item5".to_string()),
        ])],
       vec![
            ast_call("slice", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                ast_node(ast::Expr::Literal(ast::Literal::Number(4.into()))),
            ])
       ],
       Ok(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("item2".to_string()),
            RuntimeValue::String("item3".to_string()),
            RuntimeValue::String("item4".to_string()),
        ])]))]
    #[case::slice_array_from_start(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("item1".to_string()),
            RuntimeValue::String("item2".to_string()),
            RuntimeValue::String("item3".to_string()),
        ])],
       vec![
            ast_call("slice", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Number(0.into()))),
                ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
            ])
       ],
       Ok(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("item1".to_string()),
            RuntimeValue::String("item2".to_string()),
        ])]))]
    #[case::slice_array_to_end(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("item1".to_string()),
            RuntimeValue::String("item2".to_string()),
            RuntimeValue::String("item3".to_string()),
        ])],
       vec![
            ast_call("slice", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                ast_node(ast::Expr::Literal(ast::Literal::Number(3.into()))),
            ])
       ],
       Ok(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("item2".to_string()),
            RuntimeValue::String("item3".to_string()),
       ])]))]
    #[case::slice_array_out_of_bounds(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("item1".to_string()),
            RuntimeValue::String("item2".to_string()),
            RuntimeValue::String("item3".to_string()),
        ])],
       vec![
            ast_call("slice", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
                ast_node(ast::Expr::Literal(ast::Literal::Number(5.into()))),
            ])
       ],
       Ok(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("item3".to_string()),
        ])]))]
    #[case::slice_array_empty(vec![RuntimeValue::Array(Vec::new())],
       vec![
            ast_call("slice", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Number(0.into()))),
                ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
            ])
       ],
       Ok(vec![RuntimeValue::Array(Vec::new())]))]
    #[case::slice_array_mixed_types(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("item1".to_string()),
            RuntimeValue::Number(42.into()),
            RuntimeValue::Boolean(true),
            RuntimeValue::String("item4".to_string()),
        ])],
       vec![
            ast_call("slice", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                ast_node(ast::Expr::Literal(ast::Literal::Number(3.into()))),
            ])
       ],
       Ok(vec![RuntimeValue::Array(vec![
            RuntimeValue::Number(42.into()),
            RuntimeValue::Boolean(true),
        ])]))]
    #[case::slice(vec![RuntimeValue::Number(123.into())],
       vec![
            ast_call("slice", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Number(0.into()))),
                ast_node(ast::Expr::Literal(ast::Literal::Number(4.into()))),
            ])
       ],
       Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                    name: "slice".to_string(),
                                                    args: vec!["number".into(), "number".into(), "number".into()]})))]
    #[case::match_regex1(vec![RuntimeValue::String("test123".to_string())],
       vec![
            ast_call("regex_match", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String(r"\d+".to_string()))),
            ])
       ],
       Ok(vec![RuntimeValue::Array(vec![RuntimeValue::String("123".to_string())])]))]
    #[case::match_regex2(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "test123".to_string(), position: None}))],
       vec![
            ast_call("regex_match", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String(r"\d+".to_string()))),
            ])
       ],
       Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "123".to_string(), position: None}))]))]
    #[case::match_regex3(vec![RuntimeValue::Number(123.into())],
       vec![
            ast_call("regex_match", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String(r"\d+".to_string()))),
            ])
       ],
       Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                    name: "regex_match".to_string(),
                                                    args: vec!["number".into(), "string".into()]})))]
    #[case::explode(vec![RuntimeValue::String("ABC".to_string())],
       vec![
            ast_call("explode", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::Array(vec![
            RuntimeValue::Number(65.into()),
            RuntimeValue::Number(66.into()),
            RuntimeValue::Number(67.into()),
       ])]))]
    #[case::explode(vec![RuntimeValue::Number(123.into())],
       vec![
            ast_call("explode", SmallVec::new())
       ],
       Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                    name: "explode".to_string(),
                                                    args: vec!["number".into()]})))]
    #[case::implode(vec![RuntimeValue::Array(vec![
            RuntimeValue::Number(65.into()),
            RuntimeValue::Number(66.into()),
            RuntimeValue::Number(67.into()),
       ])],
       vec![
            ast_call("implode", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::String("ABC".to_string())]))]
    #[case::implode(vec!["test".to_string().into()],
       vec![
            ast_call("implode", SmallVec::new())
       ],
       Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                    name: "implode".to_string(),
                                                    args: vec!["string".into()]})))]
    #[case::explode_markdown(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "ABC".to_string(), position: None}))],
        vec![
             ast_call("explode", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "65\n66\n67".to_string(), position: None}))]))]
    #[case::to_number(vec![RuntimeValue::String("42".to_string())],
       vec![
            ast_call("to_number", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::Number(42.into())]))]
    #[case::to_number(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "42".to_string(), position: None}))],
       vec![
            ast_call("to_number", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "42".to_string(), position: None}))]))]
    #[case::to_number(vec![RuntimeValue::String("42.5".to_string())],
       vec![
            ast_call("to_number", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::Number(42.5.into())]))]
    #[case::to_number(vec![RuntimeValue::String("not a number".to_string())],
       vec![
            ast_call("to_number", SmallVec::new())
       ],
       Err(InnerError::Runtime(RuntimeError::Runtime(Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()}, "invalid float literal".to_string()))))]
    #[case::to_number_array(vec![RuntimeValue::Array(vec![RuntimeValue::String("42".to_string()), RuntimeValue::String("43".to_string()), RuntimeValue::String("44".to_string())])],
        vec![
              ast_call("to_number", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Array(vec![RuntimeValue::Number(42.into()), RuntimeValue::Number(43.into()), RuntimeValue::Number(44.into())])]))]
    #[case::to_number_array(vec![RuntimeValue::Array(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "42".to_string(), position: None}))])],
        vec![
              ast_call("to_number", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Array(vec![RuntimeValue::Number(42.into())])]))]
    #[case::to_number_array_with_invalid(vec![RuntimeValue::Array(vec![RuntimeValue::String("42".to_string()), RuntimeValue::String("not a number".to_string()), RuntimeValue::String("44".to_string())])],
        vec![
              ast_call("to_number", SmallVec::new())
        ],
        Err(InnerError::Runtime(RuntimeError::Runtime(Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()}, "invalid float literal".to_string()))))]
    #[case::to_number_array_empty(vec![RuntimeValue::Array(Vec::new())],
        vec![
              ast_call("to_number", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Array(Vec::new())]))]
    #[case::to_number_array_mixed_types(vec![RuntimeValue::Array(vec![RuntimeValue::String("42".to_string()), RuntimeValue::Number(43.into()), RuntimeValue::String("44".to_string())])],
        vec![
              ast_call("to_number", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Array(vec![RuntimeValue::Number(42.into()), RuntimeValue::Number(43.into()), RuntimeValue::Number(44.into())])]))]
    #[case::trunc(vec![RuntimeValue::Number(42.5.into())],
       vec![
            ast_call("trunc", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::Number(42.into())]))]
    #[case::trunc(vec![RuntimeValue::Number((-42.5).into())],
       vec![
            ast_call("trunc", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::Number((-42).into())]))]
    #[case::trunc(vec!["42.5".to_string().into()],
       vec![
            ast_call("trunc", SmallVec::new())
       ],
       Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                    name: "trunc".to_string(),
                                                    args: vec!["string".into()]})))]
    #[case::abs_positive(vec![RuntimeValue::Number(42.into())],
       vec![
            ast_call("abs", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::Number(42.into())]))]
    #[case::abs_negative(vec![RuntimeValue::Number((-42).into())],
        vec![
            ast_call("abs", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Number(42.into())]))]
    #[case::abs_zero(vec![RuntimeValue::Number(0.into())],
        vec![
            ast_call("abs", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Number(0.into())]))]
    #[case::abs_decimal(vec![RuntimeValue::Number((-42.5).into())],
        vec![
            ast_call("abs", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Number(42.5.into())]))]
    #[case::abs_invalid_type(vec![RuntimeValue::String("42".to_string())],
        vec![
            ast_call("abs", SmallVec::new())
        ],
        Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                     name: "abs".to_string(),
                                                     args: vec!["string".into()]})))]
    #[case::ceil(vec![RuntimeValue::Number(42.1.into())],
        vec![
            ast_call("ceil", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Number(43.into())]))]
    #[case::ceil(vec![RuntimeValue::Number((-42.1).into())],
        vec![
            ast_call("ceil", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Number((-42).into())]))]
    #[case::ceil(vec!["42".to_string().into()],
        vec![
            ast_call("ceil", SmallVec::new())
        ],
        Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                     name: "ceil".to_string(),
                                                     args: vec!["string".into()]})))]
    #[case::round(vec![RuntimeValue::Number(42.5.into())],
        vec![
            ast_call("round", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Number(43.into())]))]
    #[case::round(vec![RuntimeValue::Number(42.4.into())],
        vec![
            ast_call("round", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Number(42.into())]))]
    #[case::round(vec!["42.4".to_string().into()],
        vec![
            ast_call("round", SmallVec::new())
        ],
        Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                     name: "round".to_string(),
                                                     args: vec!["string".into()]})))]
    #[case::floor(vec![RuntimeValue::Number(42.9.into())],
        vec![
            ast_call("floor", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Number(42.into())]))]
    #[case::floor(vec![RuntimeValue::Number((-42.9).into())],
        vec![
            ast_call("floor", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Number((-43).into())]))]
    #[case::floor_error(vec!["42.9".to_string().into()],
        vec![
            ast_call("floor", SmallVec::new())
        ],
        Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                     name: "floor".to_string(),
                                                     args: vec!["string".into()]})))]
    #[case::del(vec![RuntimeValue::Array(vec![RuntimeValue::String("test1".to_string()), RuntimeValue::String("test2".to_string())])],
        vec![
              ast_call("del", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(0.into()))),
              ]),
        ],
        Ok(vec![RuntimeValue::Array(vec![RuntimeValue::String("test2".to_string())])]))]
    #[case::del(vec![RuntimeValue::Array(vec![RuntimeValue::String("test1".to_string()), RuntimeValue::String("test2".to_string())])],
        vec![
              ast_call("del", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
              ]),
        ],
        Ok(vec![RuntimeValue::Array(vec![RuntimeValue::String("test1".to_string())])]))]
    #[case::del(vec![RuntimeValue::String("test1".to_string())],
        vec![
              ast_call("del", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(4.into()))),
              ]),
        ],
        Ok(vec![RuntimeValue::String("test".to_string())]))]
    #[case::del(vec![RuntimeValue::Number(123.into())],
        vec![
              ast_call("del", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(4.into()))),
              ]),
        ],
        Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                     name: "del".to_string(),
                                                     args: vec!["number".into(), "number".into()]})))]
    #[case::to_code(vec![RuntimeValue::String("test1".to_string())],
        vec![
              ast_call("to_code", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("elm".into()))),
              ]),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Code(mq_markdown::Code{lang: Some("elm".to_string()), value: "test1".to_string(), fence: true, meta: None, position: None}))]))]
    #[case::to_code(vec![RuntimeValue::String("test1".to_string())],
        vec![
              ast_call("to_code", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("elm".into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::None)),
              ]),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Code(mq_markdown::Code{lang: None, value: "elm".to_string(), fence: true, meta: None, position: None}))]))]
    #[case::md_h1(vec![RuntimeValue::String("Heading 1".to_string())],
        vec![
              ast_call("to_h", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
              ]),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Heading(mq_markdown::Heading{depth: 1, values: vec!["Heading 1".to_string().into()], position: None}))]))]
    #[case::md_h2(vec![RuntimeValue::String("Heading 2".to_string())],
        vec![
              ast_call("to_h", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
              ]),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Heading(mq_markdown::Heading{depth: 2, values: vec!["Heading 2".to_string().into()], position: None}))]))]
    #[case::md_h3(vec![RuntimeValue::String("Heading 3".to_string())],
        vec![
              ast_call("to_h", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(3.into()))),
              ]),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Heading(mq_markdown::Heading{depth: 3, values: vec!["Heading 3".to_string().into()], position: None}))]))]
    #[case::md_h3(vec![RuntimeValue::String("Heading 3".to_string())],
        vec![
              ast_call("to_h", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("3".into()))),
              ]),
        ],
        Ok(vec![RuntimeValue::NONE]))]
    #[case::md_h(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "Heading".to_string(), position: None}))],
        vec![
              ast_call("to_h", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
              ]),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Heading(mq_markdown::Heading{depth: 2, values: vec!["Heading".to_string().into()], position: None}))]))]
    #[case::to_math(vec![RuntimeValue::String("E=mc^2".to_string())],
        vec![
              ast_call("to_math", SmallVec::new()),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Math(mq_markdown::Math{value: "E=mc^2".to_string(), position: None}))]))]
    #[case::to_math_inline(vec![RuntimeValue::String("E=mc^2".to_string())],
        vec![
              ast_call("to_math_inline", SmallVec::new()),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::MathInline(mq_markdown::MathInline{value: "E=mc^2".into(), position: None}))]))]
    #[case::to_md_text(vec![RuntimeValue::String("This is a text".to_string())],
        vec![
              ast_call("to_md_text", SmallVec::new()),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "This is a text".to_string(), position: None}))]))]
    #[case::to_strong(vec![RuntimeValue::String("Bold text".to_string())],
        vec![
              ast_call("to_strong", SmallVec::new()),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Strong(mq_markdown::Strong{values: vec!["Bold text".to_string().into()], position: None}))]))]
    #[case::to_strong(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "Bold text".to_string(), position: None}))],
        vec![
              ast_call("to_strong", SmallVec::new()),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Strong(mq_markdown::Strong{values: vec![mq_markdown::Node::Text(mq_markdown::Text{value: "Bold text".to_string(), position: None})], position: None}))]))]
    #[case::to_em(vec![RuntimeValue::String("Italic text".to_string())],
        vec![
              ast_call("to_em", SmallVec::new()),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Emphasis(mq_markdown::Emphasis{values: vec!["Italic text".to_string().into()], position: None}))]))]
    #[case::to_em(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "Italic text".to_string(), position: None}))],
        vec![
              ast_call("to_em", SmallVec::new()),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Emphasis(mq_markdown::Emphasis{values: vec![mq_markdown::Node::Text(mq_markdown::Text{value: "Italic text".to_string(), position: None})], position: None}))]))]
    #[case::to_blockquote(vec![RuntimeValue::String("Quoted text".to_string())],
        vec![
              ast_call("to_blockquote", SmallVec::new()),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Blockquote(mq_markdown::Blockquote{values: vec!["Quoted text".to_string().into()], position: None}))]))]
    #[case::to_delete(vec![RuntimeValue::String("Deleted text".to_string())],
        vec![
              ast_call("to_delete", SmallVec::new()),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Delete(mq_markdown::Delete{values: vec!["Deleted text".to_string().into()], position: None}))]))]
    #[case::to_callout(vec![RuntimeValue::String("Heads up".to_string())],
        vec![
              ast_call("to_callout", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("note".to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::String("Title".to_string()))),
              ]),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Callout(mq_markdown::Callout{kind: "NOTE".to_string(), title: Some("Title".to_string()), values: vec!["Heads up".to_string().into()], position: None}))]))]
    #[case::to_image(vec![RuntimeValue::String("Image Alt".to_string())],
        vec![
              ast_call("to_image", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("https://example.com/image.png".to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::String("Image Alt".to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::String("Image Title".to_string()))),
              ]),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Image(mq_markdown::Image{
            url: "https://example.com/image.png".to_string(),
            alt: "Image Alt".to_string(),
            title: Some("Image Title".to_string()),
            position: None
        }))]))]
    #[case::to_link(vec![RuntimeValue::String("Link Text".to_string())],
        vec![
              ast_call("to_link", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("https://example.com".to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::String("Link Value".to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::String("Link Title".to_string()))),
              ]),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Link(mq_markdown::Link{
            url: mq_markdown::Url::new("https://example.com".to_string()),
            title: Some(mq_markdown::Title::new("Link Title".to_string())),
            values: vec!["Link Value".to_string().into()],
            position: None
        }))]))]
    #[case::to_link(vec![RuntimeValue::Number(123.into())],
        vec![
              ast_call("to_link", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("Link Title".to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::String("Link Value".to_string()))),
              ]),
        ],
        Ok(vec![RuntimeValue::NONE]))]
    #[case::to_hr(vec![RuntimeValue::String("".to_owned())],
        vec![
              ast_call("to_hr", SmallVec::new()),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::HorizontalRule(mq_markdown::HorizontalRule{position: None}))]))]
    #[case::to_md_list(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "list".to_string(), position: None}))],
        vec![
              ast_call("to_md_list",
                       smallvec![
                             ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                       ]),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::List(
            mq_markdown::List{values: vec!["list".to_string().into()], ordered: false, index: 0, level: 1_u8, checked: None, position: None}))]))]
    #[case::to_md_list(vec![RuntimeValue::String("list".to_string())],
        vec![
              ast_call("to_md_list",
                       smallvec![
                             ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                       ]),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::List(
            mq_markdown::List{values: vec!["list".to_string().into()], ordered: false, index: 0, level: 1_u8, checked: None, position: None}))]))]
    #[case::to_md_fragment(vec![RuntimeValue::Array(vec![
            RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "first".to_string(), position: None})),
            RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "second".to_string(), position: None})),
        ])],
        vec![
              ast_call("to_md_fragment", SmallVec::new()),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Fragment(mq_markdown::Fragment{values: vec![
            mq_markdown::Node::Text(mq_markdown::Text{value: "first".to_string(), position: None}),
            mq_markdown::Node::Text(mq_markdown::Text{value: "second".to_string(), position: None}),
        ]}))]))]
    #[case::to_md_fragment_flattens_nested_arrays(vec![RuntimeValue::Array(vec![
            RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "first".to_string(), position: None})),
            RuntimeValue::Array(vec![
                RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "second".to_string(), position: None})),
                RuntimeValue::Array(vec![
                    RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "third".to_string(), position: None})),
                ]),
            ]),
        ])],
        vec![
              ast_call("to_md_fragment", SmallVec::new()),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Fragment(mq_markdown::Fragment{values: vec![
            mq_markdown::Node::Text(mq_markdown::Text{value: "first".to_string(), position: None}),
            mq_markdown::Node::Text(mq_markdown::Text{value: "second".to_string(), position: None}),
            mq_markdown::Node::Text(mq_markdown::Text{value: "third".to_string(), position: None}),
        ]}))]))]
    #[case::to_md_table_align(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("left".to_string()),
            RuntimeValue::String("right".to_string()),
            RuntimeValue::String("center".to_string()),
            RuntimeValue::String("none".to_string()),
        ])],
        vec![
              ast_call("to_md_table_align", SmallVec::new()),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::TableAlign(mq_markdown::TableAlign{
            align: vec![
                mq_markdown::TableAlignKind::Left,
                mq_markdown::TableAlignKind::Right,
                mq_markdown::TableAlignKind::Center,
                mq_markdown::TableAlignKind::None,
            ],
            position: None,
        }))]))]
    #[case::set_check(vec![RuntimeValue::new_markdown(mq_markdown::Node::List(mq_markdown::List{values: vec!["Checked Item".to_string().into()], ordered: false, level: 0, index: 0, checked: None, position: None}))],
        vec![
              ast_call("set_check", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
              ]),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::List(mq_markdown::List{values: vec!["Checked Item".to_string().into()], ordered: false, level: 0, index: 0, checked: Some(true), position: None}))]))]
    #[case::set_check(vec![RuntimeValue::new_markdown(mq_markdown::Node::List(mq_markdown::List{values: vec!["Unchecked Item".to_string().into()], ordered: false, level: 0, index: 0, checked: None, position: None}))],
        vec![
              ast_call("set_check", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
              ]),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::List(mq_markdown::List{values: vec!["Unchecked Item".to_string().into()], ordered: false, level: 0, index: 0, checked: Some(false), position: None}))]))]
    #[case::compact(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("test1".to_string()),
            RuntimeValue::NONE,
            RuntimeValue::String("test2".to_string()),
            RuntimeValue::NONE,
            RuntimeValue::String("test3".to_string()),
        ])],
        vec![
            ast_call("compact", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("test1".to_string()),
            RuntimeValue::String("test2".to_string()),
            RuntimeValue::String("test3".to_string()),
        ])]))]
    #[case::compact(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("test1".to_string()),
            RuntimeValue::NONE,
            RuntimeValue::String("test2".to_string()),
            RuntimeValue::NONE,
            RuntimeValue::String("test3".to_string()),
        ])],
        vec![
            ast_call("compact", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("test1".to_string()),
            RuntimeValue::String("test2".to_string()),
            RuntimeValue::String("test3".to_string()),
        ])]))]
    #[case::compact_empty(vec![RuntimeValue::Array(vec![
            RuntimeValue::NONE,
            RuntimeValue::NONE,
        ])],
        vec![
            ast_call("compact", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Array(Vec::new())]))]
    #[case::compact_no_none(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("test1".to_string()),
            RuntimeValue::String("test2".to_string()),
        ])],
        vec![
            ast_call("compact", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("test1".to_string()),
            RuntimeValue::String("test2".to_string()),
        ])]))]
    #[case::text_selector(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "test".to_string(), position: None}))],
        vec![
            ast_node(ast::Expr::Selector(Selector::Text)),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "test".to_string(), position: None}))]))]
    #[case::text_selector_heading(vec![RuntimeValue::new_markdown(mq_markdown::Node::Heading(mq_markdown::Heading{depth: 1, values: vec!["Heading 1".to_string().into()], position: None}))],
        vec![
            ast_node(ast::Expr::Selector(Selector::Text)),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Fragment(mq_markdown::Fragment { values: vec!["Heading 1".to_string().into()] }))]))]
    // SelectorCall: .h(1) matches h1
    #[case::selector_call_heading_exact_match(
        vec![RuntimeValue::new_markdown(mq_markdown::Node::Heading(mq_markdown::Heading{depth: 1, values: vec!["H1".to_string().into()], position: None}))],
        vec![ast_node(ast::Expr::SelectorCall(Selector::Heading(None), smallvec![
            ast_node(ast::Expr::Literal(ast::Literal::Number(crate::number::Number::new(1.0)))),
        ]))],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Heading(mq_markdown::Heading{depth: 1, values: vec!["H1".to_string().into()], position: None}))]))]
    // SelectorCall: .h(2) does not match a non-markdown value
    #[case::selector_call_heading_no_match(
        vec![RuntimeValue::NONE],
        vec![ast_node(ast::Expr::SelectorCall(Selector::Heading(None), smallvec![
            ast_node(ast::Expr::Literal(ast::Literal::Number(crate::number::Number::new(2.0)))),
        ]))],
        Ok(vec![RuntimeValue::NONE]))]
    // SelectorCall: .h(1, 2) matches h2
    #[case::selector_call_heading_multi_arg(
        vec![RuntimeValue::new_markdown(mq_markdown::Node::Heading(mq_markdown::Heading{depth: 2, values: vec!["H2".to_string().into()], position: None}))],
        vec![ast_node(ast::Expr::SelectorCall(Selector::Heading(None), smallvec![
            ast_node(ast::Expr::Literal(ast::Literal::Number(crate::number::Number::new(1.0)))),
            ast_node(ast::Expr::Literal(ast::Literal::Number(crate::number::Number::new(2.0)))),
        ]))],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Heading(mq_markdown::Heading{depth: 2, values: vec!["H2".to_string().into()], position: None}))]))]
    // SelectorCall: .code("rust") matches rust code block
    #[case::selector_call_code_lang_match(
        vec![RuntimeValue::new_markdown(mq_markdown::Node::Code(mq_markdown::Code{lang: Some("rust".to_string()), meta: None, value: "fn main() {}".to_string(), fence: true, position: None}))],
        vec![ast_node(ast::Expr::SelectorCall(Selector::Code, smallvec![
            ast_node(ast::Expr::Literal(ast::Literal::String("rust".to_string()))),
        ]))],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Code(mq_markdown::Code{lang: Some("rust".to_string()), meta: None, value: "fn main() {}".to_string(), fence: true, position: None}))]))]
    // SelectorCall: .code("python") does not match a non-markdown value
    #[case::selector_call_code_lang_no_match(
        vec![RuntimeValue::NONE],
        vec![ast_node(ast::Expr::SelectorCall(Selector::Code, smallvec![
            ast_node(ast::Expr::Literal(ast::Literal::String("python".to_string()))),
        ]))],
        Ok(vec![RuntimeValue::NONE]))]
    #[case::to_md_table_row(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("Cell 1".to_string()),
            RuntimeValue::String("Cell 2".to_string()),
            RuntimeValue::String("Cell 3".to_string()),
        ])],
        vec![
            ast_call("to_md_table_row", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::TableRow(mq_markdown::TableRow{
            values: vec![
                mq_markdown::Node::TableCell(mq_markdown::TableCell{
                    row: 0,
                    column: 0,
                    values: vec!["Cell 1".to_string().into()],
                    position: None
                }),
                mq_markdown::Node::TableCell(mq_markdown::TableCell{
                    row: 0,
                    column: 1,
                    values: vec!["Cell 2".to_string().into()],
                    position: None
                }),
                mq_markdown::Node::TableCell(mq_markdown::TableCell{
                    row: 0,
                    column: 2,
                    values: vec!["Cell 3".to_string().into()],
                    position: None
                }),
            ],
            position: None
        }))]))]
    #[case::to_md_table_row(vec![RuntimeValue::String("Cell 4".to_string())],
        vec![
            ast_call("to_md_table_row", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("Cell 1".to_string()))),
                ast_node(ast::Expr::Literal(ast::Literal::String("Cell 2".to_string()))),
            ])
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::TableRow(mq_markdown::TableRow{
            values: vec![
                mq_markdown::Node::TableCell(mq_markdown::TableCell{
                    row: 0,
                    column: 0,
                    values: vec!["Cell 1".to_string().into()],
                    position: None
                }),
                mq_markdown::Node::TableCell(mq_markdown::TableCell{
                    row: 0,
                    column: 1,
                    values: vec!["Cell 2".to_string().into()],
                    position: None
                }),
            ],
            position: None
        }))]))]
    #[case::get_title(vec![RuntimeValue::new_markdown(mq_markdown::Node::Link(mq_markdown::Link{url: mq_markdown::Url::new("https://example.com".to_string()), title: Some(mq_markdown::Title::new("title".to_string())), values: vec!["Link".to_string().into()], position: None}))],
        vec![
             ast_call("get_title", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "title".to_string(), position: None}))]))]
    #[case::get_title(vec![RuntimeValue::new_markdown(mq_markdown::Node::Link(mq_markdown::Link{url: mq_markdown::Url::new("https://example.com".to_string()), title: None, values: vec!["Link".to_string().into()], position: None}))],
        vec![
             ast_call("get_title", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Empty)]))]
    #[case::get_title(vec![RuntimeValue::new_markdown(mq_markdown::Node::Image(mq_markdown::Image{url: "https://example.com/image.png".to_string(), alt: "Image Alt".to_string(), title: Some("Image Title".to_string()), position: None}))],
            vec![
                 ast_call("get_title", SmallVec::new())
            ],
            Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "Image Title".to_string(), position: None}))]))]
    #[case::get_title(vec![RuntimeValue::new_markdown(mq_markdown::Node::Image(mq_markdown::Image{url: "https://example.com/image.png".to_string(), alt: "Image Alt".to_string(), title: None, position: None}))],
        vec![
             ast_call("get_title", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Empty)]))]
    #[case::get_string(vec![RuntimeValue::String("test1".to_string())],
        vec![
            ast_call("get", smallvec![ast_node(ast::Expr::Literal(ast::Literal::Number(0.into())))])
        ],
        Ok(vec![RuntimeValue::String("t".to_string())]))]
    #[case::get_string(vec![RuntimeValue::String("test1".to_string())],
        vec![
            ast_call("get", smallvec![ast_node(ast::Expr::Literal(ast::Literal::Number(5.into())))])
        ],
        Ok(vec![RuntimeValue::NONE]))]
    #[case::get_string(vec![RuntimeValue::String("test1".to_string())],
        vec![
            ast_call("get", smallvec![ast_node(ast::Expr::Literal(ast::Literal::Number(Number::new(-1.0))))])
        ],
        Ok(vec![RuntimeValue::String("1".to_string())]))]
    #[case::get_array(vec![RuntimeValue::Array(vec!["1".to_string().into()])],
        vec![
            ast_call("get", smallvec![ast_node(ast::Expr::Literal(ast::Literal::Number(2.into())))])
        ],
        Ok(vec![RuntimeValue::NONE]))]
    #[case::get_array(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("test1".to_string()),
            RuntimeValue::String("test2".to_string()),
        ])],
        vec![
            ast_call("get", smallvec![ast_node(ast::Expr::Literal(ast::Literal::Number(Number::new(-1.0))))])
        ],
        Ok(vec![RuntimeValue::String("test2".to_string())]))]
    #[case::get(vec![RuntimeValue::TRUE],
        vec![
            ast_call("get", smallvec![ast_node(ast::Expr::Literal(ast::Literal::Number(0.into())))])
        ],
        Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                     name: "get".to_string(),
                                                     args: vec!["bool".into(), "number".into()]})))]
    #[case::to_date(vec![RuntimeValue::Number(1609459200_i64.into())],
        vec![
            ast_call("to_date", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("%Y-%m-%d".to_string())))
            ])
        ],
        Ok(vec![RuntimeValue::String("2021-01-01".to_string())]))]
    #[case::to_date(vec![RuntimeValue::Number(1609459200_i64.into())],
        vec![
            ast_call("to_date", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("%Y/%m/%d %H:%M:%S".to_string())))
            ])
        ],
        Ok(vec![RuntimeValue::String("2021/01/01 00:00:00".to_string())]))]
    #[case::to_date(vec![RuntimeValue::Number(1609488000_i64.into())],
        vec![
            ast_call("to_date", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("%d %b %Y %H:%M".to_string())))
            ])
        ],
        Ok(vec![RuntimeValue::String("01 Jan 2021 08:00".to_string())]))]
    #[case::to_date(vec![RuntimeValue::String("test".to_string())],
        vec![
            ast_call("to_date", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("%Y-%m-%d".to_string())))
            ])
        ],
        Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                     name: "to_date".to_string(),
                                                     args: vec!["string".into(), "string".into()]})))]
    #[case::to_string_array(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("test".to_string()),
            RuntimeValue::Number(1.into()),
            RuntimeValue::Number(2.into()),
            RuntimeValue::Boolean(false),
        ])],
        vec![
            ast_call("to_string", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::String(r#"["test", 1, 2, false]"#.to_string())]))]
    #[case::to_string_empty_array(vec![RuntimeValue::Array(Vec::new())],
        vec![
            ast_call("to_string", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::String("[]".to_string())]))]
    #[case::to_text(vec![RuntimeValue::String("test".to_string())],
        vec![
             ast_call("to_text", SmallVec::new())
        ],
        Ok(vec!["test".to_string().into()]))]
    #[case::to_text(vec![RuntimeValue::Number(42.into())],
        vec![
             ast_call("to_text", SmallVec::new())
        ],
        Ok(vec!["42".to_string().into()]))]
    #[case::to_text(vec![RuntimeValue::Boolean(true)],
        vec![
             ast_call("to_text", SmallVec::new())
        ],
        Ok(vec!["true".to_string().into()]))]
    #[case::to_text(vec![RuntimeValue::new_markdown(mq_markdown::Node::Heading(mq_markdown::Heading{depth: 1, values: vec!["Heading".to_string().into()], position: None}))],
        vec![
             ast_call("to_text", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "Heading".to_string(), position: None}))]))]
    #[case::to_text(vec![RuntimeValue::String("Original".to_string())],
        vec![
             ast_call("to_text",
              smallvec![ast_node(ast::Expr::Literal(ast::Literal::String("Override".to_string())))])
        ],
        Ok(vec!["Override".to_string().into()]))]
    #[case::to_text(vec![RuntimeValue::Array(vec!["val1".to_string().into(), "val2".to_string().into()])],
        vec![
             ast_call("to_text", SmallVec::new())
        ],
        Ok(vec!["val1,val2".to_string().into()]))]
    #[case::url_encode(vec![RuntimeValue::String("test string with spaces".to_string())],
        vec![
             ast_call("url_encode", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::String("test%20string%20with%20spaces".to_string())]))]
    #[case::url_encode(vec![RuntimeValue::String("test!@#$%^&*()".to_string())],
        vec![
             ast_call("url_encode", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::String("test%21%40%23%24%25%5E%26%2A%28%29".to_string())]))]
    #[case::url_encode(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "test string".to_string(), position: None}))],
        vec![
             ast_call("url_encode", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "test%20string".to_string(), position: None}))]))]
    #[case::url_encode(vec![RuntimeValue::Number(1.into())],
        vec![
             ast_call("url_encode", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::String("1".to_string())]))]
    #[case::update(vec!["".to_string().into()],
        vec![
             ast_call("update", smallvec![
              ast_node(ast::Expr::Literal(ast::Literal::Number(0.into()))),
              ast_node(ast::Expr::Literal(ast::Literal::String("updated".to_string()))),
             ])
        ],
        Ok(vec![RuntimeValue::String("updated".to_string())]))]
    #[case::update(vec!["".to_string().into()],
        vec![
             ast_call("update", smallvec![
                ast_call("to_strong", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("text1".to_string()))),
                ]),
                ast_call("to_strong", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("text2".to_string()))),
                ])
             ])
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Strong(mq_markdown::Strong{values: vec![mq_markdown::Node::Text(mq_markdown::Text{value: "text2".to_string(), position: None})], position: None}))]))]
    #[case::update(vec!["".to_string().into()],
        vec![
             ast_call("update", smallvec![
                ast_call("to_strong", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("text1".to_string()))),
                ]),
                ast_node(ast::Expr::Literal(ast::Literal::String("text2".to_string()))),
             ])
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Strong(mq_markdown::Strong{values: vec![mq_markdown::Node::Text(mq_markdown::Text{value: "text2".to_string(), position: None})], position: None}))]))]
    #[case::sort_string_array(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("c".to_string()),
            RuntimeValue::String("a".to_string()),
            RuntimeValue::String("b".to_string()),
        ])],
        vec![
            ast_call("sort", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("a".to_string()),
            RuntimeValue::String("b".to_string()),
            RuntimeValue::String("c".to_string()),
        ])]))]
    #[case::sort_number_array(vec![RuntimeValue::Array(vec![
            RuntimeValue::Number(3.into()),
            RuntimeValue::Number(1.into()),
            RuntimeValue::Number(2.into()),
        ])],
        vec![
            ast_call("sort", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Array(vec![
            RuntimeValue::Number(1.into()),
            RuntimeValue::Number(2.into()),
            RuntimeValue::Number(3.into()),
        ])]))]
    #[case::sort_empty_array(vec![RuntimeValue::Array(Vec::new())],
        vec![
            ast_call("sort", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Array(Vec::new())]))]
    #[case::sort_error(vec![RuntimeValue::Number(1.into())],
        vec![
            ast_call("sort", SmallVec::new())
        ],
        Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                     name: "sort".to_string(),
                                                     args: vec!["number".into()]})))]
    #[case::uniq_string_array(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("a".to_string()),
            RuntimeValue::String("b".to_string()),
            RuntimeValue::String("a".to_string()),
            RuntimeValue::String("c".to_string()),
            RuntimeValue::String("b".to_string()),
        ])],
        vec![
            ast_call("uniq", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("a".to_string()),
            RuntimeValue::String("b".to_string()),
            RuntimeValue::String("c".to_string()),
        ])]))]
    #[case::uniq_number_array(vec![RuntimeValue::Array(vec![
            RuntimeValue::Number(1.into()),
            RuntimeValue::Number(2.into()),
            RuntimeValue::Number(1.into()),
            RuntimeValue::Number(3.into()),
            RuntimeValue::Number(2.into()),
        ])],
        vec![
            ast_call("uniq", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Array(vec![
            RuntimeValue::Number(1.into()),
            RuntimeValue::Number(2.into()),
            RuntimeValue::Number(3.into()),
        ])]))]
    #[case::uniq_error(vec![RuntimeValue::Number(1.into())],
        vec![
            ast_call("uniq", SmallVec::new())
        ],
        Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                     name: "uniq".to_string(),
                                                     args: vec!["number".into()]})))]
    #[case::to_html(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "test".to_string(), position: None}))],
        vec![
             ast_call("to_html", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "<p>test</p>".to_string(), position: None}))]))]
    #[case::to_html(vec![RuntimeValue::new_markdown(mq_markdown::Node::Heading(mq_markdown::Heading{depth: 1, values: vec!["Heading 1".to_string().into()], position: None}))],
        vec![
             ast_call("to_html", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "<h1>Heading 1</h1>".to_string(), position: None}))]))]
    #[case::to_html(vec![RuntimeValue::new_markdown(mq_markdown::Node::Strong(mq_markdown::Strong{values: vec!["Bold".to_string().into()], position: None}))],
        vec![
             ast_call("to_html", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "<p><strong>Bold</strong></p>".to_string(), position: None}))]))]
    #[case::to_html(vec![RuntimeValue::new_markdown(mq_markdown::Node::Emphasis(mq_markdown::Emphasis{values: vec!["Italic".to_string().into()], position: None}))],
        vec![
             ast_call("to_html", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "<p><em>Italic</em></p>".to_string(), position: None}))]))]
    #[case::to_html(vec![RuntimeValue::new_markdown(mq_markdown::Node::Link(mq_markdown::Link{url: mq_markdown::Url::new("https://example.com".to_string()), title: Some(mq_markdown::Title::new("Link Title".to_string())), values: vec!["Link Title".to_string().into()], position: None}))],
        vec![
             ast_call("to_html", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "<p><a href=\"https://example.com\" title=\"Link Title\">Link Title</a></p>".to_string(), position: None}))]))]
    #[case::to_html(vec![RuntimeValue::new_markdown(mq_markdown::Node::Code(mq_markdown::Code{lang: Some("rust".to_string()), value: "println!(\"Hello\");".to_string(), fence: true, meta: None, position: None}))],
        vec![
             ast_call("to_html", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "<pre><code class=\"language-rust\">println!(&quot;Hello&quot;);\n</code></pre>".to_string(), position: None}))]))]
    #[case::to_html(vec![RuntimeValue::String("Plain text".to_string())],
        vec![
             ast_call("to_html", SmallVec::new())
        ],
        Ok(vec!["<p>Plain text</p>".to_string().into()]))]
    #[case::to_html(vec![RuntimeValue::Number(1.into())],
        vec![
             ast_call("to_html", SmallVec::new())
        ],
        Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                     name: "to_html".to_string(),
                                                     args: vec!["number".into()]})))]
    #[case::repeat_string(vec![RuntimeValue::String("abc".to_string())],
        vec![
            ast_call("repeat", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Number(3.into())))
            ])
        ],
        Ok(vec![RuntimeValue::String("abcabcabc".to_string())]))]
    #[case::repeat_markdown(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "abc".to_string(), position: None}))],
        vec![
            ast_call("repeat", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Number(3.into())))
            ])
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "abcabcabc".to_string(), position: None}))]))]
    #[case::repeat_string_zero(vec![RuntimeValue::String("abc".to_string())],
        vec![
            ast_call("repeat", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Number(0.into())))
            ])
        ],
        Ok(vec![RuntimeValue::String("".to_string())]))]
    #[case::repeat_array(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("a".to_string()),
            RuntimeValue::String("b".to_string()),
        ])],
        vec![
            ast_call("repeat", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Number(2.into())))
            ])
        ],
        Ok(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("a".to_string()),
            RuntimeValue::String("b".to_string()),
            RuntimeValue::String("a".to_string()),
            RuntimeValue::String("b".to_string()),
        ])]))]
    #[case::repeat_array_zero(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("a".to_string()),
            RuntimeValue::String("b".to_string()),
        ])],
        vec![
            ast_call("repeat", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Number(0.into())))
            ])
        ],
        Ok(vec![RuntimeValue::Array(Vec::new())]))]
    #[case::repeat_invalid_count(vec![RuntimeValue::String("abc".to_string())],
        vec![
            ast_call("repeat", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Number((-1).into())))
            ])
        ],
        Ok(vec!["".to_string().into()]))]
    #[case::repeat_invalid(vec![RuntimeValue::Number(42.into())],
        vec![
            ast_call("repeat", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("".into())))
            ])
        ],
        Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
           name: "repeat".to_string(),
           args: vec!["number".into(), "string".into()]})))]
    #[case::debug(vec![RuntimeValue::String("test".to_string())],
        vec![
            ast_call("stderr", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::String("test".to_string())]))]
    #[case::from_date(vec![RuntimeValue::String("2025-03-15T20:00:00+09:00".to_string())],
        vec![
            ast_call("from_date", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Number(1742036400_i64.into())]))]
    #[case::from_date_invalid_format(vec![RuntimeValue::String("2021-01-01".to_string())],
        vec![
            ast_call("from_date", SmallVec::new())
        ],
        Err(InnerError::Runtime(RuntimeError::Runtime(Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()}, "premature end of input".to_string()))))]
    #[case::from_date(vec![RuntimeValue::Number(1.into())],
        vec![
            ast_call("from_date", SmallVec::new())
        ],
        Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                     name: "from_date".to_string(),
                                                     args: vec!["number".into()]})))]
    #[case::to_code_inline(vec![RuntimeValue::String("test1".to_string())],
        vec![
              ast_call("to_code_inline", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("elm".into()))),
              ]),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::CodeInline(mq_markdown::CodeInline{value: "elm".into(), position: None}))]))]
    #[case::to_md_name(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "name".to_string(), position: None}))],
        vec![
              ast_call("to_md_name", SmallVec::new()),
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "text".to_string(), position: None}))]))]
    #[case::to_md_name(vec![RuntimeValue::Number(123.into())],
        vec![
              ast_call("to_md_name", SmallVec::new()),
        ],
        Ok(vec![RuntimeValue::NONE]))]
    #[case::set_ref_markdown_definition(vec![RuntimeValue::new_markdown(mq_markdown::Node::Definition(mq_markdown::Definition{ident: "ident".into(), url: mq_markdown::Url::new("url".to_string()), title: None, label: None, position: None}))],
            vec![
                 ast_call("set_ref", smallvec![
                     ast_node(ast::Expr::Literal(ast::Literal::String("definition-ref".to_string())))
                 ])
            ],
            Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Definition(mq_markdown::Definition{
                ident: "ident".to_string(),
                label: Some("definition-ref".to_string()),
                url: mq_markdown::Url::new("url".to_string()),
                title: None,
                position: None
            }))]))]
    #[case::set_ref_markdown_link_ref(vec![RuntimeValue::new_markdown(mq_markdown::Node::LinkRef(mq_markdown::LinkRef{ident: "ident".into(), label: None, values: Vec::new(), position: None}))],
            vec![
                 ast_call("set_ref", smallvec![
                     ast_node(ast::Expr::Literal(ast::Literal::String("link-ref".to_string())))
                 ])
            ],
            Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::LinkRef(mq_markdown::LinkRef{
                ident: "ident".to_string(),
                label: Some("link-ref".to_string()),
                values: Vec::new(),
                position: None
            }))]))]
    #[case::set_ref_markdown_link_ref(vec![RuntimeValue::new_markdown(mq_markdown::Node::LinkRef(mq_markdown::LinkRef{ident: "ident".into(), label: None, values: Vec::new(), position: None}))],
            vec![
                 ast_call("set_ref", smallvec![
                     ast_node(ast::Expr::Literal(ast::Literal::String("ident".to_string())))
                 ])
            ],
            Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::LinkRef(mq_markdown::LinkRef{
                ident: "ident".to_string(),
                label: None,
                values: Vec::new(),
                position: None
            }))]))]
    #[case::set_ref_markdown_image_ref(vec![RuntimeValue::new_markdown(mq_markdown::Node::ImageRef(mq_markdown::ImageRef{alt: "Image Alt".to_string(), ident: "ident".into(), label: None, position: None}))],
            vec![
                 ast_call("set_ref", smallvec![
                     ast_node(ast::Expr::Literal(ast::Literal::String("image-ref".to_string())))
                 ])
            ],
            Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::ImageRef(mq_markdown::ImageRef{
                ident: "ident".to_string(),
                alt: "Image Alt".to_string(),
                label: Some("image-ref".to_string()),
                position: None
            }))]))]
    #[case::set_ref_markdown_image_ref(vec![RuntimeValue::new_markdown(mq_markdown::Node::ImageRef(mq_markdown::ImageRef{alt: "Image Alt".to_string(), ident: "ident".into(), label: None, position: None}))],
            vec![
                 ast_call("set_ref", smallvec![
                     ast_node(ast::Expr::Literal(ast::Literal::String("ident".to_string())))
                 ])
            ],
            Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::ImageRef(mq_markdown::ImageRef{
                ident: "ident".to_string(),
                alt: "Image Alt".to_string(),
                label: None,
                position: None
            }))]))]
    #[case::set_ref_markdown_footnote_ref(vec![RuntimeValue::new_markdown(mq_markdown::Node::FootnoteRef(mq_markdown::FootnoteRef{ident: "ident".into(), label: None, position: None}))],
            vec![
                 ast_call("set_ref", smallvec![
                     ast_node(ast::Expr::Literal(ast::Literal::String("footnote-ref".to_string())))
                 ])
            ],
            Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::FootnoteRef(mq_markdown::FootnoteRef{
                ident: "ident".to_string(),
                label: Some("footnote-ref".to_string()),
                position: None
            }))]))]
    #[case::set_ref_markdown_footnote(vec![RuntimeValue::new_markdown(mq_markdown::Node::Footnote(mq_markdown::Footnote{ident: "ident".into(), values: Vec::new(), position: None}))],
            vec![
                 ast_call("set_ref", smallvec![
                     ast_node(ast::Expr::Literal(ast::Literal::String("footnote".to_string())))
                 ])
            ],
            Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Footnote(mq_markdown::Footnote{
               ident: "footnote".to_string(),
                values: Vec::new(),
                position: None
            }))]))]
    #[case::set_ref_not_link_or_image(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "Simple text".to_string(), position: None}))],
            vec![
                 ast_call("set_ref", smallvec![
                     ast_node(ast::Expr::Literal(ast::Literal::String("text-ref".to_string())))
                 ])
            ],
            Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "Simple text".to_string(), position: None}))]))]
    #[case::set_ref_plain_string(vec![RuntimeValue::String("Not a markdown".to_string())],
            vec![
                 ast_call("set_ref", smallvec![
                     ast_node(ast::Expr::Literal(ast::Literal::String("string-ref".to_string())))
                 ])
            ],
            Ok(vec![RuntimeValue::String("Not a markdown".to_string())]))]
    #[case::set_ref_none(vec![RuntimeValue::NONE],
            vec![
                 ast_call("set_ref", smallvec![
                     ast_node(ast::Expr::Literal(ast::Literal::String("none-ref".to_string())))
                 ])
            ],
            Ok(vec![RuntimeValue::NONE]))]
    #[case::set_ref_with_empty_id(vec![RuntimeValue::new_markdown(mq_markdown::Node::Link(mq_markdown::Link{url: mq_markdown::Url::new("https://example.com".to_string()), title: Some(mq_markdown::Title::new("title".to_string())), values: vec!["Link".to_string().into()], position: None}))],
        vec![
             ast_call("set_ref", smallvec![
                 ast_node(ast::Expr::Literal(ast::Literal::String("".to_string())))
             ])
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Link(mq_markdown::Link{url: mq_markdown::Url::new("https://example.com".to_string()), title: Some(mq_markdown::Title::new("title".to_string())), values: vec!["Link".to_string().into()], position: None}))]))]
    #[case::get_url_link(vec![RuntimeValue::new_markdown(mq_markdown::Node::Definition(mq_markdown::Definition{url: mq_markdown::Url::new("https://example.com".to_string()), ident: "ident".to_string(), label: None, title: Some(mq_markdown::Title::new("title".to_string())), position: None}))],
        vec![
             ast_call("get_url", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "https://example.com".to_string(), position: None}))]))]
    #[case::get_url_link(vec![RuntimeValue::new_markdown(mq_markdown::Node::Link(mq_markdown::Link{url: mq_markdown::Url::new("https://example.com".to_string()), title: Some(mq_markdown::Title::new("title".to_string())), values: vec!["Link".to_string().into()], position: None}))],
        vec![
             ast_call("get_url", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "https://example.com".to_string(), position: None}))]))]
    #[case::get_url_image(vec![RuntimeValue::new_markdown(mq_markdown::Node::Image(mq_markdown::Image{url: "https://example.com/image.png".to_string(), alt: "Image Alt".to_string(), title: Some("Image Title".to_string()), position: None}))],
        vec![
             ast_call("get_url", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "https://example.com/image.png".to_string(), position: None}))]))]
    #[case::get_url_not_link_or_image(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "Simple text".to_string(), position: None}))],
        vec![
             ast_call("get_url", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Empty)]))]
    #[case::get_url_string(vec![RuntimeValue::String("Not a markdown".to_string())],
        vec![
             ast_call("get_url", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::NONE]))]
    #[case::flatten_array_of_arrays(vec![RuntimeValue::Array(vec![
                RuntimeValue::Array(vec![RuntimeValue::String("a".to_string()), RuntimeValue::String("b".to_string())]),
                RuntimeValue::Array(vec![RuntimeValue::String("c".to_string()), RuntimeValue::String("d".to_string())])
            ])],
            vec![
                ast_call("flatten", SmallVec::new())
            ],
            Ok(vec![RuntimeValue::Array(vec![
                RuntimeValue::String("a".to_string()),
                RuntimeValue::String("b".to_string()),
                RuntimeValue::String("c".to_string()),
                RuntimeValue::String("d".to_string())
            ])]))]
    #[case::flatten_array_with_nested_arrays(vec![RuntimeValue::Array(vec![
                RuntimeValue::String("a".to_string()),
                RuntimeValue::Array(vec![RuntimeValue::String("b".to_string()), RuntimeValue::String("c".to_string())]),
                RuntimeValue::String("d".to_string())
            ])],
            vec![
                ast_call("flatten", SmallVec::new())
            ],
            Ok(vec![RuntimeValue::Array(vec![
                RuntimeValue::String("a".to_string()),
                RuntimeValue::String("b".to_string()),
                RuntimeValue::String("c".to_string()),
                RuntimeValue::String("d".to_string())
            ])]))]
    #[case::flatten_deeply_nested_arrays(vec![RuntimeValue::Array(vec![
                RuntimeValue::Array(vec![
                    RuntimeValue::Array(vec![RuntimeValue::String("a".to_string()), RuntimeValue::String("b".to_string())]),
                    RuntimeValue::String("c".to_string())
                ]),
                RuntimeValue::String("d".to_string())
            ])],
            vec![
                ast_call("flatten", SmallVec::new())
            ],
            Ok(vec![RuntimeValue::Array(vec![
                RuntimeValue::String("a".to_string()),
                RuntimeValue::String("b".to_string()),
                RuntimeValue::String("c".to_string()),
                RuntimeValue::String("d".to_string())
            ])]))]
    #[case::flatten_empty_array(vec![RuntimeValue::Array(Vec::new())],
            vec![
                ast_call("flatten", SmallVec::new())
            ],
            Ok(vec![RuntimeValue::Array(Vec::new())]))]
    #[case::flatten_array_with_empty_arrays(vec![RuntimeValue::Array(vec![
                RuntimeValue::Array(Vec::new()),
                RuntimeValue::Array(Vec::new())
            ])],
            vec![
                ast_call("flatten", SmallVec::new())
            ],
            Ok(vec![RuntimeValue::Array(Vec::new())]))]
    #[case::flatten_mixed_type_arrays(vec![RuntimeValue::Array(vec![
                RuntimeValue::Array(vec![RuntimeValue::String("a".to_string()), RuntimeValue::Number(1.into())]),
                RuntimeValue::Array(vec![RuntimeValue::Boolean(true), RuntimeValue::String("b".to_string())])
            ])],
            vec![
                ast_call("flatten", SmallVec::new())
            ],
            Ok(vec![RuntimeValue::Array(vec![
                RuntimeValue::String("a".to_string()),
                RuntimeValue::Number(1.into()),
                RuntimeValue::Boolean(true),
                RuntimeValue::String("b".to_string())
            ])]))]
    #[case::flatten_array_with_none_values(vec![RuntimeValue::Array(vec![
                RuntimeValue::Array(vec![RuntimeValue::String("a".to_string()), RuntimeValue::NONE]),
                RuntimeValue::Array(vec![RuntimeValue::String("b".to_string()), RuntimeValue::String("c".to_string())])
            ])],
            vec![
                ast_call("flatten", SmallVec::new())
            ],
            Ok(vec![RuntimeValue::Array(vec![
                RuntimeValue::String("a".to_string()),
                RuntimeValue::NONE,
                RuntimeValue::String("b".to_string()),
                RuntimeValue::String("c".to_string())
            ])]))]
    #[case::flatten_non_array(vec![RuntimeValue::String("test".to_string())],
            vec![
                ast_call("flatten", SmallVec::new())
            ],
            Ok(vec![RuntimeValue::String("test".to_string())]))]
    #[case::flatten_none(vec![RuntimeValue::NONE],
            vec![
                ast_call("flatten", SmallVec::new())
            ],
            Ok(vec![RuntimeValue::NONE]))]
    #[case::set_array_valid_index(vec![RuntimeValue::Array(vec![
        RuntimeValue::String("item1".to_string()),
        RuntimeValue::String("item2".to_string()),
        RuntimeValue::String("item3".to_string()),
        ])],
        vec![
        ast_call("set", smallvec![
            ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
            ast_node(ast::Expr::Literal(ast::Literal::String("updated".to_string()))),
        ])
        ],
        Ok(vec![RuntimeValue::Array(vec![
        RuntimeValue::String("item1".to_string()),
        RuntimeValue::String("updated".to_string()),
        RuntimeValue::String("item3".to_string()),
        ])]))]
    #[case::set_array_first_index(vec![RuntimeValue::Array(vec![
        RuntimeValue::String("item1".to_string()),
        RuntimeValue::String("item2".to_string()),
        RuntimeValue::String("item3".to_string()),
        ])],
        vec![
        ast_call("set", smallvec![
            ast_node(ast::Expr::Literal(ast::Literal::Number(0.into()))),
            ast_node(ast::Expr::Literal(ast::Literal::String("first".to_string()))),
        ])
        ],
        Ok(vec![RuntimeValue::Array(vec![
        RuntimeValue::String("first".to_string()),
        RuntimeValue::String("item2".to_string()),
        RuntimeValue::String("item3".to_string()),
        ])]))]
    #[case::set_array_last_index(vec![RuntimeValue::Array(vec![
        RuntimeValue::String("item1".to_string()),
        RuntimeValue::String("item2".to_string()),
        RuntimeValue::String("item3".to_string()),
        ])],
        vec![
        ast_call("set", smallvec![
            ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
            ast_node(ast::Expr::Literal(ast::Literal::String("last".to_string()))),
        ])
        ],
        Ok(vec![RuntimeValue::Array(vec![
        RuntimeValue::String("item1".to_string()),
        RuntimeValue::String("item2".to_string()),
        RuntimeValue::String("last".to_string()),
        ])]))]
    #[case::set_array_out_of_bounds_positive(vec![RuntimeValue::Array(vec![
        RuntimeValue::String("item1".to_string()),
        RuntimeValue::String("item2".to_string()),
        ])],
        vec![
        ast_call("set", smallvec![
            ast_node(ast::Expr::Literal(ast::Literal::Number(5.into()))),
            ast_node(ast::Expr::Literal(ast::Literal::String("new".to_string()))),
        ])
        ],
        Ok(vec![RuntimeValue::Array(vec![
        RuntimeValue::String("item1".to_string()),
        RuntimeValue::String("item2".to_string()),
        RuntimeValue::NONE,
        RuntimeValue::NONE,
        RuntimeValue::NONE,
        RuntimeValue::String("new".to_string()),
        ])]))]
    #[case::set_array_negative_index(vec![RuntimeValue::Array(vec![
        RuntimeValue::String("item1".to_string()),
        RuntimeValue::String("item2".to_string()),
        RuntimeValue::String("item3".to_string()),
        ])],
        vec![
        ast_call("set", smallvec![
            ast_node(ast::Expr::Literal(ast::Literal::Number((-1).into()))),
            ast_node(ast::Expr::Literal(ast::Literal::String("negative".to_string()))),
        ])
        ],
        Ok(vec![RuntimeValue::Array(vec![
        RuntimeValue::String("negative".to_string()),
        RuntimeValue::String("item2".to_string()),
        RuntimeValue::String("item3".to_string()),
        ])]))]
    #[case::set_array_empty(vec![RuntimeValue::Array(Vec::new())],
        vec![
        ast_call("set", smallvec![
            ast_node(ast::Expr::Literal(ast::Literal::Number(0.into()))),
            ast_node(ast::Expr::Literal(ast::Literal::String("value".to_string()))),
        ])
        ],
        Ok(vec![RuntimeValue::Array(vec!["value".into()])]))]
    #[case::set_array_mixed_types(vec![RuntimeValue::Array(vec![
        RuntimeValue::String("text".to_string()),
        RuntimeValue::Number(42.into()),
        RuntimeValue::Boolean(true),
        ])],
        vec![
        ast_call("set", smallvec![
            ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
            ast_node(ast::Expr::Literal(ast::Literal::String("replaced".to_string()))),
        ])
        ],
        Ok(vec![RuntimeValue::Array(vec![
        RuntimeValue::String("text".to_string()),
        RuntimeValue::String("replaced".to_string()),
        RuntimeValue::Boolean(true),
        ])]))]
    #[case::set_array_with_none(vec![RuntimeValue::Array(vec![
        RuntimeValue::String("item1".to_string()),
        RuntimeValue::NONE,
        RuntimeValue::String("item3".to_string()),
        ])],
        vec![
        ast_call("set", smallvec![
            ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
            ast_node(ast::Expr::Literal(ast::Literal::String("not_none".to_string()))),
        ])
        ],
        Ok(vec![RuntimeValue::Array(vec![
        RuntimeValue::String("item1".to_string()),
        RuntimeValue::String("not_none".to_string()),
        RuntimeValue::String("item3".to_string()),
        ])]))]
    #[case::set_array_replace_with_none(vec![RuntimeValue::Array(vec![
        RuntimeValue::String("item1".to_string()),
        RuntimeValue::String("item2".to_string()),
        RuntimeValue::String("item3".to_string()),
        ])],
        vec![
        ast_call("set", smallvec![
            ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
            ast_node(ast::Expr::Literal(ast::Literal::None)),
        ])
        ],
        Ok(vec![RuntimeValue::Array(vec![
        RuntimeValue::String("item1".to_string()),
        RuntimeValue::NONE,
        RuntimeValue::String("item3".to_string()),
        ])]))]
    #[case::set_array_single_element(vec![RuntimeValue::Array(vec![
        RuntimeValue::String("only".to_string()),
        ])],
        vec![
        ast_call("set", smallvec![
            ast_node(ast::Expr::Literal(ast::Literal::Number(0.into()))),
            ast_node(ast::Expr::Literal(ast::Literal::String("changed".to_string()))),
        ])
        ],
        Ok(vec![RuntimeValue::Array(vec![
        RuntimeValue::String("changed".to_string()),
        ])]))]
    #[case::set_non_array(vec![RuntimeValue::String("not_an_array".to_string())],
        vec![
        ast_call("set", smallvec![
            ast_node(ast::Expr::Literal(ast::Literal::Number(0.into()))),
            ast_node(ast::Expr::Literal(ast::Literal::String("value".to_string()))),
        ])
        ],
        Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                             name: "set".to_string(),
                             args: vec!["string".into(), "number".into(), "string".into()]})))]
    #[case::set_array_non_number_index(vec![RuntimeValue::Array(vec![
        RuntimeValue::String("item1".to_string()),
        RuntimeValue::String("item2".to_string()),
        ])],
        vec![
        ast_call("set", smallvec![
            ast_node(ast::Expr::Literal(ast::Literal::String("not_a_number".to_string()))),
            ast_node(ast::Expr::Literal(ast::Literal::String("value".to_string()))),
        ])
        ],
        Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                             name: "set".to_string(),
                             args: vec!["array".into(), "string".into(), "string".into()]})))]
    #[case::del_dict_valid_key(vec![RuntimeValue::Dict(vec![
            (Ident::new("key1"), RuntimeValue::String("value1".to_string())),
            (Ident::new("key2"), RuntimeValue::String("value2".to_string())),
            (Ident::new("key3"), RuntimeValue::String("value3".to_string())),
        ].into_iter().collect())],
        vec![
              ast_call("del", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("key2".to_string()))),
              ]),
        ],
        Ok(vec![RuntimeValue::Dict(vec![
            (Ident::new("key1"), RuntimeValue::String("value1".to_string())),
            (Ident::new("key3"), RuntimeValue::String("value3".to_string())),
        ].into_iter().collect())]))]
    #[case::del_dict_nonexistent_key(vec![RuntimeValue::Dict(vec![
            (Ident::new("key1"), RuntimeValue::String("value1".to_string())),
            (Ident::new("key2"), RuntimeValue::String("value2".to_string())),
        ].into_iter().collect())],
        vec![
              ast_call("del", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("nonexistent".to_string()))),
              ]),
        ],
        Ok(vec![RuntimeValue::Dict(vec![
            (Ident::new("key1"), RuntimeValue::String("value1".to_string())),
            (Ident::new("key2"), RuntimeValue::String("value2".to_string())),
        ].into_iter().collect())]))]
    #[case::del_dict_empty(vec![RuntimeValue::new_dict()],
        vec![
              ast_call("del", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("any_key".to_string()))),
              ]),
        ],
        Ok(vec![RuntimeValue::new_dict()]))]
    #[case::del_dict_single_key(vec![RuntimeValue::Dict(vec![
            (Ident::new("only_key"), RuntimeValue::String("only_value".to_string())),
        ].into_iter().collect())],
        vec![
              ast_call("del", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("only_key".to_string()))),
              ]),
        ],
        Ok(vec![RuntimeValue::new_dict()]))]
    #[case::del_dict_mixed_value_types(vec![RuntimeValue::Dict(vec![
            (Ident::new("str_key"), RuntimeValue::String("string_value".to_string())),
            (Ident::new("num_key"), RuntimeValue::Number(42.into())),
            (Ident::new("bool_key"), RuntimeValue::Boolean(true)),
            (Ident::new("array_key"), RuntimeValue::Array(vec![RuntimeValue::String("item".to_string())])),
        ].into_iter().collect())],
        vec![
              ast_call("del", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("num_key".to_string()))),
              ]),
        ],
        Ok(vec![RuntimeValue::Dict(vec![
            (Ident::new("str_key"), RuntimeValue::String("string_value".to_string())),
            (Ident::new("bool_key"), RuntimeValue::Boolean(true)),
            (Ident::new("array_key"), RuntimeValue::Array(vec![RuntimeValue::String("item".to_string())])),
        ].into_iter().collect())]))]
    #[case::del_dict_with_number_key_as_string(vec![RuntimeValue::Dict(vec![
            (Ident::new("1"), RuntimeValue::String("value1".to_string())),
            (Ident::new("2"), RuntimeValue::String("value2".to_string())),
        ].into_iter().collect())],
        vec![
              ast_call("del", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("1".to_string()))),
              ]),
        ],
        Ok(vec![RuntimeValue::Dict(vec![
            (Ident::new("2"), RuntimeValue::String("value2".to_string())),
        ].into_iter().collect())]))]
    #[case::del_dict_with_number_index_error(vec![RuntimeValue::Dict(vec![
            (Ident::new("key1"), RuntimeValue::String("value1".to_string())),
            (Ident::new("key2"), RuntimeValue::String("value2".to_string())),
        ].into_iter().collect())],
        vec![
              ast_call("del", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
              ]),
        ],
        Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                                     name: "del".to_string(),
                                                     args: vec!["dict".into(), "number".into()]})))]
    #[case::set_code_block_lang_string(vec![RuntimeValue::new_markdown(mq_markdown::Node::Code(mq_markdown::Code {
            value: "let x = 1;".to_string(),
            lang: None,
            fence: true,
            meta: None,
            position: None,
        }))],
        vec![
            ast_call("set_code_block_lang", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("rust".to_string())))
            ])
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Code(mq_markdown::Code {
            value: "let x = 1;".to_string(),
            lang: Some("rust".to_string()),
            fence: true,
            meta: None,
            position: None,
        }))]))]
    #[case::set_code_block_lang_empty(vec![RuntimeValue::new_markdown(mq_markdown::Node::Code(mq_markdown::Code {
            value: "let x = 1;".to_string(),
            lang: Some("js".to_string()),
            fence: true,
            meta: None,
            position: None,
        }))],
        vec![
            ast_call("set_code_block_lang", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("".to_string())))
            ])
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Code(mq_markdown::Code {
            value: "let x = 1;".to_string(),
            lang: None,
            fence: true,
            meta: None,
            position: None,
        }))]))]
    #[case::set_code_block_lang_non_code(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text {
            value: "not code".to_string(),
            position: None,
        }))],
        vec![
            ast_call("set_code_block_lang", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("rust".to_string())))
            ])
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text {
            value: "not code".to_string(),
            position: None,
        }))]))]
    #[case::set_code_block_lang_none(vec![RuntimeValue::NONE],
        vec![
            ast_call("set_code_block_lang", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("rust".to_string())))
            ])
        ],
        Ok(vec![RuntimeValue::NONE]))]
    #[case::set_list_ordered_true(
        vec![RuntimeValue::new_markdown(mq_markdown::Node::List(mq_markdown::List {
            values: vec!["Item 1".to_string().into(), "Item 2".to_string().into()],
            ordered: false,
            level: 1,
            index: 0,
            checked: None,
            position: None,
        }))],
        vec![
            ast_call("set_list_ordered", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
            ])
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::List(mq_markdown::List {
            values: vec!["Item 1".to_string().into(), "Item 2".to_string().into()],
            ordered: true,
            level: 1,
            index: 0,
            checked: None,
            position: None,
        }))]))]
    #[case::set_list_ordered_false(
        vec![RuntimeValue::new_markdown(mq_markdown::Node::List(mq_markdown::List {
            values: vec!["Item 1".to_string().into(), "Item 2".to_string().into()],
            ordered: true,
            level: 1,
            index: 0,
            checked: None,
            position: None,
        }))],
        vec![
            ast_call("set_list_ordered", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
            ])
        ],
        Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::List(mq_markdown::List {
            values: vec!["Item 1".to_string().into(), "Item 2".to_string().into()],
            ordered: false,
            level: 1,
            index: 0,
            checked: None,
            position: None,
        }))]))]
    #[case::set_list_ordered_non_list(
        vec![RuntimeValue::String("not a list".to_string())],
        vec![
            ast_call("set_list_ordered", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
            ])
        ],
        Ok(vec![RuntimeValue::String("not a list".to_string())]))]
    #[case::range_number(vec![RuntimeValue::Number(1.into())],
            vec![
                ast_call("range", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(5.into()))),
                ])
            ],
            Ok(vec![RuntimeValue::Array(vec![
                RuntimeValue::Number(1.into()),
                RuntimeValue::Number(2.into()),
                RuntimeValue::Number(3.into()),
                RuntimeValue::Number(4.into()),
                RuntimeValue::Number(5.into()),
            ])]))]
    #[case::range_number_negative(vec![RuntimeValue::Number(5.into())],
            vec![
                ast_call("range", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(5.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                ])
            ],
            Ok(vec![RuntimeValue::Array(vec![
                RuntimeValue::Number(5.into()),
                RuntimeValue::Number(4.into()),
                RuntimeValue::Number(3.into()),
                RuntimeValue::Number(2.into()),
                RuntimeValue::Number(1.into()),
            ])]))]
    #[case::range_string(vec![RuntimeValue::String("a".to_string())],
            vec![
                ast_call("range", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("a".to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::String("e".to_string()))),
                ])
            ],
            Ok(vec![RuntimeValue::Array(vec![
                RuntimeValue::String("a".to_string()),
                RuntimeValue::String("b".to_string()),
                RuntimeValue::String("c".to_string()),
                RuntimeValue::String("d".to_string()),
                RuntimeValue::String("e".to_string()),
            ])]))]
    #[case::range_string(vec![RuntimeValue::String("a".to_string())],
            vec![
                ast_call("range", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("a1".to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::String("a2".to_string()))),
                ])
            ],
            Ok(vec![RuntimeValue::Array(vec![
                RuntimeValue::String("a1".to_string()),
                RuntimeValue::String("a2".to_string()),
            ])]))]
    #[case::range_string_reverse(vec![RuntimeValue::String("e".to_string())],
            vec![
                ast_call("range", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("e".to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::String("a".to_string()))),
                ])
            ],
            Ok(vec![RuntimeValue::Array(vec![
                RuntimeValue::String("e".to_string()),
                RuntimeValue::String("d".to_string()),
                RuntimeValue::String("c".to_string()),
                RuntimeValue::String("b".to_string()),
                RuntimeValue::String("a".to_string()),
            ])]))]
    #[case::range_string_step_2(vec![RuntimeValue::String("a".to_string())],
            vec![
                ast_call("range", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("a".to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::String("e".to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
                ])
            ],
            Ok(vec![RuntimeValue::Array(vec![
                RuntimeValue::String("a".to_string()),
                RuntimeValue::String("c".to_string()),
                RuntimeValue::String("e".to_string()),
            ])]))]
    #[case::range_string_step_minus_2(vec![RuntimeValue::String("e".to_string())],
            vec![
                ast_call("range", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("e".to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::String("a".to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number((-2).into()))),
                ])
            ],
            Ok(vec![RuntimeValue::Array(vec![
                RuntimeValue::String("e".to_string()),
                RuntimeValue::String("c".to_string()),
                RuntimeValue::String("a".to_string()),
            ])]))]
    #[case::insert_array_middle(vec![RuntimeValue::Array(vec![
                RuntimeValue::String("a".to_string()),
                RuntimeValue::String("b".to_string()),
                RuntimeValue::String("c".to_string()),
            ])],
                vec![
                    ast_call("insert", smallvec![
                        ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                        ast_node(ast::Expr::Literal(ast::Literal::String("x".to_string()))),
                    ])
                ],
                Ok(vec![RuntimeValue::Array(vec![
                    RuntimeValue::String("a".to_string()),
                    RuntimeValue::String("x".to_string()),
                    RuntimeValue::String("b".to_string()),
                    RuntimeValue::String("c".to_string()),
                ])]))]
    #[case::insert_array_start(vec![RuntimeValue::Array(vec![
                RuntimeValue::String("a".to_string()),
                RuntimeValue::String("b".to_string()),
            ])],
                vec![
                    ast_call("insert", smallvec![
                        ast_node(ast::Expr::Literal(ast::Literal::Number(0.into()))),
                        ast_node(ast::Expr::Literal(ast::Literal::String("z".to_string()))),
                    ])
                ],
                Ok(vec![RuntimeValue::Array(vec![
                    RuntimeValue::String("z".to_string()),
                    RuntimeValue::String("a".to_string()),
                    RuntimeValue::String("b".to_string()),
                ])]))]
    #[case::insert_array_end(vec![RuntimeValue::Array(vec![
                RuntimeValue::String("a".to_string()),
                RuntimeValue::String("b".to_string()),
            ])],
                vec![
                    ast_call("insert", smallvec![
                        ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
                        ast_node(ast::Expr::Literal(ast::Literal::String("c".to_string()))),
                    ])
                ],
                Ok(vec![RuntimeValue::Array(vec![
                    RuntimeValue::String("a".to_string()),
                    RuntimeValue::String("b".to_string()),
                    RuntimeValue::String("c".to_string()),
                ])]))]
    #[case::insert_array_out_of_bounds(vec![RuntimeValue::Array(vec![
                RuntimeValue::String("a".to_string()),
            ])],
                vec![
                    ast_call("insert", smallvec![
                        ast_node(ast::Expr::Literal(ast::Literal::Number(5.into()))),
                        ast_node(ast::Expr::Literal(ast::Literal::String("b".to_string()))),
                    ])
                ],
                Ok(vec![RuntimeValue::Array(vec![
                    RuntimeValue::String("a".to_string()),
                    RuntimeValue::NONE,
                    RuntimeValue::NONE,
                    RuntimeValue::NONE,
                    RuntimeValue::NONE,
                    RuntimeValue::String("b".to_string()),
                ])]))]
    #[case::insert_array_negative_index(vec![RuntimeValue::Array(vec![
                RuntimeValue::String("a".to_string()),
                RuntimeValue::String("b".to_string()),
            ])],
                vec![
                    ast_call("insert", smallvec![
                        ast_node(ast::Expr::Literal(ast::Literal::Number((-1).into()))),
                        ast_node(ast::Expr::Literal(ast::Literal::String("z".to_string()))),
                    ])
                ],
                Ok(vec![RuntimeValue::Array(vec![
                    RuntimeValue::String("z".to_string()),
                    RuntimeValue::String("a".to_string()),
                    RuntimeValue::String("b".to_string()),
                ])]))]
    #[case::insert_array_empty(vec![RuntimeValue::Array(Vec::new())],
                vec![
                    ast_call("insert", smallvec![
                        ast_node(ast::Expr::Literal(ast::Literal::Number(0.into()))),
                        ast_node(ast::Expr::Literal(ast::Literal::String("first".to_string()))),
                    ])
                ],
                Ok(vec![RuntimeValue::Array(vec![
                    RuntimeValue::String("first".to_string()),
                ])]))]
    #[case::insert_non_array(vec![RuntimeValue::Number(1.into())],
                vec![
                    ast_call("insert", smallvec![
                        ast_node(ast::Expr::Literal(ast::Literal::Number(0.into()))),
                        ast_node(ast::Expr::Literal(ast::Literal::String("value".to_string()))),
                    ])
                ],
                Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                     name: "insert".to_string(),
                                     args: vec!["number".into(), "number".into(), "string".into()]})))]
    #[case::insert_array_non_number_index(vec![RuntimeValue::Array(vec![
                RuntimeValue::String("item1".to_string()),
                RuntimeValue::String("item2".to_string()),
            ])],
                vec![
                    ast_call("insert", smallvec![
                        ast_node(ast::Expr::Literal(ast::Literal::String("not_a_number".to_string()))),
                        ast_node(ast::Expr::Literal(ast::Literal::String("value".to_string()))),
                    ])
                ],
                Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                     name: "insert".to_string(),
                                     args: vec!["array".into(), "string".into(), "string".into()]})))]
    #[case::insert_string_middle(vec![RuntimeValue::String("ac".to_string())],
                vec![
                    ast_call("insert", smallvec![
                        ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                        ast_node(ast::Expr::Literal(ast::Literal::String("b".to_string()))),
                    ])
                ],
                Ok(vec![RuntimeValue::String("abc".to_string())]))]
    #[case::insert_string_start(vec![RuntimeValue::String("bc".to_string())],
                vec![
                    ast_call("insert", smallvec![
                        ast_node(ast::Expr::Literal(ast::Literal::Number(0.into()))),
                        ast_node(ast::Expr::Literal(ast::Literal::String("a".to_string()))),
                    ])
                ],
                Ok(vec![RuntimeValue::String("abc".to_string())]))]
    #[case::insert_string_end(vec![RuntimeValue::String("ab".to_string())],
                vec![
                    ast_call("insert", smallvec![
                        ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
                        ast_node(ast::Expr::Literal(ast::Literal::String("c".to_string()))),
                    ])
                ],
                Ok(vec![RuntimeValue::String("abc".to_string())]))]
    #[case::insert_string_out_of_bounds(vec![RuntimeValue::String("a".to_string())],
                vec![
                    ast_call("insert", smallvec![
                        ast_node(ast::Expr::Literal(ast::Literal::Number(5.into()))),
                        ast_node(ast::Expr::Literal(ast::Literal::String("b".to_string()))),
                    ])
                ],
                Ok(vec![RuntimeValue::String("a    b".to_string())]))]
    #[case::insert_string_negative_index(vec![RuntimeValue::String("bc".to_string())],
                vec![
                    ast_call("insert", smallvec![
                        ast_node(ast::Expr::Literal(ast::Literal::Number((-1).into()))),
                        ast_node(ast::Expr::Literal(ast::Literal::String("a".to_string()))),
                    ])
                ],
                Ok(vec![RuntimeValue::String("abc".to_string())]))]
    #[case::to_markdown_string_string(vec![RuntimeValue::String("test".to_string())],
                vec![
                    ast_call("to_markdown_string", SmallVec::new())
                ],
                Ok(vec![RuntimeValue::String("test\n".to_string())]))]
    #[case::to_markdown_string_markdown_text(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "test".to_string(), position: None}))],
                vec![
                    ast_call("to_markdown_string", SmallVec::new())
                ],
                Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "test\n".to_string(), position: None}))]))]
    #[case::to_markdown_string_markdown_heading(vec![RuntimeValue::new_markdown(mq_markdown::Node::Heading(mq_markdown::Heading{depth: 2, values: vec!["Heading".to_string().into()], position: None}))],
                vec![
                    ast_call("to_markdown_string", SmallVec::new())
                ],
                Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "## Heading\n".to_string(), position: None}))]))]
    #[case::to_markdown_string_markdown_code(vec![RuntimeValue::new_markdown(mq_markdown::Node::Code(mq_markdown::Code{value: "let x = 1;".to_string(), lang: Some("rust".to_string()), fence: true, meta: None, position: None}))],
                vec![
                    ast_call("to_markdown_string", SmallVec::new())
                ],
                Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "```rust\nlet x = 1;\n```\n".to_string(), position: None}))]))]
    #[case::to_markdown_string_none(vec![RuntimeValue::NONE],
                vec![
                    ast_call("to_markdown_string", SmallVec::new())
                ],
                Ok(vec![RuntimeValue::String("".to_string())]))]
    #[case::break_in_foreach(
       vec![RuntimeValue::Array(vec![
            RuntimeValue::Number(1.into()),
            RuntimeValue::Number(2.into()),
            RuntimeValue::Number(3.into()),
        ])],
            vec![
                ast_node(ast::Expr::Foreach(
                    IdentWithToken::new("x"),
                    ast_node(ast::Expr::Self_),
                    vec![
                        ast_node(ast::Expr::If(smallvec![
                            (
                                Some(ast_node(ast::Expr::Call(
                                    IdentWithToken::new("eq"),
                                    smallvec![
                                        ast_node(ast::Expr::Ident(IdentWithToken::new("x"))),
                                        ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
                                    ],
                                ))),
                                ast_node(ast::Expr::Break(None)),
                            ),
                            (
                                None,
                                ast_node(ast::Expr::Ident(IdentWithToken::new("x"))),
                            ),
                        ])),
                    ],
                )),
            ],
            Ok(vec![RuntimeValue::Array(vec![
                RuntimeValue::Number(1.into()),
            ])])
        )]
    #[case::continue_in_foreach(
        vec![RuntimeValue::Array(vec![
            RuntimeValue::Number(1.into()),
            RuntimeValue::Number(2.into()),
            RuntimeValue::Number(3.into()),
        ])],
        vec![
            ast_node(ast::Expr::Foreach(
                IdentWithToken::new("x"),
                ast_node(ast::Expr::Self_),
                vec![
                    ast_node(ast::Expr::If(smallvec![
                        (
                            Some(ast_node(ast::Expr::Call(
                                IdentWithToken::new("eq"),
                                smallvec![
                                    ast_node(ast::Expr::Ident(IdentWithToken::new("x"))),
                                    ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
                                ],
                            ))),
                            ast_node(ast::Expr::Continue),
                        ),
                        (
                            None,
                            ast_node(ast::Expr::Ident(IdentWithToken::new("x"))),
                        ),
                    ])),
                ],
            )),
        ],
        Ok(vec![RuntimeValue::Array(vec![
            RuntimeValue::Number(1.into()),
            RuntimeValue::Number(3.into()),
        ])])
    )]
    #[case::foreach_string(
        vec![RuntimeValue::String("abc".to_string())],
        vec![
            ast_node(ast::Expr::Foreach(
                IdentWithToken::new("c"),
                ast_node(ast::Expr::Self_),
                vec![
                    ast_node(ast::Expr::Ident(IdentWithToken::new("c"))),
                ],
            )),
        ],
        Ok(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("a".to_string()),
            RuntimeValue::String("b".to_string()),
            RuntimeValue::String("c".to_string()),
        ])])
    )]
    #[case::loop_immediate_break(
        vec![RuntimeValue::Number(10.into())],
        vec![
            ast_node(ast::Expr::Loop(
                vec![
                    ast_node(ast::Expr::Break(None)),
                ],
            )),
        ],
        Ok(vec![RuntimeValue::Number(10.into())])
    )]
    #[case::to_array_string(vec![RuntimeValue::String("test".to_string())],
        vec![
            ast_call("to_array", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Array(vec![RuntimeValue::String("t".to_string()), RuntimeValue::String("e".to_string()), RuntimeValue::String("s".to_string()), RuntimeValue::String("t".to_string())])]))]
    #[case::to_array_number(vec![RuntimeValue::Number(42.into())],
        vec![
            ast_call("to_array", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Array(vec![RuntimeValue::Number(42.into())])]))]
    #[case::to_array_bool(vec![RuntimeValue::Boolean(true)],
        vec![
            ast_call("to_array", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Array(vec![RuntimeValue::Boolean(true)])]))]
    #[case::to_array_array(vec![RuntimeValue::Array(vec![RuntimeValue::String("a".to_string()), RuntimeValue::String("b".to_string())])],
        vec![
            ast_call("to_array", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Array(vec![RuntimeValue::String("a".to_string()), RuntimeValue::String("b".to_string())])]))]
    #[case::to_array_empty_array(vec![RuntimeValue::Array(Vec::new())],
        vec![
            ast_call("to_array", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Array(Vec::new())]))]
    #[case::to_array_dict(vec![RuntimeValue::Dict(vec![
            (Ident::new("key"), RuntimeValue::String("value".to_string())),
        ].into_iter().collect())],
        vec![
            ast_call("to_array", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Array(vec![RuntimeValue::Dict(vec![
            (Ident::new("key"), RuntimeValue::String("value".to_string())),
        ].into_iter().collect())])]))]
    #[case::type_none(vec![RuntimeValue::NONE],
       vec![
            ast_call("type", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::String("None".to_string())]))]
    #[case::to_text(vec![RuntimeValue::NONE],
            vec![
                 ast_call("to_text", SmallVec::new())
            ],
            Ok(vec![RuntimeValue::NONE]))]
    #[case::starts_with(vec![RuntimeValue::NONE],
       vec![
            ast_call("starts_with", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("te".to_string())))
            ])
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::ends_with(vec![RuntimeValue::NONE],
       vec![
            ast_call("ends_with", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("te".to_string())))
            ])
       ],
       Ok(vec![RuntimeValue::FALSE]))]
    #[case::rindex(vec![RuntimeValue::NONE],
       vec![
            ast_call("rindex", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("String".to_string())))
            ])
       ],
       Ok(vec![RuntimeValue::Number((-1).into())]))]
    #[case::utf8bytelen(vec![RuntimeValue::NONE],
       vec![
            ast_call("utf8bytelen", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::Number(0.into())]))]
    #[case::index(vec![RuntimeValue::NONE],
       vec![
            ast_call("index", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("test".to_string())))
            ])
       ],
       Ok(vec![RuntimeValue::Number((-1).into())]))]
    #[case::del(vec![RuntimeValue::NONE],
        vec![
              ast_call("del", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(4.into()))),
              ]),
        ],
        Ok(vec![RuntimeValue::NONE]))]
    #[case::downcase(vec![RuntimeValue::NONE],
       vec![
            ast_call("downcase", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::NONE]))]
    #[case::slice(vec![RuntimeValue::NONE],
       vec![
            ast_call("slice", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Number(0.into()))),
                ast_node(ast::Expr::Literal(ast::Literal::Number(4.into()))),
            ])
       ],
       Ok(vec![RuntimeValue::NONE]))]
    #[case::slice(vec![RuntimeValue::NONE],
       vec![
            ast_call("len", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::Number(0.into())]))]
    #[case::slice(vec![RuntimeValue::NONE],
       vec![
            ast_call("upcase", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::NONE]))]
    #[case::slice_array_negative_start_index(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("item1".to_string()),
            RuntimeValue::String("item2".to_string()),
            RuntimeValue::String("item3".to_string()),
            RuntimeValue::String("item4".to_string()),
            RuntimeValue::String("item5".to_string()),
        ])],
       vec![
            ast_call("slice", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Number((-2).into()))),
                ast_node(ast::Expr::Literal(ast::Literal::Number(4.into()))),
            ])
       ],
       Ok(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("item4".to_string()),
        ])]))]
    #[case::slice_array_negative_end_index(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("item1".to_string()),
            RuntimeValue::String("item2".to_string()),
            RuntimeValue::String("item3".to_string()),
            RuntimeValue::String("item4".to_string()),
            RuntimeValue::String("item5".to_string()),
        ])],
       vec![
            ast_call("slice", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                ast_node(ast::Expr::Literal(ast::Literal::Number((-1).into()))),
            ])
       ],
       Ok(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("item2".to_string()),
            RuntimeValue::String("item3".to_string()),
            RuntimeValue::String("item4".to_string()),
        ])]))]
    #[case::slice_array_both_negative_indices(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("item1".to_string()),
            RuntimeValue::String("item2".to_string()),
            RuntimeValue::String("item3".to_string()),
            RuntimeValue::String("item4".to_string()),
            RuntimeValue::String("item5".to_string()),
        ])],
       vec![
            ast_call("slice", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Number((-4).into()))),
                ast_node(ast::Expr::Literal(ast::Literal::Number((-2).into()))),
            ])
       ],
       Ok(vec![RuntimeValue::Array(vec![
            RuntimeValue::String("item2".to_string()),
            RuntimeValue::String("item3".to_string()),
        ])]))]
    #[case::slice_string_negative_start_index(vec![RuntimeValue::String("abcdef".to_string())],
       vec![
            ast_call("slice", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Number((-3).into()))),
                ast_node(ast::Expr::Literal(ast::Literal::Number(6.into()))),
            ])
       ],
       Ok(vec![RuntimeValue::String("def".to_string())]))]
    #[case::slice_string_negative_end_index(vec![RuntimeValue::String("abcdef".to_string())],
       vec![
            ast_call("slice", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                ast_node(ast::Expr::Literal(ast::Literal::Number((-1).into()))),
            ])
       ],
       Ok(vec![RuntimeValue::String("bcde".to_string())]))]
    #[case::slice_string_both_negative_indices(vec![RuntimeValue::String("abcdef".to_string())],
       vec![
            ast_call("slice", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Number((-5).into()))),
                ast_node(ast::Expr::Literal(ast::Literal::Number((-2).into()))),
            ])
       ],
       Ok(vec![RuntimeValue::String("bcd".to_string())]))]
    #[case::to_code(vec![RuntimeValue::NONE],
        vec![
              ast_call("to_code", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::None)),
              ]),
        ],
        Ok(vec![RuntimeValue::NONE]))]
    #[case::to_code(vec![RuntimeValue::NONE],
        vec![
              ast_call("update", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::None)),
              ]),
        ],
        Ok(vec![RuntimeValue::NONE]))]
    #[case::to_code_inline(vec![RuntimeValue::NONE],
        vec![
              ast_call("to_code_inline", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::None)),
              ]),
        ],
        Ok(vec![RuntimeValue::NONE]))]
    #[case::to_link(vec![RuntimeValue::NONE],
        vec![
              ast_call("to_link", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::None)),
                ast_node(ast::Expr::Literal(ast::Literal::None)),
                ast_node(ast::Expr::Literal(ast::Literal::None)),
              ]),
        ],
        Ok(vec![RuntimeValue::NONE]))]
    #[case::to_strong(vec![RuntimeValue::NONE],
        vec![
              ast_call("to_strong", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::None)),
              ]),
        ],
        Ok(vec![RuntimeValue::NONE]))]
    #[case::to_em(vec![RuntimeValue::NONE],
        vec![
              ast_call("to_em", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::None)),
              ]),
        ],
        Ok(vec![RuntimeValue::NONE]))]
    #[case::to_md_text(vec![RuntimeValue::NONE],
        vec![
              ast_call("to_md_text", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::None)),
              ]),
        ],
        Ok(vec![RuntimeValue::NONE]))]
    #[case::to_md_list(vec![RuntimeValue::NONE],
        vec![
              ast_call("to_md_list", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::None)),
                ast_node(ast::Expr::Literal(ast::Literal::None)),
              ]),
        ],
        Ok(vec![RuntimeValue::NONE]))]
    #[case::match_(vec![RuntimeValue::NONE],
       vec![
            ast_call("regex_match", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String(r"\d+".to_string()))),
            ])
       ],
       Ok(vec![RuntimeValue::Array(Vec::new())]))]
    #[case::gsub(vec![RuntimeValue::NONE],
       vec![
            ast_call("gsub", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String(r"\d+".to_string()))),
                ast_node(ast::Expr::Literal(ast::Literal::String(r"1".to_string()))),
            ])
       ],
       Ok(vec![RuntimeValue::NONE]))]
    #[case::replace(vec![RuntimeValue::NONE],
       vec![
            ast_call("replace", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("1".to_string()))),
                ast_node(ast::Expr::Literal(ast::Literal::String("2".to_string()))),
            ])
       ],
       Ok(vec![RuntimeValue::NONE]))]
    #[case::trim(vec![RuntimeValue::NONE],
       vec![
            ast_call("trim", SmallVec::new())
       ],
       Ok(vec![RuntimeValue::NONE]))]
    #[case::split(vec![RuntimeValue::NONE],
       vec![
            ast_call("split", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("test".to_string()))),
            ])
       ],
       Ok(vec![RuntimeValue::EMPTY_ARRAY]))]
    #[case::to_md_name(vec![RuntimeValue::NONE],
        vec![
              ast_call("to_md_name", SmallVec::new()),
        ],
        Ok(vec![RuntimeValue::NONE]))]
    #[case::get_url_none(vec![RuntimeValue::NONE],
        vec![
             ast_call("get_url", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::NONE]))]
    #[case::negate_positive(vec![RuntimeValue::Number(1.into())],
            vec![
                ast_call("negate", SmallVec::new())
            ],
            Ok(vec![RuntimeValue::Number((-1).into())]))]
    #[case::negate_negative(vec![RuntimeValue::Number((-42).into())],
            vec![
                ast_call("negate", SmallVec::new())
            ],
            Ok(vec![RuntimeValue::Number(42.into())]))]
    #[case::negate_zero(vec![RuntimeValue::Number(0.into())],
            vec![
                ast_call("negate", SmallVec::new())
            ],
            Ok(vec![RuntimeValue::Number(0.into())]))]
    #[case::negate_decimal(vec![RuntimeValue::Number(PI.into())],
            vec![
                ast_call("negate", SmallVec::new())
            ],
            Ok(vec![RuntimeValue::Number((-PI).into())]))]
    #[case::negate_invalid_type(vec![RuntimeValue::String("test".to_string())],
            vec![
                ast_call("negate", SmallVec::new())
            ],
            Err(InnerError::Runtime(RuntimeError::InvalidTypes{
                token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into() },
                name: "negate".to_string(),
                args: vec!["string".into()]
            })))]
    #[case::and_true_last_value(
                vec![RuntimeValue::Boolean(true)],
                vec![
                    ast_call("and", smallvec![
                        ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                        ast_node(ast::Expr::Literal(ast::Literal::String("last".to_string()))),
                    ])
                ],
                Ok(vec![RuntimeValue::String("last".to_string())])
            )]
    #[case::and_false_first_value(
                vec![RuntimeValue::Boolean(false)],
                vec![
                    ast_call("and", smallvec![
                        ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                        ast_node(ast::Expr::Literal(ast::Literal::String("should_not_evaluate".to_string()))),
                    ])
                ],
                Ok(vec![RuntimeValue::Boolean(false)])
            )]
    #[case::and_mixed_values(
                vec![RuntimeValue::Boolean(true)],
                vec![
                    ast_call("and", smallvec![
                        ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                        ast_node(ast::Expr::Literal(ast::Literal::Number(42.into()))),
                    ])
                ],
                Ok(vec![RuntimeValue::Number(42.into())])
            )]
    #[case::and_multiple_true(
                vec![RuntimeValue::Boolean(true)],
                vec![
                    ast_call("and", smallvec![
                        ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                        ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                        ast_node(ast::Expr::Literal(ast::Literal::String("final".to_string()))),
                    ])
                ],
                Ok(vec![RuntimeValue::String("final".to_string())])
            )]
    #[case::and_first_false(
                vec![RuntimeValue::Boolean(false)],
                vec![
                    ast_call("and", smallvec![
                        ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                        ast_node(ast::Expr::Literal(ast::Literal::String("should_not_evaluate".to_string()))),
                    ])
                ],
                Ok(vec![RuntimeValue::Boolean(false)])
            )]
    #[case::or_true_first_value(
                vec![RuntimeValue::Boolean(true)],
                vec![
                    ast_call("or", smallvec![
                        ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                        ast_node(ast::Expr::Literal(ast::Literal::String("should_not_evaluate".to_string()))),
                    ])
                ],
                Ok(vec![RuntimeValue::Boolean(true)])
            )]
    #[case::or_false_last_value(
                vec![RuntimeValue::Boolean(false)],
                vec![
                    ast_call("or", smallvec![
                        ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                        ast_node(ast::Expr::Literal(ast::Literal::String("last".to_string()))),
                    ])
                ],
                Ok(vec![RuntimeValue::String("last".to_string())])
            )]
    #[case::or_multiple_false_then_true(
                vec![RuntimeValue::Boolean(false)],
                vec![
                    ast_call("or", smallvec![
                        ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                        ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                        ast_node(ast::Expr::Literal(ast::Literal::Number(123.into()))),
                    ])
                ],
                Ok(vec![RuntimeValue::Number(123.into())])
            )]
    #[case::or_all_false(
                vec![RuntimeValue::Boolean(false)],
                vec![
                    ast_call("or", smallvec![
                        ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                        ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                        ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                    ])
                ],
                Ok(vec![RuntimeValue::Boolean(false)])
            )]
    #[case::or_first_true(
                vec![RuntimeValue::Boolean(true)],
                vec![
                    ast_call("or", smallvec![
                        ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                        ast_node(ast::Expr::Literal(ast::Literal::Number(999.into()))),
                    ])
                ],
                Ok(vec![RuntimeValue::Boolean(true)])
            )]
    #[case::expr_and_both_true(
                vec![RuntimeValue::Boolean(true)],
                vec![
                    Shared::new(AstNode {
                        token_id: 0.into(),
                        expr: Shared::new(ast::Expr::And(vec![
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                        ])),
                    })
                ],
                Ok(vec![RuntimeValue::Boolean(true)])
            )]
    #[case::expr_and_first_false(
                vec![RuntimeValue::Boolean(false)],
                vec![
                    Shared::new(AstNode {
                        token_id: 0.into(),
                        expr: Shared::new(ast::Expr::And(vec![
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                        ])),
                    })
                ],
                Ok(vec![RuntimeValue::Boolean(false)])
            )]
    #[case::expr_and_second_false(
                vec![RuntimeValue::Boolean(false)],
                vec![
                    Shared::new(AstNode {
                        token_id: 0.into(),
                        expr: Shared::new(ast::Expr::And(vec![
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                        ])),
                    })
                ],
                Ok(vec![RuntimeValue::Boolean(false)])
            )]
    #[case::expr_and_return_last_value(
                vec![RuntimeValue::Boolean(true)],
                vec![
                    Shared::new(AstNode {
                        token_id: 0.into(),
                        expr: Shared::new(ast::Expr::And(vec![
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                            ast_node(ast::Expr::Literal(ast::Literal::String("last".to_string()))),
                        ])),
                    })
                ],
                Ok(vec![RuntimeValue::String("last".to_string())])
            )]
    #[case::expr_or_both_true(
                vec![RuntimeValue::Boolean(true)],
                vec![
                    Shared::new(AstNode {
                        token_id: 0.into(),
                        expr: Shared::new(ast::Expr::Or(vec![
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                        ])),
                    })
                ],
                Ok(vec![RuntimeValue::Boolean(true)])
            )]
    #[case::expr_or_first_true(
                vec![RuntimeValue::Boolean(true)],
                vec![
                    Shared::new(AstNode {
                        token_id: 0.into(),
                        expr: Shared::new(ast::Expr::Or(vec![
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                        ])),
                    })
                ],
                Ok(vec![RuntimeValue::Boolean(true)])
            )]
    #[case::expr_or_second_true(
                vec![RuntimeValue::Boolean(false)],
                vec![
                    Shared::new(AstNode {
                        token_id: 0.into(),
                        expr: Shared::new(ast::Expr::Or(vec![
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                        ])),
                    })
                ],
                Ok(vec![RuntimeValue::Boolean(true)])
            )]
    #[case::expr_or_both_false(
                vec![RuntimeValue::Boolean(false)],
                vec![
                    Shared::new(AstNode {
                        token_id: 0.into(),
                        expr: Shared::new(ast::Expr::Or(vec![
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                        ])),
                    })
                ],
                Ok(vec![RuntimeValue::Boolean(false)])
            )]
    #[case::expr_or_return_last_value(
                vec![RuntimeValue::Boolean(false)],
                vec![
                    Shared::new(AstNode {
                        token_id: 0.into(),
                        expr: Shared::new(ast::Expr::Or(vec![
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                            ast_node(ast::Expr::Literal(ast::Literal::String("last".to_string()))),
                        ])),
                    })
                ],
                Ok(vec![RuntimeValue::String("last".to_string())])
            )]
    #[case::expr_and_three_all_true(
                vec![RuntimeValue::Boolean(true)],
                vec![
                    Shared::new(AstNode {
                        token_id: 0.into(),
                        expr: Shared::new(ast::Expr::And(vec![
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                        ])),
                    })
                ],
                Ok(vec![RuntimeValue::Boolean(true)])
            )]
    #[case::expr_and_three_middle_false(
                vec![RuntimeValue::Boolean(false)],
                vec![
                    Shared::new(AstNode {
                        token_id: 0.into(),
                        expr: Shared::new(ast::Expr::And(vec![
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                        ])),
                    })
                ],
                Ok(vec![RuntimeValue::Boolean(false)])
            )]
    #[case::expr_or_three_all_false(
                vec![RuntimeValue::Boolean(false)],
                vec![
                    Shared::new(AstNode {
                        token_id: 0.into(),
                        expr: Shared::new(ast::Expr::Or(vec![
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                        ])),
                    })
                ],
                Ok(vec![RuntimeValue::Boolean(false)])
            )]
    #[case::expr_or_three_middle_true(
                vec![RuntimeValue::Boolean(true)],
                vec![
                    Shared::new(AstNode {
                        token_id: 0.into(),
                        expr: Shared::new(ast::Expr::Or(vec![
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                        ])),
                    })
                ],
                Ok(vec![RuntimeValue::Boolean(true)])
            )]
    #[case::expr_or_three_last_true(
                vec![RuntimeValue::Boolean(false)],
                vec![
                    Shared::new(AstNode {
                        token_id: 0.into(),
                        expr: Shared::new(ast::Expr::Or(vec![
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                            ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                        ])),
                    })
                ],
                Ok(vec![RuntimeValue::Boolean(true)])
            )]
    #[case::expr_or_and_mixed_first_arm_false(
                // Or([And([true, false]), And([true, true])])  =>  true
                vec![RuntimeValue::Boolean(true)],
                vec![
                    Shared::new(AstNode {
                        token_id: 0.into(),
                        expr: Shared::new(ast::Expr::Or(vec![
                            ast_node(ast::Expr::And(vec![
                                ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                                ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                            ])),
                            ast_node(ast::Expr::And(vec![
                                ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                                ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                            ])),
                        ])),
                    })
                ],
                Ok(vec![RuntimeValue::Boolean(true)])
            )]
    #[case::expr_or_and_mixed_all_arms_false(
                // Or([And([false, true]), And([false, true])])  =>  false
                vec![RuntimeValue::Boolean(false)],
                vec![
                    Shared::new(AstNode {
                        token_id: 0.into(),
                        expr: Shared::new(ast::Expr::Or(vec![
                            ast_node(ast::Expr::And(vec![
                                ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                                ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                            ])),
                            ast_node(ast::Expr::And(vec![
                                ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                                ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                            ])),
                        ])),
                    })
                ],
                Ok(vec![RuntimeValue::Boolean(false)])
            )]
    #[case::expr_and_or_mixed_both_or_true(
                // And([Or([false, true]), Or([false, true])])  =>  true
                vec![RuntimeValue::Boolean(true)],
                vec![
                    Shared::new(AstNode {
                        token_id: 0.into(),
                        expr: Shared::new(ast::Expr::And(vec![
                            ast_node(ast::Expr::Or(vec![
                                ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                                ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                            ])),
                            ast_node(ast::Expr::Or(vec![
                                ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                                ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                            ])),
                        ])),
                    })
                ],
                Ok(vec![RuntimeValue::Boolean(true)])
            )]
    #[case::expr_and_or_mixed_second_or_false(
                // And([Or([false, true]), Or([false, false])])  =>  false
                vec![RuntimeValue::Boolean(false)],
                vec![
                    Shared::new(AstNode {
                        token_id: 0.into(),
                        expr: Shared::new(ast::Expr::And(vec![
                            ast_node(ast::Expr::Or(vec![
                                ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                                ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
                            ])),
                            ast_node(ast::Expr::Or(vec![
                                ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                                ast_node(ast::Expr::Literal(ast::Literal::Bool(false))),
                            ])),
                        ])),
                    })
                ],
                Ok(vec![RuntimeValue::Boolean(false)])
            )]
    #[case::intern_string(
                vec![RuntimeValue::String("hello".to_string())],
                vec![
                    ast_call("intern", SmallVec::new())
                ],
                Ok(vec![RuntimeValue::String("hello".to_string())])
            )]
    #[case::intern_same_string_twice(
                vec![RuntimeValue::String("repeat".to_string()), RuntimeValue::String("repeat".to_string())],
                vec![
                    ast_call("intern", SmallVec::new()),
                    ast_call("intern", SmallVec::new())
                ],
                Ok(vec![RuntimeValue::String("repeat".to_string()), RuntimeValue::String("repeat".to_string())])
            )]
    #[case::intern_different_strings(
                vec![RuntimeValue::String("a".to_string()), RuntimeValue::String("b".to_string())],
                vec![
                    ast_call("intern", SmallVec::new()),
                    ast_call("intern", SmallVec::new())
                ],
                Ok(vec![RuntimeValue::String("a".to_string()), RuntimeValue::String("b".to_string())])
            )]
    #[case::intern_number(
                vec![RuntimeValue::Number(42.into())],
                vec![
                    ast_call("intern", SmallVec::new())
                ],
                Ok(vec![RuntimeValue::String("42".to_string())])
            )]
    #[case::intern_none(
                vec![RuntimeValue::NONE],
                vec![
                    ast_call("intern", SmallVec::new())
                ],
                Ok(vec![RuntimeValue::String("".to_string())])
            )]
    #[case::infinite(
                vec![RuntimeValue::NONE],
                vec![
                    ast_call("infinite", SmallVec::new())
                ],
                Ok(vec![RuntimeValue::Number(INFINITE)])
            )]
    #[case::is_nan_with_nan(
                vec![RuntimeValue::Number(NAN)],
                vec![
                    ast_call("is_nan", SmallVec::new())
                ],
                Ok(vec![RuntimeValue::Boolean(true)])
            )]
    #[case::is_nan_with_number(
        vec![RuntimeValue::Number(42.0.into())],
        vec![
            ast_call("is_nan", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Boolean(false)])
    )]
    #[case::coalesce_first_non_none(
        vec![RuntimeValue::NONE],
        vec![
            ast_call("coalesce", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::None)),
                ast_node(ast::Expr::Literal(ast::Literal::String("first".to_string()))),
            ])
        ],
        Ok(vec![RuntimeValue::String("first".to_string())])
    )]
    #[case::coalesce_second_non_none(
        vec![RuntimeValue::NONE],
        vec![
            ast_call("coalesce", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::None)),
                ast_node(ast::Expr::Literal(ast::Literal::None)),
            ])
        ],
        Ok(vec![RuntimeValue::NONE])
    )]
    #[case::coalesce_first_value_non_none(
        vec![RuntimeValue::String("value".to_string())],
        vec![
            ast_call("coalesce", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("value".to_string()))),
                ast_node(ast::Expr::Literal(ast::Literal::String("other".to_string()))),
            ])
        ],
        Ok(vec![RuntimeValue::String("value".to_string())])
    )]
    #[case::coalesce_array(
        vec![RuntimeValue::Array(vec![RuntimeValue::NONE, RuntimeValue::String("foo".to_string())])],
        vec![
            ast_call("coalesce", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::None)),
                ast_node(ast::Expr::Literal(ast::Literal::String("bar".to_string()))),
            ])
        ],
        Ok(vec![RuntimeValue::String("bar".to_string())])
    )]
    #[case::eq_symbol(
        vec![RuntimeValue::Symbol(Ident::new("sym"))],
        vec![
            ast_call(
                "eq",
                smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Symbol(Ident::new("sym")))),
                    ast_node(ast::Expr::Literal(ast::Literal::Symbol(Ident::new("sym")))),
                ],
            ),
        ],
        Ok(vec![RuntimeValue::TRUE])
    )]
    #[case::eq_symbol_false(
        vec![RuntimeValue::Symbol(Ident::new("sym1"))],
        vec![
            ast_call(
                "eq",
                smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Symbol(Ident::new("sym1")))),
                    ast_node(ast::Expr::Literal(ast::Literal::Symbol(Ident::new("sym2")))),
                ],
            ),
        ],
        Ok(vec![RuntimeValue::FALSE])
    )]
    #[case::lt_symbol(
        vec![RuntimeValue::Symbol(Ident::new("a"))],
        vec![
            ast_call(
                "lt",
                smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Symbol(Ident::new("a")))),
                    ast_node(ast::Expr::Literal(ast::Literal::Symbol(Ident::new("b")))),
                ],
            ),
        ],
        Ok(vec![RuntimeValue::TRUE])
    )]
    #[case::lte_symbol_true(
        vec![RuntimeValue::Symbol(Ident::new("a"))],
        vec![
            ast_call(
                "lte",
                smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Symbol(Ident::new("a")))),
                    ast_node(ast::Expr::Literal(ast::Literal::Symbol(Ident::new("a")))),
                ],
            ),
        ],
        Ok(vec![RuntimeValue::TRUE])
    )]
    #[case::gt_symbol_false(
        vec![RuntimeValue::Symbol(Ident::new("a"))],
        vec![
            ast_call(
                "gt",
                smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Symbol(Ident::new("a")))),
                    ast_node(ast::Expr::Literal(ast::Literal::Symbol(Ident::new("b")))),
                ],
            ),
        ],
        Ok(vec![RuntimeValue::FALSE])
    )]
    #[case::gte_symbol_true(
        vec![RuntimeValue::Symbol(Ident::new("b"))],
        vec![
            ast_call(
                "gte",
                smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Symbol(Ident::new("b")))),
                    ast_node(ast::Expr::Literal(ast::Literal::Symbol(Ident::new("b")))),
                ],
            ),
        ],
        Ok(vec![RuntimeValue::TRUE])
    )]
    #[case::gte_symbol_false(
        vec![RuntimeValue::Symbol(Ident::new("a"))],
        vec![
            ast_call(
                "gte",
                smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Symbol(Ident::new("a")))),
                    ast_node(ast::Expr::Literal(ast::Literal::Symbol(Ident::new("b")))),
                ],
            ),
        ],
        Ok(vec![RuntimeValue::FALSE])
    )]
    #[case::get_dict_symbol_key(
        vec![RuntimeValue::Dict(vec![
            (Ident::new("key1"), RuntimeValue::String("value1".to_string())),
            (Ident::new("key2"), RuntimeValue::String("value2".to_string())),
            (Ident::new("key3"), RuntimeValue::String("value3".to_string())),
        ].into_iter().collect())],
        vec![
            ast_call("get", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Symbol(Ident::new("key2")))),
            ])
        ],
        Ok(vec![RuntimeValue::String("value2".to_string())])
    )]
    #[case::get_dict_symbol_key_not_found(
        vec![RuntimeValue::Dict(vec![
            (Ident::new("key1"), RuntimeValue::String("value1".to_string())),
        ].into_iter().collect())],
        vec![
            ast_call("get", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Symbol(Ident::new("keyX")))),
            ])
        ],
        Ok(vec![RuntimeValue::NONE])
    )]
    #[case::set_dict_symbol_key(
        vec![RuntimeValue::Dict(vec![
            (Ident::new("sym1"), RuntimeValue::String("v1".to_string())),
            (Ident::new("sym2"), RuntimeValue::String("v2".to_string())),
        ].into_iter().collect())],
        vec![
            ast_call("set", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Symbol(Ident::new("sym2")))),
                ast_node(ast::Expr::Literal(ast::Literal::String("updated".to_string()))),
            ])
        ],
        Ok(vec![RuntimeValue::Dict(vec![
            (Ident::new("sym1"), RuntimeValue::String("v1".to_string())),
            (Ident::new("sym2"), RuntimeValue::String("updated".to_string())),
        ].into_iter().collect())])
    )]
    #[case::set_dict_symbol_key_new(
        vec![RuntimeValue::Dict(vec![
            (Ident::new("sym1"), RuntimeValue::String("v1".to_string())),
        ].into_iter().collect())],
        vec![
            ast_call("set", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Symbol(Ident::new("sym3")))),
                ast_node(ast::Expr::Literal(ast::Literal::String("newval".to_string()))),
            ])
        ],
        Ok(vec![RuntimeValue::Dict(vec![
            (Ident::new("sym1"), RuntimeValue::String("v1".to_string())),
            (Ident::new("sym3"), RuntimeValue::String("newval".to_string())),
        ].into_iter().collect())])
    )]
    #[case::del_dict_symbol_key(
        vec![RuntimeValue::Dict(vec![
            (Ident::new("sym1"), RuntimeValue::String("v1".to_string())),
            (Ident::new("sym2"), RuntimeValue::String("v2".to_string())),
        ].into_iter().collect())],
        vec![
            ast_call("del", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Symbol(Ident::new("sym1")))),
            ])
        ],
        Ok(vec![RuntimeValue::Dict(vec![
            (Ident::new("sym2"), RuntimeValue::String("v2".to_string())),
        ].into_iter().collect())])
    )]
    #[case::del_dict_symbol_key_not_found(
        vec![RuntimeValue::Dict(vec![
            (Ident::new("sym1"), RuntimeValue::String("v1".to_string())),
        ].into_iter().collect())],
        vec![
            ast_call("del", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::Symbol(Ident::new("symX")))),
            ])
        ],
        Ok(vec![RuntimeValue::Dict(vec![
            (Ident::new("sym1"), RuntimeValue::String("v1".to_string())),
        ].into_iter().collect())])
    )]
    #[case::to_markdown_string_to_markdown_array(
        vec![RuntimeValue::String("a\n\nb\n\nc".to_string())],
        vec![
            ast_call("to_markdown", SmallVec::new())
        ],
        Ok(vec![RuntimeValue::Array(vec![
            RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "a".to_string(), position: None})),
            RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "b".to_string(), position: None})),
            RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "c".to_string(), position: None})),
        ])]))
    ]
    #[case::to_markdown_none(vec![RuntimeValue::NONE],
                vec![
                    ast_call("to_markdown", SmallVec::new())
                ],
                Err(InnerError::Runtime(RuntimeError::InvalidTypes{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()},
                                     name: "to_markdown".to_string(),
                                     args: vec!["None".into()]})))]
    #[case::error_with_message(vec![RuntimeValue::String("test".to_string())],
        vec![
            ast_call("error", smallvec![
                ast_node(ast::Expr::Literal(ast::Literal::String("Custom error message".to_string())))
            ])
        ],
        Err(InnerError::Runtime(RuntimeError::UserDefined{token: Token { range: Range::default(), kind: TokenKind::Eof, module_id: 1.into()}, message: "Custom error message".to_string()})))]
    #[case::get_markdown_position_line_col(
            vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{
                value: "test".to_string(),
                position: Some(mq_markdown::Position{start: mq_markdown::Point{line: 1, column: 10}, end: mq_markdown::Point{line: 2, column: 15}})
            }))],
            vec![
                ast_call("_get_markdown_position", SmallVec::new()),
                ast_call("get", smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("start_line".to_string()))),
                ])
            ],
            Ok(vec![RuntimeValue::new_markdown(mq_markdown::Node::Text(mq_markdown::Text{value: "1".to_string(), position: None}))]))]
    // --- or-pattern tests ---
    #[case::match_or_first_alt_matches(
        vec![RuntimeValue::NONE],
        vec![ast_node(ast::Expr::Match(
            ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
            smallvec![
                MatchArm {
                    pattern: Pattern::Or(vec![
                        Pattern::Literal(ast::Literal::Number(1.into())),
                        Pattern::Literal(ast::Literal::Number(2.into())),
                    ]),
                    guard: None,
                    body: ast_node(ast::Expr::Literal(ast::Literal::String("matched".to_string()))),
                },
                MatchArm {
                    pattern: Pattern::Wildcard,
                    guard: None,
                    body: ast_node(ast::Expr::Literal(ast::Literal::String("other".to_string()))),
                },
            ]
        ))],
        Ok(vec![RuntimeValue::String("matched".to_string())])
    )]
    #[case::match_or_second_alt_matches(
        vec![RuntimeValue::NONE],
        vec![ast_node(ast::Expr::Match(
            ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
            smallvec![
                MatchArm {
                    pattern: Pattern::Or(vec![
                        Pattern::Literal(ast::Literal::Number(1.into())),
                        Pattern::Literal(ast::Literal::Number(2.into())),
                    ]),
                    guard: None,
                    body: ast_node(ast::Expr::Literal(ast::Literal::String("matched".to_string()))),
                },
                MatchArm {
                    pattern: Pattern::Wildcard,
                    guard: None,
                    body: ast_node(ast::Expr::Literal(ast::Literal::String("other".to_string()))),
                },
            ]
        ))],
        Ok(vec![RuntimeValue::String("matched".to_string())])
    )]
    #[case::match_or_no_alt_matches_falls_to_wildcard(
        vec![RuntimeValue::NONE],
        vec![ast_node(ast::Expr::Match(
            ast_node(ast::Expr::Literal(ast::Literal::Number(3.into()))),
            smallvec![
                MatchArm {
                    pattern: Pattern::Or(vec![
                        Pattern::Literal(ast::Literal::Number(1.into())),
                        Pattern::Literal(ast::Literal::Number(2.into())),
                    ]),
                    guard: None,
                    body: ast_node(ast::Expr::Literal(ast::Literal::String("matched".to_string()))),
                },
                MatchArm {
                    pattern: Pattern::Wildcard,
                    guard: None,
                    body: ast_node(ast::Expr::Literal(ast::Literal::String("other".to_string()))),
                },
            ]
        ))],
        Ok(vec![RuntimeValue::String("other".to_string())])
    )]
    #[case::match_or_three_alts_middle_matches(
        vec![RuntimeValue::NONE],
        vec![ast_node(ast::Expr::Match(
            ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))),
            smallvec![
                MatchArm {
                    pattern: Pattern::Or(vec![
                        Pattern::Literal(ast::Literal::Number(1.into())),
                        Pattern::Literal(ast::Literal::Number(2.into())),
                        Pattern::Literal(ast::Literal::Number(3.into())),
                    ]),
                    guard: None,
                    body: ast_node(ast::Expr::Literal(ast::Literal::String("matched".to_string()))),
                },
            ]
        ))],
        Ok(vec![RuntimeValue::String("matched".to_string())])
    )]
    #[case::match_or_string_first_matches(
        vec![RuntimeValue::NONE],
        vec![ast_node(ast::Expr::Match(
            ast_node(ast::Expr::Literal(ast::Literal::String("a".to_string()))),
            smallvec![
                MatchArm {
                    pattern: Pattern::Or(vec![
                        Pattern::Literal(ast::Literal::String("a".to_string())),
                        Pattern::Literal(ast::Literal::String("b".to_string())),
                    ]),
                    guard: None,
                    body: ast_node(ast::Expr::Literal(ast::Literal::String("matched".to_string()))),
                },
                MatchArm {
                    pattern: Pattern::Wildcard,
                    guard: None,
                    body: ast_node(ast::Expr::Literal(ast::Literal::String("other".to_string()))),
                },
            ]
        ))],
        Ok(vec![RuntimeValue::String("matched".to_string())])
    )]
    #[case::match_or_string_second_matches(
        vec![RuntimeValue::NONE],
        vec![ast_node(ast::Expr::Match(
            ast_node(ast::Expr::Literal(ast::Literal::String("b".to_string()))),
            smallvec![
                MatchArm {
                    pattern: Pattern::Or(vec![
                        Pattern::Literal(ast::Literal::String("a".to_string())),
                        Pattern::Literal(ast::Literal::String("b".to_string())),
                    ]),
                    guard: None,
                    body: ast_node(ast::Expr::Literal(ast::Literal::String("matched".to_string()))),
                },
                MatchArm {
                    pattern: Pattern::Wildcard,
                    guard: None,
                    body: ast_node(ast::Expr::Literal(ast::Literal::String("other".to_string()))),
                },
            ]
        ))],
        Ok(vec![RuntimeValue::String("matched".to_string())])
    )]
    #[case::match_or_bool_true_matches(
        vec![RuntimeValue::NONE],
        vec![ast_node(ast::Expr::Match(
            ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
            smallvec![
                MatchArm {
                    pattern: Pattern::Or(vec![
                        Pattern::Literal(ast::Literal::Bool(true)),
                        Pattern::Literal(ast::Literal::Bool(false)),
                    ]),
                    guard: None,
                    body: ast_node(ast::Expr::Literal(ast::Literal::String("matched".to_string()))),
                },
            ]
        ))],
        Ok(vec![RuntimeValue::String("matched".to_string())])
    )]
    #[case::match_or_none_literal_first_matches(
        vec![RuntimeValue::NONE],
        vec![ast_node(ast::Expr::Match(
            ast_node(ast::Expr::Literal(ast::Literal::None)),
            smallvec![
                MatchArm {
                    pattern: Pattern::Or(vec![
                        Pattern::Literal(ast::Literal::None),
                        Pattern::Literal(ast::Literal::Number(1.into())),
                    ]),
                    guard: None,
                    body: ast_node(ast::Expr::Literal(ast::Literal::String("matched".to_string()))),
                },
                MatchArm {
                    pattern: Pattern::Wildcard,
                    guard: None,
                    body: ast_node(ast::Expr::Literal(ast::Literal::String("other".to_string()))),
                },
            ]
        ))],
        Ok(vec![RuntimeValue::String("matched".to_string())])
    )]
    #[case::match_or_type_string_matches(
        vec![RuntimeValue::NONE],
        vec![ast_node(ast::Expr::Match(
            ast_node(ast::Expr::Literal(ast::Literal::String("hello".to_string()))),
            smallvec![
                MatchArm {
                    pattern: Pattern::Or(vec![
                        Pattern::Type(crate::Ident::new("string")),
                        Pattern::Type(crate::Ident::new("number")),
                    ]),
                    guard: None,
                    body: ast_node(ast::Expr::Literal(ast::Literal::String("string or number".to_string()))),
                },
                MatchArm {
                    pattern: Pattern::Wildcard,
                    guard: None,
                    body: ast_node(ast::Expr::Literal(ast::Literal::String("other".to_string()))),
                },
            ]
        ))],
        Ok(vec![RuntimeValue::String("string or number".to_string())])
    )]
    #[case::match_or_type_number_matches(
        vec![RuntimeValue::NONE],
        vec![ast_node(ast::Expr::Match(
            ast_node(ast::Expr::Literal(ast::Literal::Number(42.into()))),
            smallvec![
                MatchArm {
                    pattern: Pattern::Or(vec![
                        Pattern::Type(crate::Ident::new("string")),
                        Pattern::Type(crate::Ident::new("number")),
                    ]),
                    guard: None,
                    body: ast_node(ast::Expr::Literal(ast::Literal::String("string or number".to_string()))),
                },
                MatchArm {
                    pattern: Pattern::Wildcard,
                    guard: None,
                    body: ast_node(ast::Expr::Literal(ast::Literal::String("other".to_string()))),
                },
            ]
        ))],
        Ok(vec![RuntimeValue::String("string or number".to_string())])
    )]
    #[case::match_or_type_no_match_falls_to_wildcard(
        vec![RuntimeValue::NONE],
        vec![ast_node(ast::Expr::Match(
            ast_node(ast::Expr::Literal(ast::Literal::Bool(true))),
            smallvec![
                MatchArm {
                    pattern: Pattern::Or(vec![
                        Pattern::Type(crate::Ident::new("string")),
                        Pattern::Type(crate::Ident::new("number")),
                    ]),
                    guard: None,
                    body: ast_node(ast::Expr::Literal(ast::Literal::String("string or number".to_string()))),
                },
                MatchArm {
                    pattern: Pattern::Wildcard,
                    guard: None,
                    body: ast_node(ast::Expr::Literal(ast::Literal::String("other".to_string()))),
                },
            ]
        ))],
        Ok(vec![RuntimeValue::String("other".to_string())])
    )]
    #[case::match_or_no_arm_matches_returns_none(
        vec![RuntimeValue::NONE],
        vec![ast_node(ast::Expr::Match(
            ast_node(ast::Expr::Literal(ast::Literal::Number(99.into()))),
            smallvec![
                MatchArm {
                    pattern: Pattern::Or(vec![
                        Pattern::Literal(ast::Literal::Number(1.into())),
                        Pattern::Literal(ast::Literal::Number(2.into())),
                    ]),
                    guard: None,
                    body: ast_node(ast::Expr::Literal(ast::Literal::String("matched".to_string()))),
                },
            ]
        ))],
        Ok(vec![RuntimeValue::NONE])
    )]
    #[case::match_or_with_guard_passes(
        vec![RuntimeValue::NONE],
        vec![ast_node(ast::Expr::Match(
            ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
            smallvec![
                MatchArm {
                    pattern: Pattern::Or(vec![
                        Pattern::Literal(ast::Literal::Number(1.into())),
                        Pattern::Literal(ast::Literal::Number(2.into())),
                    ]),
                    guard: Some(ast_node(ast::Expr::Literal(ast::Literal::Bool(true)))),
                    body: ast_node(ast::Expr::Literal(ast::Literal::String("matched".to_string()))),
                },
                MatchArm {
                    pattern: Pattern::Wildcard,
                    guard: None,
                    body: ast_node(ast::Expr::Literal(ast::Literal::String("other".to_string()))),
                },
            ]
        ))],
        Ok(vec![RuntimeValue::String("matched".to_string())])
    )]
    #[case::match_or_with_guard_fails(
        vec![RuntimeValue::NONE],
        vec![ast_node(ast::Expr::Match(
            ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
            smallvec![
                MatchArm {
                    pattern: Pattern::Or(vec![
                        Pattern::Literal(ast::Literal::Number(1.into())),
                        Pattern::Literal(ast::Literal::Number(2.into())),
                    ]),
                    guard: Some(ast_node(ast::Expr::Literal(ast::Literal::Bool(false)))),
                    body: ast_node(ast::Expr::Literal(ast::Literal::String("guarded".to_string()))),
                },
                MatchArm {
                    pattern: Pattern::Wildcard,
                    guard: None,
                    body: ast_node(ast::Expr::Literal(ast::Literal::String("other".to_string()))),
                },
            ]
        ))],
        Ok(vec![RuntimeValue::String("other".to_string())])
    )]
    #[case::match_type_bytes_matches(
        vec![RuntimeValue::NONE],
        vec![ast_node(ast::Expr::Match(
            ast_call("to_bytes", smallvec![ast_node(ast::Expr::Literal(ast::Literal::String("hello".to_string())))]),
            smallvec![
                MatchArm {
                    pattern: Pattern::Type(crate::Ident::new("bytes")),
                    guard: None,
                    body: ast_node(ast::Expr::Literal(ast::Literal::String("bytes".to_string()))),
                },
                MatchArm {
                    pattern: Pattern::Wildcard,
                    guard: None,
                    body: ast_node(ast::Expr::Literal(ast::Literal::String("other".to_string()))),
                },
            ]
        ))],
        Ok(vec![RuntimeValue::String("bytes".to_string())])
    )]
    #[case::match_type_bytes_no_match(
        vec![RuntimeValue::NONE],
        vec![ast_node(ast::Expr::Match(
            ast_node(ast::Expr::Literal(ast::Literal::String("hello".to_string()))),
            smallvec![
                MatchArm {
                    pattern: Pattern::Type(crate::Ident::new("bytes")),
                    guard: None,
                    body: ast_node(ast::Expr::Literal(ast::Literal::String("bytes".to_string()))),
                },
                MatchArm {
                    pattern: Pattern::Wildcard,
                    guard: None,
                    body: ast_node(ast::Expr::Literal(ast::Literal::String("other".to_string()))),
                },
            ]
        ))],
        Ok(vec![RuntimeValue::String("other".to_string())])
    )]
    fn test_eval(
        token_arena: Shared<SharedCell<Arena<Shared<Token>>>>,
        #[case] runtime_values: Vec<RuntimeValue>,
        #[case] program: Program,
        #[case] expected: Result<Vec<RuntimeValue>, InnerError>,
    ) {
        assert_eq!(
            Evaluator::new(DefaultModuleLoader::default(), token_arena).eval(&program, runtime_values.into_iter()),
            expected
        );
    }

    #[test]
    fn test_include() {
        let (temp_dir, temp_file_path) = create_file("test_module.mq", "def func1(): 42; | let val1 = 1");

        defer! {
            if temp_file_path.exists() {
                std::fs::remove_file(&temp_file_path).expect("Failed to delete temp file");
            }
        }

        let loader = ModuleLoader::new(DefaultModuleResolver::new(vec![temp_dir.clone()]));

        let program = vec![
            Shared::new(ast::Node {
                token_id: 0.into(),
                expr: Shared::new(ast::Expr::Include(ast::Literal::String("test_module".to_string()))),
            }),
            Shared::new(ast::Node {
                token_id: 0.into(),
                expr: Shared::new(ast::Expr::Call(IdentWithToken::new("func1"), SmallVec::new())),
            }),
        ];
        assert_eq!(
            Evaluator::new(loader, token_arena())
                .eval(&program, vec![RuntimeValue::String("".to_string())].into_iter()),
            Ok(vec![RuntimeValue::Number(42.into())])
        );
    }

    #[test]
    fn test_import_qualified_access_function() {
        let (temp_dir, temp_file_path) =
            create_file("test_qualified.mq", r#"def greet(name): "Hello, " + name + "!";"#);

        defer! {
            if temp_file_path.exists() {
                std::fs::remove_file(&temp_file_path).expect("Failed to delete temp file");
            }
        }

        let loader = ModuleLoader::new(DefaultModuleResolver::new(vec![temp_dir.clone()]));

        let program = vec![
            Shared::new(ast::Node {
                token_id: 0.into(),
                expr: Shared::new(ast::Expr::Import(ast::Literal::String("test_qualified".to_string()))),
            }),
            Shared::new(ast::Node {
                token_id: 0.into(),
                expr: Shared::new(ast::Expr::QualifiedAccess(
                    vec![IdentWithToken::new("test_qualified")],
                    ast::AccessTarget::Call(
                        IdentWithToken::new("greet"),
                        smallvec![ast_node(ast::Expr::Literal(ast::Literal::String("World".to_string())))],
                    ),
                )),
            }),
        ];
        assert_eq!(
            Evaluator::new(loader, token_arena())
                .eval(&program, vec![RuntimeValue::String("".to_string())].into_iter()),
            Ok(vec![RuntimeValue::String("Hello, World!".to_string())])
        );
    }

    #[test]
    fn test_import_qualified_access_value() {
        let (temp_dir, temp_file_path) = create_file("test_qualified_val.mq", r#"let answer = 42"#);

        defer! {
            if temp_file_path.exists() {
                std::fs::remove_file(&temp_file_path).expect("Failed to delete temp file");
            }
        }

        let loader = ModuleLoader::new(DefaultModuleResolver::new(vec![temp_dir.clone()]));

        let program = vec![
            Shared::new(ast::Node {
                token_id: 0.into(),
                expr: Shared::new(ast::Expr::Import(ast::Literal::String(
                    "test_qualified_val".to_string(),
                ))),
            }),
            Shared::new(ast::Node {
                token_id: 0.into(),
                expr: Shared::new(ast::Expr::QualifiedAccess(
                    vec![IdentWithToken::new("test_qualified_val")],
                    ast::AccessTarget::Ident(IdentWithToken::new("answer")),
                )),
            }),
        ];
        assert_eq!(
            Evaluator::new(loader, token_arena())
                .eval(&program, vec![RuntimeValue::String("".to_string())].into_iter()),
            Ok(vec![RuntimeValue::Number(42.into())])
        );
    }

    /// Builds an AST program that imports `module_name` and accesses `member_name`
    /// via a bare `QualifiedAccess(_, AccessTarget::Ident)` (i.e. without parentheses).
    fn make_paren_free_qa_program(module_name: &str, member_name: &str) -> Program {
        vec![
            Shared::new(ast::Node {
                token_id: 0.into(),
                expr: Shared::new(ast::Expr::Import(ast::Literal::String(module_name.to_string()))),
            }),
            Shared::new(ast::Node {
                token_id: 0.into(),
                expr: Shared::new(ast::Expr::QualifiedAccess(
                    vec![IdentWithToken::new(module_name)],
                    ast::AccessTarget::Ident(IdentWithToken::new(member_name)),
                )),
            }),
        ]
    }

    /// Parameterised tests for paren-free calls via `QualifiedAccess`.
    ///
    /// Verifies that:
    ///   - 0-arg functions are called immediately.
    ///   - 1-arg functions receive the current pipeline value.
    ///   - 1-required + 1-default functions behave like 1-arg.
    ///   - Non-function module members are returned as-is.
    #[rstest]
    // 0-arg user-defined function: called immediately, returns its body.
    #[case::zero_arg_user_fn(
        "qa_pf_zero_arg",
        r#"def greet(): "Hello!";"#,
        "greet",
        RuntimeValue::String("ignored".to_string()),
        Ok(vec![RuntimeValue::String("Hello!".to_string())])
    )]
    // 1-arg user-defined function: pipeline value (5) is bound to the parameter.
    #[case::one_arg_user_fn(
        "qa_pf_one_arg",
        r#"def double(x): x * 2;"#,
        "double",
        RuntimeValue::Number(5.into()),
        Ok(vec![RuntimeValue::Number(10.into())])
    )]
    // 1 required + 1 default param: ≤1 required, so pipeline value (10) is bound.
    #[case::one_required_one_default(
        "qa_pf_one_default",
        r#"def inc(x, step = 1): x + step;"#,
        "inc",
        RuntimeValue::Number(10.into()),
        Ok(vec![RuntimeValue::Number(11.into())])
    )]
    // Non-function module constant: returned unchanged (no auto-call).
    #[case::non_function_constant(
        "qa_pf_non_fn",
        r#"let answer = 42"#,
        "answer",
        RuntimeValue::String("ignored".to_string()),
        Ok(vec![RuntimeValue::Number(42.into())])
    )]
    fn test_import_qualified_access_paren_free(
        token_arena: Shared<SharedCell<Arena<Shared<Token>>>>,
        #[case] module_name: &str,
        #[case] module_content: &str,
        #[case] member_name: &str,
        #[case] input: RuntimeValue,
        #[case] expected: Result<Vec<RuntimeValue>, InnerError>,
    ) {
        let (temp_dir, temp_file_path) = create_file(&format!("{module_name}.mq"), module_content);

        defer! {
            if temp_file_path.exists() {
                std::fs::remove_file(&temp_file_path).expect("Failed to delete temp file");
            }
        }

        let loader = ModuleLoader::new(DefaultModuleResolver::new(vec![temp_dir.clone()]));

        let program = make_paren_free_qa_program(module_name, member_name);
        assert_eq!(
            Evaluator::new(loader, token_arena).eval(&program, vec![input].into_iter()),
            expected
        );
    }

    /// A 2-arg function accessed via QualifiedAccess must NOT be auto-called;
    /// the function value itself should be the pipeline output.
    #[rstest]
    fn test_import_qualified_access_paren_free_skips_multi_arg(token_arena: Shared<SharedCell<Arena<Shared<Token>>>>) {
        let (temp_dir, temp_file_path) = create_file("qa_pf_multi_skip.mq", r#"def add(a, b): a + b;"#);

        defer! {
            if temp_file_path.exists() {
                std::fs::remove_file(&temp_file_path).expect("Failed to delete temp file");
            }
        }

        let loader = ModuleLoader::new(DefaultModuleResolver::new(vec![temp_dir.clone()]));

        let program = make_paren_free_qa_program("qa_pf_multi_skip", "add");
        let result =
            Evaluator::new(loader, token_arena).eval(&program, vec![RuntimeValue::Number(1.into())].into_iter());
        assert!(matches!(result, Ok(ref v) if matches!(v[0], RuntimeValue::Function(_, _, _))));
    }

    #[test]
    fn test_import_qualified_access_with_args() {
        let (temp_dir, temp_file_path) = create_file(
            "test_qualified_math.mq",
            r#"def add2(a, b): a + b;
            def multiply(x, y): x * y;"#,
        );

        defer! {
            if temp_file_path.exists() {
                std::fs::remove_file(&temp_file_path).expect("Failed to delete temp file");
            }
        }

        let loader = ModuleLoader::new(DefaultModuleResolver::new(vec![temp_dir.clone()]));

        let program = vec![
            Shared::new(ast::Node {
                token_id: 0.into(),
                expr: Shared::new(ast::Expr::Import(ast::Literal::String(
                    "test_qualified_math".to_string(),
                ))),
            }),
            Shared::new(ast::Node {
                token_id: 0.into(),
                expr: Shared::new(ast::Expr::QualifiedAccess(
                    vec![IdentWithToken::new("test_qualified_math")],
                    ast::AccessTarget::Call(
                        IdentWithToken::new("add2"),
                        smallvec![
                            ast_node(ast::Expr::Literal(ast::Literal::Number(10.into()))),
                            ast_node(ast::Expr::Literal(ast::Literal::Number(20.into())))
                        ],
                    ),
                )),
            }),
        ];
        assert_eq!(
            Evaluator::new(loader, token_arena())
                .eval(&program, vec![RuntimeValue::String("".to_string())].into_iter()),
            Ok(vec![RuntimeValue::Number(30.into())])
        );
    }

    #[test]
    fn test_import_error() {
        let loader: ModuleLoader = ModuleLoader::default();
        let program = vec![Shared::new(ast::Node {
            token_id: 0.into(),
            expr: Shared::new(ast::Expr::Import(ast::Literal::String("not_found".to_string()))),
        })];
        assert_eq!(
            Evaluator::new(loader, token_arena())
                .eval(&program, vec![RuntimeValue::String("".to_string())].into_iter()),
            Err(InnerError::Runtime(RuntimeError::ModuleLoadError(
                ModuleError::NotFound(Cow::Owned("not_found.mq".to_string()))
            )))
        );
    }

    #[rstest]
    #[case::simple_interpolated_string(
        vec![RuntimeValue::String("world".to_string())],
        vec![
            ast_node(ast::Expr::InterpolatedString(vec![
                ast::StringSegment::Text("Hello, ".to_string()),
                ast::StringSegment::Self_,
                ast::StringSegment::Text("!".to_string()),
            ])),
        ],
        Ok(vec![RuntimeValue::String("Hello, world!".to_string())])
    )]
    #[case::interpolated_string_with_number(
        vec![RuntimeValue::Number(42.into())],
        vec![
            ast_node(ast::Expr::InterpolatedString(vec![
                ast::StringSegment::Text("The answer is ".to_string()),
                ast::StringSegment::Self_,
                ast::StringSegment::Text(".".to_string()),
            ])),
        ],
        Ok(vec![RuntimeValue::String("The answer is 42.".to_string())])
    )]
    #[case::interpolated_string_with_bool(
        vec![RuntimeValue::Boolean(true)],
        vec![
            ast_node(ast::Expr::InterpolatedString(vec![
                ast::StringSegment::Text("Value: ".to_string()),
                ast::StringSegment::Self_,
            ])),
        ],
        Ok(vec![RuntimeValue::String("Value: true".to_string())])
    )]
    #[case::interpolated_string_with_none(
        vec![RuntimeValue::NONE],
        vec![
            ast_node(ast::Expr::InterpolatedString(vec![
                ast::StringSegment::Text("None: ".to_string()),
                ast::StringSegment::Self_,
            ])),
        ],
        Ok(vec![RuntimeValue::String("None: ".to_string())])
    )]
    #[case::interpolated_string_with_array(
        vec![RuntimeValue::Array(vec![
            RuntimeValue::String("a".to_string()),
            RuntimeValue::String("b".to_string()),
        ])],
        vec![
            ast_node(ast::Expr::InterpolatedString(vec![
                ast::StringSegment::Text("Array: ".to_string()),
                ast::StringSegment::Self_,
            ])),
        ],
        Ok(vec![RuntimeValue::String(r#"Array: ["a", "b"]"#.to_string())])
    )]
    #[case::interpolated_string_only_literal(
        vec![RuntimeValue::String("ignored".to_string())],
        vec![
            ast_node(ast::Expr::InterpolatedString(vec![
                ast::StringSegment::Text("Just a string".to_string()),
            ])),
        ],
        Ok(vec![RuntimeValue::String("Just a string".to_string())])
    )]
    #[case::interpolated_string_empty(
        vec![RuntimeValue::String("ignored".to_string())],
        vec![
            ast_node(ast::Expr::InterpolatedString(vec![])),
        ],
        Ok(vec![RuntimeValue::String("".to_string())])
    )]
    #[case::interpolated_string_with_env_var(
        vec![RuntimeValue::String("ignored".to_string())],
        vec![
            ast_node(ast::Expr::InterpolatedString(vec![
                ast::StringSegment::Text("HOME: ".to_string()),
                ast::StringSegment::Env("HOME".into()),
            ])),
        ],
        {
            unsafe { std::env::set_var("HOME", "/home/testuser") };
            Ok(vec![RuntimeValue::String("HOME: /home/testuser".to_string())])
        }
    )]
    #[case::interpolated_string_with_missing_env_var(
        vec![RuntimeValue::String("ignored".to_string())],
        vec![
            ast_node(ast::Expr::InterpolatedString(vec![
                ast::StringSegment::Text("MISSING: ".to_string()),
                ast::StringSegment::Env("MQ_TEST_MISSING_ENV".into()),
            ])),
        ],
        {
            unsafe { std::env::remove_var("MQ_TEST_MISSING_ENV") };
            Err(RuntimeError::EnvNotFound(
                Token {
                    range: Range::default(),
                    kind: TokenKind::Eof,
                    module_id: 1.into(),
                },
                "MQ_TEST_MISSING_ENV".into(),
            ).into())
        }
    )]
    #[case::interpolated_string_env_and_self(
        vec![RuntimeValue::String("value".to_string())],
        vec![
            ast_node(ast::Expr::InterpolatedString(vec![
                ast::StringSegment::Env("USER".into()),
                ast::StringSegment::Text(":".to_string()),
                ast::StringSegment::Self_,
            ])),
        ],
        {
            unsafe { std::env::set_var("USER", "tester") };
            Ok(vec![RuntimeValue::String("tester:value".to_string())])
        }
    )]
    #[case::interpolated_string_env_only(
        vec![RuntimeValue::String("ignored".to_string())],
        vec![
            ast_node(ast::Expr::InterpolatedString(vec![
                ast::StringSegment::Env("USER".into()),
            ])),
        ],
        {
            unsafe { std::env::set_var("USER", "tester") };
            Ok(vec![RuntimeValue::String("tester".to_string())])
        }
    )]
    #[case::interpolated_string_env_and_literal(
        vec![RuntimeValue::String("ignored".to_string())],
        vec![
            ast_node(ast::Expr::InterpolatedString(vec![
                ast::StringSegment::Text("User: ".to_string()),
                ast::StringSegment::Env("USER".into()),
                ast::StringSegment::Text("!".to_string()),
            ])),
        ],
        {
            unsafe { std::env::set_var("USER", "tester") };
            Ok(vec![RuntimeValue::String("User: tester!".to_string())])
        }
    )]
    #[case::interpolated_string_with_expr_literal(
        vec![RuntimeValue::String("ignored".to_string())],
        vec![
            ast_node(ast::Expr::InterpolatedString(vec![
                ast::StringSegment::Text("Value: ".to_string()),
                ast::StringSegment::Expr(ast_node(ast::Expr::Literal(ast::Literal::Number(42.into())))),
            ])),
        ],
        Ok(vec![RuntimeValue::String("Value: 42".to_string())])
    )]
    #[case::interpolated_string_with_expr_string(
        vec![RuntimeValue::String("ignored".to_string())],
        vec![
            ast_node(ast::Expr::InterpolatedString(vec![
                ast::StringSegment::Text("Result: ".to_string()),
                ast::StringSegment::Expr(ast_node(ast::Expr::Literal(ast::Literal::String("hello".to_string())))),
            ])),
        ],
        Ok(vec![RuntimeValue::String("Result: hello".to_string())])
    )]
    #[case::interpolated_string_with_expr_call(
        vec![RuntimeValue::Number(10.into())],
        vec![
            ast_node(ast::Expr::InterpolatedString(vec![
                ast::StringSegment::Text("Doubled: ".to_string()),
                ast::StringSegment::Expr(ast_call("add", smallvec![ast_node(ast::Expr::Self_), ast_node(ast::Expr::Self_)])),
            ])),
        ],
        Ok(vec![RuntimeValue::String("Doubled: 20".to_string())])
    )]
    #[case::interpolated_string_with_multiple_exprs(
        vec![RuntimeValue::Number(5.into())],
        vec![
            ast_node(ast::Expr::InterpolatedString(vec![
                ast::StringSegment::Text("Value: ".to_string()),
                ast::StringSegment::Expr(ast_node(ast::Expr::Self_)),
                ast::StringSegment::Text(", Squared: ".to_string()),
                ast::StringSegment::Expr(ast_call("mul", smallvec![ast_node(ast::Expr::Self_), ast_node(ast::Expr::Self_)])),
            ])),
        ],
        Ok(vec![RuntimeValue::String("Value: 5, Squared: 25".to_string())])
    )]
    #[case::interpolated_string_with_expr_and_self(
        vec![RuntimeValue::String("world".to_string())],
        vec![
            ast_node(ast::Expr::InterpolatedString(vec![
                ast::StringSegment::Expr(ast_node(ast::Expr::Literal(ast::Literal::String("Hello".to_string())))),
                ast::StringSegment::Text(", ".to_string()),
                ast::StringSegment::Self_,
                ast::StringSegment::Text("!".to_string()),
            ])),
        ],
        Ok(vec![RuntimeValue::String("Hello, world!".to_string())])
    )]
    #[case::interpolated_string_with_expr_bool(
        vec![RuntimeValue::String("ignored".to_string())],
        vec![
            ast_node(ast::Expr::InterpolatedString(vec![
                ast::StringSegment::Text("Is true: ".to_string()),
                ast::StringSegment::Expr(ast_node(ast::Expr::Literal(ast::Literal::Bool(true)))),
            ])),
        ],
        Ok(vec![RuntimeValue::String("Is true: true".to_string())])
    )]
    fn test_interpolated_string_eval(
        token_arena: Shared<SharedCell<Arena<Shared<Token>>>>,
        #[case] runtime_values: Vec<RuntimeValue>,
        #[case] program: Program,
        #[case] expected: Result<Vec<RuntimeValue>, InnerError>,
    ) {
        assert_eq!(
            Evaluator::new(DefaultModuleLoader::default(), token_arena).eval(&program, runtime_values.into_iter()),
            expected
        );
    }

    #[test]
    fn test_expand_macro_with_module() {
        let (temp_dir, temp_file_path) = create_file(
            "test_macro.mq",
            r#"
            macro add_macro(a, b) do
              quote do
                unquote(a + b)
              end
            end"#,
        );

        defer! {
            if temp_file_path.exists() {
                std::fs::remove_file(&temp_file_path).expect("Failed to delete temp file");
            }
        }

        let loader = ModuleLoader::new(DefaultModuleResolver::new(vec![temp_dir.clone()]));

        let program = vec![
            Shared::new(ast::Node {
                token_id: 0.into(),
                expr: Shared::new(ast::Expr::Import(ast::Literal::String("test_macro".to_string()))),
            }),
            Shared::new(ast::Node {
                token_id: 0.into(),
                expr: Shared::new(ast::Expr::Call(
                    IdentWithToken::new("add_macro"),
                    smallvec![
                        ast_node(ast::Expr::Literal(ast::Literal::Number(10.into()))),
                        ast_node(ast::Expr::Literal(ast::Literal::Number(20.into()))),
                    ],
                )),
            }),
        ];
        assert_eq!(
            Evaluator::new(loader, token_arena())
                .eval(&program, vec![RuntimeValue::String("".to_string())].into_iter()),
            Ok(vec![RuntimeValue::Number(30.into())])
        );
    }

    #[test]
    fn test_expand_macro_with_def_in_module() {
        let (temp_dir, temp_file_path) = create_file(
            "test_macro2.mq",
            r#"
            macro add_macro(a, b) do
              quote do
                unquote(a + b)
              end
            end | def a(): add_macro(10, 20);"#,
        );

        defer! {
            if temp_file_path.exists() {
                std::fs::remove_file(&temp_file_path).expect("Failed to delete temp file");
            }
        }

        let loader = ModuleLoader::new(DefaultModuleResolver::new(vec![temp_dir.clone()]));

        let program = vec![
            Shared::new(ast::Node {
                token_id: 0.into(),
                expr: Shared::new(ast::Expr::Import(ast::Literal::String("test_macro2".to_string()))),
            }),
            Shared::new(ast::Node {
                token_id: 0.into(),
                expr: Shared::new(ast::Expr::QualifiedAccess(
                    vec![IdentWithToken::new("test_macro2")],
                    ast::AccessTarget::Call(IdentWithToken::new("a"), smallvec![]),
                )),
            }),
        ];
        assert_eq!(
            Evaluator::new(loader, token_arena())
                .eval(&program, vec![RuntimeValue::String("".to_string())].into_iter()),
            Ok(vec![RuntimeValue::Number(30.into())])
        );
    }

    #[test]
    fn test_default_params_with_all_args() {
        // Test: def greet(name, greeting = "Hello"): greeting with greet("Alice", "Hi")
        let params = smallvec![
            ast::Param::new(IdentWithToken::new("name")),
            ast::Param::with_default(
                IdentWithToken::new("greeting"),
                Some(ast_node(ast::Expr::Literal(ast::Literal::String("Hello".to_string()))))
            ),
        ];
        let fn_body = vec![ast_node(ast::Expr::Ident(IdentWithToken::new("greeting")))];

        let program = vec![
            ast_node(ast::Expr::Def(IdentWithToken::new("greet"), params, fn_body)),
            ast_call(
                "greet",
                smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::String("Alice".to_string()))),
                    ast_node(ast::Expr::Literal(ast::Literal::String("Hi".to_string())))
                ],
            ),
        ];

        let result = Evaluator::new(DefaultModuleLoader::default(), token_arena())
            .eval(&program, vec![RuntimeValue::String("test".to_string())].into_iter());

        assert_eq!(result, Ok(vec![RuntimeValue::String("Hi".to_string())]));
    }

    #[test]
    fn test_default_params_with_default() {
        // Test: def greet(name, greeting = "Hello"): greeting with greet("Alice")
        let params = smallvec![
            ast::Param::new(IdentWithToken::new("name")),
            ast::Param::with_default(
                IdentWithToken::new("greeting"),
                Some(ast_node(ast::Expr::Literal(ast::Literal::String("Hello".to_string()))))
            ),
        ];
        let fn_body = vec![ast_node(ast::Expr::Ident(IdentWithToken::new("greeting")))];

        let program = vec![
            ast_node(ast::Expr::Def(IdentWithToken::new("greet"), params, fn_body)),
            ast_call(
                "greet",
                smallvec![ast_node(ast::Expr::Literal(ast::Literal::String("Alice".to_string())))],
            ),
        ];

        let result = Evaluator::new(DefaultModuleLoader::default(), token_arena())
            .eval(&program, vec![RuntimeValue::String("test".to_string())].into_iter());

        assert_eq!(result, Ok(vec![RuntimeValue::String("Hello".to_string())]));
    }

    #[test]
    fn test_default_params_with_self() {
        // Test: def format(prefix = "[LOG]"): [prefix, self] with "message" | format()
        let params = smallvec![
            ast::Param::new(IdentWithToken::new("self")),
            ast::Param::with_default(
                IdentWithToken::new("prefix"),
                Some(ast_node(ast::Expr::Literal(ast::Literal::String("[LOG]".to_string()))))
            ),
        ];
        let fn_body = vec![ast_node(ast::Expr::Call(
            IdentWithToken::new("array"),
            smallvec![
                ast_node(ast::Expr::Ident(IdentWithToken::new("prefix"))),
                ast_node(ast::Expr::Ident(IdentWithToken::new("self")))
            ],
        ))];

        let program = vec![
            ast_node(ast::Expr::Def(IdentWithToken::new("format"), params, fn_body)),
            ast_call("format", smallvec![]),
        ];

        let result = Evaluator::new(DefaultModuleLoader::default(), token_arena())
            .eval(&program, vec![RuntimeValue::String("message".to_string())].into_iter());

        assert_eq!(
            result,
            Ok(vec![RuntimeValue::Array(vec![
                RuntimeValue::String("[LOG]".to_string()),
                RuntimeValue::String("message".to_string())
            ])])
        );
    }

    #[test]
    fn test_multiple_default_params() {
        // Test: def msg(a, b = 2, c = 3): [a, b, c] with msg(1)
        let params = smallvec![
            ast::Param::new(IdentWithToken::new("a")),
            ast::Param::with_default(
                IdentWithToken::new("b"),
                Some(ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))))
            ),
            ast::Param::with_default(
                IdentWithToken::new("c"),
                Some(ast_node(ast::Expr::Literal(ast::Literal::Number(3.into()))))
            ),
        ];
        let fn_body = vec![ast_node(ast::Expr::Call(
            IdentWithToken::new("array"),
            smallvec![
                ast_node(ast::Expr::Ident(IdentWithToken::new("a"))),
                ast_node(ast::Expr::Ident(IdentWithToken::new("b"))),
                ast_node(ast::Expr::Ident(IdentWithToken::new("c")))
            ],
        ))];

        let program = vec![
            ast_node(ast::Expr::Def(IdentWithToken::new("msg"), params, fn_body)),
            ast_call(
                "msg",
                smallvec![ast_node(ast::Expr::Literal(ast::Literal::Number(1.into())))],
            ),
        ];

        let result = Evaluator::new(DefaultModuleLoader::default(), token_arena())
            .eval(&program, vec![RuntimeValue::String("test".to_string())].into_iter());

        assert_eq!(
            result,
            Ok(vec![RuntimeValue::Array(vec![
                RuntimeValue::Number(1.into()),
                RuntimeValue::Number(2.into()),
                RuntimeValue::Number(3.into())
            ])])
        );
    }

    #[test]
    fn test_multiple_default_params_partial() {
        // Test: def msg(a, b = 2, c = 3): [a, b, c] with msg(1, 4)
        let params = smallvec![
            ast::Param::new(IdentWithToken::new("a")),
            ast::Param::with_default(
                IdentWithToken::new("b"),
                Some(ast_node(ast::Expr::Literal(ast::Literal::Number(2.into()))))
            ),
            ast::Param::with_default(
                IdentWithToken::new("c"),
                Some(ast_node(ast::Expr::Literal(ast::Literal::Number(3.into()))))
            ),
        ];
        let fn_body = vec![ast_node(ast::Expr::Call(
            IdentWithToken::new("array"),
            smallvec![
                ast_node(ast::Expr::Ident(IdentWithToken::new("a"))),
                ast_node(ast::Expr::Ident(IdentWithToken::new("b"))),
                ast_node(ast::Expr::Ident(IdentWithToken::new("c")))
            ],
        ))];

        let program = vec![
            ast_node(ast::Expr::Def(IdentWithToken::new("msg"), params, fn_body)),
            ast_call(
                "msg",
                smallvec![
                    ast_node(ast::Expr::Literal(ast::Literal::Number(1.into()))),
                    ast_node(ast::Expr::Literal(ast::Literal::Number(4.into())))
                ],
            ),
        ];

        let result = Evaluator::new(DefaultModuleLoader::default(), token_arena())
            .eval(&program, vec![RuntimeValue::String("test".to_string())].into_iter());

        assert_eq!(
            result,
            Ok(vec![RuntimeValue::Array(vec![
                RuntimeValue::Number(1.into()),
                RuntimeValue::Number(4.into()),
                RuntimeValue::Number(3.into())
            ])])
        );
    }

    #[cfg(feature = "http-import")]
    #[rstest]
    #[case(
        "include_http_in_module",
        r#"include "https://example.com/dep.mq""#,
        "https://example.com/dep.mq"
    )]
    #[case(
        "include_github_in_module",
        r#"include "github.com/alice/dep""#,
        "github.com/alice/dep"
    )]
    fn test_include_http_inside_module_is_blocked(
        #[case] module_name: &str,
        #[case] module_content: &str,
        #[case] blocked_url: &str,
    ) {
        let file_name = format!("{module_name}.mq");
        let (temp_dir, temp_file_path) = create_file(&file_name, module_content);

        defer! {
            if temp_file_path.exists() {
                std::fs::remove_file(&temp_file_path).expect("Failed to delete temp file");
            }
        }

        let loader = ModuleLoader::new(DefaultModuleResolver::new(vec![temp_dir.clone()]));
        let program = vec![Shared::new(ast::Node {
            token_id: 0.into(),
            expr: Shared::new(ast::Expr::Include(ast::Literal::String(module_name.to_string()))),
        })];

        assert!(
            matches!(
                Evaluator::new(loader, token_arena())
                    .eval(&program, vec![RuntimeValue::String("".to_string())].into_iter()),
                Err(InnerError::Runtime(RuntimeError::ModuleLoadError(
                    ModuleError::HttpImportNotAllowed(ref url)
                ))) if url.as_ref() == blocked_url
            ),
            "expected HttpImportNotAllowed for '{blocked_url}'"
        );
    }

    #[cfg(feature = "http-import")]
    #[rstest]
    #[case(
        "import_http_in_module",
        r#"import "https://example.com/dep.mq""#,
        "https://example.com/dep.mq"
    )]
    #[case(
        "import_github_in_module",
        r#"import "github.com/alice/dep""#,
        "github.com/alice/dep"
    )]
    fn test_import_http_inside_module_is_blocked(
        #[case] module_name: &str,
        #[case] module_content: &str,
        #[case] blocked_url: &str,
    ) {
        let file_name = format!("{module_name}.mq");
        let (temp_dir, temp_file_path) = create_file(&file_name, module_content);

        defer! {
            if temp_file_path.exists() {
                std::fs::remove_file(&temp_file_path).expect("Failed to delete temp file");
            }
        }

        let loader = ModuleLoader::new(DefaultModuleResolver::new(vec![temp_dir.clone()]));
        let program = vec![Shared::new(ast::Node {
            token_id: 0.into(),
            expr: Shared::new(ast::Expr::Include(ast::Literal::String(module_name.to_string()))),
        })];

        assert!(
            matches!(
                Evaluator::new(loader, token_arena())
                    .eval(&program, vec![RuntimeValue::String("".to_string())].into_iter()),
                Err(InnerError::Runtime(RuntimeError::ModuleLoadError(
                    ModuleError::HttpImportNotAllowed(ref url)
                ))) if url.as_ref() == blocked_url
            ),
            "expected HttpImportNotAllowed for '{blocked_url}'"
        );
    }
}

/// Implementation of MacroEvaluator trait for Evaluator.
/// This allows the macro expander to evaluate macro bodies during collection.
impl<T: ModuleResolver> MacroEvaluator for Evaluator<T> {
    fn eval_macro_body(&mut self, body: &Shared<ast::Node>, _token_id: TokenId) -> Result<RuntimeValue, RuntimeError> {
        let value = self.eval_macro(body);

        // If the result is already an AST (from quote), return it as-is
        // If the result is None (e.g., from if(false) without else), return None to indicate removal
        // Otherwise, wrap the body itself as an AST (for macros without quote)
        match value {
            Ok(RuntimeValue::Ast(ast)) => Ok(RuntimeValue::Ast(ast)),
            Ok(RuntimeValue::None) => {
                // Return None instead of empty block - this will cause the macro to be skipped
                Ok(RuntimeValue::None)
            }
            Ok(_) | Err(_) => {
                // Return the body as AST, not the evaluated result
                // This allows macros without quote to work
                Ok(RuntimeValue::Ast(Shared::clone(body)))
            }
        }
    }
}

#[cfg(test)]
#[cfg(all(feature = "debugger", feature = "sync"))]
mod debugger_tests {
    use rstest::{fixture, rstest};
    use smallvec::SmallVec;

    use super::*;
    use crate::ast::node::Args;
    use crate::eval::debugger::{DebugContext, DebuggerHandler};
    use crate::{AstNode, DebuggerAction, IdentWithToken, ModuleLoader, Range, token_alloc};

    #[fixture]
    fn token_arena() -> Shared<SharedCell<Arena<Shared<Token>>>> {
        let token_arena = Shared::new(SharedCell::new(Arena::new(10)));

        token_alloc(
            &token_arena,
            &Shared::new(Token {
                kind: TokenKind::Eof,
                range: Range::default(),
                module_id: 1.into(),
            }),
        );

        token_arena
    }

    fn ast_call(name: &str, args: Args) -> Shared<AstNode> {
        Shared::new(AstNode {
            token_id: 0.into(),
            expr: Shared::new(ast::Expr::Call(IdentWithToken::new(name), args)),
        })
    }

    #[derive(Debug)]
    struct TestDebuggerHandler {
        breakpoints_hit: Shared<SharedCell<Vec<String>>>,
        steps_taken: Shared<SharedCell<Vec<String>>>,
        next_action: DebuggerAction,
    }

    impl TestDebuggerHandler {
        fn new(action: DebuggerAction) -> Self {
            Self {
                breakpoints_hit: Shared::new(SharedCell::new(Vec::new())),
                steps_taken: Shared::new(SharedCell::new(Vec::new())),
                next_action: action,
            }
        }
    }

    impl DebuggerHandler for TestDebuggerHandler {
        fn on_breakpoint_hit(
            &self,
            _breakpoint: &crate::eval::debugger::Breakpoint,
            context: &DebugContext,
        ) -> DebuggerAction {
            self.breakpoints_hit
                .write()
                .unwrap()
                .push(format!("breakpoint:{}", context.current_value));
            self.next_action.clone()
        }

        fn on_step(&self, context: &DebugContext) -> DebuggerAction {
            self.steps_taken
                .write()
                .unwrap()
                .push(format!("step:{}", context.current_value));
            self.next_action.clone()
        }
    }

    #[rstest]
    fn test_eval_debugger_breakpoint_call(token_arena: Shared<SharedCell<Arena<Shared<Token>>>>) {
        let handler = Shared::new(SharedCell::new(
            Box::new(TestDebuggerHandler::new(DebuggerAction::Continue)) as Box<dyn DebuggerHandler>,
        ));

        let mut evaluator: Evaluator = Evaluator::new(ModuleLoader::default(), token_arena);
        evaluator.debugger_handler = Shared::clone(&handler);

        let program = vec![ast_call(constants::builtins::BREAKPOINT, SmallVec::new())];
        let runtime_values = vec![RuntimeValue::String("test".to_string())];

        let result = evaluator.eval(&program, runtime_values.into_iter());

        assert_eq!(result, Ok(vec![RuntimeValue::String("test".to_string())]));
    }
}
