use super::*;
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) enum ArithOp {
Add,
Sub,
Mul,
Mod,
Pow,
Div,
IDiv,
BAnd,
BOr,
BXor,
Shl,
Shr,
}
impl ArithOp {
pub(super) fn mm_name(self) -> &'static str {
match self {
ArithOp::Add => "add",
ArithOp::Sub => "sub",
ArithOp::Mul => "mul",
ArithOp::Mod => "mod",
ArithOp::Pow => "pow",
ArithOp::Div => "div",
ArithOp::IDiv => "idiv",
ArithOp::BAnd => "band",
ArithOp::BOr => "bor",
ArithOp::BXor => "bxor",
ArithOp::Shl => "shl",
ArithOp::Shr => "shr",
}
}
}
pub(super) fn as_num(v: Value, version: LuaVersion) -> Option<Num> {
match v {
Value::Int(i) => Some(Num::Int(i)),
Value::Float(f) => Some(Num::Float(f)),
Value::Str(s) => str_to_num(s.as_bytes(), version),
_ => None,
}
}
pub(super) fn float_mod(version: LuaVersion, a: f64, b: f64) -> f64 {
if version <= LuaVersion::Lua52 {
return a - (a / b).floor() * b;
}
let m = a % b;
let fix = if version == LuaVersion::Lua53 {
m * b < 0.0
} else {
(m > 0.0 && b < 0.0) || (m < 0.0 && b > 0.0)
};
if fix { m + b } else { m }
}
pub(super) fn concat_piece(v: Value, float_fmt: numeric::FloatFmt) -> Option<Vec<u8>> {
match v {
Value::Str(s) => Some(s.as_bytes().to_vec()),
Value::Int(x) => Some(numeric::num_to_string(Num::Int(x)).into_bytes()),
Value::Float(x) => Some(numeric::num_to_string_for(Num::Float(x), float_fmt).into_bytes()),
_ => None,
}
}
pub(super) fn type_mt_slot(v: Value) -> Option<usize> {
match v {
Value::Nil => Some(0),
Value::Bool(_) => Some(1),
Value::Int(_) | Value::Float(_) => Some(2),
Value::Str(_) => Some(3),
Value::Closure(_) | Value::Native(_) => Some(4),
Value::Table(_) | Value::Coro(_) | Value::Userdata(_) | Value::LightUserdata(_) => None,
}
}
pub(super) fn as_number(v: Value) -> Option<Num> {
match v {
Value::Int(i) => Some(Num::Int(i)),
Value::Float(f) => Some(Num::Float(f)),
_ => None,
}
}
pub(crate) fn arith_num(
version: LuaVersion,
op: ArithOp,
ln: Num,
rn: Num,
) -> Result<Value, &'static str> {
use ArithOp::*;
Ok(match (op, ln, rn) {
(Add, Num::Int(a), Num::Int(b)) => Value::Int(a.wrapping_add(b)),
(Sub, Num::Int(a), Num::Int(b)) => Value::Int(a.wrapping_sub(b)),
(Mul, Num::Int(a), Num::Int(b)) => Value::Int(a.wrapping_mul(b)),
(IDiv, Num::Int(a), Num::Int(b)) => {
if b == 0 {
return Err("attempt to divide by zero");
}
let mut q = a.wrapping_div(b);
if (a ^ b) < 0 && q.wrapping_mul(b) != a {
q -= 1;
}
Value::Int(q)
}
(Mod, Num::Int(a), Num::Int(b)) => {
if b == 0 {
return Err("attempt to perform 'n%0'");
}
let mut m = a.wrapping_rem(b);
if m != 0 && (m ^ b) < 0 {
m += b;
}
Value::Int(m)
}
(Add, a, b) => Value::Float(a.as_f64() + b.as_f64()),
(Sub, a, b) => Value::Float(a.as_f64() - b.as_f64()),
(Mul, a, b) => Value::Float(a.as_f64() * b.as_f64()),
(Div, a, b) => Value::Float(a.as_f64() / b.as_f64()),
(Pow, a, b) => {
let (a, b) = (a.as_f64(), b.as_f64());
Value::Float(if b == 2.0 && version >= LuaVersion::Lua54 {
a * a
} else {
a.powf(b)
})
}
(IDiv, a, b) => Value::Float((a.as_f64() / b.as_f64()).floor()),
(Mod, a, b) => Value::Float(float_mod(version, a.as_f64(), b.as_f64())),
(BAnd | BOr | BXor | Shl | Shr, ..) => unreachable!("bitwise op in arith_num"),
})
}
pub(super) fn int_of(n: Num) -> Option<i64> {
match n {
Num::Int(i) => Some(i),
Num::Float(f) => crate::runtime::value::f2i_exact(f),
}
}
pub(super) fn coerce_num_float(v: Value) -> Option<Num> {
match v {
Value::Str(s) => numeric::str2num(s.as_bytes(), false, true),
v => as_number(v),
}
}
pub(super) fn coerce_num(v: Value) -> Option<Num> {
match v {
Value::Str(s) => numeric::str2num(s.as_bytes(), true, true),
v => as_number(v),
}
}
pub(super) fn coerce_num_51(v: Value) -> Option<Num> {
match v {
Value::Str(s) => numeric::strtod_str(s.as_bytes()).map(Num::Float),
v => as_number(v),
}
}
pub(crate) fn str_to_num(s: &[u8], version: LuaVersion) -> Option<Num> {
match version {
LuaVersion::Lua51 => numeric::strtod_str(s).map(Num::Float),
LuaVersion::Lua52 => numeric::str2num(s, false, true),
_ => numeric::str2num(s, true, true),
}
}
pub(super) fn shift_left(a: i64, b: i64) -> i64 {
if b < 0 {
if b <= -64 {
0
} else {
((a as u64) >> (-b as u32)) as i64
}
} else if b >= 64 {
0
} else {
((a as u64) << (b as u32)) as i64
}
}
pub(super) fn int_lt_float(i: i64, f: f64) -> bool {
if f.is_nan() {
return false;
}
if f >= 9_223_372_036_854_775_808.0 {
return true;
}
if f < -9_223_372_036_854_775_808.0 {
return false;
}
let ff = f.floor();
let fi = ff as i64;
if f == ff { i < fi } else { i <= fi }
}
pub(super) fn int_le_float(i: i64, f: f64) -> bool {
if f.is_nan() {
return false;
}
if f >= 9_223_372_036_854_775_808.0 {
return true;
}
if f < -9_223_372_036_854_775_808.0 {
return false;
}
i <= f.floor() as i64
}
pub(super) fn int_for_limit(limit: Num, init: i64, step: i64) -> (i64, bool) {
match limit {
Num::Int(l) => {
let empty = if step > 0 { init > l } else { init < l };
(l, empty)
}
Num::Float(f) => {
if f.is_nan() {
return if step > 0 {
(0, true)
} else {
(i64::MIN, false)
};
}
if step > 0 {
if f >= 9_223_372_036_854_775_808.0 {
(i64::MAX, false)
} else {
let l = f.floor();
if l < -9_223_372_036_854_775_808.0 {
(i64::MIN, true)
} else {
let li = l as i64;
(li, init > li)
}
}
} else if f <= -9_223_372_036_854_775_808.0 {
(i64::MIN, false)
} else {
let l = f.ceil();
if l >= 9_223_372_036_854_775_808.0 {
(i64::MAX, true)
} else {
let li = l as i64;
(li, init < li)
}
}
}
}
}