luallaby 0.1.0

**Work in progress** A pure-Rust Lua interpreter/compiler
Documentation
use std::cell::RefCell;
use std::mem;
use std::rc::Rc;

use crate::{
    value::{FileHandle, FuncBuiltin, FuncBuiltinRaw, FuncDef, Numeric, Table, Thread},
    Error, LuaError, Result, Value, VM,
};

impl VM {
    pub fn arg_len(&self) -> usize {
        self.frames.last().unwrap().varargs.len()
    }

    pub fn arg(&mut self, idx: usize) -> Result<Value> {
        let args = &mut self.frames.last_mut().unwrap().varargs;
        if idx < args.len() {
            Ok(mem::replace(&mut args[idx], Value::Nil))
        } else {
            Err(self.arg_error(idx, Error::from_lua(LuaError::ArgumentExpected)))
        }
    }

    pub fn arg_or_nil(&mut self, idx: usize) -> Value {
        let args = &mut self.frames.last_mut().unwrap().varargs;
        if idx < args.len() {
            mem::replace(&mut args[idx], Value::Nil)
        } else {
            Value::Nil
        }
    }

    pub fn arg_opt(&mut self, idx: usize) -> Option<Value> {
        let args = &mut self.frames.last_mut().unwrap().varargs;
        if idx < args.len() {
            Some(mem::replace(&mut args[idx], Value::Nil))
        } else {
            None
        }
    }

    pub fn arg_split(&mut self, at: usize) -> Vec<Value> {
        let args = &mut self.frames.last_mut().unwrap().varargs;
        if at > args.len() {
            vec![]
        } else {
            args.split_off(at)
        }
    }

    pub fn arg_error(&self, idx: usize, err: Error) -> Error {
        let (name, namewhat) = self.call_info(&self.frames, self.frames.len() - 1);
        match namewhat {
            "method" if idx == 0 => err.map_lua_error(|l| {
                LuaError::ArgumentBadSelf(
                    String::from_utf8_lossy(&name.unwrap_or_default()).into_owned(),
                    Box::new(l),
                )
            }),
            _ => {
                let idx = match namewhat {
                    "method" => idx - 1,
                    _ => idx,
                };
                let name = match &*self.frames.last().unwrap().func_def {
                    FuncDef::Builtin(FuncBuiltin { module, name, .. })
                    | FuncDef::BuiltinRaw(FuncBuiltinRaw { module, name, .. }) => {
                        Some(if module.is_empty() {
                            name.to_string()
                        } else {
                            format!("{}.{}", module, name)
                        })
                    }
                    FuncDef::Defined(..) => name.map(|n| String::from_utf8_lossy(&n).into_owned()),
                };
                err.map_lua_error(|l| LuaError::ArgumentBad(idx, name, Box::new(l)))
            }
        }
    }

    pub fn arg_number_coerce(&mut self, idx: usize) -> Result<Numeric> {
        self.arg(idx)?
            .to_number_coerce()
            .map_err(|e| self.arg_error(idx, e))
    }

    pub fn arg_int_coerce(&mut self, idx: usize) -> Result<i64> {
        self.arg(idx)?
            .to_int_coerce()
            .map_err(|e| self.arg_error(idx, e))
    }

    pub fn arg_float_coerce(&mut self, idx: usize) -> Result<f64> {
        self.arg(idx)?
            .to_float_coerce()
            .map_err(|e| self.arg_error(idx, e))
    }

    pub fn arg_string(&mut self, idx: usize) -> Result<Vec<u8>> {
        self.arg(idx)?
            .into_string()
            .map_err(|e| self.arg_error(idx, e))
    }

    pub fn arg_string_coerce(&mut self, idx: usize) -> Result<Vec<u8>> {
        self.arg(idx)?
            .to_string_coerce()
            .map_err(|e| self.arg_error(idx, e))
    }

    pub fn arg_func(&mut self, idx: usize) -> Result<Rc<FuncDef>> {
        self.arg(idx)?.to_func().map_err(|e| self.arg_error(idx, e))
    }

    pub fn arg_table(&mut self, idx: usize) -> Result<Rc<RefCell<Table>>> {
        self.arg(idx)?
            .to_table()
            .map_err(|e| self.arg_error(idx, e))
    }

    pub fn arg_thread(&mut self, idx: usize) -> Result<Rc<RefCell<Thread>>> {
        self.arg(idx)?
            .to_thread()
            .map_err(|e| self.arg_error(idx, e))
    }

    pub fn arg_file(&mut self, idx: usize) -> Result<Rc<RefCell<FileHandle>>> {
        self.arg(idx)?.to_file().map_err(|e| self.arg_error(idx, e))
    }
}