neige-lua 0.1.3

一个简单的lua运行时
Documentation
use std::panic::{catch_unwind, AssertUnwindSafe};

use crate::{api::AccessApi, state::LuaState, value::value::LuaValue};

use super::CallApi;

pub trait MiscApi {
    fn len(&mut self, idx: isize);
    fn concat(&mut self, n: usize);
    fn next(&mut self, idx: isize) -> bool;
    fn error(&mut self) -> isize;
    fn pcall(&mut self, n_args: isize, n_results: isize, msg: isize) -> isize;
}

impl MiscApi for LuaState {
    fn len(&mut self, idx: isize) {
        let val = self.stack_get(idx);
        match val {
            LuaValue::Str(s) => self.stack_push(LuaValue::Integer(s.len() as i64)),
            LuaValue::Table(tb) => self.stack_push(LuaValue::Integer(tb.len() as i64)),
            _ => {
                let result = self.call_meta_method(val.clone(), val, "__len");
                if let Some(LuaValue::Integer(i)) = result {
                    self.stack_push(LuaValue::Integer(i))
                } else {
                    panic!("length error!")
                }
            }
        }
    }

    fn concat(&mut self, n: usize) {
        if n == 0 {
            self.stack_push(LuaValue::Str("".into()))
        } else if n >= 2 {
            for _ in 1..n {
                if self.is_string(-1) && self.is_string(-2) {
                    let s2 = self.to_string(-1);
                    let s1 = self.to_string(-2);
                    self.stack_pop();
                    self.stack_pop();
                    self.stack_push(LuaValue::Str(format!("{}{}", s1, s2)));
                    continue;
                }
                let b = self.stack_pop();
                let a = self.stack_pop();
                let result = self.call_meta_method(a, b, "__concat");
                if let Some(r) = result {
                    self.stack_push(r);
                    continue;
                }
                panic!("concatenation error!")
            }
        }
    }

    fn next(&mut self, idx: isize) -> bool {
        let val = self.stack_get(idx);
        if let LuaValue::Table(tbl) = val {
            let key = self.stack_pop();
            let next_key = tbl.next_key(&key);
            if !next_key.is_nil() {
                let next_val = tbl.get(&next_key);
                self.stack_push(next_key);
                self.stack_push(next_val);
                true
            } else {
                false
            }
        } else {
            panic!("table expected!")
        }
    }

    fn error(&mut self) -> isize {
        let val = self.stack_pop();
        panic!("{:?}", val)
    }

    fn pcall(&mut self, n_args: isize, n_results: isize, msgh: isize) -> isize {
        let node = self.get_node().clone();
        let caller = node.get_stack();
        let mut status = 2;

        let result = catch_unwind(AssertUnwindSafe(|| {
            self.call(n_args, n_results);
            status = 0;
        }));
        if let Err(err) = result {
            if msgh != 0 {
                panic!("{:?}", err);
            }
            let stack = node.get_stack();
            while *stack != *caller {
                self.pop_lua_stack()
            }
            let err_msg = if let Some(err_msg) = err.downcast_ref::<&str>() {
                err_msg.to_string()
            } else if let Some(err_msg) = err.downcast_ref::<String>() {
                err_msg.clone()
            } else {
                "Unknown error".to_string()
            };
            self.stack_push(LuaValue::Str(err_msg))
        }
        status
    }
}