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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
mod arith;
mod call;
mod utils;

use neige_infra::{code::inst::Instruction, math::fpb::fb_2_isize, LuaArith, LuaCompare};
use utils::{i32_isize, lua_upvalue_index, u16_isize, u32_isize, u8_isize};

use crate::{
    api::{
        AccessApi, ArithApi, CallApi, CompareApi, GetApi, LuaVm, MiscApi, PushApi, SetApi, StackApi,
    },
    state::LuaState,
};

impl LuaState {
    pub fn execute(&mut self, inst: &Instruction) {
        match inst {
            Instruction::Move(a, b, _) => {
                let a = u8_isize(a) + 1;
                let b = u16_isize(b) + 1;
                self.copy(b, a)
            }
            Instruction::LoadK(a, bx) => {
                let a = u8_isize(a) + 1;
                let bx = u32_isize(bx);
                self.get_const(bx);
                self.replace(a)
            }
            Instruction::LoadKx(a, _) => {
                let a = u8_isize(a) + 1;
                let inst = self.fetch();
                if let Instruction::ExtraArg(ax) = inst {
                    let ax = u32_isize(&ax);
                    self.get_const(ax);
                    self.replace(a)
                }
            }
            Instruction::LoadBool(a, b, c) => {
                let a = u8_isize(a) + 1;
                self.push_boolean(*b != 0);
                self.replace(a);
                if *c != 0 {
                    self.add_pc(1)
                }
            }
            Instruction::LoadNil(a, b, _) => {
                let a = u8_isize(a) + 1;
                let b = u16_isize(b);
                self.push_nil();
                for i in a..=b {
                    self.copy(-1, i)
                }
                self.pop(1)
            }
            Instruction::GetUpVal(a, b, _) => {
                let a = u8_isize(a) + 1;
                let b = u16_isize(b) + 1;
                self.copy(lua_upvalue_index(b), a)
            }
            Instruction::GetTabUp(a, b, c) => {
                let a = u8_isize(a) + 1;
                let b = u16_isize(b) + 1;
                let c = u16_isize(c);
                self.get_rk(c);
                self.get_table(lua_upvalue_index(b));
                self.replace(a)
            }
            Instruction::GetTable(a, b, c) => {
                let a = u8_isize(a) + 1;
                let b = u16_isize(b) + 1;
                let c = u16_isize(c);
                self.get_rk(c);
                self.get_table(b);
                self.replace(a);
            }
            Instruction::SetTabUp(a, b, c) => {
                let a = u8_isize(a) + 1;
                let b = u16_isize(b);
                let c = u16_isize(c);
                self.get_rk(b);
                self.get_rk(c);
                self.set_table(lua_upvalue_index(a))
            }
            Instruction::SetUpVal(a, b, _) => {
                let a = u8_isize(a) + 1;
                let b = u16_isize(b) + 1;
                self.copy(a, lua_upvalue_index(b))
            }
            Instruction::SetTable(a, b, c) => {
                let a = u8_isize(a) + 1;
                let b = u16_isize(b);
                let c = u16_isize(c);
                self.get_rk(b);
                self.get_rk(c);
                self.set_table(a)
            }
            Instruction::NetTable(a, b, c) => {
                let a = u8_isize(a) + 1;
                let b = u16_isize(b);
                let c = u16_isize(c);
                self.create_table(fb_2_isize(b), fb_2_isize(c));
                self.replace(a)
            }
            Instruction::_Self(a, b, c) => {
                let a = u8_isize(a) + 1;
                let b = u16_isize(b) + 1;
                let c = u16_isize(c);
                self.copy(b, a + 1);
                self.get_rk(c);
                self.get_table(b);
                self.replace(a);
            }
            Instruction::Add(a, b, c) => self.binary_arith(LuaArith::Add, a, b, c),
            Instruction::Sub(a, b, c) => self.binary_arith(LuaArith::Sub, a, b, c),
            Instruction::Mul(a, b, c) => self.binary_arith(LuaArith::Mul, a, b, c),
            Instruction::Mod(a, b, c) => self.binary_arith(LuaArith::Mod, a, b, c),
            Instruction::Pow(a, b, c) => self.binary_arith(LuaArith::Pow, a, b, c),
            Instruction::Div(a, b, c) => self.binary_arith(LuaArith::Div, a, b, c),
            Instruction::IDiv(a, b, c) => self.binary_arith(LuaArith::IDiv, a, b, c),
            Instruction::BAnd(a, b, c) => self.binary_arith(LuaArith::BAnd, a, b, c),
            Instruction::Bor(a, b, c) => self.binary_arith(LuaArith::Bor, a, b, c),
            Instruction::BXor(a, b, c) => self.binary_arith(LuaArith::BXor, a, b, c),
            Instruction::Shl(a, b, c) => self.binary_arith(LuaArith::Shl, a, b, c),
            Instruction::Shr(a, b, c) => self.binary_arith(LuaArith::Shr, a, b, c),
            Instruction::Unm(a, b, _) => self.unary_arith(LuaArith::Unm, a, b),
            Instruction::BNot(a, b, _) => self.unary_arith(LuaArith::BNot, a, b),
            Instruction::Not(a, b, _) => {
                let a = u8_isize(a) + 1;
                let b = u16_isize(b) + 1;
                self.push_boolean(!self.to_boolean(b));
                self.replace(a)
            }
            Instruction::Length(a, b, _) => {
                let a = u8_isize(a) + 1;
                let b = u16_isize(b) + 1;
                self.len(b);
                self.replace(a)
            }
            Instruction::Concat(a, b, c) => {
                let a = u8_isize(a) + 1;
                let b = u16_isize(b) + 1;
                let c = u16_isize(c) + 1;
                let n = (c - b + 1) as usize;
                self.check_stack(n);
                for i in b..=c {
                    self.push_value(i)
                }
                self.concat(n);
                self.replace(a)
            }
            Instruction::Jmp(a, sbx) => {
                let a = u8_isize(a);
                let sbx = i32_isize(sbx);
                self.add_pc(sbx);
                if a != 0 {
                    self.close_upvalue(a)
                }
            }
            Instruction::Eq(a, b, c) => self.inline_compare(LuaCompare::Eq, a, b, c),
            Instruction::Lt(a, b, c) => self.inline_compare(LuaCompare::Lt, a, b, c),
            Instruction::Le(a, b, c) => self.inline_compare(LuaCompare::Le, a, b, c),
            Instruction::Test(a, _, c) => {
                let a = u8_isize(a) + 1;
                let c = u16_isize(c);
                if self.to_boolean(a) != (c != 0) {
                    self.add_pc(1)
                }
            }
            Instruction::TestSet(a, b, c) => {
                let a = u8_isize(a) + 1;
                let b = u16_isize(b) + 1;
                let c = u16_isize(c);
                if self.to_boolean(b) != (c != 0) {
                    self.copy(b, a)
                } else {
                    self.add_pc(1)
                }
            }
            Instruction::Call(a, b, c) => {
                let a = u8_isize(a) + 1;
                let b = u16_isize(b);
                let c = u16_isize(c);
                let n_args = self.push_func_and_args(a, b);
                self.call(n_args, c - 1);
                self.pop_result(a, c)
            }
            Instruction::TailCall(a, b, _) => {
                let a = u8_isize(a) + 1;
                let b = u16_isize(b);
                let n_args = self.push_func_and_args(a, b);
                self.call(n_args, -1);
                self.pop_result(a, 0)
            }
            Instruction::Return(a, b, _) => {
                let a = u8_isize(a) + 1;
                let b = u16_isize(b);
                if b > 1 {
                    self.check_stack(b as usize - 1);
                    for i in a..=a + b - 2 {
                        self.push_value(i)
                    }
                } else if b < 1 {
                    self.fix_stack(a)
                }
            }
            Instruction::ForLoop(a, sbx) => {
                let a = u8_isize(a) + 1;
                let sbx = i32_isize(sbx);
                self.push_value(a + 2);
                self.push_value(a);
                self.arith(neige_infra::LuaArith::Add);
                self.replace(a);
                let is_positive_step = self.to_number(a + 2) >= 0.0;
                if (is_positive_step && self.compare(a, a + 1, neige_infra::LuaCompare::Le))
                    || (!is_positive_step && self.compare(a + 1, a, neige_infra::LuaCompare::Le))
                {
                    self.add_pc(sbx);
                    self.copy(a, a + 3)
                }
            }
            Instruction::ForPrep(a, sbx) => {
                let a = u8_isize(a) + 1;
                let sbx = i32_isize(sbx);

                self.push_value(a);
                self.push_value(a + 2);
                self.arith(LuaArith::Sub);
                self.replace(a);

                self.add_pc(sbx)
            }
            Instruction::TForCall(a, _, c) => {
                let a = u8_isize(a) + 1;
                let c = u16_isize(c);
                self.push_func_and_args(a, 3);
                self.call(2, c);
                self.pop_result(a + 3, c + 1)
            }
            Instruction::TForLoop(a, sbx) => {
                let a = u8_isize(a) + 1;
                let sbx = i32_isize(sbx);
                if !self.is_nil(a + 1) {
                    self.copy(a + 1, a);
                    self.add_pc(sbx)
                }
            }
            Instruction::SetList(a, b, c) => {
                let a = u8_isize(a) + 1;
                let b_is_zero = *b == 0;
                let b = if b_is_zero {
                    let b = self.to_integer(-1) as isize;
                    self.pop(1);
                    b - a - 1
                } else {
                    u16_isize(b)
                };
                let c = if *c > 0 {
                    u16_isize(c) - 1
                } else {
                    if let Instruction::ExtraArg(ax) = self.fetch() {
                        u32_isize(&ax)
                    } else {
                        panic!("SetList error")
                    }
                };
                let mut idx = (c * 50) as i64;
                for j in 1..=b {
                    idx += 1;
                    self.push_value(a + j);
                    self.set_i(a, idx)
                }

                if b_is_zero {
                    let register_count = self.register_count() as isize;
                    for j in register_count..=self.get_top() as isize {
                        idx += 1;
                        self.push_value(j);
                        self.set_i(a, idx)
                    }
                    self.set_top(register_count)
                }
            }
            Instruction::Closure(a, bx) => {
                let a = u8_isize(a) + 1;
                let bx = u32_isize(bx);
                self.load_proto(bx);
                self.replace(a)
            }
            Instruction::Vararg(a, b, _) => {
                let a = u8_isize(a) + 1;
                let b = u16_isize(b);
                if b != 1 {
                    self.load_vararg(b - 1);
                    self.pop_result(a, b)
                }
            }
            Instruction::ExtraArg(_) => todo!(),
        }
    }
}