commit
8fc0f53c84
1
.github/workflows/build.yml
vendored
1
.github/workflows/build.yml
vendored
@ -23,6 +23,7 @@ jobs:
|
|||||||
- "--features sync"
|
- "--features sync"
|
||||||
- "--features no_optimize"
|
- "--features no_optimize"
|
||||||
- "--features no_float"
|
- "--features no_float"
|
||||||
|
- "--features f32_float"
|
||||||
- "--tests --features only_i32"
|
- "--tests --features only_i32"
|
||||||
- "--features only_i64"
|
- "--features only_i64"
|
||||||
- "--features no_index"
|
- "--features no_index"
|
||||||
|
@ -32,6 +32,7 @@ unchecked = [] # unchecked arithmetic
|
|||||||
sync = [] # restrict to only types that implement Send + Sync
|
sync = [] # restrict to only types that implement Send + Sync
|
||||||
no_optimize = [] # no script optimizer
|
no_optimize = [] # no script optimizer
|
||||||
no_float = [] # no floating-point
|
no_float = [] # no floating-point
|
||||||
|
f32_float = [] # set FLOAT=f32
|
||||||
only_i32 = [] # set INT=i32 (useful for 32-bit systems)
|
only_i32 = [] # set INT=i32 (useful for 32-bit systems)
|
||||||
only_i64 = [] # set INT=i64 (default) and disable support for all other integer types
|
only_i64 = [] # set INT=i64 (default) and disable support for all other integer types
|
||||||
no_index = [] # no arrays and indexing
|
no_index = [] # no arrays and indexing
|
||||||
|
@ -24,6 +24,7 @@ Breaking changes
|
|||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
* `f32_float` feature to set `FLOAT` to `f32`.
|
||||||
* Low-level API for custom syntax allowing more flexibility in designing the syntax.
|
* Low-level API for custom syntax allowing more flexibility in designing the syntax.
|
||||||
* `Module::fill_with` to poly-fill a module with another.
|
* `Module::fill_with` to poly-fill a module with another.
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ Fast
|
|||||||
* Fairly low compile-time overhead.
|
* Fairly low compile-time overhead.
|
||||||
|
|
||||||
* Fairly efficient evaluation (1 million iterations in 0.3 sec on a single core, 2.3 GHz Linux VM).
|
* Fairly efficient evaluation (1 million iterations in 0.3 sec on a single core, 2.3 GHz Linux VM).
|
||||||
|
An unofficial Fibonacci benchmark puts Rhai somewhere between Wren and Python.
|
||||||
|
|
||||||
* Scripts are [optimized][script optimization] (useful for template-based machine-generated scripts) for repeated evaluations.
|
* Scripts are [optimized][script optimization] (useful for template-based machine-generated scripts) for repeated evaluations.
|
||||||
|
|
||||||
|
@ -6,35 +6,45 @@ What Rhai Isn't
|
|||||||
Rhai's purpose is to provide a dynamic layer over Rust code, in the same spirit of _zero cost abstractions_.
|
Rhai's purpose is to provide a dynamic layer over Rust code, in the same spirit of _zero cost abstractions_.
|
||||||
It doesn't attempt to be a new language. For example:
|
It doesn't attempt to be a new language. For example:
|
||||||
|
|
||||||
* No classes. Well, Rust doesn't either. On the other hand...
|
* **No classes**. Well, Rust doesn't either. On the other hand...
|
||||||
|
|
||||||
* No traits... so it is also not Rust. Do your Rusty stuff in Rust.
|
* **No traits**... so it is also not Rust. Do your Rusty stuff in Rust.
|
||||||
|
|
||||||
* No structures/records/tuples - define your types in Rust instead; Rhai can seamlessly work with _any Rust type_.
|
* **No structures/records/tuples** - define your types in Rust instead; Rhai can seamlessly work with _any Rust type_.
|
||||||
|
|
||||||
There is, however, a built-in [object map] type which is adequate for most uses.
|
There is, however, a built-in [object map] type which is adequate for most uses.
|
||||||
It is possible to simulate [object-oriented programming (OOP)][OOP] by storing [function pointers]
|
It is possible to simulate [object-oriented programming (OOP)][OOP] by storing [function pointers]
|
||||||
or [closures] in [object map] properties, turning them into _methods_.
|
or [closures] in [object map] properties, turning them into _methods_.
|
||||||
|
|
||||||
* No first-class functions - Code your functions in Rust instead, and register them with Rhai.
|
* **No first-class functions** - Code your functions in Rust instead, and register them with Rhai.
|
||||||
|
|
||||||
There is, however, support for simple [function pointers] to allow runtime dispatch by function name.
|
There is, however, support for simple [function pointers] to allow runtime dispatch by function name.
|
||||||
|
|
||||||
* No garbage collection - this should be expected, so...
|
* **No garbage collection** - this should be expected, so...
|
||||||
|
|
||||||
* No first-class closures - do your closure magic in Rust instead: [turn a Rhai scripted function into a Rust closure]({{rootUrl}}/engine/call-fn.md).
|
* **No first-class closures** - do your closure magic in Rust instead: [turn a Rhai scripted function into a Rust closure]({{rootUrl}}/engine/call-fn.md).
|
||||||
|
|
||||||
There is, however, support for simulated [closures] via [currying] a [function pointer] with
|
There is, however, support for simulated [closures] via [currying] a [function pointer] with
|
||||||
captured shared variables.
|
captured shared variables.
|
||||||
|
|
||||||
* No byte-codes/JIT - Rhai has an AST-walking interpreter which will not win any speed races.
|
* **No byte-codes/JIT** - Rhai has an optimized AST-walking interpreter which is fast enough for most usage scenarios.
|
||||||
The purpose of Rhai is not to be extremely _fast_, but to make it as easy as possible to
|
Essential AST data structures are packed and kept together to maximize cache friendliness.
|
||||||
|
|
||||||
|
Functions are dispatched based on pre-calculated hashes and accessing variables are mostly through pre-calculated
|
||||||
|
offsets to the variables file (a [`Scope`]), so it is seldom necessary to look something up by text name.
|
||||||
|
|
||||||
|
In addition, Rhai's design deliberately avoids maintaining a _scope chain_ so function scopes do not
|
||||||
|
pay any speed penalty. This particular design also allows variables data to be kept together in a contiguous
|
||||||
|
block, avoiding allocations and fragmentation while being cache-friendly. In a typical script evaluation run,
|
||||||
|
no data is shared and nothing is locked.
|
||||||
|
|
||||||
|
Still, the purpose of Rhai is not to be super _fast_, but to make it as easy and versatile as possible to
|
||||||
integrate with native Rust applications.
|
integrate with native Rust applications.
|
||||||
|
|
||||||
* No formal language grammar - Rhai uses a hand-coded lexer, a hand-coded top-down recursive-descent parser
|
* **No formal language grammar** - Rhai uses a hand-coded lexer, a hand-coded top-down recursive-descent parser
|
||||||
for statements, and a hand-coded Pratt parser for expressions.
|
for statements, and a hand-coded Pratt parser for expressions.
|
||||||
|
|
||||||
This lack of formalism allows the parser itself to be exposed as a service in order to support
|
This lack of formalism allows the _parser_ itself to be exposed as a service in order to support
|
||||||
[disabling keywords/operators][disable keywords and operators], adding [custom operators],
|
[disabling keywords/operators][disable keywords and operators], adding [custom operators],
|
||||||
and defining [custom syntax].
|
and defining [custom syntax].
|
||||||
|
|
||||||
@ -45,6 +55,7 @@ Do Not Write The Next 4D VR Game in Rhai
|
|||||||
Due to this intended usage, Rhai deliberately keeps the language simple and small by omitting
|
Due to this intended usage, Rhai deliberately keeps the language simple and small by omitting
|
||||||
advanced language features such as classes, inheritance, interfaces, generics,
|
advanced language features such as classes, inheritance, interfaces, generics,
|
||||||
first-class functions/closures, pattern matching, concurrency, byte-codes VM, JIT etc.
|
first-class functions/closures, pattern matching, concurrency, byte-codes VM, JIT etc.
|
||||||
|
Focus is on _flexibility_ and _ease of use_ instead of raw speed.
|
||||||
|
|
||||||
Avoid the temptation to write full-fledge application logic entirely in Rhai -
|
Avoid the temptation to write full-fledge application logic entirely in Rhai -
|
||||||
that use case is best fulfilled by more complete languages such as JavaScript or Lua.
|
that use case is best fulfilled by more complete languages such as JavaScript or Lua.
|
||||||
|
@ -8,7 +8,7 @@ The following primitive types are supported natively:
|
|||||||
| Category | Equivalent Rust types | [`type_of()`] | `to_string()` |
|
| Category | Equivalent Rust types | [`type_of()`] | `to_string()` |
|
||||||
| -------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | --------------------- | ----------------------- |
|
| -------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | --------------------- | ----------------------- |
|
||||||
| **Integer number** | `u8`, `i8`, `u16`, `i16`, <br/>`u32`, `i32` (default for [`only_i32`]),<br/>`u64`, `i64` _(default)_ | `"i32"`, `"u64"` etc. | `"42"`, `"123"` etc. |
|
| **Integer number** | `u8`, `i8`, `u16`, `i16`, <br/>`u32`, `i32` (default for [`only_i32`]),<br/>`u64`, `i64` _(default)_ | `"i32"`, `"u64"` etc. | `"42"`, `"123"` etc. |
|
||||||
| **Floating-point number** (disabled with [`no_float`]) | `f32`, `f64` _(default)_ | `"f32"` or `"f64"` | `"123.4567"` etc. |
|
| **Floating-point number** (disabled with [`no_float`]) | `f32` (default for [`f32_float`]), `f64` _(default)_ | `"f32"` or `"f64"` | `"123.4567"` etc. |
|
||||||
| **Boolean value** | `bool` | `"bool"` | `"true"` or `"false"` |
|
| **Boolean value** | `bool` | `"bool"` | `"true"` or `"false"` |
|
||||||
| **Unicode character** | `char` | `"char"` | `"A"`, `"x"` etc. |
|
| **Unicode character** | `char` | `"char"` | `"A"`, `"x"` etc. |
|
||||||
| **Immutable Unicode [string]** | `rhai::ImmutableString` (implemented as `Rc<String>` or `Arc<String>`) | `"string"` | `"hello"` etc. |
|
| **Immutable Unicode [string]** | `rhai::ImmutableString` (implemented as `Rc<String>` or `Arc<String>`) | `"string"` | `"hello"` etc. |
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
[`sync`]: {{rootUrl}}/start/features.md
|
[`sync`]: {{rootUrl}}/start/features.md
|
||||||
[`no_optimize`]: {{rootUrl}}/start/features.md
|
[`no_optimize`]: {{rootUrl}}/start/features.md
|
||||||
[`no_float`]: {{rootUrl}}/start/features.md
|
[`no_float`]: {{rootUrl}}/start/features.md
|
||||||
|
[`f32_float`]: {{rootUrl}}/start/features.md
|
||||||
[`only_i32`]: {{rootUrl}}/start/features.md
|
[`only_i32`]: {{rootUrl}}/start/features.md
|
||||||
[`only_i64`]: {{rootUrl}}/start/features.md
|
[`only_i64`]: {{rootUrl}}/start/features.md
|
||||||
[`no_index`]: {{rootUrl}}/start/features.md
|
[`no_index`]: {{rootUrl}}/start/features.md
|
||||||
|
@ -17,6 +17,7 @@ more control over what a script can (or cannot) do.
|
|||||||
| `sync` | no | restricts all values types to those that are `Send + Sync`. Under this feature, all Rhai types, including [`Engine`], [`Scope`] and [`AST`], are all `Send + Sync` |
|
| `sync` | no | restricts all values types to those that are `Send + Sync`. Under this feature, all Rhai types, including [`Engine`], [`Scope`] and [`AST`], are all `Send + Sync` |
|
||||||
| `no_optimize` | no | disables [script optimization] |
|
| `no_optimize` | no | disables [script optimization] |
|
||||||
| `no_float` | no | disables floating-point numbers and math |
|
| `no_float` | no | disables floating-point numbers and math |
|
||||||
|
| `f32_float` | no | sets the system floating-point type to `f32` instead of `f64` |
|
||||||
| `only_i32` | no | sets the system integer type to `i32` and disable all other integer types. `INT` is set to `i32` |
|
| `only_i32` | no | sets the system integer type to `i32` and disable all other integer types. `INT` is set to `i32` |
|
||||||
| `only_i64` | no | sets the system integer type to `i64` and disable all other integer types. `INT` is set to `i64` |
|
| `only_i64` | no | sets the system integer type to `i64` and disable all other integer types. `INT` is set to `i64` |
|
||||||
| `no_index` | no | disables [arrays] and indexing features |
|
| `no_index` | no | disables [arrays] and indexing features |
|
||||||
|
10
src/ast.rs
10
src/ast.rs
@ -539,7 +539,7 @@ impl AsRef<Module> for AST {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// An identifier containing a string name and a position.
|
/// An identifier containing a string name and a position.
|
||||||
#[derive(Debug, Clone, Hash)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct Ident {
|
pub struct Ident {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub pos: Position,
|
pub pos: Position,
|
||||||
@ -553,7 +553,7 @@ impl Ident {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// An identifier containing an immutable name and a position.
|
/// An identifier containing an immutable name and a position.
|
||||||
#[derive(Debug, Clone, Hash)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct IdentX {
|
pub struct IdentX {
|
||||||
pub name: ImmutableString,
|
pub name: ImmutableString,
|
||||||
pub pos: Position,
|
pub pos: Position,
|
||||||
@ -866,7 +866,7 @@ pub struct FnCallInfo {
|
|||||||
/// and the function names are predictable, so no need to allocate a new `String`.
|
/// and the function names are predictable, so no need to allocate a new `String`.
|
||||||
pub name: Cow<'static, str>,
|
pub name: Cow<'static, str>,
|
||||||
/// Namespace of the function, if any.
|
/// Namespace of the function, if any.
|
||||||
pub namespace: Option<Box<ModuleRef>>,
|
pub namespace: Option<ModuleRef>,
|
||||||
/// Call native functions only? Set to `true` to skip searching for script-defined function overrides
|
/// Call native functions only? Set to `true` to skip searching for script-defined function overrides
|
||||||
/// when it is certain that the function must be native (e.g. an operator).
|
/// when it is certain that the function must be native (e.g. an operator).
|
||||||
pub native_only: bool,
|
pub native_only: bool,
|
||||||
@ -903,8 +903,8 @@ pub enum Expr {
|
|||||||
StringConstant(Box<IdentX>),
|
StringConstant(Box<IdentX>),
|
||||||
/// FnPtr constant.
|
/// FnPtr constant.
|
||||||
FnPointer(Box<IdentX>),
|
FnPointer(Box<IdentX>),
|
||||||
/// Variable access - ((variable name, position), optional modules, hash, optional index)
|
/// Variable access - (variable name, optional modules, hash, optional index)
|
||||||
Variable(Box<(Ident, Option<Box<ModuleRef>>, u64, Option<NonZeroUsize>)>),
|
Variable(Box<(Ident, Option<ModuleRef>, u64, Option<NonZeroUsize>)>),
|
||||||
/// Property access.
|
/// Property access.
|
||||||
Property(Box<(IdentX, (String, String))>),
|
Property(Box<(IdentX, (String, String))>),
|
||||||
/// { stmt }
|
/// { stmt }
|
||||||
|
@ -589,9 +589,9 @@ fn default_print(_s: &str) {
|
|||||||
pub fn search_imports<'s>(
|
pub fn search_imports<'s>(
|
||||||
mods: &'s Imports,
|
mods: &'s Imports,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
modules: &Box<ModuleRef>,
|
modules: &ModuleRef,
|
||||||
) -> Result<&'s Module, Box<EvalAltResult>> {
|
) -> Result<&'s Module, Box<EvalAltResult>> {
|
||||||
let (root, root_pos) = &modules[0];
|
let Ident { name: root, pos } = &modules[0];
|
||||||
|
|
||||||
// Qualified - check if the root module is directly indexed
|
// Qualified - check if the root module is directly indexed
|
||||||
let index = if state.always_search {
|
let index = if state.always_search {
|
||||||
@ -608,7 +608,7 @@ pub fn search_imports<'s>(
|
|||||||
.rev()
|
.rev()
|
||||||
.find(|(n, _)| n == root)
|
.find(|(n, _)| n == root)
|
||||||
.map(|(_, m)| m)
|
.map(|(_, m)| m)
|
||||||
.ok_or_else(|| EvalAltResult::ErrorModuleNotFound(root.to_string(), *root_pos))?
|
.ok_or_else(|| EvalAltResult::ErrorModuleNotFound(root.to_string(), *pos))?
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -617,9 +617,9 @@ pub fn search_imports<'s>(
|
|||||||
pub fn search_imports_mut<'s>(
|
pub fn search_imports_mut<'s>(
|
||||||
mods: &'s mut Imports,
|
mods: &'s mut Imports,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
modules: &Box<ModuleRef>,
|
modules: &ModuleRef,
|
||||||
) -> Result<&'s mut Module, Box<EvalAltResult>> {
|
) -> Result<&'s mut Module, Box<EvalAltResult>> {
|
||||||
let (root, root_pos) = &modules[0];
|
let Ident { name: root, pos } = &modules[0];
|
||||||
|
|
||||||
// Qualified - check if the root module is directly indexed
|
// Qualified - check if the root module is directly indexed
|
||||||
let index = if state.always_search {
|
let index = if state.always_search {
|
||||||
@ -636,7 +636,7 @@ pub fn search_imports_mut<'s>(
|
|||||||
.rev()
|
.rev()
|
||||||
.find(|(n, _)| n == root)
|
.find(|(n, _)| n == root)
|
||||||
.map(|(_, m)| m)
|
.map(|(_, m)| m)
|
||||||
.ok_or_else(|| EvalAltResult::ErrorModuleNotFound(root.to_string(), *root_pos))?
|
.ok_or_else(|| EvalAltResult::ErrorModuleNotFound(root.to_string(), *pos))?
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1094,7 +1094,7 @@ impl Engine {
|
|||||||
state: &mut State,
|
state: &mut State,
|
||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
modules: &Option<Box<ModuleRef>>,
|
modules: &Option<ModuleRef>,
|
||||||
name: &str,
|
name: &str,
|
||||||
args_expr: impl AsRef<[Expr]>,
|
args_expr: impl AsRef<[Expr]>,
|
||||||
def_val: Option<bool>,
|
def_val: Option<bool>,
|
||||||
|
@ -99,8 +99,16 @@ pub type INT = i32;
|
|||||||
///
|
///
|
||||||
/// Not available under the `no_float` feature.
|
/// Not available under the `no_float` feature.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
#[cfg(not(feature = "f32_float"))]
|
||||||
pub type FLOAT = f64;
|
pub type FLOAT = f64;
|
||||||
|
|
||||||
|
/// The system floating-point type.
|
||||||
|
///
|
||||||
|
/// Not available under the `no_float` feature.
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
#[cfg(feature = "f32_float")]
|
||||||
|
pub type FLOAT = f32;
|
||||||
|
|
||||||
pub use ast::AST;
|
pub use ast::AST;
|
||||||
pub use dynamic::Dynamic;
|
pub use dynamic::Dynamic;
|
||||||
pub use engine::{Engine, EvalContext};
|
pub use engine::{Engine, EvalContext};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Module defining external-loaded modules for Rhai.
|
//! Module defining external-loaded modules for Rhai.
|
||||||
|
|
||||||
use crate::ast::FnAccess;
|
use crate::ast::{FnAccess, Ident};
|
||||||
use crate::dynamic::{Dynamic, Variant};
|
use crate::dynamic::{Dynamic, Variant};
|
||||||
use crate::fn_native::{CallableFunction, FnCallArgs, IteratorFn, NativeCallContext, SendSync};
|
use crate::fn_native::{CallableFunction, FnCallArgs, IteratorFn, NativeCallContext, SendSync};
|
||||||
use crate::fn_register::by_value as cast_arg;
|
use crate::fn_register::by_value as cast_arg;
|
||||||
@ -1502,7 +1502,7 @@ impl Module {
|
|||||||
///
|
///
|
||||||
/// This type is volatile and may change.
|
/// This type is volatile and may change.
|
||||||
#[derive(Clone, Eq, PartialEq, Default, Hash)]
|
#[derive(Clone, Eq, PartialEq, Default, Hash)]
|
||||||
pub struct ModuleRef(StaticVec<(String, Position)>, Option<NonZeroUsize>);
|
pub struct ModuleRef(StaticVec<Ident>, Option<NonZeroUsize>);
|
||||||
|
|
||||||
impl fmt::Debug for ModuleRef {
|
impl fmt::Debug for ModuleRef {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
@ -1517,7 +1517,7 @@ impl fmt::Debug for ModuleRef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for ModuleRef {
|
impl Deref for ModuleRef {
|
||||||
type Target = StaticVec<(String, Position)>;
|
type Target = StaticVec<Ident>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.0
|
&self.0
|
||||||
@ -1532,15 +1532,15 @@ impl DerefMut for ModuleRef {
|
|||||||
|
|
||||||
impl fmt::Display for ModuleRef {
|
impl fmt::Display for ModuleRef {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
for (m, _) in self.0.iter() {
|
for Ident { name, .. } in self.0.iter() {
|
||||||
write!(f, "{}{}", m, Token::DoubleColon.syntax())?;
|
write!(f, "{}{}", name, Token::DoubleColon.syntax())?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<StaticVec<(String, Position)>> for ModuleRef {
|
impl From<StaticVec<Ident>> for ModuleRef {
|
||||||
fn from(modules: StaticVec<(String, Position)>) -> Self {
|
fn from(modules: StaticVec<Ident>) -> Self {
|
||||||
Self(modules, None)
|
Self(modules, None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -224,6 +224,8 @@ gen_signed_functions!(signed_num_128 => i128);
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod f32_functions {
|
mod f32_functions {
|
||||||
|
#[cfg(not(feature = "f32_float"))]
|
||||||
|
pub mod basic_arithmetic {
|
||||||
#[rhai_fn(name = "+")]
|
#[rhai_fn(name = "+")]
|
||||||
pub fn add(x: f32, y: f32) -> f32 {
|
pub fn add(x: f32, y: f32) -> f32 {
|
||||||
x + y
|
x + y
|
||||||
@ -244,6 +246,11 @@ 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)]
|
||||||
|
pub fn pow_f_f(x: f32, y: f32) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
Ok(Dynamic::from(x.powf(y)))
|
||||||
|
}
|
||||||
|
}
|
||||||
#[rhai_fn(name = "-")]
|
#[rhai_fn(name = "-")]
|
||||||
pub fn neg(x: f32) -> f32 {
|
pub fn neg(x: f32) -> f32 {
|
||||||
-x
|
-x
|
||||||
@ -261,10 +268,6 @@ mod f32_functions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[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)))
|
|
||||||
}
|
|
||||||
#[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!(
|
||||||
@ -280,6 +283,33 @@ mod f32_functions {
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod f64_functions {
|
mod f64_functions {
|
||||||
|
#[cfg(feature = "f32_float")]
|
||||||
|
pub mod basic_arithmetic {
|
||||||
|
#[rhai_fn(name = "+")]
|
||||||
|
pub fn add(x: f64, y: f64) -> f64 {
|
||||||
|
x + y
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "-")]
|
||||||
|
pub fn subtract(x: f64, y: f64) -> f64 {
|
||||||
|
x - y
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "*")]
|
||||||
|
pub fn multiply(x: f64, y: f64) -> f64 {
|
||||||
|
x * y
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "/")]
|
||||||
|
pub fn divide(x: f64, y: f64) -> f64 {
|
||||||
|
x / y
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "%")]
|
||||||
|
pub fn modulo(x: f64, y: f64) -> f64 {
|
||||||
|
x % y
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "~", return_raw)]
|
||||||
|
pub fn pow_f_f(x: f64, y: f64) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
Ok(Dynamic::from(x.powf(y)))
|
||||||
|
}
|
||||||
|
}
|
||||||
#[rhai_fn(name = "-")]
|
#[rhai_fn(name = "-")]
|
||||||
pub fn neg(x: f64) -> f64 {
|
pub fn neg(x: f64) -> f64 {
|
||||||
-x
|
-x
|
||||||
|
@ -215,10 +215,6 @@ mod float_functions {
|
|||||||
Ok((x.trunc() as INT).into())
|
Ok((x.trunc() as INT).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "to_float")]
|
|
||||||
pub fn f32_to_float(x: f32) -> FLOAT {
|
|
||||||
x as FLOAT
|
|
||||||
}
|
|
||||||
#[rhai_fn(name = "to_int", return_raw)]
|
#[rhai_fn(name = "to_int", return_raw)]
|
||||||
pub fn f64_to_int(x: f64) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn f64_to_int(x: f64) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f64) {
|
if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f64) {
|
||||||
@ -244,6 +240,13 @@ mod float_functions {
|
|||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
#[cfg(not(feature = "f32_float"))]
|
||||||
|
pub mod f32_f64 {
|
||||||
|
#[rhai_fn(name = "to_float")]
|
||||||
|
pub fn f32_to_f64(x: f32) -> f64 {
|
||||||
|
x as f64
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
@ -265,7 +265,7 @@ fn parse_fn_call(
|
|||||||
lib: &mut FunctionsLib,
|
lib: &mut FunctionsLib,
|
||||||
id: String,
|
id: String,
|
||||||
capture: bool,
|
capture: bool,
|
||||||
mut namespace: Option<Box<ModuleRef>>,
|
mut namespace: Option<ModuleRef>,
|
||||||
settings: ParseSettings,
|
settings: ParseSettings,
|
||||||
) -> Result<Expr, ParseError> {
|
) -> Result<Expr, ParseError> {
|
||||||
let (token, token_pos) = input.peek().unwrap();
|
let (token, token_pos) = input.peek().unwrap();
|
||||||
@ -292,7 +292,7 @@ fn parse_fn_call(
|
|||||||
|
|
||||||
let hash_script = if let Some(modules) = namespace.as_mut() {
|
let hash_script = if let Some(modules) = namespace.as_mut() {
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
modules.set_index(state.find_module(&modules[0].0));
|
modules.set_index(state.find_module(&modules[0].name));
|
||||||
|
|
||||||
// Rust functions are indexed in two steps:
|
// Rust functions are indexed in two steps:
|
||||||
// 1) Calculate a hash in a similar manner to script-defined functions,
|
// 1) Calculate a hash in a similar manner to script-defined functions,
|
||||||
@ -300,7 +300,7 @@ fn parse_fn_call(
|
|||||||
// 2) Calculate a second hash with no qualifiers, empty function name,
|
// 2) Calculate a second hash with no qualifiers, empty function name,
|
||||||
// zero number of arguments, and the actual list of argument `TypeId`'s.
|
// zero number of arguments, and the actual list of argument `TypeId`'s.
|
||||||
// 3) The final hash is the XOR of the two hashes.
|
// 3) The final hash is the XOR of the two hashes.
|
||||||
let qualifiers = modules.iter().map(|(m, _)| m.as_str());
|
let qualifiers = modules.iter().map(|m| m.name.as_str());
|
||||||
calc_script_fn_hash(qualifiers, &id, 0)
|
calc_script_fn_hash(qualifiers, &id, 0)
|
||||||
} else {
|
} else {
|
||||||
// Qualifiers (none) + function name + no parameters.
|
// Qualifiers (none) + function name + no parameters.
|
||||||
@ -339,7 +339,7 @@ fn parse_fn_call(
|
|||||||
|
|
||||||
let hash_script = if let Some(modules) = namespace.as_mut() {
|
let hash_script = if let Some(modules) = namespace.as_mut() {
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
modules.set_index(state.find_module(&modules[0].0));
|
modules.set_index(state.find_module(&modules[0].name));
|
||||||
|
|
||||||
// Rust functions are indexed in two steps:
|
// Rust functions are indexed in two steps:
|
||||||
// 1) Calculate a hash in a similar manner to script-defined functions,
|
// 1) Calculate a hash in a similar manner to script-defined functions,
|
||||||
@ -347,7 +347,7 @@ fn parse_fn_call(
|
|||||||
// 2) Calculate a second hash with no qualifiers, empty function name,
|
// 2) Calculate a second hash with no qualifiers, empty function name,
|
||||||
// zero number of arguments, and the actual list of argument `TypeId`'s.
|
// zero number of arguments, and the actual list of argument `TypeId`'s.
|
||||||
// 3) The final hash is the XOR of the two hashes.
|
// 3) The final hash is the XOR of the two hashes.
|
||||||
let qualifiers = modules.iter().map(|(m, _)| m.as_str());
|
let qualifiers = modules.iter().map(|m| m.name.as_str());
|
||||||
calc_script_fn_hash(qualifiers, &id, args.len())
|
calc_script_fn_hash(qualifiers, &id, args.len())
|
||||||
} else {
|
} else {
|
||||||
// Qualifiers (none) + function name + number of arguments.
|
// Qualifiers (none) + function name + number of arguments.
|
||||||
@ -891,14 +891,14 @@ fn parse_primary(
|
|||||||
// module access
|
// module access
|
||||||
(Expr::Variable(x), Token::DoubleColon) => match input.next().unwrap() {
|
(Expr::Variable(x), Token::DoubleColon) => match input.next().unwrap() {
|
||||||
(Token::Identifier(id2), pos2) => {
|
(Token::Identifier(id2), pos2) => {
|
||||||
let (Ident { name, pos }, mut modules, _, index) = *x;
|
let (var_name_def, mut modules, _, index) = *x;
|
||||||
|
|
||||||
if let Some(ref mut modules) = modules {
|
if let Some(ref mut modules) = modules {
|
||||||
modules.push((name, pos));
|
modules.push(var_name_def);
|
||||||
} else {
|
} else {
|
||||||
let mut m: ModuleRef = Default::default();
|
let mut m: ModuleRef = Default::default();
|
||||||
m.push((name, pos));
|
m.push(var_name_def);
|
||||||
modules = Some(Box::new(m));
|
modules = Some(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Variable(Box::new((Ident::new(id2, pos2), modules, 0, index)))
|
Expr::Variable(Box::new((Ident::new(id2, pos2), modules, 0, index)))
|
||||||
@ -929,10 +929,10 @@ fn parse_primary(
|
|||||||
let modules = modules.as_mut().unwrap();
|
let modules = modules.as_mut().unwrap();
|
||||||
|
|
||||||
// Qualifiers + variable name
|
// Qualifiers + variable name
|
||||||
*hash = calc_script_fn_hash(modules.iter().map(|(v, _)| v.as_str()), name, 0);
|
*hash = calc_script_fn_hash(modules.iter().map(|v| v.name.as_str()), name, 0);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
modules.set_index(state.find_module(&modules[0].0));
|
modules.set_index(state.find_module(&modules[0].name));
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
@ -1206,7 +1206,7 @@ fn make_dot_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseEr
|
|||||||
}
|
}
|
||||||
// lhs.module::id - syntax error
|
// lhs.module::id - syntax error
|
||||||
(_, Expr::Variable(x)) if x.1.is_some() => {
|
(_, Expr::Variable(x)) if x.1.is_some() => {
|
||||||
return Err(PERR::PropertyExpected.into_err(x.1.unwrap()[0].1));
|
return Err(PERR::PropertyExpected.into_err(x.1.unwrap()[0].pos));
|
||||||
}
|
}
|
||||||
// lhs.prop
|
// lhs.prop
|
||||||
(lhs, prop @ Expr::Property(_)) => {
|
(lhs, prop @ Expr::Property(_)) => {
|
||||||
|
@ -34,19 +34,19 @@ fn test_float_parse() -> Result<(), Box<EvalAltResult>> {
|
|||||||
fn test_struct_with_float() -> Result<(), Box<EvalAltResult>> {
|
fn test_struct_with_float() -> Result<(), Box<EvalAltResult>> {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct TestStruct {
|
struct TestStruct {
|
||||||
x: f64,
|
x: FLOAT,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestStruct {
|
impl TestStruct {
|
||||||
fn update(&mut self) {
|
fn update(&mut self) {
|
||||||
self.x += 5.789_f64;
|
self.x += 5.789;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_x(&mut self) -> f64 {
|
fn get_x(&mut self) -> FLOAT {
|
||||||
self.x
|
self.x
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_x(&mut self, new_x: f64) {
|
fn set_x(&mut self, new_x: FLOAT) {
|
||||||
self.x = new_x;
|
self.x = new_x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ fn test_module_resolver() -> Result<(), Box<EvalAltResult>> {
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
module.set_fn_4_mut(
|
module.set_fn_4_mut(
|
||||||
"sum_of_three_args".to_string(),
|
"sum_of_three_args".to_string(),
|
||||||
|target: &mut INT, a: INT, b: INT, c: f64| {
|
|target: &mut INT, a: INT, b: INT, c: rhai::FLOAT| {
|
||||||
*target = a + b + c as INT;
|
*target = a + b + c as INT;
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
|
@ -4,7 +4,7 @@ use rhai::{Engine, EvalAltResult, INT};
|
|||||||
use rhai::FLOAT;
|
use rhai::FLOAT;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
const EPSILON: FLOAT = 0.000_000_000_1;
|
const EPSILON: FLOAT = 0.000_001;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_power_of() -> Result<(), Box<EvalAltResult>> {
|
fn test_power_of() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
@ -16,8 +16,14 @@ fn test_type_of() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert_eq!(engine.eval::<String>("type_of(60 + 5)")?, "i32");
|
assert_eq!(engine.eval::<String>("type_of(60 + 5)")?, "i32");
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
{
|
||||||
|
#[cfg(not(feature = "f32_float"))]
|
||||||
assert_eq!(engine.eval::<String>("type_of(1.0 + 2.0)")?, "f64");
|
assert_eq!(engine.eval::<String>("type_of(1.0 + 2.0)")?, "f64");
|
||||||
|
|
||||||
|
#[cfg(feature = "f32_float")]
|
||||||
|
assert_eq!(engine.eval::<String>("type_of(1.0 + 2.0)")?, "f32");
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<String>(r#"type_of([true, 2, "hello"])"#)?,
|
engine.eval::<String>(r#"type_of([true, 2, "hello"])"#)?,
|
||||||
|
Loading…
Reference in New Issue
Block a user