luallaby 0.1.0

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

mod basic;
mod coroutine;
mod debug;
pub(super) mod dump;
mod file;
mod io;
mod math;
pub(super) mod modules;
mod os;
mod string;
mod table;
mod utf8;

use crate::{
    value::{CallResult, FuncBuiltin, FuncBuiltinRaw, FuncDef, Table},
    Result, Value, LUA_VERSION, VM,
};

pub struct StdLib(Rc<RefCell<Table>>);

impl StdLib {
    fn new() -> Result<Self> {
        let tbl = Rc::new(RefCell::new(Table::default()));

        // Initialize package and loaded
        let package = Rc::new(RefCell::new(Table::default()));
        let loaded = Rc::new(RefCell::new(Table::default()));
        package
            .borrow_mut()
            .set(Value::str("loaded"), Value::Table(loaded.clone()))?;
        loaded
            .borrow_mut()
            .set(Value::str("package"), Value::Table(package.clone()))?;
        tbl.borrow_mut()
            .set(Value::str("package"), Value::Table(package.clone()))?;

        Ok(Self(tbl))
    }

    pub fn build() -> Result<Rc<RefCell<Table>>> {
        let mut stdlib = Self::new()?;

        let (major, minor) = LUA_VERSION;
        stdlib
            .root()
            .cons(
                "_VERSION",
                Value::string(format!("Lua {}.{}", major, minor)),
            )?
            .cons("_ENV", Value::Table(stdlib.0.clone()))?
            .cons("_G", Value::Table(stdlib.0.clone()))?
            .cons("arg", Value::Table(Rc::new(RefCell::new(Table::default()))))?;

        basic::module(&mut stdlib)?;
        coroutine::module(&mut stdlib)?;
        debug::module(&mut stdlib)?;
        io::module(&mut stdlib)?;
        math::module(&mut stdlib)?;
        modules::module(&mut stdlib)?;
        os::module(&mut stdlib)?;
        string::module(&mut stdlib)?;
        table::module(&mut stdlib)?;
        utf8::module(&mut stdlib)?;

        Ok(stdlib.0)
    }

    pub fn root(&mut self) -> Module {
        Module {
            name: "",
            tbl: self.0.clone(),
        }
    }

    pub fn module(&mut self, module: &'static str) -> Module {
        let name = Value::str(module);
        let val = self.0.borrow().get(&name);
        match val {
            Value::Table(tbl) => Module {
                name: module,
                tbl: tbl.clone(),
            },
            _ => {
                let tbl = Rc::new(RefCell::new(Table::default()));
                self.0
                    .borrow_mut()
                    .set(name.clone(), Value::Table(tbl.clone()))
                    .unwrap();

                let pkg = self
                    .0
                    .borrow()
                    .get(&Value::str("package"))
                    .to_table()
                    .unwrap();
                let loaded = pkg.borrow().get(&Value::str("loaded")).to_table().unwrap();
                loaded
                    .borrow_mut()
                    .set(name, Value::Table(tbl.clone()))
                    .unwrap();

                Module { name: module, tbl }
            }
        }
    }
}

#[derive(Default)]
pub struct Module {
    name: &'static str,
    tbl: Rc<RefCell<Table>>,
}

impl Module {
    pub fn cons(&mut self, name: &'static str, cons: Value) -> Result<&mut Self> {
        self.tbl.borrow_mut().set(Value::str(name), cons)?;
        Ok(self)
    }

    pub fn func(
        &mut self,
        name: &'static str,
        func: impl Fn(&mut VM) -> Result<Value> + 'static,
    ) -> Result<&mut Self> {
        self.tbl.borrow_mut().set(
            Value::str(name),
            Value::Func(Rc::new(FuncDef::Builtin(FuncBuiltin {
                module: self.name,
                name,
                func: Rc::new(func),
            }))),
        )?;
        Ok(self)
    }

    pub fn func_raw(
        &mut self,
        name: &'static str,
        func: impl Fn(&mut VM) -> Result<CallResult> + 'static,
    ) -> Result<&mut Self> {
        self.tbl.borrow_mut().set(
            Value::str(name),
            Value::Func(Rc::new(FuncDef::BuiltinRaw(FuncBuiltinRaw {
                module: self.name,
                name,
                func: Rc::new(func),
            }))),
        )?;
        Ok(self)
    }
}