Use ** for power/exponentiation.
This commit is contained in:
parent
8c26b49a39
commit
61638d6566
@ -13,8 +13,12 @@ Bug fixes
|
|||||||
Breaking changes
|
Breaking changes
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
* Trigonometry functions now take radians and return radians instead of degrees.
|
* In order to be consistent with other scripting languages:
|
||||||
|
* the power/exponentiation operator is changed from `~` to `**`; `~` is now a reserved symbol
|
||||||
|
* the power/exponentiation operator now binds to the right
|
||||||
|
* trigonometry functions now take radians and return radians instead of degrees
|
||||||
* `Dynamic::into_shared` is no longer available under `no_closure`. It used to panic.
|
* `Dynamic::into_shared` is no longer available under `no_closure`. It used to panic.
|
||||||
|
* `Token::is_operator` is renamed to `Token::is_symbol`.
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
------------
|
------------
|
||||||
|
@ -288,8 +288,8 @@ impl Engine {
|
|||||||
return Err(format!("'{}' is a reserved keyword", keyword).into());
|
return Err(format!("'{}' is a reserved keyword", keyword).into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Active standard operators cannot be made custom
|
// Active standard symbols cannot be made custom
|
||||||
Some(token) if token.is_operator() => {
|
Some(token) if token.is_symbol() => {
|
||||||
if !self.disabled_symbols.contains(token.syntax().as_ref()) {
|
if !self.disabled_symbols.contains(token.syntax().as_ref()) {
|
||||||
return Err(format!("'{}' is a reserved operator", keyword).into());
|
return Err(format!("'{}' is a reserved operator", keyword).into());
|
||||||
}
|
}
|
||||||
|
@ -1367,7 +1367,7 @@ pub fn run_builtin_binary_op(
|
|||||||
"*" => return multiply(x, y).map(Some),
|
"*" => return multiply(x, y).map(Some),
|
||||||
"/" => return divide(x, y).map(Some),
|
"/" => return divide(x, y).map(Some),
|
||||||
"%" => return modulo(x, y).map(Some),
|
"%" => return modulo(x, y).map(Some),
|
||||||
"~" => return power(x, y).map(Some),
|
"**" => return power(x, y).map(Some),
|
||||||
">>" => return shift_right(x, y).map(Some),
|
">>" => return shift_right(x, y).map(Some),
|
||||||
"<<" => return shift_left(x, y).map(Some),
|
"<<" => return shift_left(x, y).map(Some),
|
||||||
_ => (),
|
_ => (),
|
||||||
@ -1379,7 +1379,7 @@ pub fn run_builtin_binary_op(
|
|||||||
"*" => return Ok(Some((x * y).into())),
|
"*" => return Ok(Some((x * y).into())),
|
||||||
"/" => return Ok(Some((x / y).into())),
|
"/" => return Ok(Some((x / y).into())),
|
||||||
"%" => return Ok(Some((x % y).into())),
|
"%" => return Ok(Some((x % y).into())),
|
||||||
"~" => return Ok(Some(x.pow(y as u32).into())),
|
"**" => return Ok(Some(x.pow(y as u32).into())),
|
||||||
">>" => return Ok(Some((x >> y).into())),
|
">>" => return Ok(Some((x >> y).into())),
|
||||||
"<<" => return Ok(Some((x << y).into())),
|
"<<" => return Ok(Some((x << y).into())),
|
||||||
_ => (),
|
_ => (),
|
||||||
@ -1457,7 +1457,7 @@ pub fn run_builtin_binary_op(
|
|||||||
"*" => return Ok(Some((x * y).into())),
|
"*" => return Ok(Some((x * y).into())),
|
||||||
"/" => return Ok(Some((x / y).into())),
|
"/" => return Ok(Some((x / y).into())),
|
||||||
"%" => return Ok(Some((x % y).into())),
|
"%" => return Ok(Some((x % y).into())),
|
||||||
"~" => return Ok(Some(x.powf(y).into())),
|
"**" => return Ok(Some(x.powf(y).into())),
|
||||||
"==" => return Ok(Some((x == y).into())),
|
"==" => return Ok(Some((x == y).into())),
|
||||||
"!=" => return Ok(Some((x != y).into())),
|
"!=" => return Ok(Some((x != y).into())),
|
||||||
">" => return Ok(Some((x > y).into())),
|
">" => return Ok(Some((x > y).into())),
|
||||||
|
@ -68,7 +68,7 @@ macro_rules! gen_arithmetic_functions {
|
|||||||
Ok(Dynamic::from(x % y))
|
Ok(Dynamic::from(x % y))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "~", return_raw)]
|
#[rhai_fn(name = "**", return_raw)]
|
||||||
pub fn power(x: INT, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn power(x: INT, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
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) {
|
||||||
@ -246,7 +246,7 @@ mod f32_functions {
|
|||||||
pub fn modulo(x: f32, y: f32) -> f32 {
|
pub fn modulo(x: f32, y: f32) -> f32 {
|
||||||
x % y
|
x % y
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "~", return_raw)]
|
#[rhai_fn(name = "**", return_raw)]
|
||||||
pub fn pow_f_f(x: f32, y: f32) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn pow_f_f(x: f32, y: f32) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
Ok(Dynamic::from(x.powf(y)))
|
Ok(Dynamic::from(x.powf(y)))
|
||||||
}
|
}
|
||||||
@ -271,7 +271,7 @@ mod f32_functions {
|
|||||||
1
|
1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "~", return_raw)]
|
#[rhai_fn(name = "**", return_raw)]
|
||||||
pub fn pow_f_i(x: f32, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn pow_f_i(x: f32, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
if cfg!(not(feature = "unchecked")) && y > (i32::MAX as INT) {
|
if cfg!(not(feature = "unchecked")) && y > (i32::MAX as INT) {
|
||||||
Err(make_err(format!(
|
Err(make_err(format!(
|
||||||
@ -309,7 +309,7 @@ mod f64_functions {
|
|||||||
pub fn modulo(x: f64, y: f64) -> f64 {
|
pub fn modulo(x: f64, y: f64) -> f64 {
|
||||||
x % y
|
x % y
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "~", return_raw)]
|
#[rhai_fn(name = "**", return_raw)]
|
||||||
pub fn pow_f_f(x: f64, y: f64) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn pow_f_f(x: f64, y: f64) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
Ok(Dynamic::from(x.powf(y)))
|
Ok(Dynamic::from(x.powf(y)))
|
||||||
}
|
}
|
||||||
@ -334,7 +334,7 @@ mod f64_functions {
|
|||||||
1
|
1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "~", return_raw)]
|
#[rhai_fn(name = "**", return_raw)]
|
||||||
pub fn pow_f_i(x: FLOAT, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn pow_f_i(x: FLOAT, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
if cfg!(not(feature = "unchecked")) && y > (i32::MAX as INT) {
|
if cfg!(not(feature = "unchecked")) && y > (i32::MAX as INT) {
|
||||||
Err(make_err(format!(
|
Err(make_err(format!(
|
||||||
|
64
src/token.rs
64
src/token.rs
@ -194,7 +194,7 @@ pub enum Token {
|
|||||||
Divide,
|
Divide,
|
||||||
/// `%`
|
/// `%`
|
||||||
Modulo,
|
Modulo,
|
||||||
/// `~`
|
/// `**`
|
||||||
PowerOf,
|
PowerOf,
|
||||||
/// `<<`
|
/// `<<`
|
||||||
LeftShift,
|
LeftShift,
|
||||||
@ -305,7 +305,7 @@ pub enum Token {
|
|||||||
XOrAssign,
|
XOrAssign,
|
||||||
/// `%=`
|
/// `%=`
|
||||||
ModuloAssign,
|
ModuloAssign,
|
||||||
/// `~=`
|
/// `**=`
|
||||||
PowerOfAssign,
|
PowerOfAssign,
|
||||||
/// `private`
|
/// `private`
|
||||||
///
|
///
|
||||||
@ -422,8 +422,8 @@ impl Token {
|
|||||||
XOr => "^",
|
XOr => "^",
|
||||||
Modulo => "%",
|
Modulo => "%",
|
||||||
ModuloAssign => "%=",
|
ModuloAssign => "%=",
|
||||||
PowerOf => "~",
|
PowerOf => "**",
|
||||||
PowerOfAssign => "~=",
|
PowerOfAssign => "**=",
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
Fn => "fn",
|
Fn => "fn",
|
||||||
@ -511,8 +511,8 @@ impl Token {
|
|||||||
"^" => XOr,
|
"^" => XOr,
|
||||||
"%" => Modulo,
|
"%" => Modulo,
|
||||||
"%=" => ModuloAssign,
|
"%=" => ModuloAssign,
|
||||||
"~" => PowerOf,
|
"**" => PowerOf,
|
||||||
"~=" => PowerOfAssign,
|
"**=" => PowerOfAssign,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
"fn" => Fn,
|
"fn" => Fn,
|
||||||
@ -532,7 +532,7 @@ impl Token {
|
|||||||
#[cfg(feature = "no_module")]
|
#[cfg(feature = "no_module")]
|
||||||
"import" | "export" | "as" => Reserved(syntax.into()),
|
"import" | "export" | "as" => Reserved(syntax.into()),
|
||||||
|
|
||||||
"===" | "!==" | "->" | "<-" | ":=" | "**" | "::<" | "(*" | "*)" | "#" | "public"
|
"===" | "!==" | "->" | "<-" | ":=" | "~" | "::<" | "(*" | "*)" | "#" | "public"
|
||||||
| "new" | "use" | "module" | "package" | "var" | "static" | "begin" | "end"
|
| "new" | "use" | "module" | "package" | "var" | "static" | "begin" | "end"
|
||||||
| "shared" | "with" | "each" | "then" | "goto" | "unless" | "exit" | "match"
|
| "shared" | "with" | "each" | "then" | "goto" | "unless" | "exit" | "match"
|
||||||
| "case" | "default" | "void" | "null" | "nil" | "spawn" | "thread" | "go" | "sync"
|
| "case" | "default" | "void" | "null" | "nil" | "spawn" | "thread" | "go" | "sync"
|
||||||
@ -599,6 +599,8 @@ impl Token {
|
|||||||
DivideAssign |
|
DivideAssign |
|
||||||
LeftShiftAssign |
|
LeftShiftAssign |
|
||||||
RightShiftAssign |
|
RightShiftAssign |
|
||||||
|
PowerOf |
|
||||||
|
PowerOfAssign |
|
||||||
AndAssign |
|
AndAssign |
|
||||||
OrAssign |
|
OrAssign |
|
||||||
XOrAssign |
|
XOrAssign |
|
||||||
@ -609,9 +611,7 @@ impl Token {
|
|||||||
ModuloAssign |
|
ModuloAssign |
|
||||||
Return |
|
Return |
|
||||||
Throw |
|
Throw |
|
||||||
PowerOf |
|
In => true,
|
||||||
In |
|
|
||||||
PowerOfAssign => true,
|
|
||||||
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
@ -623,9 +623,9 @@ impl Token {
|
|||||||
|
|
||||||
match self {
|
match self {
|
||||||
// Assignments are not considered expressions - set to zero
|
// Assignments are not considered expressions - set to zero
|
||||||
Equals | PlusAssign | MinusAssign | MultiplyAssign | DivideAssign | LeftShiftAssign
|
Equals | PlusAssign | MinusAssign | MultiplyAssign | DivideAssign | PowerOfAssign
|
||||||
| RightShiftAssign | AndAssign | OrAssign | XOrAssign | ModuloAssign
|
| LeftShiftAssign | RightShiftAssign | AndAssign | OrAssign | XOrAssign
|
||||||
| PowerOfAssign => 0,
|
| ModuloAssign => 0,
|
||||||
|
|
||||||
Or | XOr | Pipe => 30,
|
Or | XOr | Pipe => 30,
|
||||||
|
|
||||||
@ -657,19 +657,22 @@ impl Token {
|
|||||||
|
|
||||||
match self {
|
match self {
|
||||||
// Assignments bind to the right
|
// Assignments bind to the right
|
||||||
Equals | PlusAssign | MinusAssign | MultiplyAssign | DivideAssign | LeftShiftAssign
|
Equals | PlusAssign | MinusAssign | MultiplyAssign | DivideAssign | PowerOfAssign
|
||||||
| RightShiftAssign | AndAssign | OrAssign | XOrAssign | ModuloAssign
|
| LeftShiftAssign | RightShiftAssign | AndAssign | OrAssign | XOrAssign
|
||||||
| PowerOfAssign => true,
|
| ModuloAssign => true,
|
||||||
|
|
||||||
// Property access binds to the right
|
// Property access binds to the right
|
||||||
Period => true,
|
Period => true,
|
||||||
|
|
||||||
|
// Exponentiation binds to the right
|
||||||
|
PowerOf => true,
|
||||||
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is this token an operator?
|
/// Is this token a standard symbol used in the language?
|
||||||
pub fn is_operator(&self) -> bool {
|
pub fn is_symbol(&self) -> bool {
|
||||||
use Token::*;
|
use Token::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
@ -1284,10 +1287,6 @@ fn get_next_token_inner(
|
|||||||
('-', _) if !state.non_unary => return Some((Token::UnaryMinus, start_pos)),
|
('-', _) if !state.non_unary => return Some((Token::UnaryMinus, start_pos)),
|
||||||
('-', _) => return Some((Token::Minus, start_pos)),
|
('-', _) => return Some((Token::Minus, start_pos)),
|
||||||
|
|
||||||
('*', '*') => {
|
|
||||||
eat_next(stream, pos);
|
|
||||||
return Some((Token::Reserved("**".into()), start_pos));
|
|
||||||
}
|
|
||||||
('*', ')') => {
|
('*', ')') => {
|
||||||
eat_next(stream, pos);
|
eat_next(stream, pos);
|
||||||
return Some((Token::Reserved("*)".into()), start_pos));
|
return Some((Token::Reserved("*)".into()), start_pos));
|
||||||
@ -1296,6 +1295,19 @@ fn get_next_token_inner(
|
|||||||
eat_next(stream, pos);
|
eat_next(stream, pos);
|
||||||
return Some((Token::MultiplyAssign, start_pos));
|
return Some((Token::MultiplyAssign, start_pos));
|
||||||
}
|
}
|
||||||
|
('*', '*') => {
|
||||||
|
eat_next(stream, pos);
|
||||||
|
|
||||||
|
return Some((
|
||||||
|
if stream.peek_next() == Some('=') {
|
||||||
|
eat_next(stream, pos);
|
||||||
|
Token::PowerOfAssign
|
||||||
|
} else {
|
||||||
|
Token::PowerOf
|
||||||
|
},
|
||||||
|
start_pos,
|
||||||
|
));
|
||||||
|
}
|
||||||
('*', _) => return Some((Token::Multiply, start_pos)),
|
('*', _) => return Some((Token::Multiply, start_pos)),
|
||||||
|
|
||||||
// Comments
|
// Comments
|
||||||
@ -1490,18 +1502,14 @@ fn get_next_token_inner(
|
|||||||
}
|
}
|
||||||
('^', _) => return Some((Token::XOr, start_pos)),
|
('^', _) => return Some((Token::XOr, start_pos)),
|
||||||
|
|
||||||
|
('~', _) => return Some((Token::Reserved("~".into()), start_pos)),
|
||||||
|
|
||||||
('%', '=') => {
|
('%', '=') => {
|
||||||
eat_next(stream, pos);
|
eat_next(stream, pos);
|
||||||
return Some((Token::ModuloAssign, start_pos));
|
return Some((Token::ModuloAssign, start_pos));
|
||||||
}
|
}
|
||||||
('%', _) => return Some((Token::Modulo, start_pos)),
|
('%', _) => return Some((Token::Modulo, start_pos)),
|
||||||
|
|
||||||
('~', '=') => {
|
|
||||||
eat_next(stream, pos);
|
|
||||||
return Some((Token::PowerOfAssign, start_pos));
|
|
||||||
}
|
|
||||||
('~', _) => return Some((Token::PowerOf, start_pos)),
|
|
||||||
|
|
||||||
('@', _) => return Some((Token::Reserved("@".into()), start_pos)),
|
('@', _) => return Some((Token::Reserved("@".into()), start_pos)),
|
||||||
|
|
||||||
('$', _) => return Some((Token::Reserved("$".into()), start_pos)),
|
('$', _) => return Some((Token::Reserved("$".into()), start_pos)),
|
||||||
|
@ -10,18 +10,20 @@ const EPSILON: FLOAT = 0.000_001;
|
|||||||
fn test_power_of() -> Result<(), Box<EvalAltResult>> {
|
fn test_power_of() -> Result<(), Box<EvalAltResult>> {
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(engine.eval::<INT>("2 ~ 3")?, 8);
|
assert_eq!(engine.eval::<INT>("2 ** 3")?, 8);
|
||||||
assert_eq!(engine.eval::<INT>("(-2 ~ 3)")?, -8);
|
assert_eq!(engine.eval::<INT>("(-2 ** 3)")?, -8);
|
||||||
|
assert_eq!(engine.eval::<INT>("2 ** 3 ** 2")?, 512);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
{
|
{
|
||||||
assert!(
|
assert!(
|
||||||
(engine.eval::<FLOAT>("2.2 ~ 3.3")? - 13.489_468_760_533_386 as FLOAT).abs() <= EPSILON
|
(engine.eval::<FLOAT>("2.2 ** 3.3")? - 13.489_468_760_533_386 as FLOAT).abs()
|
||||||
|
<= EPSILON
|
||||||
);
|
);
|
||||||
assert!((engine.eval::<FLOAT>("2.0~-2.0")? - 0.25 as FLOAT).abs() < EPSILON);
|
assert!((engine.eval::<FLOAT>("2.0**-2.0")? - 0.25 as FLOAT).abs() < EPSILON);
|
||||||
assert!((engine.eval::<FLOAT>("(-2.0~-2.0)")? - 0.25 as FLOAT).abs() < EPSILON);
|
assert!((engine.eval::<FLOAT>("(-2.0**-2.0)")? - 0.25 as FLOAT).abs() < EPSILON);
|
||||||
assert!((engine.eval::<FLOAT>("(-2.0~-2)")? - 0.25 as FLOAT).abs() < EPSILON);
|
assert!((engine.eval::<FLOAT>("(-2.0**-2)")? - 0.25 as FLOAT).abs() < EPSILON);
|
||||||
assert_eq!(engine.eval::<INT>("4~3")?, 64);
|
assert_eq!(engine.eval::<INT>("4**3")?, 64);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -31,26 +33,26 @@ fn test_power_of() -> Result<(), Box<EvalAltResult>> {
|
|||||||
fn test_power_of_equals() -> Result<(), Box<EvalAltResult>> {
|
fn test_power_of_equals() -> Result<(), Box<EvalAltResult>> {
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(engine.eval::<INT>("let x = 2; x ~= 3; x")?, 8);
|
assert_eq!(engine.eval::<INT>("let x = 2; x **= 3; x")?, 8);
|
||||||
assert_eq!(engine.eval::<INT>("let x = -2; x ~= 3; x")?, -8);
|
assert_eq!(engine.eval::<INT>("let x = -2; x **= 3; x")?, -8);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
{
|
{
|
||||||
assert!(
|
assert!(
|
||||||
(engine.eval::<FLOAT>("let x = 2.2; x ~= 3.3; x")? - 13.489_468_760_533_386 as FLOAT)
|
(engine.eval::<FLOAT>("let x = 2.2; x **= 3.3; x")? - 13.489_468_760_533_386 as FLOAT)
|
||||||
.abs()
|
.abs()
|
||||||
<= EPSILON
|
<= EPSILON
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
(engine.eval::<FLOAT>("let x = 2.0; x ~= -2.0; x")? - 0.25 as FLOAT).abs() < EPSILON
|
(engine.eval::<FLOAT>("let x = 2.0; x **= -2.0; x")? - 0.25 as FLOAT).abs() < EPSILON
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
(engine.eval::<FLOAT>("let x = -2.0; x ~= -2.0; x")? - 0.25 as FLOAT).abs() < EPSILON
|
(engine.eval::<FLOAT>("let x = -2.0; x **= -2.0; x")? - 0.25 as FLOAT).abs() < EPSILON
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
(engine.eval::<FLOAT>("let x = -2.0; x ~= -2; x")? - 0.25 as FLOAT).abs() < EPSILON
|
(engine.eval::<FLOAT>("let x = -2.0; x **= -2; x")? - 0.25 as FLOAT).abs() < EPSILON
|
||||||
);
|
);
|
||||||
assert_eq!(engine.eval::<INT>("let x =4; x ~= 3; x")?, 64);
|
assert_eq!(engine.eval::<INT>("let x =4; x **= 3; x")?, 64);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
Loading…
Reference in New Issue
Block a user