Merge pull request #347 from schungx/master
Use radians for trig and ** for exponentiation.
This commit is contained in:
commit
bd633a2540
@ -13,7 +13,12 @@ Bug fixes
|
||||
Breaking changes
|
||||
----------------
|
||||
|
||||
* 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.
|
||||
* `Token::is_operator` is renamed to `Token::is_symbol`.
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
@ -55,6 +60,7 @@ Enhancements
|
||||
* `ahash` is used to hash function call parameters. This should yield speed improvements.
|
||||
* `Dynamic` and `ImmutableString` now implement `serde::Serialize` and `serde::Deserialize`.
|
||||
* `NativeCallContext` has a new field containing the name of the function called, useful when the same Rust function is registered under multiple names in Rhai.
|
||||
* New functions `PI()` and `E()` to return mathematical constants, and `to_radians` and `to_degrees` to convert between radians and degrees.
|
||||
|
||||
|
||||
Version 0.19.10
|
||||
|
@ -288,8 +288,8 @@ impl Engine {
|
||||
return Err(format!("'{}' is a reserved keyword", keyword).into());
|
||||
}
|
||||
}
|
||||
// Active standard operators cannot be made custom
|
||||
Some(token) if token.is_operator() => {
|
||||
// Active standard symbols cannot be made custom
|
||||
Some(token) if token.is_symbol() => {
|
||||
if !self.disabled_symbols.contains(token.syntax().as_ref()) {
|
||||
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 divide(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_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.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())),
|
||||
_ => (),
|
||||
@ -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.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())),
|
||||
|
@ -68,7 +68,7 @@ macro_rules! gen_arithmetic_functions {
|
||||
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>> {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
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 {
|
||||
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>> {
|
||||
Ok(Dynamic::from(x.powf(y)))
|
||||
}
|
||||
@ -271,7 +271,7 @@ mod f32_functions {
|
||||
1
|
||||
}
|
||||
}
|
||||
#[rhai_fn(name = "~", return_raw)]
|
||||
#[rhai_fn(name = "**", return_raw)]
|
||||
pub fn pow_f_i(x: f32, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
if cfg!(not(feature = "unchecked")) && y > (i32::MAX as INT) {
|
||||
Err(make_err(format!(
|
||||
@ -309,7 +309,7 @@ mod f64_functions {
|
||||
pub fn modulo(x: f64, y: f64) -> f64 {
|
||||
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>> {
|
||||
Ok(Dynamic::from(x.powf(y)))
|
||||
}
|
||||
@ -334,7 +334,7 @@ mod f64_functions {
|
||||
1
|
||||
}
|
||||
}
|
||||
#[rhai_fn(name = "~", return_raw)]
|
||||
#[rhai_fn(name = "**", return_raw)]
|
||||
pub fn pow_f_i(x: FLOAT, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
if cfg!(not(feature = "unchecked")) && y > (i32::MAX as INT) {
|
||||
Err(make_err(format!(
|
||||
|
@ -112,40 +112,40 @@ mod trig_functions {
|
||||
use crate::FLOAT;
|
||||
|
||||
pub fn sin(x: FLOAT) -> FLOAT {
|
||||
x.to_radians().sin()
|
||||
x.sin()
|
||||
}
|
||||
pub fn cos(x: FLOAT) -> FLOAT {
|
||||
x.to_radians().cos()
|
||||
x.cos()
|
||||
}
|
||||
pub fn tan(x: FLOAT) -> FLOAT {
|
||||
x.to_radians().tan()
|
||||
x.tan()
|
||||
}
|
||||
pub fn sinh(x: FLOAT) -> FLOAT {
|
||||
x.to_radians().sinh()
|
||||
x.sinh()
|
||||
}
|
||||
pub fn cosh(x: FLOAT) -> FLOAT {
|
||||
x.to_radians().cosh()
|
||||
x.cosh()
|
||||
}
|
||||
pub fn tanh(x: FLOAT) -> FLOAT {
|
||||
x.to_radians().tanh()
|
||||
x.tanh()
|
||||
}
|
||||
pub fn asin(x: FLOAT) -> FLOAT {
|
||||
x.asin().to_degrees()
|
||||
x.asin()
|
||||
}
|
||||
pub fn acos(x: FLOAT) -> FLOAT {
|
||||
x.acos().to_degrees()
|
||||
x.acos()
|
||||
}
|
||||
pub fn atan(x: FLOAT) -> FLOAT {
|
||||
x.atan().to_degrees()
|
||||
x.atan()
|
||||
}
|
||||
pub fn asinh(x: FLOAT) -> FLOAT {
|
||||
x.asinh().to_degrees()
|
||||
x.asinh()
|
||||
}
|
||||
pub fn acosh(x: FLOAT) -> FLOAT {
|
||||
x.acosh().to_degrees()
|
||||
x.acosh()
|
||||
}
|
||||
pub fn atanh(x: FLOAT) -> FLOAT {
|
||||
x.atanh().to_degrees()
|
||||
x.atanh()
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,6 +154,26 @@ mod trig_functions {
|
||||
mod float_functions {
|
||||
use crate::FLOAT;
|
||||
|
||||
#[rhai_fn(name = "E")]
|
||||
pub fn e() -> FLOAT {
|
||||
#[cfg(not(feature = "f32_float"))]
|
||||
return crate::stdlib::f64::consts::E;
|
||||
#[cfg(feature = "f32_float")]
|
||||
return crate::stdlib::f32::consts::E;
|
||||
}
|
||||
#[rhai_fn(name = "PI")]
|
||||
pub fn pi() -> FLOAT {
|
||||
#[cfg(not(feature = "f32_float"))]
|
||||
return crate::stdlib::f64::consts::PI;
|
||||
#[cfg(feature = "f32_float")]
|
||||
return crate::stdlib::f32::consts::PI;
|
||||
}
|
||||
pub fn to_radians(x: FLOAT) -> FLOAT {
|
||||
x.to_radians()
|
||||
}
|
||||
pub fn to_degrees(x: FLOAT) -> FLOAT {
|
||||
x.to_degrees()
|
||||
}
|
||||
pub fn sqrt(x: FLOAT) -> FLOAT {
|
||||
x.sqrt()
|
||||
}
|
||||
|
64
src/token.rs
64
src/token.rs
@ -194,7 +194,7 @@ pub enum Token {
|
||||
Divide,
|
||||
/// `%`
|
||||
Modulo,
|
||||
/// `~`
|
||||
/// `**`
|
||||
PowerOf,
|
||||
/// `<<`
|
||||
LeftShift,
|
||||
@ -305,7 +305,7 @@ pub enum Token {
|
||||
XOrAssign,
|
||||
/// `%=`
|
||||
ModuloAssign,
|
||||
/// `~=`
|
||||
/// `**=`
|
||||
PowerOfAssign,
|
||||
/// `private`
|
||||
///
|
||||
@ -422,8 +422,8 @@ impl Token {
|
||||
XOr => "^",
|
||||
Modulo => "%",
|
||||
ModuloAssign => "%=",
|
||||
PowerOf => "~",
|
||||
PowerOfAssign => "~=",
|
||||
PowerOf => "**",
|
||||
PowerOfAssign => "**=",
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Fn => "fn",
|
||||
@ -511,8 +511,8 @@ impl Token {
|
||||
"^" => XOr,
|
||||
"%" => Modulo,
|
||||
"%=" => ModuloAssign,
|
||||
"~" => PowerOf,
|
||||
"~=" => PowerOfAssign,
|
||||
"**" => PowerOf,
|
||||
"**=" => PowerOfAssign,
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
"fn" => Fn,
|
||||
@ -532,7 +532,7 @@ impl Token {
|
||||
#[cfg(feature = "no_module")]
|
||||
"import" | "export" | "as" => Reserved(syntax.into()),
|
||||
|
||||
"===" | "!==" | "->" | "<-" | ":=" | "**" | "::<" | "(*" | "*)" | "#" | "public"
|
||||
"===" | "!==" | "->" | "<-" | ":=" | "~" | "::<" | "(*" | "*)" | "#" | "public"
|
||||
| "new" | "use" | "module" | "package" | "var" | "static" | "begin" | "end"
|
||||
| "shared" | "with" | "each" | "then" | "goto" | "unless" | "exit" | "match"
|
||||
| "case" | "default" | "void" | "null" | "nil" | "spawn" | "thread" | "go" | "sync"
|
||||
@ -599,6 +599,8 @@ impl Token {
|
||||
DivideAssign |
|
||||
LeftShiftAssign |
|
||||
RightShiftAssign |
|
||||
PowerOf |
|
||||
PowerOfAssign |
|
||||
AndAssign |
|
||||
OrAssign |
|
||||
XOrAssign |
|
||||
@ -609,9 +611,7 @@ impl Token {
|
||||
ModuloAssign |
|
||||
Return |
|
||||
Throw |
|
||||
PowerOf |
|
||||
In |
|
||||
PowerOfAssign => true,
|
||||
In => true,
|
||||
|
||||
_ => false,
|
||||
}
|
||||
@ -623,9 +623,9 @@ impl Token {
|
||||
|
||||
match self {
|
||||
// Assignments are not considered expressions - set to zero
|
||||
Equals | PlusAssign | MinusAssign | MultiplyAssign | DivideAssign | LeftShiftAssign
|
||||
| RightShiftAssign | AndAssign | OrAssign | XOrAssign | ModuloAssign
|
||||
| PowerOfAssign => 0,
|
||||
Equals | PlusAssign | MinusAssign | MultiplyAssign | DivideAssign | PowerOfAssign
|
||||
| LeftShiftAssign | RightShiftAssign | AndAssign | OrAssign | XOrAssign
|
||||
| ModuloAssign => 0,
|
||||
|
||||
Or | XOr | Pipe => 30,
|
||||
|
||||
@ -657,19 +657,22 @@ impl Token {
|
||||
|
||||
match self {
|
||||
// Assignments bind to the right
|
||||
Equals | PlusAssign | MinusAssign | MultiplyAssign | DivideAssign | LeftShiftAssign
|
||||
| RightShiftAssign | AndAssign | OrAssign | XOrAssign | ModuloAssign
|
||||
| PowerOfAssign => true,
|
||||
Equals | PlusAssign | MinusAssign | MultiplyAssign | DivideAssign | PowerOfAssign
|
||||
| LeftShiftAssign | RightShiftAssign | AndAssign | OrAssign | XOrAssign
|
||||
| ModuloAssign => true,
|
||||
|
||||
// Property access binds to the right
|
||||
Period => true,
|
||||
|
||||
// Exponentiation binds to the right
|
||||
PowerOf => true,
|
||||
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Is this token an operator?
|
||||
pub fn is_operator(&self) -> bool {
|
||||
/// Is this token a standard symbol used in the language?
|
||||
pub fn is_symbol(&self) -> bool {
|
||||
use Token::*;
|
||||
|
||||
match self {
|
||||
@ -1284,10 +1287,6 @@ fn get_next_token_inner(
|
||||
('-', _) if !state.non_unary => return Some((Token::UnaryMinus, start_pos)),
|
||||
('-', _) => return Some((Token::Minus, start_pos)),
|
||||
|
||||
('*', '*') => {
|
||||
eat_next(stream, pos);
|
||||
return Some((Token::Reserved("**".into()), start_pos));
|
||||
}
|
||||
('*', ')') => {
|
||||
eat_next(stream, pos);
|
||||
return Some((Token::Reserved("*)".into()), start_pos));
|
||||
@ -1296,6 +1295,19 @@ fn get_next_token_inner(
|
||||
eat_next(stream, 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)),
|
||||
|
||||
// Comments
|
||||
@ -1490,18 +1502,14 @@ fn get_next_token_inner(
|
||||
}
|
||||
('^', _) => return Some((Token::XOr, start_pos)),
|
||||
|
||||
('~', _) => return Some((Token::Reserved("~".into()), start_pos)),
|
||||
|
||||
('%', '=') => {
|
||||
eat_next(stream, pos);
|
||||
return Some((Token::ModuloAssign, 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)),
|
||||
|
@ -14,10 +14,10 @@ fn test_math() -> Result<(), Box<EvalAltResult>> {
|
||||
assert_eq!(engine.eval::<INT>("3 % 2")?, 1);
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
assert!((engine.eval::<FLOAT>("sin(30.0)")? - 0.5).abs() < 0.001);
|
||||
assert!((engine.eval::<FLOAT>("sin(PI()/6.0)")? - 0.5).abs() < 0.001);
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
assert!(engine.eval::<FLOAT>("cos(90.0)")?.abs() < 0.001);
|
||||
assert!(engine.eval::<FLOAT>("cos(PI()/2.0)")?.abs() < 0.001);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
assert_eq!(
|
||||
|
@ -10,18 +10,20 @@ const EPSILON: FLOAT = 0.000_001;
|
||||
fn test_power_of() -> Result<(), Box<EvalAltResult>> {
|
||||
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"))]
|
||||
{
|
||||
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.25 as FLOAT).abs() < EPSILON);
|
||||
assert_eq!(engine.eval::<INT>("4~3")?, 64);
|
||||
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_eq!(engine.eval::<INT>("4**3")?, 64);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -31,26 +33,26 @@ fn test_power_of() -> Result<(), Box<EvalAltResult>> {
|
||||
fn test_power_of_equals() -> Result<(), Box<EvalAltResult>> {
|
||||
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"))]
|
||||
{
|
||||
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()
|
||||
<= EPSILON
|
||||
);
|
||||
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!(
|
||||
(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!(
|
||||
(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(())
|
||||
|
Loading…
Reference in New Issue
Block a user