Merge pull request #365 from schungx/master
Speed and other improvements.
This commit is contained in:
commit
86a571fda9
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,5 +2,6 @@ target/
|
|||||||
Cargo.lock
|
Cargo.lock
|
||||||
.vscode/
|
.vscode/
|
||||||
.cargo/
|
.cargo/
|
||||||
|
benches/results
|
||||||
before*
|
before*
|
||||||
after*
|
after*
|
||||||
|
35
CHANGELOG.md
35
CHANGELOG.md
@ -1,11 +1,42 @@
|
|||||||
Rhai Release Notes
|
Rhai Release Notes
|
||||||
==================
|
==================
|
||||||
|
|
||||||
This version introduces functions with `Dynamic` parameters acting as wildcards.
|
Version 0.19.14
|
||||||
|
===============
|
||||||
|
|
||||||
|
Bug fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
* Errors in native Rust functions now contain the correct function call positions.
|
||||||
|
* Fixed error types in `EvalAltResult::ErrorMismatchDataType` which were swapped.
|
||||||
|
|
||||||
|
Breaking changes
|
||||||
|
----------------
|
||||||
|
|
||||||
|
* Zero step in the `range` function now raises an error instead of creating an infinite stream.
|
||||||
|
* Error variable captured by `catch` is now an _object map_ containing error fields.
|
||||||
|
* `EvalAltResult::clear_position` is renamed `EvalAltResult::take_position` and returns the position taken.
|
||||||
|
* `private` functions in an `AST` can now be called with `call_fn` etc.
|
||||||
|
* `NativeCallContext::call_fn_dynamic_raw` no longer has the `pub_only` parameter.
|
||||||
|
* `Module::update_fn_metadata` input parameter is changed.
|
||||||
|
* Function keywords (e.g. `type_of`, `eval`, `Fn`) can no longer be overloaded. It is more trouble than worth. To disable these keywords, use `Engine::disable_symbol`.
|
||||||
|
* `is_def_var` and `is_def_fn` are now reserved keywords.
|
||||||
|
* `Engine::id` field is removed.
|
||||||
|
|
||||||
|
Enhancements
|
||||||
|
------------
|
||||||
|
|
||||||
|
* Function calls are more optimized and should now run faster.
|
||||||
|
* `range` function now supports negative step and decreasing streams (i.e. to < from).
|
||||||
|
* More information is provided to the error variable captured by the `catch` statement in an _object map_.
|
||||||
|
* Previously, `private` functions in an `AST` cannot be called with `call_fn` etc. This is inconvenient when trying to call a function inside a script which also serves as a loadable module exporting part (but not all) of the functions. Now, all functions (`private` or not) can be called in an `AST`. The `private` keyword is relegated to preventing a function from being exported.
|
||||||
|
|
||||||
|
|
||||||
Version 0.19.13
|
Version 0.19.13
|
||||||
===============
|
===============
|
||||||
|
|
||||||
|
This version introduces functions with `Dynamic` parameters acting as wildcards.
|
||||||
|
|
||||||
Bug fixes
|
Bug fixes
|
||||||
---------
|
---------
|
||||||
|
|
||||||
@ -694,7 +725,7 @@ Breaking changes
|
|||||||
----------------
|
----------------
|
||||||
|
|
||||||
* `Engine::compile_XXX` functions now return `ParseError` instead of `Box<ParseError>`.
|
* `Engine::compile_XXX` functions now return `ParseError` instead of `Box<ParseError>`.
|
||||||
* The `RegisterDynamicFn` trait is merged into the `RegisterResultFn` trait which now always returns `Result<Dynamic, Box<EvalAltResult>>`.
|
* The `RegisterDynamicFn` trait is merged into the `RegisterResultFn` trait which now always returns `RhaiResult`.
|
||||||
* Default maximum limit on levels of nested function calls is fine-tuned and set to a different value.
|
* Default maximum limit on levels of nested function calls is fine-tuned and set to a different value.
|
||||||
* Some operator functions are now built in (see _Speed enhancements_ below), so they are available even under `Engine::new_raw`.
|
* Some operator functions are now built in (see _Speed enhancements_ below), so they are available even under `Engine::new_raw`.
|
||||||
* Strings are now immutable. The type `rhai::ImmutableString` is used instead of `std::string::String`. This is to avoid excessive cloning of strings. All native-Rust functions taking string parameters should switch to `rhai::ImmutableString` (which is either `Rc<String>` or `Arc<String>` depending on whether the `sync` feature is used).
|
* Strings are now immutable. The type `rhai::ImmutableString` is used instead of `std::string::String`. This is to avoid excessive cloning of strings. All native-Rust functions taking string parameters should switch to `rhai::ImmutableString` (which is either `Rc<String>` or `Arc<String>` depending on whether the `sync` feature is used).
|
||||||
|
@ -6,11 +6,11 @@ members = [
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "rhai"
|
name = "rhai"
|
||||||
version = "0.19.13"
|
version = "0.19.14"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"]
|
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"]
|
||||||
description = "Embedded scripting for Rust"
|
description = "Embedded scripting for Rust"
|
||||||
homepage = "https://rhai.rs/book"
|
homepage = "https://rhai.rs"
|
||||||
repository = "https://github.com/rhaiscript"
|
repository = "https://github.com/rhaiscript"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
@ -10,7 +10,7 @@ Rhai - Embedded Scripting for Rust
|
|||||||
[![chat](https://img.shields.io/discord/767611025456889857.svg?logo=discord)](https://discord.gg/HquqbYFcZ9)
|
[![chat](https://img.shields.io/discord/767611025456889857.svg?logo=discord)](https://discord.gg/HquqbYFcZ9)
|
||||||
[![Reddit](https://img.shields.io/reddit/subreddit-subscribers/Rhai?logo=reddit)](https://www.reddit.com/r/Rhai)
|
[![Reddit](https://img.shields.io/reddit/subreddit-subscribers/Rhai?logo=reddit)](https://www.reddit.com/r/Rhai)
|
||||||
|
|
||||||
![Rhai logo](https://rhai.rs/book/images/logo/rhai-banner-transparent-colour.svg)
|
[![Rhai logo](https://rhai.rs/book/images/logo/rhai-banner-transparent-colour.svg)](https://rhai.rs)
|
||||||
|
|
||||||
Rhai is an embedded scripting language and evaluation engine for Rust that gives a safe and easy way
|
Rhai is an embedded scripting language and evaluation engine for Rust that gives a safe and easy way
|
||||||
to add scripting to any application.
|
to add scripting to any application.
|
||||||
@ -66,6 +66,12 @@ For those who actually want their own language
|
|||||||
* Extend the language with [custom syntax](https://rhai.rs/book/engine/custom-syntax.html).
|
* Extend the language with [custom syntax](https://rhai.rs/book/engine/custom-syntax.html).
|
||||||
|
|
||||||
|
|
||||||
|
Project Site
|
||||||
|
------------
|
||||||
|
|
||||||
|
[`rhai.rs`](https://rhai.rs)
|
||||||
|
|
||||||
|
|
||||||
Documentation
|
Documentation
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
33
src/ast.rs
33
src/ast.rs
@ -38,25 +38,6 @@ pub enum FnAccess {
|
|||||||
Private,
|
Private,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FnAccess {
|
|
||||||
/// Is this access mode [private][FnAccess::Private]?
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn is_private(self) -> bool {
|
|
||||||
match self {
|
|
||||||
Self::Private => true,
|
|
||||||
Self::Public => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Is this access mode [public][FnAccess::Public]?
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn is_public(self) -> bool {
|
|
||||||
match self {
|
|
||||||
Self::Private => false,
|
|
||||||
Self::Public => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// _(INTERNALS)_ A type containing information on a scripted function.
|
/// _(INTERNALS)_ A type containing information on a scripted function.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
@ -91,10 +72,9 @@ impl fmt::Display for ScriptFnDef {
|
|||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"{}{}({})",
|
"{}{}({})",
|
||||||
if self.access.is_private() {
|
match self.access {
|
||||||
"private "
|
FnAccess::Public => "",
|
||||||
} else {
|
FnAccess::Private => "private",
|
||||||
""
|
|
||||||
},
|
},
|
||||||
self.name,
|
self.name,
|
||||||
self.params
|
self.params
|
||||||
@ -134,10 +114,9 @@ impl fmt::Display for ScriptFnMetadata<'_> {
|
|||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"{}{}({})",
|
"{}{}({})",
|
||||||
if self.access.is_private() {
|
match self.access {
|
||||||
"private "
|
FnAccess::Public => "",
|
||||||
} else {
|
FnAccess::Private => "private",
|
||||||
""
|
|
||||||
},
|
},
|
||||||
self.name,
|
self.name,
|
||||||
self.params.iter().cloned().collect::<Vec<_>>().join(", ")
|
self.params.iter().cloned().collect::<Vec<_>>().join(", ")
|
||||||
|
@ -13,8 +13,7 @@ use std::{
|
|||||||
/// Pretty-print error.
|
/// Pretty-print error.
|
||||||
fn print_error(input: &str, mut err: EvalAltResult) {
|
fn print_error(input: &str, mut err: EvalAltResult) {
|
||||||
let lines: Vec<_> = input.trim().split('\n').collect();
|
let lines: Vec<_> = input.trim().split('\n').collect();
|
||||||
let pos = err.position();
|
let pos = err.take_position();
|
||||||
err.clear_position();
|
|
||||||
|
|
||||||
let line_no = if lines.len() > 1 {
|
let line_no = if lines.len() > 1 {
|
||||||
if pos.is_none() {
|
if pos.is_none() {
|
||||||
|
@ -23,8 +23,7 @@ fn eprint_error(input: &str, mut err: EvalAltResult) {
|
|||||||
let lines: Vec<_> = input.split('\n').collect();
|
let lines: Vec<_> = input.split('\n').collect();
|
||||||
|
|
||||||
// Print error
|
// Print error
|
||||||
let pos = err.position();
|
let pos = err.take_position();
|
||||||
err.clear_position();
|
|
||||||
|
|
||||||
if pos.is_none() {
|
if pos.is_none() {
|
||||||
// No position
|
// No position
|
||||||
|
@ -1447,9 +1447,9 @@ impl Dynamic {
|
|||||||
/// Exported under the `decimal` feature only.
|
/// Exported under the `decimal` feature only.
|
||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_decimal(self) -> Result<Decimal, &'static str> {
|
pub fn as_decimal(&self) -> Result<Decimal, &'static str> {
|
||||||
match self.0 {
|
match &self.0 {
|
||||||
Union::Decimal(n, _) => Ok(*n),
|
Union::Decimal(n, _) => Ok(**n),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(_, _) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()),
|
Union::Shared(_, _) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()),
|
||||||
_ => Err(self.type_name()),
|
_ => Err(self.type_name()),
|
||||||
@ -1503,7 +1503,6 @@ impl Dynamic {
|
|||||||
pub fn take_immutable_string(self) -> Result<ImmutableString, &'static str> {
|
pub fn take_immutable_string(self) -> Result<ImmutableString, &'static str> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Str(s, _) => Ok(s),
|
Union::Str(s, _) => Ok(s),
|
||||||
Union::FnPtr(f, _) => Ok(f.take_data().0),
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(cell, _) => {
|
Union::Shared(cell, _) => {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
@ -1513,7 +1512,6 @@ impl Dynamic {
|
|||||||
|
|
||||||
match &data.0 {
|
match &data.0 {
|
||||||
Union::Str(s, _) => Ok(s.clone()),
|
Union::Str(s, _) => Ok(s.clone()),
|
||||||
Union::FnPtr(f, _) => Ok(f.get_fn_name().clone()),
|
|
||||||
_ => Err((*data).type_name()),
|
_ => Err((*data).type_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
295
src/engine.rs
295
src/engine.rs
@ -17,7 +17,7 @@ use crate::stdlib::{
|
|||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
fmt, format,
|
fmt, format,
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
iter::{empty, FromIterator},
|
iter::empty,
|
||||||
num::{NonZeroU64, NonZeroU8, NonZeroUsize},
|
num::{NonZeroU64, NonZeroU8, NonZeroUsize},
|
||||||
ops::DerefMut,
|
ops::DerefMut,
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
@ -25,8 +25,8 @@ use crate::stdlib::{
|
|||||||
use crate::syntax::CustomSyntax;
|
use crate::syntax::CustomSyntax;
|
||||||
use crate::utils::{get_hasher, StraightHasherBuilder};
|
use crate::utils::{get_hasher, StraightHasherBuilder};
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_native_fn_hash, Dynamic, EvalAltResult, FnPtr, ImmutableString, Module, Position, Scope,
|
calc_native_fn_hash, Dynamic, EvalAltResult, FnPtr, ImmutableString, Module, Position,
|
||||||
Shared, StaticVec,
|
RhaiResult, Scope, Shared, StaticVec,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -107,6 +107,11 @@ impl Imports {
|
|||||||
pub(crate) fn iter_raw(&self) -> impl Iterator<Item = (&ImmutableString, &Shared<Module>)> {
|
pub(crate) fn iter_raw(&self) -> impl Iterator<Item = (&ImmutableString, &Shared<Module>)> {
|
||||||
self.0.iter().rev().map(|(n, m)| (n, m))
|
self.0.iter().rev().map(|(n, m)| (n, m))
|
||||||
}
|
}
|
||||||
|
/// Get an iterator to this stack of imported [modules][Module] in forward order.
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn scan_raw(&self) -> impl Iterator<Item = (&ImmutableString, &Shared<Module>)> {
|
||||||
|
self.0.iter().map(|(n, m)| (n, m))
|
||||||
|
}
|
||||||
/// Get a consuming iterator to this stack of imported [modules][Module] in reverse order.
|
/// Get a consuming iterator to this stack of imported [modules][Module] in reverse order.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn into_iter(self) -> impl Iterator<Item = (ImmutableString, Shared<Module>)> {
|
pub fn into_iter(self) -> impl Iterator<Item = (ImmutableString, Shared<Module>)> {
|
||||||
@ -151,24 +156,6 @@ impl Imports {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: IntoIterator<Item = (&'a ImmutableString, &'a Shared<Module>)>> From<T> for Imports {
|
|
||||||
#[inline(always)]
|
|
||||||
fn from(value: T) -> Self {
|
|
||||||
Self(
|
|
||||||
value
|
|
||||||
.into_iter()
|
|
||||||
.map(|(k, v)| (k.clone(), v.clone()))
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl FromIterator<(ImmutableString, Shared<Module>)> for Imports {
|
|
||||||
#[inline(always)]
|
|
||||||
fn from_iter<T: IntoIterator<Item = (ImmutableString, Shared<Module>)>>(iter: T) -> Self {
|
|
||||||
Self(iter.into_iter().collect())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
@ -205,6 +192,8 @@ pub const KEYWORD_FN_PTR_CURRY: &str = "curry";
|
|||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
pub const KEYWORD_IS_SHARED: &str = "is_shared";
|
pub const KEYWORD_IS_SHARED: &str = "is_shared";
|
||||||
pub const KEYWORD_IS_DEF_VAR: &str = "is_def_var";
|
pub const KEYWORD_IS_DEF_VAR: &str = "is_def_var";
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
pub const KEYWORD_IS_DEF_FN: &str = "is_def_fn";
|
||||||
pub const KEYWORD_THIS: &str = "this";
|
pub const KEYWORD_THIS: &str = "this";
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
pub const FN_GET: &str = "get$";
|
pub const FN_GET: &str = "get$";
|
||||||
@ -422,8 +411,8 @@ impl<'a> Target<'a> {
|
|||||||
// Replace the character at the specified index position
|
// Replace the character at the specified index position
|
||||||
let new_ch = new_val.as_char().map_err(|err| {
|
let new_ch = new_val.as_char().map_err(|err| {
|
||||||
Box::new(EvalAltResult::ErrorMismatchDataType(
|
Box::new(EvalAltResult::ErrorMismatchDataType(
|
||||||
err.to_string(),
|
|
||||||
"char".to_string(),
|
"char".to_string(),
|
||||||
|
err.to_string(),
|
||||||
pos,
|
pos,
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
@ -536,18 +525,6 @@ impl State {
|
|||||||
pub fn is_global(&self) -> bool {
|
pub fn is_global(&self) -> bool {
|
||||||
self.scope_level == 0
|
self.scope_level == 0
|
||||||
}
|
}
|
||||||
/// Get the current functions resolution cache.
|
|
||||||
pub fn fn_resolution_cache(
|
|
||||||
&self,
|
|
||||||
) -> Option<
|
|
||||||
&HashMap<
|
|
||||||
NonZeroU64,
|
|
||||||
Option<(CallableFunction, Option<ImmutableString>)>,
|
|
||||||
StraightHasherBuilder,
|
|
||||||
>,
|
|
||||||
> {
|
|
||||||
self.fn_resolution_caches.last()
|
|
||||||
}
|
|
||||||
/// Get a mutable reference to the current functions resolution cache.
|
/// Get a mutable reference to the current functions resolution cache.
|
||||||
pub fn fn_resolution_cache_mut(
|
pub fn fn_resolution_cache_mut(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -628,17 +605,17 @@ pub struct Limits {
|
|||||||
|
|
||||||
/// Context of a script evaluation process.
|
/// Context of a script evaluation process.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct EvalContext<'e, 'x, 'px, 'a, 's, 'm, 't, 'pt> {
|
pub struct EvalContext<'a, 'x, 'px, 'm, 's, 't, 'pt> {
|
||||||
pub(crate) engine: &'e Engine,
|
pub(crate) engine: &'a Engine,
|
||||||
pub(crate) scope: &'x mut Scope<'px>,
|
pub(crate) scope: &'x mut Scope<'px>,
|
||||||
pub(crate) mods: &'a mut Imports,
|
pub(crate) mods: &'m mut Imports,
|
||||||
pub(crate) state: &'s mut State,
|
pub(crate) state: &'s mut State,
|
||||||
pub(crate) lib: &'m [&'m Module],
|
pub(crate) lib: &'a [&'a Module],
|
||||||
pub(crate) this_ptr: &'t mut Option<&'pt mut Dynamic>,
|
pub(crate) this_ptr: &'t mut Option<&'pt mut Dynamic>,
|
||||||
pub(crate) level: usize,
|
pub(crate) level: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'e, 'x, 'px, 'a, 's, 'm, 't, 'pt> EvalContext<'e, 'x, 'px, 'a, 's, 'm, 't, 'pt> {
|
impl<'x, 'px> EvalContext<'_, 'x, 'px, '_, '_, '_, '_> {
|
||||||
/// The current [`Engine`].
|
/// The current [`Engine`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn engine(&self) -> &Engine {
|
pub fn engine(&self) -> &Engine {
|
||||||
@ -721,9 +698,6 @@ impl<'e, 'x, 'px, 'a, 's, 'm, 't, 'pt> EvalContext<'e, 'x, 'px, 'a, 's, 'm, 't,
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub struct Engine {
|
pub struct Engine {
|
||||||
/// A unique ID identifying this scripting [`Engine`].
|
|
||||||
pub id: String,
|
|
||||||
|
|
||||||
/// A module containing all functions directly loaded into the Engine.
|
/// A module containing all functions directly loaded into the Engine.
|
||||||
pub(crate) global_namespace: Module,
|
pub(crate) global_namespace: Module,
|
||||||
/// A collection of all modules loaded into the global namespace of the Engine.
|
/// A collection of all modules loaded into the global namespace of the Engine.
|
||||||
@ -768,13 +742,9 @@ pub struct Engine {
|
|||||||
impl fmt::Debug for Engine {
|
impl fmt::Debug for Engine {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
if !self.id.is_empty() {
|
|
||||||
write!(f, "Engine({})", self.id)
|
|
||||||
} else {
|
|
||||||
f.write_str("Engine")
|
f.write_str("Engine")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Engine {
|
impl Default for Engine {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -824,40 +794,12 @@ fn default_debug(_s: &str, _source: Option<&str>, _pos: Position) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Search for a module within an imports stack.
|
|
||||||
/// [`Position`] in [`EvalAltResult`] is [`None`][Position::None] and must be set afterwards.
|
|
||||||
pub fn search_imports(
|
|
||||||
mods: &Imports,
|
|
||||||
state: &mut State,
|
|
||||||
namespace: &NamespaceRef,
|
|
||||||
) -> Result<Shared<Module>, Box<EvalAltResult>> {
|
|
||||||
let Ident { name: root, pos } = &namespace[0];
|
|
||||||
|
|
||||||
// Qualified - check if the root module is directly indexed
|
|
||||||
let index = if state.always_search {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
namespace.index()
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(if let Some(index) = index {
|
|
||||||
let offset = mods.len() - index.get();
|
|
||||||
mods.get(offset).expect("invalid index in Imports")
|
|
||||||
} else {
|
|
||||||
mods.find(root)
|
|
||||||
.map(|n| mods.get(n).expect("invalid index in Imports"))
|
|
||||||
.ok_or_else(|| EvalAltResult::ErrorModuleNotFound(root.to_string(), *pos))?
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
/// Create a new [`Engine`]
|
/// Create a new [`Engine`]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
// Create the new scripting Engine
|
// Create the new scripting Engine
|
||||||
let mut engine = Self {
|
let mut engine = Self {
|
||||||
id: Default::default(),
|
|
||||||
|
|
||||||
global_namespace: Default::default(),
|
global_namespace: Default::default(),
|
||||||
global_modules: Default::default(),
|
global_modules: Default::default(),
|
||||||
global_sub_modules: Default::default(),
|
global_sub_modules: Default::default(),
|
||||||
@ -923,8 +865,6 @@ impl Engine {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn new_raw() -> Self {
|
pub fn new_raw() -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: Default::default(),
|
|
||||||
|
|
||||||
global_namespace: Default::default(),
|
global_namespace: Default::default(),
|
||||||
global_modules: Default::default(),
|
global_modules: Default::default(),
|
||||||
global_sub_modules: Default::default(),
|
global_sub_modules: Default::default(),
|
||||||
@ -970,6 +910,32 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Search for a module within an imports stack.
|
||||||
|
pub(crate) fn search_imports(
|
||||||
|
&self,
|
||||||
|
mods: &Imports,
|
||||||
|
state: &mut State,
|
||||||
|
namespace: &NamespaceRef,
|
||||||
|
) -> Option<Shared<Module>> {
|
||||||
|
let root = &namespace[0].name;
|
||||||
|
|
||||||
|
// Qualified - check if the root module is directly indexed
|
||||||
|
let index = if state.always_search {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
namespace.index()
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(index) = index {
|
||||||
|
let offset = mods.len() - index.get();
|
||||||
|
Some(mods.get(offset).expect("invalid index in Imports"))
|
||||||
|
} else {
|
||||||
|
mods.find(root)
|
||||||
|
.map(|n| mods.get(n).expect("invalid index in Imports"))
|
||||||
|
.or_else(|| self.global_sub_modules.get(root).cloned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Search for a variable within the scope or within imports,
|
/// Search for a variable within the scope or within imports,
|
||||||
/// depending on whether the variable name is namespace-qualified.
|
/// depending on whether the variable name is namespace-qualified.
|
||||||
pub(crate) fn search_namespace<'s>(
|
pub(crate) fn search_namespace<'s>(
|
||||||
@ -985,7 +951,12 @@ impl Engine {
|
|||||||
Expr::Variable(v) => match v.as_ref() {
|
Expr::Variable(v) => match v.as_ref() {
|
||||||
// Qualified variable
|
// Qualified variable
|
||||||
(_, Some((hash_var, modules)), Ident { name, pos }) => {
|
(_, Some((hash_var, modules)), Ident { name, pos }) => {
|
||||||
let module = search_imports(mods, state, modules)?;
|
let module = self.search_imports(mods, state, modules).ok_or_else(|| {
|
||||||
|
EvalAltResult::ErrorModuleNotFound(
|
||||||
|
modules[0].name.to_string(),
|
||||||
|
modules[0].pos,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
let target = module.get_qualified_var(*hash_var).map_err(|mut err| {
|
let target = module.get_qualified_var(*hash_var).map_err(|mut err| {
|
||||||
match *err {
|
match *err {
|
||||||
EvalAltResult::ErrorVariableNotFound(ref mut err_name, _) => {
|
EvalAltResult::ErrorVariableNotFound(ref mut err_name, _) => {
|
||||||
@ -1077,7 +1048,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Chain-evaluate a dot/index chain.
|
/// Chain-evaluate a dot/index chain.
|
||||||
/// [`Position`] in [`EvalAltResult`] is [`None`][Position::None] and must be set afterwards.
|
/// [`Position`] in [`EvalAltResult`] is [`NONE`][Position::NONE] and must be set afterwards.
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
fn eval_dot_index_chain_helper(
|
fn eval_dot_index_chain_helper(
|
||||||
&self,
|
&self,
|
||||||
@ -1164,8 +1135,8 @@ impl Engine {
|
|||||||
let args = &mut [target_val, &mut idx_val2, &mut (new_val.0).0];
|
let args = &mut [target_val, &mut idx_val2, &mut (new_val.0).0];
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, FN_IDX_SET, None, args, is_ref, true, false,
|
mods, state, lib, FN_IDX_SET, None, args, is_ref, true, val_pos,
|
||||||
val_pos, None, level,
|
None, level,
|
||||||
)
|
)
|
||||||
.map_err(|err| match *err {
|
.map_err(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
@ -1205,7 +1176,7 @@ impl Engine {
|
|||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
let args = idx_val.as_fn_call_args();
|
let args = idx_val.as_fn_call_args();
|
||||||
self.make_method_call(
|
self.make_method_call(
|
||||||
mods, state, lib, name, *hash, target, args, false, *pos, level,
|
mods, state, lib, name, *hash, target, args, *pos, level,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// xxx.fn_name(...) = ???
|
// xxx.fn_name(...) = ???
|
||||||
@ -1245,8 +1216,8 @@ impl Engine {
|
|||||||
let mut new_val = new_val;
|
let mut new_val = new_val;
|
||||||
let mut args = [target_val, &mut (new_val.as_mut().unwrap().0).0];
|
let mut args = [target_val, &mut (new_val.as_mut().unwrap().0).0];
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, setter, None, &mut args, is_ref, true, false, *pos,
|
mods, state, lib, setter, None, &mut args, is_ref, true, *pos, None,
|
||||||
None, level,
|
level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| (v, true))
|
.map(|(v, _)| (v, true))
|
||||||
}
|
}
|
||||||
@ -1255,8 +1226,8 @@ impl Engine {
|
|||||||
let (getter, _, Ident { pos, .. }) = x.as_ref();
|
let (getter, _, Ident { pos, .. }) = x.as_ref();
|
||||||
let mut args = [target_val];
|
let mut args = [target_val];
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, getter, None, &mut args, is_ref, true, false, *pos,
|
mods, state, lib, getter, None, &mut args, is_ref, true, *pos, None,
|
||||||
None, level,
|
level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| (v, false))
|
.map(|(v, _)| (v, false))
|
||||||
}
|
}
|
||||||
@ -1280,7 +1251,7 @@ impl Engine {
|
|||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
let args = idx_val.as_fn_call_args();
|
let args = idx_val.as_fn_call_args();
|
||||||
let (val, _) = self.make_method_call(
|
let (val, _) = self.make_method_call(
|
||||||
mods, state, lib, name, *hash, target, args, false, *pos, level,
|
mods, state, lib, name, *hash, target, args, *pos, level,
|
||||||
)?;
|
)?;
|
||||||
val.into()
|
val.into()
|
||||||
}
|
}
|
||||||
@ -1307,8 +1278,8 @@ impl Engine {
|
|||||||
let arg_values = &mut [target_val, &mut Default::default()];
|
let arg_values = &mut [target_val, &mut Default::default()];
|
||||||
let args = &mut arg_values[..1];
|
let args = &mut arg_values[..1];
|
||||||
let (mut val, updated) = self.exec_fn_call(
|
let (mut val, updated) = self.exec_fn_call(
|
||||||
mods, state, lib, getter, None, args, is_ref, true, false,
|
mods, state, lib, getter, None, args, is_ref, true, *pos, None,
|
||||||
*pos, None, level,
|
level,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let val = &mut val;
|
let val = &mut val;
|
||||||
@ -1334,7 +1305,7 @@ impl Engine {
|
|||||||
arg_values[1] = val;
|
arg_values[1] = val;
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, setter, None, arg_values, is_ref, true,
|
mods, state, lib, setter, None, arg_values, is_ref, true,
|
||||||
false, *pos, None, level,
|
*pos, None, level,
|
||||||
)
|
)
|
||||||
.or_else(
|
.or_else(
|
||||||
|err| match *err {
|
|err| match *err {
|
||||||
@ -1359,7 +1330,7 @@ impl Engine {
|
|||||||
} = f.as_ref();
|
} = f.as_ref();
|
||||||
let args = idx_val.as_fn_call_args();
|
let args = idx_val.as_fn_call_args();
|
||||||
let (mut val, _) = self.make_method_call(
|
let (mut val, _) = self.make_method_call(
|
||||||
mods, state, lib, name, *hash, target, args, false, *pos, level,
|
mods, state, lib, name, *hash, target, args, *pos, level,
|
||||||
)?;
|
)?;
|
||||||
let val = &mut val;
|
let val = &mut val;
|
||||||
let target = &mut val.into();
|
let target = &mut val.into();
|
||||||
@ -1399,7 +1370,7 @@ impl Engine {
|
|||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
level: usize,
|
level: usize,
|
||||||
new_val: Option<((Dynamic, Position), (&str, Position))>,
|
new_val: Option<((Dynamic, Position), (&str, Position))>,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> RhaiResult {
|
||||||
let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, op_pos) = match expr {
|
let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, op_pos) = match expr {
|
||||||
Expr::Index(x, pos) => (x.as_ref(), ChainType::Index, *pos),
|
Expr::Index(x, pos) => (x.as_ref(), ChainType::Index, *pos),
|
||||||
Expr::Dot(x, pos) => (x.as_ref(), ChainType::Dot, *pos),
|
Expr::Dot(x, pos) => (x.as_ref(), ChainType::Dot, *pos),
|
||||||
@ -1629,8 +1600,8 @@ impl Engine {
|
|||||||
let mut idx = idx;
|
let mut idx = idx;
|
||||||
let args = &mut [target, &mut idx];
|
let args = &mut [target, &mut idx];
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
_mods, state, _lib, FN_IDX_GET, None, args, _is_ref, true, false, idx_pos,
|
_mods, state, _lib, FN_IDX_GET, None, args, _is_ref, true, idx_pos, None,
|
||||||
None, _level,
|
_level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| v.into())
|
.map(|(v, _)| v.into())
|
||||||
.map_err(|err| match *err {
|
.map_err(|err| match *err {
|
||||||
@ -1663,7 +1634,7 @@ impl Engine {
|
|||||||
lhs: &Expr,
|
lhs: &Expr,
|
||||||
rhs: &Expr,
|
rhs: &Expr,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> RhaiResult {
|
||||||
self.inc_operations(state, rhs.position())?;
|
self.inc_operations(state, rhs.position())?;
|
||||||
|
|
||||||
let lhs_value = self.eval_expr(scope, mods, state, lib, this_ptr, lhs, level)?;
|
let lhs_value = self.eval_expr(scope, mods, state, lib, this_ptr, lhs, level)?;
|
||||||
@ -1721,7 +1692,7 @@ impl Engine {
|
|||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> RhaiResult {
|
||||||
self.inc_operations(state, expr.position())?;
|
self.inc_operations(state, expr.position())?;
|
||||||
|
|
||||||
let result = match expr {
|
let result = match expr {
|
||||||
@ -1742,9 +1713,16 @@ impl Engine {
|
|||||||
.map(|(val, _)| val.take_or_clone()),
|
.map(|(val, _)| val.take_or_clone()),
|
||||||
|
|
||||||
// Statement block
|
// Statement block
|
||||||
Expr::Stmt(x, _) => {
|
Expr::Stmt(x, _) => self.eval_stmt_block(
|
||||||
self.eval_stmt_block(scope, mods, state, lib, this_ptr, x.as_ref(), true, level)
|
scope,
|
||||||
}
|
mods,
|
||||||
|
state,
|
||||||
|
lib,
|
||||||
|
this_ptr,
|
||||||
|
x.as_ref().as_ref(),
|
||||||
|
true,
|
||||||
|
level,
|
||||||
|
),
|
||||||
|
|
||||||
// lhs[idx_expr]
|
// lhs[idx_expr]
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -1791,8 +1769,7 @@ impl Engine {
|
|||||||
..
|
..
|
||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
self.make_function_call(
|
self.make_function_call(
|
||||||
scope, mods, state, lib, this_ptr, name, args, *hash, false, *pos, *cap_scope,
|
scope, mods, state, lib, this_ptr, name, args, *hash, *pos, *cap_scope, level,
|
||||||
level,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1874,47 +1851,59 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a statements block.
|
/// Evaluate a statements block.
|
||||||
pub(crate) fn eval_stmt_block<'a>(
|
pub(crate) fn eval_stmt_block(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
mods: &mut Imports,
|
mods: &mut Imports,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
statements: impl IntoIterator<Item = &'a Stmt>,
|
statements: &[Stmt],
|
||||||
restore: bool,
|
restore_prev_state: bool,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> RhaiResult {
|
||||||
let mut _has_imports = false;
|
let mut _restore_fn_resolution_cache = false;
|
||||||
let prev_always_search = state.always_search;
|
let prev_always_search = state.always_search;
|
||||||
let prev_scope_len = scope.len();
|
let prev_scope_len = scope.len();
|
||||||
let prev_mods_len = mods.len();
|
let prev_mods_len = mods.len();
|
||||||
|
|
||||||
if restore {
|
if restore_prev_state {
|
||||||
state.scope_level += 1;
|
state.scope_level += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = statements.into_iter().try_fold(Dynamic::UNIT, |_, stmt| {
|
let result = statements.iter().try_fold(Dynamic::UNIT, |_, stmt| {
|
||||||
|
let _mods_len = mods.len();
|
||||||
|
|
||||||
|
let r = self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level)?;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
match stmt {
|
if matches!(stmt, Stmt::Import(_, _, _)) {
|
||||||
Stmt::Import(_, _, _) => {
|
// Get the extra modules - see if any functions are marked global.
|
||||||
// When imports list is modified, clear the functions lookup cache
|
// Without global functions, the extra modules never affect function resolution.
|
||||||
if _has_imports {
|
if mods
|
||||||
|
.scan_raw()
|
||||||
|
.skip(_mods_len)
|
||||||
|
.any(|(_, m)| m.has_namespace(crate::FnNamespace::Global, true))
|
||||||
|
{
|
||||||
|
if _restore_fn_resolution_cache {
|
||||||
|
// When new module is imported with global functions and there is already
|
||||||
|
// a new cache, clear it - notice that this is expensive as all function
|
||||||
|
// resolutions must start again
|
||||||
state.clear_fn_resolution_cache();
|
state.clear_fn_resolution_cache();
|
||||||
} else if restore {
|
} else if restore_prev_state {
|
||||||
|
// When new module is imported with global functions, push a new cache
|
||||||
state.push_fn_resolution_cache();
|
state.push_fn_resolution_cache();
|
||||||
_has_imports = true;
|
_restore_fn_resolution_cache = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level)
|
Ok(r)
|
||||||
});
|
});
|
||||||
|
|
||||||
if restore {
|
if restore_prev_state {
|
||||||
scope.rewind(prev_scope_len);
|
scope.rewind(prev_scope_len);
|
||||||
if _has_imports {
|
if _restore_fn_resolution_cache {
|
||||||
// If imports list is modified, pop the functions lookup cache
|
// If imports list is modified, pop the functions lookup cache
|
||||||
state.pop_fn_resolution_cache();
|
state.pop_fn_resolution_cache();
|
||||||
}
|
}
|
||||||
@ -2000,7 +1989,7 @@ impl Engine {
|
|||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
stmt: &Stmt,
|
stmt: &Stmt,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> RhaiResult {
|
||||||
self.inc_operations(state, stmt.position())?;
|
self.inc_operations(state, stmt.position())?;
|
||||||
|
|
||||||
let result = match stmt {
|
let result = match stmt {
|
||||||
@ -2175,6 +2164,13 @@ impl Engine {
|
|||||||
let iter_obj = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
let iter_obj = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
||||||
let iter_type = iter_obj.type_id();
|
let iter_type = iter_obj.type_id();
|
||||||
|
|
||||||
|
// lib should only contain scripts, so technically they cannot have iterators
|
||||||
|
|
||||||
|
// Search order:
|
||||||
|
// 1) Global namespace - functions registered via Engine::register_XXX
|
||||||
|
// 2) Global modules - packages
|
||||||
|
// 3) Imported modules - functions marked with global namespace
|
||||||
|
// 4) Global sub-modules - functions marked with global namespace
|
||||||
let func = self
|
let func = self
|
||||||
.global_namespace
|
.global_namespace
|
||||||
.get_iter(iter_type)
|
.get_iter(iter_type)
|
||||||
@ -2183,7 +2179,12 @@ impl Engine {
|
|||||||
.iter()
|
.iter()
|
||||||
.find_map(|m| m.get_iter(iter_type))
|
.find_map(|m| m.get_iter(iter_type))
|
||||||
})
|
})
|
||||||
.or_else(|| mods.get_iter(iter_type));
|
.or_else(|| mods.get_iter(iter_type))
|
||||||
|
.or_else(|| {
|
||||||
|
self.global_sub_modules
|
||||||
|
.values()
|
||||||
|
.find_map(|m| m.get_qualified_iter(iter_type))
|
||||||
|
});
|
||||||
|
|
||||||
if let Some(func) = func {
|
if let Some(func) = func {
|
||||||
// Add the loop variable
|
// Add the loop variable
|
||||||
@ -2245,19 +2246,55 @@ impl Engine {
|
|||||||
Err(err) if err.is_pseudo_error() => Err(err),
|
Err(err) if err.is_pseudo_error() => Err(err),
|
||||||
Err(err) if !err.is_catchable() => Err(err),
|
Err(err) if !err.is_catchable() => Err(err),
|
||||||
Err(mut err) => {
|
Err(mut err) => {
|
||||||
let value = match *err {
|
let err_value = match *err {
|
||||||
EvalAltResult::ErrorRuntime(ref x, _) => x.clone(),
|
EvalAltResult::ErrorRuntime(ref x, _) => x.clone(),
|
||||||
|
|
||||||
|
#[cfg(feature = "no_object")]
|
||||||
_ => {
|
_ => {
|
||||||
err.set_position(Position::NONE);
|
err.take_position();
|
||||||
err.to_string().into()
|
err.to_string().into()
|
||||||
}
|
}
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
_ => {
|
||||||
|
use crate::INT;
|
||||||
|
|
||||||
|
let mut err_map: Map = Default::default();
|
||||||
|
let err_pos = err.take_position();
|
||||||
|
|
||||||
|
err_map.insert("message".into(), err.to_string().into());
|
||||||
|
|
||||||
|
if let Some(ref source) = state.source {
|
||||||
|
err_map.insert("source".into(), source.clone().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
if err_pos.is_none() {
|
||||||
|
// No position info
|
||||||
|
} else {
|
||||||
|
err_map.insert(
|
||||||
|
"line".into(),
|
||||||
|
(err_pos.line().unwrap() as INT).into(),
|
||||||
|
);
|
||||||
|
err_map.insert(
|
||||||
|
"position".into(),
|
||||||
|
if err_pos.is_beginning_of_line() {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
err_pos.position().unwrap() as INT
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
err.dump_fields(&mut err_map);
|
||||||
|
err_map.into()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let orig_scope_len = scope.len();
|
let orig_scope_len = scope.len();
|
||||||
state.scope_level += 1;
|
state.scope_level += 1;
|
||||||
|
|
||||||
if let Some(Ident { name, .. }) = err_var {
|
if let Some(Ident { name, .. }) = err_var {
|
||||||
scope.push(unsafe_cast_var_name_to_lifetime(&name), value);
|
scope.push(unsafe_cast_var_name_to_lifetime(&name), err_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
let result =
|
let result =
|
||||||
@ -2427,21 +2464,13 @@ impl Engine {
|
|||||||
/// [`Position`] in [`EvalAltResult`] may be None and should be set afterwards.
|
/// [`Position`] in [`EvalAltResult`] may be None and should be set afterwards.
|
||||||
#[cfg(feature = "unchecked")]
|
#[cfg(feature = "unchecked")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn check_data_size(
|
fn check_data_size(&self, result: RhaiResult, _pos: Position) -> RhaiResult {
|
||||||
&self,
|
|
||||||
result: Result<Dynamic, Box<EvalAltResult>>,
|
|
||||||
_pos: Position,
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check a result to ensure that the data size is within allowable limit.
|
/// Check a result to ensure that the data size is within allowable limit.
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
fn check_data_size(
|
fn check_data_size(&self, result: RhaiResult, pos: Position) -> RhaiResult {
|
||||||
&self,
|
|
||||||
result: Result<Dynamic, Box<EvalAltResult>>,
|
|
||||||
pos: Position,
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
// Simply return all errors
|
// Simply return all errors
|
||||||
if result.is_err() {
|
if result.is_err() {
|
||||||
return result;
|
return result;
|
||||||
@ -2589,8 +2618,8 @@ impl Engine {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn make_type_mismatch_err<T>(&self, typ: &str, pos: Position) -> Box<EvalAltResult> {
|
pub(crate) fn make_type_mismatch_err<T>(&self, typ: &str, pos: Position) -> Box<EvalAltResult> {
|
||||||
EvalAltResult::ErrorMismatchDataType(
|
EvalAltResult::ErrorMismatchDataType(
|
||||||
typ.into(),
|
|
||||||
self.map_type_name(type_name::<T>()).into(),
|
self.map_type_name(type_name::<T>()).into(),
|
||||||
|
typ.into(),
|
||||||
pos,
|
pos,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
|
@ -13,7 +13,7 @@ use crate::stdlib::{
|
|||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, Module, NativeCallContext,
|
scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, Module, NativeCallContext,
|
||||||
ParseError, Position, Shared, AST,
|
ParseError, Position, RhaiResult, Shared, AST,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -33,9 +33,9 @@ impl Engine {
|
|||||||
/// Arguments are simply passed in as a mutable array of [`&mut Dynamic`][Dynamic],
|
/// Arguments are simply passed in as a mutable array of [`&mut Dynamic`][Dynamic],
|
||||||
/// The arguments are guaranteed to be of the correct types matching the [`TypeId`][std::any::TypeId]'s.
|
/// The arguments are guaranteed to be of the correct types matching the [`TypeId`][std::any::TypeId]'s.
|
||||||
///
|
///
|
||||||
/// To access a primary parameter value (i.e. cloning is cheap), use: `args[n].clone().cast::<T>()`
|
/// To access a primary argument value (i.e. cloning is cheap), use: `args[n].as_xxx().unwrap()`
|
||||||
///
|
///
|
||||||
/// To access a parameter value and avoid cloning, use `std::mem::take(args[n]).cast::<T>()`.
|
/// To access an argument value and avoid cloning, use `std::mem::take(args[n]).cast::<T>()`.
|
||||||
/// Notice that this will _consume_ the argument, replacing it with `()`.
|
/// Notice that this will _consume_ the argument, replacing it with `()`.
|
||||||
///
|
///
|
||||||
/// To access the first mutable parameter, use `args.get_mut(0).unwrap()`
|
/// To access the first mutable parameter, use `args.get_mut(0).unwrap()`
|
||||||
@ -71,6 +71,7 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { Self { field: 1 } }
|
/// fn new() -> Self { Self { field: 1 } }
|
||||||
|
///
|
||||||
/// fn update(&mut self, offset: i64) { self.field += offset; }
|
/// fn update(&mut self, offset: i64) { self.field += offset; }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
@ -170,6 +171,7 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { Self { field: 1 } }
|
/// fn new() -> Self { Self { field: 1 } }
|
||||||
|
///
|
||||||
/// // Even a getter must start with `&mut self` and not `&self`.
|
/// // Even a getter must start with `&mut self` and not `&self`.
|
||||||
/// fn get_field(&mut self) -> i64 { self.field }
|
/// fn get_field(&mut self) -> i64 { self.field }
|
||||||
/// }
|
/// }
|
||||||
@ -216,6 +218,7 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { Self { field: 1 } }
|
/// fn new() -> Self { Self { field: 1 } }
|
||||||
|
///
|
||||||
/// // Even a getter must start with `&mut self` and not `&self`.
|
/// // Even a getter must start with `&mut self` and not `&self`.
|
||||||
/// fn get_field(&mut self) -> Result<Dynamic, Box<EvalAltResult>> {
|
/// fn get_field(&mut self) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
/// Ok(self.field.into())
|
/// Ok(self.field.into())
|
||||||
@ -241,7 +244,7 @@ impl Engine {
|
|||||||
pub fn register_get_result<T: Variant + Clone>(
|
pub fn register_get_result<T: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
get_fn: impl Fn(&mut T) -> Result<Dynamic, Box<EvalAltResult>> + SendSync + 'static,
|
get_fn: impl Fn(&mut T) -> RhaiResult + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
use crate::{engine::make_getter, RegisterResultFn};
|
use crate::{engine::make_getter, RegisterResultFn};
|
||||||
self.register_result_fn(&make_getter(name), get_fn)
|
self.register_result_fn(&make_getter(name), get_fn)
|
||||||
@ -258,6 +261,7 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { Self { field: 1 } }
|
/// fn new() -> Self { Self { field: 1 } }
|
||||||
|
///
|
||||||
/// fn set_field(&mut self, new_val: i64) { self.field = new_val; }
|
/// fn set_field(&mut self, new_val: i64) { self.field = new_val; }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
@ -305,6 +309,7 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { Self { field: 1 } }
|
/// fn new() -> Self { Self { field: 1 } }
|
||||||
|
///
|
||||||
/// fn set_field(&mut self, new_val: i64) -> Result<(), Box<EvalAltResult>> {
|
/// fn set_field(&mut self, new_val: i64) -> Result<(), Box<EvalAltResult>> {
|
||||||
/// self.field = new_val;
|
/// self.field = new_val;
|
||||||
/// Ok(())
|
/// Ok(())
|
||||||
@ -356,8 +361,10 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { Self { field: 1 } }
|
/// fn new() -> Self { Self { field: 1 } }
|
||||||
|
///
|
||||||
/// // Even a getter must start with `&mut self` and not `&self`.
|
/// // Even a getter must start with `&mut self` and not `&self`.
|
||||||
/// fn get_field(&mut self) -> i64 { self.field }
|
/// fn get_field(&mut self) -> i64 { self.field }
|
||||||
|
///
|
||||||
/// fn set_field(&mut self, new_val: i64) { self.field = new_val; }
|
/// fn set_field(&mut self, new_val: i64) { self.field = new_val; }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
@ -407,6 +414,7 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } }
|
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } }
|
||||||
|
///
|
||||||
/// // Even a getter must start with `&mut self` and not `&self`.
|
/// // Even a getter must start with `&mut self` and not `&self`.
|
||||||
/// fn get_field(&mut self, index: i64) -> i64 { self.fields[index as usize] }
|
/// fn get_field(&mut self, index: i64) -> i64 { self.fields[index as usize] }
|
||||||
/// }
|
/// }
|
||||||
@ -473,6 +481,7 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } }
|
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } }
|
||||||
|
///
|
||||||
/// // Even a getter must start with `&mut self` and not `&self`.
|
/// // Even a getter must start with `&mut self` and not `&self`.
|
||||||
/// fn get_field(&mut self, index: i64) -> Result<Dynamic, Box<EvalAltResult>> {
|
/// fn get_field(&mut self, index: i64) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
/// Ok(self.fields[index as usize].into())
|
/// Ok(self.fields[index as usize].into())
|
||||||
@ -499,7 +508,7 @@ impl Engine {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn register_indexer_get_result<T: Variant + Clone, X: Variant + Clone>(
|
pub fn register_indexer_get_result<T: Variant + Clone, X: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
get_fn: impl Fn(&mut T, X) -> Result<Dynamic, Box<EvalAltResult>> + SendSync + 'static,
|
get_fn: impl Fn(&mut T, X) -> RhaiResult + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||||
panic!("Cannot register indexer for arrays.");
|
panic!("Cannot register indexer for arrays.");
|
||||||
@ -535,6 +544,7 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } }
|
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } }
|
||||||
|
///
|
||||||
/// fn set_field(&mut self, index: i64, value: i64) { self.fields[index as usize] = value; }
|
/// fn set_field(&mut self, index: i64, value: i64) { self.fields[index as usize] = value; }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
@ -601,6 +611,7 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } }
|
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } }
|
||||||
|
///
|
||||||
/// fn set_field(&mut self, index: i64, value: i64) -> Result<(), Box<EvalAltResult>> {
|
/// fn set_field(&mut self, index: i64, value: i64) -> Result<(), Box<EvalAltResult>> {
|
||||||
/// self.fields[index as usize] = value;
|
/// self.fields[index as usize] = value;
|
||||||
/// Ok(())
|
/// Ok(())
|
||||||
@ -672,8 +683,10 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } }
|
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } }
|
||||||
|
///
|
||||||
/// // Even a getter must start with `&mut self` and not `&self`.
|
/// // Even a getter must start with `&mut self` and not `&self`.
|
||||||
/// fn get_field(&mut self, index: i64) -> i64 { self.fields[index as usize] }
|
/// fn get_field(&mut self, index: i64) -> i64 { self.fields[index as usize] }
|
||||||
|
///
|
||||||
/// fn set_field(&mut self, index: i64, value: i64) { self.fields[index as usize] = value; }
|
/// fn set_field(&mut self, index: i64, value: i64) { self.fields[index as usize] = value; }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
@ -764,29 +777,25 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
pub fn register_static_module(
|
pub fn register_static_module(&mut self, name: &str, module: Shared<Module>) -> &mut Self {
|
||||||
&mut self,
|
|
||||||
name: impl AsRef<str>,
|
|
||||||
module: Shared<Module>,
|
|
||||||
) -> &mut Self {
|
|
||||||
fn register_static_module_raw(
|
fn register_static_module_raw(
|
||||||
root: &mut crate::stdlib::collections::HashMap<crate::ImmutableString, Shared<Module>>,
|
root: &mut crate::stdlib::collections::HashMap<crate::ImmutableString, Shared<Module>>,
|
||||||
name: impl AsRef<str>,
|
name: &str,
|
||||||
module: Shared<Module>,
|
module: Shared<Module>,
|
||||||
) {
|
) {
|
||||||
let separator = crate::token::Token::DoubleColon.syntax();
|
let separator = crate::token::Token::DoubleColon.syntax();
|
||||||
|
|
||||||
if !name.as_ref().contains(separator.as_ref()) {
|
if !name.contains(separator.as_ref()) {
|
||||||
if !module.is_indexed() {
|
if !module.is_indexed() {
|
||||||
// Index the module (making a clone copy if necessary) if it is not indexed
|
// Index the module (making a clone copy if necessary) if it is not indexed
|
||||||
let mut module = crate::fn_native::shared_take_or_clone(module);
|
let mut module = crate::fn_native::shared_take_or_clone(module);
|
||||||
module.build_index();
|
module.build_index();
|
||||||
root.insert(name.as_ref().trim().into(), module.into());
|
root.insert(name.trim().into(), module.into());
|
||||||
} else {
|
} else {
|
||||||
root.insert(name.as_ref().trim().into(), module);
|
root.insert(name.trim().into(), module);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let mut iter = name.as_ref().splitn(2, separator.as_ref());
|
let mut iter = name.splitn(2, separator.as_ref());
|
||||||
let sub_module = iter.next().unwrap().trim();
|
let sub_module = iter.next().unwrap().trim();
|
||||||
let remainder = iter.next().unwrap().trim();
|
let remainder = iter.next().unwrap().trim();
|
||||||
|
|
||||||
@ -817,11 +826,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[deprecated = "use `register_static_module` instead"]
|
#[deprecated = "use `register_static_module` instead"]
|
||||||
pub fn register_module(
|
pub fn register_module(&mut self, name: &str, module: impl Into<Shared<Module>>) -> &mut Self {
|
||||||
&mut self,
|
|
||||||
name: impl AsRef<str>,
|
|
||||||
module: impl Into<Shared<Module>>,
|
|
||||||
) -> &mut Self {
|
|
||||||
self.register_static_module(name, module.into())
|
self.register_static_module(name, module.into())
|
||||||
}
|
}
|
||||||
/// Compile a string into an [`AST`], which can be used later for evaluation.
|
/// Compile a string into an [`AST`], which can be used later for evaluation.
|
||||||
@ -1023,7 +1028,7 @@ impl Engine {
|
|||||||
scripts: &[&str],
|
scripts: &[&str],
|
||||||
optimization_level: OptimizationLevel,
|
optimization_level: OptimizationLevel,
|
||||||
) -> Result<AST, ParseError> {
|
) -> Result<AST, ParseError> {
|
||||||
let stream = self.lex(scripts);
|
let stream = self.lex_raw(scripts, None);
|
||||||
self.parse(&mut stream.peekable(), scope, optimization_level)
|
self.parse(&mut stream.peekable(), scope, optimization_level)
|
||||||
}
|
}
|
||||||
/// Read the contents of a file into a string.
|
/// Read the contents of a file into a string.
|
||||||
@ -1185,9 +1190,9 @@ impl Engine {
|
|||||||
.into());
|
.into());
|
||||||
};
|
};
|
||||||
|
|
||||||
let stream = self.lex_with_map(
|
let stream = self.lex_raw(
|
||||||
&scripts,
|
&scripts,
|
||||||
if has_null {
|
Some(if has_null {
|
||||||
|token| match token {
|
|token| match token {
|
||||||
// If `null` is present, make sure `null` is treated as a variable
|
// If `null` is present, make sure `null` is treated as a variable
|
||||||
Token::Reserved(s) if s == "null" => Token::Identifier(s),
|
Token::Reserved(s) if s == "null" => Token::Identifier(s),
|
||||||
@ -1195,7 +1200,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|t| t
|
|t| t
|
||||||
},
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
let ast =
|
let ast =
|
||||||
@ -1278,7 +1283,7 @@ impl Engine {
|
|||||||
script: &str,
|
script: &str,
|
||||||
) -> Result<AST, ParseError> {
|
) -> Result<AST, ParseError> {
|
||||||
let scripts = [script];
|
let scripts = [script];
|
||||||
let stream = self.lex(&scripts);
|
let stream = self.lex_raw(&scripts, None);
|
||||||
|
|
||||||
let mut peekable = stream.peekable();
|
let mut peekable = stream.peekable();
|
||||||
self.parse_global_expr(&mut peekable, scope, self.optimization_level)
|
self.parse_global_expr(&mut peekable, scope, self.optimization_level)
|
||||||
@ -1439,7 +1444,7 @@ impl Engine {
|
|||||||
script: &str,
|
script: &str,
|
||||||
) -> Result<T, Box<EvalAltResult>> {
|
) -> Result<T, Box<EvalAltResult>> {
|
||||||
let scripts = [script];
|
let scripts = [script];
|
||||||
let stream = self.lex(&scripts);
|
let stream = self.lex_raw(&scripts, None);
|
||||||
|
|
||||||
// No need to optimize a lone expression
|
// No need to optimize a lone expression
|
||||||
let ast = self.parse_global_expr(&mut stream.peekable(), scope, OptimizationLevel::None)?;
|
let ast = self.parse_global_expr(&mut stream.peekable(), scope, OptimizationLevel::None)?;
|
||||||
@ -1503,7 +1508,7 @@ impl Engine {
|
|||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
ast: &AST,
|
ast: &AST,
|
||||||
) -> Result<T, Box<EvalAltResult>> {
|
) -> Result<T, Box<EvalAltResult>> {
|
||||||
let mods = &mut (&self.global_sub_modules).into();
|
let mods = &mut Default::default();
|
||||||
|
|
||||||
let result = self.eval_ast_with_scope_raw(scope, mods, ast, 0)?;
|
let result = self.eval_ast_with_scope_raw(scope, mods, ast, 0)?;
|
||||||
|
|
||||||
@ -1526,7 +1531,7 @@ impl Engine {
|
|||||||
mods: &mut Imports,
|
mods: &mut Imports,
|
||||||
ast: &'a AST,
|
ast: &'a AST,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> RhaiResult {
|
||||||
let mut state: State = Default::default();
|
let mut state: State = Default::default();
|
||||||
state.source = ast.clone_source();
|
state.source = ast.clone_source();
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -1580,7 +1585,7 @@ impl Engine {
|
|||||||
script: &str,
|
script: &str,
|
||||||
) -> Result<(), Box<EvalAltResult>> {
|
) -> Result<(), Box<EvalAltResult>> {
|
||||||
let scripts = [script];
|
let scripts = [script];
|
||||||
let stream = self.lex(&scripts);
|
let stream = self.lex_raw(&scripts, None);
|
||||||
let ast = self.parse(&mut stream.peekable(), scope, self.optimization_level)?;
|
let ast = self.parse(&mut stream.peekable(), scope, self.optimization_level)?;
|
||||||
self.consume_ast_with_scope(scope, &ast)
|
self.consume_ast_with_scope(scope, &ast)
|
||||||
}
|
}
|
||||||
@ -1598,7 +1603,7 @@ impl Engine {
|
|||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
ast: &AST,
|
ast: &AST,
|
||||||
) -> Result<(), Box<EvalAltResult>> {
|
) -> Result<(), Box<EvalAltResult>> {
|
||||||
let mods = &mut (&self.global_sub_modules).into();
|
let mods = &mut Default::default();
|
||||||
let mut state: State = Default::default();
|
let mut state: State = Default::default();
|
||||||
state.source = ast.clone_source();
|
state.source = ast.clone_source();
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -1741,7 +1746,7 @@ impl Engine {
|
|||||||
name: &str,
|
name: &str,
|
||||||
mut this_ptr: Option<&mut Dynamic>,
|
mut this_ptr: Option<&mut Dynamic>,
|
||||||
mut arg_values: impl AsMut<[Dynamic]>,
|
mut arg_values: impl AsMut<[Dynamic]>,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> RhaiResult {
|
||||||
let mut args: crate::StaticVec<_> = arg_values.as_mut().iter_mut().collect();
|
let mut args: crate::StaticVec<_> = arg_values.as_mut().iter_mut().collect();
|
||||||
|
|
||||||
self.call_fn_dynamic_raw(scope, ast, eval_ast, name, &mut this_ptr, args.as_mut())
|
self.call_fn_dynamic_raw(scope, ast, eval_ast, name, &mut this_ptr, args.as_mut())
|
||||||
@ -1764,9 +1769,9 @@ impl Engine {
|
|||||||
name: &str,
|
name: &str,
|
||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> RhaiResult {
|
||||||
let state = &mut Default::default();
|
let state = &mut Default::default();
|
||||||
let mods = &mut (&self.global_sub_modules).into();
|
let mods = &mut Default::default();
|
||||||
let lib = &[ast.lib()];
|
let lib = &[ast.lib()];
|
||||||
|
|
||||||
if eval_ast {
|
if eval_ast {
|
||||||
@ -1775,7 +1780,7 @@ impl Engine {
|
|||||||
|
|
||||||
let fn_def = ast
|
let fn_def = ast
|
||||||
.lib()
|
.lib()
|
||||||
.get_script_fn(name, args.len(), true)
|
.get_script_fn(name, args.len(), false)
|
||||||
.ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::NONE))?;
|
.ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::NONE))?;
|
||||||
|
|
||||||
// Check for data race.
|
// Check for data race.
|
||||||
|
@ -203,7 +203,7 @@ impl Engine {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// The following will raise an error during parsing because the `if` keyword is disabled
|
/// The following will raise an error during parsing because the `if` keyword is disabled
|
||||||
/// and is recognized as a variable name!
|
/// and is recognized as a reserved symbol!
|
||||||
///
|
///
|
||||||
/// ```rust,should_panic
|
/// ```rust,should_panic
|
||||||
/// # fn main() -> Result<(), rhai::ParseError> {
|
/// # fn main() -> Result<(), rhai::ParseError> {
|
||||||
@ -214,7 +214,7 @@ impl Engine {
|
|||||||
/// engine.disable_symbol("if"); // disable the 'if' keyword
|
/// engine.disable_symbol("if"); // disable the 'if' keyword
|
||||||
///
|
///
|
||||||
/// engine.compile("let x = if true { 42 } else { 0 };")?;
|
/// engine.compile("let x = if true { 42 } else { 0 };")?;
|
||||||
/// // ^ 'if' is rejected as a reserved keyword
|
/// // ^ 'if' is rejected as a reserved symbol
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
601
src/fn_builtin.rs
Normal file
601
src/fn_builtin.rs
Normal file
@ -0,0 +1,601 @@
|
|||||||
|
//! Built-in implementations for common operators.
|
||||||
|
|
||||||
|
use crate::fn_native::{FnCallArgs, NativeCallContext};
|
||||||
|
use crate::stdlib::{any::TypeId, format, string::ToString};
|
||||||
|
use crate::{Dynamic, ImmutableString, RhaiResult, INT};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
use crate::FLOAT;
|
||||||
|
|
||||||
|
#[cfg(feature = "decimal")]
|
||||||
|
use rust_decimal::Decimal;
|
||||||
|
|
||||||
|
#[cfg(feature = "no_std")]
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
use num_traits::float::Float;
|
||||||
|
|
||||||
|
/// Is the type a numeric type?
|
||||||
|
fn is_numeric(type_id: TypeId) -> bool {
|
||||||
|
let result = type_id == TypeId::of::<u8>()
|
||||||
|
|| type_id == TypeId::of::<u16>()
|
||||||
|
|| type_id == TypeId::of::<u32>()
|
||||||
|
|| type_id == TypeId::of::<u64>()
|
||||||
|
|| type_id == TypeId::of::<i8>()
|
||||||
|
|| type_id == TypeId::of::<i16>()
|
||||||
|
|| type_id == TypeId::of::<i32>()
|
||||||
|
|| type_id == TypeId::of::<i64>();
|
||||||
|
|
||||||
|
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
||||||
|
let result = result || type_id == TypeId::of::<u128>() || type_id == TypeId::of::<i128>();
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
let result = result || type_id == TypeId::of::<f32>() || type_id == TypeId::of::<f64>();
|
||||||
|
|
||||||
|
#[cfg(feature = "decimal")]
|
||||||
|
let result = result || type_id == TypeId::of::<rust_decimal::Decimal>();
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build in common binary operator implementations to avoid the cost of calling a registered function.
|
||||||
|
pub fn get_builtin_binary_op_fn(
|
||||||
|
op: &str,
|
||||||
|
x: &Dynamic,
|
||||||
|
y: &Dynamic,
|
||||||
|
) -> Option<fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult> {
|
||||||
|
let type1 = x.type_id();
|
||||||
|
let type2 = y.type_id();
|
||||||
|
|
||||||
|
// One of the operands is a custom type, so it is never built-in
|
||||||
|
if x.is_variant() || y.is_variant() {
|
||||||
|
if is_numeric(type1) && is_numeric(type2) {
|
||||||
|
// Disallow comparisons between different numeric types
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the types are not the same, default to not compare
|
||||||
|
if type1 != type2 {
|
||||||
|
return match op {
|
||||||
|
"!=" => Some(|_, _| Ok(Dynamic::TRUE)),
|
||||||
|
"==" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disallow comparisons between the same type
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let types_pair = (type1, type2);
|
||||||
|
|
||||||
|
macro_rules! impl_op {
|
||||||
|
($func:ident ( $op:tt )) => {
|
||||||
|
return Some(|_, args| {
|
||||||
|
let (x, y) = $func(args);
|
||||||
|
Ok((x $op y).into())
|
||||||
|
})
|
||||||
|
};
|
||||||
|
($xx:ident $op:tt $yy:ident -> $base:ty) => {
|
||||||
|
return Some(|_, args| {
|
||||||
|
let x = args[0].$xx().unwrap() as $base;
|
||||||
|
let y = args[1].$yy().unwrap() as $base;
|
||||||
|
Ok((x $op y).into())
|
||||||
|
})
|
||||||
|
};
|
||||||
|
($xx:ident . $func:ident ( $yy:ident as $yyy:ty) -> $base:ty) => {
|
||||||
|
return Some(|_, args| {
|
||||||
|
let x = args[0].$xx().unwrap() as $base;
|
||||||
|
let y = args[1].$yy().unwrap() as $base;
|
||||||
|
Ok(x.$func(y as $yyy).into())
|
||||||
|
})
|
||||||
|
};
|
||||||
|
($func:ident ( $xx:ident, $yy:ident ) -> $base:ty) => {
|
||||||
|
return Some(|_, args| {
|
||||||
|
let x = args[0].$xx().unwrap() as $base;
|
||||||
|
let y = args[1].$yy().unwrap() as $base;
|
||||||
|
$func(x, y)
|
||||||
|
})
|
||||||
|
};
|
||||||
|
($xx:ident $op:tt $yy:ident -> from $base:ty) => {
|
||||||
|
return Some(|_, args| {
|
||||||
|
let x = <$base>::from(args[0].$xx().unwrap());
|
||||||
|
let y = <$base>::from(args[1].$yy().unwrap());
|
||||||
|
Ok((x $op y).into())
|
||||||
|
})
|
||||||
|
};
|
||||||
|
($xx:ident . $func:ident ( $yy:ident ) -> from $base:ty) => {
|
||||||
|
return Some(|_, args| {
|
||||||
|
let x = <$base>::from(args[0].$xx().unwrap());
|
||||||
|
let y = <$base>::from(args[1].$yy().unwrap());
|
||||||
|
Ok(x.$func(y).into())
|
||||||
|
})
|
||||||
|
};
|
||||||
|
($func:ident ( $xx:ident, $yy:ident ) -> from $base:ty) => {
|
||||||
|
return Some(|_, args| {
|
||||||
|
let x = <$base>::from(args[0].$xx().unwrap());
|
||||||
|
let y = <$base>::from(args[1].$yy().unwrap());
|
||||||
|
$func(x, y)
|
||||||
|
})
|
||||||
|
};
|
||||||
|
(& $x:ident $op:tt & $y:ident) => {
|
||||||
|
return Some(|_, args| {
|
||||||
|
let x = &*args[0].read_lock::<$x>().unwrap();
|
||||||
|
let y = &*args[1].read_lock::<$y>().unwrap();
|
||||||
|
Ok((x $op y).into())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_float {
|
||||||
|
($x:ty, $xx:ident, $y:ty, $yy:ident) => {
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
if types_pair == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
||||||
|
match op {
|
||||||
|
"+" => impl_op!($xx + $yy -> FLOAT),
|
||||||
|
"-" => impl_op!($xx - $yy -> FLOAT),
|
||||||
|
"*" => impl_op!($xx * $yy -> FLOAT),
|
||||||
|
"/" => impl_op!($xx / $yy -> FLOAT),
|
||||||
|
"%" => impl_op!($xx % $yy -> FLOAT),
|
||||||
|
"**" => impl_op!($xx.powf($yy as FLOAT) -> FLOAT),
|
||||||
|
"==" => impl_op!($xx == $yy -> FLOAT),
|
||||||
|
"!=" => impl_op!($xx != $yy -> FLOAT),
|
||||||
|
">" => impl_op!($xx > $yy -> FLOAT),
|
||||||
|
">=" => impl_op!($xx >= $yy -> FLOAT),
|
||||||
|
"<" => impl_op!($xx < $yy -> FLOAT),
|
||||||
|
"<=" => impl_op!($xx <= $yy -> FLOAT),
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_float!(FLOAT, as_float, FLOAT, as_float);
|
||||||
|
impl_float!(FLOAT, as_float, INT, as_int);
|
||||||
|
impl_float!(INT, as_int, FLOAT, as_float);
|
||||||
|
|
||||||
|
macro_rules! impl_decimal {
|
||||||
|
($x:ty, $xx:ident, $y:ty, $yy:ident) => {
|
||||||
|
#[cfg(feature = "decimal")]
|
||||||
|
if types_pair == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
||||||
|
if cfg!(not(feature = "unchecked")) {
|
||||||
|
use crate::packages::arithmetic::decimal_functions::*;
|
||||||
|
|
||||||
|
match op {
|
||||||
|
"+" => impl_op!(add($xx, $yy) -> from Decimal),
|
||||||
|
"-" => impl_op!(subtract($xx, $yy) -> from Decimal),
|
||||||
|
"*" => impl_op!(multiply($xx, $yy) -> from Decimal),
|
||||||
|
"/" => impl_op!(divide($xx, $yy) -> from Decimal),
|
||||||
|
"%" => impl_op!(modulo($xx, $yy) -> from Decimal),
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match op {
|
||||||
|
"+" => impl_op!($xx + $yy -> from Decimal),
|
||||||
|
"-" => impl_op!($xx - $yy -> from Decimal),
|
||||||
|
"*" => impl_op!($xx * $yy -> from Decimal),
|
||||||
|
"/" => impl_op!($xx / $yy -> from Decimal),
|
||||||
|
"%" => impl_op!($xx % $yy -> from Decimal),
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match op {
|
||||||
|
"==" => impl_op!($xx == $yy -> from Decimal),
|
||||||
|
"!=" => impl_op!($xx != $yy -> from Decimal),
|
||||||
|
">" => impl_op!($xx > $yy -> from Decimal),
|
||||||
|
">=" => impl_op!($xx >= $yy -> from Decimal),
|
||||||
|
"<" => impl_op!($xx < $yy -> from Decimal),
|
||||||
|
"<=" => impl_op!($xx <= $yy -> from Decimal),
|
||||||
|
_ => return None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_decimal!(Decimal, as_decimal, Decimal, as_decimal);
|
||||||
|
impl_decimal!(Decimal, as_decimal, INT, as_int);
|
||||||
|
impl_decimal!(INT, as_int, Decimal, as_decimal);
|
||||||
|
|
||||||
|
// char op string
|
||||||
|
if types_pair == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
|
||||||
|
#[inline(always)]
|
||||||
|
fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) {
|
||||||
|
let x = args[0].as_char().unwrap();
|
||||||
|
let y = &*args[1].read_lock::<ImmutableString>().unwrap();
|
||||||
|
let s1 = [x, '\0'];
|
||||||
|
let mut y = y.chars();
|
||||||
|
let s2 = [y.next().unwrap_or('\0'), y.next().unwrap_or('\0')];
|
||||||
|
(s1, s2)
|
||||||
|
}
|
||||||
|
|
||||||
|
match op {
|
||||||
|
"+" => {
|
||||||
|
return Some(|_, args| {
|
||||||
|
let x = args[0].as_char().unwrap();
|
||||||
|
let y = &*args[1].read_lock::<ImmutableString>().unwrap();
|
||||||
|
Ok(format!("{}{}", x, y).into())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
"==" => impl_op!(get_s1s2(==)),
|
||||||
|
"!=" => impl_op!(get_s1s2(!=)),
|
||||||
|
">" => impl_op!(get_s1s2(>)),
|
||||||
|
">=" => impl_op!(get_s1s2(>=)),
|
||||||
|
"<" => impl_op!(get_s1s2(<)),
|
||||||
|
"<=" => impl_op!(get_s1s2(<=)),
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// string op char
|
||||||
|
if types_pair == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
|
||||||
|
#[inline(always)]
|
||||||
|
fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) {
|
||||||
|
let x = &*args[0].read_lock::<ImmutableString>().unwrap();
|
||||||
|
let y = args[1].as_char().unwrap();
|
||||||
|
let mut x = x.chars();
|
||||||
|
let s1 = [x.next().unwrap_or('\0'), x.next().unwrap_or('\0')];
|
||||||
|
let s2 = [y, '\0'];
|
||||||
|
(s1, s2)
|
||||||
|
}
|
||||||
|
|
||||||
|
match op {
|
||||||
|
"+" => {
|
||||||
|
return Some(|_, args| {
|
||||||
|
let x = &*args[0].read_lock::<ImmutableString>().unwrap();
|
||||||
|
let y = args[1].as_char().unwrap();
|
||||||
|
Ok((x + y).into())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
"-" => {
|
||||||
|
return Some(|_, args| {
|
||||||
|
let x = &*args[0].read_lock::<ImmutableString>().unwrap();
|
||||||
|
let y = args[1].as_char().unwrap();
|
||||||
|
Ok((x - y).into())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
"==" => impl_op!(get_s1s2(==)),
|
||||||
|
"!=" => impl_op!(get_s1s2(!=)),
|
||||||
|
">" => impl_op!(get_s1s2(>)),
|
||||||
|
">=" => impl_op!(get_s1s2(>=)),
|
||||||
|
"<" => impl_op!(get_s1s2(<)),
|
||||||
|
"<=" => impl_op!(get_s1s2(<=)),
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default comparison operators for different types
|
||||||
|
if type2 != type1 {
|
||||||
|
return match op {
|
||||||
|
"!=" => Some(|_, _| Ok(Dynamic::TRUE)),
|
||||||
|
"==" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Beyond here, type1 == type2
|
||||||
|
|
||||||
|
if type1 == TypeId::of::<INT>() {
|
||||||
|
if cfg!(not(feature = "unchecked")) {
|
||||||
|
use crate::packages::arithmetic::arith_basic::INT::functions::*;
|
||||||
|
|
||||||
|
match op {
|
||||||
|
"+" => impl_op!(add(as_int, as_int) -> INT),
|
||||||
|
"-" => impl_op!(subtract(as_int, as_int) -> INT),
|
||||||
|
"*" => impl_op!(multiply(as_int, as_int) -> INT),
|
||||||
|
"/" => impl_op!(divide(as_int, as_int) -> INT),
|
||||||
|
"%" => impl_op!(modulo(as_int, as_int) -> INT),
|
||||||
|
"**" => impl_op!(power(as_int, as_int) -> INT),
|
||||||
|
">>" => impl_op!(shift_right(as_int, as_int) -> INT),
|
||||||
|
"<<" => impl_op!(shift_left(as_int, as_int) -> INT),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match op {
|
||||||
|
"+" => impl_op!(as_int + as_int -> INT),
|
||||||
|
"-" => impl_op!(as_int - as_int -> INT),
|
||||||
|
"*" => impl_op!(as_int * as_int -> INT),
|
||||||
|
"/" => impl_op!(as_int / as_int -> INT),
|
||||||
|
"%" => impl_op!(as_int % as_int -> INT),
|
||||||
|
"**" => impl_op!(as_int.pow(as_int as u32) -> INT),
|
||||||
|
">>" => impl_op!(as_int >> as_int -> INT),
|
||||||
|
"<<" => impl_op!(as_int << as_int -> INT),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match op {
|
||||||
|
"==" => impl_op!(as_int == as_int -> INT),
|
||||||
|
"!=" => impl_op!(as_int != as_int -> INT),
|
||||||
|
">" => impl_op!(as_int > as_int -> INT),
|
||||||
|
">=" => impl_op!(as_int >= as_int -> INT),
|
||||||
|
"<" => impl_op!(as_int < as_int -> INT),
|
||||||
|
"<=" => impl_op!(as_int <= as_int -> INT),
|
||||||
|
"&" => impl_op!(as_int & as_int -> INT),
|
||||||
|
"|" => impl_op!(as_int | as_int -> INT),
|
||||||
|
"^" => impl_op!(as_int ^ as_int -> INT),
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if type1 == TypeId::of::<bool>() {
|
||||||
|
match op {
|
||||||
|
"==" => impl_op!(as_bool == as_bool -> bool),
|
||||||
|
"!=" => impl_op!(as_bool != as_bool -> bool),
|
||||||
|
">" => impl_op!(as_bool > as_bool -> bool),
|
||||||
|
">=" => impl_op!(as_bool >= as_bool -> bool),
|
||||||
|
"<" => impl_op!(as_bool < as_bool -> bool),
|
||||||
|
"<=" => impl_op!(as_bool <= as_bool -> bool),
|
||||||
|
"&" => impl_op!(as_bool & as_bool -> bool),
|
||||||
|
"|" => impl_op!(as_bool | as_bool -> bool),
|
||||||
|
"^" => impl_op!(as_bool ^ as_bool -> bool),
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if type1 == TypeId::of::<ImmutableString>() {
|
||||||
|
match op {
|
||||||
|
"+" => impl_op!(&ImmutableString + &ImmutableString),
|
||||||
|
"-" => impl_op!(&ImmutableString - &ImmutableString),
|
||||||
|
"==" => impl_op!(&ImmutableString == &ImmutableString),
|
||||||
|
"!=" => impl_op!(&ImmutableString != &ImmutableString),
|
||||||
|
">" => impl_op!(&ImmutableString > &ImmutableString),
|
||||||
|
">=" => impl_op!(&ImmutableString >= &ImmutableString),
|
||||||
|
"<" => impl_op!(&ImmutableString < &ImmutableString),
|
||||||
|
"<=" => impl_op!(&ImmutableString <= &ImmutableString),
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if type1 == TypeId::of::<char>() {
|
||||||
|
match op {
|
||||||
|
"==" => impl_op!(as_char == as_char -> char),
|
||||||
|
"!=" => impl_op!(as_char != as_char -> char),
|
||||||
|
">" => impl_op!(as_char > as_char -> char),
|
||||||
|
">=" => impl_op!(as_char >= as_char -> char),
|
||||||
|
"<" => impl_op!(as_char < as_char -> char),
|
||||||
|
"<=" => impl_op!(as_char <= as_char -> char),
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if type1 == TypeId::of::<()>() {
|
||||||
|
match op {
|
||||||
|
"==" => return Some(|_, _| Ok(Dynamic::TRUE)),
|
||||||
|
"!=" | ">" | ">=" | "<" | "<=" => return Some(|_, _| Ok(Dynamic::FALSE)),
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build in common operator assignment implementations to avoid the cost of calling a registered function.
|
||||||
|
pub fn get_builtin_op_assignment_fn(
|
||||||
|
op: &str,
|
||||||
|
x: &Dynamic,
|
||||||
|
y: &Dynamic,
|
||||||
|
) -> Option<fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult> {
|
||||||
|
let type1 = x.type_id();
|
||||||
|
let type2 = y.type_id();
|
||||||
|
|
||||||
|
let types_pair = (type1, type2);
|
||||||
|
|
||||||
|
macro_rules! impl_op {
|
||||||
|
($x:ident = x $op:tt $yy:ident) => {
|
||||||
|
return Some(|_, args| {
|
||||||
|
let x = args[0].$yy().unwrap();
|
||||||
|
let y = args[1].$yy().unwrap() as $x;
|
||||||
|
Ok((*args[0].write_lock::<$x>().unwrap() = x $op y).into())
|
||||||
|
})
|
||||||
|
};
|
||||||
|
($x:ident $op:tt $yy:ident) => {
|
||||||
|
return Some(|_, args| {
|
||||||
|
let y = args[1].$yy().unwrap() as $x;
|
||||||
|
Ok((*args[0].write_lock::<$x>().unwrap() $op y).into())
|
||||||
|
})
|
||||||
|
};
|
||||||
|
($x:ident $op:tt $yy:ident as $yyy:ty) => {
|
||||||
|
return Some(|_, args| {
|
||||||
|
let y = args[1].$yy().unwrap() as $yyy;
|
||||||
|
Ok((*args[0].write_lock::<$x>().unwrap() $op y).into())
|
||||||
|
})
|
||||||
|
};
|
||||||
|
($xx:ident . $func:ident ( $yy:ident as $yyy:ty ) -> $x:ty) => {
|
||||||
|
return Some(|_, args| {
|
||||||
|
let x = args[0].$xx().unwrap();
|
||||||
|
let y = args[1].$yy().unwrap() as $x;
|
||||||
|
Ok((*args[0].write_lock::<$x>().unwrap() = x.$func(y as $yyy)).into())
|
||||||
|
})
|
||||||
|
};
|
||||||
|
($func:ident ( $xx:ident, $yy:ident ) -> $x:ty) => {
|
||||||
|
return Some(|_, args| {
|
||||||
|
let x = args[0].$xx().unwrap();
|
||||||
|
let y = args[1].$yy().unwrap() as $x;
|
||||||
|
Ok((*args[0].write_lock().unwrap() = $func(x, y)?).into())
|
||||||
|
})
|
||||||
|
};
|
||||||
|
(from $x:ident $op:tt $yy:ident) => {
|
||||||
|
return Some(|_, args| {
|
||||||
|
let y = <$x>::from(args[1].$yy().unwrap());
|
||||||
|
Ok((*args[0].write_lock::<$x>().unwrap() $op y).into())
|
||||||
|
})
|
||||||
|
};
|
||||||
|
($xx:ident . $func:ident ( $yy:ident ) -> from $x:ty) => {
|
||||||
|
return Some(|_, args| {
|
||||||
|
let x = args[0].$xx().unwrap();
|
||||||
|
let y = <$x>::from(args[1].$yy().unwrap());
|
||||||
|
Ok((*args[0].write_lock::<$x>().unwrap() = x.$func(y)).into())
|
||||||
|
})
|
||||||
|
};
|
||||||
|
($func:ident ( $xx:ident, $yy:ident ) -> from $x:ty) => {
|
||||||
|
return Some(|_, args| {
|
||||||
|
let x = args[0].$xx().unwrap();
|
||||||
|
let y = <$x>::from(args[1].$yy().unwrap());
|
||||||
|
Ok((*args[0].write_lock().unwrap() = $func(x, y)?).into())
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_float {
|
||||||
|
($x:ident, $xx:ident, $y:ty, $yy:ident) => {
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
if types_pair == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
||||||
|
match op {
|
||||||
|
"+=" => impl_op!($x += $yy),
|
||||||
|
"-=" => impl_op!($x -= $yy),
|
||||||
|
"*=" => impl_op!($x *= $yy),
|
||||||
|
"/=" => impl_op!($x /= $yy),
|
||||||
|
"%=" => impl_op!($x %= $yy),
|
||||||
|
"**=" => impl_op!($xx.powf($yy as $x) -> $x),
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_float!(FLOAT, as_float, FLOAT, as_float);
|
||||||
|
impl_float!(FLOAT, as_float, INT, as_int);
|
||||||
|
|
||||||
|
macro_rules! impl_decimal {
|
||||||
|
($x:ident, $xx:ident, $y:ty, $yy:ident) => {
|
||||||
|
#[cfg(feature = "decimal")]
|
||||||
|
if types_pair == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
||||||
|
if cfg!(not(feature = "unchecked")) {
|
||||||
|
use crate::packages::arithmetic::decimal_functions::*;
|
||||||
|
|
||||||
|
match op {
|
||||||
|
"+=" => impl_op!(add($xx, $yy) -> from $x),
|
||||||
|
"-=" => impl_op!(subtract($xx, $yy) -> from $x),
|
||||||
|
"*=" => impl_op!(multiply($xx, $yy) -> from $x),
|
||||||
|
"/=" => impl_op!(divide($xx, $yy) -> from $x),
|
||||||
|
"%=" => impl_op!(modulo($xx, $yy) -> from $x),
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match op {
|
||||||
|
"+=" => impl_op!(from $x += $yy),
|
||||||
|
"-=" => impl_op!(from $x -= $yy),
|
||||||
|
"*=" => impl_op!(from $x *= $yy),
|
||||||
|
"/=" => impl_op!(from $x /= $yy),
|
||||||
|
"%=" => impl_op!(from $x %= $yy),
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_decimal!(Decimal, as_decimal, Decimal, as_decimal);
|
||||||
|
impl_decimal!(Decimal, as_decimal, INT, as_int);
|
||||||
|
|
||||||
|
// string op= char
|
||||||
|
if types_pair == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
|
||||||
|
match op {
|
||||||
|
"+=" => impl_op!(ImmutableString += as_char as char),
|
||||||
|
"-=" => impl_op!(ImmutableString -= as_char as char),
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// char op= string
|
||||||
|
if types_pair == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
|
||||||
|
match op {
|
||||||
|
"+=" => {
|
||||||
|
return Some(|_, args| {
|
||||||
|
let mut ch = args[0].as_char().unwrap().to_string();
|
||||||
|
ch.push_str(args[1].read_lock::<ImmutableString>().unwrap().as_str());
|
||||||
|
|
||||||
|
let mut x = args[0].write_lock::<Dynamic>().unwrap();
|
||||||
|
Ok((*x = ch.into()).into())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No built-in op-assignments for different types.
|
||||||
|
if type2 != type1 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Beyond here, type1 == type2
|
||||||
|
if type1 == TypeId::of::<INT>() {
|
||||||
|
if cfg!(not(feature = "unchecked")) {
|
||||||
|
use crate::packages::arithmetic::arith_basic::INT::functions::*;
|
||||||
|
|
||||||
|
match op {
|
||||||
|
"+=" => impl_op!(add(as_int, as_int) -> INT),
|
||||||
|
"-=" => impl_op!(subtract(as_int, as_int) -> INT),
|
||||||
|
"*=" => impl_op!(multiply(as_int, as_int) -> INT),
|
||||||
|
"/=" => impl_op!(divide(as_int, as_int) -> INT),
|
||||||
|
"%=" => impl_op!(modulo(as_int, as_int) -> INT),
|
||||||
|
"**=" => impl_op!(power(as_int, as_int) -> INT),
|
||||||
|
">>=" => impl_op!(shift_right(as_int, as_int) -> INT),
|
||||||
|
"<<=" => impl_op!(shift_left(as_int, as_int) -> INT),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match op {
|
||||||
|
"+=" => impl_op!(INT += as_int),
|
||||||
|
"-=" => impl_op!(INT -= as_int),
|
||||||
|
"*=" => impl_op!(INT *= as_int),
|
||||||
|
"/=" => impl_op!(INT /= as_int),
|
||||||
|
"%=" => impl_op!(INT %= as_int),
|
||||||
|
"**=" => impl_op!(as_int.pow(as_int as u32) -> INT),
|
||||||
|
">>=" => impl_op!(INT >>= as_int),
|
||||||
|
"<<=" => impl_op!(INT <<= as_int),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match op {
|
||||||
|
"&=" => impl_op!(INT &= as_int),
|
||||||
|
"|=" => impl_op!(INT |= as_int),
|
||||||
|
"^=" => impl_op!(INT ^= as_int),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if type1 == TypeId::of::<bool>() {
|
||||||
|
match op {
|
||||||
|
"&=" => impl_op!(bool = x && as_bool),
|
||||||
|
"|=" => impl_op!(bool = x || as_bool),
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if type1 == TypeId::of::<char>() {
|
||||||
|
match op {
|
||||||
|
"+=" => {
|
||||||
|
return Some(|_, args| {
|
||||||
|
let y = args[1].as_char().unwrap();
|
||||||
|
let mut x = args[0].write_lock::<Dynamic>().unwrap();
|
||||||
|
Ok((*x = format!("{}{}", *x, y).into()).into())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if type1 == TypeId::of::<ImmutableString>() {
|
||||||
|
match op {
|
||||||
|
"+=" => {
|
||||||
|
return Some(|_, args| {
|
||||||
|
let (first, second) = args.split_first_mut().unwrap();
|
||||||
|
let mut x = first.write_lock::<ImmutableString>().unwrap();
|
||||||
|
let y = &*second[0].read_lock::<ImmutableString>().unwrap();
|
||||||
|
Ok((*x += y).into())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
"-=" => {
|
||||||
|
return Some(|_, args| {
|
||||||
|
let (first, second) = args.split_first_mut().unwrap();
|
||||||
|
let mut x = first.write_lock::<ImmutableString>().unwrap();
|
||||||
|
let y = &*second[0].read_lock::<ImmutableString>().unwrap();
|
||||||
|
Ok((*x -= y).into())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
1135
src/fn_call.rs
1135
src/fn_call.rs
File diff suppressed because it is too large
Load Diff
@ -15,7 +15,7 @@ use crate::stdlib::{
|
|||||||
use crate::token::is_valid_identifier;
|
use crate::token::is_valid_identifier;
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_script_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, ImmutableString, Module,
|
calc_script_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, ImmutableString, Module,
|
||||||
Position,
|
Position, RhaiResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
@ -55,20 +55,19 @@ pub type Locked<T> = crate::stdlib::sync::RwLock<T>;
|
|||||||
|
|
||||||
/// Context of a native Rust function call.
|
/// Context of a native Rust function call.
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct NativeCallContext<'e, 'n, 's, 'a, 'm> {
|
pub struct NativeCallContext<'a> {
|
||||||
engine: &'e Engine,
|
engine: &'a Engine,
|
||||||
fn_name: &'n str,
|
fn_name: &'a str,
|
||||||
source: Option<&'s str>,
|
source: Option<&'a str>,
|
||||||
pub(crate) mods: Option<&'a Imports>,
|
mods: Option<&'a Imports>,
|
||||||
pub(crate) lib: &'m [&'m Module],
|
lib: &'a [&'a Module],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'e, 'n, 's, 'a, 'm, M: AsRef<[&'m Module]> + ?Sized>
|
impl<'a, M: AsRef<[&'a Module]> + ?Sized>
|
||||||
From<(&'e Engine, &'n str, Option<&'s str>, &'a Imports, &'m M)>
|
From<(&'a Engine, &'a str, Option<&'a str>, &'a Imports, &'a M)> for NativeCallContext<'a>
|
||||||
for NativeCallContext<'e, 'n, 's, 'a, 'm>
|
|
||||||
{
|
{
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(value: (&'e Engine, &'n str, Option<&'s str>, &'a Imports, &'m M)) -> Self {
|
fn from(value: (&'a Engine, &'a str, Option<&'a str>, &'a Imports, &'a M)) -> Self {
|
||||||
Self {
|
Self {
|
||||||
engine: value.0,
|
engine: value.0,
|
||||||
fn_name: value.1,
|
fn_name: value.1,
|
||||||
@ -79,11 +78,11 @@ impl<'e, 'n, 's, 'a, 'm, M: AsRef<[&'m Module]> + ?Sized>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'e, 'n, 'm, M: AsRef<[&'m Module]> + ?Sized> From<(&'e Engine, &'n str, &'m M)>
|
impl<'a, M: AsRef<[&'a Module]> + ?Sized> From<(&'a Engine, &'a str, &'a M)>
|
||||||
for NativeCallContext<'e, 'n, '_, '_, 'm>
|
for NativeCallContext<'a>
|
||||||
{
|
{
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(value: (&'e Engine, &'n str, &'m M)) -> Self {
|
fn from(value: (&'a Engine, &'a str, &'a M)) -> Self {
|
||||||
Self {
|
Self {
|
||||||
engine: value.0,
|
engine: value.0,
|
||||||
fn_name: value.1,
|
fn_name: value.1,
|
||||||
@ -94,16 +93,16 @@ impl<'e, 'n, 'm, M: AsRef<[&'m Module]> + ?Sized> From<(&'e Engine, &'n str, &'m
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'e, 'n, 's, 'a, 'm> NativeCallContext<'e, 'n, 's, 'a, 'm> {
|
impl<'a> NativeCallContext<'a> {
|
||||||
/// Create a new [`NativeCallContext`].
|
/// Create a new [`NativeCallContext`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new(engine: &'e Engine, fn_name: &'n str, lib: &'m impl AsRef<[&'m Module]>) -> Self {
|
pub fn new(engine: &'a Engine, fn_name: &'a str, lib: &'a [&Module]) -> Self {
|
||||||
Self {
|
Self {
|
||||||
engine,
|
engine,
|
||||||
fn_name,
|
fn_name,
|
||||||
source: None,
|
source: None,
|
||||||
mods: None,
|
mods: None,
|
||||||
lib: lib.as_ref(),
|
lib,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// _(INTERNALS)_ Create a new [`NativeCallContext`].
|
/// _(INTERNALS)_ Create a new [`NativeCallContext`].
|
||||||
@ -112,18 +111,18 @@ impl<'e, 'n, 's, 'a, 'm> NativeCallContext<'e, 'n, 's, 'a, 'm> {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new_with_all_fields(
|
pub fn new_with_all_fields(
|
||||||
engine: &'e Engine,
|
engine: &'a Engine,
|
||||||
fn_name: &'n str,
|
fn_name: &'a str,
|
||||||
source: &'s Option<&str>,
|
source: &'a Option<&str>,
|
||||||
imports: &'a mut Imports,
|
imports: &'a Imports,
|
||||||
lib: &'m impl AsRef<[&'m Module]>,
|
lib: &'a [&Module],
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
engine,
|
engine,
|
||||||
fn_name,
|
fn_name,
|
||||||
source: source.clone(),
|
source: source.clone(),
|
||||||
mods: Some(imports),
|
mods: Some(imports),
|
||||||
lib: lib.as_ref(),
|
lib,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// The current [`Engine`].
|
/// The current [`Engine`].
|
||||||
@ -147,6 +146,14 @@ impl<'e, 'n, 's, 'a, 'm> NativeCallContext<'e, 'n, 's, 'a, 'm> {
|
|||||||
pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &Module)> {
|
pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &Module)> {
|
||||||
self.mods.iter().flat_map(|&m| m.iter())
|
self.mods.iter().flat_map(|&m| m.iter())
|
||||||
}
|
}
|
||||||
|
/// Get an iterator over the current set of modules imported via `import` statements.
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn iter_imports_raw(
|
||||||
|
&self,
|
||||||
|
) -> impl Iterator<Item = (&ImmutableString, &Shared<Module>)> {
|
||||||
|
self.mods.iter().flat_map(|&m| m.iter_raw())
|
||||||
|
}
|
||||||
/// _(INTERNALS)_ The current set of modules imported via `import` statements.
|
/// _(INTERNALS)_ The current set of modules imported via `import` statements.
|
||||||
/// Available under the `internals` feature only.
|
/// Available under the `internals` feature only.
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
@ -184,9 +191,8 @@ impl<'e, 'n, 's, 'a, 'm> NativeCallContext<'e, 'n, 's, 'a, 'm> {
|
|||||||
&self,
|
&self,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
is_method: bool,
|
is_method: bool,
|
||||||
public_only: bool,
|
|
||||||
args: &mut [&mut Dynamic],
|
args: &mut [&mut Dynamic],
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> RhaiResult {
|
||||||
self.engine()
|
self.engine()
|
||||||
.exec_fn_call(
|
.exec_fn_call(
|
||||||
&mut self.mods.cloned().unwrap_or_default(),
|
&mut self.mods.cloned().unwrap_or_default(),
|
||||||
@ -197,7 +203,6 @@ impl<'e, 'n, 's, 'a, 'm> NativeCallContext<'e, 'n, 's, 'a, 'm> {
|
|||||||
args,
|
args,
|
||||||
is_method,
|
is_method,
|
||||||
is_method,
|
is_method,
|
||||||
public_only,
|
|
||||||
Position::NONE,
|
Position::NONE,
|
||||||
None,
|
None,
|
||||||
0,
|
0,
|
||||||
@ -319,7 +324,7 @@ impl FnPtr {
|
|||||||
ctx: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
this_ptr: Option<&mut Dynamic>,
|
this_ptr: Option<&mut Dynamic>,
|
||||||
mut arg_values: impl AsMut<[Dynamic]>,
|
mut arg_values: impl AsMut<[Dynamic]>,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> RhaiResult {
|
||||||
let arg_values = arg_values.as_mut();
|
let arg_values = arg_values.as_mut();
|
||||||
|
|
||||||
let mut args_data = self
|
let mut args_data = self
|
||||||
@ -337,7 +342,7 @@ impl FnPtr {
|
|||||||
args.insert(0, obj);
|
args.insert(0, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.call_fn_dynamic_raw(self.fn_name(), is_method, true, args.as_mut())
|
ctx.call_fn_dynamic_raw(self.fn_name(), is_method, args.as_mut())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -383,11 +388,10 @@ impl TryFrom<&str> for FnPtr {
|
|||||||
|
|
||||||
/// A general function trail object.
|
/// A general function trail object.
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub type FnAny = dyn Fn(NativeCallContext, &mut FnCallArgs) -> Result<Dynamic, Box<EvalAltResult>>;
|
pub type FnAny = dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult;
|
||||||
/// A general function trail object.
|
/// A general function trail object.
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub type FnAny =
|
pub type FnAny = dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult + Send + Sync;
|
||||||
dyn Fn(NativeCallContext, &mut FnCallArgs) -> Result<Dynamic, Box<EvalAltResult>> + Send + Sync;
|
|
||||||
|
|
||||||
/// A standard function that gets an iterator from a type.
|
/// A standard function that gets an iterator from a type.
|
||||||
pub type IteratorFn = fn(Dynamic) -> Box<dyn Iterator<Item = Dynamic>>;
|
pub type IteratorFn = fn(Dynamic) -> Box<dyn Iterator<Item = Dynamic>>;
|
||||||
|
@ -7,7 +7,7 @@ use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync};
|
|||||||
use crate::r#unsafe::unsafe_cast_box;
|
use crate::r#unsafe::unsafe_cast_box;
|
||||||
use crate::stdlib::{any::TypeId, boxed::Box, mem, string::String};
|
use crate::stdlib::{any::TypeId, boxed::Box, mem, string::String};
|
||||||
use crate::{
|
use crate::{
|
||||||
Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, ImmutableString, NativeCallContext,
|
Dynamic, Engine, FnAccess, FnNamespace, ImmutableString, NativeCallContext, RhaiResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Trait to register custom functions with the [`Engine`].
|
/// Trait to register custom functions with the [`Engine`].
|
||||||
@ -42,7 +42,7 @@ pub trait RegisterFn<FN, ARGS, RET> {
|
|||||||
fn register_fn(&mut self, name: &str, f: FN) -> &mut Self;
|
fn register_fn(&mut self, name: &str, f: FN) -> &mut Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait to register fallible custom functions returning [`Result`]`<`[`Dynamic`]`, `[`Box`]`<`[`EvalAltResult`]`>>` with the [`Engine`].
|
/// Trait to register fallible custom functions returning [`Result`]`<`[`Dynamic`]`, `[`Box`]`<`[`EvalAltResult`][crate::EvalAltResult]`>>` with the [`Engine`].
|
||||||
pub trait RegisterResultFn<FN, ARGS> {
|
pub trait RegisterResultFn<FN, ARGS> {
|
||||||
/// Register a custom fallible function with the [`Engine`].
|
/// Register a custom fallible function with the [`Engine`].
|
||||||
///
|
///
|
||||||
@ -131,7 +131,7 @@ macro_rules! make_func {
|
|||||||
$($let)*
|
$($let)*
|
||||||
$($par = ($convert)(_drain.next().unwrap()); )*
|
$($par = ($convert)(_drain.next().unwrap()); )*
|
||||||
|
|
||||||
// Call the function with each parameter value
|
// Call the function with each argument value
|
||||||
let r = $fn($($arg),*);
|
let r = $fn($($arg),*);
|
||||||
|
|
||||||
// Map the result
|
// Map the result
|
||||||
@ -142,15 +142,13 @@ macro_rules! make_func {
|
|||||||
|
|
||||||
/// To Dynamic mapping function.
|
/// To Dynamic mapping function.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn map_dynamic(data: impl Variant + Clone) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn map_dynamic(data: impl Variant + Clone) -> RhaiResult {
|
||||||
Ok(data.into_dynamic())
|
Ok(data.into_dynamic())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// To Dynamic mapping function.
|
/// To Dynamic mapping function.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn map_result(
|
pub fn map_result(data: RhaiResult) -> RhaiResult {
|
||||||
data: Result<Dynamic, Box<EvalAltResult>>,
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,7 +195,7 @@ macro_rules! def_register {
|
|||||||
|
|
||||||
impl<
|
impl<
|
||||||
$($par: Variant + Clone,)*
|
$($par: Variant + Clone,)*
|
||||||
FN: Fn($($param),*) -> Result<Dynamic, Box<EvalAltResult>> + SendSync + 'static,
|
FN: Fn($($param),*) -> RhaiResult + SendSync + 'static,
|
||||||
> RegisterResultFn<FN, ($($mark,)*)> for Engine
|
> RegisterResultFn<FN, ($($mark,)*)> for Engine
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -69,6 +69,7 @@ mod engine;
|
|||||||
mod engine_api;
|
mod engine_api;
|
||||||
mod engine_settings;
|
mod engine_settings;
|
||||||
mod fn_args;
|
mod fn_args;
|
||||||
|
mod fn_builtin;
|
||||||
mod fn_call;
|
mod fn_call;
|
||||||
mod fn_func;
|
mod fn_func;
|
||||||
mod fn_native;
|
mod fn_native;
|
||||||
@ -87,6 +88,8 @@ mod token;
|
|||||||
mod r#unsafe;
|
mod r#unsafe;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
|
type RhaiResult = Result<Dynamic, stdlib::boxed::Box<EvalAltResult>>;
|
||||||
|
|
||||||
/// The system integer type. It is defined as [`i64`].
|
/// The system integer type. It is defined as [`i64`].
|
||||||
///
|
///
|
||||||
/// If the `only_i32` feature is enabled, this will be [`i32`] instead.
|
/// If the `only_i32` feature is enabled, this will be [`i32`] instead.
|
||||||
|
@ -47,25 +47,6 @@ impl Default for FnNamespace {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FnNamespace {
|
|
||||||
/// Is this namespace [global][FnNamespace::Global]?
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn is_global(self) -> bool {
|
|
||||||
match self {
|
|
||||||
Self::Global => true,
|
|
||||||
Self::Internal => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Is this namespace [internal][FnNamespace::Internal]?
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn is_internal(self) -> bool {
|
|
||||||
match self {
|
|
||||||
Self::Global => false,
|
|
||||||
Self::Internal => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Data structure containing a single registered function.
|
/// Data structure containing a single registered function.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct FuncInfo {
|
pub struct FuncInfo {
|
||||||
@ -372,12 +353,15 @@ impl Module {
|
|||||||
self.indexed
|
self.indexed
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate signatures for all the functions in the [`Module`].
|
/// Generate signatures for all the non-private functions in the [`Module`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn gen_fn_signatures(&self) -> impl Iterator<Item = String> + '_ {
|
pub fn gen_fn_signatures(&self) -> impl Iterator<Item = String> + '_ {
|
||||||
self.functions
|
self.functions
|
||||||
.values()
|
.values()
|
||||||
.filter(|FuncInfo { access, .. }| !access.is_private())
|
.filter(|FuncInfo { access, .. }| match access {
|
||||||
|
FnAccess::Public => true,
|
||||||
|
FnAccess::Private => false,
|
||||||
|
})
|
||||||
.map(FuncInfo::gen_signature)
|
.map(FuncInfo::gen_signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -619,7 +603,10 @@ impl Module {
|
|||||||
if public_only {
|
if public_only {
|
||||||
self.functions
|
self.functions
|
||||||
.get(&hash_fn)
|
.get(&hash_fn)
|
||||||
.map_or(false, |FuncInfo { access, .. }| access.is_public())
|
.map_or(false, |FuncInfo { access, .. }| match access {
|
||||||
|
FnAccess::Public => true,
|
||||||
|
FnAccess::Private => false,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
self.functions.contains_key(&hash_fn)
|
self.functions.contains_key(&hash_fn)
|
||||||
}
|
}
|
||||||
@ -640,13 +627,9 @@ impl Module {
|
|||||||
/// The _last entry_ in the list should be the _return type_ of the function.
|
/// The _last entry_ in the list should be the _return type_ of the function.
|
||||||
/// In other words, the number of entries should be one larger than the number of parameters.
|
/// In other words, the number of entries should be one larger than the number of parameters.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn update_fn_metadata<'a>(
|
pub fn update_fn_metadata(&mut self, hash_fn: NonZeroU64, arg_names: &[&str]) -> &mut Self {
|
||||||
&mut self,
|
|
||||||
hash_fn: NonZeroU64,
|
|
||||||
arg_names: impl AsRef<[&'a str]>,
|
|
||||||
) -> &mut Self {
|
|
||||||
if let Some(f) = self.functions.get_mut(&hash_fn) {
|
if let Some(f) = self.functions.get_mut(&hash_fn) {
|
||||||
f.param_names = arg_names.as_ref().iter().map(|&n| n.into()).collect();
|
f.param_names = arg_names.iter().map(|&n| n.into()).collect();
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -739,6 +722,8 @@ impl Module {
|
|||||||
///
|
///
|
||||||
/// This function is very low level.
|
/// This function is very low level.
|
||||||
///
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
/// A list of [`TypeId`]'s is taken as the argument types.
|
/// A list of [`TypeId`]'s is taken as the argument types.
|
||||||
///
|
///
|
||||||
/// Arguments are simply passed in as a mutable array of [`&mut Dynamic`][Dynamic],
|
/// Arguments are simply passed in as a mutable array of [`&mut Dynamic`][Dynamic],
|
||||||
@ -747,12 +732,12 @@ impl Module {
|
|||||||
/// The function is assumed to be a _method_, meaning that the first argument should not be consumed.
|
/// The function is assumed to be a _method_, meaning that the first argument should not be consumed.
|
||||||
/// All other arguments can be consumed.
|
/// All other arguments can be consumed.
|
||||||
///
|
///
|
||||||
/// To access a primary parameter value (i.e. cloning is cheap), use: `args[n].clone().cast::<T>()`
|
/// To access a primary argument value (i.e. cloning is cheap), use: `args[n].as_xxx().unwrap()`
|
||||||
///
|
///
|
||||||
/// To access a parameter value and avoid cloning, use `std::mem::take(args[n]).cast::<T>()`.
|
/// To access an argument value and avoid cloning, use `std::mem::take(args[n]).cast::<T>()`.
|
||||||
/// Notice that this will _consume_ the argument, replacing it with `()`.
|
/// Notice that this will _consume_ the argument, replacing it with `()`.
|
||||||
///
|
///
|
||||||
/// To access the first mutable parameter, use `args.get_mut(0).unwrap()`
|
/// To access the first mutable argument, use `args.get_mut(0).unwrap()`
|
||||||
///
|
///
|
||||||
/// # Function Metadata
|
/// # Function Metadata
|
||||||
///
|
///
|
||||||
@ -1783,7 +1768,7 @@ impl Module {
|
|||||||
ast: &crate::AST,
|
ast: &crate::AST,
|
||||||
engine: &crate::Engine,
|
engine: &crate::Engine,
|
||||||
) -> Result<Self, Box<EvalAltResult>> {
|
) -> Result<Self, Box<EvalAltResult>> {
|
||||||
let mut mods: crate::engine::Imports = (&engine.global_sub_modules).into();
|
let mut mods: crate::engine::Imports = Default::default();
|
||||||
let orig_mods_len = mods.len();
|
let orig_mods_len = mods.len();
|
||||||
|
|
||||||
// Run the script
|
// Run the script
|
||||||
@ -1813,11 +1798,14 @@ impl Module {
|
|||||||
|
|
||||||
// Non-private functions defined become module functions
|
// Non-private functions defined become module functions
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
{
|
|
||||||
ast.lib()
|
ast.lib()
|
||||||
.functions
|
.functions
|
||||||
.values()
|
.values()
|
||||||
.filter(|FuncInfo { access, func, .. }| !access.is_private() && func.is_script())
|
.filter(|FuncInfo { access, .. }| match access {
|
||||||
|
FnAccess::Public => true,
|
||||||
|
FnAccess::Private => false,
|
||||||
|
})
|
||||||
|
.filter(|FuncInfo { func, .. }| func.is_script())
|
||||||
.for_each(|FuncInfo { func, .. }| {
|
.for_each(|FuncInfo { func, .. }| {
|
||||||
// Encapsulate AST environment
|
// Encapsulate AST environment
|
||||||
let mut func = func.get_fn_def().clone();
|
let mut func = func.get_fn_def().clone();
|
||||||
@ -1825,7 +1813,6 @@ impl Module {
|
|||||||
func.mods = func_mods.clone();
|
func.mods = func_mods.clone();
|
||||||
module.set_script_fn(func);
|
module.set_script_fn(func);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
module.set_id(ast.clone_source());
|
module.set_id(ast.clone_source());
|
||||||
module.build_index();
|
module.build_index();
|
||||||
@ -1833,6 +1820,31 @@ impl Module {
|
|||||||
Ok(module)
|
Ok(module)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Are there functions (or type iterators) marked for the specified namespace?
|
||||||
|
pub fn has_namespace(&self, namespace: FnNamespace, recursive: bool) -> bool {
|
||||||
|
// Type iterators are default global
|
||||||
|
if !self.type_iterators.is_empty() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Any function marked global?
|
||||||
|
if self.functions.values().any(|f| f.namespace == namespace) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan sub-modules
|
||||||
|
if recursive {
|
||||||
|
if self
|
||||||
|
.modules
|
||||||
|
.values()
|
||||||
|
.any(|m| m.has_namespace(namespace, recursive))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
/// Scan through all the sub-modules in the [`Module`] and build a hash index of all
|
/// Scan through all the sub-modules in the [`Module`] and build a hash index of all
|
||||||
/// variables and functions as one flattened namespace.
|
/// variables and functions as one flattened namespace.
|
||||||
///
|
///
|
||||||
@ -1866,26 +1878,30 @@ impl Module {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Index all Rust functions
|
// Index all Rust functions
|
||||||
module
|
module.functions.iter().for_each(
|
||||||
.functions
|
|
||||||
.iter()
|
|
||||||
.filter(|(_, FuncInfo { access, .. })| access.is_public())
|
|
||||||
.for_each(
|
|
||||||
|(
|
|(
|
||||||
&hash,
|
&hash,
|
||||||
FuncInfo {
|
FuncInfo {
|
||||||
name,
|
name,
|
||||||
namespace,
|
namespace,
|
||||||
|
access,
|
||||||
params,
|
params,
|
||||||
param_types,
|
param_types,
|
||||||
func,
|
func,
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
)| {
|
)| {
|
||||||
|
match namespace {
|
||||||
|
FnNamespace::Global => {
|
||||||
// Flatten all functions with global namespace
|
// Flatten all functions with global namespace
|
||||||
if namespace.is_global() {
|
|
||||||
functions.insert(hash, func.clone());
|
functions.insert(hash, func.clone());
|
||||||
}
|
}
|
||||||
|
FnNamespace::Internal => (),
|
||||||
|
}
|
||||||
|
match access {
|
||||||
|
FnAccess::Public => (),
|
||||||
|
FnAccess::Private => return, // Do not index private functions
|
||||||
|
}
|
||||||
|
|
||||||
let hash_qualified_script =
|
let hash_qualified_script =
|
||||||
crate::calc_script_fn_hash(qualifiers.iter().cloned(), name, *params)
|
crate::calc_script_fn_hash(qualifiers.iter().cloned(), name, *params)
|
||||||
@ -1899,15 +1915,11 @@ impl Module {
|
|||||||
// i.e. qualifiers + function name + number of arguments.
|
// i.e. qualifiers + function name + number of arguments.
|
||||||
// 2) Calculate a second hash with no qualifiers, empty function name,
|
// 2) Calculate a second hash with no qualifiers, empty function name,
|
||||||
// and the actual list of argument [`TypeId`]'.s
|
// and the actual list of argument [`TypeId`]'.s
|
||||||
let hash_fn_args = crate::calc_native_fn_hash(
|
let hash_fn_args =
|
||||||
empty(),
|
crate::calc_native_fn_hash(empty(), "", param_types.iter().cloned())
|
||||||
"",
|
|
||||||
param_types.iter().cloned(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
// 3) The two hashes are combined.
|
// 3) The two hashes are combined.
|
||||||
let hash_qualified_fn =
|
let hash_qualified_fn = combine_hashes(hash_qualified_script, hash_fn_args);
|
||||||
combine_hashes(hash_qualified_script, hash_fn_args);
|
|
||||||
|
|
||||||
functions.insert(hash_qualified_fn, func.clone());
|
functions.insert(hash_qualified_fn, func.clone());
|
||||||
} else if cfg!(not(feature = "no_function")) {
|
} else if cfg!(not(feature = "no_function")) {
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
use crate::ast::{Expr, ScriptFnDef, Stmt};
|
use crate::ast::{Expr, ScriptFnDef, Stmt};
|
||||||
use crate::dynamic::AccessMode;
|
use crate::dynamic::AccessMode;
|
||||||
use crate::engine::{Imports, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF};
|
use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF};
|
||||||
use crate::fn_call::run_builtin_binary_op;
|
use crate::fn_builtin::get_builtin_binary_op_fn;
|
||||||
use crate::parser::map_dynamic_to_expr;
|
use crate::parser::map_dynamic_to_expr;
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
@ -62,8 +62,6 @@ struct State<'a> {
|
|||||||
propagate_constants: bool,
|
propagate_constants: bool,
|
||||||
/// An [`Engine`] instance for eager function evaluation.
|
/// An [`Engine`] instance for eager function evaluation.
|
||||||
engine: &'a Engine,
|
engine: &'a Engine,
|
||||||
/// Collection of sub-modules.
|
|
||||||
mods: Imports,
|
|
||||||
/// [Module] containing script-defined functions.
|
/// [Module] containing script-defined functions.
|
||||||
lib: &'a [&'a Module],
|
lib: &'a [&'a Module],
|
||||||
/// Optimization level.
|
/// Optimization level.
|
||||||
@ -79,7 +77,6 @@ impl<'a> State<'a> {
|
|||||||
variables: vec![],
|
variables: vec![],
|
||||||
propagate_constants: true,
|
propagate_constants: true,
|
||||||
engine,
|
engine,
|
||||||
mods: (&engine.global_sub_modules).into(),
|
|
||||||
lib,
|
lib,
|
||||||
optimization_level: level,
|
optimization_level: level,
|
||||||
}
|
}
|
||||||
@ -669,13 +666,17 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
|||||||
&& x.args.iter().all(Expr::is_constant) // all arguments are constants
|
&& x.args.iter().all(Expr::is_constant) // all arguments are constants
|
||||||
&& !is_valid_identifier(x.name.chars()) // cannot be scripted
|
&& !is_valid_identifier(x.name.chars()) // cannot be scripted
|
||||||
=> {
|
=> {
|
||||||
let arg_values: StaticVec<_> = x.args.iter().map(|e| e.get_constant_value().unwrap()).collect();
|
let mut arg_values: StaticVec<_> = x.args.iter().map(|e| e.get_constant_value().unwrap()).collect();
|
||||||
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
|
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
|
||||||
|
|
||||||
// Search for overloaded operators (can override built-in).
|
// Search for overloaded operators (can override built-in).
|
||||||
if !state.engine.has_override_by_name_and_arguments(Some(&state.mods), None, state.lib, x.name.as_ref(), arg_types.as_ref(), false) {
|
if !state.engine.has_override_by_name_and_arguments(Some(&Default::default()), &mut Default::default(), state.lib, x.name.as_ref(), arg_types.as_ref()) {
|
||||||
if let Some(result) = run_builtin_binary_op(x.name.as_ref(), &arg_values[0], &arg_values[1])
|
if let Some(result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1])
|
||||||
.ok().flatten()
|
.and_then(|f| {
|
||||||
|
let ctx = (state.engine, x.name.as_ref(), state.lib).into();
|
||||||
|
let (first, second) = arg_values.split_first_mut().unwrap();
|
||||||
|
(f)(ctx, &mut [ first, &mut second[0] ]).ok()
|
||||||
|
})
|
||||||
.and_then(|result| map_dynamic_to_expr(result, *pos))
|
.and_then(|result| map_dynamic_to_expr(result, *pos))
|
||||||
{
|
{
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
|
@ -649,7 +649,7 @@ mod array_functions {
|
|||||||
|
|
||||||
for (a1, a2) in array.iter_mut().zip(array2.iter_mut()) {
|
for (a1, a2) in array.iter_mut().zip(array2.iter_mut()) {
|
||||||
let equals = ctx
|
let equals = ctx
|
||||||
.call_fn_dynamic_raw(OP_EQUALS, true, false, &mut [a1, a2])
|
.call_fn_dynamic_raw(OP_EQUALS, true, &mut [a1, a2])
|
||||||
.map(|v| v.as_bool().unwrap_or(false))?;
|
.map(|v| v.as_bool().unwrap_or(false))?;
|
||||||
|
|
||||||
if !equals {
|
if !equals {
|
||||||
|
@ -19,23 +19,10 @@ mod fn_ptr_functions {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub mod functions {
|
pub mod functions {
|
||||||
use crate::{calc_script_fn_hash, stdlib::iter::empty, INT};
|
|
||||||
|
|
||||||
#[rhai_fn(name = "is_anonymous", get = "is_anonymous", pure)]
|
#[rhai_fn(name = "is_anonymous", get = "is_anonymous", pure)]
|
||||||
pub fn is_anonymous(f: &mut FnPtr) -> bool {
|
pub fn is_anonymous(f: &mut FnPtr) -> bool {
|
||||||
f.is_anonymous()
|
f.is_anonymous()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_def_fn(ctx: NativeCallContext, fn_name: &str, num_params: INT) -> bool {
|
|
||||||
if num_params < 0 {
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
let hash_script = calc_script_fn_hash(empty(), fn_name, num_params as usize);
|
|
||||||
|
|
||||||
ctx.engine()
|
|
||||||
.has_override(ctx.mods, None, ctx.lib, None, hash_script, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
@ -123,15 +110,13 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> Array {
|
|||||||
|
|
||||||
let mut list: Array = Default::default();
|
let mut list: Array = Default::default();
|
||||||
|
|
||||||
ctx.lib
|
ctx.iter_namespaces()
|
||||||
.iter()
|
|
||||||
.flat_map(|m| m.iter_script_fn())
|
.flat_map(|m| m.iter_script_fn())
|
||||||
.for_each(|(_, _, _, _, f)| list.push(make_metadata(&dict, None, f).into()));
|
.for_each(|(_, _, _, _, f)| list.push(make_metadata(&dict, None, f).into()));
|
||||||
|
|
||||||
if let Some(mods) = ctx.mods {
|
#[cfg(not(feature = "no_module"))]
|
||||||
mods.iter_raw()
|
ctx.iter_imports_raw()
|
||||||
.for_each(|(ns, m)| scan_module(&mut list, &dict, ns.clone(), m.as_ref()));
|
.for_each(|(ns, m)| scan_module(&mut list, &dict, ns.clone(), m.as_ref()));
|
||||||
}
|
|
||||||
|
|
||||||
list
|
list
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,9 @@ use crate::dynamic::Variant;
|
|||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
ops::{Add, Range},
|
ops::{Add, Range},
|
||||||
|
string::ToString,
|
||||||
};
|
};
|
||||||
use crate::{def_package, EvalAltResult, INT};
|
use crate::{def_package, EvalAltResult, Position, INT};
|
||||||
|
|
||||||
fn get_range<T: Variant + Clone>(from: T, to: T) -> Result<Range<T>, Box<EvalAltResult>> {
|
fn get_range<T: Variant + Clone>(from: T, to: T) -> Result<Range<T>, Box<EvalAltResult>> {
|
||||||
Ok(from..to)
|
Ok(from..to)
|
||||||
@ -16,6 +17,23 @@ where
|
|||||||
for<'a> &'a T: Add<&'a T, Output = T>,
|
for<'a> &'a T: Add<&'a T, Output = T>,
|
||||||
T: Variant + Clone + PartialOrd;
|
T: Variant + Clone + PartialOrd;
|
||||||
|
|
||||||
|
impl<T> StepRange<T>
|
||||||
|
where
|
||||||
|
for<'a> &'a T: Add<&'a T, Output = T>,
|
||||||
|
T: Variant + Clone + PartialOrd,
|
||||||
|
{
|
||||||
|
pub fn new(from: T, to: T, step: T) -> Result<Self, Box<EvalAltResult>> {
|
||||||
|
if &from + &step == from {
|
||||||
|
Err(Box::new(EvalAltResult::ErrorArithmetic(
|
||||||
|
"invalid step value".to_string(),
|
||||||
|
Position::NONE,
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
Ok(Self(from, to, step))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> Iterator for StepRange<T>
|
impl<T> Iterator for StepRange<T>
|
||||||
where
|
where
|
||||||
for<'a> &'a T: Add<&'a T, Output = T>,
|
for<'a> &'a T: Add<&'a T, Output = T>,
|
||||||
@ -24,12 +42,18 @@ where
|
|||||||
type Item = T;
|
type Item = T;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<T> {
|
fn next(&mut self) -> Option<T> {
|
||||||
if self.0 < self.1 {
|
if self.0 == self.1 {
|
||||||
|
None
|
||||||
|
} else if self.0 < self.1 {
|
||||||
let v = self.0.clone();
|
let v = self.0.clone();
|
||||||
self.0 = self.0.add(&self.2);
|
let n = self.0.add(&self.2);
|
||||||
|
self.0 = if n >= self.1 { self.1.clone() } else { n };
|
||||||
Some(v)
|
Some(v)
|
||||||
} else {
|
} else {
|
||||||
None
|
let v = self.0.clone();
|
||||||
|
let n = self.0.add(&self.2);
|
||||||
|
self.0 = if n <= self.1 { self.1.clone() } else { n };
|
||||||
|
Some(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -39,7 +63,7 @@ where
|
|||||||
for<'a> &'a T: Add<&'a T, Output = T>,
|
for<'a> &'a T: Add<&'a T, Output = T>,
|
||||||
T: Variant + Clone + PartialOrd,
|
T: Variant + Clone + PartialOrd,
|
||||||
{
|
{
|
||||||
Ok(StepRange::<T>(from, to, step))
|
StepRange::<T>::new(from, to, step)
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! reg_range {
|
macro_rules! reg_range {
|
||||||
@ -47,7 +71,7 @@ macro_rules! reg_range {
|
|||||||
$(
|
$(
|
||||||
$lib.set_iterator::<Range<$y>>();
|
$lib.set_iterator::<Range<$y>>();
|
||||||
let hash = $lib.set_fn_2($x, get_range::<$y>);
|
let hash = $lib.set_fn_2($x, get_range::<$y>);
|
||||||
$lib.update_fn_metadata(hash, [
|
$lib.update_fn_metadata(hash, &[
|
||||||
concat!("from: ", stringify!($y)),
|
concat!("from: ", stringify!($y)),
|
||||||
concat!("to: ", stringify!($y)),
|
concat!("to: ", stringify!($y)),
|
||||||
concat!("Iterator<Item=", stringify!($y), ">")
|
concat!("Iterator<Item=", stringify!($y), ">")
|
||||||
@ -61,7 +85,7 @@ macro_rules! reg_stepped_range {
|
|||||||
$(
|
$(
|
||||||
$lib.set_iterator::<StepRange<$y>>();
|
$lib.set_iterator::<StepRange<$y>>();
|
||||||
let hash = $lib.set_fn_3($x, get_step_range::<$y>);
|
let hash = $lib.set_fn_3($x, get_step_range::<$y>);
|
||||||
$lib.update_fn_metadata(hash, [
|
$lib.update_fn_metadata(hash, &[
|
||||||
concat!("from: ", stringify!($y)),
|
concat!("from: ", stringify!($y)),
|
||||||
concat!("to: ", stringify!($y)),
|
concat!("to: ", stringify!($y)),
|
||||||
concat!("step: ", stringify!($y)),
|
concat!("step: ", stringify!($y)),
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use crate::def_package;
|
use crate::def_package;
|
||||||
use crate::fn_call::run_builtin_binary_op;
|
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
|
|
||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
@ -40,8 +39,6 @@ macro_rules! reg_functions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def_package!(crate:LogicPackage:"Logical operators.", lib, {
|
def_package!(crate:LogicPackage:"Logical operators.", lib, {
|
||||||
combine_with_exported_module!(lib, "logic", logic_functions);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
#[cfg(not(feature = "only_i64"))]
|
#[cfg(not(feature = "only_i64"))]
|
||||||
{
|
{
|
||||||
@ -97,65 +94,6 @@ gen_cmp_functions!(float => f64);
|
|||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
gen_cmp_functions!(decimal => Decimal);
|
gen_cmp_functions!(decimal => Decimal);
|
||||||
|
|
||||||
#[export_module]
|
|
||||||
mod logic_functions {
|
|
||||||
fn is_numeric(type_id: TypeId) -> bool {
|
|
||||||
let result = type_id == TypeId::of::<u8>()
|
|
||||||
|| type_id == TypeId::of::<u16>()
|
|
||||||
|| type_id == TypeId::of::<u32>()
|
|
||||||
|| type_id == TypeId::of::<u64>()
|
|
||||||
|| type_id == TypeId::of::<i8>()
|
|
||||||
|| type_id == TypeId::of::<i16>()
|
|
||||||
|| type_id == TypeId::of::<i32>()
|
|
||||||
|| type_id == TypeId::of::<i64>();
|
|
||||||
|
|
||||||
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
|
||||||
let result = result || type_id == TypeId::of::<u128>() || type_id == TypeId::of::<i128>();
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
let result = result || type_id == TypeId::of::<f32>() || type_id == TypeId::of::<f64>();
|
|
||||||
|
|
||||||
#[cfg(feature = "decimal")]
|
|
||||||
let result = result || type_id == TypeId::of::<rust_decimal::Decimal>();
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rhai_fn(
|
|
||||||
name = "==",
|
|
||||||
name = "!=",
|
|
||||||
name = ">",
|
|
||||||
name = ">=",
|
|
||||||
name = "<",
|
|
||||||
name = "<=",
|
|
||||||
return_raw
|
|
||||||
)]
|
|
||||||
pub fn cmp(
|
|
||||||
ctx: NativeCallContext,
|
|
||||||
x: Dynamic,
|
|
||||||
y: Dynamic,
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
let type_x = x.type_id();
|
|
||||||
let type_y = y.type_id();
|
|
||||||
|
|
||||||
if type_x != type_y && is_numeric(type_x) && is_numeric(type_y) {
|
|
||||||
// Disallow comparisons between different number types
|
|
||||||
} else if let Some(x) = run_builtin_binary_op(ctx.fn_name(), &x, &y)? {
|
|
||||||
return Ok(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(Box::new(EvalAltResult::ErrorFunctionNotFound(
|
|
||||||
format!(
|
|
||||||
"{} ({}, {})",
|
|
||||||
ctx.fn_name(),
|
|
||||||
ctx.engine().map_type_name(x.type_name()),
|
|
||||||
ctx.engine().map_type_name(y.type_name())
|
|
||||||
),
|
|
||||||
Position::NONE,
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod f32_functions {
|
mod f32_functions {
|
||||||
|
@ -61,7 +61,7 @@ mod map_functions {
|
|||||||
for (m1, v1) in map.iter_mut() {
|
for (m1, v1) in map.iter_mut() {
|
||||||
if let Some(v2) = map2.get_mut(m1) {
|
if let Some(v2) = map2.get_mut(m1) {
|
||||||
let equals = ctx
|
let equals = ctx
|
||||||
.call_fn_dynamic_raw(OP_EQUALS, true, false, &mut [v1, v2])
|
.call_fn_dynamic_raw(OP_EQUALS, true, &mut [v1, v2])
|
||||||
.map(|v| v.as_bool().unwrap_or(false))?;
|
.map(|v| v.as_bool().unwrap_or(false))?;
|
||||||
|
|
||||||
if !equals {
|
if !equals {
|
||||||
|
@ -21,7 +21,7 @@ def_package!(crate:BasicStringPackage:"Basic string utilities, including printin
|
|||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn print_with_func(fn_name: &str, ctx: &NativeCallContext, value: &mut Dynamic) -> ImmutableString {
|
fn print_with_func(fn_name: &str, ctx: &NativeCallContext, value: &mut Dynamic) -> ImmutableString {
|
||||||
match ctx.call_fn_dynamic_raw(fn_name, true, false, &mut [value]) {
|
match ctx.call_fn_dynamic_raw(fn_name, true, &mut [value]) {
|
||||||
Ok(result) if result.is::<ImmutableString>() => result.take_immutable_string().unwrap(),
|
Ok(result) if result.is::<ImmutableString>() => result.take_immutable_string().unwrap(),
|
||||||
Ok(result) => ctx.engine().map_type_name(result.type_name()).into(),
|
Ok(result) => ctx.engine().map_type_name(result.type_name()).into(),
|
||||||
Err(_) => ctx.engine().map_type_name(value.type_name()).into(),
|
Err(_) => ctx.engine().map_type_name(value.type_name()).into(),
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
pub use crate::fn_native::{CallableFunction, FnCallArgs};
|
pub use crate::fn_native::{CallableFunction, FnCallArgs};
|
||||||
pub use crate::stdlib::{any::TypeId, boxed::Box, format, mem, string::ToString, vec as new_vec};
|
pub use crate::stdlib::{any::TypeId, boxed::Box, format, mem, string::ToString, vec as new_vec};
|
||||||
|
use crate::RhaiResult;
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, ImmutableString, Module,
|
Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, ImmutableString, Module,
|
||||||
NativeCallContext, Position, RegisterFn, RegisterResultFn,
|
NativeCallContext, Position, RegisterFn, RegisterResultFn,
|
||||||
@ -18,11 +19,7 @@ pub use rhai_codegen::{export_fn, register_exported_fn};
|
|||||||
/// Use the `#[export_module]` and `#[export_fn]` procedural attributes instead.
|
/// Use the `#[export_module]` and `#[export_fn]` procedural attributes instead.
|
||||||
pub trait PluginFunction {
|
pub trait PluginFunction {
|
||||||
/// Call the plugin function with the arguments provided.
|
/// Call the plugin function with the arguments provided.
|
||||||
fn call(
|
fn call(&self, context: NativeCallContext, args: &mut FnCallArgs) -> RhaiResult;
|
||||||
&self,
|
|
||||||
context: NativeCallContext,
|
|
||||||
args: &mut FnCallArgs,
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>>;
|
|
||||||
|
|
||||||
/// Is this plugin function a method?
|
/// Is this plugin function a method?
|
||||||
fn is_method_call(&self) -> bool;
|
fn is_method_call(&self) -> bool;
|
||||||
|
@ -274,7 +274,7 @@ impl<T: AsRef<str>> From<T> for Box<EvalAltResult> {
|
|||||||
impl EvalAltResult {
|
impl EvalAltResult {
|
||||||
/// Is this a pseudo error? A pseudo error is one that does not occur naturally.
|
/// Is this a pseudo error? A pseudo error is one that does not occur naturally.
|
||||||
///
|
///
|
||||||
/// [`LoopBreak`][EvalAltResult::LoopBreak] or [`Return`][EvalAltResult::Return] are pseudo errors.
|
/// [`LoopBreak`][EvalAltResult::LoopBreak] and [`Return`][EvalAltResult::Return] are pseudo errors.
|
||||||
pub fn is_pseudo_error(&self) -> bool {
|
pub fn is_pseudo_error(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::LoopBreak(_, _) | Self::Return(_, _) => true,
|
Self::LoopBreak(_, _) | Self::Return(_, _) => true,
|
||||||
@ -344,6 +344,73 @@ impl EvalAltResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get the [position][Position] of this error.
|
/// Get the [position][Position] of this error.
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
pub(crate) fn dump_fields(&self, map: &mut crate::Map) {
|
||||||
|
map.insert(
|
||||||
|
"error".into(),
|
||||||
|
crate::stdlib::format!("{:?}", self)
|
||||||
|
.split('(')
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Self::LoopBreak(_, _) | Self::Return(_, _) => (),
|
||||||
|
|
||||||
|
Self::ErrorSystem(_, _)
|
||||||
|
| Self::ErrorParsing(_, _)
|
||||||
|
| Self::ErrorUnboundThis(_)
|
||||||
|
| Self::ErrorFor(_)
|
||||||
|
| Self::ErrorInExpr(_)
|
||||||
|
| Self::ErrorArithmetic(_, _)
|
||||||
|
| Self::ErrorTooManyOperations(_)
|
||||||
|
| Self::ErrorTooManyModules(_)
|
||||||
|
| Self::ErrorStackOverflow(_)
|
||||||
|
| Self::ErrorRuntime(_, _) => (),
|
||||||
|
|
||||||
|
Self::ErrorFunctionNotFound(f, _) => {
|
||||||
|
map.insert("function".into(), f.into());
|
||||||
|
}
|
||||||
|
Self::ErrorInFunctionCall(f, s, _, _) => {
|
||||||
|
map.insert("function".into(), f.into());
|
||||||
|
map.insert("source".into(), s.into());
|
||||||
|
}
|
||||||
|
Self::ErrorInModule(m, _, _) => {
|
||||||
|
map.insert("module".into(), m.into());
|
||||||
|
}
|
||||||
|
Self::ErrorMismatchDataType(r, a, _) | Self::ErrorMismatchOutputType(r, a, _) => {
|
||||||
|
map.insert("requested".into(), r.into());
|
||||||
|
map.insert("actual".into(), a.into());
|
||||||
|
}
|
||||||
|
Self::ErrorArrayBounds(n, i, _) | Self::ErrorStringBounds(n, i, _) => {
|
||||||
|
map.insert("length".into(), (*n as INT).into());
|
||||||
|
map.insert("index".into(), (*i as INT).into());
|
||||||
|
}
|
||||||
|
Self::ErrorIndexingType(t, _) => {
|
||||||
|
map.insert("type".into(), t.into());
|
||||||
|
}
|
||||||
|
Self::ErrorVariableNotFound(v, _)
|
||||||
|
| Self::ErrorDataRace(v, _)
|
||||||
|
| Self::ErrorAssignmentToConstant(v, _) => {
|
||||||
|
map.insert("variable".into(), v.into());
|
||||||
|
}
|
||||||
|
Self::ErrorModuleNotFound(m, _) => {
|
||||||
|
map.insert("module".into(), m.into());
|
||||||
|
}
|
||||||
|
Self::ErrorDotExpr(p, _) => {
|
||||||
|
map.insert("property".into(), p.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::ErrorDataTooLarge(t, _) => {
|
||||||
|
map.insert("type".into(), t.into());
|
||||||
|
}
|
||||||
|
Self::ErrorTerminated(t, _) => {
|
||||||
|
map.insert("token".into(), t.clone());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/// Get the [position][Position] of this error.
|
||||||
pub fn position(&self) -> Position {
|
pub fn position(&self) -> Position {
|
||||||
match self {
|
match self {
|
||||||
Self::ErrorSystem(_, _) => Position::NONE,
|
Self::ErrorSystem(_, _) => Position::NONE,
|
||||||
@ -376,10 +443,13 @@ impl EvalAltResult {
|
|||||||
| Self::Return(_, pos) => *pos,
|
| Self::Return(_, pos) => *pos,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Clear the [position][Position] information of this error.
|
/// Remove the [position][Position] information from this error and return it.
|
||||||
pub fn clear_position(&mut self) -> &mut Self {
|
///
|
||||||
|
/// The [position][Position] of this error is set to [`NONE`][Position::NONE] afterwards.
|
||||||
|
pub fn take_position(&mut self) -> Position {
|
||||||
|
let pos = self.position();
|
||||||
self.set_position(Position::NONE);
|
self.set_position(Position::NONE);
|
||||||
self
|
pos
|
||||||
}
|
}
|
||||||
/// Override the [position][Position] of this error.
|
/// Override the [position][Position] of this error.
|
||||||
pub fn set_position(&mut self, new_position: Position) {
|
pub fn set_position(&mut self, new_position: Position) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! Implement serialization support of [`Dynamic`][crate::Dynamic] for [`serde`].
|
//! Implement serialization support of [`Dynamic`][crate::Dynamic] for [`serde`].
|
||||||
|
|
||||||
use crate::stdlib::{boxed::Box, fmt, string::ToString};
|
use crate::stdlib::{boxed::Box, fmt, string::ToString};
|
||||||
use crate::{Dynamic, EvalAltResult, Position};
|
use crate::{Dynamic, EvalAltResult, Position, RhaiResult};
|
||||||
use serde::ser::{
|
use serde::ser::{
|
||||||
Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple, SerializeTupleStruct,
|
Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple, SerializeTupleStruct,
|
||||||
};
|
};
|
||||||
@ -79,7 +79,7 @@ impl DynamicSerializer {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn to_dynamic<T: Serialize>(value: T) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn to_dynamic<T: Serialize>(value: T) -> RhaiResult {
|
||||||
let mut s = DynamicSerializer::new(Default::default());
|
let mut s = DynamicSerializer::new(Default::default());
|
||||||
value.serialize(&mut s)
|
value.serialize(&mut s)
|
||||||
}
|
}
|
||||||
@ -690,7 +690,7 @@ impl serde::ser::SerializeStructVariant for StructVariantSerializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
fn make_variant(variant: &'static str, value: Dynamic) -> Result<Dynamic, Box<EvalAltResult>> {
|
fn make_variant(variant: &'static str, value: Dynamic) -> RhaiResult {
|
||||||
let mut map = Map::with_capacity(1);
|
let mut map = Map::with_capacity(1);
|
||||||
map.insert(variant.into(), value);
|
map.insert(variant.into(), value);
|
||||||
Ok(map.into())
|
Ok(map.into())
|
||||||
|
@ -6,8 +6,7 @@ use crate::fn_native::SendSync;
|
|||||||
use crate::stdlib::{boxed::Box, format, string::ToString};
|
use crate::stdlib::{boxed::Box, format, string::ToString};
|
||||||
use crate::token::{is_valid_identifier, Token};
|
use crate::token::{is_valid_identifier, Token};
|
||||||
use crate::{
|
use crate::{
|
||||||
Dynamic, Engine, EvalAltResult, ImmutableString, LexError, ParseError, Position, Shared,
|
Engine, ImmutableString, LexError, ParseError, Position, RhaiResult, Shared, StaticVec,
|
||||||
StaticVec,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const MARKER_EXPR: &str = "$expr$";
|
pub const MARKER_EXPR: &str = "$expr$";
|
||||||
@ -16,12 +15,10 @@ pub const MARKER_IDENT: &str = "$ident$";
|
|||||||
|
|
||||||
/// A general expression evaluation trait object.
|
/// A general expression evaluation trait object.
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub type FnCustomSyntaxEval =
|
pub type FnCustomSyntaxEval = dyn Fn(&mut EvalContext, &[Expression]) -> RhaiResult;
|
||||||
dyn Fn(&mut EvalContext, &[Expression]) -> Result<Dynamic, Box<EvalAltResult>>;
|
|
||||||
/// A general expression evaluation trait object.
|
/// A general expression evaluation trait object.
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub type FnCustomSyntaxEval =
|
pub type FnCustomSyntaxEval = dyn Fn(&mut EvalContext, &[Expression]) -> RhaiResult + Send + Sync;
|
||||||
dyn Fn(&mut EvalContext, &[Expression]) -> Result<Dynamic, Box<EvalAltResult>> + Send + Sync;
|
|
||||||
|
|
||||||
/// A general expression parsing trait object.
|
/// A general expression parsing trait object.
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
@ -61,17 +58,14 @@ impl Expression<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EvalContext<'_, '_, '_, '_, '_, '_, '_, '_> {
|
impl EvalContext<'_, '_, '_, '_, '_, '_, '_> {
|
||||||
/// Evaluate an [expression tree][Expression].
|
/// Evaluate an [expression tree][Expression].
|
||||||
///
|
///
|
||||||
/// # WARNING - Low Level API
|
/// # WARNING - Low Level API
|
||||||
///
|
///
|
||||||
/// This function is very low level. It evaluates an expression from an [`AST`][crate::AST].
|
/// This function is very low level. It evaluates an expression from an [`AST`][crate::AST].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn eval_expression_tree(
|
pub fn eval_expression_tree(&mut self, expr: &Expression) -> RhaiResult {
|
||||||
&mut self,
|
|
||||||
expr: &Expression,
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
self.engine.eval_expr(
|
self.engine.eval_expr(
|
||||||
self.scope,
|
self.scope,
|
||||||
self.mods,
|
self.mods,
|
||||||
@ -111,11 +105,9 @@ impl Engine {
|
|||||||
/// current [`Scope`][crate::Scope] will be _popped_. Do not randomly remove variables.
|
/// current [`Scope`][crate::Scope] will be _popped_. Do not randomly remove variables.
|
||||||
pub fn register_custom_syntax<S: AsRef<str> + Into<ImmutableString>>(
|
pub fn register_custom_syntax<S: AsRef<str> + Into<ImmutableString>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
keywords: impl AsRef<[S]>,
|
keywords: &[S],
|
||||||
new_vars: isize,
|
new_vars: isize,
|
||||||
func: impl Fn(&mut EvalContext, &[Expression]) -> Result<Dynamic, Box<EvalAltResult>>
|
func: impl Fn(&mut EvalContext, &[Expression]) -> RhaiResult + SendSync + 'static,
|
||||||
+ SendSync
|
|
||||||
+ 'static,
|
|
||||||
) -> Result<&mut Self, ParseError> {
|
) -> Result<&mut Self, ParseError> {
|
||||||
let keywords = keywords.as_ref();
|
let keywords = keywords.as_ref();
|
||||||
|
|
||||||
@ -234,9 +226,7 @@ impl Engine {
|
|||||||
+ SendSync
|
+ SendSync
|
||||||
+ 'static,
|
+ 'static,
|
||||||
new_vars: isize,
|
new_vars: isize,
|
||||||
func: impl Fn(&mut EvalContext, &[Expression]) -> Result<Dynamic, Box<EvalAltResult>>
|
func: impl Fn(&mut EvalContext, &[Expression]) -> RhaiResult + SendSync + 'static,
|
||||||
+ SendSync
|
|
||||||
+ 'static,
|
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
let syntax = CustomSyntax {
|
let syntax = CustomSyntax {
|
||||||
parse: Box::new(parse),
|
parse: Box::new(parse),
|
||||||
|
58
src/token.rs
58
src/token.rs
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::engine::{
|
use crate::engine::{
|
||||||
KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY,
|
KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY,
|
||||||
KEYWORD_PRINT, KEYWORD_THIS, KEYWORD_TYPE_OF,
|
KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_THIS, KEYWORD_TYPE_OF,
|
||||||
};
|
};
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
@ -21,13 +21,16 @@ use crate::ast::FloatWrapper;
|
|||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
use rust_decimal::Decimal;
|
use rust_decimal::Decimal;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
use crate::engine::KEYWORD_IS_DEF_FN;
|
||||||
|
|
||||||
type LERR = LexError;
|
type LERR = LexError;
|
||||||
|
|
||||||
/// Separator character for numbers.
|
/// Separator character for numbers.
|
||||||
const NUM_SEP: char = '_';
|
const NUM_SEP: char = '_';
|
||||||
|
|
||||||
/// A stream of tokens.
|
/// A stream of tokens.
|
||||||
pub type TokenStream<'a, 't> = Peekable<TokenIterator<'a, 't>>;
|
pub type TokenStream<'a> = Peekable<TokenIterator<'a>>;
|
||||||
|
|
||||||
/// A location (line number + character position) in the input script.
|
/// A location (line number + character position) in the input script.
|
||||||
///
|
///
|
||||||
@ -579,7 +582,12 @@ impl Token {
|
|||||||
| "async" | "await" | "yield" => Reserved(syntax.into()),
|
| "async" | "await" | "yield" => Reserved(syntax.into()),
|
||||||
|
|
||||||
KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR
|
KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR
|
||||||
| KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_THIS => Reserved(syntax.into()),
|
| KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_THIS | KEYWORD_IS_DEF_VAR => {
|
||||||
|
Reserved(syntax.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
KEYWORD_IS_DEF_FN => Reserved(syntax.into()),
|
||||||
|
|
||||||
_ => return None,
|
_ => return None,
|
||||||
})
|
})
|
||||||
@ -759,7 +767,6 @@ impl Token {
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub(crate) fn into_function_name_for_override(self) -> Result<String, Self> {
|
pub(crate) fn into_function_name_for_override(self) -> Result<String, Self> {
|
||||||
match self {
|
match self {
|
||||||
Self::Reserved(s) if can_override_keyword(&s) => Ok(s),
|
|
||||||
Self::Custom(s) | Self::Identifier(s) if is_valid_identifier(s.chars()) => Ok(s),
|
Self::Custom(s) | Self::Identifier(s) if is_valid_identifier(s.chars()) => Ok(s),
|
||||||
_ => Err(self),
|
_ => Err(self),
|
||||||
}
|
}
|
||||||
@ -1625,17 +1632,11 @@ fn get_identifier(
|
|||||||
pub fn is_keyword_function(name: &str) -> bool {
|
pub fn is_keyword_function(name: &str) -> bool {
|
||||||
match name {
|
match name {
|
||||||
KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR
|
KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR
|
||||||
| KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY => true,
|
| KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_IS_DEF_VAR => true,
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Can this keyword be overridden as a function?
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[inline(always)]
|
KEYWORD_IS_DEF_FN => true,
|
||||||
pub fn can_override_keyword(name: &str) -> bool {
|
|
||||||
match name {
|
|
||||||
KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR => true,
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1740,9 +1741,9 @@ impl InputStream for MultiInputsStream<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// An iterator on a [`Token`] stream.
|
/// An iterator on a [`Token`] stream.
|
||||||
pub struct TokenIterator<'a, 'e> {
|
pub struct TokenIterator<'a> {
|
||||||
/// Reference to the scripting `Engine`.
|
/// Reference to the scripting `Engine`.
|
||||||
engine: &'e Engine,
|
engine: &'a Engine,
|
||||||
/// Current state.
|
/// Current state.
|
||||||
state: TokenizeState,
|
state: TokenizeState,
|
||||||
/// Current position.
|
/// Current position.
|
||||||
@ -1753,7 +1754,7 @@ pub struct TokenIterator<'a, 'e> {
|
|||||||
map: Option<fn(Token) -> Token>,
|
map: Option<fn(Token) -> Token>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for TokenIterator<'a, '_> {
|
impl<'a> Iterator for TokenIterator<'a> {
|
||||||
type Item = (Token, Position);
|
type Item = (Token, Position);
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
@ -1836,30 +1837,31 @@ impl<'a> Iterator for TokenIterator<'a, '_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
/// Tokenize an input text stream.
|
/// _(INTERNALS)_ Tokenize an input text stream.
|
||||||
|
/// Exported under the `internals` feature only.
|
||||||
|
#[cfg(feature = "internals")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn lex<'a, 'e>(
|
pub fn lex<'a>(&'a self, input: impl IntoIterator<Item = &'a &'a str>) -> TokenIterator<'a> {
|
||||||
&'e self,
|
|
||||||
input: impl IntoIterator<Item = &'a &'a str>,
|
|
||||||
) -> TokenIterator<'a, 'e> {
|
|
||||||
self.lex_raw(input, None)
|
self.lex_raw(input, None)
|
||||||
}
|
}
|
||||||
/// Tokenize an input text stream with a mapping function.
|
/// _(INTERNALS)_ Tokenize an input text stream with a mapping function.
|
||||||
|
/// Exported under the `internals` feature only.
|
||||||
|
#[cfg(feature = "internals")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn lex_with_map<'a, 'e>(
|
pub fn lex_with_map<'a>(
|
||||||
&'e self,
|
&'a self,
|
||||||
input: impl IntoIterator<Item = &'a &'a str>,
|
input: impl IntoIterator<Item = &'a &'a str>,
|
||||||
map: fn(Token) -> Token,
|
map: fn(Token) -> Token,
|
||||||
) -> TokenIterator<'a, 'e> {
|
) -> TokenIterator<'a> {
|
||||||
self.lex_raw(input, Some(map))
|
self.lex_raw(input, Some(map))
|
||||||
}
|
}
|
||||||
/// Tokenize an input text stream with an optional mapping function.
|
/// Tokenize an input text stream with an optional mapping function.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn lex_raw<'a, 'e>(
|
pub(crate) fn lex_raw<'a>(
|
||||||
&'e self,
|
&'a self,
|
||||||
input: impl IntoIterator<Item = &'a &'a str>,
|
input: impl IntoIterator<Item = &'a &'a str>,
|
||||||
map: Option<fn(Token) -> Token>,
|
map: Option<fn(Token) -> Token>,
|
||||||
) -> TokenIterator<'a, 'e> {
|
) -> TokenIterator<'a> {
|
||||||
TokenIterator {
|
TokenIterator {
|
||||||
engine: self,
|
engine: self,
|
||||||
state: TokenizeState {
|
state: TokenizeState {
|
||||||
|
@ -4,19 +4,134 @@ use rhai::{Engine, EvalAltResult, INT};
|
|||||||
fn test_binary_ops() -> Result<(), Box<EvalAltResult>> {
|
fn test_binary_ops() -> Result<(), Box<EvalAltResult>> {
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
assert_eq!(engine.eval::<INT>("10 + 4")?, 14);
|
||||||
|
assert_eq!(engine.eval::<INT>("10 - 4")?, 6);
|
||||||
|
assert_eq!(engine.eval::<INT>("10 * 4")?, 40);
|
||||||
|
assert_eq!(engine.eval::<INT>("10 / 4")?, 2);
|
||||||
assert_eq!(engine.eval::<INT>("10 % 4")?, 2);
|
assert_eq!(engine.eval::<INT>("10 % 4")?, 2);
|
||||||
|
assert_eq!(engine.eval::<INT>("10 ** 4")?, 10000);
|
||||||
assert_eq!(engine.eval::<INT>("10 << 4")?, 160);
|
assert_eq!(engine.eval::<INT>("10 << 4")?, 160);
|
||||||
assert_eq!(engine.eval::<INT>("10 >> 4")?, 0);
|
assert_eq!(engine.eval::<INT>("10 >> 4")?, 0);
|
||||||
assert_eq!(engine.eval::<INT>("10 & 4")?, 0);
|
assert_eq!(engine.eval::<INT>("10 & 4")?, 0);
|
||||||
assert_eq!(engine.eval::<INT>("10 | 4")?, 14);
|
assert_eq!(engine.eval::<INT>("10 | 4")?, 14);
|
||||||
assert_eq!(engine.eval::<INT>("10 ^ 4")?, 14);
|
assert_eq!(engine.eval::<INT>("10 ^ 4")?, 14);
|
||||||
|
|
||||||
assert_eq!(engine.eval::<bool>("42 == 42")?, true);
|
assert!(engine.eval::<bool>("42 == 42")?);
|
||||||
assert_eq!(engine.eval::<bool>("42 > 42")?, false);
|
assert!(!engine.eval::<bool>("42 != 42")?);
|
||||||
|
assert!(!engine.eval::<bool>("42 > 42")?);
|
||||||
|
assert!(engine.eval::<bool>("42 >= 42")?);
|
||||||
|
assert!(!engine.eval::<bool>("42 < 42")?);
|
||||||
|
assert!(engine.eval::<bool>("42 <= 42")?);
|
||||||
|
|
||||||
|
assert_eq!(engine.eval::<INT>("let x = 10; x += 4; x")?, 14);
|
||||||
|
assert_eq!(engine.eval::<INT>("let x = 10; x -= 4; x")?, 6);
|
||||||
|
assert_eq!(engine.eval::<INT>("let x = 10; x *= 4; x")?, 40);
|
||||||
|
assert_eq!(engine.eval::<INT>("let x = 10; x /= 4; x")?, 2);
|
||||||
|
assert_eq!(engine.eval::<INT>("let x = 10; x %= 4; x")?, 2);
|
||||||
|
assert_eq!(engine.eval::<INT>("let x = 10; x **= 4; x")?, 10000);
|
||||||
|
assert_eq!(engine.eval::<INT>("let x = 10; x <<= 4; x")?, 160);
|
||||||
|
assert_eq!(engine.eval::<INT>("let x = 10; x >>= 4; x")?, 0);
|
||||||
|
assert_eq!(engine.eval::<INT>("let x = 10; x &= 4; x")?, 0);
|
||||||
|
assert_eq!(engine.eval::<INT>("let x = 10; x |= 4; x")?, 14);
|
||||||
|
assert_eq!(engine.eval::<INT>("let x = 10; x ^= 4; x")?, 14);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
{
|
||||||
|
use rhai::FLOAT;
|
||||||
|
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("10.0 + 4.0")?, 14.0);
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("10.0 - 4.0")?, 6.0);
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("10.0 * 4.0")?, 40.0);
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("10.0 / 4.0")?, 2.5);
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("10.0 % 4.0")?, 2.0);
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("10.0 ** 4.0")?, 10000.0);
|
||||||
|
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("10.0 + 4")?, 14.0);
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("10.0 - 4")?, 6.0);
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("10.0 * 4")?, 40.0);
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("10.0 / 4")?, 2.5);
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("10.0 % 4")?, 2.0);
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("10.0 ** 4")?, 10000.0);
|
||||||
|
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("10 + 4.0")?, 14.0);
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("10 - 4.0")?, 6.0);
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("10 * 4.0")?, 40.0);
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("10 / 4.0")?, 2.5);
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("10 % 4.0")?, 2.0);
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("10 ** 4.0")?, 10000.0);
|
||||||
|
|
||||||
|
assert!(engine.eval::<bool>("42 == 42.0")?);
|
||||||
|
assert!(!engine.eval::<bool>("42 != 42.0")?);
|
||||||
|
assert!(!engine.eval::<bool>("42 > 42.0")?);
|
||||||
|
assert!(engine.eval::<bool>("42 >= 42.0")?);
|
||||||
|
assert!(!engine.eval::<bool>("42 < 42.0")?);
|
||||||
|
assert!(engine.eval::<bool>("42 <= 42.0")?);
|
||||||
|
|
||||||
|
assert!(engine.eval::<bool>("42.0 == 42")?);
|
||||||
|
assert!(!engine.eval::<bool>("42.0 != 42")?);
|
||||||
|
assert!(!engine.eval::<bool>("42.0 > 42")?);
|
||||||
|
assert!(engine.eval::<bool>("42.0 >= 42")?);
|
||||||
|
assert!(!engine.eval::<bool>("42.0 < 42")?);
|
||||||
|
assert!(engine.eval::<bool>("42.0 <= 42")?);
|
||||||
|
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("let x = 10.0; x += 4.0; x")?, 14.0);
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("let x = 10.0; x -= 4.0; x")?, 6.0);
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("let x = 10.0; x *= 4.0; x")?, 40.0);
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("let x = 10.0; x /= 4.0; x")?, 2.5);
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("let x = 10.0; x %= 4.0; x")?, 2.0);
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("let x = 10.0; x **= 4.0; x")?, 10000.0);
|
||||||
|
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("let x = 10.0; x += 4; x")?, 14.0);
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("let x = 10.0; x -= 4; x")?, 6.0);
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("let x = 10.0; x *= 4; x")?, 40.0);
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("let x = 10.0; x /= 4; x")?, 2.5);
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("let x = 10.0; x %= 4; x")?, 2.0);
|
||||||
|
assert_eq!(engine.eval::<FLOAT>("let x = 10.0; x **= 4; x")?, 10000.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<String>(r#""hello" + ", world""#)?,
|
||||||
|
"hello, world"
|
||||||
|
);
|
||||||
|
assert_eq!(engine.eval::<String>(r#""hello" + '!'"#)?, "hello!");
|
||||||
|
assert_eq!(engine.eval::<String>(r#""hello" - "el""#)?, "hlo");
|
||||||
|
assert_eq!(engine.eval::<String>(r#""hello" - 'l'"#)?, "heo");
|
||||||
|
|
||||||
|
assert!(!engine.eval::<bool>(r#""a" == "x""#)?);
|
||||||
|
assert!(engine.eval::<bool>(r#""a" != "x""#)?);
|
||||||
|
assert!(!engine.eval::<bool>(r#""a" > "x""#)?);
|
||||||
|
assert!(!engine.eval::<bool>(r#""a" >= "x""#)?);
|
||||||
|
assert!(engine.eval::<bool>(r#""a" < "x""#)?);
|
||||||
|
assert!(engine.eval::<bool>(r#""a" <= "x""#)?);
|
||||||
|
|
||||||
|
assert!(engine.eval::<bool>(r#""x" == 'x'"#)?);
|
||||||
|
assert!(!engine.eval::<bool>(r#""x" != 'x'"#)?);
|
||||||
|
assert!(!engine.eval::<bool>(r#""x" > 'x'"#)?);
|
||||||
|
assert!(engine.eval::<bool>(r#""x" >= 'x'"#)?);
|
||||||
|
assert!(!engine.eval::<bool>(r#""x" < 'x'"#)?);
|
||||||
|
assert!(engine.eval::<bool>(r#""x" <= 'x'"#)?);
|
||||||
|
|
||||||
|
assert!(engine.eval::<bool>(r#"'x' == "x""#)?);
|
||||||
|
assert!(!engine.eval::<bool>(r#"'x' != "x""#)?);
|
||||||
|
assert!(!engine.eval::<bool>(r#"'x' > "x""#)?);
|
||||||
|
assert!(engine.eval::<bool>(r#"'x' >= "x""#)?);
|
||||||
|
assert!(!engine.eval::<bool>(r#"'x' < "x""#)?);
|
||||||
|
assert!(engine.eval::<bool>(r#"'x' <= "x""#)?);
|
||||||
|
|
||||||
// Incompatible types compare to false
|
// Incompatible types compare to false
|
||||||
assert_eq!(engine.eval::<bool>("true == 42")?, false);
|
assert!(!engine.eval::<bool>("true == 42")?);
|
||||||
assert_eq!(engine.eval::<bool>(r#""42" == 42"#)?, false);
|
assert!(engine.eval::<bool>("true != 42")?);
|
||||||
|
assert!(!engine.eval::<bool>("true > 42")?);
|
||||||
|
assert!(!engine.eval::<bool>("true >= 42")?);
|
||||||
|
assert!(!engine.eval::<bool>("true < 42")?);
|
||||||
|
assert!(!engine.eval::<bool>("true <= 42")?);
|
||||||
|
|
||||||
|
assert!(!engine.eval::<bool>(r#""42" == 42"#)?);
|
||||||
|
assert!(engine.eval::<bool>(r#""42" != 42"#)?);
|
||||||
|
assert!(!engine.eval::<bool>(r#""42" > 42"#)?);
|
||||||
|
assert!(!engine.eval::<bool>(r#""42" >= 42"#)?);
|
||||||
|
assert!(!engine.eval::<bool>(r#""42" < 42"#)?);
|
||||||
|
assert!(!engine.eval::<bool>(r#""42" <= 42"#)?);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -105,11 +105,8 @@ fn test_call_fn_private() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
let ast = engine.compile("private fn add(x, n, ) { x + n }")?;
|
let ast = engine.compile("private fn add(x, n, ) { x + n }")?;
|
||||||
|
|
||||||
assert!(matches!(
|
let r: INT = engine.call_fn(&mut scope, &ast, "add", (40 as INT, 2 as INT))?;
|
||||||
*(engine.call_fn(&mut scope, &ast, "add", (40 as INT, 2 as INT)) as Result<INT, Box<EvalAltResult>>)
|
assert_eq!(r, 42);
|
||||||
.expect_err("should error"),
|
|
||||||
EvalAltResult::ErrorFunctionNotFound(fn_name, _) if fn_name == "add"
|
|
||||||
));
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -165,8 +162,8 @@ fn test_fn_ptr_raw() -> Result<(), Box<EvalAltResult>> {
|
|||||||
42
|
42
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(matches!(
|
assert_eq!(
|
||||||
*engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
r#"
|
r#"
|
||||||
private fn foo(x) { this += x; }
|
private fn foo(x) { this += x; }
|
||||||
|
|
||||||
@ -174,9 +171,9 @@ fn test_fn_ptr_raw() -> Result<(), Box<EvalAltResult>> {
|
|||||||
x.bar(Fn("foo"), 1);
|
x.bar(Fn("foo"), 1);
|
||||||
x
|
x
|
||||||
"#
|
"#
|
||||||
).expect_err("should error"),
|
)?,
|
||||||
EvalAltResult::ErrorFunctionNotFound(x, _) if x.starts_with("foo (")
|
42
|
||||||
));
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
@ -210,16 +207,13 @@ fn test_anonymous_fn() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
assert_eq!(calc_func(42, "hello".to_string(), 9)?, 423);
|
assert_eq!(calc_func(42, "hello".to_string(), 9)?, 423);
|
||||||
|
|
||||||
let calc_func = Func::<(INT, INT, INT), INT>::create_from_script(
|
let calc_func = Func::<(INT, String, INT), INT>::create_from_script(
|
||||||
Engine::new(),
|
Engine::new(),
|
||||||
"private fn calc(x, y, z) { (x + y) * z }",
|
"private fn calc(x, y, z) { (x + len(y)) * z }",
|
||||||
"calc",
|
"calc",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
assert!(matches!(
|
assert_eq!(calc_func(42, "hello".to_string(), 9)?, 423);
|
||||||
*calc_func(42, 123, 9).expect_err("should error"),
|
|
||||||
EvalAltResult::ErrorFunctionNotFound(fn_name, _) if fn_name == "calc"
|
|
||||||
));
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -102,6 +102,14 @@ fn test_closures() -> Result<(), Box<EvalAltResult>> {
|
|||||||
"#
|
"#
|
||||||
)?);
|
)?);
|
||||||
|
|
||||||
|
assert!(engine.eval::<bool>(
|
||||||
|
r#"
|
||||||
|
let a = 41;
|
||||||
|
let foo = |x| { a += x };
|
||||||
|
is_shared(a)
|
||||||
|
"#
|
||||||
|
)?);
|
||||||
|
|
||||||
engine.register_fn("plus_one", |x: INT| x + 1);
|
engine.register_fn("plus_one", |x: INT| x + 1);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -37,32 +37,6 @@ fn test_constant_scope() -> Result<(), Box<EvalAltResult>> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_var_is_def() -> Result<(), Box<EvalAltResult>> {
|
|
||||||
let engine = Engine::new();
|
|
||||||
|
|
||||||
assert!(engine.eval::<bool>(
|
|
||||||
r#"
|
|
||||||
let x = 42;
|
|
||||||
is_def_var("x")
|
|
||||||
"#
|
|
||||||
)?);
|
|
||||||
assert!(!engine.eval::<bool>(
|
|
||||||
r#"
|
|
||||||
let x = 42;
|
|
||||||
is_def_var("y")
|
|
||||||
"#
|
|
||||||
)?);
|
|
||||||
assert!(engine.eval::<bool>(
|
|
||||||
r#"
|
|
||||||
const x = 42;
|
|
||||||
is_def_var("x")
|
|
||||||
"#
|
|
||||||
)?);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_constant_mut() -> Result<(), Box<EvalAltResult>> {
|
fn test_constant_mut() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use rhai::{Engine, EvalAltResult, LexError, ParseErrorType, RegisterFn, Scope, INT};
|
use rhai::{Engine, EvalAltResult, LexError, ParseErrorType, Scope, INT};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_eval() -> Result<(), Box<EvalAltResult>> {
|
fn test_eval() -> Result<(), Box<EvalAltResult>> {
|
||||||
@ -80,32 +80,6 @@ fn test_eval_function() -> Result<(), Box<EvalAltResult>> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
fn test_eval_override() -> Result<(), Box<EvalAltResult>> {
|
|
||||||
let engine = Engine::new();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
engine.eval::<String>(
|
|
||||||
r#"
|
|
||||||
fn eval(x) { x } // reflect the script back
|
|
||||||
|
|
||||||
eval("40 + 2")
|
|
||||||
"#
|
|
||||||
)?,
|
|
||||||
"40 + 2"
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut engine = Engine::new();
|
|
||||||
|
|
||||||
// Reflect the script back
|
|
||||||
engine.register_fn("eval", |script: &str| script.to_string());
|
|
||||||
|
|
||||||
assert_eq!(engine.eval::<String>(r#"eval("40 + 2")"#)?, "40 + 2");
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_eval_disabled() -> Result<(), Box<EvalAltResult>> {
|
fn test_eval_disabled() -> Result<(), Box<EvalAltResult>> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
@ -24,7 +24,10 @@ fn test_module_sub_module() -> Result<(), Box<EvalAltResult>> {
|
|||||||
sub_module2.set_var("answer", 41 as INT);
|
sub_module2.set_var("answer", 41 as INT);
|
||||||
|
|
||||||
let hash_inc = sub_module2.set_fn_1_mut("inc", FnNamespace::Internal, |x: &mut INT| Ok(*x + 1));
|
let hash_inc = sub_module2.set_fn_1_mut("inc", FnNamespace::Internal, |x: &mut INT| Ok(*x + 1));
|
||||||
|
assert!(!sub_module2.has_namespace(FnNamespace::Global, true));
|
||||||
|
|
||||||
sub_module2.set_fn_1_mut("super_inc", FnNamespace::Global, |x: &mut INT| Ok(*x + 1));
|
sub_module2.set_fn_1_mut("super_inc", FnNamespace::Global, |x: &mut INT| Ok(*x + 1));
|
||||||
|
assert!(sub_module2.has_namespace(FnNamespace::Global, true));
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
sub_module2.set_getter_fn("doubled", |x: &mut INT| Ok(*x * 2));
|
sub_module2.set_getter_fn("doubled", |x: &mut INT| Ok(*x * 2));
|
||||||
@ -33,6 +36,9 @@ fn test_module_sub_module() -> Result<(), Box<EvalAltResult>> {
|
|||||||
module.set_sub_module("life", sub_module);
|
module.set_sub_module("life", sub_module);
|
||||||
module.set_var("MYSTIC_NUMBER", Dynamic::from(42 as INT));
|
module.set_var("MYSTIC_NUMBER", Dynamic::from(42 as INT));
|
||||||
|
|
||||||
|
assert!(module.has_namespace(FnNamespace::Global, true));
|
||||||
|
assert!(!module.has_namespace(FnNamespace::Global, false));
|
||||||
|
|
||||||
assert!(module.contains_sub_module("life"));
|
assert!(module.contains_sub_module("life"));
|
||||||
let m = module.get_sub_module("life").unwrap();
|
let m = module.get_sub_module("life").unwrap();
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use rhai::{Dynamic, Engine, EvalAltResult, ImmutableString, RegisterFn, Scope, INT};
|
use rhai::{Engine, EvalAltResult, ImmutableString, RegisterFn, Scope, INT};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_string() -> Result<(), Box<EvalAltResult>> {
|
fn test_string() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
@ -46,6 +46,9 @@ fn test_type_of() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
assert_eq!(engine.eval::<String>(r#"type_of("hello")"#)?, "string");
|
assert_eq!(engine.eval::<String>(r#"type_of("hello")"#)?, "string");
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
assert_eq!(engine.eval::<String>(r#""hello".type_of()"#)?, "string");
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
assert_eq!(engine.eval::<String>("let x = 123; type_of(x)")?, "i64");
|
assert_eq!(engine.eval::<String>("let x = 123; type_of(x)")?, "i64");
|
||||||
|
|
||||||
|
@ -19,6 +19,32 @@ fn test_var_scope() -> Result<(), Box<EvalAltResult>> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_var_is_def() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
assert!(engine.eval::<bool>(
|
||||||
|
r#"
|
||||||
|
let x = 42;
|
||||||
|
is_def_var("x")
|
||||||
|
"#
|
||||||
|
)?);
|
||||||
|
assert!(!engine.eval::<bool>(
|
||||||
|
r#"
|
||||||
|
let x = 42;
|
||||||
|
is_def_var("y")
|
||||||
|
"#
|
||||||
|
)?);
|
||||||
|
assert!(engine.eval::<bool>(
|
||||||
|
r#"
|
||||||
|
const x = 42;
|
||||||
|
is_def_var("x")
|
||||||
|
"#
|
||||||
|
)?);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_scope_eval() -> Result<(), Box<EvalAltResult>> {
|
fn test_scope_eval() -> Result<(), Box<EvalAltResult>> {
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
Loading…
Reference in New Issue
Block a user