luallaby 0.1.0-alpha.3

**Work in progress** A pure-Rust Lua interpreter/compiler
Documentation
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::rc::Rc;

use crate::error::{LuaError, Result};
use crate::num::{from_str_radix_wrapping, parse_f64_dec, parse_f64_hex};
use crate::vm::{StackFrame, VM};

#[derive(Clone, Debug)]
pub enum Value {
    Nil,
    Bool(bool),
    Number(Numeric),
    String(String),
    Func(Rc<FuncDef>),
    Table(Rc<RefCell<Table>>),
    LUData(*const u8),
    Thread(Rc<RefCell<Thread>>),
    Mult(Vec<Value>), // Used for varargs
}

#[derive(Clone, Copy, Debug)]
pub enum ValueType {
    Nil,
    Bool,
    Number,
    String,
    Func,
    Table,
    UData,
    Thread,
}

#[derive(Clone, Debug)]
pub enum Numeric {
    Integer(i64),
    Float(f64),
}

#[derive(Clone)]
pub enum FuncDef {
    Builtin(FuncBuiltin),
    Defined(FuncClosure),
    Protected(bool),
    Yield,
}

#[derive(Clone, Debug)]
pub struct FuncObject {
    pub regs: usize,
    pub locals_cap: usize,
    pub params: Vec<String>,
    pub ups: Vec<VarType>,
    pub varargs: bool,
    pub chunk: usize,
}

#[derive(Clone)]
pub struct FuncClosure {
    pub regs: usize,
    pub locals_cap: usize,
    pub params: Vec<String>,
    pub ups: Vec<(Rc<RefCell<Value>>, String)>,
    pub varargs: bool,
    pub chunk: usize,
}

#[derive(Clone)]
pub struct FuncBuiltin {
    pub name: &'static str,
    pub func: Rc<dyn Fn(&mut VM, Vec<Value>) -> Result<Value>>,
}

#[derive(Clone, Copy, Debug)]
pub enum LocalType {
    Local,
    Param,
}

#[derive(Clone, Copy, Debug)]
pub enum VarType {
    Local(usize),
    Up(usize),
}

// TODO: Split map into list part (elements indexed by positive integers) and hashmap part
#[derive(Clone, Debug, Default)]
pub struct Table {
    map: HashMap<Value, Value>,
    meta: Option<Rc<RefCell<Table>>>,
    next_key: Option<HashMap<Value, Value>>,
}

#[derive(Debug)]
pub struct Thread {
    pub state: ThreadState,
    pub func: Option<Rc<FuncDef>>,
    pub frames: Vec<StackFrame>,
    pub pc: Option<(usize, usize)>,
    pub protected: Option<ProtectedHandler>,
    pub yld_reg: Option<usize>,
    pub error: Option<LuaError>,
}

#[derive(Clone, Copy, Debug)]
pub enum ThreadState {
    Created,
    Suspended,
    Running,
    Normal,
    Dead,
}

#[derive(Clone, Debug)]
pub enum ProtectedHandler {
    PCall,
    XPCall(Rc<FuncDef>),
}

#[derive(Clone, Debug)]
pub enum Source {
    None,
    Field(Value),
    Local(String),
    Global(String),
    Up(String),
}

impl Value {
    pub fn empty() -> Self {
        Self::Mult(Vec::new())
    }

    pub fn empty_cap(cap: usize) -> Self {
        Self::Mult(Vec::with_capacity(cap))
    }

    pub fn int(n: i64) -> Self {
        Self::Number(Numeric::Integer(n))
    }

    pub fn float(f: f64) -> Self {
        Self::Number(Numeric::Float(f))
    }

    pub fn is_falsy(&self) -> bool {
        match self {
            Value::Nil | Value::Bool(false) => true,
            Value::Mult(vec) => vec.first().map(Value::is_falsy).unwrap_or(true),
            _ => false,
        }
    }

    pub fn is_truthy(&self) -> bool {
        !self.is_falsy()
    }

    pub fn value_type(&self) -> ValueType {
        match self {
            Value::Nil => ValueType::Nil,
            Value::Bool(..) => ValueType::Bool,
            Value::Number(..) => ValueType::Number,
            Value::String(..) => ValueType::String,
            Value::Func(..) => ValueType::Func,
            Value::Table(..) => ValueType::Table,
            Value::LUData(..) => ValueType::UData,
            Value::Thread(..) => ValueType::Thread,
            Value::Mult(vec) => match vec.first() {
                Some(val) => val.value_type(),
                None => panic!("empty multi-value"),
            },
        }
    }

    pub fn to_bool(&self) -> Result<bool> {
        match self.to_single() {
            Value::Bool(b) => Ok(*b),
            v => err!(LuaError::ExpectedType(ValueType::Bool, v.value_type())),
        }
    }

    pub fn to_number(&self) -> Result<&Numeric> {
        match self.to_single() {
            Value::Number(n) => Ok(n),
            v => err!(LuaError::ExpectedType(ValueType::Number, v.value_type())),
        }
    }

    pub fn to_number_coerce(&self) -> Result<Numeric> {
        match self.to_single() {
            Value::Number(n) => Ok(n.clone()),
            Value::String(s) => match Numeric::from_str(s) {
                Ok(n) => Ok(n),
                Err(..) => err!(LuaError::ExpectedType(ValueType::Number, ValueType::String)),
            },
            v => err!(LuaError::ExpectedType(ValueType::Number, v.value_type())),
        }
    }

    pub fn to_string(&self) -> Result<&str> {
        match self.to_single() {
            Value::String(s) => Ok(s.as_str()),
            v => err!(LuaError::ExpectedType(ValueType::String, v.value_type())),
        }
    }

    pub fn to_string_coerce(&self) -> Result<String> {
        match self.to_single() {
            Value::String(s) => Ok(s.clone()),
            Value::Number(n) => Ok(format!("{}", n)),
            v => err!(LuaError::ExpectedType(ValueType::String, v.value_type())),
        }
    }

    pub fn to_func(&self) -> Result<Rc<FuncDef>> {
        match self.to_single() {
            Value::Func(f) => Ok(f.clone()),
            v => err!(LuaError::ExpectedType(ValueType::Func, v.value_type())),
        }
    }

    pub fn to_table(&self) -> Result<Rc<RefCell<Table>>> {
        match self.to_single() {
            Value::Table(t) => Ok(t.clone()),
            v => err!(LuaError::ExpectedType(ValueType::Table, v.value_type())),
        }
    }

    pub fn to_thread(&self) -> Result<Rc<RefCell<Thread>>> {
        match self.to_single() {
            Value::Thread(t) => Ok(t.clone()),
            v => err!(LuaError::ExpectedType(ValueType::Thread, v.value_type())),
        }
    }

    // TODO: Make private, conversion should be implicit
    pub fn to_single(&self) -> &Self {
        match self {
            Value::Mult(vec) => vec.first().unwrap_or(&Value::Nil),
            v => v,
        }
    }

    // TODO: Make private, conversion should be implicit
    pub fn into_single(self) -> Self {
        match self {
            Value::Mult(vec) => vec.into_iter().next().unwrap_or(Value::Nil),
            v => v,
        }
    }

    pub fn into_vec(self) -> Vec<Value> {
        match self {
            Value::Mult(vec) => vec,
            v => vec![v],
        }
    }
}

impl fmt::Display for Value {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Value::Nil => write!(f, "nil"),
            Value::Bool(bool) => write!(f, "{}", bool),
            Value::Number(num) => write!(f, "{}", num),
            Value::String(str) => write!(f, "{}", str),
            Value::Func(func) => write!(f, "function: {:?}", Rc::as_ptr(func)),
            Value::Table(tbl) => write!(f, "table: {:?}", Rc::as_ptr(tbl)),
            Value::LUData(ptr) => write!(f, "userdata: {:?}", ptr),
            Value::Thread(thrd) => write!(f, "thread: {:?}", Rc::as_ptr(thrd)),
            Value::Mult(..) => unreachable!(),
        }
    }
}

impl Hash for Value {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match self {
            Value::Nil => state.write_u8(0),
            Value::Bool(bool) => {
                state.write_u8(1);
                bool.hash(state);
            }
            Value::Number(num) => {
                state.write_u8(2);
                num.hash(state);
            }
            Value::String(str) => {
                state.write_u8(3);
                str.hash(state);
            }
            Value::Func(func) => {
                state.write_u8(4);
                Rc::as_ptr(func).hash(state);
            }
            Value::Table(tbl) => {
                state.write_u8(5);
                Rc::as_ptr(tbl).hash(state);
            }
            Value::LUData(ptr) => {
                state.write_u8(6);
                ptr.hash(state);
            }
            Value::Thread(thrd) => {
                state.write_u8(7);
                Rc::as_ptr(thrd).hash(state);
            }
            Value::Mult(..) => unreachable!(),
        }
    }
}

impl PartialEq for Value {
    fn eq(&self, other: &Self) -> bool {
        match (&self, &other) {
            (&Value::Nil, &Value::Nil) => true,
            (&Value::Bool(b1), &Value::Bool(b2)) => b1 == b2,
            (&Value::Number(n1), &Value::Number(n2)) => n1 == n2,
            (&Value::String(s1), &Value::String(s2)) => s1 == s2,
            (&Value::Func(f1), &Value::Func(f2)) => Rc::as_ptr(f1) == Rc::as_ptr(f2),
            (&Value::Table(v1), &Value::Table(v2)) => Rc::as_ptr(v1) == Rc::as_ptr(v2),
            (&Value::LUData(p1), &Value::LUData(p2)) => p1 == p2,
            (&Value::Thread(t1), &Value::Thread(t2)) => Rc::as_ptr(t1) == Rc::as_ptr(t2),
            _ => false,
        }
    }
}

impl Eq for Value {}

impl fmt::Display for ValueType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            ValueType::Nil => write!(f, "nil"),
            ValueType::Bool => write!(f, "boolean"),
            ValueType::Number => write!(f, "number"),
            ValueType::String => write!(f, "string"),
            ValueType::Func => write!(f, "function"),
            ValueType::Table => write!(f, "table"),
            ValueType::UData => write!(f, "userdata"),
            ValueType::Thread => write!(f, "thread"),
        }
    }
}

impl Numeric {
    pub fn from_str(str: &str) -> Result<Self> {
        Self::from_str_radix(str, None)
    }

    pub fn from_str_radix(str: &str, base: Option<u32>) -> Result<Self> {
        let str = str.trim();
        Ok(match base {
            // Base provided, so must be int without any prefix (0x)
            Some(base) => Numeric::Integer(i64::from_str_radix(str, base)?),
            // Figure out base from prefix (0x), then try int first, otherwise float
            None => {
                let hex = if !str.is_empty() && matches!(&str[0..1], "+" | "-") {
                    str.len() >= 3 && matches!(&str[1..3], "0x" | "0X")
                } else {
                    str.len() >= 2 && matches!(&str[0..2], "0x" | "0X")
                };
                if hex {
                    match from_str_radix_wrapping(str, 16) {
                        Some(n) => Numeric::Integer(n),
                        None => match parse_f64_hex(str) {
                            Some(f) => Numeric::Float(f),
                            None => {
                                return err!(LuaError::CustomString(
                                    "invalid hex float".to_string()
                                ))
                            }
                        },
                    }
                } else {
                    match str.parse() {
                        Ok(n) => Numeric::Integer(n),
                        Err(..) => Numeric::Float(parse_f64_dec(str)?),
                    }
                }
            }
        })
    }

    pub(super) fn from_usize_try_int(u: usize) -> Numeric {
        if u <= i64::MAX as usize {
            Numeric::Integer(u as i64)
        } else {
            Numeric::Float(u as f64)
        }
    }

    pub(super) fn from_f64_try_int(f: f64) -> Numeric {
        if f > i128::MIN as f64
            && f < i128::MAX as f64
            && f as i128 >= i64::MIN as i128
            && f as i128 <= i64::MAX as i128
        {
            Numeric::Integer(f as i64)
        } else {
            Numeric::Float(f)
        }
    }

    pub(super) fn to_int(&self) -> Result<i64> {
        match self {
            Numeric::Integer(n) => Ok(*n),
            Numeric::Float(..) => err!(LuaError::FloatToInt(Source::None)),
        }
    }

    pub(super) fn coerce_int(&self) -> Result<i64> {
        match self {
            Numeric::Integer(n) => Ok(*n),
            Numeric::Float(n) => {
                if n.fract().abs() <= f64::EPSILON
                    && *n > i128::MIN as f64
                    && *n < i128::MAX as f64
                    && *n as i128 >= i64::MIN as i128
                    && *n as i128 <= i64::MAX as i128
                {
                    Ok(*n as i64)
                } else {
                    err!(LuaError::FloatToInt(Source::None))
                }
            }
        }
    }

    pub(super) fn to_float(&self) -> f64 {
        match self {
            Numeric::Integer(n) => *n as f64,
            Numeric::Float(n) => *n,
        }
    }

    fn coerce_int_failover(&self) -> Numeric {
        match self {
            Numeric::Integer(n) => Numeric::Integer(*n),
            Numeric::Float(n) => {
                if n.fract().abs() <= f64::EPSILON {
                    Numeric::Integer(*n as i64)
                } else {
                    Numeric::Float(*n)
                }
            }
        }
    }
}

impl fmt::Display for Numeric {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Numeric::Integer(n) => write!(f, "{}", n),
            Numeric::Float(n) => write!(f, "{}", n),
        }
    }
}

impl Hash for Numeric {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match self {
            Numeric::Integer(n) => {
                state.write_u8(0);
                state.write_i64(*n);
            }
            Numeric::Float(f) => {
                state.write_u8(1);
                for byte in f.to_be_bytes() {
                    state.write_u8(byte);
                }
            }
        }
    }
}

impl PartialEq for Numeric {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (&Numeric::Integer(n1), &Numeric::Integer(n2)) => n1 == n2,
            (&Numeric::Float(f1), &Numeric::Float(f2)) => f1 == f2,
            _ => false,
        }
    }
}

impl Eq for Numeric {}

impl fmt::Debug for FuncDef {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            FuncDef::Builtin(func) => {
                write!(f, "builtin:{}", func.name)
            }
            FuncDef::Defined(func) => write!(f, "defined:{}", func.chunk),
            FuncDef::Protected(typ) => {
                write!(f, "builtin:{}", if *typ { "xpcall" } else { "pcall" })
            }
            FuncDef::Yield => write!(f, "builtin:yield"),
        }
    }
}

impl Table {
    pub(super) fn get_meta(&self) -> &Option<Rc<RefCell<Table>>> {
        &self.meta
    }

    pub(super) fn set_meta(&mut self, meta: Rc<RefCell<Table>>) {
        self.meta = Some(meta);
    }

    pub(super) fn init_next_keys(&mut self) -> Value {
        let keys: Vec<Value> = self.map.keys().cloned().collect();
        self.next_key = Some(
            keys.iter()
                .cloned()
                .zip(keys.iter().skip(1).cloned())
                .collect(),
        );
        keys.first().cloned().unwrap_or(Value::Nil)
    }

    pub(super) fn next_key(&mut self, key: Value) -> Value {
        self.next_key
            .as_ref()
            .unwrap()
            .get(&key)
            .cloned()
            .unwrap_or(Value::Nil)
    }

    pub(super) fn len(&self) -> usize {
        self.map.len()
    }

    pub(super) fn border(&self) -> usize {
        let mut border = 1;
        while let Some(..) = self.map.get(&Value::int(border as i64)) {
            border += 1;
        }
        border - 1
    }

    pub(super) fn get(&self, key: &Value) -> Value {
        match key {
            Value::Number(n) => self.map.get(&Value::Number(n.coerce_int_failover())),
            v => self.map.get(v),
        }
        .cloned()
        .unwrap_or(Value::Nil)
    }

    pub(super) fn set(&mut self, key: Value, value: Value) -> Result<()> {
        if matches!(key, Value::Nil)
            || matches!(key, Value::Number(Numeric::Float(f)) if f.is_nan())
        {
            return err!(LuaError::TableIndex);
        }
        let key = match key {
            Value::Number(n) => Value::Number(n.coerce_int_failover()),
            v => v,
        };
        if let Value::Nil = value {
            self.map.remove(&key);
        } else {
            self.map.insert(key, value);
        }
        Ok(())
    }
}

impl fmt::Display for Source {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Source::None => write!(f, ""),
            Source::Field(name) => write!(f, " (field '{}')", name),
            Source::Local(name) => write!(f, " (local '{}')", name),
            Source::Global(name) => write!(f, " (global '{}')", name),
            Source::Up(name) => write!(f, " (upvalue '{}')", name),
        }
    }
}