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
use neige_infra::math::float_to_integer;

use crate::{state::LuaState, value::value::LuaValue};
use neige_infra::LuaArith as Arith;

pub trait ArithApi {
    fn arith(&mut self, op: Arith);
}

impl ArithApi for LuaState {
    fn arith(&mut self, op: Arith) {
        let b = self.stack_pop();
        let a = if op == Arith::Unm || op == Arith::BNot {
            b.clone()
        } else {
            self.stack_pop()
        };
        let c = a.clone();
        let d = b.clone();
        let result = match op {
            Arith::Add => do_arith(a, b, |x, y| x + y, |x, y| x + y),
            Arith::Sub => do_arith(a, b, |x, y| x - y, |x, y| x - y),
            Arith::Mul => do_arith(a, b, |x, y| x * y, |x, y| x * y),
            Arith::Mod => do_arith(a, b, |x, y| x % y, |x, y| x % y),
            Arith::Pow => do_arith_f(a, b, |x, y| x.powf(y)),
            Arith::Div => do_arith_f(a, b, |x, y| x / y),
            Arith::IDiv => do_arith(a, b, |x, y| x.div_euclid(y), |x, y| x.div_euclid(y)),
            Arith::BAnd => do_arith_i(a, b, |x, y| x & y),
            Arith::Bor => do_arith_i(a, b, |x, y| x | y),
            Arith::BXor => do_arith_i(a, b, |x, y| x ^ y),
            Arith::Shl => do_arith_i(a, b, |x, n| if n > 0 { x << n } else { x >> -n }),
            Arith::Shr => do_arith_i(a, b, |x, n| if n > 0 { x >> n } else { x << -n }),
            Arith::Unm => do_arith(a, b, |x, _| -x, |x, _| -x),
            Arith::BNot => do_arith_i(a, b, |x, _| !x),
        };
        if let Some(res) = result {
            self.stack_push(res);
            return;
        }
        let result = self.call_meta_method(c, d, op.get_meta_name().into());
        if let Some(res) = result {
            self.stack_push(res);
            return;
        }
        panic!("arithmetic error!")
    }
}

fn do_arith_i(a: LuaValue, b: LuaValue, arith_i: fn(i64, i64) -> i64) -> Option<LuaValue> {
    let (a, b) = match (a, b) {
        (LuaValue::Integer(i1), LuaValue::Integer(i2)) => (i1, i2),
        (LuaValue::Integer(i1), LuaValue::Number(f2)) => (i1, float_to_integer(f2).unwrap()),
        (LuaValue::Number(f1), LuaValue::Integer(i2)) => (float_to_integer(f1).unwrap(), i2),
        (LuaValue::Number(f1), LuaValue::Number(f2)) => {
            (float_to_integer(f1).unwrap(), float_to_integer(f2).unwrap())
        }
        (_, _) => return None,
    };
    Some(LuaValue::Integer(arith_i(a, b)))
}

fn do_arith_f(a: LuaValue, b: LuaValue, arith_f: fn(f64, f64) -> f64) -> Option<LuaValue> {
    let (a, b) = match (a, b) {
        (LuaValue::Integer(i1), LuaValue::Integer(i2)) => (i1 as f64, i2 as f64),
        (LuaValue::Integer(i1), LuaValue::Number(f2)) => (i1 as f64, f2),
        (LuaValue::Number(f1), LuaValue::Integer(i2)) => (f1, i2 as f64),
        (LuaValue::Number(f1), LuaValue::Number(f2)) => (f1, f2),
        (_, _) => return None,
    };
    Some(LuaValue::Number(arith_f(a, b)))
}

fn do_arith(
    a: LuaValue,
    b: LuaValue,
    arith_i: fn(i64, i64) -> i64,
    arith_f: fn(f64, f64) -> f64,
) -> Option<LuaValue> {
    match (a, b) {
        (LuaValue::Integer(i1), LuaValue::Integer(i2)) => Some(LuaValue::Integer(arith_i(i1, i2))),
        (LuaValue::Integer(i1), LuaValue::Number(f2)) => {
            Some(LuaValue::Number(arith_f(i1 as f64, f2)))
        }
        (LuaValue::Number(f1), LuaValue::Integer(i2)) => {
            Some(LuaValue::Number(arith_f(f1, i2 as f64)))
        }
        (LuaValue::Number(f1), LuaValue::Number(f2)) => Some(LuaValue::Number(arith_f(f1, f2))),
        (_, _) => None,
    }
}