Merge pull request #680 from schungx/master
Line to function definition in FnPtr.
This commit is contained in:
commit
04a963ab41
@ -9,6 +9,7 @@ Bug fixes
|
|||||||
|
|
||||||
* Integer numbers that are too large to deserialize into `INT` now fall back to `Decimal` or `FLOAT` instead of silently truncating.
|
* Integer numbers that are too large to deserialize into `INT` now fall back to `Decimal` or `FLOAT` instead of silently truncating.
|
||||||
* Parsing deeply-nested closures (e.g. `||{||{||{||{||{||{||{...}}}}}}}`) no longer panics but will be confined to the nesting limit.
|
* Parsing deeply-nested closures (e.g. `||{||{||{||{||{||{||{...}}}}}}}`) no longer panics but will be confined to the nesting limit.
|
||||||
|
* Closures containing a single expression are now allowed in `Engine::eval_expression` etc.
|
||||||
|
|
||||||
Breaking API changes
|
Breaking API changes
|
||||||
--------------------
|
--------------------
|
||||||
@ -31,6 +32,12 @@ Speed improvements
|
|||||||
Net features
|
Net features
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
### First class functions (sort of)
|
||||||
|
|
||||||
|
* A function pointer created via a closure definition now links to the particular anonymous function itself.
|
||||||
|
* This avoids a potentially expensive function lookup when the function pointer is called, speeding up closures.
|
||||||
|
* It does _not_, however, allow the function pointer to be `export`ed as a constant from a script module because the closure may cross-call other functions defined in the module and the function pointer won't keep the fully encapsulated environment.
|
||||||
|
|
||||||
### `!in`
|
### `!in`
|
||||||
|
|
||||||
* A new operator `!in` is added which maps to `!(... in ...)`.
|
* A new operator `!in` is added which maps to `!(... in ...)`.
|
||||||
@ -53,6 +60,7 @@ Enhancements
|
|||||||
* Line-style doc-comments are now merged into a single string to avoid creating many strings. Block-style doc-comments continue to be independent strings.
|
* Line-style doc-comments are now merged into a single string to avoid creating many strings. Block-style doc-comments continue to be independent strings.
|
||||||
* Block-style doc-comments are now "un-indented" for better formatting.
|
* Block-style doc-comments are now "un-indented" for better formatting.
|
||||||
* Doc-comments on plugin modules are now captured in the module's `doc` field.
|
* Doc-comments on plugin modules are now captured in the module's `doc` field.
|
||||||
|
* Expression nesting levels is refined such that it grows less excessively for common patterns.
|
||||||
|
|
||||||
|
|
||||||
Version 1.11.0
|
Version 1.11.0
|
||||||
|
@ -4,14 +4,14 @@ error: duplicated attribute 'rhai_fn'
|
|||||||
6 | #[rhai_fn(pure)]
|
6 | #[rhai_fn(pure)]
|
||||||
| ^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error[E0433]: failed to resolve: use of undeclared crate or module `test_module`
|
|
||||||
--> ui_tests/rhai_fn_duplicate_attr.rs:13:8
|
|
||||||
|
|
|
||||||
13 | if test_module::test_fn(n) {
|
|
||||||
| ^^^^^^^^^^^ use of undeclared crate or module `test_module`
|
|
||||||
|
|
||||||
error[E0425]: cannot find value `n` in this scope
|
error[E0425]: cannot find value `n` in this scope
|
||||||
--> ui_tests/rhai_fn_duplicate_attr.rs:13:29
|
--> ui_tests/rhai_fn_duplicate_attr.rs:13:29
|
||||||
|
|
|
|
||||||
13 | if test_module::test_fn(n) {
|
13 | if test_module::test_fn(n) {
|
||||||
| ^ not found in this scope
|
| ^ not found in this scope
|
||||||
|
|
||||||
|
error[E0433]: failed to resolve: use of undeclared crate or module `test_module`
|
||||||
|
--> ui_tests/rhai_fn_duplicate_attr.rs:13:8
|
||||||
|
|
|
||||||
|
13 | if test_module::test_fn(n) {
|
||||||
|
| ^^^^^^^^^^^ use of undeclared crate or module `test_module`
|
||||||
|
@ -18,9 +18,9 @@ use std::{
|
|||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub struct CallFnOptions<'t> {
|
pub struct CallFnOptions<'t> {
|
||||||
/// A value for binding to the `this` pointer (if any).
|
/// A value for binding to the `this` pointer (if any). Default [`None`].
|
||||||
pub this_ptr: Option<&'t mut Dynamic>,
|
pub this_ptr: Option<&'t mut Dynamic>,
|
||||||
/// The custom state of this evaluation run (if any), overrides [`Engine::default_tag`].
|
/// The custom state of this evaluation run (if any), overrides [`Engine::default_tag`]. Default [`None`].
|
||||||
pub tag: Option<Dynamic>,
|
pub tag: Option<Dynamic>,
|
||||||
/// Evaluate the [`AST`] to load necessary modules before calling the function? Default `true`.
|
/// Evaluate the [`AST`] to load necessary modules before calling the function? Default `true`.
|
||||||
pub eval_ast: bool,
|
pub eval_ast: bool,
|
||||||
@ -171,27 +171,28 @@ impl Engine {
|
|||||||
let mut arg_values = StaticVec::new_const();
|
let mut arg_values = StaticVec::new_const();
|
||||||
args.parse(&mut arg_values);
|
args.parse(&mut arg_values);
|
||||||
|
|
||||||
let result = self._call_fn(
|
self._call_fn(
|
||||||
options,
|
|
||||||
scope,
|
scope,
|
||||||
&mut GlobalRuntimeState::new(self),
|
&mut GlobalRuntimeState::new(self),
|
||||||
&mut Caches::new(),
|
&mut Caches::new(),
|
||||||
ast,
|
ast,
|
||||||
name.as_ref(),
|
name.as_ref(),
|
||||||
arg_values.as_mut(),
|
arg_values.as_mut(),
|
||||||
)?;
|
options,
|
||||||
|
)
|
||||||
|
.and_then(|result| {
|
||||||
|
// Bail out early if the return type needs no cast
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||||
|
return Ok(reify! { result => T });
|
||||||
|
}
|
||||||
|
|
||||||
// Bail out early if the return type needs no cast
|
// Cast return type
|
||||||
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
let typ = self.map_type_name(result.type_name());
|
||||||
return Ok(reify!(result => T));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cast return type
|
result.try_cast().ok_or_else(|| {
|
||||||
let typ = self.map_type_name(result.type_name());
|
let t = self.map_type_name(type_name::<T>()).into();
|
||||||
|
ERR::ErrorMismatchOutputType(t, typ.into(), Position::NONE).into()
|
||||||
result.try_cast().ok_or_else(|| {
|
})
|
||||||
let t = self.map_type_name(type_name::<T>()).into();
|
|
||||||
ERR::ErrorMismatchOutputType(t, typ.into(), Position::NONE).into()
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
||||||
@ -206,13 +207,13 @@ impl Engine {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn _call_fn(
|
pub(crate) fn _call_fn(
|
||||||
&self,
|
&self,
|
||||||
options: CallFnOptions,
|
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
global: &mut GlobalRuntimeState,
|
global: &mut GlobalRuntimeState,
|
||||||
caches: &mut Caches,
|
caches: &mut Caches,
|
||||||
ast: &AST,
|
ast: &AST,
|
||||||
name: &str,
|
name: &str,
|
||||||
arg_values: &mut [Dynamic],
|
arg_values: &mut [Dynamic],
|
||||||
|
options: CallFnOptions,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
let statements = ast.statements();
|
let statements = ast.statements();
|
||||||
|
|
||||||
|
@ -130,16 +130,16 @@ impl Expression<'_> {
|
|||||||
pub fn get_literal_value<T: Variant>(&self) -> Option<T> {
|
pub fn get_literal_value<T: Variant>(&self) -> Option<T> {
|
||||||
// Coded this way in order to maximally leverage potentials for dead-code removal.
|
// Coded this way in order to maximally leverage potentials for dead-code removal.
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Expr::IntegerConstant(x, ..) => reify!(*x => Option<T>),
|
Expr::IntegerConstant(x, ..) => reify! { *x => Option<T> },
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Expr::FloatConstant(x, ..) => reify!(*x => Option<T>),
|
Expr::FloatConstant(x, ..) => reify! { *x => Option<T> },
|
||||||
|
|
||||||
Expr::CharConstant(x, ..) => reify!(*x => Option<T>),
|
Expr::CharConstant(x, ..) => reify! { *x => Option<T> },
|
||||||
Expr::StringConstant(x, ..) => reify!(x.clone() => Option<T>),
|
Expr::StringConstant(x, ..) => reify! { x.clone() => Option<T> },
|
||||||
Expr::Variable(x, ..) => reify!(x.3.clone() => Option<T>),
|
Expr::Variable(x, ..) => reify! { x.3.clone() => Option<T> },
|
||||||
Expr::BoolConstant(x, ..) => reify!(*x => Option<T>),
|
Expr::BoolConstant(x, ..) => reify! { *x => Option<T> },
|
||||||
Expr::Unit(..) => reify!(() => Option<T>),
|
Expr::Unit(..) => reify! { () => Option<T> },
|
||||||
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
@ -167,13 +167,13 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
self._call_fn(
|
self._call_fn(
|
||||||
options,
|
|
||||||
scope,
|
scope,
|
||||||
&mut crate::eval::GlobalRuntimeState::new(self),
|
&mut crate::eval::GlobalRuntimeState::new(self),
|
||||||
&mut crate::eval::Caches::new(),
|
&mut crate::eval::Caches::new(),
|
||||||
ast,
|
ast,
|
||||||
name.as_ref(),
|
name.as_ref(),
|
||||||
arg_values.as_mut(),
|
arg_values.as_mut(),
|
||||||
|
options,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/// Register a custom fallible function with the [`Engine`].
|
/// Register a custom fallible function with the [`Engine`].
|
||||||
|
@ -200,7 +200,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Bail out early if the return type needs no cast
|
// Bail out early if the return type needs no cast
|
||||||
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||||
return Ok(reify!(result => T));
|
return Ok(reify! { result => T });
|
||||||
}
|
}
|
||||||
|
|
||||||
let typ = self.map_type_name(result.type_name());
|
let typ = self.map_type_name(result.type_name());
|
||||||
@ -233,34 +233,29 @@ impl Engine {
|
|||||||
ast.resolver().cloned(),
|
ast.resolver().cloned(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let statements = ast.statements();
|
auto_restore!(global => move |g| {
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
{
|
||||||
|
g.embedded_module_resolver = orig_embedded_module_resolver;
|
||||||
|
}
|
||||||
|
|
||||||
if statements.is_empty() {
|
#[cfg(not(feature = "no_function"))]
|
||||||
return Ok(Dynamic::UNIT);
|
g.lib.truncate(orig_lib_len);
|
||||||
}
|
|
||||||
|
|
||||||
let result = self.eval_global_statements(global, caches, scope, statements);
|
g.source = orig_source;
|
||||||
|
});
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
self.eval_global_statements(global, caches, scope, ast.statements())
|
||||||
if self.is_debugger_registered() {
|
.and_then(|r| {
|
||||||
global.debugger_mut().status = crate::eval::DebuggerStatus::Terminate;
|
#[cfg(feature = "debugging")]
|
||||||
let mut this = Dynamic::NULL;
|
if self.is_debugger_registered() {
|
||||||
let node = &crate::ast::Stmt::Noop(Position::NONE);
|
global.debugger_mut().status = crate::eval::DebuggerStatus::Terminate;
|
||||||
|
let mut this_ptr = Dynamic::NULL;
|
||||||
self.run_debugger(global, caches, scope, &mut this, node)?;
|
let node = &crate::ast::Stmt::Noop(Position::NONE);
|
||||||
}
|
self.run_debugger(global, caches, scope, &mut this_ptr, node)?;
|
||||||
|
}
|
||||||
#[cfg(not(feature = "no_module"))]
|
Ok(r)
|
||||||
{
|
})
|
||||||
global.embedded_module_resolver = orig_embedded_module_resolver;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
global.lib.truncate(orig_lib_len);
|
|
||||||
|
|
||||||
global.source = orig_source;
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ use crate::{Dynamic, Engine, EvalContext, Position, RhaiResultOf};
|
|||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
/// Information on a variable definition.
|
/// Information on a variable definition.
|
||||||
|
#[derive(Debug, Hash)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct VarDefInfo<'a> {
|
pub struct VarDefInfo<'a> {
|
||||||
/// Name of the variable to be defined.
|
/// Name of the variable to be defined.
|
||||||
|
@ -22,7 +22,7 @@ bitflags! {
|
|||||||
/// Is looping allowed?
|
/// Is looping allowed?
|
||||||
const LOOPING = 0b_0000_0010_0000;
|
const LOOPING = 0b_0000_0010_0000;
|
||||||
/// Is variables shadowing allowed?
|
/// Is variables shadowing allowed?
|
||||||
const SHADOW = 0b_0000_0100_0000;
|
const SHADOWING = 0b_0000_0100_0000;
|
||||||
/// Strict variables mode?
|
/// Strict variables mode?
|
||||||
const STRICT_VAR = 0b_0000_1000_0000;
|
const STRICT_VAR = 0b_0000_1000_0000;
|
||||||
/// Raise error if an object map property does not exist?
|
/// Raise error if an object map property does not exist?
|
||||||
@ -43,7 +43,7 @@ impl LangOptions {
|
|||||||
| Self::SWITCH_EXPR
|
| Self::SWITCH_EXPR
|
||||||
| Self::STMT_EXPR
|
| Self::STMT_EXPR
|
||||||
| Self::LOOPING
|
| Self::LOOPING
|
||||||
| Self::SHADOW
|
| Self::SHADOWING
|
||||||
| Self::FAST_OPS
|
| Self::FAST_OPS
|
||||||
| {
|
| {
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
@ -148,12 +148,12 @@ impl Engine {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn allow_shadowing(&self) -> bool {
|
pub const fn allow_shadowing(&self) -> bool {
|
||||||
self.options.contains(LangOptions::SHADOW)
|
self.options.contains(LangOptions::SHADOWING)
|
||||||
}
|
}
|
||||||
/// Set whether variables shadowing is allowed.
|
/// Set whether variables shadowing is allowed.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_allow_shadowing(&mut self, enable: bool) -> &mut Self {
|
pub fn set_allow_shadowing(&mut self, enable: bool) -> &mut Self {
|
||||||
self.options.set(LangOptions::SHADOW, enable);
|
self.options.set(LangOptions::SHADOWING, enable);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Is strict variables mode enabled?
|
/// Is strict variables mode enabled?
|
||||||
|
@ -126,24 +126,17 @@ impl Engine {
|
|||||||
global.embedded_module_resolver = ast.resolver().cloned();
|
global.embedded_module_resolver = ast.resolver().cloned();
|
||||||
}
|
}
|
||||||
|
|
||||||
let statements = ast.statements();
|
self.eval_global_statements(global, caches, scope, ast.statements())
|
||||||
|
.and_then(|_| {
|
||||||
let result = if !statements.is_empty() {
|
#[cfg(feature = "debugging")]
|
||||||
self.eval_global_statements(global, caches, scope, statements)
|
if self.is_debugger_registered() {
|
||||||
.map(|_| ())
|
global.debugger_mut().status = crate::eval::DebuggerStatus::Terminate;
|
||||||
} else {
|
let mut this_ptr = crate::Dynamic::NULL;
|
||||||
Ok(())
|
let node = &crate::ast::Stmt::Noop(crate::Position::NONE);
|
||||||
};
|
self.run_debugger(global, caches, scope, &mut this_ptr, node)?;
|
||||||
|
}
|
||||||
#[cfg(feature = "debugging")]
|
Ok(())
|
||||||
if self.is_debugger_registered() {
|
})
|
||||||
global.debugger_mut().status = crate::eval::DebuggerStatus::Terminate;
|
|
||||||
let mut this = crate::Dynamic::NULL;
|
|
||||||
let node = &crate::ast::Stmt::Noop(crate::Position::NONE);
|
|
||||||
self.run_debugger(global, caches, scope, &mut this, node)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#![cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#![cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
|
|
||||||
use super::{Caches, GlobalRuntimeState, Target};
|
use super::{Caches, GlobalRuntimeState, Target};
|
||||||
use crate::ast::{ASTFlags, Expr, OpAssignment};
|
use crate::ast::{ASTFlags, BinaryExpr, Expr, OpAssignment};
|
||||||
use crate::config::hashing::SusLock;
|
use crate::config::hashing::SusLock;
|
||||||
use crate::engine::{FN_IDX_GET, FN_IDX_SET};
|
use crate::engine::{FN_IDX_GET, FN_IDX_SET};
|
||||||
use crate::tokenizer::NO_TOKEN;
|
use crate::tokenizer::NO_TOKEN;
|
||||||
@ -346,7 +346,7 @@ impl Engine {
|
|||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
let chain_type = ChainType::from(expr);
|
let chain_type = ChainType::from(expr);
|
||||||
|
|
||||||
let crate::ast::BinaryExpr { lhs, rhs } = match expr {
|
let BinaryExpr { lhs, rhs } = match expr {
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Index(x, ..) => &**x,
|
Expr::Index(x, ..) => &**x,
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
@ -378,11 +378,9 @@ impl Engine {
|
|||||||
idx_values.push(rhs.get_literal_value().unwrap())
|
idx_values.push(rhs.get_literal_value().unwrap())
|
||||||
}
|
}
|
||||||
// All other patterns - evaluate the arguments chain
|
// All other patterns - evaluate the arguments chain
|
||||||
_ => {
|
_ => self.eval_dot_index_chain_arguments(
|
||||||
self.eval_dot_index_chain_arguments(
|
global, caches, scope, this_ptr, expr, rhs, idx_values,
|
||||||
global, caches, scope, this_ptr, expr, rhs, idx_values,
|
)?,
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match lhs {
|
match lhs {
|
||||||
@ -392,13 +390,19 @@ impl Engine {
|
|||||||
self.run_debugger(global, caches, scope, this_ptr, lhs)?;
|
self.run_debugger(global, caches, scope, this_ptr, lhs)?;
|
||||||
self.track_operation(global, *var_pos)?;
|
self.track_operation(global, *var_pos)?;
|
||||||
|
|
||||||
let mut target = self.search_namespace(global, caches, scope, this_ptr, lhs)?;
|
let target = &mut self.search_namespace(global, caches, scope, this_ptr, lhs)?;
|
||||||
|
let mut this_ptr = Dynamic::NULL;
|
||||||
let obj_ptr = &mut target;
|
|
||||||
let mut this = Dynamic::NULL;
|
|
||||||
|
|
||||||
self.eval_dot_index_chain_raw(
|
self.eval_dot_index_chain_raw(
|
||||||
global, caches, &mut this, lhs, expr, obj_ptr, rhs, idx_values, new_val,
|
global,
|
||||||
|
caches,
|
||||||
|
&mut this_ptr,
|
||||||
|
lhs,
|
||||||
|
expr,
|
||||||
|
target,
|
||||||
|
rhs,
|
||||||
|
idx_values,
|
||||||
|
new_val,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// {expr}.??? = ??? or {expr}[???] = ???
|
// {expr}.??? = ??? or {expr}[???] = ???
|
||||||
@ -456,7 +460,7 @@ impl Engine {
|
|||||||
Expr::Index(x, ..) | Expr::Dot(x, ..)
|
Expr::Index(x, ..) | Expr::Dot(x, ..)
|
||||||
if !parent.options().contains(ASTFlags::BREAK) =>
|
if !parent.options().contains(ASTFlags::BREAK) =>
|
||||||
{
|
{
|
||||||
let crate::ast::BinaryExpr { lhs, rhs, .. } = &**x;
|
let BinaryExpr { lhs, rhs, .. } = &**x;
|
||||||
|
|
||||||
let mut _arg_values = FnArgsVec::new_const();
|
let mut _arg_values = FnArgsVec::new_const();
|
||||||
|
|
||||||
@ -684,17 +688,14 @@ impl Engine {
|
|||||||
let reset =
|
let reset =
|
||||||
self.run_debugger_with_reset(global, caches, scope, this_ptr, rhs)?;
|
self.run_debugger_with_reset(global, caches, scope, this_ptr, rhs)?;
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
auto_restore!(global if reset.is_some() => move |g| g.debugger_mut().reset_status(reset));
|
auto_restore!(global if Some(reset) => move |g| g.debugger_mut().reset_status(reset));
|
||||||
|
|
||||||
let crate::ast::FnCallExpr {
|
let crate::ast::FnCallExpr {
|
||||||
name, hashes, args, ..
|
name, hashes, args, ..
|
||||||
} = &**x;
|
} = &**x;
|
||||||
|
|
||||||
// Truncate the index values upon exit
|
// Truncate the index values upon exit
|
||||||
auto_restore! {
|
auto_restore! { idx_values => truncate; let offset = idx_values.len() - args.len(); }
|
||||||
idx_values => truncate;
|
|
||||||
let offset = idx_values.len() - args.len();
|
|
||||||
}
|
|
||||||
|
|
||||||
let call_args = &mut idx_values[offset..];
|
let call_args = &mut idx_values[offset..];
|
||||||
let arg1_pos = args.get(0).map_or(Position::NONE, Expr::position);
|
let arg1_pos = args.get(0).map_or(Position::NONE, Expr::position);
|
||||||
@ -856,17 +857,14 @@ impl Engine {
|
|||||||
global, caches, scope, this_ptr, _node,
|
global, caches, scope, this_ptr, _node,
|
||||||
)?;
|
)?;
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
auto_restore!(global if reset.is_some() => move |g| g.debugger_mut().reset_status(reset));
|
auto_restore!(global if Some(reset) => move |g| g.debugger_mut().reset_status(reset));
|
||||||
|
|
||||||
let crate::ast::FnCallExpr {
|
let crate::ast::FnCallExpr {
|
||||||
name, hashes, args, ..
|
name, hashes, args, ..
|
||||||
} = &**x;
|
} = &**x;
|
||||||
|
|
||||||
// Truncate the index values upon exit
|
// Truncate the index values upon exit
|
||||||
auto_restore! {
|
auto_restore! { idx_values => truncate; let offset = idx_values.len() - args.len(); }
|
||||||
idx_values => truncate;
|
|
||||||
let offset = idx_values.len() - args.len();
|
|
||||||
}
|
|
||||||
|
|
||||||
let call_args = &mut idx_values[offset..];
|
let call_args = &mut idx_values[offset..];
|
||||||
let arg1_pos = args.get(0).map_or(Position::NONE, Expr::position);
|
let arg1_pos = args.get(0).map_or(Position::NONE, Expr::position);
|
||||||
@ -977,17 +975,14 @@ impl Engine {
|
|||||||
global, caches, scope, this_ptr, _node,
|
global, caches, scope, this_ptr, _node,
|
||||||
)?;
|
)?;
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
auto_restore!(global if reset.is_some() => move |g| g.debugger_mut().reset_status(reset));
|
auto_restore!(global if Some(reset) => move |g| g.debugger_mut().reset_status(reset));
|
||||||
|
|
||||||
let crate::ast::FnCallExpr {
|
let crate::ast::FnCallExpr {
|
||||||
name, hashes, args, ..
|
name, hashes, args, ..
|
||||||
} = &**f;
|
} = &**f;
|
||||||
|
|
||||||
// Truncate the index values upon exit
|
// Truncate the index values upon exit
|
||||||
auto_restore! {
|
auto_restore! { idx_values => truncate; let offset = idx_values.len() - args.len(); }
|
||||||
idx_values => truncate;
|
|
||||||
let offset = idx_values.len() - args.len();
|
|
||||||
}
|
|
||||||
|
|
||||||
let call_args = &mut idx_values[offset..];
|
let call_args = &mut idx_values[offset..];
|
||||||
let pos1 = args.get(0).map_or(Position::NONE, Expr::position);
|
let pos1 = args.get(0).map_or(Position::NONE, Expr::position);
|
||||||
|
@ -314,14 +314,12 @@ impl Debugger {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Override the status of this [`Debugger`] if it is [`Some`] the current status is
|
/// Override the status of this [`Debugger`] if the current status is
|
||||||
/// [`CONTINUE`][DebuggerStatus::CONTINUE].
|
/// [`CONTINUE`][DebuggerStatus::CONTINUE].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn reset_status(&mut self, status: Option<DebuggerStatus>) {
|
pub(crate) fn reset_status(&mut self, status: DebuggerStatus) {
|
||||||
if self.status == DebuggerStatus::CONTINUE {
|
if self.status == DebuggerStatus::CONTINUE {
|
||||||
if let Some(cmd) = status {
|
self.status = status;
|
||||||
self.status = cmd;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Returns the first break-point triggered by a particular [`AST` Node][ASTNode].
|
/// Returns the first break-point triggered by a particular [`AST` Node][ASTNode].
|
||||||
|
@ -152,20 +152,21 @@ impl Engine {
|
|||||||
_ if global.always_search_scope => 0,
|
_ if global.always_search_scope => 0,
|
||||||
|
|
||||||
Expr::Variable(_, Some(i), ..) => i.get() as usize,
|
Expr::Variable(_, Some(i), ..) => i.get() as usize,
|
||||||
// Scripted function with the same name
|
Expr::Variable(v, None, ..) => {
|
||||||
#[cfg(not(feature = "no_function"))]
|
// Scripted function with the same name
|
||||||
Expr::Variable(v, None, ..)
|
#[cfg(not(feature = "no_function"))]
|
||||||
if global
|
if let Some(fn_def) = global.lib.iter().flat_map(|m| m.iter_script_fn()).find_map(
|
||||||
.lib
|
|(_, _, f, _, func)| if f == v.3.as_str() { Some(func) } else { None },
|
||||||
.iter()
|
) {
|
||||||
.flat_map(|m| m.iter_script_fn())
|
let mut fn_ptr =
|
||||||
.any(|(_, _, f, ..)| f == v.3.as_str()) =>
|
crate::FnPtr::new_unchecked(v.3.clone(), crate::StaticVec::new_const());
|
||||||
{
|
fn_ptr.set_fn_def(Some(fn_def.clone()));
|
||||||
let val: Dynamic =
|
let val: Dynamic = fn_ptr.into();
|
||||||
crate::FnPtr::new_unchecked(v.3.as_str(), crate::StaticVec::default()).into();
|
return Ok(val.into());
|
||||||
return Ok(val.into());
|
}
|
||||||
|
|
||||||
|
v.0.map_or(0, NonZeroUsize::get)
|
||||||
}
|
}
|
||||||
Expr::Variable(v, None, ..) => v.0.map_or(0, NonZeroUsize::get),
|
|
||||||
|
|
||||||
_ => unreachable!("Expr::Variable expected but gets {:?}", expr),
|
_ => unreachable!("Expr::Variable expected but gets {:?}", expr),
|
||||||
};
|
};
|
||||||
@ -234,7 +235,7 @@ impl Engine {
|
|||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
let reset = self.run_debugger_with_reset(global, caches, scope, this_ptr, expr)?;
|
let reset = self.run_debugger_with_reset(global, caches, scope, this_ptr, expr)?;
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
auto_restore!(global if reset.is_some() => move |g| g.debugger_mut().reset_status(reset));
|
auto_restore!(global if Some(reset) => move |g| g.debugger_mut().reset_status(reset));
|
||||||
|
|
||||||
self.track_operation(global, expr.position())?;
|
self.track_operation(global, expr.position())?;
|
||||||
|
|
||||||
@ -265,7 +266,7 @@ impl Engine {
|
|||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
let reset = self.run_debugger_with_reset(global, caches, scope, this_ptr, expr)?;
|
let reset = self.run_debugger_with_reset(global, caches, scope, this_ptr, expr)?;
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
auto_restore!(global if reset.is_some() => move |g| g.debugger_mut().reset_status(reset));
|
auto_restore!(global if Some(reset) => move |g| g.debugger_mut().reset_status(reset));
|
||||||
|
|
||||||
self.track_operation(global, expr.position())?;
|
self.track_operation(global, expr.position())?;
|
||||||
|
|
||||||
|
@ -41,10 +41,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Restore scope at end of block if necessary
|
// Restore scope at end of block if necessary
|
||||||
auto_restore! {
|
auto_restore! { scope if restore_orig_state => rewind; let orig_scope_len = scope.len(); }
|
||||||
scope if restore_orig_state => rewind;
|
|
||||||
let orig_scope_len = scope.len();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore global state at end of block if necessary
|
// Restore global state at end of block if necessary
|
||||||
let orig_always_search_scope = global.always_search_scope;
|
let orig_always_search_scope = global.always_search_scope;
|
||||||
@ -208,7 +205,7 @@ impl Engine {
|
|||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
let reset = self.run_debugger_with_reset(global, caches, scope, this_ptr, stmt)?;
|
let reset = self.run_debugger_with_reset(global, caches, scope, this_ptr, stmt)?;
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
auto_restore!(global if reset.is_some() => move |g| g.debugger_mut().reset_status(reset));
|
auto_restore!(global if Some(reset) => move |g| g.debugger_mut().reset_status(reset));
|
||||||
|
|
||||||
// Coded this way for better branch prediction.
|
// Coded this way for better branch prediction.
|
||||||
// Popular branches are lifted out of the `match` statement into their own branches.
|
// Popular branches are lifted out of the `match` statement into their own branches.
|
||||||
@ -519,10 +516,8 @@ impl Engine {
|
|||||||
let iter_func = iter_func.ok_or_else(|| ERR::ErrorFor(expr.start_position()))?;
|
let iter_func = iter_func.ok_or_else(|| ERR::ErrorFor(expr.start_position()))?;
|
||||||
|
|
||||||
// Restore scope at end of statement
|
// Restore scope at end of statement
|
||||||
auto_restore! {
|
auto_restore! { scope => rewind; let orig_scope_len = scope.len(); }
|
||||||
scope => rewind;
|
|
||||||
let orig_scope_len = scope.len();
|
|
||||||
}
|
|
||||||
// Add the loop variables
|
// Add the loop variables
|
||||||
let counter_index = if counter.is_empty() {
|
let counter_index = if counter.is_empty() {
|
||||||
usize::MAX
|
usize::MAX
|
||||||
@ -649,10 +644,7 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Restore scope at end of block
|
// Restore scope at end of block
|
||||||
auto_restore! {
|
auto_restore! { scope if !catch_var.is_empty() => rewind; let orig_scope_len = scope.len(); }
|
||||||
scope if !catch_var.is_empty() => rewind;
|
|
||||||
let orig_scope_len = scope.len();
|
|
||||||
}
|
|
||||||
|
|
||||||
if !catch_var.is_empty() {
|
if !catch_var.is_empty() {
|
||||||
scope.push(catch_var.name.clone(), err_value);
|
scope.push(catch_var.name.clone(), err_value);
|
||||||
@ -882,9 +874,9 @@ impl Engine {
|
|||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
statements: &[Stmt],
|
statements: &[Stmt],
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
let mut this = Dynamic::NULL;
|
let mut this_ptr = Dynamic::NULL;
|
||||||
|
|
||||||
self.eval_stmt_block(global, caches, scope, &mut this, statements, false)
|
self.eval_stmt_block(global, caches, scope, &mut this_ptr, statements, false)
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
ERR::Return(out, ..) => Ok(out),
|
ERR::Return(out, ..) => Ok(out),
|
||||||
ERR::LoopBreak(..) => {
|
ERR::LoopBreak(..) => {
|
||||||
|
222
src/func/call.rs
222
src/func/call.rs
@ -429,7 +429,7 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
if trigger {
|
if trigger {
|
||||||
let scope = &mut Scope::new();
|
let scope = &mut Scope::new();
|
||||||
let mut this = Dynamic::NULL;
|
let mut this_ptr = Dynamic::NULL;
|
||||||
let node = crate::ast::Stmt::Noop(pos);
|
let node = crate::ast::Stmt::Noop(pos);
|
||||||
let node = (&node).into();
|
let node = (&node).into();
|
||||||
let event = match _result {
|
let event = match _result {
|
||||||
@ -438,7 +438,7 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Err(err) =
|
if let Err(err) =
|
||||||
self.run_debugger_raw(global, caches, scope, &mut this, node, event)
|
self.run_debugger_raw(global, caches, scope, &mut this_ptr, node, event)
|
||||||
{
|
{
|
||||||
_result = Err(err);
|
_result = Err(err);
|
||||||
}
|
}
|
||||||
@ -681,9 +681,9 @@ impl Engine {
|
|||||||
|
|
||||||
auto_restore!(args = (_args) if swap => move |a| backup.restore_first_arg(a));
|
auto_restore!(args = (_args) if swap => move |a| backup.restore_first_arg(a));
|
||||||
|
|
||||||
let mut this = Dynamic::NULL;
|
let mut this_ptr = Dynamic::NULL;
|
||||||
|
|
||||||
self.call_script_fn(global, caches, scope, &mut this, func, args, true, pos)
|
self.call_script_fn(global, caches, scope, &mut this_ptr, func, args, true, pos)
|
||||||
}
|
}
|
||||||
.map(|r| (r, false));
|
.map(|r| (r, false));
|
||||||
}
|
}
|
||||||
@ -725,7 +725,7 @@ impl Engine {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
auto_restore!(global if reset.is_some() => move |g| g.debugger_mut().reset_status(reset));
|
auto_restore!(global if Some(reset) => move |g| g.debugger_mut().reset_status(reset));
|
||||||
|
|
||||||
self.eval_expr(global, caches, scope, this_ptr, arg_expr)
|
self.eval_expr(global, caches, scope, this_ptr, arg_expr)
|
||||||
.map(|r| (r, arg_expr.start_position()))
|
.map(|r| (r, arg_expr.start_position()))
|
||||||
@ -747,10 +747,38 @@ impl Engine {
|
|||||||
let is_ref_mut = target.is_ref();
|
let is_ref_mut = target.is_ref();
|
||||||
|
|
||||||
let (result, updated) = match fn_name {
|
let (result, updated) = match fn_name {
|
||||||
|
// Handle fn_ptr.call(...)
|
||||||
KEYWORD_FN_PTR_CALL if target.is_fnptr() => {
|
KEYWORD_FN_PTR_CALL if target.is_fnptr() => {
|
||||||
// FnPtr call
|
|
||||||
let fn_ptr = target.read_lock::<FnPtr>().expect("`FnPtr`");
|
let fn_ptr = target.read_lock::<FnPtr>().expect("`FnPtr`");
|
||||||
|
|
||||||
|
// Arguments are passed as-is, adding the curried arguments
|
||||||
|
let mut curry = FnArgsVec::with_capacity(fn_ptr.curry().len());
|
||||||
|
curry.extend(fn_ptr.curry().iter().cloned());
|
||||||
|
let args = &mut FnArgsVec::with_capacity(curry.len() + call_args.len());
|
||||||
|
args.extend(curry.iter_mut());
|
||||||
|
args.extend(call_args.iter_mut());
|
||||||
|
|
||||||
|
// Linked to scripted function?
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
if let Some(fn_def) = fn_ptr.fn_def() {
|
||||||
|
if fn_def.params.len() == args.len() {
|
||||||
|
let mut this_ptr = Dynamic::NULL;
|
||||||
|
|
||||||
|
return self
|
||||||
|
.call_script_fn(
|
||||||
|
global,
|
||||||
|
caches,
|
||||||
|
&mut Scope::new(),
|
||||||
|
&mut this_ptr,
|
||||||
|
fn_def,
|
||||||
|
args,
|
||||||
|
true,
|
||||||
|
fn_call_pos,
|
||||||
|
)
|
||||||
|
.map(|v| (v, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
let is_anon = fn_ptr.is_anonymous();
|
let is_anon = fn_ptr.is_anonymous();
|
||||||
#[cfg(feature = "no_function")]
|
#[cfg(feature = "no_function")]
|
||||||
@ -759,18 +787,11 @@ impl Engine {
|
|||||||
// Redirect function name
|
// Redirect function name
|
||||||
let fn_name = fn_ptr.fn_name();
|
let fn_name = fn_ptr.fn_name();
|
||||||
// Recalculate hashes
|
// Recalculate hashes
|
||||||
let args_len = call_args.len() + fn_ptr.curry().len();
|
|
||||||
let new_hash = if !is_anon && !is_valid_function_name(fn_name) {
|
let new_hash = if !is_anon && !is_valid_function_name(fn_name) {
|
||||||
FnCallHashes::from_native(calc_fn_hash(None, fn_name, args_len))
|
FnCallHashes::from_native(calc_fn_hash(None, fn_name, args.len()))
|
||||||
} else {
|
} else {
|
||||||
calc_fn_hash(None, fn_name, args_len).into()
|
calc_fn_hash(None, fn_name, args.len()).into()
|
||||||
};
|
};
|
||||||
// Arguments are passed as-is, adding the curried arguments
|
|
||||||
let mut curry = FnArgsVec::with_capacity(fn_ptr.curry().len());
|
|
||||||
curry.extend(fn_ptr.curry().iter().cloned());
|
|
||||||
let mut args = FnArgsVec::with_capacity(curry.len() + call_args.len());
|
|
||||||
args.extend(curry.iter_mut());
|
|
||||||
args.extend(call_args.iter_mut());
|
|
||||||
|
|
||||||
// Map it to name(args) in function-call style
|
// Map it to name(args) in function-call style
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
@ -780,12 +801,14 @@ impl Engine {
|
|||||||
fn_name,
|
fn_name,
|
||||||
NO_TOKEN,
|
NO_TOKEN,
|
||||||
new_hash,
|
new_hash,
|
||||||
&mut args,
|
args,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
fn_call_pos,
|
fn_call_pos,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle obj.call(fn_ptr, ...)
|
||||||
KEYWORD_FN_PTR_CALL => {
|
KEYWORD_FN_PTR_CALL => {
|
||||||
if call_args.is_empty() {
|
if call_args.is_empty() {
|
||||||
let typ = self.map_type_name(target.type_name());
|
let typ = self.map_type_name(target.type_name());
|
||||||
@ -799,32 +822,62 @@ impl Engine {
|
|||||||
let fn_ptr = mem::take(&mut call_args[0]).cast::<FnPtr>();
|
let fn_ptr = mem::take(&mut call_args[0]).cast::<FnPtr>();
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
let is_anon = fn_ptr.is_anonymous();
|
let (fn_name, is_anon, fn_curry, fn_def) = {
|
||||||
|
let is_anon = fn_ptr.is_anonymous();
|
||||||
|
let (fn_name, fn_curry, fn_def) = fn_ptr.take_data();
|
||||||
|
(fn_name, is_anon, fn_curry, fn_def)
|
||||||
|
};
|
||||||
#[cfg(feature = "no_function")]
|
#[cfg(feature = "no_function")]
|
||||||
let is_anon = false;
|
let (fn_name, is_anon, fn_curry) = {
|
||||||
|
let (fn_name, fn_curry) = fn_ptr.take_data();
|
||||||
|
(fn_name, false, fn_curry)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Replace the first argument with the object pointer, adding the curried arguments
|
||||||
call_args = &mut call_args[1..];
|
call_args = &mut call_args[1..];
|
||||||
|
|
||||||
// Redirect function name
|
let mut curry = FnArgsVec::with_capacity(fn_curry.len());
|
||||||
let (fn_name, fn_curry) = fn_ptr.take_data();
|
curry.extend(fn_curry.into_iter());
|
||||||
|
let args = &mut FnArgsVec::with_capacity(curry.len() + call_args.len() + 1);
|
||||||
|
args.extend(curry.iter_mut());
|
||||||
|
args.extend(call_args.iter_mut());
|
||||||
|
|
||||||
|
// Linked to scripted function?
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
if let Some(fn_def) = fn_def {
|
||||||
|
if fn_def.params.len() == args.len() {
|
||||||
|
// Check for data race.
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
ensure_no_data_race(&fn_def.name, args, false)?;
|
||||||
|
|
||||||
|
return self
|
||||||
|
.call_script_fn(
|
||||||
|
global,
|
||||||
|
caches,
|
||||||
|
&mut Scope::new(),
|
||||||
|
target,
|
||||||
|
&fn_def,
|
||||||
|
args,
|
||||||
|
true,
|
||||||
|
fn_call_pos,
|
||||||
|
)
|
||||||
|
.map(|v| (v, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the first argument with the object pointer
|
||||||
|
args.insert(0, target.as_mut());
|
||||||
|
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
let args_len = call_args.len() + fn_curry.len();
|
|
||||||
let new_hash = if !is_anon && !is_valid_function_name(&fn_name) {
|
let new_hash = if !is_anon && !is_valid_function_name(&fn_name) {
|
||||||
FnCallHashes::from_native(calc_fn_hash(None, &fn_name, args_len + 1))
|
FnCallHashes::from_native(calc_fn_hash(None, &fn_name, args.len()))
|
||||||
} else {
|
} else {
|
||||||
FnCallHashes::from_all(
|
FnCallHashes::from_all(
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
calc_fn_hash(None, &fn_name, args_len),
|
calc_fn_hash(None, &fn_name, args.len() - 1),
|
||||||
calc_fn_hash(None, &fn_name, args_len + 1),
|
calc_fn_hash(None, &fn_name, args.len()),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
// Replace the first argument with the object pointer, adding the curried arguments
|
|
||||||
let mut curry = FnArgsVec::with_capacity(fn_curry.len());
|
|
||||||
curry.extend(fn_curry.into_iter());
|
|
||||||
let mut args = FnArgsVec::with_capacity(curry.len() + call_args.len() + 1);
|
|
||||||
args.push(target.as_mut());
|
|
||||||
args.extend(curry.iter_mut());
|
|
||||||
args.extend(call_args.iter_mut());
|
|
||||||
|
|
||||||
// Map it to name(args) in function-call style
|
// Map it to name(args) in function-call style
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
@ -834,7 +887,7 @@ impl Engine {
|
|||||||
&fn_name,
|
&fn_name,
|
||||||
NO_TOKEN,
|
NO_TOKEN,
|
||||||
new_hash,
|
new_hash,
|
||||||
&mut args,
|
args,
|
||||||
is_ref_mut,
|
is_ref_mut,
|
||||||
true,
|
true,
|
||||||
fn_call_pos,
|
fn_call_pos,
|
||||||
@ -846,26 +899,14 @@ impl Engine {
|
|||||||
return Err(self.make_type_mismatch_err::<FnPtr>(typ, fn_call_pos));
|
return Err(self.make_type_mismatch_err::<FnPtr>(typ, fn_call_pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
let fn_ptr = target.read_lock::<FnPtr>().expect("`FnPtr`");
|
let mut fn_ptr = target.read_lock::<FnPtr>().expect("`FnPtr`").clone();
|
||||||
|
|
||||||
// Curry call
|
// Append the new curried arguments to the existing list.
|
||||||
Ok((
|
call_args.iter_mut().map(mem::take).for_each(|value| {
|
||||||
if call_args.is_empty() {
|
fn_ptr.add_curry(value);
|
||||||
fn_ptr.clone()
|
});
|
||||||
} else {
|
|
||||||
FnPtr::new_unchecked(
|
Ok((fn_ptr.into(), false))
|
||||||
fn_ptr.fn_name_raw().clone(),
|
|
||||||
fn_ptr
|
|
||||||
.curry()
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.chain(call_args.iter_mut().map(mem::take))
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
false,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle is_shared()
|
// Handle is_shared()
|
||||||
@ -975,7 +1016,7 @@ impl Engine {
|
|||||||
match name {
|
match name {
|
||||||
_ if op_token != NO_TOKEN => (),
|
_ if op_token != NO_TOKEN => (),
|
||||||
|
|
||||||
// Handle call()
|
// Handle call(fn_ptr, ...)
|
||||||
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();
|
||||||
let (arg_value, arg_pos) =
|
let (arg_value, arg_pos) =
|
||||||
@ -989,13 +1030,48 @@ impl Engine {
|
|||||||
let fn_ptr = arg_value.cast::<FnPtr>();
|
let fn_ptr = arg_value.cast::<FnPtr>();
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
let is_anon = fn_ptr.is_anonymous();
|
let (fn_name, is_anon, fn_curry, fn_def) = {
|
||||||
|
let is_anon = fn_ptr.is_anonymous();
|
||||||
|
let (fn_name, fn_curry, fn_def) = fn_ptr.take_data();
|
||||||
|
(fn_name, is_anon, fn_curry, fn_def)
|
||||||
|
};
|
||||||
#[cfg(feature = "no_function")]
|
#[cfg(feature = "no_function")]
|
||||||
let is_anon = false;
|
let (fn_name, is_anon, fn_curry) = {
|
||||||
|
let (fn_name, fn_curry) = fn_ptr.take_data();
|
||||||
|
(fn_name, false, fn_curry)
|
||||||
|
};
|
||||||
|
|
||||||
let (fn_name, fn_curry) = fn_ptr.take_data();
|
|
||||||
curry.extend(fn_curry.into_iter());
|
curry.extend(fn_curry.into_iter());
|
||||||
|
|
||||||
|
// Linked to scripted function?
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
if let Some(fn_def) = fn_def {
|
||||||
|
if fn_def.params.len() == curry.len() + a_expr.len() {
|
||||||
|
// Evaluate arguments
|
||||||
|
let mut arg_values = curry
|
||||||
|
.into_iter()
|
||||||
|
.map(Ok)
|
||||||
|
.chain(a_expr.iter().map(|expr| -> Result<_, RhaiError> {
|
||||||
|
self.get_arg_value(global, caches, scope, this_ptr, expr)
|
||||||
|
.map(|(v, ..)| v)
|
||||||
|
}))
|
||||||
|
.collect::<RhaiResultOf<FnArgsVec<_>>>()?;
|
||||||
|
let args = &mut arg_values.iter_mut().collect::<FnArgsVec<_>>();
|
||||||
|
let mut this_ptr = Dynamic::NULL;
|
||||||
|
|
||||||
|
return self.call_script_fn(
|
||||||
|
global,
|
||||||
|
caches,
|
||||||
|
&mut Scope::new(),
|
||||||
|
&mut this_ptr,
|
||||||
|
&fn_def,
|
||||||
|
args,
|
||||||
|
true,
|
||||||
|
pos,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Redirect function name
|
// Redirect function name
|
||||||
redirected = fn_name;
|
redirected = fn_name;
|
||||||
name = &redirected;
|
name = &redirected;
|
||||||
@ -1042,16 +1118,16 @@ impl Engine {
|
|||||||
return Err(self.make_type_mismatch_err::<FnPtr>(typ, arg_pos));
|
return Err(self.make_type_mismatch_err::<FnPtr>(typ, arg_pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (name, fn_curry) = arg_value.cast::<FnPtr>().take_data();
|
let mut fn_ptr = arg_value.cast::<FnPtr>();
|
||||||
|
|
||||||
// Append the new curried arguments to the existing list.
|
// Append the new curried arguments to the existing list.
|
||||||
let fn_curry = a_expr.iter().try_fold(fn_curry, |mut curried, expr| {
|
a_expr.iter().try_for_each(|expr| -> Result<_, RhaiError> {
|
||||||
let (value, ..) = self.get_arg_value(global, caches, scope, this_ptr, expr)?;
|
let (value, ..) = self.get_arg_value(global, caches, scope, this_ptr, expr)?;
|
||||||
curried.push(value);
|
fn_ptr.add_curry(value);
|
||||||
Ok::<_, RhaiError>(curried)
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
return Ok(FnPtr::new_unchecked(name, fn_curry).into());
|
return Ok(fn_ptr.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle is_shared()
|
// Handle is_shared()
|
||||||
@ -1248,7 +1324,7 @@ impl Engine {
|
|||||||
pos: Position,
|
pos: Position,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
let mut arg_values = FnArgsVec::with_capacity(args_expr.len());
|
let mut arg_values = FnArgsVec::with_capacity(args_expr.len());
|
||||||
let mut args = FnArgsVec::with_capacity(args_expr.len());
|
let args = &mut FnArgsVec::with_capacity(args_expr.len());
|
||||||
let mut first_arg_value = None;
|
let mut first_arg_value = None;
|
||||||
|
|
||||||
if args_expr.is_empty() {
|
if args_expr.is_empty() {
|
||||||
@ -1366,16 +1442,14 @@ impl Engine {
|
|||||||
match func {
|
match func {
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
Some(f) if f.is_script() => {
|
Some(f) if f.is_script() => {
|
||||||
let fn_def = f.get_script_fn_def().expect("script-defined function");
|
let f = f.get_script_fn_def().expect("script-defined function");
|
||||||
let new_scope = &mut Scope::new();
|
let scope = &mut Scope::new();
|
||||||
let mut this = Dynamic::NULL;
|
let mut this_ptr = Dynamic::NULL;
|
||||||
|
|
||||||
let orig_source = mem::replace(&mut global.source, module.id_raw().cloned());
|
let orig_source = mem::replace(&mut global.source, module.id_raw().cloned());
|
||||||
auto_restore!(global => move |g| g.source = orig_source);
|
auto_restore!(global => move |g| g.source = orig_source);
|
||||||
|
|
||||||
self.call_script_fn(
|
self.call_script_fn(global, caches, scope, &mut this_ptr, f, args, true, pos)
|
||||||
global, caches, new_scope, &mut this, fn_def, &mut args, true, pos,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(f) if f.is_plugin_fn() => {
|
Some(f) if f.is_plugin_fn() => {
|
||||||
@ -1388,7 +1462,7 @@ impl Engine {
|
|||||||
if !f.is_pure() && !args.is_empty() && args[0].is_read_only() {
|
if !f.is_pure() && !args.is_empty() && args[0].is_read_only() {
|
||||||
Err(ERR::ErrorNonPureMethodCallOnConstant(fn_name.to_string(), pos).into())
|
Err(ERR::ErrorNonPureMethodCallOnConstant(fn_name.to_string(), pos).into())
|
||||||
} else {
|
} else {
|
||||||
f.call(context, &mut args)
|
f.call(context, args)
|
||||||
.and_then(|r| self.check_data_size(r, pos))
|
.and_then(|r| self.check_data_size(r, pos))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1400,19 +1474,19 @@ impl Engine {
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
func(context, &mut args).and_then(|r| self.check_data_size(r, pos))
|
func(context, args).and_then(|r| self.check_data_size(r, pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(f) => unreachable!("unknown function type: {:?}", f),
|
Some(f) => unreachable!("unknown function type: {:?}", f),
|
||||||
|
|
||||||
None => {
|
None => {
|
||||||
let sig = if namespace.is_empty() {
|
let sig = if namespace.is_empty() {
|
||||||
self.gen_fn_call_signature(fn_name, &args)
|
self.gen_fn_call_signature(fn_name, args)
|
||||||
} else {
|
} else {
|
||||||
format!(
|
format!(
|
||||||
"{namespace}{}{}",
|
"{namespace}{}{}",
|
||||||
crate::tokenizer::Token::DoubleColon.literal_syntax(),
|
crate::tokenizer::Token::DoubleColon.literal_syntax(),
|
||||||
self.gen_fn_call_signature(fn_name, &args)
|
self.gen_fn_call_signature(fn_name, args)
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1551,14 +1625,14 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Normal function call
|
// Normal function call
|
||||||
let (first_arg, args) = args.split_first().map_or_else(
|
let (first_arg, rest_args) = args.split_first().map_or_else(
|
||||||
|| (None, args.as_ref()),
|
|| (None, args.as_ref()),
|
||||||
|(first, rest)| (Some(first), rest),
|
|(first, rest)| (Some(first), rest),
|
||||||
);
|
);
|
||||||
|
|
||||||
self.make_function_call(
|
self.make_function_call(
|
||||||
global, caches, scope, this_ptr, name, op_token, first_arg, args, *hashes, *capture,
|
global, caches, scope, this_ptr, name, op_token, first_arg, rest_args, *hashes,
|
||||||
pos,
|
*capture, pos,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -271,6 +271,14 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
pub const fn global_runtime_state(&self) -> &GlobalRuntimeState {
|
pub const fn global_runtime_state(&self) -> &GlobalRuntimeState {
|
||||||
self.global
|
self.global
|
||||||
}
|
}
|
||||||
|
/// _(internals)_ The current [`GlobalRuntimeState`], if any.
|
||||||
|
#[cfg(not(feature = "internals"))]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) const fn global_runtime_state(&self) -> &GlobalRuntimeState {
|
||||||
|
self.global
|
||||||
|
}
|
||||||
/// Get an iterator over the namespaces containing definitions of all script-defined functions
|
/// Get an iterator over the namespaces containing definitions of all script-defined functions
|
||||||
/// in reverse order (i.e. parent namespaces are iterated after child namespaces).
|
/// in reverse order (i.e. parent namespaces are iterated after child namespaces).
|
||||||
///
|
///
|
||||||
@ -303,19 +311,20 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
|
|
||||||
let mut args: StaticVec<_> = arg_values.iter_mut().collect();
|
let mut args: StaticVec<_> = arg_values.iter_mut().collect();
|
||||||
|
|
||||||
let result = self._call_fn_raw(fn_name, &mut args, false, false, false)?;
|
self._call_fn_raw(fn_name, &mut args, false, false, false)
|
||||||
|
.and_then(|result| {
|
||||||
|
// Bail out early if the return type needs no cast
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||||
|
return Ok(reify! { result => T });
|
||||||
|
}
|
||||||
|
|
||||||
// Bail out early if the return type needs no cast
|
let typ = self.engine().map_type_name(result.type_name());
|
||||||
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
|
||||||
return Ok(reify!(result => T));
|
|
||||||
}
|
|
||||||
|
|
||||||
let typ = self.engine().map_type_name(result.type_name());
|
result.try_cast().ok_or_else(|| {
|
||||||
|
let t = self.engine().map_type_name(type_name::<T>()).into();
|
||||||
result.try_cast().ok_or_else(|| {
|
ERR::ErrorMismatchOutputType(t, typ.into(), Position::NONE).into()
|
||||||
let t = self.engine().map_type_name(type_name::<T>()).into();
|
})
|
||||||
ERR::ErrorMismatchOutputType(t, typ.into(), Position::NONE).into()
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
/// Call a registered native Rust function inside the call context with the provided arguments.
|
/// Call a registered native Rust function inside the call context with the provided arguments.
|
||||||
///
|
///
|
||||||
@ -333,19 +342,20 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
|
|
||||||
let mut args: StaticVec<_> = arg_values.iter_mut().collect();
|
let mut args: StaticVec<_> = arg_values.iter_mut().collect();
|
||||||
|
|
||||||
let result = self._call_fn_raw(fn_name, &mut args, true, false, false)?;
|
self._call_fn_raw(fn_name, &mut args, true, false, false)
|
||||||
|
.and_then(|result| {
|
||||||
|
// Bail out early if the return type needs no cast
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||||
|
return Ok(reify! { result => T });
|
||||||
|
}
|
||||||
|
|
||||||
// Bail out early if the return type needs no cast
|
let typ = self.engine().map_type_name(result.type_name());
|
||||||
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
|
||||||
return Ok(reify!(result => T));
|
|
||||||
}
|
|
||||||
|
|
||||||
let typ = self.engine().map_type_name(result.type_name());
|
result.try_cast().ok_or_else(|| {
|
||||||
|
let t = self.engine().map_type_name(type_name::<T>()).into();
|
||||||
result.try_cast().ok_or_else(|| {
|
ERR::ErrorMismatchOutputType(t, typ.into(), Position::NONE).into()
|
||||||
let t = self.engine().map_type_name(type_name::<T>()).into();
|
})
|
||||||
ERR::ErrorMismatchOutputType(t, typ.into(), Position::NONE).into()
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
/// Call a function (native Rust or scripted) inside the call context.
|
/// Call a function (native Rust or scripted) inside the call context.
|
||||||
///
|
///
|
||||||
@ -420,15 +430,15 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
is_ref_mut: bool,
|
is_ref_mut: bool,
|
||||||
is_method_call: bool,
|
is_method_call: bool,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
let mut global = &mut self.global.clone();
|
let global = &mut self.global.clone();
|
||||||
|
global.level += 1;
|
||||||
|
|
||||||
let caches = &mut Caches::new();
|
let caches = &mut Caches::new();
|
||||||
|
|
||||||
let fn_name = fn_name.as_ref();
|
let fn_name = fn_name.as_ref();
|
||||||
let op_token = Token::lookup_symbol_from_syntax(fn_name).unwrap_or(NO_TOKEN);
|
let op_token = Token::lookup_symbol_from_syntax(fn_name).unwrap_or(NO_TOKEN);
|
||||||
let args_len = args.len();
|
let args_len = args.len();
|
||||||
|
|
||||||
global.level += 1;
|
|
||||||
|
|
||||||
if native_only {
|
if native_only {
|
||||||
return self
|
return self
|
||||||
.engine()
|
.engine()
|
||||||
|
@ -58,7 +58,7 @@ pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T {
|
|||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<String>() {
|
if TypeId::of::<T>() == TypeId::of::<String>() {
|
||||||
// If T is `String`, data must be `ImmutableString`, so map directly to it
|
// If T is `String`, data must be `ImmutableString`, so map directly to it
|
||||||
return reify!(mem::take(data).into_string().expect("`ImmutableString`") => T);
|
return reify! { mem::take(data).into_string().expect("`ImmutableString`") => T };
|
||||||
}
|
}
|
||||||
|
|
||||||
// We consume the argument and then replace it with () - the argument is not supposed to be used again.
|
// We consume the argument and then replace it with () - the argument is not supposed to be used again.
|
||||||
|
@ -33,7 +33,7 @@ impl Engine {
|
|||||||
rewind_scope: bool,
|
rewind_scope: bool,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
assert!(fn_def.params.len() == args.len());
|
assert_eq!(fn_def.params.len(), args.len());
|
||||||
|
|
||||||
self.track_operation(global, pos)?;
|
self.track_operation(global, pos)?;
|
||||||
|
|
||||||
@ -205,12 +205,12 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// First check script-defined functions
|
// First check script-defined functions
|
||||||
let result = global.lib.iter().any(|m| m.contains_fn(hash_script))
|
let r = global.lib.iter().any(|m| m.contains_fn(hash_script))
|
||||||
// Then check the global namespace and packages
|
// Then check the global namespace and packages
|
||||||
|| self.global_modules.iter().any(|m| m.contains_fn(hash_script));
|
|| self.global_modules.iter().any(|m| m.contains_fn(hash_script));
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
let result = result ||
|
let r = r ||
|
||||||
// Then check imported modules
|
// Then check imported modules
|
||||||
global.contains_qualified_fn(hash_script)
|
global.contains_qualified_fn(hash_script)
|
||||||
// Then check sub-modules
|
// Then check sub-modules
|
||||||
@ -218,11 +218,11 @@ impl Engine {
|
|||||||
m.values().any(|m| m.contains_qualified_fn(hash_script))
|
m.values().any(|m| m.contains_qualified_fn(hash_script))
|
||||||
});
|
});
|
||||||
|
|
||||||
if !result && !cache.filter.is_absent_and_set(hash_script) {
|
if !r && !cache.filter.is_absent_and_set(hash_script) {
|
||||||
// Do not cache "one-hit wonders"
|
// Do not cache "one-hit wonders"
|
||||||
cache.map.insert(hash_script, None);
|
cache.map.insert(hash_script, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
r
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -253,6 +253,17 @@ impl fmt::Debug for Module {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
impl<T: IntoIterator<Item = Shared<crate::ast::ScriptFnDef>>> From<T> for Module {
|
||||||
|
fn from(iter: T) -> Self {
|
||||||
|
let mut module = Self::new();
|
||||||
|
iter.into_iter().for_each(|fn_def| {
|
||||||
|
module.set_script_fn(fn_def);
|
||||||
|
});
|
||||||
|
module
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<M: AsRef<Module>> Add<M> for &Module {
|
impl<M: AsRef<Module>> Add<M> for &Module {
|
||||||
type Output = Module;
|
type Output = Module;
|
||||||
|
|
||||||
@ -2129,7 +2140,11 @@ impl Module {
|
|||||||
|
|
||||||
// Variables with an alias left in the scope become module variables
|
// Variables with an alias left in the scope become module variables
|
||||||
for (_name, value, mut aliases) in scope {
|
for (_name, value, mut aliases) in scope {
|
||||||
// It is an error to export function pointers that refer to encapsulated local functions
|
// It is an error to export function pointers that refer to encapsulated local functions.
|
||||||
|
//
|
||||||
|
// Even if the function pointer already links to a scripted function definition, it may
|
||||||
|
// cross-call other functions inside the module and won't have the full encapsulated
|
||||||
|
// environment available.
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
if let Some(fn_ptr) = value.downcast_ref::<crate::FnPtr>() {
|
if let Some(fn_ptr) = value.downcast_ref::<crate::FnPtr>() {
|
||||||
if ast.iter_fn_def().any(|f| f.name == fn_ptr.fn_name()) {
|
if ast.iter_fn_def().any(|f| f.name == fn_ptr.fn_name()) {
|
||||||
|
@ -24,6 +24,52 @@ def_package! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Make a call to a function pointer.
|
||||||
|
///
|
||||||
|
/// If the function pointer is linked to a scripted function definition, use the appropriate number
|
||||||
|
/// of arguments to call it directly (one version attaches an extra numeric argument).
|
||||||
|
fn make_dual_arity_fn_ptr_call<const N: usize>(
|
||||||
|
fn_name: &str,
|
||||||
|
fn_ptr: &FnPtr,
|
||||||
|
ctx: &NativeCallContext,
|
||||||
|
items: [Dynamic; N],
|
||||||
|
number: usize,
|
||||||
|
) -> RhaiResult {
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
{
|
||||||
|
let arity = fn_ptr.fn_def().map(|f| f.params.len()).unwrap_or(0);
|
||||||
|
|
||||||
|
if arity == N {
|
||||||
|
return fn_ptr.call_raw(&ctx, None, items);
|
||||||
|
} else if arity == N + 1 {
|
||||||
|
let mut items2 = crate::StaticVec::new_const();
|
||||||
|
items2.extend(IntoIterator::into_iter(items));
|
||||||
|
items2.push((number as INT).into());
|
||||||
|
return fn_ptr.call_raw(&ctx, None, items2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn_ptr
|
||||||
|
.call_raw(&ctx, None, items.clone())
|
||||||
|
.or_else(|err| match *err {
|
||||||
|
ERR::ErrorFunctionNotFound(sig, ..) if sig.starts_with(fn_ptr.fn_name()) => {
|
||||||
|
let mut items2 = crate::StaticVec::new_const();
|
||||||
|
items2.extend(IntoIterator::into_iter(items));
|
||||||
|
items2.push((number as INT).into());
|
||||||
|
fn_ptr.call_raw(&ctx, None, items2)
|
||||||
|
}
|
||||||
|
_ => Err(err),
|
||||||
|
})
|
||||||
|
.map_err(|err| {
|
||||||
|
Box::new(ERR::ErrorInFunctionCall(
|
||||||
|
fn_name.to_string(),
|
||||||
|
ctx.source().unwrap_or("").to_string(),
|
||||||
|
err,
|
||||||
|
Position::NONE,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[export_module]
|
#[export_module]
|
||||||
pub mod array_functions {
|
pub mod array_functions {
|
||||||
/// Number of elements in the array.
|
/// Number of elements in the array.
|
||||||
@ -658,26 +704,13 @@ pub mod array_functions {
|
|||||||
let mut ar = Array::with_capacity(array.len());
|
let mut ar = Array::with_capacity(array.len());
|
||||||
|
|
||||||
for (i, item) in array.into_iter().enumerate() {
|
for (i, item) in array.into_iter().enumerate() {
|
||||||
ar.push(
|
ar.push(make_dual_arity_fn_ptr_call(
|
||||||
mapper
|
"map",
|
||||||
.call_raw(&ctx, None, [item.clone()])
|
&mapper,
|
||||||
.or_else(|err| match *err {
|
&ctx,
|
||||||
ERR::ErrorFunctionNotFound(fn_sig, ..)
|
[item],
|
||||||
if fn_sig.starts_with(mapper.fn_name()) =>
|
i,
|
||||||
{
|
)?);
|
||||||
mapper.call_raw(&ctx, None, [item, (i as INT).into()])
|
|
||||||
}
|
|
||||||
_ => Err(err),
|
|
||||||
})
|
|
||||||
.map_err(|err| {
|
|
||||||
Box::new(ERR::ErrorInFunctionCall(
|
|
||||||
"map".to_string(),
|
|
||||||
ctx.source().unwrap_or("").to_string(),
|
|
||||||
err,
|
|
||||||
Position::NONE,
|
|
||||||
))
|
|
||||||
})?,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ar)
|
Ok(ar)
|
||||||
@ -748,24 +781,7 @@ pub mod array_functions {
|
|||||||
let mut ar = Array::new();
|
let mut ar = Array::new();
|
||||||
|
|
||||||
for (i, item) in array.into_iter().enumerate() {
|
for (i, item) in array.into_iter().enumerate() {
|
||||||
if filter
|
if make_dual_arity_fn_ptr_call("filter", &filter, &ctx, [item.clone()], i)?
|
||||||
.call_raw(&ctx, None, [item.clone()])
|
|
||||||
.or_else(|err| match *err {
|
|
||||||
ERR::ErrorFunctionNotFound(fn_sig, ..)
|
|
||||||
if fn_sig.starts_with(filter.fn_name()) =>
|
|
||||||
{
|
|
||||||
filter.call_raw(&ctx, None, [item.clone(), (i as INT).into()])
|
|
||||||
}
|
|
||||||
_ => Err(err),
|
|
||||||
})
|
|
||||||
.map_err(|err| {
|
|
||||||
Box::new(ERR::ErrorInFunctionCall(
|
|
||||||
"filter".to_string(),
|
|
||||||
ctx.source().unwrap_or("").to_string(),
|
|
||||||
err,
|
|
||||||
Position::NONE,
|
|
||||||
))
|
|
||||||
})?
|
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
@ -1058,24 +1074,7 @@ pub mod array_functions {
|
|||||||
let (start, ..) = calc_offset_len(array.len(), start, 0);
|
let (start, ..) = calc_offset_len(array.len(), start, 0);
|
||||||
|
|
||||||
for (i, item) in array.iter().enumerate().skip(start) {
|
for (i, item) in array.iter().enumerate().skip(start) {
|
||||||
if filter
|
if make_dual_arity_fn_ptr_call("index_of", &filter, &ctx, [item.clone()], i)?
|
||||||
.call_raw(&ctx, None, [item.clone()])
|
|
||||||
.or_else(|err| match *err {
|
|
||||||
ERR::ErrorFunctionNotFound(fn_sig, ..)
|
|
||||||
if fn_sig.starts_with(filter.fn_name()) =>
|
|
||||||
{
|
|
||||||
filter.call_raw(&ctx, None, [item.clone(), (i as INT).into()])
|
|
||||||
}
|
|
||||||
_ => Err(err),
|
|
||||||
})
|
|
||||||
.map_err(|err| {
|
|
||||||
Box::new(ERR::ErrorInFunctionCall(
|
|
||||||
"index_of".to_string(),
|
|
||||||
ctx.source().unwrap_or("").to_string(),
|
|
||||||
err,
|
|
||||||
Position::NONE,
|
|
||||||
))
|
|
||||||
})?
|
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
@ -1157,24 +1156,7 @@ pub mod array_functions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (i, item) in array.iter().enumerate() {
|
for (i, item) in array.iter().enumerate() {
|
||||||
if filter
|
if make_dual_arity_fn_ptr_call("some", &filter, &ctx, [item.clone()], i)?
|
||||||
.call_raw(&ctx, None, [item.clone()])
|
|
||||||
.or_else(|err| match *err {
|
|
||||||
ERR::ErrorFunctionNotFound(fn_sig, ..)
|
|
||||||
if fn_sig.starts_with(filter.fn_name()) =>
|
|
||||||
{
|
|
||||||
filter.call_raw(&ctx, None, [item.clone(), (i as INT).into()])
|
|
||||||
}
|
|
||||||
_ => Err(err),
|
|
||||||
})
|
|
||||||
.map_err(|err| {
|
|
||||||
Box::new(ERR::ErrorInFunctionCall(
|
|
||||||
"some".to_string(),
|
|
||||||
ctx.source().unwrap_or("").to_string(),
|
|
||||||
err,
|
|
||||||
Position::NONE,
|
|
||||||
))
|
|
||||||
})?
|
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
@ -1244,24 +1226,7 @@ pub mod array_functions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (i, item) in array.iter().enumerate() {
|
for (i, item) in array.iter().enumerate() {
|
||||||
if !filter
|
if !make_dual_arity_fn_ptr_call("all", &filter, &ctx, [item.clone()], i)?
|
||||||
.call_raw(&ctx, None, [item.clone()])
|
|
||||||
.or_else(|err| match *err {
|
|
||||||
ERR::ErrorFunctionNotFound(fn_sig, ..)
|
|
||||||
if fn_sig.starts_with(filter.fn_name()) =>
|
|
||||||
{
|
|
||||||
filter.call_raw(&ctx, None, [item.clone(), (i as INT).into()])
|
|
||||||
}
|
|
||||||
_ => Err(err),
|
|
||||||
})
|
|
||||||
.map_err(|err| {
|
|
||||||
Box::new(ERR::ErrorInFunctionCall(
|
|
||||||
"all".to_string(),
|
|
||||||
ctx.source().unwrap_or("").to_string(),
|
|
||||||
err,
|
|
||||||
Position::NONE,
|
|
||||||
))
|
|
||||||
})?
|
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
@ -1482,32 +1447,12 @@ pub mod array_functions {
|
|||||||
return Ok(initial);
|
return Ok(initial);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut result = initial;
|
array
|
||||||
|
.iter()
|
||||||
for (i, item) in array.iter().enumerate() {
|
.enumerate()
|
||||||
let item = item.clone();
|
.try_fold(initial, |result, (i, item)| {
|
||||||
|
make_dual_arity_fn_ptr_call("reduce", &reducer, &ctx, [result, item.clone()], i)
|
||||||
result = reducer
|
})
|
||||||
.call_raw(&ctx, None, [result.clone(), item.clone()])
|
|
||||||
.or_else(|err| match *err {
|
|
||||||
ERR::ErrorFunctionNotFound(fn_sig, ..)
|
|
||||||
if fn_sig.starts_with(reducer.fn_name()) =>
|
|
||||||
{
|
|
||||||
reducer.call_raw(&ctx, None, [result, item, (i as INT).into()])
|
|
||||||
}
|
|
||||||
_ => Err(err),
|
|
||||||
})
|
|
||||||
.map_err(|err| {
|
|
||||||
Box::new(ERR::ErrorInFunctionCall(
|
|
||||||
"reduce".to_string(),
|
|
||||||
ctx.source().unwrap_or("").to_string(),
|
|
||||||
err,
|
|
||||||
Position::NONE,
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(result)
|
|
||||||
}
|
}
|
||||||
/// Reduce an array by iterating through all elements while applying a function named by `reducer`.
|
/// Reduce an array by iterating through all elements while applying a function named by `reducer`.
|
||||||
///
|
///
|
||||||
@ -1643,33 +1588,19 @@ pub mod array_functions {
|
|||||||
return Ok(initial);
|
return Ok(initial);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut result = initial;
|
array
|
||||||
let len = array.len();
|
.iter()
|
||||||
|
.rev()
|
||||||
for (i, item) in array.iter().rev().enumerate() {
|
.enumerate()
|
||||||
let item = item.clone();
|
.try_fold(initial, |result, (i, item)| {
|
||||||
|
make_dual_arity_fn_ptr_call(
|
||||||
result = reducer
|
"reduce_rev",
|
||||||
.call_raw(&ctx, None, [result.clone(), item.clone()])
|
&reducer,
|
||||||
.or_else(|err| match *err {
|
&ctx,
|
||||||
ERR::ErrorFunctionNotFound(fn_sig, ..)
|
[result, item.clone()],
|
||||||
if fn_sig.starts_with(reducer.fn_name()) =>
|
array.len() - 1 - i,
|
||||||
{
|
)
|
||||||
reducer.call_raw(&ctx, None, [result, item, ((len - 1 - i) as INT).into()])
|
})
|
||||||
}
|
|
||||||
_ => Err(err),
|
|
||||||
})
|
|
||||||
.map_err(|err| {
|
|
||||||
Box::new(ERR::ErrorInFunctionCall(
|
|
||||||
"reduce_rev".to_string(),
|
|
||||||
ctx.source().unwrap_or("").to_string(),
|
|
||||||
err,
|
|
||||||
Position::NONE,
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(result)
|
|
||||||
}
|
}
|
||||||
/// Reduce an array by iterating through all elements, in _reverse_ order,
|
/// Reduce an array by iterating through all elements, in _reverse_ order,
|
||||||
/// while applying a function named by `reducer`.
|
/// while applying a function named by `reducer`.
|
||||||
@ -1928,24 +1859,7 @@ pub mod array_functions {
|
|||||||
let mut x = 0;
|
let mut x = 0;
|
||||||
|
|
||||||
while x < array.len() {
|
while x < array.len() {
|
||||||
if filter
|
if make_dual_arity_fn_ptr_call("drain", &filter, &ctx, [array[x].clone()], i)?
|
||||||
.call_raw(&ctx, None, [array[x].clone()])
|
|
||||||
.or_else(|err| match *err {
|
|
||||||
ERR::ErrorFunctionNotFound(fn_sig, ..)
|
|
||||||
if fn_sig.starts_with(filter.fn_name()) =>
|
|
||||||
{
|
|
||||||
filter.call_raw(&ctx, None, [array[x].clone(), (i as INT).into()])
|
|
||||||
}
|
|
||||||
_ => Err(err),
|
|
||||||
})
|
|
||||||
.map_err(|err| {
|
|
||||||
Box::new(ERR::ErrorInFunctionCall(
|
|
||||||
"drain".to_string(),
|
|
||||||
ctx.source().unwrap_or("").to_string(),
|
|
||||||
err,
|
|
||||||
Position::NONE,
|
|
||||||
))
|
|
||||||
})?
|
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
@ -2124,24 +2038,7 @@ pub mod array_functions {
|
|||||||
let mut x = 0;
|
let mut x = 0;
|
||||||
|
|
||||||
while x < array.len() {
|
while x < array.len() {
|
||||||
if filter
|
if make_dual_arity_fn_ptr_call("retain", &filter, &ctx, [array[x].clone()], i)?
|
||||||
.call_raw(&ctx, None, [array[x].clone()])
|
|
||||||
.or_else(|err| match *err {
|
|
||||||
ERR::ErrorFunctionNotFound(fn_sig, ..)
|
|
||||||
if fn_sig.starts_with(filter.fn_name()) =>
|
|
||||||
{
|
|
||||||
filter.call_raw(&ctx, None, [array[x].clone(), (i as INT).into()])
|
|
||||||
}
|
|
||||||
_ => Err(err),
|
|
||||||
})
|
|
||||||
.map_err(|err| {
|
|
||||||
Box::new(ERR::ErrorInFunctionCall(
|
|
||||||
"retain".to_string(),
|
|
||||||
ctx.source().unwrap_or("").to_string(),
|
|
||||||
err,
|
|
||||||
Position::NONE,
|
|
||||||
))
|
|
||||||
})?
|
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
|
@ -75,8 +75,7 @@ pub mod blob_functions {
|
|||||||
len: INT,
|
len: INT,
|
||||||
value: INT,
|
value: INT,
|
||||||
) -> RhaiResultOf<Blob> {
|
) -> RhaiResultOf<Blob> {
|
||||||
let len = len.min(MAX_USIZE_INT);
|
let len = len.min(MAX_USIZE_INT).max(0) as usize;
|
||||||
let len = if len < 0 { 0 } else { len as usize };
|
|
||||||
let _ctx = ctx;
|
let _ctx = ctx;
|
||||||
|
|
||||||
// Check if blob will be over max size limit
|
// Check if blob will be over max size limit
|
||||||
|
250
src/parser.rs
250
src/parser.rs
@ -343,14 +343,15 @@ impl ParseSettings {
|
|||||||
/// Create a new `ParseSettings` with one higher expression level.
|
/// Create a new `ParseSettings` with one higher expression level.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn level_up(&self) -> ParseResult<Self> {
|
pub fn level_up(&self) -> ParseResult<Self> {
|
||||||
let level = self.level + 1;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
if self.max_expr_depth > 0 && level > self.max_expr_depth {
|
if self.max_expr_depth > 0 && self.level >= self.max_expr_depth {
|
||||||
return Err(PERR::ExprTooDeep.into_err(self.pos));
|
return Err(PERR::ExprTooDeep.into_err(self.pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self { level, ..*self })
|
Ok(Self {
|
||||||
|
level: self.level + 1,
|
||||||
|
..*self
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -537,50 +538,7 @@ fn parse_var_name(input: &mut TokenStream) -> ParseResult<(SmartString, Position
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a symbol.
|
|
||||||
#[cfg(not(feature = "no_custom_syntax"))]
|
|
||||||
fn parse_symbol(input: &mut TokenStream) -> ParseResult<(SmartString, Position)> {
|
|
||||||
match input.next().expect(NEVER_ENDS) {
|
|
||||||
// Symbol
|
|
||||||
(token, pos) if token.is_standard_symbol() => Ok((token.literal_syntax().into(), pos)),
|
|
||||||
// Reserved symbol
|
|
||||||
(Token::Reserved(s), pos) if !is_valid_identifier(s.as_str()) => Ok((*s, pos)),
|
|
||||||
// Bad symbol
|
|
||||||
(Token::LexError(err), pos) => Err(err.into_err(pos)),
|
|
||||||
// Not a symbol
|
|
||||||
(.., pos) => Err(PERR::MissingSymbol(String::new()).into_err(pos)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
/// Parse `(` expr `)`
|
|
||||||
fn parse_paren_expr(
|
|
||||||
&self,
|
|
||||||
input: &mut TokenStream,
|
|
||||||
state: &mut ParseState,
|
|
||||||
lib: &mut FnLib,
|
|
||||||
settings: ParseSettings,
|
|
||||||
) -> ParseResult<Expr> {
|
|
||||||
// ( ...
|
|
||||||
let mut settings = settings;
|
|
||||||
settings.pos = eat_token(input, Token::LeftParen);
|
|
||||||
|
|
||||||
let expr = self.parse_expr(input, state, lib, settings.level_up()?)?;
|
|
||||||
|
|
||||||
match input.next().expect(NEVER_ENDS) {
|
|
||||||
// ( ... )
|
|
||||||
(Token::RightParen, ..) => Ok(expr),
|
|
||||||
// ( <error>
|
|
||||||
(Token::LexError(err), pos) => Err(err.into_err(pos)),
|
|
||||||
// ( ... ???
|
|
||||||
(.., pos) => Err(PERR::MissingToken(
|
|
||||||
Token::RightParen.into(),
|
|
||||||
"for a matching ( in this expression".into(),
|
|
||||||
)
|
|
||||||
.into_err(pos)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse a function call.
|
/// Parse a function call.
|
||||||
fn parse_fn_call(
|
fn parse_fn_call(
|
||||||
&self,
|
&self,
|
||||||
@ -974,10 +932,7 @@ impl Engine {
|
|||||||
)
|
)
|
||||||
.into_err(*pos))
|
.into_err(*pos))
|
||||||
}
|
}
|
||||||
_ => {
|
_ => array.push(self.parse_expr(input, state, lib, settings.level_up()?)?),
|
||||||
let expr = self.parse_expr(input, state, lib, settings.level_up()?)?;
|
|
||||||
array.push(expr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match input.peek().expect(NEVER_ENDS) {
|
match input.peek().expect(NEVER_ENDS) {
|
||||||
@ -1139,10 +1094,10 @@ impl Engine {
|
|||||||
settings: ParseSettings,
|
settings: ParseSettings,
|
||||||
) -> ParseResult<Stmt> {
|
) -> ParseResult<Stmt> {
|
||||||
// switch ...
|
// switch ...
|
||||||
let mut settings = settings;
|
let mut settings = settings.level_up()?;
|
||||||
settings.pos = eat_token(input, Token::Switch);
|
settings.pos = eat_token(input, Token::Switch);
|
||||||
|
|
||||||
let item = self.parse_expr(input, state, lib, settings.level_up()?)?;
|
let item = self.parse_expr(input, state, lib, settings)?;
|
||||||
|
|
||||||
match input.next().expect(NEVER_ENDS) {
|
match input.next().expect(NEVER_ENDS) {
|
||||||
(Token::LeftBrace, ..) => (),
|
(Token::LeftBrace, ..) => (),
|
||||||
@ -1201,7 +1156,7 @@ impl Engine {
|
|||||||
loop {
|
loop {
|
||||||
let filter = state.expr_filter;
|
let filter = state.expr_filter;
|
||||||
state.expr_filter = |t| t != &Token::Pipe;
|
state.expr_filter = |t| t != &Token::Pipe;
|
||||||
let expr = self.parse_expr(input, state, lib, settings.level_up()?);
|
let expr = self.parse_expr(input, state, lib, settings);
|
||||||
state.expr_filter = filter;
|
state.expr_filter = filter;
|
||||||
|
|
||||||
match expr {
|
match expr {
|
||||||
@ -1219,7 +1174,7 @@ impl Engine {
|
|||||||
let condition = if match_token(input, Token::If).0 {
|
let condition = if match_token(input, Token::If).0 {
|
||||||
ensure_not_statement_expr(input, "a boolean")?;
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
let guard = self
|
let guard = self
|
||||||
.parse_expr(input, state, lib, settings.level_up()?)?
|
.parse_expr(input, state, lib, settings)?
|
||||||
.ensure_bool_expr()?;
|
.ensure_bool_expr()?;
|
||||||
ensure_not_assignment(input)?;
|
ensure_not_assignment(input)?;
|
||||||
guard
|
guard
|
||||||
@ -1244,16 +1199,13 @@ impl Engine {
|
|||||||
|
|
||||||
let (action_expr, need_comma) =
|
let (action_expr, need_comma) =
|
||||||
if !settings.has_flag(ParseSettingFlags::DISALLOW_STATEMENTS_IN_BLOCKS) {
|
if !settings.has_flag(ParseSettingFlags::DISALLOW_STATEMENTS_IN_BLOCKS) {
|
||||||
let stmt = self.parse_stmt(input, state, lib, settings.level_up()?)?;
|
let stmt = self.parse_stmt(input, state, lib, settings)?;
|
||||||
let need_comma = !stmt.is_self_terminated();
|
let need_comma = !stmt.is_self_terminated();
|
||||||
|
|
||||||
let stmt_block: StmtBlock = stmt.into();
|
let stmt_block: StmtBlock = stmt.into();
|
||||||
(Expr::Stmt(stmt_block.into()), need_comma)
|
(Expr::Stmt(stmt_block.into()), need_comma)
|
||||||
} else {
|
} else {
|
||||||
(
|
(self.parse_expr(input, state, lib, settings)?, true)
|
||||||
self.parse_expr(input, state, lib, settings.level_up()?)?,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
let has_condition = !matches!(condition, Expr::BoolConstant(true, ..));
|
let has_condition = !matches!(condition, Expr::BoolConstant(true, ..));
|
||||||
|
|
||||||
@ -1410,7 +1362,26 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ( - grouped expression
|
// ( - grouped expression
|
||||||
Token::LeftParen => self.parse_paren_expr(input, state, lib, settings.level_up()?)?,
|
Token::LeftParen => {
|
||||||
|
settings.pos = eat_token(input, Token::LeftParen);
|
||||||
|
|
||||||
|
let expr = self.parse_expr(input, state, lib, settings.level_up()?)?;
|
||||||
|
|
||||||
|
match input.next().expect(NEVER_ENDS) {
|
||||||
|
// ( ... )
|
||||||
|
(Token::RightParen, ..) => expr,
|
||||||
|
// ( <error>
|
||||||
|
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||||
|
// ( ... ???
|
||||||
|
(.., pos) => {
|
||||||
|
return Err(PERR::MissingToken(
|
||||||
|
Token::RightParen.into(),
|
||||||
|
"for a matching ( in this expression".into(),
|
||||||
|
)
|
||||||
|
.into_err(pos))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If statement is allowed to act as expressions
|
// If statement is allowed to act as expressions
|
||||||
Token::If if settings.has_option(LangOptions::IF_EXPR) => Expr::Stmt(Box::new(
|
Token::If if settings.has_option(LangOptions::IF_EXPR) => Expr::Stmt(Box::new(
|
||||||
@ -1493,7 +1464,7 @@ impl Engine {
|
|||||||
// Restore the strings interner by swapping it back
|
// Restore the strings interner by swapping it back
|
||||||
std::mem::swap(state.interned_strings, new_state.interned_strings);
|
std::mem::swap(state.interned_strings, new_state.interned_strings);
|
||||||
|
|
||||||
let (expr, f) = result?;
|
let (expr, fn_def) = result?;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
new_state
|
new_state
|
||||||
@ -1519,8 +1490,8 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let hash_script = calc_fn_hash(None, &f.name, f.params.len());
|
let hash_script = calc_fn_hash(None, &fn_def.name, fn_def.params.len());
|
||||||
lib.insert(hash_script, f.into());
|
lib.insert(hash_script, fn_def);
|
||||||
|
|
||||||
expr
|
expr
|
||||||
}
|
}
|
||||||
@ -1528,6 +1499,7 @@ impl Engine {
|
|||||||
// Interpolated string
|
// Interpolated string
|
||||||
Token::InterpolatedString(..) => {
|
Token::InterpolatedString(..) => {
|
||||||
let mut segments = StaticVec::<Expr>::new();
|
let mut segments = StaticVec::<Expr>::new();
|
||||||
|
let settings = settings.level_up()?;
|
||||||
|
|
||||||
match input.next().expect(NEVER_ENDS) {
|
match input.next().expect(NEVER_ENDS) {
|
||||||
(Token::InterpolatedString(s), ..) if s.is_empty() => (),
|
(Token::InterpolatedString(s), ..) if s.is_empty() => (),
|
||||||
@ -1540,7 +1512,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let expr = match self.parse_block(input, state, lib, settings.level_up()?)? {
|
let expr = match self.parse_block(input, state, lib, settings)? {
|
||||||
block @ Stmt::Block(..) => Expr::Stmt(Box::new(block.into())),
|
block @ Stmt::Block(..) => Expr::Stmt(Box::new(block.into())),
|
||||||
stmt => unreachable!("Stmt::Block expected but gets {:?}", stmt),
|
stmt => unreachable!("Stmt::Block expected but gets {:?}", stmt),
|
||||||
};
|
};
|
||||||
@ -1784,7 +1756,6 @@ impl Engine {
|
|||||||
|
|
||||||
let (.., ns, _, name) = *x;
|
let (.., ns, _, name) = *x;
|
||||||
settings.pos = pos;
|
settings.pos = pos;
|
||||||
let settings = settings.level_up()?;
|
|
||||||
self.parse_fn_call(input, state, lib, name, no_args, true, ns, settings)?
|
self.parse_fn_call(input, state, lib, name, no_args, true, ns, settings)?
|
||||||
}
|
}
|
||||||
// Function call
|
// Function call
|
||||||
@ -1792,7 +1763,6 @@ impl Engine {
|
|||||||
let (.., ns, _, name) = *x;
|
let (.., ns, _, name) = *x;
|
||||||
let no_args = t == Token::Unit;
|
let no_args = t == Token::Unit;
|
||||||
settings.pos = pos;
|
settings.pos = pos;
|
||||||
let settings = settings.level_up()?;
|
|
||||||
self.parse_fn_call(input, state, lib, name, no_args, false, ns, settings)?
|
self.parse_fn_call(input, state, lib, name, no_args, false, ns, settings)?
|
||||||
}
|
}
|
||||||
// module access
|
// module access
|
||||||
@ -2014,7 +1984,7 @@ impl Engine {
|
|||||||
// <EOF>
|
// <EOF>
|
||||||
Token::EOF => Err(PERR::UnexpectedEOF.into_err(settings.pos)),
|
Token::EOF => Err(PERR::UnexpectedEOF.into_err(settings.pos)),
|
||||||
// All other tokens
|
// All other tokens
|
||||||
_ => self.parse_primary(input, state, lib, false, settings.level_up()?),
|
_ => self.parse_primary(input, state, lib, false, settings),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2123,33 +2093,6 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an operator-assignment expression (if any).
|
|
||||||
fn parse_op_assignment_stmt(
|
|
||||||
&self,
|
|
||||||
input: &mut TokenStream,
|
|
||||||
state: &mut ParseState,
|
|
||||||
lib: &mut FnLib,
|
|
||||||
lhs: Expr,
|
|
||||||
settings: ParseSettings,
|
|
||||||
) -> ParseResult<Stmt> {
|
|
||||||
let (op, pos) = match input.peek().expect(NEVER_ENDS) {
|
|
||||||
// var = ...
|
|
||||||
(Token::Equals, ..) => (NO_TOKEN, eat_token(input, Token::Equals)),
|
|
||||||
// var op= ...
|
|
||||||
(token, ..) if token.is_op_assignment() => {
|
|
||||||
input.next().map(|(op, pos)| (op, pos)).expect(NEVER_ENDS)
|
|
||||||
}
|
|
||||||
// Not op-assignment
|
|
||||||
_ => return Ok(Stmt::Expr(lhs.into())),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut settings = settings;
|
|
||||||
settings.pos = pos;
|
|
||||||
|
|
||||||
let rhs = self.parse_expr(input, state, lib, settings.level_up()?)?;
|
|
||||||
Self::make_assignment_stmt(op, state, lhs, rhs, pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Make a dot expression.
|
/// Make a dot expression.
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
fn make_dot_expr(
|
fn make_dot_expr(
|
||||||
@ -2545,7 +2488,20 @@ impl Engine {
|
|||||||
inputs.push(Expr::Variable((None, ns, 0, name).into(), None, pos));
|
inputs.push(Expr::Variable((None, ns, 0, name).into(), None, pos));
|
||||||
}
|
}
|
||||||
CUSTOM_SYNTAX_MARKER_SYMBOL => {
|
CUSTOM_SYNTAX_MARKER_SYMBOL => {
|
||||||
let (symbol, pos) = parse_symbol(input)?;
|
let (symbol, pos) = match input.next().expect(NEVER_ENDS) {
|
||||||
|
// Standard symbol
|
||||||
|
(token, pos) if token.is_standard_symbol() => {
|
||||||
|
Ok((token.literal_syntax().into(), pos))
|
||||||
|
}
|
||||||
|
// Reserved symbol
|
||||||
|
(Token::Reserved(s), pos) if !is_valid_identifier(s.as_str()) => {
|
||||||
|
Ok((*s, pos))
|
||||||
|
}
|
||||||
|
// Bad symbol
|
||||||
|
(Token::LexError(err), pos) => Err(err.into_err(pos)),
|
||||||
|
// Not a symbol
|
||||||
|
(.., pos) => Err(PERR::MissingSymbol(String::new()).into_err(pos)),
|
||||||
|
}?;
|
||||||
let symbol = state.get_interned_string(symbol);
|
let symbol = state.get_interned_string(symbol);
|
||||||
segments.push(symbol.clone());
|
segments.push(symbol.clone());
|
||||||
tokens.push(state.get_interned_string(CUSTOM_SYNTAX_MARKER_SYMBOL));
|
tokens.push(state.get_interned_string(CUSTOM_SYNTAX_MARKER_SYMBOL));
|
||||||
@ -2677,8 +2633,9 @@ impl Engine {
|
|||||||
|
|
||||||
// Parse expression normally.
|
// Parse expression normally.
|
||||||
let precedence = Precedence::new(1);
|
let precedence = Precedence::new(1);
|
||||||
let lhs = self.parse_unary(input, state, lib, settings.level_up()?)?;
|
let settings = settings.level_up()?;
|
||||||
self.parse_binary_op(input, state, lib, precedence, lhs, settings.level_up()?)
|
let lhs = self.parse_unary(input, state, lib, settings)?;
|
||||||
|
self.parse_binary_op(input, state, lib, precedence, lhs, settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an if statement.
|
/// Parse an if statement.
|
||||||
@ -2690,25 +2647,25 @@ impl Engine {
|
|||||||
settings: ParseSettings,
|
settings: ParseSettings,
|
||||||
) -> ParseResult<Stmt> {
|
) -> ParseResult<Stmt> {
|
||||||
// if ...
|
// if ...
|
||||||
let mut settings = settings;
|
let mut settings = settings.level_up()?;
|
||||||
settings.pos = eat_token(input, Token::If);
|
settings.pos = eat_token(input, Token::If);
|
||||||
|
|
||||||
// if guard { if_body }
|
// if guard { if_body }
|
||||||
ensure_not_statement_expr(input, "a boolean")?;
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
let guard = self
|
let guard = self
|
||||||
.parse_expr(input, state, lib, settings.level_up()?)?
|
.parse_expr(input, state, lib, settings)?
|
||||||
.ensure_bool_expr()?;
|
.ensure_bool_expr()?;
|
||||||
ensure_not_assignment(input)?;
|
ensure_not_assignment(input)?;
|
||||||
let if_body = self.parse_block(input, state, lib, settings.level_up()?)?;
|
let if_body = self.parse_block(input, state, lib, settings)?;
|
||||||
|
|
||||||
// if guard { if_body } else ...
|
// if guard { if_body } else ...
|
||||||
let else_body = if match_token(input, Token::Else).0 {
|
let else_body = if match_token(input, Token::Else).0 {
|
||||||
if let (Token::If, ..) = input.peek().expect(NEVER_ENDS) {
|
if let (Token::If, ..) = input.peek().expect(NEVER_ENDS) {
|
||||||
// if guard { if_body } else if ...
|
// if guard { if_body } else if ...
|
||||||
self.parse_if(input, state, lib, settings.level_up()?)?
|
self.parse_if(input, state, lib, settings)?
|
||||||
} else {
|
} else {
|
||||||
// if guard { if_body } else { else-body }
|
// if guard { if_body } else { else-body }
|
||||||
self.parse_block(input, state, lib, settings.level_up()?)?
|
self.parse_block(input, state, lib, settings)?
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Stmt::Noop(Position::NONE)
|
Stmt::Noop(Position::NONE)
|
||||||
@ -2728,14 +2685,14 @@ impl Engine {
|
|||||||
lib: &mut FnLib,
|
lib: &mut FnLib,
|
||||||
settings: ParseSettings,
|
settings: ParseSettings,
|
||||||
) -> ParseResult<Stmt> {
|
) -> ParseResult<Stmt> {
|
||||||
let mut settings = settings;
|
let mut settings = settings.level_up()?;
|
||||||
|
|
||||||
// while|loops ...
|
// while|loops ...
|
||||||
let (guard, token_pos) = match input.next().expect(NEVER_ENDS) {
|
let (guard, token_pos) = match input.next().expect(NEVER_ENDS) {
|
||||||
(Token::While, pos) => {
|
(Token::While, pos) => {
|
||||||
ensure_not_statement_expr(input, "a boolean")?;
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
let expr = self
|
let expr = self
|
||||||
.parse_expr(input, state, lib, settings.level_up()?)?
|
.parse_expr(input, state, lib, settings)?
|
||||||
.ensure_bool_expr()?;
|
.ensure_bool_expr()?;
|
||||||
ensure_not_assignment(input)?;
|
ensure_not_assignment(input)?;
|
||||||
(expr, pos)
|
(expr, pos)
|
||||||
@ -2746,7 +2703,7 @@ impl Engine {
|
|||||||
settings.pos = token_pos;
|
settings.pos = token_pos;
|
||||||
settings.flags |= ParseSettingFlags::BREAKABLE;
|
settings.flags |= ParseSettingFlags::BREAKABLE;
|
||||||
|
|
||||||
let body = self.parse_block(input, state, lib, settings.level_up()?)?;
|
let body = self.parse_block(input, state, lib, settings)?;
|
||||||
|
|
||||||
Ok(Stmt::While((guard, body.into()).into(), settings.pos))
|
Ok(Stmt::While((guard, body.into()).into(), settings.pos))
|
||||||
}
|
}
|
||||||
@ -2760,7 +2717,7 @@ impl Engine {
|
|||||||
settings: ParseSettings,
|
settings: ParseSettings,
|
||||||
) -> ParseResult<Stmt> {
|
) -> ParseResult<Stmt> {
|
||||||
// do ...
|
// do ...
|
||||||
let mut settings = settings;
|
let mut settings = settings.level_up()?;
|
||||||
let orig_breakable = settings.flags.contains(ParseSettingFlags::BREAKABLE);
|
let orig_breakable = settings.flags.contains(ParseSettingFlags::BREAKABLE);
|
||||||
settings.flags |= ParseSettingFlags::BREAKABLE;
|
settings.flags |= ParseSettingFlags::BREAKABLE;
|
||||||
|
|
||||||
@ -2768,7 +2725,7 @@ impl Engine {
|
|||||||
|
|
||||||
// do { body } [while|until] guard
|
// do { body } [while|until] guard
|
||||||
|
|
||||||
let body = self.parse_block(input, state, lib, settings.level_up()?)?;
|
let body = self.parse_block(input, state, lib, settings)?;
|
||||||
|
|
||||||
let negated = match input.next().expect(NEVER_ENDS) {
|
let negated = match input.next().expect(NEVER_ENDS) {
|
||||||
(Token::While, ..) => ASTFlags::NONE,
|
(Token::While, ..) => ASTFlags::NONE,
|
||||||
@ -2787,7 +2744,7 @@ impl Engine {
|
|||||||
|
|
||||||
ensure_not_statement_expr(input, "a boolean")?;
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
let guard = self
|
let guard = self
|
||||||
.parse_expr(input, state, lib, settings.level_up()?)?
|
.parse_expr(input, state, lib, settings)?
|
||||||
.ensure_bool_expr()?;
|
.ensure_bool_expr()?;
|
||||||
ensure_not_assignment(input)?;
|
ensure_not_assignment(input)?;
|
||||||
|
|
||||||
@ -2803,7 +2760,7 @@ impl Engine {
|
|||||||
settings: ParseSettings,
|
settings: ParseSettings,
|
||||||
) -> ParseResult<Stmt> {
|
) -> ParseResult<Stmt> {
|
||||||
// for ...
|
// for ...
|
||||||
let mut settings = settings;
|
let mut settings = settings.level_up()?;
|
||||||
settings.pos = eat_token(input, Token::For);
|
settings.pos = eat_token(input, Token::For);
|
||||||
|
|
||||||
// for name ...
|
// for name ...
|
||||||
@ -2856,7 +2813,7 @@ impl Engine {
|
|||||||
// for name in expr { body }
|
// for name in expr { body }
|
||||||
ensure_not_statement_expr(input, "a boolean")?;
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
let expr = self
|
let expr = self
|
||||||
.parse_expr(input, state, lib, settings.level_up()?)?
|
.parse_expr(input, state, lib, settings)?
|
||||||
.ensure_iterable()?;
|
.ensure_iterable()?;
|
||||||
|
|
||||||
let counter_var = Ident {
|
let counter_var = Ident {
|
||||||
@ -2883,7 +2840,7 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
settings.flags |= ParseSettingFlags::BREAKABLE;
|
settings.flags |= ParseSettingFlags::BREAKABLE;
|
||||||
let body = self.parse_block(input, state, lib, settings.level_up()?)?;
|
let body = self.parse_block(input, state, lib, settings)?;
|
||||||
|
|
||||||
state.stack.as_deref_mut().unwrap().rewind(prev_stack_len);
|
state.stack.as_deref_mut().unwrap().rewind(prev_stack_len);
|
||||||
|
|
||||||
@ -2933,9 +2890,8 @@ impl Engine {
|
|||||||
will_shadow,
|
will_shadow,
|
||||||
};
|
};
|
||||||
let caches = &mut Caches::new();
|
let caches = &mut Caches::new();
|
||||||
let mut this = Dynamic::NULL;
|
let mut this_ptr = Dynamic::NULL;
|
||||||
|
let context = EvalContext::new(self, global, caches, stack, &mut this_ptr);
|
||||||
let context = EvalContext::new(self, global, caches, stack, &mut this);
|
|
||||||
|
|
||||||
match filter(false, info, context) {
|
match filter(false, info, context) {
|
||||||
Ok(true) => (),
|
Ok(true) => (),
|
||||||
@ -3008,11 +2964,11 @@ impl Engine {
|
|||||||
settings: ParseSettings,
|
settings: ParseSettings,
|
||||||
) -> ParseResult<Stmt> {
|
) -> ParseResult<Stmt> {
|
||||||
// import ...
|
// import ...
|
||||||
let mut settings = settings;
|
let mut settings = settings.level_up()?;
|
||||||
settings.pos = eat_token(input, Token::Import);
|
settings.pos = eat_token(input, Token::Import);
|
||||||
|
|
||||||
// import expr ...
|
// import expr ...
|
||||||
let expr = self.parse_expr(input, state, lib, settings.level_up()?)?;
|
let expr = self.parse_expr(input, state, lib, settings)?;
|
||||||
|
|
||||||
let export = if match_token(input, Token::As).0 {
|
let export = if match_token(input, Token::As).0 {
|
||||||
// import expr as name ...
|
// import expr as name ...
|
||||||
@ -3052,6 +3008,7 @@ impl Engine {
|
|||||||
match input.peek().expect(NEVER_ENDS) {
|
match input.peek().expect(NEVER_ENDS) {
|
||||||
(Token::Let, pos) => {
|
(Token::Let, pos) => {
|
||||||
let pos = *pos;
|
let pos = *pos;
|
||||||
|
let settings = settings.level_up()?;
|
||||||
let mut stmt =
|
let mut stmt =
|
||||||
self.parse_let(input, state, lib, AccessMode::ReadWrite, true, settings)?;
|
self.parse_let(input, state, lib, AccessMode::ReadWrite, true, settings)?;
|
||||||
stmt.set_position(pos);
|
stmt.set_position(pos);
|
||||||
@ -3059,6 +3016,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
(Token::Const, pos) => {
|
(Token::Const, pos) => {
|
||||||
let pos = *pos;
|
let pos = *pos;
|
||||||
|
let settings = settings.level_up()?;
|
||||||
let mut stmt =
|
let mut stmt =
|
||||||
self.parse_let(input, state, lib, AccessMode::ReadOnly, true, settings)?;
|
self.parse_let(input, state, lib, AccessMode::ReadOnly, true, settings)?;
|
||||||
stmt.set_position(pos);
|
stmt.set_position(pos);
|
||||||
@ -3099,7 +3057,7 @@ impl Engine {
|
|||||||
settings: ParseSettings,
|
settings: ParseSettings,
|
||||||
) -> ParseResult<Stmt> {
|
) -> ParseResult<Stmt> {
|
||||||
// Must start with {
|
// Must start with {
|
||||||
let mut settings = settings;
|
let mut settings = settings.level_up()?;
|
||||||
settings.pos = match input.next().expect(NEVER_ENDS) {
|
settings.pos = match input.next().expect(NEVER_ENDS) {
|
||||||
(Token::LeftBrace, pos) => pos,
|
(Token::LeftBrace, pos) => pos,
|
||||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||||
@ -3115,7 +3073,7 @@ impl Engine {
|
|||||||
let mut statements = StaticVec::new_const();
|
let mut statements = StaticVec::new_const();
|
||||||
|
|
||||||
if settings.has_flag(ParseSettingFlags::DISALLOW_STATEMENTS_IN_BLOCKS) {
|
if settings.has_flag(ParseSettingFlags::DISALLOW_STATEMENTS_IN_BLOCKS) {
|
||||||
let stmt = self.parse_expr_stmt(input, state, lib, settings.level_up()?)?;
|
let stmt = self.parse_expr_stmt(input, state, lib, settings)?;
|
||||||
statements.push(stmt);
|
statements.push(stmt);
|
||||||
|
|
||||||
// Must end with }
|
// Must end with }
|
||||||
@ -3153,7 +3111,7 @@ impl Engine {
|
|||||||
// Parse statements inside the block
|
// Parse statements inside the block
|
||||||
settings.flags &= !ParseSettingFlags::GLOBAL_LEVEL;
|
settings.flags &= !ParseSettingFlags::GLOBAL_LEVEL;
|
||||||
|
|
||||||
let stmt = self.parse_stmt(input, state, lib, settings.level_up()?)?;
|
let stmt = self.parse_stmt(input, state, lib, settings)?;
|
||||||
|
|
||||||
if stmt.is_noop() {
|
if stmt.is_noop() {
|
||||||
continue;
|
continue;
|
||||||
@ -3215,9 +3173,24 @@ impl Engine {
|
|||||||
let mut settings = settings;
|
let mut settings = settings;
|
||||||
settings.pos = input.peek().expect(NEVER_ENDS).1;
|
settings.pos = input.peek().expect(NEVER_ENDS).1;
|
||||||
|
|
||||||
let expr = self.parse_expr(input, state, lib, settings.level_up()?)?;
|
let expr = self.parse_expr(input, state, lib, settings)?;
|
||||||
let stmt = self.parse_op_assignment_stmt(input, state, lib, expr, settings.level_up()?)?;
|
|
||||||
Ok(stmt)
|
let (op, pos) = match input.peek().expect(NEVER_ENDS) {
|
||||||
|
// var = ...
|
||||||
|
(Token::Equals, ..) => (NO_TOKEN, eat_token(input, Token::Equals)),
|
||||||
|
// var op= ...
|
||||||
|
(token, ..) if token.is_op_assignment() => {
|
||||||
|
input.next().map(|(op, pos)| (op, pos)).expect(NEVER_ENDS)
|
||||||
|
}
|
||||||
|
// Not op-assignment
|
||||||
|
_ => return Ok(Stmt::Expr(expr.into())),
|
||||||
|
};
|
||||||
|
|
||||||
|
settings.pos = pos;
|
||||||
|
|
||||||
|
let rhs = self.parse_expr(input, state, lib, settings)?;
|
||||||
|
|
||||||
|
Self::make_assignment_stmt(op, state, expr, rhs, pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a single statement.
|
/// Parse a single statement.
|
||||||
@ -3292,6 +3265,7 @@ impl Engine {
|
|||||||
(Token::EOF, pos) => return Ok(Stmt::Noop(*pos)),
|
(Token::EOF, pos) => return Ok(Stmt::Noop(*pos)),
|
||||||
(x, pos) => (x, *pos),
|
(x, pos) => (x, *pos),
|
||||||
};
|
};
|
||||||
|
|
||||||
settings.pos = token_pos;
|
settings.pos = token_pos;
|
||||||
|
|
||||||
match token {
|
match token {
|
||||||
@ -3503,11 +3477,11 @@ impl Engine {
|
|||||||
settings: ParseSettings,
|
settings: ParseSettings,
|
||||||
) -> ParseResult<Stmt> {
|
) -> ParseResult<Stmt> {
|
||||||
// try ...
|
// try ...
|
||||||
let mut settings = settings;
|
let mut settings = settings.level_up()?;
|
||||||
settings.pos = eat_token(input, Token::Try);
|
settings.pos = eat_token(input, Token::Try);
|
||||||
|
|
||||||
// try { try_block }
|
// try { try_block }
|
||||||
let try_block = self.parse_block(input, state, lib, settings.level_up()?)?;
|
let try_block = self.parse_block(input, state, lib, settings)?;
|
||||||
|
|
||||||
// try { try_block } catch
|
// try { try_block } catch
|
||||||
let (matched, catch_pos) = match_token(input, Token::Catch);
|
let (matched, catch_pos) = match_token(input, Token::Catch);
|
||||||
@ -3546,7 +3520,7 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// try { try_block } catch ( var ) { catch_block }
|
// try { try_block } catch ( var ) { catch_block }
|
||||||
let catch_block = self.parse_block(input, state, lib, settings.level_up()?)?;
|
let catch_block = self.parse_block(input, state, lib, settings)?;
|
||||||
|
|
||||||
if !catch_var.is_empty() {
|
if !catch_var.is_empty() {
|
||||||
// Remove the error variable from the stack
|
// Remove the error variable from the stack
|
||||||
@ -3575,7 +3549,7 @@ impl Engine {
|
|||||||
settings: ParseSettings,
|
settings: ParseSettings,
|
||||||
#[cfg(feature = "metadata")] comments: impl IntoIterator<Item = Identifier>,
|
#[cfg(feature = "metadata")] comments: impl IntoIterator<Item = Identifier>,
|
||||||
) -> ParseResult<ScriptFnDef> {
|
) -> ParseResult<ScriptFnDef> {
|
||||||
let settings = settings;
|
let settings = settings.level_up()?;
|
||||||
|
|
||||||
let (token, pos) = input.next().expect(NEVER_ENDS);
|
let (token, pos) = input.next().expect(NEVER_ENDS);
|
||||||
|
|
||||||
@ -3642,7 +3616,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Parse function body
|
// Parse function body
|
||||||
let body = match input.peek().expect(NEVER_ENDS) {
|
let body = match input.peek().expect(NEVER_ENDS) {
|
||||||
(Token::LeftBrace, ..) => self.parse_block(input, state, lib, settings.level_up()?)?,
|
(Token::LeftBrace, ..) => self.parse_block(input, state, lib, settings)?,
|
||||||
(.., pos) => return Err(PERR::FnMissingBody(name.into()).into_err(*pos)),
|
(.., pos) => return Err(PERR::FnMissingBody(name.into()).into_err(*pos)),
|
||||||
}
|
}
|
||||||
.into();
|
.into();
|
||||||
@ -3733,8 +3707,8 @@ impl Engine {
|
|||||||
_parent: &mut ParseState,
|
_parent: &mut ParseState,
|
||||||
lib: &mut FnLib,
|
lib: &mut FnLib,
|
||||||
settings: ParseSettings,
|
settings: ParseSettings,
|
||||||
) -> ParseResult<(Expr, ScriptFnDef)> {
|
) -> ParseResult<(Expr, Shared<ScriptFnDef>)> {
|
||||||
let settings = settings;
|
let settings = settings.level_up()?;
|
||||||
let mut params_list = StaticVec::<ImmutableString>::new_const();
|
let mut params_list = StaticVec::<ImmutableString>::new_const();
|
||||||
|
|
||||||
if input.next().expect(NEVER_ENDS).0 != Token::Or && !match_token(input, Token::Pipe).0 {
|
if input.next().expect(NEVER_ENDS).0 != Token::Or && !match_token(input, Token::Pipe).0 {
|
||||||
@ -3781,7 +3755,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse function body
|
// Parse function body
|
||||||
let body = self.parse_stmt(input, state, lib, settings.level_up()?)?;
|
let body = self.parse_stmt(input, state, lib, settings)?;
|
||||||
|
|
||||||
// External variables may need to be processed in a consistent order,
|
// External variables may need to be processed in a consistent order,
|
||||||
// so extract them into a list.
|
// so extract them into a list.
|
||||||
@ -3812,7 +3786,7 @@ impl Engine {
|
|||||||
let fn_name = state.get_interned_string(make_anonymous_fn(hash));
|
let fn_name = state.get_interned_string(make_anonymous_fn(hash));
|
||||||
|
|
||||||
// Define the function
|
// Define the function
|
||||||
let script = ScriptFnDef {
|
let script = Shared::new(ScriptFnDef {
|
||||||
name: fn_name.clone(),
|
name: fn_name.clone(),
|
||||||
access: crate::FnAccess::Public,
|
access: crate::FnAccess::Public,
|
||||||
params,
|
params,
|
||||||
@ -3822,9 +3796,10 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
comments: Box::default(),
|
comments: Box::default(),
|
||||||
};
|
});
|
||||||
|
|
||||||
let fn_ptr = crate::FnPtr::new_unchecked(fn_name, StaticVec::new_const());
|
let mut fn_ptr = crate::FnPtr::new_unchecked(fn_name, StaticVec::new_const());
|
||||||
|
fn_ptr.set_fn_def(Some(script.clone()));
|
||||||
let expr = Expr::DynamicConstant(Box::new(fn_ptr.into()), settings.pos);
|
let expr = Expr::DynamicConstant(Box::new(fn_ptr.into()), settings.pos);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
@ -3845,8 +3820,6 @@ impl Engine {
|
|||||||
let mut functions = StraightHashMap::default();
|
let mut functions = StraightHashMap::default();
|
||||||
|
|
||||||
let options = self.options & !LangOptions::STMT_EXPR & !LangOptions::LOOP_EXPR;
|
let options = self.options & !LangOptions::STMT_EXPR & !LangOptions::LOOP_EXPR;
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
let options = options & !LangOptions::ANON_FN;
|
|
||||||
|
|
||||||
let mut settings = ParseSettings {
|
let mut settings = ParseSettings {
|
||||||
level: 0,
|
level: 0,
|
||||||
@ -3861,6 +3834,7 @@ impl Engine {
|
|||||||
|
|
||||||
let expr = self.parse_expr(&mut input, state, &mut functions, settings)?;
|
let expr = self.parse_expr(&mut input, state, &mut functions, settings)?;
|
||||||
|
|
||||||
|
#[cfg(feature = "no_function")]
|
||||||
assert!(functions.is_empty());
|
assert!(functions.is_empty());
|
||||||
|
|
||||||
match input.peek().expect(NEVER_ENDS) {
|
match input.peek().expect(NEVER_ENDS) {
|
||||||
@ -3877,7 +3851,7 @@ impl Engine {
|
|||||||
state.scope,
|
state.scope,
|
||||||
statements,
|
statements,
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
StaticVec::new_const(),
|
functions.into_iter().map(|(.., v)| v).collect(),
|
||||||
_optimization_level,
|
_optimization_level,
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -3885,7 +3859,7 @@ impl Engine {
|
|||||||
return Ok(AST::new(
|
return Ok(AST::new(
|
||||||
statements,
|
statements,
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
crate::Module::new(),
|
crate::Module::from(functions.into_iter().map(|(.., v)| v)),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
30
src/reify.rs
30
src/reify.rs
@ -4,12 +4,12 @@
|
|||||||
///
|
///
|
||||||
/// # Syntax
|
/// # Syntax
|
||||||
///
|
///
|
||||||
/// * `reify!(`_variable_ or _expression_`,|`_temp-variable_`: `_type_`|` _code_`,` `||` _fallback_ `)`
|
/// * `reify! { `_variable_ or _expression_` => |`_temp-variable_`: `_type_`|` _code_`,` `||` _fallback_ `)`
|
||||||
/// * `reify!(`_variable_ or _expression_`,|`_temp-variable_`: `_type_`|` _code_ `)`
|
/// * `reify! { `_variable_ or _expression_` => |`_temp-variable_`: `_type_`|` _code_ `)`
|
||||||
/// * `reify!(`_variable_ or _expression_ `=>` `Option<`_type_`>` `)`
|
/// * `reify! { `_variable_ or _expression_ `=>` `Option<`_type_`>` `)`
|
||||||
/// * `reify!(`_variable_ or _expression_ `=>` _type_ `)`
|
/// * `reify! { `_variable_ or _expression_ `=>` _type_ `)`
|
||||||
macro_rules! reify {
|
macro_rules! reify {
|
||||||
($old:ident, |$new:ident : $t:ty| $code:expr, || $fallback:expr) => {{
|
($old:ident => |$new:ident : $t:ty| $code:expr, || $fallback:expr) => {{
|
||||||
#[allow(clippy::redundant_else)]
|
#[allow(clippy::redundant_else)]
|
||||||
if std::any::TypeId::of::<$t>() == std::any::Any::type_id(&$old) {
|
if std::any::TypeId::of::<$t>() == std::any::Any::type_id(&$old) {
|
||||||
// SAFETY: This is safe because we already checked to make sure the two types
|
// SAFETY: This is safe because we already checked to make sure the two types
|
||||||
@ -20,29 +20,29 @@ macro_rules! reify {
|
|||||||
$fallback
|
$fallback
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
($old:expr, |$new:ident : $t:ty| $code:expr, || $fallback:expr) => {{
|
($old:expr => |$new:ident : $t:ty| $code:expr, || $fallback:expr) => {{
|
||||||
let old = $old;
|
let old = $old;
|
||||||
reify!(old, |$new: $t| $code, || $fallback)
|
reify! { old => |$new: $t| $code, || $fallback }
|
||||||
}};
|
}};
|
||||||
|
|
||||||
($old:ident, |$new:ident : $t:ty| $code:expr) => {
|
($old:ident => |$new:ident : $t:ty| $code:expr) => {
|
||||||
reify!($old, |$new: $t| $code, || ())
|
reify! { $old => |$new: $t| $code, || () }
|
||||||
};
|
};
|
||||||
($old:expr, |$new:ident : $t:ty| $code:expr) => {
|
($old:expr => |$new:ident : $t:ty| $code:expr) => {
|
||||||
reify!($old, |$new: $t| $code, || ())
|
reify! { $old => |$new: $t| $code, || () }
|
||||||
};
|
};
|
||||||
|
|
||||||
($old:ident => Option<$t:ty>) => {
|
($old:ident => Option<$t:ty>) => {
|
||||||
reify!($old, |v: $t| Some(v), || None)
|
reify! { $old => |v: $t| Some(v), || None }
|
||||||
};
|
};
|
||||||
($old:expr => Option<$t:ty>) => {
|
($old:expr => Option<$t:ty>) => {
|
||||||
reify!($old, |v: $t| Some(v), || None)
|
reify! { $old => |v: $t| Some(v), || None }
|
||||||
};
|
};
|
||||||
|
|
||||||
($old:ident => $t:ty) => {
|
($old:ident => $t:ty) => {
|
||||||
reify!($old, |v: $t| v, || unreachable!())
|
reify! { $old => |v: $t| v, || unreachable!() }
|
||||||
};
|
};
|
||||||
($old:expr => $t:ty) => {
|
($old:expr => $t:ty) => {
|
||||||
reify!($old, |v: $t| v, || unreachable!())
|
reify! { $old => |v: $t| v, || unreachable!() }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,14 @@ fn check_struct_sizes() {
|
|||||||
#[cfg(target_pointer_width = "64")]
|
#[cfg(target_pointer_width = "64")]
|
||||||
{
|
{
|
||||||
assert_eq!(size_of::<Scope>(), 536);
|
assert_eq!(size_of::<Scope>(), 536);
|
||||||
assert_eq!(size_of::<FnPtr>(), 64);
|
assert_eq!(
|
||||||
|
size_of::<FnPtr>(),
|
||||||
|
if cfg!(feature = "no_function") {
|
||||||
|
64
|
||||||
|
} else {
|
||||||
|
72
|
||||||
|
}
|
||||||
|
);
|
||||||
assert_eq!(size_of::<LexError>(), 56);
|
assert_eq!(size_of::<LexError>(), 56);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
size_of::<ParseError>(),
|
size_of::<ParseError>(),
|
||||||
|
@ -1083,38 +1083,35 @@ impl Dynamic {
|
|||||||
pub fn from<T: Variant + Clone>(value: T) -> Self {
|
pub fn from<T: Variant + Clone>(value: T) -> Self {
|
||||||
// Coded this way in order to maximally leverage potentials for dead-code removal.
|
// Coded this way in order to maximally leverage potentials for dead-code removal.
|
||||||
|
|
||||||
reify!(value, |v: Self| return v);
|
reify! { value => |v: Self| return v }
|
||||||
reify!(value, |v: INT| return v.into());
|
reify! { value => |v: INT| return v.into() }
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
reify!(value, |v: crate::FLOAT| return v.into());
|
reify! { value => |v: crate::FLOAT| return v.into() }
|
||||||
|
|
||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
reify!(value, |v: rust_decimal::Decimal| return v.into());
|
reify! { value => |v: rust_decimal::Decimal| return v.into() }
|
||||||
|
|
||||||
reify!(value, |v: bool| return v.into());
|
reify! { value => |v: bool| return v.into() }
|
||||||
reify!(value, |v: char| return v.into());
|
reify! { value => |v: char| return v.into() }
|
||||||
reify!(value, |v: ImmutableString| return v.into());
|
reify! { value => |v: ImmutableString| return v.into() }
|
||||||
reify!(value, |v: String| return v.into());
|
reify! { value => |v: String| return v.into() }
|
||||||
reify!(value, |v: &str| return v.into());
|
reify! { value => |v: &str| return v.into() }
|
||||||
reify!(value, |v: ()| return v.into());
|
reify! { value => |v: ()| return v.into() }
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
reify!(value, |v: crate::Array| return v.into());
|
reify! { value => |v: crate::Array| return v.into() }
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
reify!(value, |v: crate::Blob| {
|
// don't use blob.into() because it'll be converted into an Array
|
||||||
// don't use blob.into() because it'll be converted into an Array
|
reify! { value => |v: crate::Blob| return Self::from_blob(v) }
|
||||||
return Self::from_blob(v);
|
|
||||||
});
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
reify!(value, |v: crate::Map| return v.into());
|
reify! { value => |v: crate::Map| return v.into() }
|
||||||
reify!(value, |v: FnPtr| return v.into());
|
reify! { value => |v: FnPtr| return v.into() }
|
||||||
|
|
||||||
#[cfg(not(feature = "no_time"))]
|
#[cfg(not(feature = "no_time"))]
|
||||||
reify!(value, |v: Instant| return v.into());
|
reify! { value => |v: Instant| return v.into() }
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
reify!(value, |v: crate::Shared<crate::Locked<Self>>| return v
|
reify! { value => |v: crate::Shared<crate::Locked<Self>>| return v.into() }
|
||||||
.into());
|
|
||||||
|
|
||||||
Self(Union::Variant(
|
Self(Union::Variant(
|
||||||
Box::new(Box::new(value)),
|
Box::new(Box::new(value)),
|
||||||
@ -1183,89 +1180,89 @@ impl Dynamic {
|
|||||||
self.flatten_in_place();
|
self.flatten_in_place();
|
||||||
|
|
||||||
if TypeId::of::<T>() == TypeId::of::<Self>() {
|
if TypeId::of::<T>() == TypeId::of::<Self>() {
|
||||||
return Some(reify!(self => T));
|
return Some(reify! { self => T });
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<()>() {
|
if TypeId::of::<T>() == TypeId::of::<()>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Unit(..) => Some(reify!(() => T)),
|
Union::Unit(..) => Some(reify! { () => T }),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Int(n, ..) => Some(reify!(n => T)),
|
Union::Int(n, ..) => Some(reify! { n => T }),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<crate::FLOAT>() {
|
if TypeId::of::<T>() == TypeId::of::<crate::FLOAT>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Float(v, ..) => Some(reify!(*v => T)),
|
Union::Float(v, ..) => Some(reify! { *v => T }),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
if TypeId::of::<T>() == TypeId::of::<rust_decimal::Decimal>() {
|
if TypeId::of::<T>() == TypeId::of::<rust_decimal::Decimal>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Decimal(v, ..) => Some(reify!(*v => T)),
|
Union::Decimal(v, ..) => Some(reify! { *v => T }),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<bool>() {
|
if TypeId::of::<T>() == TypeId::of::<bool>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Bool(b, ..) => Some(reify!(b => T)),
|
Union::Bool(b, ..) => Some(reify! { b => T }),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
|
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Str(s, ..) => Some(reify!(s => T)),
|
Union::Str(s, ..) => Some(reify! { s => T }),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<String>() {
|
if TypeId::of::<T>() == TypeId::of::<String>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Str(s, ..) => Some(reify!(s.to_string() => T)),
|
Union::Str(s, ..) => Some(reify! { s.to_string() => T }),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<char>() {
|
if TypeId::of::<T>() == TypeId::of::<char>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Char(c, ..) => Some(reify!(c => T)),
|
Union::Char(c, ..) => Some(reify! { c => T }),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<crate::Array>() {
|
if TypeId::of::<T>() == TypeId::of::<crate::Array>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Array(a, ..) => Some(reify!(*a => T)),
|
Union::Array(a, ..) => Some(reify! { *a => T }),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<crate::Blob>() {
|
if TypeId::of::<T>() == TypeId::of::<crate::Blob>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Blob(b, ..) => Some(reify!(*b => T)),
|
Union::Blob(b, ..) => Some(reify! { *b => T }),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<crate::Map>() {
|
if TypeId::of::<T>() == TypeId::of::<crate::Map>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Map(m, ..) => Some(reify!(*m => T)),
|
Union::Map(m, ..) => Some(reify! { *m => T }),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<FnPtr>() {
|
if TypeId::of::<T>() == TypeId::of::<FnPtr>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::FnPtr(f, ..) => Some(reify!(*f => T)),
|
Union::FnPtr(f, ..) => Some(reify! { *f => T }),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_time"))]
|
#[cfg(not(feature = "no_time"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<Instant>() {
|
if TypeId::of::<T>() == TypeId::of::<Instant>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::TimeStamp(t, ..) => Some(reify!(*t => T)),
|
Union::TimeStamp(t, ..) => Some(reify! { *t => T }),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -1305,7 +1302,7 @@ impl Dynamic {
|
|||||||
pub fn cast<T: Any + Clone>(self) -> T {
|
pub fn cast<T: Any + Clone>(self) -> T {
|
||||||
// Bail out early if the return type needs no cast
|
// Bail out early if the return type needs no cast
|
||||||
if TypeId::of::<T>() == TypeId::of::<Self>() {
|
if TypeId::of::<T>() == TypeId::of::<Self>() {
|
||||||
return reify!(self => T);
|
return reify! { self => T };
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
@ -2039,7 +2036,7 @@ impl Dynamic {
|
|||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
Union::Blob(b, ..) if TypeId::of::<T>() == TypeId::of::<u8>() => {
|
Union::Blob(b, ..) if TypeId::of::<T>() == TypeId::of::<u8>() => {
|
||||||
Ok(reify!(*b => Vec<T>))
|
Ok(reify! { *b => Vec<T> })
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(ref cell, ..) => {
|
Union::Shared(ref cell, ..) => {
|
||||||
@ -2062,7 +2059,7 @@ impl Dynamic {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
Union::Blob(ref b, ..) if TypeId::of::<T>() == TypeId::of::<u8>() => {
|
Union::Blob(ref b, ..) if TypeId::of::<T>() == TypeId::of::<u8>() => {
|
||||||
Ok(reify!(b.clone() => Vec<T>))
|
Ok(reify! { b.clone() => Vec<T> })
|
||||||
}
|
}
|
||||||
_ => Err(cell.type_name()),
|
_ => Err(cell.type_name()),
|
||||||
}
|
}
|
||||||
|
@ -4,23 +4,42 @@ use crate::eval::GlobalRuntimeState;
|
|||||||
use crate::tokenizer::is_valid_function_name;
|
use crate::tokenizer::is_valid_function_name;
|
||||||
use crate::types::dynamic::Variant;
|
use crate::types::dynamic::Variant;
|
||||||
use crate::{
|
use crate::{
|
||||||
Dynamic, Engine, FuncArgs, ImmutableString, NativeCallContext, Position, RhaiError, RhaiResult,
|
Dynamic, Engine, FnArgsVec, FuncArgs, ImmutableString, NativeCallContext, Position, RhaiError,
|
||||||
RhaiResultOf, StaticVec, AST, ERR,
|
RhaiResult, RhaiResultOf, StaticVec, AST, ERR,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{
|
use std::{
|
||||||
any::{type_name, TypeId},
|
any::{type_name, TypeId},
|
||||||
convert::{TryFrom, TryInto},
|
convert::{TryFrom, TryInto},
|
||||||
fmt, mem,
|
fmt,
|
||||||
|
hash::Hash,
|
||||||
|
mem,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A general function pointer, which may carry additional (i.e. curried) argument values
|
/// A general function pointer, which may carry additional (i.e. curried) argument values
|
||||||
/// to be passed onto a function during a call.
|
/// to be passed onto a function during a call.
|
||||||
#[derive(Clone, Hash)]
|
#[derive(Clone)]
|
||||||
pub struct FnPtr {
|
pub struct FnPtr {
|
||||||
name: ImmutableString,
|
name: ImmutableString,
|
||||||
curry: StaticVec<Dynamic>,
|
curry: StaticVec<Dynamic>,
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
fn_def: Option<crate::Shared<crate::ast::ScriptFnDef>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for FnPtr {
|
||||||
|
#[inline(always)]
|
||||||
|
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.name.hash(state);
|
||||||
|
self.curry.hash(state);
|
||||||
|
|
||||||
|
// Hash the linked [`ScriptFnDef`][crate::ast::ScriptFnDef] by hashing its shared pointer.
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
self.fn_def
|
||||||
|
.as_ref()
|
||||||
|
.map(|f| crate::Shared::as_ptr(f))
|
||||||
|
.hash(state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for FnPtr {
|
impl fmt::Debug for FnPtr {
|
||||||
@ -56,6 +75,8 @@ impl FnPtr {
|
|||||||
Self {
|
Self {
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
curry,
|
curry,
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
fn_def: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get the name of the function.
|
/// Get the name of the function.
|
||||||
@ -71,6 +92,20 @@ impl FnPtr {
|
|||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
/// Get the underlying data of the function pointer.
|
/// Get the underlying data of the function pointer.
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub(crate) fn take_data(
|
||||||
|
self,
|
||||||
|
) -> (
|
||||||
|
ImmutableString,
|
||||||
|
StaticVec<Dynamic>,
|
||||||
|
Option<crate::Shared<crate::ast::ScriptFnDef>>,
|
||||||
|
) {
|
||||||
|
(self.name, self.curry, self.fn_def)
|
||||||
|
}
|
||||||
|
/// Get the underlying data of the function pointer.
|
||||||
|
#[cfg(feature = "no_function")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn take_data(self) -> (ImmutableString, StaticVec<Dynamic>) {
|
pub(crate) fn take_data(self) -> (ImmutableString, StaticVec<Dynamic>) {
|
||||||
@ -158,18 +193,18 @@ impl FnPtr {
|
|||||||
|
|
||||||
let ctx = (engine, self.fn_name(), None, &*global, Position::NONE).into();
|
let ctx = (engine, self.fn_name(), None, &*global, Position::NONE).into();
|
||||||
|
|
||||||
let result = self.call_raw(&ctx, None, arg_values)?;
|
self.call_raw(&ctx, None, arg_values).and_then(|result| {
|
||||||
|
// Bail out early if the return type needs no cast
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||||
|
return Ok(reify! { result => T });
|
||||||
|
}
|
||||||
|
|
||||||
// Bail out early if the return type needs no cast
|
let typ = engine.map_type_name(result.type_name());
|
||||||
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
|
||||||
return Ok(reify!(result => T));
|
|
||||||
}
|
|
||||||
|
|
||||||
let typ = engine.map_type_name(result.type_name());
|
result.try_cast().ok_or_else(|| {
|
||||||
|
let t = engine.map_type_name(type_name::<T>()).into();
|
||||||
result.try_cast().ok_or_else(|| {
|
ERR::ErrorMismatchOutputType(t, typ.into(), Position::NONE).into()
|
||||||
let t = engine.map_type_name(type_name::<T>()).into();
|
})
|
||||||
ERR::ErrorMismatchOutputType(t, typ.into(), Position::NONE).into()
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/// Call the function pointer with curried arguments (if any).
|
/// Call the function pointer with curried arguments (if any).
|
||||||
@ -187,18 +222,18 @@ impl FnPtr {
|
|||||||
let mut arg_values = crate::StaticVec::new_const();
|
let mut arg_values = crate::StaticVec::new_const();
|
||||||
args.parse(&mut arg_values);
|
args.parse(&mut arg_values);
|
||||||
|
|
||||||
let result = self.call_raw(context, None, arg_values)?;
|
self.call_raw(context, None, arg_values).and_then(|result| {
|
||||||
|
// Bail out early if the return type needs no cast
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||||
|
return Ok(reify! { result => T });
|
||||||
|
}
|
||||||
|
|
||||||
// Bail out early if the return type needs no cast
|
let typ = context.engine().map_type_name(result.type_name());
|
||||||
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
|
||||||
return Ok(reify!(result => T));
|
|
||||||
}
|
|
||||||
|
|
||||||
let typ = context.engine().map_type_name(result.type_name());
|
result.try_cast().ok_or_else(|| {
|
||||||
|
let t = context.engine().map_type_name(type_name::<T>()).into();
|
||||||
result.try_cast().ok_or_else(|| {
|
ERR::ErrorMismatchOutputType(t, typ.into(), Position::NONE).into()
|
||||||
let t = context.engine().map_type_name(type_name::<T>()).into();
|
})
|
||||||
ERR::ErrorMismatchOutputType(t, typ.into(), Position::NONE).into()
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/// Call the function pointer with curried arguments (if any).
|
/// Call the function pointer with curried arguments (if any).
|
||||||
@ -231,21 +266,61 @@ impl FnPtr {
|
|||||||
let mut args_data;
|
let mut args_data;
|
||||||
|
|
||||||
if self.is_curried() {
|
if self.is_curried() {
|
||||||
args_data = StaticVec::with_capacity(self.curry().len() + arg_values.len());
|
args_data = FnArgsVec::with_capacity(self.curry().len() + arg_values.len());
|
||||||
args_data.extend(self.curry().iter().cloned());
|
args_data.extend(self.curry().iter().cloned());
|
||||||
args_data.extend(arg_values.iter_mut().map(mem::take));
|
args_data.extend(arg_values.iter_mut().map(mem::take));
|
||||||
arg_values = &mut *args_data;
|
arg_values = &mut *args_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
let is_method = this_ptr.is_some();
|
let args = &mut StaticVec::with_capacity(arg_values.len() + 1);
|
||||||
|
|
||||||
let mut args = StaticVec::with_capacity(arg_values.len() + 1);
|
|
||||||
if let Some(obj) = this_ptr {
|
|
||||||
args.push(obj);
|
|
||||||
}
|
|
||||||
args.extend(arg_values.iter_mut());
|
args.extend(arg_values.iter_mut());
|
||||||
|
|
||||||
context.call_fn_raw(self.fn_name(), is_method, is_method, &mut args)
|
// Linked to scripted function?
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
if let Some(fn_def) = self.fn_def() {
|
||||||
|
if fn_def.params.len() == args.len() {
|
||||||
|
let global = &mut context.global_runtime_state().clone();
|
||||||
|
global.level += 1;
|
||||||
|
|
||||||
|
let caches = &mut crate::eval::Caches::new();
|
||||||
|
let mut null_ptr = Dynamic::NULL;
|
||||||
|
|
||||||
|
return context.engine().call_script_fn(
|
||||||
|
global,
|
||||||
|
caches,
|
||||||
|
&mut crate::Scope::new(),
|
||||||
|
this_ptr.unwrap_or(&mut null_ptr),
|
||||||
|
&fn_def,
|
||||||
|
args,
|
||||||
|
true,
|
||||||
|
context.position(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_method = this_ptr.is_some();
|
||||||
|
|
||||||
|
if let Some(obj) = this_ptr {
|
||||||
|
args.insert(0, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.call_fn_raw(self.fn_name(), is_method, is_method, args)
|
||||||
|
}
|
||||||
|
/// Get a reference to the linked [`ScriptFnDef`][crate::ast::ScriptFnDef].
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub(crate) fn fn_def(&self) -> Option<&crate::Shared<crate::ast::ScriptFnDef>> {
|
||||||
|
self.fn_def.as_ref()
|
||||||
|
}
|
||||||
|
/// Set a reference to the linked [`ScriptFnDef`][crate::ast::ScriptFnDef].
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn set_fn_def(
|
||||||
|
&mut self,
|
||||||
|
value: Option<impl Into<crate::Shared<crate::ast::ScriptFnDef>>>,
|
||||||
|
) {
|
||||||
|
self.fn_def = value.map(Into::into);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,9 +339,25 @@ impl TryFrom<ImmutableString> for FnPtr {
|
|||||||
Ok(Self {
|
Ok(Self {
|
||||||
name: value,
|
name: value,
|
||||||
curry: StaticVec::new_const(),
|
curry: StaticVec::new_const(),
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
fn_def: None,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(ERR::ErrorFunctionNotFound(value.to_string(), Position::NONE).into())
|
Err(ERR::ErrorFunctionNotFound(value.to_string(), Position::NONE).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
impl<T: Into<crate::Shared<crate::ast::ScriptFnDef>>> From<T> for FnPtr {
|
||||||
|
#[inline(always)]
|
||||||
|
fn from(value: T) -> Self {
|
||||||
|
let fn_def = value.into();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
name: fn_def.name.clone(),
|
||||||
|
curry: StaticVec::new_const(),
|
||||||
|
fn_def: Some(fn_def),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -92,14 +92,7 @@ impl StringsInterner {
|
|||||||
|
|
||||||
let result = match self.cache.entry(hash) {
|
let result = match self.cache.entry(hash) {
|
||||||
Entry::Occupied(e) => return e.get().clone(),
|
Entry::Occupied(e) => return e.get().clone(),
|
||||||
Entry::Vacant(e) => {
|
Entry::Vacant(e) => e.insert(mapper(text)).clone(),
|
||||||
let value = mapper(text);
|
|
||||||
|
|
||||||
if value.strong_count() > 1 {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
e.insert(value).clone()
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Throttle the cache upon exit
|
// Throttle the cache upon exit
|
||||||
|
@ -10,7 +10,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// A location (line number + character position) in the input script.
|
/// A location (line number + character position) in the input script.
|
||||||
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
|
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, Default)]
|
||||||
pub struct Position;
|
pub struct Position;
|
||||||
|
|
||||||
impl Position {
|
impl Position {
|
||||||
@ -81,14 +81,6 @@ impl Position {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Position {
|
|
||||||
#[inline(always)]
|
|
||||||
#[must_use]
|
|
||||||
fn default() -> Self {
|
|
||||||
Self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Position {
|
impl fmt::Display for Position {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "none")
|
write!(f, "none")
|
||||||
@ -119,17 +111,9 @@ impl AddAssign for Position {
|
|||||||
|
|
||||||
/// _(internals)_ A span consisting of a starting and an ending [positions][Position].
|
/// _(internals)_ A span consisting of a starting and an ending [positions][Position].
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
|
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, Default)]
|
||||||
pub struct Span;
|
pub struct Span;
|
||||||
|
|
||||||
impl Default for Span {
|
|
||||||
#[inline(always)]
|
|
||||||
#[must_use]
|
|
||||||
fn default() -> Self {
|
|
||||||
Self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Span {
|
impl Span {
|
||||||
/// Empty [`Span`].
|
/// Empty [`Span`].
|
||||||
pub const NONE: Self = Self;
|
pub const NONE: Self = Self;
|
||||||
|
@ -36,6 +36,18 @@ macro_rules! auto_restore {
|
|||||||
($var:ident = $value:expr => $restore:expr) => {
|
($var:ident = $value:expr => $restore:expr) => {
|
||||||
let $var = &mut *crate::types::RestoreOnDrop::lock($value, $restore);
|
let $var = &mut *crate::types::RestoreOnDrop::lock($value, $restore);
|
||||||
};
|
};
|
||||||
|
($var:ident if Some($guard:ident) => $restore:expr) => {
|
||||||
|
auto_restore!($var = ($var) if Some($guard) => $restore);
|
||||||
|
};
|
||||||
|
($var:ident = ( $value:expr ) if Some($guard:ident) => $restore:expr) => {
|
||||||
|
let mut __rx__;
|
||||||
|
let $var = if let Some($guard) = $guard {
|
||||||
|
__rx__ = crate::types::RestoreOnDrop::lock($value, $restore);
|
||||||
|
&mut *__rx__
|
||||||
|
} else {
|
||||||
|
&mut *$value
|
||||||
|
};
|
||||||
|
};
|
||||||
($var:ident if $guard:expr => $restore:expr) => {
|
($var:ident if $guard:expr => $restore:expr) => {
|
||||||
auto_restore!($var = ($var) if $guard => $restore);
|
auto_restore!($var = ($var) if $guard => $restore);
|
||||||
};
|
};
|
||||||
|
@ -16,6 +16,24 @@ fn test_expressions() -> Result<(), Box<EvalAltResult>> {
|
|||||||
engine.eval_expression_with_scope::<INT>(&mut scope, "if x > 0 { 42 } else { 123 }")?,
|
engine.eval_expression_with_scope::<INT>(&mut scope, "if x > 0 { 42 } else { 123 }")?,
|
||||||
42
|
42
|
||||||
);
|
);
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
{
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval_expression_with_scope::<INT>(
|
||||||
|
&mut scope,
|
||||||
|
"[1, 2, 3, 4].map(|x| x * x).reduce(|a, v| a + v, 0)"
|
||||||
|
)?,
|
||||||
|
30
|
||||||
|
);
|
||||||
|
assert!(engine
|
||||||
|
.eval_expression_with_scope::<INT>(
|
||||||
|
&mut scope,
|
||||||
|
"[1, 2, 3, 4].map(|x| { let r = 2; x * r }).reduce(|a, v| a + v, 0)"
|
||||||
|
)
|
||||||
|
.is_err());
|
||||||
|
}
|
||||||
assert!(engine
|
assert!(engine
|
||||||
.eval_expression_with_scope::<INT>(&mut scope, "if x > 0 { let y = 42; y } else { 123 }")
|
.eval_expression_with_scope::<INT>(&mut scope, "if x > 0 { let y = 42; y } else { 123 }")
|
||||||
.is_err());
|
.is_err());
|
||||||
|
@ -55,7 +55,8 @@ fn test_stack_overflow_parsing() -> Result<(), Box<EvalAltResult>> {
|
|||||||
"
|
"
|
||||||
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
||||||
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
||||||
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
||||||
|
1
|
||||||
",
|
",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -67,7 +68,7 @@ fn test_stack_overflow_parsing() -> Result<(), Box<EvalAltResult>> {
|
|||||||
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
||||||
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
||||||
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
||||||
1
|
1 + 2
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
.expect_err("should error")
|
.expect_err("should error")
|
||||||
@ -92,7 +93,7 @@ fn test_stack_overflow_parsing() -> Result<(), Box<EvalAltResult>> {
|
|||||||
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
||||||
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
||||||
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
||||||
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9
|
||||||
",
|
",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -109,7 +110,7 @@ fn test_stack_overflow_parsing() -> Result<(), Box<EvalAltResult>> {
|
|||||||
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
||||||
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
||||||
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 +
|
||||||
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9
|
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
.expect_err("should error")
|
.expect_err("should error")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user