Allow negative shift bits.
This commit is contained in:
parent
3da1d060ec
commit
b880893154
@ -64,6 +64,7 @@ Enhancements
|
||||
* The traits `Index` and `IndexMut` are added to `FnPtr`.
|
||||
* `FnPtr::iter_curry` and `FnPtr::iter_curry_mut` are added.
|
||||
* `Dynamic::deep_scan` is added to recursively scan for `Dynamic` values.
|
||||
* `>>` and `<<` operators on integers no longer throw errors when the number of bits to shift is out of bounds. Shifting by a negative number of bits simply reverses the shift direction.
|
||||
|
||||
|
||||
Version 1.11.0
|
||||
|
@ -118,6 +118,11 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option<F
|
||||
let y = args[1].$yy().unwrap() as $base;
|
||||
Ok(x.$func(y as $yyy).into())
|
||||
}, false) };
|
||||
($base:ty => Ok($func:ident ( $xx:ident, $yy:ident ))) => { (|_, args| {
|
||||
let x = args[0].$xx().unwrap() as $base;
|
||||
let y = args[1].$yy().unwrap() as $base;
|
||||
Ok($func(x, y).into())
|
||||
}, false) };
|
||||
($base:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| {
|
||||
let x = args[0].$xx().unwrap() as $base;
|
||||
let y = args[1].$yy().unwrap() as $base;
|
||||
@ -133,6 +138,11 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option<F
|
||||
let y = <$base>::from(args[1].$yy().unwrap());
|
||||
Ok(x.$func(y).into())
|
||||
}, false) };
|
||||
(from $base:ty => Ok($func:ident ( $xx:ident, $yy:ident ))) => { (|_, args| {
|
||||
let x = <$base>::from(args[0].$xx().unwrap());
|
||||
let y = <$base>::from(args[1].$yy().unwrap());
|
||||
Ok($func(x, y).into())
|
||||
}, false) };
|
||||
(from $base:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| {
|
||||
let x = <$base>::from(args[0].$xx().unwrap());
|
||||
let y = <$base>::from(args[1].$yy().unwrap());
|
||||
@ -155,8 +165,8 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option<F
|
||||
Divide => return Some(impl_op!(INT => divide(as_int, as_int))),
|
||||
Modulo => return Some(impl_op!(INT => modulo(as_int, as_int))),
|
||||
PowerOf => return Some(impl_op!(INT => power(as_int, as_int))),
|
||||
RightShift => return Some(impl_op!(INT => shift_right(as_int, as_int))),
|
||||
LeftShift => return Some(impl_op!(INT => shift_left(as_int, as_int))),
|
||||
RightShift => return Some(impl_op!(INT => Ok(shift_right(as_int, as_int)))),
|
||||
LeftShift => return Some(impl_op!(INT => Ok(shift_left(as_int, as_int)))),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
@ -168,8 +178,26 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option<F
|
||||
Divide => return Some(impl_op!(INT => as_int / as_int)),
|
||||
Modulo => return Some(impl_op!(INT => as_int % as_int)),
|
||||
PowerOf => return Some(impl_op!(INT => as_int.pow(as_int as u32))),
|
||||
RightShift => return Some(impl_op!(INT => as_int >> as_int)),
|
||||
LeftShift => return Some(impl_op!(INT => as_int << as_int)),
|
||||
RightShift => {
|
||||
return Some((
|
||||
|_, args| {
|
||||
let x = args[0].as_int().unwrap();
|
||||
let y = args[1].as_int().unwrap();
|
||||
Ok((if y < 0 { x << -y } else { x >> y }).into())
|
||||
},
|
||||
false,
|
||||
))
|
||||
}
|
||||
LeftShift => {
|
||||
return Some((
|
||||
|_, args| {
|
||||
let x = args[0].as_int().unwrap();
|
||||
let y = args[1].as_int().unwrap();
|
||||
Ok((if y < 0 { x >> -y } else { x << y }).into())
|
||||
},
|
||||
false,
|
||||
))
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
@ -614,6 +642,12 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti
|
||||
let y = args[1].$yy().unwrap() as $x;
|
||||
Ok((*args[0].write_lock::<$x>().unwrap() = x.$func(y as $yyy)).into())
|
||||
}, false) };
|
||||
($x:ty => Ok($func:ident ( $xx:ident, $yy:ident ))) => { (|_, args| {
|
||||
let x = args[0].$xx().unwrap();
|
||||
let y = args[1].$yy().unwrap() as $x;
|
||||
let v: Dynamic = $func(x, y).into();
|
||||
Ok((*args[0].write_lock().unwrap() = v).into())
|
||||
}, false) };
|
||||
($x:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| {
|
||||
let x = args[0].$xx().unwrap();
|
||||
let y = args[1].$yy().unwrap() as $x;
|
||||
@ -628,6 +662,11 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti
|
||||
let y = <$x>::from(args[1].$yy().unwrap());
|
||||
Ok((*args[0].write_lock::<$x>().unwrap() = x.$func(y)).into())
|
||||
}, false) };
|
||||
(from $x:ty => Ok($func:ident ( $xx:ident, $yy:ident ))) => { (|_, args| {
|
||||
let x = args[0].$xx().unwrap();
|
||||
let y = <$x>::from(args[1].$yy().unwrap());
|
||||
Ok((*args[0].write_lock().unwrap() = $func(x, y).into()).into())
|
||||
}, false) };
|
||||
(from $x:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| {
|
||||
let x = args[0].$xx().unwrap();
|
||||
let y = <$x>::from(args[1].$yy().unwrap());
|
||||
@ -650,8 +689,8 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti
|
||||
DivideAssign => return Some(impl_op!(INT => divide(as_int, as_int))),
|
||||
ModuloAssign => return Some(impl_op!(INT => modulo(as_int, as_int))),
|
||||
PowerOfAssign => return Some(impl_op!(INT => power(as_int, as_int))),
|
||||
RightShiftAssign => return Some(impl_op!(INT => shift_right(as_int, as_int))),
|
||||
LeftShiftAssign => return Some(impl_op!(INT => shift_left(as_int, as_int))),
|
||||
RightShiftAssign => return Some(impl_op!(INT => Ok(shift_right(as_int, as_int)))),
|
||||
LeftShiftAssign => return Some(impl_op!(INT => Ok(shift_left(as_int, as_int)))),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
@ -663,8 +702,28 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti
|
||||
DivideAssign => return Some(impl_op!(INT /= as_int)),
|
||||
ModuloAssign => return Some(impl_op!(INT %= as_int)),
|
||||
PowerOfAssign => return Some(impl_op!(INT => as_int.pow(as_int as u32))),
|
||||
RightShiftAssign => return Some(impl_op!(INT >>= as_int)),
|
||||
LeftShiftAssign => return Some(impl_op!(INT <<= as_int)),
|
||||
RightShiftAssign => {
|
||||
return Some((
|
||||
|_, args| {
|
||||
let x = args[0].as_int().unwrap();
|
||||
let y = args[1].as_int().unwrap();
|
||||
let v = if y < 0 { x << -y } else { x >> y };
|
||||
Ok((*args[0].write_lock::<Dynamic>().unwrap() = v.into()).into())
|
||||
},
|
||||
false,
|
||||
))
|
||||
}
|
||||
LeftShiftAssign => {
|
||||
return Some((
|
||||
|_, args| {
|
||||
let x = args[0].as_int().unwrap();
|
||||
let y = args[1].as_int().unwrap();
|
||||
let v = if y < 0 { x >> -y } else { x << y };
|
||||
Ok((*args[0].write_lock::<Dynamic>().unwrap() = v.into()).into())
|
||||
},
|
||||
false,
|
||||
))
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
|
@ -72,9 +72,9 @@ macro_rules! gen_arithmetic_functions {
|
||||
pub fn power(x: $arg_type, y: INT) -> RhaiResultOf<$arg_type> {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) {
|
||||
Err(make_err(format!("Integer raised to too large an index: {x} ** {y}")))
|
||||
Err(make_err(format!("Exponential overflow: {x} ** {y}")))
|
||||
} else if y < 0 {
|
||||
Err(make_err(format!("Integer raised to a negative index: {x} ** {y}")))
|
||||
Err(make_err(format!("Integer raised to a negative power: {x} ** {y}")))
|
||||
} else {
|
||||
x.checked_pow(y as u32).ok_or_else(|| make_err(format!("Exponential overflow: {x} ** {y}")))
|
||||
}
|
||||
@ -83,32 +83,36 @@ macro_rules! gen_arithmetic_functions {
|
||||
}
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "<<", return_raw)]
|
||||
pub fn shift_left(x: $arg_type, y: INT) -> RhaiResultOf<$arg_type> {
|
||||
#[rhai_fn(name = "<<")]
|
||||
pub fn shift_left(x: $arg_type, y: INT) -> $arg_type {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) {
|
||||
Err(make_err(format!("Left-shift by too many bits: {x} << {y}")))
|
||||
0
|
||||
} else if y < 0 {
|
||||
Err(make_err(format!("Left-shift by a negative number: {x} << {y}")))
|
||||
shift_right(x, y.checked_abs().unwrap_or(INT::MAX))
|
||||
} else {
|
||||
x.checked_shl(y as u32).ok_or_else(|| make_err(format!("Left-shift by too many bits: {x} << {y}")))
|
||||
x.checked_shl(y as u32).unwrap_or_else(|| 0)
|
||||
}
|
||||
} else if y < 0 {
|
||||
x >> -y
|
||||
} else {
|
||||
Ok(x << y)
|
||||
x << y
|
||||
}
|
||||
}
|
||||
#[rhai_fn(name = ">>", return_raw)]
|
||||
pub fn shift_right(x: $arg_type, y: INT) -> RhaiResultOf<$arg_type> {
|
||||
#[rhai_fn(name = ">>")]
|
||||
pub fn shift_right(x: $arg_type, y: INT) -> $arg_type {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) {
|
||||
Err(make_err(format!("Right-shift by too many bits: {x} >> {y}")))
|
||||
x.wrapping_shr(u32::MAX)
|
||||
} else if y < 0 {
|
||||
Err(make_err(format!("Right-shift by a negative number: {x} >> {y}")))
|
||||
shift_left(x, y.checked_abs().unwrap_or(INT::MAX))
|
||||
} else {
|
||||
x.checked_shr(y as u32).ok_or_else(|| make_err(format!("Right-shift by too many bits: {x} >> {y}")))
|
||||
x.checked_shr(y as u32).unwrap_or_else(|| x.wrapping_shr(u32::MAX))
|
||||
}
|
||||
} else if y < 0 {
|
||||
x << -y
|
||||
} else {
|
||||
Ok(x >> y)
|
||||
x >> y
|
||||
}
|
||||
}
|
||||
#[rhai_fn(name = "&")]
|
||||
|
@ -30,7 +30,9 @@ fn test_binary_ops() -> Result<(), Box<EvalAltResult>> {
|
||||
assert_eq!(engine.eval::<INT>("let x = 10; x %= 4; x")?, 2);
|
||||
assert_eq!(engine.eval::<INT>("let x = 10; x **= 4; x")?, 10000);
|
||||
assert_eq!(engine.eval::<INT>("let x = 10; x <<= 4; x")?, 160);
|
||||
assert_eq!(engine.eval::<INT>("let x = 10; x <<= -1; x")?, 5);
|
||||
assert_eq!(engine.eval::<INT>("let x = 10; x >>= 4; x")?, 0);
|
||||
assert_eq!(engine.eval::<INT>("let x = 10; x >>= -2; x")?, 40);
|
||||
assert_eq!(engine.eval::<INT>("let x = 10; x &= 4; x")?, 0);
|
||||
assert_eq!(engine.eval::<INT>("let x = 10; x |= 4; x")?, 14);
|
||||
assert_eq!(engine.eval::<INT>("let x = 10; x ^= 4; x")?, 14);
|
||||
|
Loading…
Reference in New Issue
Block a user