commit
aaa5254c29
17
CHANGELOG.md
17
CHANGELOG.md
@ -4,6 +4,19 @@ Rhai Release Notes
|
|||||||
Version 1.10.0
|
Version 1.10.0
|
||||||
==============
|
==============
|
||||||
|
|
||||||
|
This version introduces _Fast Operators_ mode, which is turned on by default but can be disabled via
|
||||||
|
a new options API: `Engine::set_fast_operators`.
|
||||||
|
|
||||||
|
_Fast Operators_ mode assumes that none of Rhai's built-in operators for standard data types are
|
||||||
|
overloaded by user-registered functions. In the vast majority of cases this should be so (really,
|
||||||
|
who overloads the `+` operator for integers anyway?).
|
||||||
|
|
||||||
|
This assumption allows the `Engine` to avoid checking for overloads for every single operator call.
|
||||||
|
This usually results in substantial speed improvements, especially for expressions.
|
||||||
|
|
||||||
|
Minimum Rust Version
|
||||||
|
--------------------
|
||||||
|
|
||||||
The minimum Rust version is now `1.61.0` in order to use some `const` generics.
|
The minimum Rust version is now `1.61.0` in order to use some `const` generics.
|
||||||
|
|
||||||
Bug fixes
|
Bug fixes
|
||||||
@ -21,6 +34,10 @@ Deprecated API
|
|||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
### Fast operators
|
||||||
|
|
||||||
|
* A new option `Engine::fast_operators` is introduced (default to `true`) to enable/disable _Fast Operators_ mode.
|
||||||
|
|
||||||
### Fallible type iterators
|
### Fallible type iterators
|
||||||
|
|
||||||
* For very special needs, the ability to register fallible type iterators is added.
|
* For very special needs, the ability to register fallible type iterators is added.
|
||||||
|
@ -235,6 +235,7 @@ fn multiple_fn_rename_test() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
let m = rhai::exported_module!(crate::multiple_fn_rename::my_adds);
|
let m = rhai::exported_module!(crate::multiple_fn_rename::my_adds);
|
||||||
engine.register_global_module(m.into());
|
engine.register_global_module(m.into());
|
||||||
|
engine.set_fast_operators(false);
|
||||||
|
|
||||||
let output_array = engine.eval::<Array>(
|
let output_array = engine.eval::<Array>(
|
||||||
"
|
"
|
||||||
|
6480
scripts/all_in_one.d.rhai
Normal file
6480
scripts/all_in_one.d.rhai
Normal file
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
let now = timestamp();
|
let now = timestamp();
|
||||||
|
|
||||||
const MAX_NUMBER_TO_CHECK = 1_000_000; // 9592 primes <= 100000
|
const ANSWER = 78_498;
|
||||||
|
const MAX_NUMBER_TO_CHECK = 1_000_000;
|
||||||
|
|
||||||
let prime_mask = [];
|
let prime_mask = [];
|
||||||
prime_mask.pad(MAX_NUMBER_TO_CHECK + 1, true);
|
prime_mask.pad(MAX_NUMBER_TO_CHECK + 1, true);
|
||||||
@ -27,6 +28,6 @@ for p in 2..=MAX_NUMBER_TO_CHECK {
|
|||||||
print(`Total ${total_primes_found} primes <= ${MAX_NUMBER_TO_CHECK}`);
|
print(`Total ${total_primes_found} primes <= ${MAX_NUMBER_TO_CHECK}`);
|
||||||
print(`Run time = ${now.elapsed} seconds.`);
|
print(`Run time = ${now.elapsed} seconds.`);
|
||||||
|
|
||||||
if total_primes_found != 78_498 {
|
if total_primes_found != ANSWER {
|
||||||
print("The answer is WRONG! Should be 78,498!");
|
print(`The answer is WRONG! Should be ${ANSWER}!`);
|
||||||
}
|
}
|
||||||
|
@ -7,34 +7,43 @@ use std::prelude::v1::*;
|
|||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
/// Bit-flags containing all language options for the [`Engine`].
|
/// Bit-flags containing all language options for the [`Engine`].
|
||||||
pub struct LangOptions: u8 {
|
pub struct LangOptions: u16 {
|
||||||
/// Is `if`-expression allowed?
|
/// Is `if`-expression allowed?
|
||||||
const IF_EXPR = 0b_0000_0001;
|
const IF_EXPR = 0b_0000_0000_0001;
|
||||||
/// Is `switch` expression allowed?
|
/// Is `switch` expression allowed?
|
||||||
const SWITCH_EXPR = 0b_0000_0010;
|
const SWITCH_EXPR = 0b_0000_0000_0010;
|
||||||
/// Is statement-expression allowed?
|
/// Is statement-expression allowed?
|
||||||
const STMT_EXPR = 0b_0000_0100;
|
const STMT_EXPR = 0b_0000_0000_0100;
|
||||||
/// Is anonymous function allowed?
|
/// Is anonymous function allowed?
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
const ANON_FN = 0b_0000_1000;
|
const ANON_FN = 0b_0000_0000_1000;
|
||||||
/// Is looping allowed?
|
/// Is looping allowed?
|
||||||
const LOOPING = 0b_0001_0000;
|
const LOOPING = 0b_0000_0001_0000;
|
||||||
/// Is variables shadowing allowed?
|
/// Is variables shadowing allowed?
|
||||||
const SHADOW = 0b_0010_0000;
|
const SHADOW = 0b_0000_0010_0000;
|
||||||
/// Strict variables mode?
|
/// Strict variables mode?
|
||||||
const STRICT_VAR = 0b_0100_0000;
|
const STRICT_VAR = 0b_0000_0100_0000;
|
||||||
/// Raise error if an object map property does not exist?
|
/// Raise error if an object map property does not exist?
|
||||||
/// Returns `()` if `false`.
|
/// Returns `()` if `false`.
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
const FAIL_ON_INVALID_MAP_PROPERTY = 0b_1000_0000;
|
const FAIL_ON_INVALID_MAP_PROPERTY = 0b_0000_1000_0000;
|
||||||
|
/// Fast operators mode?
|
||||||
|
const FAST_OPS = 0b_0001_0000_0000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LangOptions {
|
impl LangOptions {
|
||||||
/// Create a new [`LangOptions`] with default values.
|
/// Create a new [`LangOptions`] with default values.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::IF_EXPR | Self::SWITCH_EXPR | Self::STMT_EXPR | Self::LOOPING | Self::SHADOW | {
|
Self::IF_EXPR
|
||||||
|
| Self::SWITCH_EXPR
|
||||||
|
| Self::STMT_EXPR
|
||||||
|
| Self::LOOPING
|
||||||
|
| Self::SHADOW
|
||||||
|
| Self::FAST_OPS
|
||||||
|
| {
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
{
|
{
|
||||||
Self::ANON_FN
|
Self::ANON_FN
|
||||||
@ -158,4 +167,16 @@ impl Engine {
|
|||||||
self.options
|
self.options
|
||||||
.set(LangOptions::FAIL_ON_INVALID_MAP_PROPERTY, enable);
|
.set(LangOptions::FAIL_ON_INVALID_MAP_PROPERTY, enable);
|
||||||
}
|
}
|
||||||
|
/// Is fast operators mode enabled?
|
||||||
|
/// Default is `false`.
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub const fn fast_operators(&self) -> bool {
|
||||||
|
self.options.contains(LangOptions::FAST_OPS)
|
||||||
|
}
|
||||||
|
/// Set whether fast operators mode is enabled.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn set_fast_operators(&mut self, enable: bool) {
|
||||||
|
self.options.set(LangOptions::FAST_OPS, enable);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,6 +190,8 @@ pub struct FnCallExpr {
|
|||||||
pub args: StaticVec<Expr>,
|
pub args: StaticVec<Expr>,
|
||||||
/// Does this function call capture the parent scope?
|
/// Does this function call capture the parent scope?
|
||||||
pub capture_parent_scope: bool,
|
pub capture_parent_scope: bool,
|
||||||
|
/// Is this function call a native operator?
|
||||||
|
pub is_native_operator: bool,
|
||||||
/// [Position] of the function name.
|
/// [Position] of the function name.
|
||||||
pub pos: Position,
|
pub pos: Position,
|
||||||
}
|
}
|
||||||
@ -204,6 +206,9 @@ impl fmt::Debug for FnCallExpr {
|
|||||||
if self.capture_parent_scope {
|
if self.capture_parent_scope {
|
||||||
ff.field("capture_parent_scope", &self.capture_parent_scope);
|
ff.field("capture_parent_scope", &self.capture_parent_scope);
|
||||||
}
|
}
|
||||||
|
if self.is_native_operator {
|
||||||
|
ff.field("is_native_operator", &self.is_native_operator);
|
||||||
|
}
|
||||||
ff.field("hash", &self.hashes)
|
ff.field("hash", &self.hashes)
|
||||||
.field("name", &self.name)
|
.field("name", &self.name)
|
||||||
.field("args", &self.args);
|
.field("args", &self.args);
|
||||||
@ -662,6 +667,7 @@ impl Expr {
|
|||||||
hashes: calc_fn_hash(f.fn_name(), 1).into(),
|
hashes: calc_fn_hash(f.fn_name(), 1).into(),
|
||||||
args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(),
|
args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(),
|
||||||
capture_parent_scope: false,
|
capture_parent_scope: false,
|
||||||
|
is_native_operator: false,
|
||||||
pos,
|
pos,
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
|
@ -3,11 +3,16 @@
|
|||||||
use super::{Caches, EvalContext, GlobalRuntimeState, Target};
|
use super::{Caches, EvalContext, GlobalRuntimeState, Target};
|
||||||
use crate::ast::{Expr, FnCallExpr, OpAssignment};
|
use crate::ast::{Expr, FnCallExpr, OpAssignment};
|
||||||
use crate::engine::{KEYWORD_THIS, OP_CONCAT};
|
use crate::engine::{KEYWORD_THIS, OP_CONCAT};
|
||||||
|
use crate::eval::FnResolutionCacheEntry;
|
||||||
|
use crate::func::{
|
||||||
|
calc_fn_params_hash, combine_hashes, gen_fn_call_signature, get_builtin_binary_op_fn,
|
||||||
|
CallableFunction, FnAny,
|
||||||
|
};
|
||||||
use crate::types::dynamic::AccessMode;
|
use crate::types::dynamic::AccessMode;
|
||||||
use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR};
|
use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR};
|
||||||
use std::num::NonZeroUsize;
|
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
use std::{collections::btree_map::Entry, num::NonZeroUsize};
|
||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
/// Search for a module within an imports stack.
|
/// Search for a module within an imports stack.
|
||||||
@ -218,19 +223,78 @@ impl Engine {
|
|||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
let FnCallExpr {
|
let FnCallExpr {
|
||||||
name,
|
name, hashes, args, ..
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
namespace,
|
|
||||||
capture_parent_scope: capture,
|
|
||||||
hashes,
|
|
||||||
args,
|
|
||||||
..
|
|
||||||
} = expr;
|
} = expr;
|
||||||
|
|
||||||
|
// Short-circuit native binary operator call if under Fast Operators mode
|
||||||
|
if expr.is_native_operator && self.fast_operators() && (args.len() == 1 || args.len() == 2)
|
||||||
|
{
|
||||||
|
let mut lhs = self
|
||||||
|
.get_arg_value(scope, global, caches, lib, this_ptr, &args[0], level)?
|
||||||
|
.0
|
||||||
|
.flatten();
|
||||||
|
|
||||||
|
let mut rhs = if args.len() == 2 {
|
||||||
|
self.get_arg_value(scope, global, caches, lib, this_ptr, &args[1], level)?
|
||||||
|
.0
|
||||||
|
.flatten()
|
||||||
|
} else {
|
||||||
|
Dynamic::UNIT
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut operands = [&mut lhs, &mut rhs];
|
||||||
|
let operands = if args.len() == 2 {
|
||||||
|
&mut operands[..]
|
||||||
|
} else {
|
||||||
|
&mut operands[0..1]
|
||||||
|
};
|
||||||
|
|
||||||
|
let hash = calc_fn_params_hash(operands.iter().map(|a| a.type_id()));
|
||||||
|
let hash = combine_hashes(hashes.native, hash);
|
||||||
|
|
||||||
|
let cache = caches.fn_resolution_cache_mut();
|
||||||
|
|
||||||
|
let func = if let Entry::Vacant(entry) = cache.entry(hash) {
|
||||||
|
let func = if args.len() == 2 {
|
||||||
|
get_builtin_binary_op_fn(&name, operands[0], operands[1])
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(f) = func {
|
||||||
|
entry.insert(Some(FnResolutionCacheEntry {
|
||||||
|
func: CallableFunction::from_method(Box::new(f) as Box<FnAny>),
|
||||||
|
source: None,
|
||||||
|
}));
|
||||||
|
&cache.get(&hash).unwrap().as_ref().unwrap().func
|
||||||
|
} else {
|
||||||
|
let result = self.exec_fn_call(
|
||||||
|
None, global, caches, lib, name, *hashes, operands, false, false, pos,
|
||||||
|
level,
|
||||||
|
);
|
||||||
|
return result.map(|(v, ..)| v);
|
||||||
|
}
|
||||||
|
} else if let Some(entry) = cache.get(&hash).unwrap() {
|
||||||
|
&entry.func
|
||||||
|
} else {
|
||||||
|
let sig = gen_fn_call_signature(self, name, operands);
|
||||||
|
return Err(ERR::ErrorFunctionNotFound(sig, pos).into());
|
||||||
|
};
|
||||||
|
|
||||||
|
let context = (self, name, None, &*global, lib, pos, level).into();
|
||||||
|
let result = if func.is_plugin_fn() {
|
||||||
|
func.get_plugin_fn().unwrap().call(context, operands)
|
||||||
|
} else {
|
||||||
|
func.get_native_fn().unwrap()(context, operands)
|
||||||
|
};
|
||||||
|
return self.check_return_value(result, pos);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
if !namespace.is_empty() {
|
if !expr.namespace.is_empty() {
|
||||||
// Qualified function call
|
// Qualified function call
|
||||||
let hash = hashes.native;
|
let hash = hashes.native;
|
||||||
|
let namespace = &expr.namespace;
|
||||||
|
|
||||||
return self.make_qualified_function_call(
|
return self.make_qualified_function_call(
|
||||||
scope, global, caches, lib, this_ptr, namespace, name, args, hash, pos, level,
|
scope, global, caches, lib, this_ptr, namespace, name, args, hash, pos, level,
|
||||||
@ -244,7 +308,18 @@ impl Engine {
|
|||||||
);
|
);
|
||||||
|
|
||||||
self.make_function_call(
|
self.make_function_call(
|
||||||
scope, global, caches, lib, this_ptr, name, first_arg, args, *hashes, *capture, pos,
|
scope,
|
||||||
|
global,
|
||||||
|
caches,
|
||||||
|
lib,
|
||||||
|
this_ptr,
|
||||||
|
name,
|
||||||
|
first_arg,
|
||||||
|
args,
|
||||||
|
*hashes,
|
||||||
|
expr.capture_parent_scope,
|
||||||
|
expr.is_native_operator,
|
||||||
|
pos,
|
||||||
level,
|
level,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -115,6 +115,143 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
|
|||||||
} };
|
} };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for common patterns
|
||||||
|
if type1 == type2 {
|
||||||
|
if type1 == TypeId::of::<INT>() {
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
use crate::packages::arithmetic::arith_basic::INT::functions::*;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
match op {
|
||||||
|
"+" => return Some(impl_op!(INT => add(as_int, as_int))),
|
||||||
|
"-" => return Some(impl_op!(INT => subtract(as_int, as_int))),
|
||||||
|
"*" => return Some(impl_op!(INT => multiply(as_int, as_int))),
|
||||||
|
"/" => return Some(impl_op!(INT => divide(as_int, as_int))),
|
||||||
|
"%" => return Some(impl_op!(INT => modulo(as_int, as_int))),
|
||||||
|
"**" => return Some(impl_op!(INT => power(as_int, as_int))),
|
||||||
|
">>" => return Some(impl_op!(INT => shift_right(as_int, as_int))),
|
||||||
|
"<<" => return Some(impl_op!(INT => shift_left(as_int, as_int))),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unchecked")]
|
||||||
|
match op {
|
||||||
|
"+" => return Some(impl_op!(INT => as_int + as_int)),
|
||||||
|
"-" => return Some(impl_op!(INT => as_int - as_int)),
|
||||||
|
"*" => return Some(impl_op!(INT => as_int * as_int)),
|
||||||
|
"/" => return Some(impl_op!(INT => as_int / as_int)),
|
||||||
|
"%" => return Some(impl_op!(INT => as_int % as_int)),
|
||||||
|
"**" => return Some(impl_op!(INT => as_int.pow(as_int as u32))),
|
||||||
|
">>" => return Some(impl_op!(INT => as_int >> as_int)),
|
||||||
|
"<<" => return Some(impl_op!(INT => as_int << as_int)),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
return match op {
|
||||||
|
"==" => Some(impl_op!(INT => as_int == as_int)),
|
||||||
|
"!=" => Some(impl_op!(INT => as_int != as_int)),
|
||||||
|
">" => Some(impl_op!(INT => as_int > as_int)),
|
||||||
|
">=" => Some(impl_op!(INT => as_int >= as_int)),
|
||||||
|
"<" => Some(impl_op!(INT => as_int < as_int)),
|
||||||
|
"<=" => Some(impl_op!(INT => as_int <= as_int)),
|
||||||
|
"&" => Some(impl_op!(INT => as_int & as_int)),
|
||||||
|
"|" => Some(impl_op!(INT => as_int | as_int)),
|
||||||
|
"^" => Some(impl_op!(INT => as_int ^ as_int)),
|
||||||
|
".." => Some(|_, args| {
|
||||||
|
let x = args[0].as_int().expect(BUILTIN);
|
||||||
|
let y = args[1].as_int().expect(BUILTIN);
|
||||||
|
Ok((x..y).into())
|
||||||
|
}),
|
||||||
|
"..=" => Some(|_, args| {
|
||||||
|
let x = args[0].as_int().expect(BUILTIN);
|
||||||
|
let y = args[1].as_int().expect(BUILTIN);
|
||||||
|
Ok((x..=y).into())
|
||||||
|
}),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if type1 == TypeId::of::<bool>() {
|
||||||
|
return match op {
|
||||||
|
"==" => Some(impl_op!(bool => as_bool == as_bool)),
|
||||||
|
"!=" => Some(impl_op!(bool => as_bool != as_bool)),
|
||||||
|
">" => Some(impl_op!(bool => as_bool > as_bool)),
|
||||||
|
">=" => Some(impl_op!(bool => as_bool >= as_bool)),
|
||||||
|
"<" => Some(impl_op!(bool => as_bool < as_bool)),
|
||||||
|
"<=" => Some(impl_op!(bool => as_bool <= as_bool)),
|
||||||
|
"&" => Some(impl_op!(bool => as_bool & as_bool)),
|
||||||
|
"|" => Some(impl_op!(bool => as_bool | as_bool)),
|
||||||
|
"^" => Some(impl_op!(bool => as_bool ^ as_bool)),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if type1 == TypeId::of::<ImmutableString>() {
|
||||||
|
return match op {
|
||||||
|
"+" => Some(impl_op!(ImmutableString + ImmutableString)),
|
||||||
|
"-" => Some(impl_op!(ImmutableString - ImmutableString)),
|
||||||
|
"==" => Some(impl_op!(ImmutableString == ImmutableString)),
|
||||||
|
"!=" => Some(impl_op!(ImmutableString != ImmutableString)),
|
||||||
|
">" => Some(impl_op!(ImmutableString > ImmutableString)),
|
||||||
|
">=" => Some(impl_op!(ImmutableString >= ImmutableString)),
|
||||||
|
"<" => Some(impl_op!(ImmutableString < ImmutableString)),
|
||||||
|
"<=" => Some(impl_op!(ImmutableString <= ImmutableString)),
|
||||||
|
OP_CONTAINS => Some(impl_op!(ImmutableString.contains(ImmutableString.as_str()))),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if type1 == TypeId::of::<char>() {
|
||||||
|
return match op {
|
||||||
|
"+" => Some(|_, args| {
|
||||||
|
let x = args[0].as_char().expect(BUILTIN);
|
||||||
|
let y = args[1].as_char().expect(BUILTIN);
|
||||||
|
Ok(format!("{x}{y}").into())
|
||||||
|
}),
|
||||||
|
"==" => Some(impl_op!(char => as_char == as_char)),
|
||||||
|
"!=" => Some(impl_op!(char => as_char != as_char)),
|
||||||
|
">" => Some(impl_op!(char => as_char > as_char)),
|
||||||
|
">=" => Some(impl_op!(char => as_char >= as_char)),
|
||||||
|
"<" => Some(impl_op!(char => as_char < as_char)),
|
||||||
|
"<=" => Some(impl_op!(char => as_char <= as_char)),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
if type1 == TypeId::of::<crate::Blob>() {
|
||||||
|
use crate::Blob;
|
||||||
|
|
||||||
|
return match op {
|
||||||
|
"+" => Some(|_, args| {
|
||||||
|
let blob1 = &*args[0].read_lock::<Blob>().expect(BUILTIN);
|
||||||
|
let blob2 = &*args[1].read_lock::<Blob>().expect(BUILTIN);
|
||||||
|
|
||||||
|
Ok(Dynamic::from_blob(if blob2.is_empty() {
|
||||||
|
blob1.clone()
|
||||||
|
} else if blob1.is_empty() {
|
||||||
|
blob2.clone()
|
||||||
|
} else {
|
||||||
|
let mut blob = blob1.clone();
|
||||||
|
blob.extend(blob2);
|
||||||
|
blob
|
||||||
|
}))
|
||||||
|
}),
|
||||||
|
"==" => Some(impl_op!(Blob == Blob)),
|
||||||
|
"!=" => Some(impl_op!(Blob != Blob)),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if type1 == TypeId::of::<()>() {
|
||||||
|
return match op {
|
||||||
|
"==" => Some(|_, _| Ok(Dynamic::TRUE)),
|
||||||
|
"!=" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
macro_rules! impl_float {
|
macro_rules! impl_float {
|
||||||
($x:ty, $xx:ident, $y:ty, $yy:ident) => {
|
($x:ty, $xx:ident, $y:ty, $yy:ident) => {
|
||||||
@ -406,141 +543,6 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Beyond here, type1 == type2
|
// Beyond here, type1 == type2
|
||||||
|
|
||||||
if type1 == TypeId::of::<INT>() {
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
use crate::packages::arithmetic::arith_basic::INT::functions::*;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
match op {
|
|
||||||
"+" => return Some(impl_op!(INT => add(as_int, as_int))),
|
|
||||||
"-" => return Some(impl_op!(INT => subtract(as_int, as_int))),
|
|
||||||
"*" => return Some(impl_op!(INT => multiply(as_int, as_int))),
|
|
||||||
"/" => return Some(impl_op!(INT => divide(as_int, as_int))),
|
|
||||||
"%" => return Some(impl_op!(INT => modulo(as_int, as_int))),
|
|
||||||
"**" => return Some(impl_op!(INT => power(as_int, as_int))),
|
|
||||||
">>" => return Some(impl_op!(INT => shift_right(as_int, as_int))),
|
|
||||||
"<<" => return Some(impl_op!(INT => shift_left(as_int, as_int))),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "unchecked")]
|
|
||||||
match op {
|
|
||||||
"+" => return Some(impl_op!(INT => as_int + as_int)),
|
|
||||||
"-" => return Some(impl_op!(INT => as_int - as_int)),
|
|
||||||
"*" => return Some(impl_op!(INT => as_int * as_int)),
|
|
||||||
"/" => return Some(impl_op!(INT => as_int / as_int)),
|
|
||||||
"%" => return Some(impl_op!(INT => as_int % as_int)),
|
|
||||||
"**" => return Some(impl_op!(INT => as_int.pow(as_int as u32))),
|
|
||||||
">>" => return Some(impl_op!(INT => as_int >> as_int)),
|
|
||||||
"<<" => return Some(impl_op!(INT => as_int << as_int)),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
return match op {
|
|
||||||
"==" => Some(impl_op!(INT => as_int == as_int)),
|
|
||||||
"!=" => Some(impl_op!(INT => as_int != as_int)),
|
|
||||||
">" => Some(impl_op!(INT => as_int > as_int)),
|
|
||||||
">=" => Some(impl_op!(INT => as_int >= as_int)),
|
|
||||||
"<" => Some(impl_op!(INT => as_int < as_int)),
|
|
||||||
"<=" => Some(impl_op!(INT => as_int <= as_int)),
|
|
||||||
"&" => Some(impl_op!(INT => as_int & as_int)),
|
|
||||||
"|" => Some(impl_op!(INT => as_int | as_int)),
|
|
||||||
"^" => Some(impl_op!(INT => as_int ^ as_int)),
|
|
||||||
".." => Some(|_, args| {
|
|
||||||
let x = args[0].as_int().expect(BUILTIN);
|
|
||||||
let y = args[1].as_int().expect(BUILTIN);
|
|
||||||
Ok((x..y).into())
|
|
||||||
}),
|
|
||||||
"..=" => Some(|_, args| {
|
|
||||||
let x = args[0].as_int().expect(BUILTIN);
|
|
||||||
let y = args[1].as_int().expect(BUILTIN);
|
|
||||||
Ok((x..=y).into())
|
|
||||||
}),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if type1 == TypeId::of::<bool>() {
|
|
||||||
return match op {
|
|
||||||
"==" => Some(impl_op!(bool => as_bool == as_bool)),
|
|
||||||
"!=" => Some(impl_op!(bool => as_bool != as_bool)),
|
|
||||||
">" => Some(impl_op!(bool => as_bool > as_bool)),
|
|
||||||
">=" => Some(impl_op!(bool => as_bool >= as_bool)),
|
|
||||||
"<" => Some(impl_op!(bool => as_bool < as_bool)),
|
|
||||||
"<=" => Some(impl_op!(bool => as_bool <= as_bool)),
|
|
||||||
"&" => Some(impl_op!(bool => as_bool & as_bool)),
|
|
||||||
"|" => Some(impl_op!(bool => as_bool | as_bool)),
|
|
||||||
"^" => Some(impl_op!(bool => as_bool ^ as_bool)),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if type1 == TypeId::of::<ImmutableString>() {
|
|
||||||
return match op {
|
|
||||||
"+" => Some(impl_op!(ImmutableString + ImmutableString)),
|
|
||||||
"-" => Some(impl_op!(ImmutableString - ImmutableString)),
|
|
||||||
"==" => Some(impl_op!(ImmutableString == ImmutableString)),
|
|
||||||
"!=" => Some(impl_op!(ImmutableString != ImmutableString)),
|
|
||||||
">" => Some(impl_op!(ImmutableString > ImmutableString)),
|
|
||||||
">=" => Some(impl_op!(ImmutableString >= ImmutableString)),
|
|
||||||
"<" => Some(impl_op!(ImmutableString < ImmutableString)),
|
|
||||||
"<=" => Some(impl_op!(ImmutableString <= ImmutableString)),
|
|
||||||
OP_CONTAINS => Some(impl_op!(ImmutableString.contains(ImmutableString.as_str()))),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if type1 == TypeId::of::<char>() {
|
|
||||||
return match op {
|
|
||||||
"+" => Some(|_, args| {
|
|
||||||
let x = args[0].as_char().expect(BUILTIN);
|
|
||||||
let y = args[1].as_char().expect(BUILTIN);
|
|
||||||
Ok(format!("{x}{y}").into())
|
|
||||||
}),
|
|
||||||
"==" => Some(impl_op!(char => as_char == as_char)),
|
|
||||||
"!=" => Some(impl_op!(char => as_char != as_char)),
|
|
||||||
">" => Some(impl_op!(char => as_char > as_char)),
|
|
||||||
">=" => Some(impl_op!(char => as_char >= as_char)),
|
|
||||||
"<" => Some(impl_op!(char => as_char < as_char)),
|
|
||||||
"<=" => Some(impl_op!(char => as_char <= as_char)),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
if type1 == TypeId::of::<crate::Blob>() {
|
|
||||||
use crate::Blob;
|
|
||||||
|
|
||||||
return match op {
|
|
||||||
"+" => Some(|_, args| {
|
|
||||||
let blob1 = &*args[0].read_lock::<Blob>().expect(BUILTIN);
|
|
||||||
let blob2 = &*args[1].read_lock::<Blob>().expect(BUILTIN);
|
|
||||||
|
|
||||||
Ok(Dynamic::from_blob(if blob2.is_empty() {
|
|
||||||
blob1.clone()
|
|
||||||
} else if blob1.is_empty() {
|
|
||||||
blob2.clone()
|
|
||||||
} else {
|
|
||||||
let mut blob = blob1.clone();
|
|
||||||
blob.extend(blob2);
|
|
||||||
blob
|
|
||||||
}))
|
|
||||||
}),
|
|
||||||
"==" => Some(impl_op!(Blob == Blob)),
|
|
||||||
"!=" => Some(impl_op!(Blob != Blob)),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if type1 == TypeId::of::<()>() {
|
|
||||||
return match op {
|
|
||||||
"==" => Some(|_, _| Ok(Dynamic::TRUE)),
|
|
||||||
"!=" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -594,6 +596,98 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
|
|||||||
} };
|
} };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for common patterns
|
||||||
|
if type1 == type2 {
|
||||||
|
if type1 == TypeId::of::<INT>() {
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
use crate::packages::arithmetic::arith_basic::INT::functions::*;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
match op {
|
||||||
|
"+=" => return Some(impl_op!(INT => add(as_int, as_int))),
|
||||||
|
"-=" => return Some(impl_op!(INT => subtract(as_int, as_int))),
|
||||||
|
"*=" => return Some(impl_op!(INT => multiply(as_int, as_int))),
|
||||||
|
"/=" => return Some(impl_op!(INT => divide(as_int, as_int))),
|
||||||
|
"%=" => return Some(impl_op!(INT => modulo(as_int, as_int))),
|
||||||
|
"**=" => return Some(impl_op!(INT => power(as_int, as_int))),
|
||||||
|
">>=" => return Some(impl_op!(INT => shift_right(as_int, as_int))),
|
||||||
|
"<<=" => return Some(impl_op!(INT => shift_left(as_int, as_int))),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unchecked")]
|
||||||
|
match op {
|
||||||
|
"+=" => return Some(impl_op!(INT += as_int)),
|
||||||
|
"-=" => return Some(impl_op!(INT -= as_int)),
|
||||||
|
"*=" => return Some(impl_op!(INT *= as_int)),
|
||||||
|
"/=" => return Some(impl_op!(INT /= as_int)),
|
||||||
|
"%=" => return Some(impl_op!(INT %= as_int)),
|
||||||
|
"**=" => return Some(impl_op!(INT => as_int.pow(as_int as u32))),
|
||||||
|
">>=" => return Some(impl_op!(INT >>= as_int)),
|
||||||
|
"<<=" => return Some(impl_op!(INT <<= as_int)),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
return match op {
|
||||||
|
"&=" => Some(impl_op!(INT &= as_int)),
|
||||||
|
"|=" => Some(impl_op!(INT |= as_int)),
|
||||||
|
"^=" => Some(impl_op!(INT ^= as_int)),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if type1 == TypeId::of::<bool>() {
|
||||||
|
return match op {
|
||||||
|
"&=" => Some(impl_op!(bool = x && as_bool)),
|
||||||
|
"|=" => Some(impl_op!(bool = x || as_bool)),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if type1 == TypeId::of::<char>() {
|
||||||
|
return match op {
|
||||||
|
"+=" => Some(|_, args| {
|
||||||
|
let y = args[1].as_char().expect(BUILTIN);
|
||||||
|
let x = &mut *args[0].write_lock::<Dynamic>().expect(BUILTIN);
|
||||||
|
Ok((*x = format!("{x}{y}").into()).into())
|
||||||
|
}),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if type1 == TypeId::of::<ImmutableString>() {
|
||||||
|
return match op {
|
||||||
|
"+=" => Some(|_, args| {
|
||||||
|
let (first, second) = args.split_first_mut().expect(BUILTIN);
|
||||||
|
let x = &mut *first.write_lock::<ImmutableString>().expect(BUILTIN);
|
||||||
|
let y = std::mem::take(second[0]).cast::<ImmutableString>();
|
||||||
|
Ok((*x += y).into())
|
||||||
|
}),
|
||||||
|
"-=" => Some(|_, args| {
|
||||||
|
let (first, second) = args.split_first_mut().expect(BUILTIN);
|
||||||
|
let x = &mut *first.write_lock::<ImmutableString>().expect(BUILTIN);
|
||||||
|
let y = std::mem::take(second[0]).cast::<ImmutableString>();
|
||||||
|
Ok((*x -= y).into())
|
||||||
|
}),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
if type1 == TypeId::of::<crate::Blob>() {
|
||||||
|
use crate::Blob;
|
||||||
|
|
||||||
|
return match op {
|
||||||
|
"+=" => Some(|_, args| {
|
||||||
|
let blob2 = std::mem::take(args[1]).cast::<Blob>();
|
||||||
|
let blob1 = &mut *args[0].write_lock::<Blob>().expect(BUILTIN);
|
||||||
|
Ok(crate::packages::blob_basic::blob_functions::append(blob1, blob2).into())
|
||||||
|
}),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
macro_rules! impl_float {
|
macro_rules! impl_float {
|
||||||
($x:ident, $xx:ident, $y:ty, $yy:ident) => {
|
($x:ident, $xx:ident, $y:ty, $yy:ident) => {
|
||||||
@ -752,100 +846,5 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// No built-in op-assignments for different types.
|
|
||||||
if type2 != type1 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Beyond here, type1 == type2
|
|
||||||
if type1 == TypeId::of::<INT>() {
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
use crate::packages::arithmetic::arith_basic::INT::functions::*;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
match op {
|
|
||||||
"+=" => return Some(impl_op!(INT => add(as_int, as_int))),
|
|
||||||
"-=" => return Some(impl_op!(INT => subtract(as_int, as_int))),
|
|
||||||
"*=" => return Some(impl_op!(INT => multiply(as_int, as_int))),
|
|
||||||
"/=" => return Some(impl_op!(INT => divide(as_int, as_int))),
|
|
||||||
"%=" => return Some(impl_op!(INT => modulo(as_int, as_int))),
|
|
||||||
"**=" => return Some(impl_op!(INT => power(as_int, as_int))),
|
|
||||||
">>=" => return Some(impl_op!(INT => shift_right(as_int, as_int))),
|
|
||||||
"<<=" => return Some(impl_op!(INT => shift_left(as_int, as_int))),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "unchecked")]
|
|
||||||
match op {
|
|
||||||
"+=" => return Some(impl_op!(INT += as_int)),
|
|
||||||
"-=" => return Some(impl_op!(INT -= as_int)),
|
|
||||||
"*=" => return Some(impl_op!(INT *= as_int)),
|
|
||||||
"/=" => return Some(impl_op!(INT /= as_int)),
|
|
||||||
"%=" => return Some(impl_op!(INT %= as_int)),
|
|
||||||
"**=" => return Some(impl_op!(INT => as_int.pow(as_int as u32))),
|
|
||||||
">>=" => return Some(impl_op!(INT >>= as_int)),
|
|
||||||
"<<=" => return Some(impl_op!(INT <<= as_int)),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
return match op {
|
|
||||||
"&=" => Some(impl_op!(INT &= as_int)),
|
|
||||||
"|=" => Some(impl_op!(INT |= as_int)),
|
|
||||||
"^=" => Some(impl_op!(INT ^= as_int)),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if type1 == TypeId::of::<bool>() {
|
|
||||||
return match op {
|
|
||||||
"&=" => Some(impl_op!(bool = x && as_bool)),
|
|
||||||
"|=" => Some(impl_op!(bool = x || as_bool)),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if type1 == TypeId::of::<char>() {
|
|
||||||
return match op {
|
|
||||||
"+=" => Some(|_, args| {
|
|
||||||
let y = args[1].as_char().expect(BUILTIN);
|
|
||||||
let x = &mut *args[0].write_lock::<Dynamic>().expect(BUILTIN);
|
|
||||||
Ok((*x = format!("{x}{y}").into()).into())
|
|
||||||
}),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if type1 == TypeId::of::<ImmutableString>() {
|
|
||||||
return match op {
|
|
||||||
"+=" => Some(|_, args| {
|
|
||||||
let (first, second) = args.split_first_mut().expect(BUILTIN);
|
|
||||||
let x = &mut *first.write_lock::<ImmutableString>().expect(BUILTIN);
|
|
||||||
let y = std::mem::take(second[0]).cast::<ImmutableString>();
|
|
||||||
Ok((*x += y).into())
|
|
||||||
}),
|
|
||||||
"-=" => Some(|_, args| {
|
|
||||||
let (first, second) = args.split_first_mut().expect(BUILTIN);
|
|
||||||
let x = &mut *first.write_lock::<ImmutableString>().expect(BUILTIN);
|
|
||||||
let y = std::mem::take(second[0]).cast::<ImmutableString>();
|
|
||||||
Ok((*x -= y).into())
|
|
||||||
}),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
if type1 == TypeId::of::<crate::Blob>() {
|
|
||||||
use crate::Blob;
|
|
||||||
|
|
||||||
return match op {
|
|
||||||
"+=" => Some(|_, args| {
|
|
||||||
let blob2 = std::mem::take(args[1]).cast::<Blob>();
|
|
||||||
let blob1 = &mut *args[0].write_lock::<Blob>().expect(BUILTIN);
|
|
||||||
Ok(crate::packages::blob_basic::blob_functions::append(blob1, blob2).into())
|
|
||||||
}),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -127,17 +127,35 @@ pub fn ensure_no_data_race(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Engine {
|
|
||||||
/// Generate the signature for a function call.
|
/// Generate the signature for a function call.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn gen_call_signature(
|
pub fn gen_fn_call_signature(engine: &Engine, fn_name: &str, args: &[&mut Dynamic]) -> String {
|
||||||
&self,
|
format!(
|
||||||
#[cfg(not(feature = "no_module"))] namespace: &crate::ast::Namespace,
|
"{fn_name} ({})",
|
||||||
|
args.iter()
|
||||||
|
.map(|a| if a.is::<ImmutableString>() {
|
||||||
|
"&str | ImmutableString | String"
|
||||||
|
} else {
|
||||||
|
engine.map_type_name(a.type_name())
|
||||||
|
})
|
||||||
|
.collect::<FnArgsVec<_>>()
|
||||||
|
.join(", ")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate the signature for a namespace-qualified function call.
|
||||||
|
///
|
||||||
|
/// Not available under `no_module`.
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn gen_qualified_fn_call_signature(
|
||||||
|
engine: &Engine,
|
||||||
|
namespace: &crate::ast::Namespace,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
args: &[&mut Dynamic],
|
args: &[&mut Dynamic],
|
||||||
) -> String {
|
) -> String {
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
let (ns, sep) = (
|
let (ns, sep) = (
|
||||||
namespace.to_string(),
|
namespace.to_string(),
|
||||||
if namespace.is_empty() {
|
if namespace.is_empty() {
|
||||||
@ -146,22 +164,11 @@ impl Engine {
|
|||||||
crate::tokenizer::Token::DoubleColon.literal_syntax()
|
crate::tokenizer::Token::DoubleColon.literal_syntax()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
#[cfg(feature = "no_module")]
|
|
||||||
let (ns, sep) = ("", "");
|
|
||||||
|
|
||||||
format!(
|
format!("{ns}{sep}{}", gen_fn_call_signature(engine, fn_name, args))
|
||||||
"{ns}{sep}{fn_name} ({})",
|
|
||||||
args.iter()
|
|
||||||
.map(|a| if a.is::<ImmutableString>() {
|
|
||||||
"&str | ImmutableString | String"
|
|
||||||
} else {
|
|
||||||
self.map_type_name(a.type_name())
|
|
||||||
})
|
|
||||||
.collect::<FnArgsVec<_>>()
|
|
||||||
.join(", ")
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Engine {
|
||||||
/// Resolve a normal (non-qualified) function call.
|
/// Resolve a normal (non-qualified) function call.
|
||||||
///
|
///
|
||||||
/// Search order:
|
/// Search order:
|
||||||
@ -174,26 +181,26 @@ impl Engine {
|
|||||||
fn resolve_fn<'s>(
|
fn resolve_fn<'s>(
|
||||||
&self,
|
&self,
|
||||||
_global: &GlobalRuntimeState,
|
_global: &GlobalRuntimeState,
|
||||||
state: &'s mut Caches,
|
caches: &'s mut Caches,
|
||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
hash_script: u64,
|
hash_base: u64,
|
||||||
args: Option<&mut FnCallArgs>,
|
args: Option<&mut FnCallArgs>,
|
||||||
allow_dynamic: bool,
|
allow_dynamic: bool,
|
||||||
is_op_assignment: bool,
|
is_op_assignment: bool,
|
||||||
) -> Option<&'s FnResolutionCacheEntry> {
|
) -> Option<&'s FnResolutionCacheEntry> {
|
||||||
if hash_script == 0 {
|
if hash_base == 0 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut hash = args.as_ref().map_or(hash_script, |args| {
|
let mut hash = args.as_ref().map_or(hash_base, |args| {
|
||||||
combine_hashes(
|
combine_hashes(
|
||||||
hash_script,
|
hash_base,
|
||||||
calc_fn_params_hash(args.iter().map(|a| a.type_id())),
|
calc_fn_params_hash(args.iter().map(|a| a.type_id())),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
let result = state
|
let result = caches
|
||||||
.fn_resolution_cache_mut()
|
.fn_resolution_cache_mut()
|
||||||
.entry(hash)
|
.entry(hash)
|
||||||
.or_insert_with(|| {
|
.or_insert_with(|| {
|
||||||
@ -248,21 +255,21 @@ impl Engine {
|
|||||||
|
|
||||||
// Check `Dynamic` parameters for functions with parameters
|
// Check `Dynamic` parameters for functions with parameters
|
||||||
if allow_dynamic && max_bitmask == 0 && num_args > 0 {
|
if allow_dynamic && max_bitmask == 0 && num_args > 0 {
|
||||||
let is_dynamic = lib.iter().any(|&m| m.contains_dynamic_fn(hash_script))
|
let is_dynamic = lib.iter().any(|&m| m.contains_dynamic_fn(hash_base))
|
||||||
|| self
|
|| self
|
||||||
.global_modules
|
.global_modules
|
||||||
.iter()
|
.iter()
|
||||||
.any(|m| m.contains_dynamic_fn(hash_script));
|
.any(|m| m.contains_dynamic_fn(hash_base));
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
let is_dynamic = is_dynamic
|
let is_dynamic = is_dynamic
|
||||||
|| _global
|
|| _global
|
||||||
.iter_imports_raw()
|
.iter_imports_raw()
|
||||||
.any(|(_, m)| m.contains_dynamic_fn(hash_script))
|
.any(|(_, m)| m.contains_dynamic_fn(hash_base))
|
||||||
|| self
|
|| self
|
||||||
.global_sub_modules
|
.global_sub_modules
|
||||||
.values()
|
.values()
|
||||||
.any(|m| m.contains_dynamic_fn(hash_script));
|
.any(|m| m.contains_dynamic_fn(hash_base));
|
||||||
|
|
||||||
// Set maximum bitmask when there are dynamic versions of the function
|
// Set maximum bitmask when there are dynamic versions of the function
|
||||||
if is_dynamic {
|
if is_dynamic {
|
||||||
@ -317,7 +324,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
hash = combine_hashes(hash_script, hash_params);
|
hash = combine_hashes(hash_base, hash_params);
|
||||||
|
|
||||||
bitmask += 1;
|
bitmask += 1;
|
||||||
}
|
}
|
||||||
@ -405,11 +412,9 @@ impl Engine {
|
|||||||
let context = (self, name, source, &*global, lib, pos, level).into();
|
let context = (self, name, source, &*global, lib, pos, level).into();
|
||||||
|
|
||||||
let result = if func.is_plugin_fn() {
|
let result = if func.is_plugin_fn() {
|
||||||
func.get_plugin_fn()
|
func.get_plugin_fn().unwrap().call(context, args)
|
||||||
.expect("plugin function")
|
|
||||||
.call(context, args)
|
|
||||||
} else {
|
} else {
|
||||||
func.get_native_fn().expect("native function")(context, args)
|
func.get_native_fn().unwrap()(context, args)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Restore the original reference
|
// Restore the original reference
|
||||||
@ -541,16 +546,9 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Raise error
|
// Raise error
|
||||||
_ => Err(ERR::ErrorFunctionNotFound(
|
_ => {
|
||||||
self.gen_call_signature(
|
Err(ERR::ErrorFunctionNotFound(gen_fn_call_signature(self, name, args), pos).into())
|
||||||
#[cfg(not(feature = "no_module"))]
|
}
|
||||||
&crate::ast::Namespace::NONE,
|
|
||||||
name,
|
|
||||||
args,
|
|
||||||
),
|
|
||||||
pos,
|
|
||||||
)
|
|
||||||
.into()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -989,6 +987,7 @@ impl Engine {
|
|||||||
args_expr: &[Expr],
|
args_expr: &[Expr],
|
||||||
hashes: FnCallHashes,
|
hashes: FnCallHashes,
|
||||||
capture_scope: bool,
|
capture_scope: bool,
|
||||||
|
is_operator: bool,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
@ -1001,6 +1000,8 @@ impl Engine {
|
|||||||
let redirected; // Handle call() - Redirect function call
|
let redirected; // Handle call() - Redirect function call
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
|
_ if is_operator => (),
|
||||||
|
|
||||||
// Handle call()
|
// Handle call()
|
||||||
KEYWORD_FN_PTR_CALL if total_args >= 1 => {
|
KEYWORD_FN_PTR_CALL if total_args >= 1 => {
|
||||||
let arg = first_arg.unwrap();
|
let arg = first_arg.unwrap();
|
||||||
@ -1429,7 +1430,7 @@ impl Engine {
|
|||||||
Some(f) => unreachable!("unknown function type: {:?}", f),
|
Some(f) => unreachable!("unknown function type: {:?}", f),
|
||||||
|
|
||||||
None => Err(ERR::ErrorFunctionNotFound(
|
None => Err(ERR::ErrorFunctionNotFound(
|
||||||
self.gen_call_signature(namespace, fn_name, &args),
|
gen_qualified_fn_call_signature(self, namespace, fn_name, &args),
|
||||||
pos,
|
pos,
|
||||||
)
|
)
|
||||||
.into()),
|
.into()),
|
||||||
|
@ -13,7 +13,9 @@ pub mod script;
|
|||||||
|
|
||||||
pub use args::FuncArgs;
|
pub use args::FuncArgs;
|
||||||
pub use builtin::{get_builtin_binary_op_fn, get_builtin_op_assignment_fn};
|
pub use builtin::{get_builtin_binary_op_fn, get_builtin_op_assignment_fn};
|
||||||
pub use call::FnCallArgs;
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
pub use call::gen_qualified_fn_call_signature;
|
||||||
|
pub use call::{gen_fn_call_signature, FnCallArgs};
|
||||||
pub use callable_function::CallableFunction;
|
pub use callable_function::CallableFunction;
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub use func::Func;
|
pub use func::Func;
|
||||||
|
@ -1181,7 +1181,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Overloaded operators can override built-in.
|
// Overloaded operators can override built-in.
|
||||||
_ if x.args.len() == 2 && !has_native_fn_override(state.engine, x.hashes.native, &arg_types) => {
|
_ if x.args.len() == 2 && (state.engine.fast_operators() || !has_native_fn_override(state.engine, x.hashes.native, &arg_types)) => {
|
||||||
if let Some(result) = get_builtin_binary_op_fn(&x.name, &arg_values[0], &arg_values[1])
|
if let Some(result) = get_builtin_binary_op_fn(&x.name, &arg_values[0], &arg_values[1])
|
||||||
.and_then(|f| {
|
.and_then(|f| {
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
@ -123,7 +123,7 @@ pub mod array_functions {
|
|||||||
/// let x = [1, 2, 3];
|
/// let x = [1, 2, 3];
|
||||||
/// let y = [true, 'x'];
|
/// let y = [true, 'x'];
|
||||||
///
|
///
|
||||||
/// x.push(y);
|
/// x.append(y);
|
||||||
///
|
///
|
||||||
/// print(x); // prints "[1, 2, 3, true, 'x']"
|
/// print(x); // prints "[1, 2, 3, true, 'x']"
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -614,8 +614,9 @@ impl Engine {
|
|||||||
args.shrink_to_fit();
|
args.shrink_to_fit();
|
||||||
|
|
||||||
return Ok(FnCallExpr {
|
return Ok(FnCallExpr {
|
||||||
name: id,
|
name: state.get_interned_string(id),
|
||||||
capture_parent_scope,
|
capture_parent_scope,
|
||||||
|
is_native_operator: false,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
namespace,
|
namespace,
|
||||||
hashes,
|
hashes,
|
||||||
@ -687,6 +688,7 @@ impl Engine {
|
|||||||
return Ok(FnCallExpr {
|
return Ok(FnCallExpr {
|
||||||
name: state.get_interned_string(id),
|
name: state.get_interned_string(id),
|
||||||
capture_parent_scope,
|
capture_parent_scope,
|
||||||
|
is_native_operator: false,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
namespace,
|
namespace,
|
||||||
hashes,
|
hashes,
|
||||||
@ -1920,6 +1922,7 @@ impl Engine {
|
|||||||
hashes: FnCallHashes::from_native(calc_fn_hash("-", 1)),
|
hashes: FnCallHashes::from_native(calc_fn_hash("-", 1)),
|
||||||
args,
|
args,
|
||||||
pos,
|
pos,
|
||||||
|
is_native_operator: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.into_fn_call_expr(pos))
|
.into_fn_call_expr(pos))
|
||||||
@ -1947,6 +1950,7 @@ impl Engine {
|
|||||||
hashes: FnCallHashes::from_native(calc_fn_hash("+", 1)),
|
hashes: FnCallHashes::from_native(calc_fn_hash("+", 1)),
|
||||||
args,
|
args,
|
||||||
pos,
|
pos,
|
||||||
|
is_native_operator: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.into_fn_call_expr(pos))
|
.into_fn_call_expr(pos))
|
||||||
@ -1965,6 +1969,7 @@ impl Engine {
|
|||||||
hashes: FnCallHashes::from_native(calc_fn_hash("!", 1)),
|
hashes: FnCallHashes::from_native(calc_fn_hash("!", 1)),
|
||||||
args,
|
args,
|
||||||
pos,
|
pos,
|
||||||
|
is_native_operator: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.into_fn_call_expr(pos))
|
.into_fn_call_expr(pos))
|
||||||
@ -2339,6 +2344,7 @@ impl Engine {
|
|||||||
name: state.get_interned_string(op.as_ref()),
|
name: state.get_interned_string(op.as_ref()),
|
||||||
hashes: FnCallHashes::from_native(hash),
|
hashes: FnCallHashes::from_native(hash),
|
||||||
pos,
|
pos,
|
||||||
|
is_native_operator: !is_valid_function_name(&op),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -169,6 +169,22 @@ fn test_arrays() -> Result<(), Box<EvalAltResult>> {
|
|||||||
"
|
"
|
||||||
)?);
|
)?);
|
||||||
|
|
||||||
|
let value = vec![
|
||||||
|
String::from("hello"),
|
||||||
|
String::from("world"),
|
||||||
|
String::from("foo"),
|
||||||
|
String::from("bar"),
|
||||||
|
];
|
||||||
|
|
||||||
|
let array: Dynamic = value.into();
|
||||||
|
|
||||||
|
assert_eq!(array.type_name(), "array");
|
||||||
|
|
||||||
|
let array = array.cast::<Array>();
|
||||||
|
|
||||||
|
assert_eq!(array[0].type_name(), "string");
|
||||||
|
assert_eq!(array.len(), 4);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +75,17 @@ fn test_native_overload() -> Result<(), Box<EvalAltResult>> {
|
|||||||
format!("{s1} Foo!").into()
|
format!("{s1} Foo!").into()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<String>(r#"let x = "hello"; let y = "world"; x + y"#)?,
|
||||||
|
"helloworld"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<String>(r#"let x = "hello"; let y = (); x + y"#)?,
|
||||||
|
"hello"
|
||||||
|
);
|
||||||
|
|
||||||
|
engine.set_fast_operators(false);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<String>(r#"let x = "hello"; let y = "world"; x + y"#)?,
|
engine.eval::<String>(r#"let x = "hello"; let y = "world"; x + y"#)?,
|
||||||
"hello***world"
|
"hello***world"
|
||||||
|
@ -53,6 +53,13 @@ fn test_optimizer_run() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
engine.set_optimization_level(OptimizationLevel::Simple);
|
engine.set_optimization_level(OptimizationLevel::Simple);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>("if 1 == 1 || 2 > 3 { 42 } else { 123 }")?,
|
||||||
|
42
|
||||||
|
);
|
||||||
|
|
||||||
|
engine.set_fast_operators(false);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>("if 1 == 1 || 2 > 3 { 42 } else { 123 }")?,
|
engine.eval::<INT>("if 1 == 1 || 2 > 3 { 42 } else { 123 }")?,
|
||||||
123
|
123
|
||||||
|
@ -75,12 +75,36 @@ fn make_greeting(n: impl std::fmt::Display) -> String {
|
|||||||
|
|
||||||
gen_unary_functions!(greet = make_greeting(INT, bool, char) -> String);
|
gen_unary_functions!(greet = make_greeting(INT, bool, char) -> String);
|
||||||
|
|
||||||
|
macro_rules! expand_enum {
|
||||||
|
($module:ident : $typ:ty => $($variant:ident),+) => {
|
||||||
|
#[export_module]
|
||||||
|
pub mod $module {
|
||||||
|
$(
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
pub const $variant: $typ = <$typ>::$variant;
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub enum MyEnum {
|
||||||
|
Foo,
|
||||||
|
Bar,
|
||||||
|
Baz,
|
||||||
|
Hello,
|
||||||
|
World,
|
||||||
|
}
|
||||||
|
|
||||||
|
expand_enum! { my_enum_module: MyEnum => Foo, Bar, Baz, Hello, World }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_plugins_package() -> Result<(), Box<EvalAltResult>> {
|
fn test_plugins_package() -> Result<(), Box<EvalAltResult>> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
let mut m = Module::new();
|
let mut m = Module::new();
|
||||||
combine_with_exported_module!(&mut m, "test", test::special_array_package);
|
combine_with_exported_module!(&mut m, "test", test::special_array_package);
|
||||||
|
combine_with_exported_module!(&mut m, "enum", my_enum_module);
|
||||||
engine.register_global_module(m.into());
|
engine.register_global_module(m.into());
|
||||||
|
|
||||||
reg_functions!(engine += greet::single(INT, bool, char));
|
reg_functions!(engine += greet::single(INT, bool, char));
|
||||||
@ -104,11 +128,14 @@ fn test_plugins_package() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert_eq!(engine.eval::<INT>("let a = [1, 2, 3]; test(a, 2)")?, 6);
|
assert_eq!(engine.eval::<INT>("let a = [1, 2, 3]; test(a, 2)")?, 6);
|
||||||
assert_eq!(engine.eval::<INT>("let a = [1, 2, 3]; hi(a, 2)")?, 6);
|
assert_eq!(engine.eval::<INT>("let a = [1, 2, 3]; hi(a, 2)")?, 6);
|
||||||
assert_eq!(engine.eval::<INT>("let a = [1, 2, 3]; test(a, 2)")?, 6);
|
assert_eq!(engine.eval::<INT>("let a = [1, 2, 3]; test(a, 2)")?, 6);
|
||||||
assert_eq!(engine.eval::<INT>("2 + 2")?, 5);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<String>("let a = [1, 2, 3]; greet(test(a, 2))")?,
|
engine.eval::<String>("let a = [1, 2, 3]; greet(test(a, 2))")?,
|
||||||
"6 kitties"
|
"6 kitties"
|
||||||
);
|
);
|
||||||
|
assert_eq!(engine.eval::<INT>("2 + 2")?, 4);
|
||||||
|
|
||||||
|
engine.set_fast_operators(false);
|
||||||
|
assert_eq!(engine.eval::<INT>("2 + 2")?, 5);
|
||||||
|
|
||||||
engine.register_static_module("test", exported_module!(test::special_array_package).into());
|
engine.register_static_module("test", exported_module!(test::special_array_package).into());
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user