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`.
|
* The traits `Index` and `IndexMut` are added to `FnPtr`.
|
||||||
* `FnPtr::iter_curry` and `FnPtr::iter_curry_mut` are added.
|
* `FnPtr::iter_curry` and `FnPtr::iter_curry_mut` are added.
|
||||||
* `Dynamic::deep_scan` is added to recursively scan for `Dynamic` values.
|
* `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
|
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;
|
let y = args[1].$yy().unwrap() as $base;
|
||||||
Ok(x.$func(y as $yyy).into())
|
Ok(x.$func(y as $yyy).into())
|
||||||
}, false) };
|
}, 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| {
|
($base:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| {
|
||||||
let x = args[0].$xx().unwrap() as $base;
|
let x = args[0].$xx().unwrap() as $base;
|
||||||
let y = args[1].$yy().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());
|
let y = <$base>::from(args[1].$yy().unwrap());
|
||||||
Ok(x.$func(y).into())
|
Ok(x.$func(y).into())
|
||||||
}, false) };
|
}, 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| {
|
(from $base:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| {
|
||||||
let x = <$base>::from(args[0].$xx().unwrap());
|
let x = <$base>::from(args[0].$xx().unwrap());
|
||||||
let y = <$base>::from(args[1].$yy().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))),
|
Divide => return Some(impl_op!(INT => divide(as_int, as_int))),
|
||||||
Modulo => return Some(impl_op!(INT => modulo(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))),
|
PowerOf => return Some(impl_op!(INT => power(as_int, as_int))),
|
||||||
RightShift => return Some(impl_op!(INT => shift_right(as_int, as_int))),
|
RightShift => return Some(impl_op!(INT => Ok(shift_right(as_int, as_int)))),
|
||||||
LeftShift => return Some(impl_op!(INT => shift_left(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)),
|
Divide => return Some(impl_op!(INT => as_int / as_int)),
|
||||||
Modulo => 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))),
|
PowerOf => return Some(impl_op!(INT => as_int.pow(as_int as u32))),
|
||||||
RightShift => return Some(impl_op!(INT => as_int >> as_int)),
|
RightShift => {
|
||||||
LeftShift => return Some(impl_op!(INT => as_int << as_int)),
|
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;
|
let y = args[1].$yy().unwrap() as $x;
|
||||||
Ok((*args[0].write_lock::<$x>().unwrap() = x.$func(y as $yyy)).into())
|
Ok((*args[0].write_lock::<$x>().unwrap() = x.$func(y as $yyy)).into())
|
||||||
}, false) };
|
}, 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| {
|
($x:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| {
|
||||||
let x = args[0].$xx().unwrap();
|
let x = args[0].$xx().unwrap();
|
||||||
let y = args[1].$yy().unwrap() as $x;
|
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());
|
let y = <$x>::from(args[1].$yy().unwrap());
|
||||||
Ok((*args[0].write_lock::<$x>().unwrap() = x.$func(y)).into())
|
Ok((*args[0].write_lock::<$x>().unwrap() = x.$func(y)).into())
|
||||||
}, false) };
|
}, 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| {
|
(from $x:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| {
|
||||||
let x = args[0].$xx().unwrap();
|
let x = args[0].$xx().unwrap();
|
||||||
let y = <$x>::from(args[1].$yy().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))),
|
DivideAssign => return Some(impl_op!(INT => divide(as_int, as_int))),
|
||||||
ModuloAssign => return Some(impl_op!(INT => modulo(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))),
|
PowerOfAssign => return Some(impl_op!(INT => power(as_int, as_int))),
|
||||||
RightShiftAssign => return Some(impl_op!(INT => shift_right(as_int, as_int))),
|
RightShiftAssign => return Some(impl_op!(INT => Ok(shift_right(as_int, as_int)))),
|
||||||
LeftShiftAssign => return Some(impl_op!(INT => shift_left(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)),
|
DivideAssign => return Some(impl_op!(INT /= as_int)),
|
||||||
ModuloAssign => 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))),
|
PowerOfAssign => return Some(impl_op!(INT => as_int.pow(as_int as u32))),
|
||||||
RightShiftAssign => return Some(impl_op!(INT >>= as_int)),
|
RightShiftAssign => {
|
||||||
LeftShiftAssign => return Some(impl_op!(INT <<= as_int)),
|
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> {
|
pub fn power(x: $arg_type, y: INT) -> RhaiResultOf<$arg_type> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unchecked")) {
|
||||||
if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) {
|
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 {
|
} 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 {
|
} else {
|
||||||
x.checked_pow(y as u32).ok_or_else(|| make_err(format!("Exponential overflow: {x} ** {y}")))
|
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)]
|
#[rhai_fn(name = "<<")]
|
||||||
pub fn shift_left(x: $arg_type, y: INT) -> RhaiResultOf<$arg_type> {
|
pub fn shift_left(x: $arg_type, y: INT) -> $arg_type {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unchecked")) {
|
||||||
if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) {
|
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 {
|
} 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 {
|
} 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 {
|
} else {
|
||||||
Ok(x << y)
|
x << y
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = ">>", return_raw)]
|
#[rhai_fn(name = ">>")]
|
||||||
pub fn shift_right(x: $arg_type, y: INT) -> RhaiResultOf<$arg_type> {
|
pub fn shift_right(x: $arg_type, y: INT) -> $arg_type {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unchecked")) {
|
||||||
if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) {
|
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 {
|
} 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 {
|
} 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 {
|
} else {
|
||||||
Ok(x >> y)
|
x >> y
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "&")]
|
#[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")?, 2);
|
||||||
assert_eq!(engine.eval::<INT>("let x = 10; x **= 4; x")?, 10000);
|
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 <<= 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 >>= 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")?, 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);
|
||||||
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