Add standard math functions and make power functions checked.
This commit is contained in:
parent
e54fb54da2
commit
b9d562eba4
98
README.md
98
README.md
@ -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 ";
|
||||||
|
106
src/builtin.rs
106
src/builtin.rs
@ -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);
|
||||||
self.register_fn("~", pow_f64_i64);
|
|
||||||
|
#[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);
|
||||||
|
}
|
||||||
|
|
||||||
#[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,11 +512,38 @@ 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", |x: f32| x as i64);
|
|
||||||
self.register_fn("to_int", |x: f64| x as i64);
|
|
||||||
|
|
||||||
self.register_fn("to_int", |ch: char| ch 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: f64| x 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) {
|
||||||
list.push(Box::new(item));
|
list.push(Box::new(item));
|
||||||
|
Loading…
Reference in New Issue
Block a user