1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
use std::{fmt::Debug, hash::Hash, rc::Rc};

use neige_infra::{math::float_to_integer, Constant, LuaType, Prototype};
use serde::{ser::SerializeMap, Serialize};

use super::{
    closure::{Closure, RustFn},
    table::LuaTable,
};

#[derive(Clone)]
pub enum LuaValue {
    Nil,
    Boolean(bool),
    Integer(i64),
    Number(f64),
    Str(String),
    Table(Rc<LuaTable>),
    Function(Rc<Closure>),
}

impl LuaValue {
    pub fn is_nil(&self) -> bool {
        match self {
            Self::Nil => true,
            _ => false,
        }
    }

    pub fn new_table(n_arr: usize, n_rec: usize) -> LuaValue {
        LuaValue::Table(Rc::new(LuaTable::new(n_arr, n_rec)))
    }

    pub fn new_lua_closure(proto: Rc<Prototype>) -> LuaValue {
        LuaValue::Function(Rc::new(Closure::new_lua_closure(proto)))
    }

    pub fn new_rust_closure(f: RustFn, n_upvals: usize) -> LuaValue {
        LuaValue::Function(Rc::new(Closure::new_rust_closure(f, n_upvals)))
    }

    pub fn type_of(&self) -> LuaType {
        match self {
            LuaValue::Nil => LuaType::Nil,
            LuaValue::Boolean(_) => LuaType::Boolean,
            LuaValue::Integer(_) => LuaType::Number,
            LuaValue::Number(_) => LuaType::Number,
            LuaValue::Str(_) => LuaType::String,
            LuaValue::Table(_) => LuaType::Table,
            LuaValue::Function(_) => LuaType::Function,
        }
    }
}

impl LuaValue {
    pub fn from_const(val: &Constant) -> Self {
        match val {
            Constant::Nil => LuaValue::Nil,
            Constant::Boolean(b) => LuaValue::Boolean(*b),
            Constant::Number(n) => LuaValue::Number(*n),
            Constant::Integer(i) => LuaValue::Integer(*i),
            Constant::Str(s) => LuaValue::Str(s.clone()),
        }
    }

    pub fn convert_to_boolean(&self) -> bool {
        match self {
            LuaValue::Nil => false,
            LuaValue::Boolean(b) => *b,
            _ => true,
        }
    }

    pub fn convert_to_integer(&self) -> Option<i64> {
        match self {
            LuaValue::Integer(i) => Some(*i),
            LuaValue::Number(n) => float_to_integer(*n),
            LuaValue::Str(s) => {
                if let Ok(i) = s.parse() {
                    Some(i)
                } else {
                    None
                }
            }
            _ => None,
        }
    }

    pub fn convert_to_float(&self) -> Option<f64> {
        match self {
            LuaValue::Integer(i) => Some(*i as f64),
            LuaValue::Number(f) => Some(*f),
            LuaValue::Str(s) => {
                if let Ok(f) = s.parse() {
                    Some(f)
                } else {
                    None
                }
            }
            _ => None,
        }
    }
}

impl Debug for LuaValue {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Nil => write!(f, "(nil)"),
            Self::Boolean(b) => write!(f, "({})", b),
            Self::Integer(i) => write!(f, "({})", i),
            Self::Number(n) => write!(f, "({})", n),
            Self::Str(s) => write!(f, "({})", s),
            Self::Table(_) => write!(f, "(table)"),
            Self::Function(_) => write!(f, "(function)"),
        }
    }
}

impl PartialEq for LuaValue {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (Self::Nil, Self::Nil) => true,
            (&Self::Boolean(x), &Self::Boolean(y)) => x == y,
            (&Self::Integer(x), &Self::Integer(y)) => x == y,
            (&Self::Integer(i), &Self::Number(f)) | (&Self::Number(f), &Self::Integer(i)) => {
                i as f64 == f && f as i64 == i
            }
            (&Self::Number(x), &Self::Number(y)) => x == y,
            (Self::Str(x), Self::Str(y)) => x == y,
            (Self::Table(x), Self::Table(y)) => Rc::ptr_eq(x, y),
            (Self::Function(x), Self::Function(y)) => Rc::ptr_eq(x, y),
            _ => false,
        }
    }
}
impl Eq for LuaValue {}

impl Hash for LuaValue {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        match self {
            LuaValue::Nil => (),
            LuaValue::Boolean(b) => b.hash(state),
            LuaValue::Integer(i) => i.hash(state),
            &LuaValue::Number(f) => {
                if let Some(i) = float_to_integer(f) {
                    i.hash(state)
                } else {
                    (f.to_bits() as i64).hash(state)
                }
            }
            LuaValue::Str(s) => s.hash(state),
            LuaValue::Table(t) => t.as_ref().hash(state),
            LuaValue::Function(f) => f.as_ref().hash(state),
        }
    }
}

impl Serialize for LuaValue {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        match self {
            LuaValue::Nil => serializer.serialize_unit(),
            LuaValue::Boolean(b) => serializer.serialize_bool(*b),
            LuaValue::Integer(i) => serializer.serialize_i64(*i),
            LuaValue::Number(f) => serializer.serialize_f64(*f),
            LuaValue::Str(s) => serializer.serialize_str(s),
            LuaValue::Function(_) => panic!("function can not serialize"),
            LuaValue::Table(table) => {
                let map = table.map.borrow();
                if map.len() == 0 {
                    table.arr.borrow().serialize(serializer)
                } else {
                    let arr = table.arr.borrow();
                    let mut obj = serializer.serialize_map(Some(map.len() + arr.len()))?;
                    for (i, val) in arr.iter().enumerate() {
                        obj.serialize_entry(&(i + 1).to_string(), val)?;
                    }
                    for (k, val) in map.iter() {
                        obj.serialize_entry(k, val)?;
                    }
                    obj.end()
                }
            }
        }
    }
}