Add standard math functions and make power functions checked.

This commit is contained in:
Stephen Chung 2020-03-09 11:42:10 +08:00
parent e54fb54da2
commit b9d562eba4
2 changed files with 176 additions and 28 deletions

View File

@ -157,12 +157,14 @@ let result: i64 = engine.call_fn("hello", &ast, (&mut String::from("abc"), &mut
The following primitive types are supported natively: The following primitive types are supported natively:
* Integer: `i32`, `u32`, `i64` (default), `u64` | Category | Types |
* Floating-point: `f32`, `f64` (default) | ------------------------------ | -------------------------------------- |
* Character: `char` | Integer | `i32`, `u32`, `i64` _(default)_, `u64` |
* Boolean: `bool` | Floating-point | `f32`, `f64` _(default)_ |
* Array: `rhai::Array` | Character | `char` |
* Dynamic (i.e. can be anything): `rhai::Dynamic` | Boolean | `bool` |
| Array | `rhai::Array` |
| Dynamic (i.e. can be anything) | `rhai::Dynamic` |
# Value conversions # Value conversions
@ -508,16 +510,60 @@ fn main() {
## Variables ## Variables
Variables in `Rhai` follow normal naming rules:
* Must start with an ASCII letter
* Must contain only ASCII letters, digits and `_` underscores
Example:
```rust ```rust
let x = 3; let x = 3;
``` ```
## Numbers
| Format | Type |
| ---------------- | ------------------------------------------------------ |
| `123_345`, `-42` | `i64` in decimal, '`_`' separator can be used anywhere |
| `0o07_76` | `i64` in octal, '`_`' separator can be used anywhere |
| `0xabcd_ef` | `i64` in hex, '`_`' separator can be used anywhere |
| `0b0101_1001` | `i64` in binary, '`_`' separator can be used anywhere |
| `123_456.789` | `f64`, '`_`' separator can be used anywhere |
## Numeric operators ## Numeric operators
```rust ```rust
let x = (1 + 2) * (6 - 4) / 2; let x = (1 + 2) * (6 - 4) / 2; // arithmetic
let reminder = 42 % 10; // modulo
let power = 42 ~ 2; // power (i64 and f64 only)
let left_shifted = 42 << 3; // left shift
let right_shifted = 42 >> 3; // right shift
let bit_op = 42 | 99; // bit masking
``` ```
## Numeric functions
The following standard functions (defined in the standard library but excluded if you use the `no_stdlib` feature) operate on `i8`, `i16`, `i32`, `i64`, `f32` and `f64` only:
| Category | Functions |
| -------- | -------------- |
| `abs` | absolute value |
## Floating-point functions
The following standard functions (defined in the standard library but excluded if you use the `no_stdlib` feature) operate on `f64` only:
| Category | Functions |
| ---------------- | ------------------------------------------------------------ |
| Trigonometry | `sin`, `cos`, `tan`, `sinh`, `cosh`, `tanh` in degrees |
| Arc-trigonometry | `asin`, `acos`, `atan`, `asinh`, `acosh`, `atanh` in degrees |
| Square root | `sqrt` |
| Exponential | `exp` (base _e_) |
| Logarithmic | `ln` (base _e_), `log10` (base 10), `log` (any base) |
| Rounding | `floor`, `ceiling`, `round`, `int`, `fraction` |
| Tests | `is_nan`, `is_finite`, `is_infinite` |
## Comparison operators ## Comparison operators
You can compare most values of the same data type. If you compare two values of _different_ data types, the result is always `false`. You can compare most values of the same data type. If you compare two values of _different_ data types, the result is always `false`.
@ -664,13 +710,17 @@ You can create arrays of values, and then access them with numeric indices.
The following functions (defined in the standard library but excluded if you use the `no_stdlib` feature) operate on arrays: The following functions (defined in the standard library but excluded if you use the `no_stdlib` feature) operate on arrays:
* `push` - inserts an element at the end | Function | Description |
* `pop` - removes the last element and returns it (() if empty) | ---------- | ------------------------------------------------------------------------------------- |
* `shift` - removes the first element and returns it (() if empty) | `push` | inserts an element at the end |
* `len` - returns the number of elements | `pop` | removes the last element and returns it (`()` if empty) |
* `pad` - pads the array with an element until a specified length | `shift` | removes the first element and returns it (`()` if empty) |
* `clear` - empties the array | `len` | returns the number of elements |
* `truncate` - cuts off the array at exactly a specified length (discarding all subsequent elements) | `pad` | pads the array with an element until a specified length |
| `clear` | empties the array |
| `truncate` | cuts off the array at exactly a specified length (discarding all subsequent elements) |
Examples:
```rust ```rust
let y = [1, 2, 3]; // 3 elements let y = [1, 2, 3]; // 3 elements
@ -813,14 +863,18 @@ record == "Bob X. Davis: age 42 ❤\n";
The following standard functions (defined in the standard library but excluded if you use the `no_stdlib` feature) operate on strings: The following standard functions (defined in the standard library but excluded if you use the `no_stdlib` feature) operate on strings:
* `len` - returns the number of characters (not number of bytes) in the string | Function | Description |
* `pad` - pads the string with an character until a specified number of characters | ---------- | ------------------------------------------------------------------------ |
* `append` - Adds a character or a string to the end of another string | `len` | returns the number of characters (not number of bytes) in the string |
* `clear` - empties the string | `pad` | pads the string with an character until a specified number of characters |
* `truncate` - cuts off the string at exactly a specified number of characters | `append` | Adds a character or a string to the end of another string |
* `contains` - checks if a certain character or sub-string occurs in the string | `clear` | empties the string |
* `replace` - replaces a substring with another | `truncate` | cuts off the string at exactly a specified number of characters |
* `trim` - trims the string | `contains` | checks if a certain character or sub-string occurs in the string |
| `replace` | replaces a substring with another |
| `trim` | trims the string |
Examples:
```rust ```rust
let full_name == " Bob C. Davis "; let full_name == " Bob C. Davis ";

View File

@ -6,6 +6,7 @@ use crate::engine::{Array, Engine};
use crate::fn_register::RegisterFn; use crate::fn_register::RegisterFn;
use std::fmt::{Debug, Display}; use std::fmt::{Debug, Display};
use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Range, Rem, Sub}; use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Range, Rem, Sub};
use std::{i32, i64, u32};
#[cfg(feature = "unchecked")] #[cfg(feature = "unchecked")]
use std::ops::{Shl, Shr}; use std::ops::{Shl, Shr};
@ -293,12 +294,41 @@ impl Engine<'_> {
fn modulo_u<T: Rem>(x: T, y: T) -> <T as Rem>::Output { fn modulo_u<T: Rem>(x: T, y: T) -> <T as Rem>::Output {
x % y x % y
} }
#[cfg(not(feature = "unchecked"))]
fn pow_i64_i64_u(x: i64, y: i64) -> Result<i64, EvalAltResult> {
if y > (u32::MAX as i64) {
return Err(EvalAltResult::ErrorArithmetic(
format!("Power overflow: {} ~ {}", x, y),
Position::none(),
));
}
x.checked_pow(y as u32).ok_or_else(|| {
EvalAltResult::ErrorArithmetic(
format!("Power overflow: {} ~ {}", x, y),
Position::none(),
)
})
}
#[cfg(feature = "unchecked")]
fn pow_i64_i64(x: i64, y: i64) -> i64 { fn pow_i64_i64(x: i64, y: i64) -> i64 {
x.pow(y as u32) x.powi(y as u32)
} }
fn pow_f64_f64(x: f64, y: f64) -> f64 { fn pow_f64_f64(x: f64, y: f64) -> f64 {
x.powf(y) x.powf(y)
} }
#[cfg(not(feature = "unchecked"))]
fn pow_f64_i64_u(x: f64, y: i64) -> Result<f64, EvalAltResult> {
if y > (i32::MAX as i64) {
return Err(EvalAltResult::ErrorArithmetic(
format!("Power overflow: {} ~ {}", x, y),
Position::none(),
));
}
Ok(x.powi(y as i32))
}
#[cfg(feature = "unchecked")]
fn pow_f64_i64(x: f64, y: i64) -> f64 { fn pow_f64_i64(x: f64, y: i64) -> f64 {
x.powi(y as i32) x.powi(y as i32)
} }
@ -361,9 +391,19 @@ impl Engine<'_> {
reg_op!(self, "%", modulo_u, f32, f64); reg_op!(self, "%", modulo_u, f32, f64);
self.register_fn("~", pow_i64_i64);
self.register_fn("~", pow_f64_f64); self.register_fn("~", pow_f64_f64);
#[cfg(not(feature = "unchecked"))]
{
self.register_result_fn("~", pow_i64_i64_u);
self.register_result_fn("~", pow_f64_i64_u);
}
#[cfg(feature = "unchecked")]
{
self.register_fn("~", pow_i64_i64);
self.register_fn("~", pow_f64_i64); self.register_fn("~", pow_f64_i64);
}
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
{ {
@ -427,6 +467,33 @@ impl Engine<'_> {
pub(crate) fn register_stdlib(&mut self) { pub(crate) fn register_stdlib(&mut self) {
use crate::fn_register::RegisterDynamicFn; use crate::fn_register::RegisterDynamicFn;
// Advanced math functions
self.register_fn("sin", |x: f64| x.to_radians().sin());
self.register_fn("cos", |x: f64| x.to_radians().cos());
self.register_fn("tan", |x: f64| x.to_radians().tan());
self.register_fn("sinh", |x: f64| x.to_radians().sinh());
self.register_fn("cosh", |x: f64| x.to_radians().cosh());
self.register_fn("tanh", |x: f64| x.to_radians().tanh());
self.register_fn("asin", |x: f64| x.asin().to_degrees());
self.register_fn("acos", |x: f64| x.acos().to_degrees());
self.register_fn("atan", |x: f64| x.atan().to_degrees());
self.register_fn("asinh", |x: f64| x.asinh().to_degrees());
self.register_fn("acosh", |x: f64| x.acosh().to_degrees());
self.register_fn("atanh", |x: f64| x.atanh().to_degrees());
self.register_fn("sqrt", |x: f64| x.sqrt());
self.register_fn("exp", |x: f64| x.exp());
self.register_fn("ln", |x: f64| x.ln());
self.register_fn("log", |x: f64, base: f64| x.log(base));
self.register_fn("log10", |x: f64| x.log10());
self.register_fn("floor", |x: f64| x.floor());
self.register_fn("ceiling", |x: f64| x.ceil());
self.register_fn("round", |x: f64| x.ceil());
self.register_fn("int", |x: f64| x.trunc());
self.register_fn("fraction", |x: f64| x.fract());
self.register_fn("is_nan", |x: f64| x.is_nan());
self.register_fn("is_finite", |x: f64| x.is_finite());
self.register_fn("is_infinite", |x: f64| x.is_infinite());
// Register conversion functions // Register conversion functions
self.register_fn("to_float", |x: i8| x as f64); self.register_fn("to_float", |x: i8| x as f64);
self.register_fn("to_float", |x: u8| x as f64); self.register_fn("to_float", |x: u8| x as f64);
@ -445,10 +512,37 @@ impl Engine<'_> {
self.register_fn("to_int", |x: i32| x as i64); self.register_fn("to_int", |x: i32| x as i64);
self.register_fn("to_int", |x: u32| x as i64); self.register_fn("to_int", |x: u32| x as i64);
self.register_fn("to_int", |x: u64| x as i64); self.register_fn("to_int", |x: u64| x as i64);
self.register_fn("to_int", |ch: char| ch as i64);
#[cfg(not(feature = "unchecked"))]
{
self.register_result_fn("to_int", |x: f32| {
if x > (i64::MAX as f32) {
return Err(EvalAltResult::ErrorArithmetic(
format!("Integer overflow: to_int({})", x),
Position::none(),
));
}
Ok(x.trunc() as i64)
});
self.register_result_fn("to_int", |x: f64| {
if x > (i64::MAX as f64) {
return Err(EvalAltResult::ErrorArithmetic(
format!("Integer overflow: to_int({})", x),
Position::none(),
));
}
Ok(x.trunc() as i64)
});
}
#[cfg(feature = "unchecked")]
{
self.register_fn("to_int", |x: f32| x as i64); self.register_fn("to_int", |x: f32| x as i64);
self.register_fn("to_int", |x: f64| x as i64); self.register_fn("to_int", |x: f64| x as i64);
}
self.register_fn("to_int", |ch: char| ch as i64);
// Register array utility functions // Register array utility functions
fn push<T: Any>(list: &mut Array, item: T) { fn push<T: Any>(list: &mut Array, item: T) {