use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Not, Sub};
use std::rc::Rc;
use serde::{Deserialize, Serialize};
use super::{FuncCont, FuncDef, Local, Oper};
use crate::error::{LuaError, LuaErrorSrc, Result};
use crate::vm::{Numeric, OpCode, Value, ValueType, VM};
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub enum BinOp {
Add,
Sub,
Mul,
Div,
FlDiv,
Pow,
Mod,
BitAnd,
BitXor,
BitOr,
Shl,
Shr,
Conc,
Eq,
Neq,
Lt,
Leq,
Gt,
Geq,
And,
Or,
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub enum UnOp {
Minus,
BitNot,
Not,
Len,
}
impl BinOp {
pub fn precedence(&self) -> usize {
match self {
BinOp::Or => 0,
BinOp::And => 1,
BinOp::Lt | BinOp::Leq | BinOp::Gt | BinOp::Geq | BinOp::Eq | BinOp::Neq => 2,
BinOp::BitOr => 3,
BinOp::BitXor => 4,
BinOp::BitAnd => 5,
BinOp::Shl | BinOp::Shr => 6,
BinOp::Conc => 7,
BinOp::Add | BinOp::Sub => 8,
BinOp::Mul | BinOp::Div | BinOp::FlDiv | BinOp::Mod => 9,
BinOp::Pow => 11,
}
}
pub fn is_left_ass(&self) -> bool {
!matches!(self, BinOp::Conc | BinOp::Pow)
}
}
impl UnOp {
pub fn precedence(&self) -> usize {
10 }
}
impl Numeric {
pub fn rem(&self, rhs: &Self) -> Result<Self> {
num_rem(self, rhs)
}
}
enum OperatorResult<T> {
Direct(T),
Meta(Value, Vec<Value>, &'static str),
}
impl<T> OperatorResult<T> {
fn map_direct<U, F>(self, func: F) -> OperatorResult<U>
where
F: FnOnce(T) -> U,
{
match self {
OperatorResult::Direct(val) => OperatorResult::Direct(func(val)),
OperatorResult::Meta(meta, args, name) => OperatorResult::Meta(meta, args, name),
}
}
}
macro_rules! op_arith {
( $self:ident, $lop:ident, $rop:ident, $num_op:ident, $meta:literal ) => {{
let lhs = $self.fetch_oper(&$lop)?;
let rhs = $self.fetch_oper(&$rop)?;
let lnum = lhs.to_number_coerce().ok();
let rnum = rhs.to_number_coerce().ok();
if let Some((lnum, rnum)) = lnum.as_ref().zip(rnum) {
$num_op(&lnum, &rnum)
.map(Value::Number)
.map(OperatorResult::Direct)
} else if let Some(meta) = $self.get_metavalue_bin_op(&lhs, &rhs, $meta) {
Ok(OperatorResult::Meta(meta, vec![lhs, rhs], &$meta[2..]))
} else if lnum.is_some() {
err!(LuaError::OperatorArith(
rhs.value_type(),
$self.oper_err_src(&$rop)
))
} else {
err!(LuaError::OperatorArith(
lhs.value_type(),
$self.oper_err_src(&$lop)
))
}
}};
}
macro_rules! op_bitwise {
( $self:ident, $lop:ident, $rop:ident, $num_op:ident, $meta:literal ) => {{
let lhs = $self.fetch_oper(&$lop)?;
let rhs = $self.fetch_oper(&$rop)?;
let lnum = lhs.to_number().and_then(|n| n.coerce_int()).ok();
let rnum = rhs.to_number().and_then(|n| n.coerce_int()).ok();
if let Some((lnum, rnum)) = lnum.zip(rnum) {
Ok(OperatorResult::Direct(Value::Number($num_op(lnum, rnum))))
} else if let Some(meta) = $self.get_metavalue_bin_op(&lhs, &rhs, $meta) {
Ok(OperatorResult::Meta(meta, vec![lhs, rhs], &$meta[2..]))
} else {
if let Ok(num) = lhs.to_number() {
if num.coerce_int().is_err() {
return err!(LuaError::FloatToInt($self.oper_err_src(&$lop)));
}
}
if let Ok(num) = rhs.to_number() {
if num.coerce_int().is_err() {
return err!(LuaError::FloatToInt($self.oper_err_src(&$rop)));
}
}
err!(LuaError::OperatorBitwise(if lnum.is_some() {
rhs.value_type()
} else {
lhs.value_type()
}))
}
}};
}
macro_rules! op_comp {
( $self:ident, $lop:ident, $rop:ident, $num_op:ident, $str_op:ident, $meta:literal ) => {{
let lhs = $self.fetch_oper(&$lop)?;
let rhs = $self.fetch_oper(&$rop)?;
match (lhs, rhs) {
(Value::Number(n1), Value::Number(n2)) => {
Ok(OperatorResult::Direct(Value::Bool($num_op(&n1, &n2)?)))
}
(Value::String(s1), Value::String(s2)) => {
Ok(OperatorResult::Direct(Value::Bool(s1.$str_op(&s2))))
}
(lhs, rhs) => {
if let Some(meta) = $self.get_metavalue_bin_op(&lhs, &rhs, $meta) {
Ok(OperatorResult::Meta(meta, vec![lhs, rhs], &$meta[2..]))
} else {
let ltype = lhs.value_type();
let rtype = rhs.value_type();
if ltype == rtype {
err!(LuaError::OperatorCompareSame(ltype))
} else {
err!(LuaError::OperatorCompare(ltype, rtype,))
}
}
}
}
}};
}
impl VM {
fn fetch_oper(&mut self, oper: &Oper) -> Result<Value> {
let frame = self.frames.last_mut().unwrap();
Ok(match oper {
Oper::Local(ind) => match &frame.locals[*ind] {
Local::Temp => unreachable!(),
Local::Stack { val, .. } => val.clone(),
Local::Heap { var, .. } => var.borrow().clone(),
},
Oper::Up(ind) => {
let (var, _) = &frame.ups[*ind];
var.borrow().borrow().clone()
}
Oper::Reg(reg) => std::mem::replace(&mut frame.regs[*reg], Value::Nil).into_single(),
Oper::Raw(val) => val.clone(),
})
}
fn oper_err_src(&self, oper: &Oper) -> LuaErrorSrc {
match oper {
Oper::Local(ind) => {
let loc = self.frames.last().unwrap().locals.get(*ind).unwrap();
match loc {
Local::Temp => unreachable!(),
Local::Stack { name, .. } | Local::Heap { name, .. } => {
LuaErrorSrc::Local(name.to_string())
}
}
}
Oper::Up(ind) => {
let (_, name) = self.frames.last().unwrap().ups.get(*ind).unwrap();
LuaErrorSrc::Up(name.to_string())
}
Oper::Reg(reg) => {
for pc in (0..self.pc).rev() {
match &self.code.get_op(pc) {
OpCode::GlobalGet { name, dst_reg, .. } if dst_reg == reg => {
return LuaErrorSrc::Global(name.to_string());
}
OpCode::TableGet {
ind_reg, dst_reg, ..
} if dst_reg == reg => {
let ind = self.frames.last().unwrap().regs[*ind_reg].to_single();
return LuaErrorSrc::Field(
String::from_utf8_lossy(&ind.write_as_string().unwrap())
.into_owned(),
);
}
OpCode::JumpIf { .. } | OpCode::JumpIfNot { .. } => {
return LuaErrorSrc::None
}
_ => {}
}
}
LuaErrorSrc::None
}
Oper::Raw(..) => LuaErrorSrc::None,
}
}
fn get_metavalue_opt(&self, val: &Value, ind: &str) -> Option<Value> {
match self.get_metatable(val) {
Some(meta) => match meta.borrow().get(&Value::str(ind)) {
Value::Nil => None,
val => Some(val),
},
None => None,
}
}
fn get_metavalue_bin_op(&self, lhs: &Value, rhs: &Value, ind: &str) -> Option<Value> {
match self.get_metavalue_opt(lhs, ind) {
Some(meta) => Some(meta),
None => self.get_metavalue_opt(rhs, ind),
}
}
fn get_metamethod(&mut self, meta: Value, name: &str) -> Result<Rc<FuncDef>> {
match meta.to_single() {
Value::Func(func) => Ok(func.clone()),
val => {
return err!(LuaError::CallInvalid(
val.value_type(),
LuaErrorSrc::Metamethod(name.to_string())
));
}
}
}
pub(super) fn bin_op(&mut self, lhs: Oper, op: BinOp, rhs: Oper) -> Result<Value> {
match self.bin_op_internal(lhs, op, rhs)? {
OperatorResult::Direct(val) => Ok(val),
OperatorResult::Meta(meta, args, name) => {
let func = self.get_metamethod(meta, name)?;
self.call_recursive_meta(func, args, Some(name))
}
}
}
pub(super) fn bin_op_yieldable(
&mut self,
lhs: Oper,
op: BinOp,
rhs: Oper,
dst_reg: usize,
) -> Result<()> {
match self.bin_op_internal(lhs, op, rhs)? {
OperatorResult::Direct(val) => {
self.frames.last_mut().unwrap().regs[dst_reg] = val;
}
OperatorResult::Meta(meta, args, name) => {
let func = self.get_metamethod(meta, name)?;
self.call_prepare(
func,
args,
Some((
FuncCont {
name: "__bin_op_cont",
func: Rc::new(move |_, res| match res {
Ok(val) => match op {
BinOp::Eq | BinOp::Lt | BinOp::Leq | BinOp::Gt | BinOp::Geq => {
Ok(Value::Bool(val.is_truthy()))
}
BinOp::Neq => Ok(Value::Bool(val.is_falsy())),
_ => Ok(val),
},
Err(e) => Err(e),
}),
},
true,
)),
Some(dst_reg),
false,
Some(name),
)?;
self.frames.last_mut().unwrap().meta = Some(name);
}
}
Ok(())
}
fn bin_op_internal(
&mut self,
lhs: Oper,
op: BinOp,
rhs: Oper,
) -> Result<OperatorResult<Value>> {
match op {
BinOp::Add => op_arith!(self, lhs, rhs, num_add, "__add"),
BinOp::Sub => op_arith!(self, lhs, rhs, num_sub, "__sub"),
BinOp::Mul => op_arith!(self, lhs, rhs, num_mul, "__mul"),
BinOp::Div => op_arith!(self, lhs, rhs, num_div, "__div"),
BinOp::FlDiv => op_arith!(self, lhs, rhs, num_fldiv, "__idiv"),
BinOp::Mod => op_arith!(self, lhs, rhs, num_rem, "__mod"),
BinOp::Pow => op_arith!(self, lhs, rhs, num_pow, "__pow"),
BinOp::BitAnd => op_bitwise!(self, lhs, rhs, num_bitand, "__band"),
BinOp::BitOr => op_bitwise!(self, lhs, rhs, num_bitor, "__bor"),
BinOp::BitXor => op_bitwise!(self, lhs, rhs, num_bitxor, "__bxor"),
BinOp::Shl => op_bitwise!(self, lhs, rhs, num_shl, "__shl"),
BinOp::Shr => op_bitwise!(self, lhs, rhs, num_shr, "__shr"),
BinOp::Conc => self.val_conc(lhs, rhs),
BinOp::Eq => Ok(self.val_eq(lhs, rhs)?.map_direct(Value::Bool)),
BinOp::Neq => Ok(self
.val_eq(lhs, rhs)?
.map_direct(Not::not)
.map_direct(Value::Bool)),
BinOp::Lt => op_comp!(self, lhs, rhs, num_lt, lt, "__lt"),
BinOp::Leq => op_comp!(self, lhs, rhs, num_le, le, "__le"),
BinOp::Gt => op_comp!(self, rhs, lhs, num_lt, lt, "__lt"),
BinOp::Geq => op_comp!(self, rhs, lhs, num_le, le, "__le"),
BinOp::And | BinOp::Or => unreachable!(),
}
}
fn val_conc(&mut self, lop: Oper, rop: Oper) -> Result<OperatorResult<Value>> {
let lhs = self.fetch_oper(&lop)?;
let rhs = self.fetch_oper(&rop)?;
let lstr = lhs.to_string_coerce().ok();
let rstr = rhs.to_string_coerce().ok();
if let Some((mut lhs, rhs)) = lstr.zip(rstr) {
lhs.extend(rhs);
Ok(OperatorResult::Direct(Value::String(
self.alloc_string(lhs),
)))
} else if let Some(meta) = self.get_metavalue_bin_op(&lhs, &rhs, "__concat") {
Ok(OperatorResult::Meta(meta, vec![lhs, rhs], "concat"))
} else if lhs.to_string_coerce().is_ok() {
err!(LuaError::OperatorConcat(rhs.value_type()))
} else {
err!(LuaError::OperatorConcat(lhs.value_type()))
}
}
fn val_eq(&mut self, lop: Oper, rop: Oper) -> Result<OperatorResult<bool>> {
let lhs = self.fetch_oper(&lop)?;
let rhs = self.fetch_oper(&rop)?;
let eq = lhs == rhs;
Ok(
if !eq
&& lhs.value_type() == rhs.value_type()
&& matches!(lhs.value_type(), ValueType::Table | ValueType::UData)
{
if let Some(meta) = self.get_metavalue_bin_op(&lhs, &rhs, "__eq") {
OperatorResult::Meta(meta, vec![lhs, rhs], "eq")
} else {
OperatorResult::Direct(eq)
}
} else {
OperatorResult::Direct(eq)
},
)
}
pub(super) fn un_op(&mut self, lhs: Oper, op: UnOp) -> Result<Value> {
match self.un_op_internal(lhs, op)? {
OperatorResult::Direct(val) => Ok(val),
OperatorResult::Meta(meta, args, name) => {
let func = self.get_metamethod(meta, name)?;
self.call_recursive_meta(func, args, Some(name))
}
}
}
pub(super) fn un_op_yieldable(&mut self, lhs: Oper, op: UnOp, dst_reg: usize) -> Result<()> {
match self.un_op_internal(lhs, op)? {
OperatorResult::Direct(val) => {
self.frames.last_mut().unwrap().regs[dst_reg] = val;
}
OperatorResult::Meta(meta, args, name) => {
let func = self.get_metamethod(meta, name)?;
self.call_prepare(func, args, None, Some(dst_reg), false, Some(name))?;
self.frames.last_mut().unwrap().meta = Some(name);
}
}
Ok(())
}
fn un_op_internal(&mut self, lop: Oper, op: UnOp) -> Result<OperatorResult<Value>> {
let lhs = self.fetch_oper(&lop)?;
match op {
UnOp::Minus => {
if let Some(num) = lhs.to_number_coerce().ok() {
Ok(OperatorResult::Direct(Value::Number(num_minus(&num))))
} else if let Some(meta) = self.get_metavalue_opt(&lhs, "__unm") {
Ok(OperatorResult::Meta(meta, vec![lhs.clone(), lhs], "unm"))
} else {
err!(LuaError::OperatorArith(
lhs.value_type(),
self.oper_err_src(&lop)
))
}
}
UnOp::BitNot => {
if let Some(num) = lhs.to_number().and_then(|n| n.coerce_int()).ok() {
Ok(OperatorResult::Direct(Value::Number(num_bitnot(num))))
} else if let Some(meta) = self.get_metavalue_opt(&lhs, "__bnot") {
Ok(OperatorResult::Meta(meta, vec![lhs.clone(), lhs], "bnot"))
} else {
if let Ok(num) = lhs.to_number() {
if num.coerce_int().is_err() {
return err!(LuaError::FloatToInt(self.oper_err_src(&lop)));
}
}
err!(LuaError::OperatorBitwise(lhs.value_type()))
}
}
UnOp::Not => Ok(OperatorResult::Direct(Value::Bool(lhs.is_falsy()))),
UnOp::Len => {
if let Value::String(str) = &lhs {
Ok(OperatorResult::Direct(Value::int(str.len() as i64)))
} else if let Some(meta) = self.get_metavalue_opt(&lhs, "__len") {
Ok(OperatorResult::Meta(meta, vec![lhs.clone(), lhs], "len"))
} else if let Value::Table(tbl) = &lhs {
Ok(OperatorResult::Direct(Value::int(
tbl.borrow().border() as i64
)))
} else {
err!(LuaError::OperatorLength(lhs.value_type()))
}
}
}
}
pub(super) fn get_table_field(&mut self, tbl: Value, ind: Value) -> Result<Value> {
match self.get_table_field_internal(tbl, ind)? {
OperatorResult::Direct(val) => Ok(val),
OperatorResult::Meta(meta, args, name) => {
let func = self.get_metamethod(meta, name)?;
self.call_recursive_meta(func, args, Some(name))
.map(Value::into_single)
}
}
}
pub(super) fn get_table_field_yieldable(
&mut self,
tbl: Value,
ind: Value,
dst_reg: usize,
) -> Result<()> {
match self.get_table_field_internal(tbl, ind)? {
OperatorResult::Direct(val) => {
self.frames.last_mut().unwrap().regs[dst_reg] = val;
}
OperatorResult::Meta(meta, args, name) => {
let func = self.get_metamethod(meta, name)?;
self.call_prepare(func, args, None, Some(dst_reg), false, Some(name))?;
self.frames.last_mut().unwrap().meta = Some(name);
}
}
Ok(())
}
fn get_table_field_internal(
&mut self,
mut tbl: Value,
ind: Value,
) -> Result<OperatorResult<Value>> {
for _ in 0..2000 {
let (is_tbl, val) = match tbl.to_single() {
Value::Table(tbl) => (true, tbl.borrow().get(&ind)),
_ => (false, Value::Nil),
};
match val {
Value::Nil => {
let meta = self
.get_metatable(&tbl)
.map(|t| t.borrow().get(&Value::str("__index")))
.unwrap_or(Value::Nil);
match meta {
Value::Nil => {
if is_tbl {
return Ok(OperatorResult::Direct(val));
} else {
let (name, namewhat) = if let OpCode::TableGet { tbl_reg, .. } =
self.code.get_op(self.pc.saturating_sub(1))
{
let mut test_pc = self.pc.saturating_sub(1);
let pc_tbl = loop {
test_pc -= 1;
match self.code.get_op(test_pc) {
OpCode::GlobalGet { dst_reg, .. }
| OpCode::LocalGet { dst_reg, .. }
| OpCode::TableGet { dst_reg, .. }
| OpCode::UpGet { dst_reg, .. }
if dst_reg == tbl_reg =>
{
break test_pc + 1;
}
_ => {}
}
if test_pc == 0 {
break self.pc.saturating_sub(1);
}
};
Self::reg_info(
self.frames.last().unwrap(),
tbl_reg,
self.code.clone(),
pc_tbl,
)
} else {
(None, "?")
};
return err!(LuaError::IndexNonTable(
tbl.value_type(),
name.map(|n| String::from_utf8_lossy(&n).into_owned())
.unwrap_or("".to_string()),
namewhat
));
}
}
Value::Table(inner) => tbl = Value::Table(inner), Value::Func(func) => {
return Ok(OperatorResult::Meta(
Value::Func(func),
vec![tbl.clone(), ind.clone()],
"index",
))
}
v => {
return err!(LuaError::IndexNonTable(
v.value_type(),
"".to_string(),
""
))
}
}
}
val => return Ok(OperatorResult::Direct(val)),
}
}
err!(LuaError::MetatableIndex)
}
pub(super) fn set_table_field(&mut self, tbl: Value, ind: Value, val: Value) -> Result<()> {
match self.set_table_field_internal(tbl, ind, val)? {
OperatorResult::Direct(val) => Ok(val),
OperatorResult::Meta(meta, args, name) => {
let func = self.get_metamethod(meta, name)?;
self.call_recursive_meta(func, args, Some(name))?;
Ok(())
}
}
}
pub(super) fn set_table_field_yieldable(
&mut self,
tbl: Value,
ind: Value,
val: Value,
) -> Result<()> {
match self.set_table_field_internal(tbl, ind, val)? {
OperatorResult::Direct(_) => {}
OperatorResult::Meta(meta, args, name) => {
let func = self.get_metamethod(meta, name)?;
self.call_prepare(func, args, None, None, false, Some(name))?;
self.frames.last_mut().unwrap().meta = Some(name);
}
}
Ok(())
}
fn set_table_field_internal(
&mut self,
mut tbl: Value,
ind: Value,
val: Value,
) -> Result<OperatorResult<()>> {
let mut depth = 0;
loop {
depth += 1;
if depth >= 2000 {
return err!(LuaError::MetatableNewIndex);
}
let (is_tbl, check) = match tbl.to_single() {
Value::Table(tbl) => (true, tbl.borrow().get(&ind)),
_ => (false, Value::Nil),
};
match check {
Value::Nil => {
let meta = self
.get_metatable(&tbl)
.map(|t| t.borrow().get(&Value::str("__newindex")))
.unwrap_or(Value::Nil);
match meta {
Value::Nil => {
if is_tbl {
break;
} else {
let (name, namewhat) = if let OpCode::TableSet { tbl_reg, .. } =
self.code.get_op(self.pc.saturating_sub(1))
{
Self::reg_info(
self.frames.last().unwrap(),
tbl_reg,
self.code.clone(),
self.pc.saturating_sub(1),
)
} else {
(None, "?")
};
return err!(LuaError::IndexNonTable(
tbl.value_type(),
name.map(|n| String::from_utf8_lossy(&n).into_owned())
.unwrap_or("".to_string()),
namewhat
));
}
}
Value::Table(inner) => tbl = Value::Table(inner), Value::Func(func) => {
return Ok(OperatorResult::Meta(
Value::Func(func),
vec![tbl, ind, val],
"newindex",
))
}
_ => break,
}
}
_ => break,
}
}
tbl.to_table()?
.borrow_mut()
.set(ind, val)
.map(OperatorResult::Direct)
}
}
macro_rules! op_num_arith {
( $fn:ident, $int_op:ident, $flt_op:ident ) => {
fn $fn(lhs: &Numeric, rhs: &Numeric) -> Result<Numeric> {
if let (Ok(lhs), Ok(rhs)) = (lhs.to_int(), rhs.to_int()) {
Ok(Numeric::Integer(lhs.$int_op(rhs)))
} else {
Ok(Numeric::Float(lhs.to_float().$flt_op(rhs.to_float())))
}
}
};
}
op_num_arith!(num_add, wrapping_add, add);
op_num_arith!(num_sub, wrapping_sub, sub);
op_num_arith!(num_mul, wrapping_mul, mul);
fn num_rem(lhs: &Numeric, rhs: &Numeric) -> Result<Numeric> {
if let (Numeric::Integer(lhs), Numeric::Integer(rhs)) = (lhs, rhs) {
if *rhs == 0 {
return err!(LuaError::RemainderByZero);
}
let mut rem = lhs.wrapping_rem(*rhs);
if rem != 0 && lhs.signum() != rhs.signum() {
rem += rhs;
}
Ok(Numeric::Integer(rem))
} else {
let lhs = lhs.to_float();
let rhs = rhs.to_float();
let mut rem = lhs % rhs;
if rem.abs() > f64::EPSILON && lhs.signum() != rhs.signum() {
rem += rhs;
}
Ok(Numeric::Float(rem))
}
}
fn num_div(lhs: &Numeric, rhs: &Numeric) -> Result<Numeric> {
Ok(Numeric::Float(lhs.to_float().div(rhs.to_float())))
}
fn num_fldiv(lhs: &Numeric, rhs: &Numeric) -> Result<Numeric> {
if let (Numeric::Integer(n1), Numeric::Integer(n2)) = (lhs, rhs) {
if *n2 == 0 {
return err!(LuaError::DivideByZero);
}
let (mut res, _) = n1.overflowing_div(*n2);
if (n1 ^ n2) < 0 && n1 % n2 != 0 {
res -= 1;
}
Ok(Numeric::Integer(res))
} else {
Ok(Numeric::Float(lhs.to_float().div(rhs.to_float()).floor()))
}
}
fn num_pow(lhs: &Numeric, rhs: &Numeric) -> Result<Numeric> {
Ok(Numeric::Float(lhs.to_float().powf(rhs.to_float())))
}
macro_rules! op_num_bitw {
( $fn:ident, $op:ident ) => {
fn $fn(lhs: i64, rhs: i64) -> Numeric {
Numeric::Integer(lhs.$op(rhs))
}
};
}
op_num_bitw!(num_bitand, bitand);
op_num_bitw!(num_bitor, bitor);
op_num_bitw!(num_bitxor, bitxor);
fn num_shl(lhs: i64, rhs: i64) -> Numeric {
if rhs < 0 {
num_shr(lhs, rhs.saturating_neg())
} else {
let (val, over) = lhs.overflowing_shl(rhs as u32);
Numeric::Integer(if over { 0 } else { val })
}
}
fn num_shr(lhs: i64, rhs: i64) -> Numeric {
if rhs < 0 {
num_shl(lhs, rhs.saturating_neg())
} else {
let (val, over) = (lhs as u64).overflowing_shr(rhs as u32);
Numeric::Integer(if over { 0 } else { val as i64 })
}
}
fn num_lt(lhs: &Numeric, rhs: &Numeric) -> Result<bool> {
Ok(match (lhs.to_int(), rhs.to_int()) {
(Ok(lhs), Ok(rhs)) => lhs < rhs,
(Ok(lhs), ..) => {
let rhs = Numeric::Float(rhs.to_float().ceil());
rhs.coerce_int()
.map(|rhs| lhs < rhs)
.unwrap_or(rhs.to_float() > 0.)
}
(.., Ok(rhs)) => {
let lhs = Numeric::Float(lhs.to_float().floor());
lhs.coerce_int()
.map(|lhs| lhs < rhs)
.unwrap_or(lhs.to_float() < 0.)
}
_ => lhs.to_float() < rhs.to_float(),
})
}
pub(super) fn num_le(lhs: &Numeric, rhs: &Numeric) -> Result<bool> {
Ok(match (lhs.to_int(), rhs.to_int()) {
(Ok(lhs), Ok(rhs)) => lhs <= rhs,
(Ok(lhs), ..) => {
let rhs = Numeric::Float(rhs.to_float().floor());
rhs.coerce_int()
.map(|rhs| lhs <= rhs)
.unwrap_or(rhs.to_float() > 0.)
}
(.., Ok(rhs)) => {
let lhs = Numeric::Float(lhs.to_float().ceil());
lhs.coerce_int()
.map(|lhs| lhs <= rhs)
.unwrap_or(lhs.to_float() < 0.)
}
_ => lhs.to_float() <= rhs.to_float(),
})
}
fn num_minus(op: &Numeric) -> Numeric {
match op {
Numeric::Integer(n) => Numeric::Integer(n.wrapping_neg()),
Numeric::Float(f) => Numeric::Float(-f),
}
}
fn num_bitnot(op: i64) -> Numeric {
Numeric::Integer(!op)
}