commit
c941bbf123
@ -7,11 +7,13 @@ Version 0.19.14
|
|||||||
Bug fixes
|
Bug fixes
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
* Panic when passing a shared string into a registered function as `&str` argument is fixed.
|
||||||
|
* Panic when calling `switch` statements on custom types is fixed.
|
||||||
|
* Potential overflow panics in `range(from, to, step)` is fixed.
|
||||||
|
* `&mut String` parameters in registered functions no longer panic when passed a string.
|
||||||
|
* Some expressions involving shared variables now work properly, for example `x in shared_value`, `return shared_value`, `obj.field = shared_value` etc. Previously, the resultant value is still shared which is counter-intuitive.
|
||||||
* Errors in native Rust functions now contain the correct function call positions.
|
* Errors in native Rust functions now contain the correct function call positions.
|
||||||
* Fixed error types in `EvalAltResult::ErrorMismatchDataType` which were swapped.
|
* Fixed error types in `EvalAltResult::ErrorMismatchDataType` which were swapped.
|
||||||
* Some expressions involving shared variables now work properly, for example `x in shared_value`, `return shared_value`, `obj.field = shared_value` etc. Previously, the resultant value is still shared which is counter-intuitive.
|
|
||||||
* Potential overflow panics in `range(from, to, step)` is fixed.
|
|
||||||
* `switch` statements no longer panic on custom types.
|
|
||||||
|
|
||||||
Breaking changes
|
Breaking changes
|
||||||
----------------
|
----------------
|
||||||
@ -36,6 +38,7 @@ Enhancements
|
|||||||
* More information is provided to the error variable captured by the `catch` statement in an _object map_.
|
* More information is provided to the error variable captured by the `catch` statement in an _object map_.
|
||||||
* Previously, `private` functions in an `AST` cannot be called with `call_fn` etc. This is inconvenient when trying to call a function inside a script which also serves as a loadable module exporting part (but not all) of the functions. Now, all functions (`private` or not) can be called in an `AST`. The `private` keyword is relegated to preventing a function from being exported.
|
* Previously, `private` functions in an `AST` cannot be called with `call_fn` etc. This is inconvenient when trying to call a function inside a script which also serves as a loadable module exporting part (but not all) of the functions. Now, all functions (`private` or not) can be called in an `AST`. The `private` keyword is relegated to preventing a function from being exported.
|
||||||
* `Dynamic::as_unit` just for completeness sake.
|
* `Dynamic::as_unit` just for completeness sake.
|
||||||
|
* `bytes` method added for strings to get length quickly (if the string is ASCII-only).
|
||||||
|
|
||||||
|
|
||||||
Version 0.19.13
|
Version 0.19.13
|
||||||
|
115
src/ast.rs
115
src/ast.rs
@ -8,7 +8,7 @@ use crate::stdlib::{
|
|||||||
boxed::Box,
|
boxed::Box,
|
||||||
fmt,
|
fmt,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
num::{NonZeroU64, NonZeroUsize},
|
num::NonZeroUsize,
|
||||||
ops::{Add, AddAssign},
|
ops::{Add, AddAssign},
|
||||||
string::String,
|
string::String,
|
||||||
vec,
|
vec,
|
||||||
@ -89,6 +89,7 @@ impl fmt::Display for ScriptFnDef {
|
|||||||
/// A type containing the metadata of a script-defined function.
|
/// A type containing the metadata of a script-defined function.
|
||||||
///
|
///
|
||||||
/// Created by [`AST::iter_functions`].
|
/// Created by [`AST::iter_functions`].
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
|
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
|
||||||
pub struct ScriptFnMetadata<'a> {
|
pub struct ScriptFnMetadata<'a> {
|
||||||
/// Function doc-comments (if any).
|
/// Function doc-comments (if any).
|
||||||
@ -108,6 +109,7 @@ pub struct ScriptFnMetadata<'a> {
|
|||||||
pub params: Vec<&'a str>,
|
pub params: Vec<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
impl fmt::Display for ScriptFnMetadata<'_> {
|
impl fmt::Display for ScriptFnMetadata<'_> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
@ -124,6 +126,7 @@ impl fmt::Display for ScriptFnMetadata<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
impl<'a> Into<ScriptFnMetadata<'a>> for &'a ScriptFnDef {
|
impl<'a> Into<ScriptFnMetadata<'a>> for &'a ScriptFnDef {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn into(self) -> ScriptFnMetadata<'a> {
|
fn into(self) -> ScriptFnMetadata<'a> {
|
||||||
@ -823,7 +826,7 @@ pub enum Stmt {
|
|||||||
Position,
|
Position,
|
||||||
),
|
),
|
||||||
/// `while` expr `{` stmt `}`
|
/// `while` expr `{` stmt `}`
|
||||||
While(Expr, Box<Stmt>, Position),
|
While(Option<Expr>, Box<Stmt>, Position),
|
||||||
/// `do` `{` stmt `}` `while`|`until` expr
|
/// `do` `{` stmt `}` `while`|`until` expr
|
||||||
Do(Box<Stmt>, Expr, bool, Position),
|
Do(Box<Stmt>, Expr, bool, Position),
|
||||||
/// `for` id `in` expr `{` stmt `}`
|
/// `for` id `in` expr `{` stmt `}`
|
||||||
@ -833,7 +836,7 @@ pub enum Stmt {
|
|||||||
/// \[`export`\] `const` id `=` expr
|
/// \[`export`\] `const` id `=` expr
|
||||||
Const(Box<Ident>, Option<Expr>, bool, Position),
|
Const(Box<Ident>, Option<Expr>, bool, Position),
|
||||||
/// expr op`=` expr
|
/// expr op`=` expr
|
||||||
Assignment(Box<(Expr, Cow<'static, str>, Expr)>, Position),
|
Assignment(Box<(Expr, Expr, Option<OpAssignment>)>, Position),
|
||||||
/// `{` stmt`;` ... `}`
|
/// `{` stmt`;` ... `}`
|
||||||
Block(Vec<Stmt>, Position),
|
Block(Vec<Stmt>, Position),
|
||||||
/// `try` `{` stmt; ... `}` `catch` `(` var `)` `{` stmt; ... `}`
|
/// `try` `{` stmt; ... `}` `catch` `(` var `)` `{` stmt; ... `}`
|
||||||
@ -981,9 +984,10 @@ impl Stmt {
|
|||||||
&& x.0.values().all(Stmt::is_pure)
|
&& x.0.values().all(Stmt::is_pure)
|
||||||
&& x.1.as_ref().map(Stmt::is_pure).unwrap_or(true)
|
&& x.1.as_ref().map(Stmt::is_pure).unwrap_or(true)
|
||||||
}
|
}
|
||||||
Self::While(condition, block, _) | Self::Do(block, condition, _, _) => {
|
Self::While(Some(condition), block, _) | Self::Do(block, condition, _, _) => {
|
||||||
condition.is_pure() && block.is_pure()
|
condition.is_pure() && block.is_pure()
|
||||||
}
|
}
|
||||||
|
Self::While(None, block, _) => block.is_pure(),
|
||||||
Self::For(iterable, x, _) => iterable.is_pure() && x.1.is_pure(),
|
Self::For(iterable, x, _) => iterable.is_pure() && x.1.is_pure(),
|
||||||
Self::Let(_, _, _, _) | Self::Const(_, _, _, _) | Self::Assignment(_, _) => false,
|
Self::Let(_, _, _, _) | Self::Const(_, _, _, _) | Self::Assignment(_, _) => false,
|
||||||
Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()),
|
Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()),
|
||||||
@ -1021,17 +1025,18 @@ impl Stmt {
|
|||||||
s.walk(path, on_node);
|
s.walk(path, on_node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::While(e, s, _) | Self::Do(s, e, _, _) => {
|
Self::While(Some(e), s, _) | Self::Do(s, e, _, _) => {
|
||||||
e.walk(path, on_node);
|
e.walk(path, on_node);
|
||||||
s.walk(path, on_node);
|
s.walk(path, on_node);
|
||||||
}
|
}
|
||||||
|
Self::While(None, s, _) => s.walk(path, on_node),
|
||||||
Self::For(e, x, _) => {
|
Self::For(e, x, _) => {
|
||||||
e.walk(path, on_node);
|
e.walk(path, on_node);
|
||||||
x.1.walk(path, on_node);
|
x.1.walk(path, on_node);
|
||||||
}
|
}
|
||||||
Self::Assignment(x, _) => {
|
Self::Assignment(x, _) => {
|
||||||
x.0.walk(path, on_node);
|
x.0.walk(path, on_node);
|
||||||
x.2.walk(path, on_node);
|
x.1.walk(path, on_node);
|
||||||
}
|
}
|
||||||
Self::Block(x, _) => x.iter().for_each(|s| s.walk(path, on_node)),
|
Self::Block(x, _) => x.iter().for_each(|s| s.walk(path, on_node)),
|
||||||
Self::TryCatch(x, _, _) => {
|
Self::TryCatch(x, _, _) => {
|
||||||
@ -1078,6 +1083,79 @@ pub struct BinaryExpr {
|
|||||||
pub rhs: Expr,
|
pub rhs: Expr,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// _(INTERNALS)_ An op-assignment operator.
|
||||||
|
/// Exported under the `internals` feature only.
|
||||||
|
///
|
||||||
|
/// # Volatile Data Structure
|
||||||
|
///
|
||||||
|
/// This type is volatile and may change.
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub struct OpAssignment {
|
||||||
|
pub hash_op_assign: u64,
|
||||||
|
pub hash_op: u64,
|
||||||
|
pub op: Cow<'static, str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// _(INTERNALS)_ An set of function call hashes.
|
||||||
|
/// Exported under the `internals` feature only.
|
||||||
|
///
|
||||||
|
/// # Volatile Data Structure
|
||||||
|
///
|
||||||
|
/// This type is volatile and may change.
|
||||||
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default)]
|
||||||
|
pub struct FnHash {
|
||||||
|
/// Pre-calculated hash for a script-defined function ([`None`] if native functions only).
|
||||||
|
script: Option<u64>,
|
||||||
|
/// Pre-calculated hash for a native Rust function with no parameter types.
|
||||||
|
native: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FnHash {
|
||||||
|
/// Create a [`FnHash`] with only the native Rust hash.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn from_native(hash: u64) -> Self {
|
||||||
|
Self {
|
||||||
|
script: None,
|
||||||
|
native: hash,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Create a [`FnHash`] with both native Rust and script function hashes set to the same value.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn from_script(hash: u64) -> Self {
|
||||||
|
Self {
|
||||||
|
script: Some(hash),
|
||||||
|
native: hash,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Create a [`FnHash`] with both native Rust and script function hashes.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn from_script_and_native(script: u64, native: u64) -> Self {
|
||||||
|
Self {
|
||||||
|
script: Some(script),
|
||||||
|
native,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Is this [`FnHash`] native Rust only?
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn is_native_only(&self) -> bool {
|
||||||
|
self.script.is_none()
|
||||||
|
}
|
||||||
|
/// Get the script function hash from this [`FnHash`].
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the [`FnHash`] is native Rust only.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn script_hash(&self) -> u64 {
|
||||||
|
self.script.unwrap()
|
||||||
|
}
|
||||||
|
/// Get the naive Rust function hash from this [`FnHash`].
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn native_hash(&self) -> u64 {
|
||||||
|
self.native
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// _(INTERNALS)_ A function call.
|
/// _(INTERNALS)_ A function call.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
@ -1086,19 +1164,18 @@ pub struct BinaryExpr {
|
|||||||
/// This type is volatile and may change.
|
/// This type is volatile and may change.
|
||||||
#[derive(Debug, Clone, Default, Hash)]
|
#[derive(Debug, Clone, Default, Hash)]
|
||||||
pub struct FnCallExpr {
|
pub struct FnCallExpr {
|
||||||
/// Pre-calculated hash for a script-defined function of the same name and number of parameters.
|
/// Pre-calculated hash.
|
||||||
/// None if native Rust only.
|
pub hash: FnHash,
|
||||||
pub hash_script: Option<NonZeroU64>,
|
|
||||||
/// Does this function call capture the parent scope?
|
/// Does this function call capture the parent scope?
|
||||||
pub capture: bool,
|
pub capture: bool,
|
||||||
|
/// List of function call arguments.
|
||||||
|
pub args: StaticVec<Expr>,
|
||||||
/// Namespace of the function, if any. Boxed because it occurs rarely.
|
/// Namespace of the function, if any. Boxed because it occurs rarely.
|
||||||
pub namespace: Option<NamespaceRef>,
|
pub namespace: Option<NamespaceRef>,
|
||||||
/// Function name.
|
/// Function name.
|
||||||
/// Use [`Cow<'static, str>`][Cow] because a lot of operators (e.g. `==`, `>=`) are implemented as
|
/// Use [`Cow<'static, str>`][Cow] because a lot of operators (e.g. `==`, `>=`) are implemented as
|
||||||
/// function calls and the function names are predictable, so no need to allocate a new [`String`].
|
/// function calls and the function names are predictable, so no need to allocate a new [`String`].
|
||||||
pub name: Cow<'static, str>,
|
pub name: Cow<'static, str>,
|
||||||
/// List of function call arguments.
|
|
||||||
pub args: StaticVec<Expr>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type that wraps a [`FLOAT`] and implements [`Hash`].
|
/// A type that wraps a [`FLOAT`] and implements [`Hash`].
|
||||||
@ -1220,15 +1297,9 @@ pub enum Expr {
|
|||||||
/// ()
|
/// ()
|
||||||
Unit(Position),
|
Unit(Position),
|
||||||
/// Variable access - (optional index, optional (hash, modules), variable name)
|
/// Variable access - (optional index, optional (hash, modules), variable name)
|
||||||
Variable(
|
Variable(Box<(Option<NonZeroUsize>, Option<(u64, NamespaceRef)>, Ident)>),
|
||||||
Box<(
|
/// Property access - (getter, hash, setter, hash, prop)
|
||||||
Option<NonZeroUsize>,
|
Property(Box<(ImmutableString, u64, ImmutableString, u64, Ident)>),
|
||||||
Option<(NonZeroU64, NamespaceRef)>,
|
|
||||||
Ident,
|
|
||||||
)>,
|
|
||||||
),
|
|
||||||
/// Property access - (getter, setter), prop
|
|
||||||
Property(Box<(ImmutableString, ImmutableString, Ident)>),
|
|
||||||
/// { [statement][Stmt] }
|
/// { [statement][Stmt] }
|
||||||
Stmt(Box<StaticVec<Stmt>>, Position),
|
Stmt(Box<StaticVec<Stmt>>, Position),
|
||||||
/// func `(` expr `,` ... `)`
|
/// func `(` expr `,` ... `)`
|
||||||
@ -1321,7 +1392,7 @@ impl Expr {
|
|||||||
Self::FnPointer(_, pos) => *pos,
|
Self::FnPointer(_, pos) => *pos,
|
||||||
Self::Array(_, pos) => *pos,
|
Self::Array(_, pos) => *pos,
|
||||||
Self::Map(_, pos) => *pos,
|
Self::Map(_, pos) => *pos,
|
||||||
Self::Property(x) => (x.2).pos,
|
Self::Property(x) => (x.4).pos,
|
||||||
Self::Stmt(_, pos) => *pos,
|
Self::Stmt(_, pos) => *pos,
|
||||||
Self::Variable(x) => (x.2).pos,
|
Self::Variable(x) => (x.2).pos,
|
||||||
Self::FnCall(_, pos) => *pos,
|
Self::FnCall(_, pos) => *pos,
|
||||||
@ -1350,7 +1421,7 @@ impl Expr {
|
|||||||
Self::Array(_, pos) => *pos = new_pos,
|
Self::Array(_, pos) => *pos = new_pos,
|
||||||
Self::Map(_, pos) => *pos = new_pos,
|
Self::Map(_, pos) => *pos = new_pos,
|
||||||
Self::Variable(x) => (x.2).pos = new_pos,
|
Self::Variable(x) => (x.2).pos = new_pos,
|
||||||
Self::Property(x) => (x.2).pos = new_pos,
|
Self::Property(x) => (x.4).pos = new_pos,
|
||||||
Self::Stmt(_, pos) => *pos = new_pos,
|
Self::Stmt(_, pos) => *pos = new_pos,
|
||||||
Self::FnCall(_, pos) => *pos = new_pos,
|
Self::FnCall(_, pos) => *pos = new_pos,
|
||||||
Self::And(_, pos) | Self::Or(_, pos) | Self::In(_, pos) => *pos = new_pos,
|
Self::And(_, pos) | Self::Or(_, pos) | Self::In(_, pos) => *pos = new_pos,
|
||||||
|
126
src/dynamic.rs
126
src/dynamic.rs
@ -284,11 +284,13 @@ impl Dynamic {
|
|||||||
/// Always [`false`] under the `no_closure` feature.
|
/// Always [`false`] under the `no_closure` feature.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_shared(&self) -> bool {
|
pub fn is_shared(&self) -> bool {
|
||||||
match self.0 {
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(_, _) => true,
|
match self.0 {
|
||||||
_ => false,
|
Union::Shared(_, _) => return true,
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
false
|
||||||
}
|
}
|
||||||
/// Is the value held by this [`Dynamic`] a particular type?
|
/// Is the value held by this [`Dynamic`] a particular type?
|
||||||
///
|
///
|
||||||
@ -747,27 +749,26 @@ impl Dynamic {
|
|||||||
/// if its value is going to be modified. This safe-guards constant values from being modified
|
/// if its value is going to be modified. This safe-guards constant values from being modified
|
||||||
/// from within Rust functions.
|
/// from within Rust functions.
|
||||||
pub fn is_read_only(&self) -> bool {
|
pub fn is_read_only(&self) -> bool {
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
match self.0 {
|
match self.0 {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
Union::Shared(_, AccessMode::ReadOnly) => return true,
|
||||||
Union::Shared(_, AccessMode::ReadOnly) => true,
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
|
||||||
Union::Shared(ref cell, _) => {
|
Union::Shared(ref cell, _) => {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
let value = cell.borrow();
|
let value = cell.borrow();
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
let value = cell.read().unwrap();
|
let value = cell.read().unwrap();
|
||||||
|
|
||||||
match value.access_mode() {
|
return match value.access_mode() {
|
||||||
AccessMode::ReadWrite => false,
|
AccessMode::ReadWrite => false,
|
||||||
AccessMode::ReadOnly => true,
|
AccessMode::ReadOnly => true,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => match self.access_mode() {
|
match self.access_mode() {
|
||||||
AccessMode::ReadWrite => false,
|
AccessMode::ReadWrite => false,
|
||||||
AccessMode::ReadOnly => true,
|
AccessMode::ReadOnly => true,
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Can this [`Dynamic`] be hashed?
|
/// Can this [`Dynamic`] be hashed?
|
||||||
@ -947,13 +948,10 @@ impl Dynamic {
|
|||||||
pub fn into_shared(self) -> Self {
|
pub fn into_shared(self) -> Self {
|
||||||
let _access = self.access_mode();
|
let _access = self.access_mode();
|
||||||
|
|
||||||
return match self.0 {
|
match self.0 {
|
||||||
Union::Shared(_, _) => self,
|
Union::Shared(_, _) => self,
|
||||||
_ => Self(Union::Shared(crate::Locked::new(self).into(), _access)),
|
_ => Self(Union::Shared(crate::Locked::new(self).into(), _access)),
|
||||||
};
|
}
|
||||||
|
|
||||||
#[cfg(feature = "no_closure")]
|
|
||||||
panic!("converting into a shared value is not supported under 'no_closure'");
|
|
||||||
}
|
}
|
||||||
/// Convert the [`Dynamic`] value into specific type.
|
/// Convert the [`Dynamic`] value into specific type.
|
||||||
///
|
///
|
||||||
@ -1137,18 +1135,20 @@ impl Dynamic {
|
|||||||
/// If the [`Dynamic`] is a shared value, it a cloned copy of the shared value.
|
/// If the [`Dynamic`] is a shared value, it a cloned copy of the shared value.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn flatten_clone(&self) -> Self {
|
pub fn flatten_clone(&self) -> Self {
|
||||||
match &self.0 {
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
match &self.0 {
|
||||||
Union::Shared(cell, _) => {
|
Union::Shared(cell, _) => {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
let value = cell.borrow();
|
let value = cell.borrow();
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
let value = cell.read().unwrap();
|
let value = cell.read().unwrap();
|
||||||
|
|
||||||
value.clone()
|
return value.clone();
|
||||||
}
|
}
|
||||||
_ => self.clone(),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.clone()
|
||||||
}
|
}
|
||||||
/// Flatten the [`Dynamic`].
|
/// Flatten the [`Dynamic`].
|
||||||
///
|
///
|
||||||
@ -1158,9 +1158,10 @@ impl Dynamic {
|
|||||||
/// outstanding references, or a cloned copy.
|
/// outstanding references, or a cloned copy.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn flatten(self) -> Self {
|
pub fn flatten(self) -> Self {
|
||||||
match self.0 {
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(cell, _) => crate::fn_native::shared_try_take(cell).map_or_else(
|
match self.0 {
|
||||||
|
Union::Shared(cell, _) => {
|
||||||
|
return crate::fn_native::shared_try_take(cell).map_or_else(
|
||||||
|cell| {
|
|cell| {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
let value = cell.borrow();
|
let value = cell.borrow();
|
||||||
@ -1175,8 +1176,45 @@ impl Dynamic {
|
|||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
return value.into_inner().unwrap();
|
return value.into_inner().unwrap();
|
||||||
},
|
},
|
||||||
),
|
)
|
||||||
_ => self,
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Flatten the [`Dynamic`] in place.
|
||||||
|
///
|
||||||
|
/// If the [`Dynamic`] is not a shared value, it does nothing.
|
||||||
|
///
|
||||||
|
/// If the [`Dynamic`] is a shared value, it is set to the shared value if there are no
|
||||||
|
/// outstanding references, or a cloned copy otherwise.
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn flatten_in_place(&mut self) {
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
match self.0 {
|
||||||
|
Union::Shared(_, _) => match crate::stdlib::mem::take(self).0 {
|
||||||
|
Union::Shared(cell, _) => {
|
||||||
|
*self = crate::fn_native::shared_try_take(cell).map_or_else(
|
||||||
|
|cell| {
|
||||||
|
#[cfg(not(feature = "sync"))]
|
||||||
|
let value = cell.borrow();
|
||||||
|
#[cfg(feature = "sync")]
|
||||||
|
let value = cell.read().unwrap();
|
||||||
|
|
||||||
|
value.clone()
|
||||||
|
},
|
||||||
|
|value| {
|
||||||
|
#[cfg(not(feature = "sync"))]
|
||||||
|
return value.into_inner();
|
||||||
|
#[cfg(feature = "sync")]
|
||||||
|
return value.into_inner().unwrap();
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Is the [`Dynamic`] a shared value that is locked?
|
/// Is the [`Dynamic`] a shared value that is locked?
|
||||||
@ -1188,8 +1226,8 @@ impl Dynamic {
|
|||||||
/// So this method always returns [`false`] under [`Sync`].
|
/// So this method always returns [`false`] under [`Sync`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_locked(&self) -> bool {
|
pub fn is_locked(&self) -> bool {
|
||||||
match self.0 {
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
match self.0 {
|
||||||
Union::Shared(ref _cell, _) => {
|
Union::Shared(ref _cell, _) => {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
return _cell.try_borrow().is_err();
|
return _cell.try_borrow().is_err();
|
||||||
@ -1197,8 +1235,10 @@ impl Dynamic {
|
|||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
false
|
||||||
}
|
}
|
||||||
/// Get a reference of a specific type to the [`Dynamic`].
|
/// Get a reference of a specific type to the [`Dynamic`].
|
||||||
/// Casting to [`Dynamic`] just returns a reference to it.
|
/// Casting to [`Dynamic`] just returns a reference to it.
|
||||||
@ -1222,15 +1262,16 @@ impl Dynamic {
|
|||||||
let type_id = (*value).type_id();
|
let type_id = (*value).type_id();
|
||||||
|
|
||||||
if type_id != TypeId::of::<T>() && TypeId::of::<Dynamic>() != TypeId::of::<T>() {
|
if type_id != TypeId::of::<T>() && TypeId::of::<Dynamic>() != TypeId::of::<T>() {
|
||||||
None
|
return None;
|
||||||
} else {
|
} else {
|
||||||
Some(DynamicReadLock(DynamicReadLockInner::Guard(value)))
|
return Some(DynamicReadLock(DynamicReadLockInner::Guard(value)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => self
|
_ => (),
|
||||||
.downcast_ref()
|
|
||||||
.map(|r| DynamicReadLock(DynamicReadLockInner::Reference(r))),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.downcast_ref()
|
||||||
|
.map(|r| DynamicReadLock(DynamicReadLockInner::Reference(r)))
|
||||||
}
|
}
|
||||||
/// Get a mutable reference of a specific type to the [`Dynamic`].
|
/// Get a mutable reference of a specific type to the [`Dynamic`].
|
||||||
/// Casting to [`Dynamic`] just returns a mutable reference to it.
|
/// Casting to [`Dynamic`] just returns a mutable reference to it.
|
||||||
@ -1254,15 +1295,16 @@ impl Dynamic {
|
|||||||
let type_id = (*value).type_id();
|
let type_id = (*value).type_id();
|
||||||
|
|
||||||
if type_id != TypeId::of::<T>() && TypeId::of::<Dynamic>() != TypeId::of::<T>() {
|
if type_id != TypeId::of::<T>() && TypeId::of::<Dynamic>() != TypeId::of::<T>() {
|
||||||
None
|
return None;
|
||||||
} else {
|
} else {
|
||||||
Some(DynamicWriteLock(DynamicWriteLockInner::Guard(value)))
|
return Some(DynamicWriteLock(DynamicWriteLockInner::Guard(value)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => self
|
_ => (),
|
||||||
.downcast_mut()
|
|
||||||
.map(|r| DynamicWriteLock(DynamicWriteLockInner::Reference(r))),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.downcast_mut()
|
||||||
|
.map(|r| DynamicWriteLock(DynamicWriteLockInner::Reference(r)))
|
||||||
}
|
}
|
||||||
/// Get a reference of a specific type to the [`Dynamic`].
|
/// Get a reference of a specific type to the [`Dynamic`].
|
||||||
/// Casting to [`Dynamic`] just returns a reference to it.
|
/// Casting to [`Dynamic`] just returns a reference to it.
|
||||||
@ -1355,10 +1397,8 @@ impl Dynamic {
|
|||||||
|
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
Union::Variant(value, _) => value.as_ref().as_ref().as_any().downcast_ref::<T>(),
|
Union::Variant(value, _) => value.as_ref().as_ref().as_any().downcast_ref::<T>(),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(_, _) => None,
|
Union::Shared(_, _) => None,
|
||||||
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1447,10 +1487,8 @@ impl Dynamic {
|
|||||||
|
|
||||||
match &mut self.0 {
|
match &mut self.0 {
|
||||||
Union::Variant(value, _) => value.as_mut().as_mut_any().downcast_mut::<T>(),
|
Union::Variant(value, _) => value.as_mut().as_mut_any().downcast_mut::<T>(),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(_, _) => None,
|
Union::Shared(_, _) => None,
|
||||||
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1533,7 +1571,7 @@ impl Dynamic {
|
|||||||
///
|
///
|
||||||
/// Panics if the value is shared.
|
/// Panics if the value is shared.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn as_str(&self) -> Result<&str, &'static str> {
|
pub(crate) fn as_str_ref(&self) -> Result<&str, &'static str> {
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
Union::Str(s, _) => Ok(s),
|
Union::Str(s, _) => Ok(s),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
@ -1646,6 +1684,16 @@ impl<T: Variant + Clone> From<&[T]> for Dynamic {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
impl<T: Variant + Clone> crate::stdlib::iter::FromIterator<T> for Dynamic {
|
||||||
|
#[inline(always)]
|
||||||
|
fn from_iter<X: IntoIterator<Item = T>>(iter: X) -> Self {
|
||||||
|
Self(Union::Array(
|
||||||
|
Box::new(iter.into_iter().map(Dynamic::from).collect()),
|
||||||
|
AccessMode::ReadWrite,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
impl<K: Into<ImmutableString>, T: Variant + Clone> From<crate::stdlib::collections::HashMap<K, T>>
|
impl<K: Into<ImmutableString>, T: Variant + Clone> From<crate::stdlib::collections::HashMap<K, T>>
|
||||||
for Dynamic
|
for Dynamic
|
||||||
|
230
src/engine.rs
230
src/engine.rs
@ -1,6 +1,6 @@
|
|||||||
//! Main module defining the script evaluation [`Engine`].
|
//! Main module defining the script evaluation [`Engine`].
|
||||||
|
|
||||||
use crate::ast::{Expr, FnCallExpr, Ident, ReturnType, Stmt};
|
use crate::ast::{Expr, FnCallExpr, FnHash, Ident, OpAssignment, ReturnType, Stmt};
|
||||||
use crate::dynamic::{map_std_type_name, AccessMode, Union, Variant};
|
use crate::dynamic::{map_std_type_name, AccessMode, Union, Variant};
|
||||||
use crate::fn_native::{
|
use crate::fn_native::{
|
||||||
CallableFunction, IteratorFn, OnDebugCallback, OnPrintCallback, OnProgressCallback,
|
CallableFunction, IteratorFn, OnDebugCallback, OnPrintCallback, OnProgressCallback,
|
||||||
@ -25,8 +25,8 @@ use crate::stdlib::{
|
|||||||
use crate::syntax::CustomSyntax;
|
use crate::syntax::CustomSyntax;
|
||||||
use crate::utils::{get_hasher, StraightHasherBuilder};
|
use crate::utils::{get_hasher, StraightHasherBuilder};
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_native_fn_hash, Dynamic, EvalAltResult, FnPtr, ImmutableString, Module, Position,
|
calc_fn_hash, Dynamic, EvalAltResult, FnPtr, ImmutableString, Module, Position, RhaiResult,
|
||||||
RhaiResult, Scope, Shared, StaticVec,
|
Scope, Shared, StaticVec,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -108,6 +108,7 @@ impl Imports {
|
|||||||
self.0.iter().rev().map(|(n, m)| (n, m))
|
self.0.iter().rev().map(|(n, m)| (n, m))
|
||||||
}
|
}
|
||||||
/// Get an iterator to this stack of imported [modules][Module] in forward order.
|
/// Get an iterator to this stack of imported [modules][Module] in forward order.
|
||||||
|
#[allow(dead_code)]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn scan_raw(&self) -> impl Iterator<Item = (&ImmutableString, &Shared<Module>)> {
|
pub(crate) fn scan_raw(&self) -> impl Iterator<Item = (&ImmutableString, &Shared<Module>)> {
|
||||||
self.0.iter().map(|(n, m)| (n, m))
|
self.0.iter().map(|(n, m)| (n, m))
|
||||||
@ -125,15 +126,12 @@ impl Imports {
|
|||||||
/// Does the specified function hash key exist in this stack of imported [modules][Module]?
|
/// Does the specified function hash key exist in this stack of imported [modules][Module]?
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn contains_fn(&self, hash: NonZeroU64) -> bool {
|
pub fn contains_fn(&self, hash: u64) -> bool {
|
||||||
self.0.iter().any(|(_, m)| m.contains_qualified_fn(hash))
|
self.0.iter().any(|(_, m)| m.contains_qualified_fn(hash))
|
||||||
}
|
}
|
||||||
/// Get specified function via its hash key.
|
/// Get specified function via its hash key.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn get_fn(
|
pub fn get_fn(&self, hash: u64) -> Option<(&CallableFunction, Option<&ImmutableString>)> {
|
||||||
&self,
|
|
||||||
hash: NonZeroU64,
|
|
||||||
) -> Option<(&CallableFunction, Option<&ImmutableString>)> {
|
|
||||||
self.0
|
self.0
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
@ -405,8 +403,8 @@ impl<'a> Target<'a> {
|
|||||||
Self::LockGuard((r, _)) => **r = new_val,
|
Self::LockGuard((r, _)) => **r = new_val,
|
||||||
Self::Value(_) => panic!("cannot update a value"),
|
Self::Value(_) => panic!("cannot update a value"),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::StringChar(string, index, _) if string.is::<ImmutableString>() => {
|
Self::StringChar(s, index, _) => {
|
||||||
let mut s = string.write_lock::<ImmutableString>().unwrap();
|
let s = &mut *s.write_lock::<ImmutableString>().unwrap();
|
||||||
|
|
||||||
// Replace the character at the specified index position
|
// Replace the character at the specified index position
|
||||||
let new_ch = new_val.as_char().map_err(|err| {
|
let new_ch = new_val.as_char().map_err(|err| {
|
||||||
@ -417,17 +415,15 @@ impl<'a> Target<'a> {
|
|||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let mut chars = s.chars().collect::<StaticVec<_>>();
|
let index = *index;
|
||||||
|
|
||||||
// See if changed - if so, update the String
|
*s = s
|
||||||
if chars[*index] != new_ch {
|
.chars()
|
||||||
chars[*index] = new_ch;
|
.enumerate()
|
||||||
*s = chars.iter().collect::<String>().into();
|
.map(|(i, ch)| if i == index { new_ch } else { ch })
|
||||||
|
.collect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Self::StringChar(_, _, _) => unreachable!(),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -511,11 +507,7 @@ pub struct State {
|
|||||||
pub resolver: Option<Shared<crate::module::resolvers::StaticModuleResolver>>,
|
pub resolver: Option<Shared<crate::module::resolvers::StaticModuleResolver>>,
|
||||||
/// Functions resolution cache.
|
/// Functions resolution cache.
|
||||||
fn_resolution_caches: StaticVec<
|
fn_resolution_caches: StaticVec<
|
||||||
HashMap<
|
HashMap<u64, Option<(CallableFunction, Option<ImmutableString>)>, StraightHasherBuilder>,
|
||||||
NonZeroU64,
|
|
||||||
Option<(CallableFunction, Option<ImmutableString>)>,
|
|
||||||
StraightHasherBuilder,
|
|
||||||
>,
|
|
||||||
>,
|
>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -528,11 +520,8 @@ impl State {
|
|||||||
/// Get a mutable reference to the current functions resolution cache.
|
/// Get a mutable reference to the current functions resolution cache.
|
||||||
pub fn fn_resolution_cache_mut(
|
pub fn fn_resolution_cache_mut(
|
||||||
&mut self,
|
&mut self,
|
||||||
) -> &mut HashMap<
|
) -> &mut HashMap<u64, Option<(CallableFunction, Option<ImmutableString>)>, StraightHasherBuilder>
|
||||||
NonZeroU64,
|
{
|
||||||
Option<(CallableFunction, Option<ImmutableString>)>,
|
|
||||||
StraightHasherBuilder,
|
|
||||||
> {
|
|
||||||
if self.fn_resolution_caches.is_empty() {
|
if self.fn_resolution_caches.is_empty() {
|
||||||
self.fn_resolution_caches
|
self.fn_resolution_caches
|
||||||
.push(HashMap::with_capacity_and_hasher(16, StraightHasherBuilder));
|
.push(HashMap::with_capacity_and_hasher(16, StraightHasherBuilder));
|
||||||
@ -540,6 +529,7 @@ impl State {
|
|||||||
self.fn_resolution_caches.last_mut().unwrap()
|
self.fn_resolution_caches.last_mut().unwrap()
|
||||||
}
|
}
|
||||||
/// Push an empty functions resolution cache onto the stack and make it current.
|
/// Push an empty functions resolution cache onto the stack and make it current.
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn push_fn_resolution_cache(&mut self) {
|
pub fn push_fn_resolution_cache(&mut self) {
|
||||||
self.fn_resolution_caches.push(Default::default());
|
self.fn_resolution_caches.push(Default::default());
|
||||||
}
|
}
|
||||||
@ -547,14 +537,6 @@ impl State {
|
|||||||
pub fn pop_fn_resolution_cache(&mut self) {
|
pub fn pop_fn_resolution_cache(&mut self) {
|
||||||
self.fn_resolution_caches.pop();
|
self.fn_resolution_caches.pop();
|
||||||
}
|
}
|
||||||
/// Clear the current functions resolution cache.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if there is no current functions resolution cache.
|
|
||||||
pub fn clear_fn_resolution_cache(&mut self) {
|
|
||||||
self.fn_resolution_caches.last_mut().unwrap().clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _(INTERNALS)_ A type containing all the limits imposed by the [`Engine`].
|
/// _(INTERNALS)_ A type containing all the limits imposed by the [`Engine`].
|
||||||
@ -1061,11 +1043,9 @@ impl Engine {
|
|||||||
idx_values: &mut StaticVec<ChainArgument>,
|
idx_values: &mut StaticVec<ChainArgument>,
|
||||||
chain_type: ChainType,
|
chain_type: ChainType,
|
||||||
level: usize,
|
level: usize,
|
||||||
new_val: Option<((Dynamic, Position), (&str, Position))>,
|
new_val: Option<((Dynamic, Position), (&Option<OpAssignment>, Position))>,
|
||||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||||
if chain_type == ChainType::NonChaining {
|
assert!(chain_type != ChainType::NonChaining);
|
||||||
unreachable!("should not be ChainType::NonChaining");
|
|
||||||
}
|
|
||||||
|
|
||||||
let is_ref = target.is_ref();
|
let is_ref = target.is_ref();
|
||||||
|
|
||||||
@ -1112,9 +1092,9 @@ impl Engine {
|
|||||||
) {
|
) {
|
||||||
// Indexed value is a reference - update directly
|
// Indexed value is a reference - update directly
|
||||||
Ok(obj_ptr) => {
|
Ok(obj_ptr) => {
|
||||||
let ((new_val, new_pos), (op, op_pos)) = new_val.unwrap();
|
let ((new_val, new_pos), (op_info, op_pos)) = new_val.unwrap();
|
||||||
self.eval_op_assignment(
|
self.eval_op_assignment(
|
||||||
mods, state, lib, op, op_pos, obj_ptr, new_val, new_pos,
|
mods, state, lib, op_info, op_pos, obj_ptr, new_val, new_pos,
|
||||||
)?;
|
)?;
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -1132,11 +1112,13 @@ impl Engine {
|
|||||||
let val_type_name = target_val.type_name();
|
let val_type_name = target_val.type_name();
|
||||||
let ((_, val_pos), _) = new_val;
|
let ((_, val_pos), _) = new_val;
|
||||||
|
|
||||||
|
let hash_set =
|
||||||
|
FnHash::from_native(calc_fn_hash(empty(), FN_IDX_SET, 3));
|
||||||
let args = &mut [target_val, &mut idx_val2, &mut (new_val.0).0];
|
let args = &mut [target_val, &mut idx_val2, &mut (new_val.0).0];
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, FN_IDX_SET, None, args, is_ref, true, val_pos,
|
mods, state, lib, FN_IDX_SET, hash_set, args, is_ref, true,
|
||||||
None, level,
|
val_pos, None, level,
|
||||||
)
|
)
|
||||||
.map_err(|err| match *err {
|
.map_err(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
@ -1169,11 +1151,7 @@ impl Engine {
|
|||||||
match rhs {
|
match rhs {
|
||||||
// xxx.fn_name(arg_expr_list)
|
// xxx.fn_name(arg_expr_list)
|
||||||
Expr::FnCall(x, pos) if x.namespace.is_none() && new_val.is_none() => {
|
Expr::FnCall(x, pos) if x.namespace.is_none() && new_val.is_none() => {
|
||||||
let FnCallExpr {
|
let FnCallExpr { name, hash, .. } = x.as_ref();
|
||||||
name,
|
|
||||||
hash_script: hash,
|
|
||||||
..
|
|
||||||
} = x.as_ref();
|
|
||||||
let args = idx_val.as_fn_call_args();
|
let args = idx_val.as_fn_call_args();
|
||||||
self.make_method_call(
|
self.make_method_call(
|
||||||
mods, state, lib, name, *hash, target, args, *pos, level,
|
mods, state, lib, name, *hash, target, args, *pos, level,
|
||||||
@ -1189,20 +1167,20 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// {xxx:map}.id op= ???
|
// {xxx:map}.id op= ???
|
||||||
Expr::Property(x) if target_val.is::<Map>() && new_val.is_some() => {
|
Expr::Property(x) if target_val.is::<Map>() && new_val.is_some() => {
|
||||||
let Ident { name, pos } = &x.2;
|
let Ident { name, pos } = &x.4;
|
||||||
let index = name.clone().into();
|
let index = name.clone().into();
|
||||||
let val = self.get_indexed_mut(
|
let val = self.get_indexed_mut(
|
||||||
mods, state, lib, target_val, index, *pos, true, is_ref, false, level,
|
mods, state, lib, target_val, index, *pos, true, is_ref, false, level,
|
||||||
)?;
|
)?;
|
||||||
let ((new_val, new_pos), (op, op_pos)) = new_val.unwrap();
|
let ((new_val, new_pos), (op_info, op_pos)) = new_val.unwrap();
|
||||||
self.eval_op_assignment(
|
self.eval_op_assignment(
|
||||||
mods, state, lib, op, op_pos, val, new_val, new_pos,
|
mods, state, lib, op_info, op_pos, val, new_val, new_pos,
|
||||||
)?;
|
)?;
|
||||||
Ok((Dynamic::UNIT, true))
|
Ok((Dynamic::UNIT, true))
|
||||||
}
|
}
|
||||||
// {xxx:map}.id
|
// {xxx:map}.id
|
||||||
Expr::Property(x) if target_val.is::<Map>() => {
|
Expr::Property(x) if target_val.is::<Map>() => {
|
||||||
let Ident { name, pos } = &x.2;
|
let Ident { name, pos } = &x.4;
|
||||||
let index = name.clone().into();
|
let index = name.clone().into();
|
||||||
let val = self.get_indexed_mut(
|
let val = self.get_indexed_mut(
|
||||||
mods, state, lib, target_val, index, *pos, false, is_ref, false, level,
|
mods, state, lib, target_val, index, *pos, false, is_ref, false, level,
|
||||||
@ -1212,21 +1190,23 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// xxx.id = ???
|
// xxx.id = ???
|
||||||
Expr::Property(x) if new_val.is_some() => {
|
Expr::Property(x) if new_val.is_some() => {
|
||||||
let (_, setter, Ident { pos, .. }) = x.as_ref();
|
let (_, _, setter, hash_set, Ident { pos, .. }) = x.as_ref();
|
||||||
|
let hash = FnHash::from_native(*hash_set);
|
||||||
let mut new_val = new_val;
|
let mut new_val = new_val;
|
||||||
let mut args = [target_val, &mut (new_val.as_mut().unwrap().0).0];
|
let mut args = [target_val, &mut (new_val.as_mut().unwrap().0).0];
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, setter, None, &mut args, is_ref, true, *pos, None,
|
mods, state, lib, setter, hash, &mut args, is_ref, true, *pos, None,
|
||||||
level,
|
level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| (v, true))
|
.map(|(v, _)| (v, true))
|
||||||
}
|
}
|
||||||
// xxx.id
|
// xxx.id
|
||||||
Expr::Property(x) => {
|
Expr::Property(x) => {
|
||||||
let (getter, _, Ident { pos, .. }) = x.as_ref();
|
let (getter, hash_get, _, _, Ident { pos, .. }) = x.as_ref();
|
||||||
|
let hash = FnHash::from_native(*hash_get);
|
||||||
let mut args = [target_val];
|
let mut args = [target_val];
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, getter, None, &mut args, is_ref, true, *pos, None,
|
mods, state, lib, getter, hash, &mut args, is_ref, true, *pos, None,
|
||||||
level,
|
level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| (v, false))
|
.map(|(v, _)| (v, false))
|
||||||
@ -1235,7 +1215,7 @@ impl Engine {
|
|||||||
Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target_val.is::<Map>() => {
|
Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target_val.is::<Map>() => {
|
||||||
let mut val = match &x.lhs {
|
let mut val = match &x.lhs {
|
||||||
Expr::Property(p) => {
|
Expr::Property(p) => {
|
||||||
let Ident { name, pos } = &p.2;
|
let Ident { name, pos } = &p.4;
|
||||||
let index = name.clone().into();
|
let index = name.clone().into();
|
||||||
self.get_indexed_mut(
|
self.get_indexed_mut(
|
||||||
mods, state, lib, target_val, index, *pos, false, is_ref, true,
|
mods, state, lib, target_val, index, *pos, false, is_ref, true,
|
||||||
@ -1244,11 +1224,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr
|
// {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr
|
||||||
Expr::FnCall(x, pos) if x.namespace.is_none() => {
|
Expr::FnCall(x, pos) if x.namespace.is_none() => {
|
||||||
let FnCallExpr {
|
let FnCallExpr { name, hash, .. } = x.as_ref();
|
||||||
name,
|
|
||||||
hash_script: hash,
|
|
||||||
..
|
|
||||||
} = x.as_ref();
|
|
||||||
let args = idx_val.as_fn_call_args();
|
let args = idx_val.as_fn_call_args();
|
||||||
let (val, _) = self.make_method_call(
|
let (val, _) = self.make_method_call(
|
||||||
mods, state, lib, name, *hash, target, args, *pos, level,
|
mods, state, lib, name, *hash, target, args, *pos, level,
|
||||||
@ -1274,12 +1250,15 @@ impl Engine {
|
|||||||
match &x.lhs {
|
match &x.lhs {
|
||||||
// xxx.prop[expr] | xxx.prop.expr
|
// xxx.prop[expr] | xxx.prop.expr
|
||||||
Expr::Property(p) => {
|
Expr::Property(p) => {
|
||||||
let (getter, setter, Ident { pos, .. }) = p.as_ref();
|
let (getter, hash_get, setter, hash_set, Ident { pos, .. }) =
|
||||||
|
p.as_ref();
|
||||||
|
let hash_get = FnHash::from_native(*hash_get);
|
||||||
|
let hash_set = FnHash::from_native(*hash_set);
|
||||||
let arg_values = &mut [target_val, &mut Default::default()];
|
let arg_values = &mut [target_val, &mut Default::default()];
|
||||||
let args = &mut arg_values[..1];
|
let args = &mut arg_values[..1];
|
||||||
let (mut val, updated) = self.exec_fn_call(
|
let (mut val, updated) = self.exec_fn_call(
|
||||||
mods, state, lib, getter, None, args, is_ref, true, *pos, None,
|
mods, state, lib, getter, hash_get, args, is_ref, true, *pos,
|
||||||
level,
|
None, level,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let val = &mut val;
|
let val = &mut val;
|
||||||
@ -1304,8 +1283,8 @@ impl Engine {
|
|||||||
// Re-use args because the first &mut parameter will not be consumed
|
// Re-use args because the first &mut parameter will not be consumed
|
||||||
arg_values[1] = val;
|
arg_values[1] = val;
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, setter, None, arg_values, is_ref, true,
|
mods, state, lib, setter, hash_set, arg_values, is_ref,
|
||||||
*pos, None, level,
|
true, *pos, None, level,
|
||||||
)
|
)
|
||||||
.or_else(
|
.or_else(
|
||||||
|err| match *err {
|
|err| match *err {
|
||||||
@ -1323,11 +1302,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr
|
// xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr
|
||||||
Expr::FnCall(f, pos) if f.namespace.is_none() => {
|
Expr::FnCall(f, pos) if f.namespace.is_none() => {
|
||||||
let FnCallExpr {
|
let FnCallExpr { name, hash, .. } = f.as_ref();
|
||||||
name,
|
|
||||||
hash_script: hash,
|
|
||||||
..
|
|
||||||
} = f.as_ref();
|
|
||||||
let args = idx_val.as_fn_call_args();
|
let args = idx_val.as_fn_call_args();
|
||||||
let (mut val, _) = self.make_method_call(
|
let (mut val, _) = self.make_method_call(
|
||||||
mods, state, lib, name, *hash, target, args, *pos, level,
|
mods, state, lib, name, *hash, target, args, *pos, level,
|
||||||
@ -1369,7 +1344,7 @@ impl Engine {
|
|||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
level: usize,
|
level: usize,
|
||||||
new_val: Option<((Dynamic, Position), (&str, Position))>,
|
new_val: Option<((Dynamic, Position), (&Option<OpAssignment>, Position))>,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, op_pos) = match expr {
|
let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, op_pos) = match expr {
|
||||||
Expr::Index(x, pos) => (x.as_ref(), ChainType::Index, *pos),
|
Expr::Index(x, pos) => (x.as_ref(), ChainType::Index, *pos),
|
||||||
@ -1603,8 +1578,9 @@ impl Engine {
|
|||||||
let type_name = target.type_name();
|
let type_name = target.type_name();
|
||||||
let mut idx = idx;
|
let mut idx = idx;
|
||||||
let args = &mut [target, &mut idx];
|
let args = &mut [target, &mut idx];
|
||||||
|
let hash_get = FnHash::from_native(calc_fn_hash(empty(), FN_IDX_GET, 2));
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
_mods, state, _lib, FN_IDX_GET, None, args, _is_ref, true, idx_pos, None,
|
_mods, state, _lib, FN_IDX_GET, hash_get, args, _is_ref, true, idx_pos, None,
|
||||||
_level,
|
_level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| v.into())
|
.map(|(v, _)| v.into())
|
||||||
@ -1650,17 +1626,13 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Dynamic(Union::Array(mut rhs_value, _)) => {
|
Dynamic(Union::Array(mut rhs_value, _)) => {
|
||||||
// Call the `==` operator to compare each value
|
// Call the `==` operator to compare each value
|
||||||
|
let hash = calc_fn_hash(empty(), OP_EQUALS, 2);
|
||||||
for value in rhs_value.iter_mut() {
|
for value in rhs_value.iter_mut() {
|
||||||
let args = &mut [&mut lhs_value.clone(), value];
|
let args = &mut [&mut lhs_value.clone(), value];
|
||||||
let hash_fn =
|
|
||||||
calc_native_fn_hash(empty(), OP_EQUALS, args.iter().map(|a| a.type_id()))
|
|
||||||
.unwrap();
|
|
||||||
let pos = rhs.position();
|
let pos = rhs.position();
|
||||||
|
|
||||||
if self
|
if self
|
||||||
.call_native_fn(
|
.call_native_fn(mods, state, lib, OP_EQUALS, hash, args, false, false, pos)?
|
||||||
mods, state, lib, OP_EQUALS, hash_fn, args, false, false, pos,
|
|
||||||
)?
|
|
||||||
.0
|
.0
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
@ -1774,7 +1746,7 @@ impl Engine {
|
|||||||
let FnCallExpr {
|
let FnCallExpr {
|
||||||
name,
|
name,
|
||||||
capture: cap_scope,
|
capture: cap_scope,
|
||||||
hash_script: hash,
|
hash,
|
||||||
args,
|
args,
|
||||||
..
|
..
|
||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
@ -1788,12 +1760,12 @@ impl Engine {
|
|||||||
let FnCallExpr {
|
let FnCallExpr {
|
||||||
name,
|
name,
|
||||||
namespace,
|
namespace,
|
||||||
hash_script,
|
hash,
|
||||||
args,
|
args,
|
||||||
..
|
..
|
||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
let namespace = namespace.as_ref();
|
let namespace = namespace.as_ref();
|
||||||
let hash = hash_script.unwrap();
|
let hash = hash.native_hash();
|
||||||
self.make_qualified_function_call(
|
self.make_qualified_function_call(
|
||||||
scope, mods, state, lib, this_ptr, namespace, name, args, hash, *pos, level,
|
scope, mods, state, lib, this_ptr, namespace, name, args, hash, *pos, level,
|
||||||
)
|
)
|
||||||
@ -1872,7 +1844,7 @@ impl Engine {
|
|||||||
restore_prev_state: bool,
|
restore_prev_state: bool,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
let mut _restore_fn_resolution_cache = false;
|
let mut _extra_fn_resolution_cache = false;
|
||||||
let prev_always_search = state.always_search;
|
let prev_always_search = state.always_search;
|
||||||
let prev_scope_len = scope.len();
|
let prev_scope_len = scope.len();
|
||||||
let prev_mods_len = mods.len();
|
let prev_mods_len = mods.len();
|
||||||
@ -1895,15 +1867,18 @@ impl Engine {
|
|||||||
.skip(_mods_len)
|
.skip(_mods_len)
|
||||||
.any(|(_, m)| m.contains_indexed_global_functions())
|
.any(|(_, m)| m.contains_indexed_global_functions())
|
||||||
{
|
{
|
||||||
if _restore_fn_resolution_cache {
|
if _extra_fn_resolution_cache {
|
||||||
// When new module is imported with global functions and there is already
|
// When new module is imported with global functions and there is already
|
||||||
// a new cache, clear it - notice that this is expensive as all function
|
// a new cache, clear it - notice that this is expensive as all function
|
||||||
// resolutions must start again
|
// resolutions must start again
|
||||||
state.clear_fn_resolution_cache();
|
state.fn_resolution_cache_mut().clear();
|
||||||
} else if restore_prev_state {
|
} else if restore_prev_state {
|
||||||
// When new module is imported with global functions, push a new cache
|
// When new module is imported with global functions, push a new cache
|
||||||
state.push_fn_resolution_cache();
|
state.push_fn_resolution_cache();
|
||||||
_restore_fn_resolution_cache = true;
|
_extra_fn_resolution_cache = true;
|
||||||
|
} else {
|
||||||
|
// When the block is to be evaluated in-place, just clear the current cache
|
||||||
|
state.fn_resolution_cache_mut().clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1911,12 +1886,13 @@ impl Engine {
|
|||||||
Ok(r)
|
Ok(r)
|
||||||
});
|
});
|
||||||
|
|
||||||
if restore_prev_state {
|
if _extra_fn_resolution_cache {
|
||||||
scope.rewind(prev_scope_len);
|
|
||||||
if _restore_fn_resolution_cache {
|
|
||||||
// If imports list is modified, pop the functions lookup cache
|
// If imports list is modified, pop the functions lookup cache
|
||||||
state.pop_fn_resolution_cache();
|
state.pop_fn_resolution_cache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if restore_prev_state {
|
||||||
|
scope.rewind(prev_scope_len);
|
||||||
mods.truncate(prev_mods_len);
|
mods.truncate(prev_mods_len);
|
||||||
state.scope_level -= 1;
|
state.scope_level -= 1;
|
||||||
|
|
||||||
@ -1933,7 +1909,7 @@ impl Engine {
|
|||||||
mods: &mut Imports,
|
mods: &mut Imports,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
op: &str,
|
op_info: &Option<OpAssignment>,
|
||||||
op_pos: Position,
|
op_pos: Position,
|
||||||
mut target: Target,
|
mut target: Target,
|
||||||
mut new_value: Dynamic,
|
mut new_value: Dynamic,
|
||||||
@ -1943,11 +1919,12 @@ impl Engine {
|
|||||||
unreachable!("LHS should not be read-only");
|
unreachable!("LHS should not be read-only");
|
||||||
}
|
}
|
||||||
|
|
||||||
if op.is_empty() {
|
if let Some(OpAssignment {
|
||||||
// Normal assignment
|
hash_op_assign,
|
||||||
target.set_value(new_value, new_value_pos)?;
|
hash_op,
|
||||||
Ok(())
|
op,
|
||||||
} else {
|
}) = op_info
|
||||||
|
{
|
||||||
let mut lock_guard;
|
let mut lock_guard;
|
||||||
let lhs_ptr_inner;
|
let lhs_ptr_inner;
|
||||||
|
|
||||||
@ -1958,28 +1935,30 @@ impl Engine {
|
|||||||
lhs_ptr_inner = target.as_mut();
|
lhs_ptr_inner = target.as_mut();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let hash = *hash_op_assign;
|
||||||
let args = &mut [lhs_ptr_inner, &mut new_value];
|
let args = &mut [lhs_ptr_inner, &mut new_value];
|
||||||
let hash_fn =
|
|
||||||
calc_native_fn_hash(empty(), op, args.iter().map(|a| a.type_id())).unwrap();
|
|
||||||
|
|
||||||
match self.call_native_fn(mods, state, lib, op, hash_fn, args, true, true, op_pos) {
|
match self.call_native_fn(mods, state, lib, op, hash, args, true, true, op_pos) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) if matches!(err.as_ref(), EvalAltResult::ErrorFunctionNotFound(f, _) if f.starts_with(op)) =>
|
Err(err) if matches!(err.as_ref(), EvalAltResult::ErrorFunctionNotFound(f, _) if f.starts_with(op.as_ref())) =>
|
||||||
{
|
{
|
||||||
// Expand to `var = var op rhs`
|
// Expand to `var = var op rhs`
|
||||||
let op = &op[..op.len() - 1]; // extract operator without =
|
let op = &op[..op.len() - 1]; // extract operator without =
|
||||||
let hash_fn =
|
|
||||||
calc_native_fn_hash(empty(), op, args.iter().map(|a| a.type_id())).unwrap();
|
|
||||||
|
|
||||||
// Run function
|
// Run function
|
||||||
let (value, _) = self
|
let (value, _) = self.call_native_fn(
|
||||||
.call_native_fn(mods, state, lib, op, hash_fn, args, true, false, op_pos)?;
|
mods, state, lib, op, *hash_op, args, true, false, op_pos,
|
||||||
|
)?;
|
||||||
|
|
||||||
*args[0] = value.flatten();
|
*args[0] = value.flatten();
|
||||||
}
|
}
|
||||||
err => return err.map(|_| ()),
|
err => return err.map(|_| ()),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
// Normal assignment
|
||||||
|
target.set_value(new_value, new_value_pos)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2013,7 +1992,7 @@ impl Engine {
|
|||||||
|
|
||||||
// var op= rhs
|
// var op= rhs
|
||||||
Stmt::Assignment(x, op_pos) if x.0.get_variable_access(false).is_some() => {
|
Stmt::Assignment(x, op_pos) if x.0.get_variable_access(false).is_some() => {
|
||||||
let (lhs_expr, op, rhs_expr) = x.as_ref();
|
let (lhs_expr, rhs_expr, op_info) = x.as_ref();
|
||||||
let rhs_val = self
|
let rhs_val = self
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?
|
.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?
|
||||||
.flatten();
|
.flatten();
|
||||||
@ -2041,7 +2020,7 @@ impl Engine {
|
|||||||
mods,
|
mods,
|
||||||
state,
|
state,
|
||||||
lib,
|
lib,
|
||||||
op,
|
op_info,
|
||||||
*op_pos,
|
*op_pos,
|
||||||
lhs_ptr,
|
lhs_ptr,
|
||||||
rhs_val,
|
rhs_val,
|
||||||
@ -2053,11 +2032,11 @@ impl Engine {
|
|||||||
|
|
||||||
// lhs op= rhs
|
// lhs op= rhs
|
||||||
Stmt::Assignment(x, op_pos) => {
|
Stmt::Assignment(x, op_pos) => {
|
||||||
let (lhs_expr, op, rhs_expr) = x.as_ref();
|
let (lhs_expr, rhs_expr, op_info) = x.as_ref();
|
||||||
let rhs_val = self
|
let rhs_val = self
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?
|
.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?
|
||||||
.flatten();
|
.flatten();
|
||||||
let _new_val = Some(((rhs_val, rhs_expr.position()), (op.as_ref(), *op_pos)));
|
let _new_val = Some(((rhs_val, rhs_expr.position()), (op_info, *op_pos)));
|
||||||
|
|
||||||
// Must be either `var[index] op= val` or `var.prop op= val`
|
// Must be either `var[index] op= val` or `var.prop op= val`
|
||||||
match lhs_expr {
|
match lhs_expr {
|
||||||
@ -2138,11 +2117,15 @@ impl Engine {
|
|||||||
|
|
||||||
// While loop
|
// While loop
|
||||||
Stmt::While(expr, body, _) => loop {
|
Stmt::While(expr, body, _) => loop {
|
||||||
match self
|
let condition = if let Some(expr) = expr {
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
{
|
.map_err(|err| self.make_type_mismatch_err::<bool>(err, expr.position()))?
|
||||||
Ok(true) => {
|
} else {
|
||||||
|
true
|
||||||
|
};
|
||||||
|
|
||||||
|
if condition {
|
||||||
match self.eval_stmt(scope, mods, state, lib, this_ptr, body, level) {
|
match self.eval_stmt(scope, mods, state, lib, this_ptr, body, level) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
@ -2151,11 +2134,8 @@ impl Engine {
|
|||||||
_ => return Err(err),
|
_ => return Err(err),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
Ok(false) => return Ok(Dynamic::UNIT),
|
return Ok(Dynamic::UNIT);
|
||||||
Err(err) => {
|
|
||||||
return Err(self.make_type_mismatch_err::<bool>(err, expr.position()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -2170,15 +2150,17 @@ impl Engine {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
match self
|
if self
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
|
.map_err(|err| self.make_type_mismatch_err::<bool>(err, expr.position()))?
|
||||||
{
|
{
|
||||||
Ok(true) if !*is_while => return Ok(Dynamic::UNIT),
|
if !*is_while {
|
||||||
Ok(false) if *is_while => return Ok(Dynamic::UNIT),
|
return Ok(Dynamic::UNIT);
|
||||||
Ok(_) => (),
|
}
|
||||||
Err(err) => {
|
} else {
|
||||||
return Err(self.make_type_mismatch_err::<bool>(err, expr.position()))
|
if *is_while {
|
||||||
|
return Ok(Dynamic::UNIT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
234
src/fn_call.rs
234
src/fn_call.rs
@ -1,5 +1,6 @@
|
|||||||
//! Implement function-calling mechanism for [`Engine`].
|
//! Implement function-calling mechanism for [`Engine`].
|
||||||
|
|
||||||
|
use crate::ast::FnHash;
|
||||||
use crate::engine::{
|
use crate::engine::{
|
||||||
Imports, State, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL,
|
Imports, State, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL,
|
||||||
KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF,
|
KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF,
|
||||||
@ -16,19 +17,17 @@ use crate::stdlib::{
|
|||||||
format,
|
format,
|
||||||
iter::{empty, once},
|
iter::{empty, once},
|
||||||
mem,
|
mem,
|
||||||
num::NonZeroU64,
|
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
use crate::utils::combine_hashes;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{Expr, Stmt},
|
ast::{Expr, Stmt},
|
||||||
fn_native::CallableFunction,
|
fn_native::CallableFunction,
|
||||||
RhaiResult,
|
RhaiResult,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_native_fn_hash, calc_script_fn_hash, Dynamic, Engine, EvalAltResult, FnPtr,
|
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, EvalAltResult, FnPtr,
|
||||||
ImmutableString, Module, ParseErrorType, Position, Scope, StaticVec, INT,
|
ImmutableString, Module, ParseErrorType, Position, Scope, StaticVec,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
@ -183,20 +182,27 @@ impl Engine {
|
|||||||
state: &'s mut State,
|
state: &'s mut State,
|
||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
mut hash: NonZeroU64,
|
hash_script: u64,
|
||||||
args: &mut FnCallArgs,
|
args: Option<&mut FnCallArgs>,
|
||||||
allow_dynamic: bool,
|
allow_dynamic: bool,
|
||||||
is_op_assignment: bool,
|
is_op_assignment: bool,
|
||||||
) -> &'s Option<(CallableFunction, Option<ImmutableString>)> {
|
) -> &'s Option<(CallableFunction, Option<ImmutableString>)> {
|
||||||
|
let mut hash = if let Some(ref args) = args {
|
||||||
|
let hash_params = calc_fn_params_hash(args.iter().map(|a| a.type_id()));
|
||||||
|
combine_hashes(hash_script, hash_params)
|
||||||
|
} else {
|
||||||
|
hash_script
|
||||||
|
};
|
||||||
|
|
||||||
&*state
|
&*state
|
||||||
.fn_resolution_cache_mut()
|
.fn_resolution_cache_mut()
|
||||||
.entry(hash)
|
.entry(hash)
|
||||||
.or_insert_with(|| {
|
.or_insert_with(|| {
|
||||||
let num_args = args.len();
|
let num_args = args.as_ref().map(|a| a.len()).unwrap_or(0);
|
||||||
let max_bitmask = if !allow_dynamic {
|
let max_bitmask = if !allow_dynamic {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
1usize << args.len().min(MAX_DYNAMIC_PARAMETERS)
|
1usize << num_args.min(MAX_DYNAMIC_PARAMETERS)
|
||||||
};
|
};
|
||||||
let mut bitmask = 1usize; // Bitmask of which parameter to replace with `Dynamic`
|
let mut bitmask = 1usize; // Bitmask of which parameter to replace with `Dynamic`
|
||||||
|
|
||||||
@ -238,7 +244,8 @@ impl Engine {
|
|||||||
None if bitmask >= max_bitmask => {
|
None if bitmask >= max_bitmask => {
|
||||||
return if num_args != 2 {
|
return if num_args != 2 {
|
||||||
None
|
None
|
||||||
} else if !is_op_assignment {
|
} else if let Some(ref args) = args {
|
||||||
|
if !is_op_assignment {
|
||||||
if let Some(f) =
|
if let Some(f) =
|
||||||
get_builtin_binary_op_fn(fn_name, &args[0], &args[1])
|
get_builtin_binary_op_fn(fn_name, &args[0], &args[1])
|
||||||
{
|
{
|
||||||
@ -263,14 +270,15 @@ impl Engine {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try all permutations with `Dynamic` wildcards
|
// Try all permutations with `Dynamic` wildcards
|
||||||
None => {
|
None => {
|
||||||
hash = calc_native_fn_hash(
|
let hash_params = calc_fn_params_hash(
|
||||||
empty(),
|
args.as_ref().unwrap().iter().enumerate().map(|(i, a)| {
|
||||||
fn_name,
|
|
||||||
args.iter().enumerate().map(|(i, a)| {
|
|
||||||
let mask = 1usize << (num_args - i - 1);
|
let mask = 1usize << (num_args - i - 1);
|
||||||
if bitmask & mask != 0 {
|
if bitmask & mask != 0 {
|
||||||
// Replace with `Dynamic`
|
// Replace with `Dynamic`
|
||||||
@ -279,8 +287,8 @@ impl Engine {
|
|||||||
a.type_id()
|
a.type_id()
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
)
|
);
|
||||||
.unwrap();
|
hash = combine_hashes(hash_script, hash_params);
|
||||||
|
|
||||||
bitmask += 1;
|
bitmask += 1;
|
||||||
}
|
}
|
||||||
@ -302,7 +310,7 @@ impl Engine {
|
|||||||
state: &mut State,
|
state: &mut State,
|
||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
hash_fn: NonZeroU64,
|
hash_native: u64,
|
||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
is_ref: bool,
|
is_ref: bool,
|
||||||
is_op_assignment: bool,
|
is_op_assignment: bool,
|
||||||
@ -318,8 +326,8 @@ impl Engine {
|
|||||||
state,
|
state,
|
||||||
lib,
|
lib,
|
||||||
fn_name,
|
fn_name,
|
||||||
hash_fn,
|
hash_native,
|
||||||
args,
|
Some(args),
|
||||||
true,
|
true,
|
||||||
is_op_assignment,
|
is_op_assignment,
|
||||||
);
|
);
|
||||||
@ -569,71 +577,37 @@ impl Engine {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Has a system function an override?
|
// Does a scripted function exist?
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn has_override_by_name_and_arguments(
|
pub(crate) fn has_script_fn(
|
||||||
&self,
|
&self,
|
||||||
mods: Option<&Imports>,
|
mods: Option<&Imports>,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
fn_name: &str,
|
hash_script: u64,
|
||||||
arg_types: &[TypeId],
|
|
||||||
) -> bool {
|
|
||||||
let arg_types = arg_types.as_ref();
|
|
||||||
|
|
||||||
self.has_override(
|
|
||||||
mods,
|
|
||||||
state,
|
|
||||||
lib,
|
|
||||||
calc_native_fn_hash(empty(), fn_name, arg_types.iter().cloned()),
|
|
||||||
calc_script_fn_hash(empty(), fn_name, arg_types.len()),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Has a system function an override?
|
|
||||||
#[inline(always)]
|
|
||||||
pub(crate) fn has_override(
|
|
||||||
&self,
|
|
||||||
mods: Option<&Imports>,
|
|
||||||
state: &mut State,
|
|
||||||
lib: &[&Module],
|
|
||||||
hash_fn: Option<NonZeroU64>,
|
|
||||||
hash_script: Option<NonZeroU64>,
|
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let cache = state.fn_resolution_cache_mut();
|
let cache = state.fn_resolution_cache_mut();
|
||||||
|
|
||||||
if hash_script.map_or(false, |hash| cache.contains_key(&hash))
|
if let Some(result) = cache.get(&hash_script).map(|v| v.is_some()) {
|
||||||
|| hash_fn.map_or(false, |hash| cache.contains_key(&hash))
|
return result;
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// First check script-defined functions
|
// First check script-defined functions
|
||||||
if hash_script.map_or(false, |hash| lib.iter().any(|&m| m.contains_fn(hash, false)))
|
let result = lib.iter().any(|&m| m.contains_fn(hash_script, false))
|
||||||
//|| hash_fn.map_or(false, |hash| lib.iter().any(|&m| m.contains_fn(hash, false)))
|
|
||||||
// Then check registered functions
|
// Then check registered functions
|
||||||
|| hash_script.map_or(false, |hash| self.global_namespace.contains_fn(hash, false))
|
|| self.global_namespace.contains_fn(hash_script, false)
|
||||||
|| hash_fn.map_or(false, |hash| self.global_namespace.contains_fn(hash, false))
|
|
||||||
// Then check packages
|
// Then check packages
|
||||||
|| hash_script.map_or(false, |hash| self.global_modules.iter().any(|m| m.contains_fn(hash, false)))
|
|| self.global_modules.iter().any(|m| m.contains_fn(hash_script, false))
|
||||||
|| hash_fn.map_or(false, |hash| self.global_modules.iter().any(|m| m.contains_fn(hash, false)))
|
|
||||||
// Then check imported modules
|
// Then check imported modules
|
||||||
|| hash_script.map_or(false, |hash| mods.map_or(false, |m| m.contains_fn(hash)))
|
|| mods.map_or(false, |m| m.contains_fn(hash_script))
|
||||||
|| hash_fn.map_or(false, |hash| mods.map_or(false, |m| m.contains_fn(hash)))
|
|
||||||
// Then check sub-modules
|
// Then check sub-modules
|
||||||
|| hash_script.map_or(false, |hash| self.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash)))
|
|| self.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash_script));
|
||||||
|| hash_fn.map_or(false, |hash| self.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash)))
|
|
||||||
{
|
if !result {
|
||||||
true
|
|
||||||
} else {
|
|
||||||
if let Some(hash_fn) = hash_fn {
|
|
||||||
cache.insert(hash_fn, None);
|
|
||||||
}
|
|
||||||
if let Some(hash_script) = hash_script {
|
|
||||||
cache.insert(hash_script, None);
|
cache.insert(hash_script, None);
|
||||||
}
|
}
|
||||||
false
|
|
||||||
}
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform an actual function call, native Rust or scripted, taking care of special functions.
|
/// Perform an actual function call, native Rust or scripted, taking care of special functions.
|
||||||
@ -649,7 +623,7 @@ impl Engine {
|
|||||||
state: &mut State,
|
state: &mut State,
|
||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
hash_script: Option<NonZeroU64>,
|
hash: FnHash,
|
||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
is_ref: bool,
|
is_ref: bool,
|
||||||
_is_method: bool,
|
_is_method: bool,
|
||||||
@ -675,18 +649,17 @@ impl Engine {
|
|||||||
// Handle is_def_fn()
|
// Handle is_def_fn()
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
crate::engine::KEYWORD_IS_DEF_FN
|
crate::engine::KEYWORD_IS_DEF_FN
|
||||||
if args.len() == 2 && args[0].is::<FnPtr>() && args[1].is::<INT>() =>
|
if args.len() == 2 && args[0].is::<FnPtr>() && args[1].is::<crate::INT>() =>
|
||||||
{
|
{
|
||||||
let fn_name = mem::take(args[0]).take_immutable_string().unwrap();
|
let fn_name = args[0].read_lock::<ImmutableString>().unwrap();
|
||||||
let num_params = args[1].as_int().unwrap();
|
let num_params = args[1].as_int().unwrap();
|
||||||
|
|
||||||
return Ok((
|
return Ok((
|
||||||
if num_params < 0 {
|
if num_params < 0 {
|
||||||
Dynamic::FALSE
|
Dynamic::FALSE
|
||||||
} else {
|
} else {
|
||||||
let hash_script =
|
let hash_script = calc_fn_hash(empty(), &fn_name, num_params as usize);
|
||||||
calc_script_fn_hash(empty(), &fn_name, num_params as usize);
|
self.has_script_fn(Some(mods), state, lib, hash_script)
|
||||||
self.has_override(Some(mods), state, lib, None, hash_script)
|
|
||||||
.into()
|
.into()
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
@ -731,9 +704,16 @@ impl Engine {
|
|||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Scripted function call?
|
||||||
|
let hash_script = if hash.is_native_only() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(hash.script_hash())
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
if let Some((func, source)) = hash_script.and_then(|hash| {
|
if let Some((func, source)) = hash_script.and_then(|hash| {
|
||||||
self.resolve_function(mods, state, lib, fn_name, hash, args, false, false)
|
self.resolve_function(mods, state, lib, fn_name, hash, None, false, false)
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|(f, s)| (f.clone(), s.clone()))
|
.map(|(f, s)| (f.clone(), s.clone()))
|
||||||
}) {
|
}) {
|
||||||
@ -815,9 +795,17 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Native function call
|
// Native function call
|
||||||
let hash_fn =
|
self.call_native_fn(
|
||||||
calc_native_fn_hash(empty(), fn_name, args.iter().map(|a| a.type_id())).unwrap();
|
mods,
|
||||||
self.call_native_fn(mods, state, lib, fn_name, hash_fn, args, is_ref, false, pos)
|
state,
|
||||||
|
lib,
|
||||||
|
fn_name,
|
||||||
|
hash.native_hash(),
|
||||||
|
args,
|
||||||
|
is_ref,
|
||||||
|
false,
|
||||||
|
pos,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a list of statements with no `this` pointer.
|
/// Evaluate a list of statements with no `this` pointer.
|
||||||
@ -894,7 +882,7 @@ impl Engine {
|
|||||||
state: &mut State,
|
state: &mut State,
|
||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
hash_script: Option<NonZeroU64>,
|
mut hash: FnHash,
|
||||||
target: &mut crate::engine::Target,
|
target: &mut crate::engine::Target,
|
||||||
mut call_args: StaticVec<Dynamic>,
|
mut call_args: StaticVec<Dynamic>,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
@ -913,9 +901,8 @@ impl Engine {
|
|||||||
// Redirect function name
|
// Redirect function name
|
||||||
let fn_name = fn_ptr.fn_name();
|
let fn_name = fn_ptr.fn_name();
|
||||||
let args_len = call_args.len() + fn_ptr.curry().len();
|
let args_len = call_args.len() + fn_ptr.curry().len();
|
||||||
// Recalculate hash
|
// Recalculate hashes
|
||||||
let hash =
|
let hash = FnHash::from_script(calc_fn_hash(empty(), fn_name, args_len));
|
||||||
hash_script.and_then(|_| calc_script_fn_hash(empty(), fn_name, args_len));
|
|
||||||
// Arguments are passed as-is, adding the curried arguments
|
// Arguments are passed as-is, adding the curried arguments
|
||||||
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
|
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
|
||||||
let mut arg_values = curry
|
let mut arg_values = curry
|
||||||
@ -936,8 +923,10 @@ impl Engine {
|
|||||||
let fn_name = fn_ptr.fn_name();
|
let fn_name = fn_ptr.fn_name();
|
||||||
let args_len = call_args.len() + fn_ptr.curry().len();
|
let args_len = call_args.len() + fn_ptr.curry().len();
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
let hash =
|
let hash = FnHash::from_script_and_native(
|
||||||
hash_script.and_then(|_| calc_script_fn_hash(empty(), fn_name, args_len));
|
calc_fn_hash(empty(), fn_name, args_len),
|
||||||
|
calc_fn_hash(empty(), fn_name, args_len + 1),
|
||||||
|
);
|
||||||
// Replace the first argument with the object pointer, adding the curried arguments
|
// Replace the first argument with the object pointer, adding the curried arguments
|
||||||
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
|
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
|
||||||
let mut arg_values = once(obj)
|
let mut arg_values = once(obj)
|
||||||
@ -977,7 +966,6 @@ impl Engine {
|
|||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
let _redirected;
|
let _redirected;
|
||||||
let mut hash = hash_script;
|
|
||||||
|
|
||||||
// Check if it is a map method call in OOP style
|
// Check if it is a map method call in OOP style
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
@ -995,17 +983,14 @@ impl Engine {
|
|||||||
.enumerate()
|
.enumerate()
|
||||||
.for_each(|(i, v)| call_args.insert(i, v));
|
.for_each(|(i, v)| call_args.insert(i, v));
|
||||||
// Recalculate the hash based on the new function name and new arguments
|
// Recalculate the hash based on the new function name and new arguments
|
||||||
hash = hash_script.and_then(|_| {
|
hash = FnHash::from_script_and_native(
|
||||||
calc_script_fn_hash(empty(), fn_name, call_args.len())
|
calc_fn_hash(empty(), fn_name, call_args.len()),
|
||||||
});
|
calc_fn_hash(empty(), fn_name, call_args.len() + 1),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if hash_script.is_none() {
|
|
||||||
hash = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attached object pointer in front of the arguments
|
// Attached object pointer in front of the arguments
|
||||||
let mut arg_values = once(obj)
|
let mut arg_values = once(obj)
|
||||||
.chain(call_args.iter_mut())
|
.chain(call_args.iter_mut())
|
||||||
@ -1036,7 +1021,7 @@ impl Engine {
|
|||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
args_expr: &[Expr],
|
args_expr: &[Expr],
|
||||||
mut hash_script: Option<NonZeroU64>,
|
mut hash: FnHash,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
capture_scope: bool,
|
capture_scope: bool,
|
||||||
level: usize,
|
level: usize,
|
||||||
@ -1074,7 +1059,11 @@ impl Engine {
|
|||||||
|
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
let args_len = args_expr.len() + curry.len();
|
let args_len = args_expr.len() + curry.len();
|
||||||
hash_script = calc_script_fn_hash(empty(), name, args_len);
|
hash = if !hash.is_native_only() {
|
||||||
|
FnHash::from_script(calc_fn_hash(empty(), name, args_len))
|
||||||
|
} else {
|
||||||
|
FnHash::from_native(calc_fn_hash(empty(), name, args_len))
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle Fn()
|
// Handle Fn()
|
||||||
@ -1129,31 +1118,34 @@ impl Engine {
|
|||||||
// Handle is_def_fn()
|
// Handle is_def_fn()
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
crate::engine::KEYWORD_IS_DEF_FN if args_expr.len() == 2 => {
|
crate::engine::KEYWORD_IS_DEF_FN if args_expr.len() == 2 => {
|
||||||
let fn_name =
|
let fn_name = self
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
|
.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?
|
||||||
let fn_name = fn_name.take_immutable_string().map_err(|err| {
|
.take_immutable_string()
|
||||||
|
.map_err(|err| {
|
||||||
self.make_type_mismatch_err::<ImmutableString>(err, args_expr[0].position())
|
self.make_type_mismatch_err::<ImmutableString>(err, args_expr[0].position())
|
||||||
})?;
|
})?;
|
||||||
let num_params =
|
let num_params = self
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[1], level)?;
|
.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[1], level)?
|
||||||
let num_params = num_params.as_int().map_err(|err| {
|
.as_int()
|
||||||
self.make_type_mismatch_err::<INT>(err, args_expr[0].position())
|
.map_err(|err| {
|
||||||
|
self.make_type_mismatch_err::<crate::INT>(err, args_expr[0].position())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
return Ok(if num_params < 0 {
|
return Ok(if num_params < 0 {
|
||||||
Dynamic::FALSE
|
Dynamic::FALSE
|
||||||
} else {
|
} else {
|
||||||
let hash_script = calc_script_fn_hash(empty(), &fn_name, num_params as usize);
|
let hash_script = calc_fn_hash(empty(), &fn_name, num_params as usize);
|
||||||
self.has_override(Some(mods), state, lib, None, hash_script)
|
self.has_script_fn(Some(mods), state, lib, hash_script)
|
||||||
.into()
|
.into()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle is_def_var()
|
// Handle is_def_var()
|
||||||
KEYWORD_IS_DEF_VAR if args_expr.len() == 1 => {
|
KEYWORD_IS_DEF_VAR if args_expr.len() == 1 => {
|
||||||
let var_name =
|
let var_name = self
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
|
.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?
|
||||||
let var_name = var_name.take_immutable_string().map_err(|err| {
|
.take_immutable_string()
|
||||||
|
.map_err(|err| {
|
||||||
self.make_type_mismatch_err::<ImmutableString>(err, args_expr[0].position())
|
self.make_type_mismatch_err::<ImmutableString>(err, args_expr[0].position())
|
||||||
})?;
|
})?;
|
||||||
return Ok(scope.contains(&var_name).into());
|
return Ok(scope.contains(&var_name).into());
|
||||||
@ -1166,9 +1158,10 @@ impl Engine {
|
|||||||
|
|
||||||
// eval - only in function call style
|
// eval - only in function call style
|
||||||
let prev_len = scope.len();
|
let prev_len = scope.len();
|
||||||
let script =
|
let script = self
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, script_expr, level)?;
|
.eval_expr(scope, mods, state, lib, this_ptr, script_expr, level)?
|
||||||
let script = script.take_immutable_string().map_err(|typ| {
|
.take_immutable_string()
|
||||||
|
.map_err(|typ| {
|
||||||
self.make_type_mismatch_err::<ImmutableString>(typ, script_pos)
|
self.make_type_mismatch_err::<ImmutableString>(typ, script_pos)
|
||||||
})?;
|
})?;
|
||||||
let result = self.eval_script_expr_in_place(
|
let result = self.eval_script_expr_in_place(
|
||||||
@ -1262,17 +1255,7 @@ impl Engine {
|
|||||||
let args = args.as_mut();
|
let args = args.as_mut();
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods,
|
mods, state, lib, name, hash, args, is_ref, false, pos, capture, level,
|
||||||
state,
|
|
||||||
lib,
|
|
||||||
name,
|
|
||||||
hash_script,
|
|
||||||
args,
|
|
||||||
is_ref,
|
|
||||||
false,
|
|
||||||
pos,
|
|
||||||
capture,
|
|
||||||
level,
|
|
||||||
)
|
)
|
||||||
.map(|(v, _)| v)
|
.map(|(v, _)| v)
|
||||||
}
|
}
|
||||||
@ -1288,7 +1271,7 @@ impl Engine {
|
|||||||
namespace: Option<&NamespaceRef>,
|
namespace: Option<&NamespaceRef>,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
args_expr: &[Expr],
|
args_expr: &[Expr],
|
||||||
hash_script: NonZeroU64,
|
hash: u64,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
@ -1353,20 +1336,13 @@ impl Engine {
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
// First search in script-defined functions (can override built-in)
|
// First search in script-defined functions (can override built-in)
|
||||||
let func = match module.get_qualified_fn(hash_script) {
|
let func = match module.get_qualified_fn(hash) {
|
||||||
// Then search in Rust functions
|
// Then search in Rust functions
|
||||||
None => {
|
None => {
|
||||||
self.inc_operations(state, pos)?;
|
self.inc_operations(state, pos)?;
|
||||||
|
|
||||||
// Namespace-qualified Rust functions are indexed in two steps:
|
let hash_params = calc_fn_params_hash(args.iter().map(|a| a.type_id()));
|
||||||
// 1) Calculate a hash in a similar manner to script-defined functions,
|
let hash_qualified_fn = combine_hashes(hash, hash_params);
|
||||||
// i.e. qualifiers + function name + number of arguments.
|
|
||||||
// 2) Calculate a second hash with no qualifiers, empty function name,
|
|
||||||
// and the actual list of argument `TypeId`'.s
|
|
||||||
let hash_fn_args =
|
|
||||||
calc_native_fn_hash(empty(), "", args.iter().map(|a| a.type_id())).unwrap();
|
|
||||||
// 3) The two hashes are combined.
|
|
||||||
let hash_qualified_fn = combine_hashes(hash_script, hash_fn_args);
|
|
||||||
|
|
||||||
module.get_qualified_fn(hash_qualified_fn)
|
module.get_qualified_fn(hash_qualified_fn)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Module defining interfaces to native-Rust functions.
|
//! Module defining interfaces to native-Rust functions.
|
||||||
|
|
||||||
use crate::ast::{FnAccess, ScriptFnDef};
|
use crate::ast::{FnAccess, FnHash};
|
||||||
use crate::engine::Imports;
|
use crate::engine::Imports;
|
||||||
use crate::plugin::PluginFunction;
|
use crate::plugin::PluginFunction;
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
@ -14,8 +14,8 @@ use crate::stdlib::{
|
|||||||
};
|
};
|
||||||
use crate::token::is_valid_identifier;
|
use crate::token::is_valid_identifier;
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_script_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, ImmutableString, Module,
|
calc_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, ImmutableString, Module, Position,
|
||||||
Position, RhaiResult,
|
RhaiResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Trait that maps to `Send + Sync` only under the `sync` feature.
|
/// Trait that maps to `Send + Sync` only under the `sync` feature.
|
||||||
@ -143,6 +143,7 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
}
|
}
|
||||||
/// Get an iterator over the current set of modules imported via `import` statements.
|
/// Get an iterator over the current set of modules imported via `import` statements.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
#[allow(dead_code)]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn iter_imports_raw(
|
pub(crate) fn iter_imports_raw(
|
||||||
&self,
|
&self,
|
||||||
@ -188,13 +189,22 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
is_method: bool,
|
is_method: bool,
|
||||||
args: &mut [&mut Dynamic],
|
args: &mut [&mut Dynamic],
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
|
let hash = if is_method {
|
||||||
|
FnHash::from_script_and_native(
|
||||||
|
calc_fn_hash(empty(), fn_name, args.len() - 1),
|
||||||
|
calc_fn_hash(empty(), fn_name, args.len()),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
FnHash::from_script(calc_fn_hash(empty(), fn_name, args.len()))
|
||||||
|
};
|
||||||
|
|
||||||
self.engine()
|
self.engine()
|
||||||
.exec_fn_call(
|
.exec_fn_call(
|
||||||
&mut self.mods.cloned().unwrap_or_default(),
|
&mut self.mods.cloned().unwrap_or_default(),
|
||||||
&mut Default::default(),
|
&mut Default::default(),
|
||||||
self.lib,
|
self.lib,
|
||||||
fn_name,
|
fn_name,
|
||||||
calc_script_fn_hash(empty(), fn_name, args.len() - if is_method { 1 } else { 0 }),
|
hash,
|
||||||
args,
|
args,
|
||||||
is_method,
|
is_method,
|
||||||
is_method,
|
is_method,
|
||||||
@ -438,7 +448,7 @@ pub enum CallableFunction {
|
|||||||
Plugin(Shared<FnPlugin>),
|
Plugin(Shared<FnPlugin>),
|
||||||
/// A script-defined function.
|
/// A script-defined function.
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
Script(Shared<ScriptFnDef>),
|
Script(Shared<crate::ast::ScriptFnDef>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for CallableFunction {
|
impl fmt::Debug for CallableFunction {
|
||||||
@ -576,7 +586,7 @@ impl CallableFunction {
|
|||||||
/// Panics if the [`CallableFunction`] is not [`Script`][CallableFunction::Script].
|
/// Panics if the [`CallableFunction`] is not [`Script`][CallableFunction::Script].
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn get_fn_def(&self) -> &ScriptFnDef {
|
pub fn get_fn_def(&self) -> &crate::ast::ScriptFnDef {
|
||||||
match self {
|
match self {
|
||||||
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Plugin(_) => {
|
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Plugin(_) => {
|
||||||
panic!("function should be scripted")
|
panic!("function should be scripted")
|
||||||
@ -642,24 +652,18 @@ impl From<IteratorFn> for CallableFunction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ScriptFnDef> for CallableFunction {
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
impl From<crate::ast::ScriptFnDef> for CallableFunction {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(_func: ScriptFnDef) -> Self {
|
fn from(_func: crate::ast::ScriptFnDef) -> Self {
|
||||||
#[cfg(feature = "no_function")]
|
|
||||||
unreachable!("no_function active");
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
Self::Script(_func.into())
|
Self::Script(_func.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Shared<ScriptFnDef>> for CallableFunction {
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
impl From<Shared<crate::ast::ScriptFnDef>> for CallableFunction {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(_func: Shared<ScriptFnDef>) -> Self {
|
fn from(_func: Shared<crate::ast::ScriptFnDef>) -> Self {
|
||||||
#[cfg(feature = "no_function")]
|
|
||||||
unreachable!("no_function active");
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
Self::Script(_func)
|
Self::Script(_func)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,7 @@ use crate::dynamic::{DynamicWriteLock, Variant};
|
|||||||
use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync};
|
use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync};
|
||||||
use crate::r#unsafe::unsafe_cast_box;
|
use crate::r#unsafe::unsafe_cast_box;
|
||||||
use crate::stdlib::{any::TypeId, boxed::Box, mem, string::String};
|
use crate::stdlib::{any::TypeId, boxed::Box, mem, string::String};
|
||||||
use crate::{
|
use crate::{Dynamic, Engine, FnAccess, FnNamespace, NativeCallContext, RhaiResult};
|
||||||
Dynamic, Engine, FnAccess, FnNamespace, ImmutableString, NativeCallContext, RhaiResult,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Trait to register custom functions with the [`Engine`].
|
/// Trait to register custom functions with the [`Engine`].
|
||||||
pub trait RegisterFn<FN, ARGS, RET> {
|
pub trait RegisterFn<FN, ARGS, RET> {
|
||||||
@ -100,13 +98,16 @@ pub fn by_ref<T: Variant + Clone>(data: &mut Dynamic) -> DynamicWriteLock<T> {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T {
|
pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T {
|
||||||
if TypeId::of::<T>() == TypeId::of::<&str>() {
|
if TypeId::of::<T>() == TypeId::of::<&str>() {
|
||||||
// If T is &str, data must be ImmutableString, so map directly to it
|
// If T is `&str`, data must be `ImmutableString`, so map directly to it
|
||||||
let ref_str = data.as_str().unwrap();
|
data.flatten_in_place();
|
||||||
let ref_T = unsafe { mem::transmute::<_, &T>(&ref_str) };
|
let ref_str = data
|
||||||
ref_T.clone()
|
.as_str_ref()
|
||||||
|
.expect("argument passed by value should not be shared");
|
||||||
|
let ref_t = unsafe { mem::transmute::<_, &T>(&ref_str) };
|
||||||
|
ref_t.clone()
|
||||||
} else if TypeId::of::<T>() == TypeId::of::<String>() {
|
} else 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
|
||||||
*unsafe_cast_box(Box::new(data.clone().take_string().unwrap())).unwrap()
|
*unsafe_cast_box(Box::new(mem::take(data).take_string().unwrap())).unwrap()
|
||||||
} else {
|
} else {
|
||||||
// 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.
|
||||||
// This way, we avoid having to clone the argument again, because it is already a clone when passed here.
|
// This way, we avoid having to clone the argument again, because it is already a clone when passed here.
|
||||||
@ -128,8 +129,7 @@ macro_rules! make_func {
|
|||||||
// The arguments are assumed to be of the correct number and types!
|
// The arguments are assumed to be of the correct number and types!
|
||||||
|
|
||||||
let mut _drain = args.iter_mut();
|
let mut _drain = args.iter_mut();
|
||||||
$($let)*
|
$($let $par = ($convert)(_drain.next().unwrap()); )*
|
||||||
$($par = ($convert)(_drain.next().unwrap()); )*
|
|
||||||
|
|
||||||
// Call the function with each argument value
|
// Call the function with each argument value
|
||||||
let r = $fn($($arg),*);
|
let r = $fn($($arg),*);
|
||||||
@ -152,20 +152,6 @@ pub fn map_result(data: RhaiResult) -> RhaiResult {
|
|||||||
data
|
data
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remap `&str` | `String` to `ImmutableString`.
|
|
||||||
#[inline(always)]
|
|
||||||
fn map_type_id<T: 'static>() -> TypeId {
|
|
||||||
let id = TypeId::of::<T>();
|
|
||||||
|
|
||||||
if id == TypeId::of::<&str>() {
|
|
||||||
TypeId::of::<ImmutableString>()
|
|
||||||
} else if id == TypeId::of::<String>() {
|
|
||||||
TypeId::of::<ImmutableString>()
|
|
||||||
} else {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! def_register {
|
macro_rules! def_register {
|
||||||
() => {
|
() => {
|
||||||
def_register!(imp from_pure :);
|
def_register!(imp from_pure :);
|
||||||
@ -186,7 +172,7 @@ macro_rules! def_register {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn register_fn(&mut self, name: &str, f: FN) -> &mut Self {
|
fn register_fn(&mut self, name: &str, f: FN) -> &mut Self {
|
||||||
self.global_namespace.set_fn(name, FnNamespace::Global, FnAccess::Public, None,
|
self.global_namespace.set_fn(name, FnNamespace::Global, FnAccess::Public, None,
|
||||||
&[$(map_type_id::<$par>()),*],
|
&[$(TypeId::of::<$par>()),*],
|
||||||
CallableFunction::$abi(make_func!(f : map_dynamic ; $($par => $let => $clone => $arg),*))
|
CallableFunction::$abi(make_func!(f : map_dynamic ; $($par => $let => $clone => $arg),*))
|
||||||
);
|
);
|
||||||
self
|
self
|
||||||
@ -201,7 +187,7 @@ macro_rules! def_register {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn register_result_fn(&mut self, name: &str, f: FN) -> &mut Self {
|
fn register_result_fn(&mut self, name: &str, f: FN) -> &mut Self {
|
||||||
self.global_namespace.set_fn(name, FnNamespace::Global, FnAccess::Public, None,
|
self.global_namespace.set_fn(name, FnNamespace::Global, FnAccess::Public, None,
|
||||||
&[$(map_type_id::<$par>()),*],
|
&[$(TypeId::of::<$par>()),*],
|
||||||
CallableFunction::$abi(make_func!(f : map_result ; $($par => $let => $clone => $arg),*))
|
CallableFunction::$abi(make_func!(f : map_result ; $($par => $let => $clone => $arg),*))
|
||||||
);
|
);
|
||||||
self
|
self
|
||||||
@ -213,8 +199,8 @@ macro_rules! def_register {
|
|||||||
($p0:ident $(, $p:ident)*) => {
|
($p0:ident $(, $p:ident)*) => {
|
||||||
def_register!(imp from_pure : $p0 => $p0 => $p0 => $p0 => let $p0 => by_value $(, $p => $p => $p => $p => let $p => by_value)*);
|
def_register!(imp from_pure : $p0 => $p0 => $p0 => $p0 => let $p0 => by_value $(, $p => $p => $p => $p => let $p => by_value)*);
|
||||||
def_register!(imp from_method : $p0 => &mut $p0 => Mut<$p0> => &mut $p0 => let mut $p0 => by_ref $(, $p => $p => $p => $p => let $p => by_value)*);
|
def_register!(imp from_method : $p0 => &mut $p0 => Mut<$p0> => &mut $p0 => let mut $p0 => by_ref $(, $p => $p => $p => $p => let $p => by_value)*);
|
||||||
// ^ CallableFunction
|
// ^ CallableFunction constructor
|
||||||
// handle the first parameter ^ first parameter passed through
|
// ^ first parameter passed through
|
||||||
// ^ others passed by value (by_value)
|
// ^ others passed by value (by_value)
|
||||||
|
|
||||||
// Currently does not support first argument which is a reference, as there will be
|
// Currently does not support first argument which is a reference, as there will be
|
||||||
|
15
src/lib.rs
15
src/lib.rs
@ -122,7 +122,7 @@ pub type FLOAT = f64;
|
|||||||
#[cfg(feature = "f32_float")]
|
#[cfg(feature = "f32_float")]
|
||||||
pub type FLOAT = f32;
|
pub type FLOAT = f32;
|
||||||
|
|
||||||
pub use ast::{FnAccess, ScriptFnMetadata, AST};
|
pub use ast::{FnAccess, AST};
|
||||||
pub use dynamic::Dynamic;
|
pub use dynamic::Dynamic;
|
||||||
pub use engine::{Engine, EvalContext};
|
pub use engine::{Engine, EvalContext};
|
||||||
pub use fn_native::{FnPtr, NativeCallContext};
|
pub use fn_native::{FnPtr, NativeCallContext};
|
||||||
@ -141,11 +141,7 @@ pub use fn_native::Shared;
|
|||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
use fn_native::Locked;
|
use fn_native::Locked;
|
||||||
|
|
||||||
#[cfg(feature = "internals")]
|
pub(crate) use utils::{calc_fn_hash, calc_fn_params_hash, combine_hashes};
|
||||||
pub use utils::{calc_native_fn_hash, calc_script_fn_hash, HashableHashMap};
|
|
||||||
|
|
||||||
#[cfg(not(feature = "internals"))]
|
|
||||||
pub(crate) use utils::{calc_native_fn_hash, calc_script_fn_hash};
|
|
||||||
|
|
||||||
pub use rhai_codegen::*;
|
pub use rhai_codegen::*;
|
||||||
|
|
||||||
@ -155,6 +151,9 @@ pub use fn_func::Func;
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub use fn_args::FuncArgs;
|
pub use fn_args::FuncArgs;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
pub use ast::ScriptFnMetadata;
|
||||||
|
|
||||||
/// Variable-sized array of [`Dynamic`] values.
|
/// Variable-sized array of [`Dynamic`] values.
|
||||||
///
|
///
|
||||||
/// Not available under `no_index`.
|
/// Not available under `no_index`.
|
||||||
@ -192,8 +191,8 @@ pub use token::{get_next_token, parse_string_literal, InputStream, Token, Tokeni
|
|||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[deprecated = "this type is volatile and may change"]
|
#[deprecated = "this type is volatile and may change"]
|
||||||
pub use ast::{
|
pub use ast::{
|
||||||
ASTNode, BinaryExpr, CustomExpr, Expr, FloatWrapper, FnCallExpr, Ident, ReturnType,
|
ASTNode, BinaryExpr, CustomExpr, Expr, FloatWrapper, FnCallExpr, FnHash, Ident, OpAssignment,
|
||||||
ScriptFnDef, Stmt,
|
ReturnType, ScriptFnDef, Stmt,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
|
@ -10,21 +10,18 @@ use crate::stdlib::{
|
|||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fmt, format,
|
fmt, format,
|
||||||
iter::empty,
|
iter::empty,
|
||||||
num::NonZeroU64,
|
|
||||||
num::NonZeroUsize,
|
num::NonZeroUsize,
|
||||||
ops::{Add, AddAssign, Deref, DerefMut},
|
ops::{Add, AddAssign, Deref, DerefMut},
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
use crate::token::Token;
|
use crate::token::Token;
|
||||||
use crate::utils::{combine_hashes, StraightHasherBuilder};
|
use crate::utils::StraightHasherBuilder;
|
||||||
use crate::{
|
use crate::{
|
||||||
Dynamic, EvalAltResult, ImmutableString, NativeCallContext, Position, Shared, StaticVec,
|
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, EvalAltResult, ImmutableString,
|
||||||
|
NativeCallContext, Position, Shared, StaticVec,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
use crate::ast::ScriptFnDef;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
use crate::Array;
|
use crate::Array;
|
||||||
|
|
||||||
@ -104,6 +101,27 @@ impl FuncInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// _(INTERNALS)_ Calculate a [`u64`] hash key from a namespace-qualified function name and
|
||||||
|
/// parameter types.
|
||||||
|
/// Exported under the `internals` feature only.
|
||||||
|
///
|
||||||
|
/// Module names are passed in via `&str` references from an iterator.
|
||||||
|
/// Parameter types are passed in via [`TypeId`] values from an iterator.
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
///
|
||||||
|
/// The first module name is skipped. Hashing starts from the _second_ module in the chain.
|
||||||
|
#[inline(always)]
|
||||||
|
fn calc_native_fn_hash<'a>(
|
||||||
|
modules: impl Iterator<Item = &'a str>,
|
||||||
|
fn_name: &str,
|
||||||
|
params: &[TypeId],
|
||||||
|
) -> u64 {
|
||||||
|
let hash_script = calc_fn_hash(modules, fn_name, params.len());
|
||||||
|
let hash_params = calc_fn_params_hash(params.iter().cloned());
|
||||||
|
combine_hashes(hash_script, hash_params)
|
||||||
|
}
|
||||||
|
|
||||||
/// A module which may contain variables, sub-modules, external Rust functions,
|
/// A module which may contain variables, sub-modules, external Rust functions,
|
||||||
/// and/or script-defined functions.
|
/// and/or script-defined functions.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -115,12 +133,12 @@ pub struct Module {
|
|||||||
/// [`Module`] variables.
|
/// [`Module`] variables.
|
||||||
variables: HashMap<ImmutableString, Dynamic>,
|
variables: HashMap<ImmutableString, Dynamic>,
|
||||||
/// Flattened collection of all [`Module`] variables, including those in sub-modules.
|
/// Flattened collection of all [`Module`] variables, including those in sub-modules.
|
||||||
all_variables: HashMap<NonZeroU64, Dynamic, StraightHasherBuilder>,
|
all_variables: HashMap<u64, Dynamic, StraightHasherBuilder>,
|
||||||
/// External Rust functions.
|
/// External Rust functions.
|
||||||
functions: HashMap<NonZeroU64, FuncInfo, StraightHasherBuilder>,
|
functions: HashMap<u64, FuncInfo, StraightHasherBuilder>,
|
||||||
/// Flattened collection of all external Rust functions, native or scripted.
|
/// Flattened collection of all external Rust functions, native or scripted.
|
||||||
/// including those in sub-modules.
|
/// including those in sub-modules.
|
||||||
all_functions: HashMap<NonZeroU64, CallableFunction, StraightHasherBuilder>,
|
all_functions: HashMap<u64, CallableFunction, StraightHasherBuilder>,
|
||||||
/// Iterator functions, keyed by the type producing the iterator.
|
/// Iterator functions, keyed by the type producing the iterator.
|
||||||
type_iterators: HashMap<TypeId, IteratorFn>,
|
type_iterators: HashMap<TypeId, IteratorFn>,
|
||||||
/// Flattened collection of iterator functions, including those in sub-modules.
|
/// Flattened collection of iterator functions, including those in sub-modules.
|
||||||
@ -437,19 +455,13 @@ impl Module {
|
|||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.variables.insert(name.into(), Dynamic::from(value));
|
self.variables.insert(name.into(), Dynamic::from(value));
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
self.contains_indexed_global_functions = false;
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a reference to a namespace-qualified variable.
|
/// Get a reference to a namespace-qualified variable.
|
||||||
/// Name and Position in [`EvalAltResult`] are [`None`] and [`NONE`][Position::NONE] and must be set afterwards.
|
/// Name and Position in [`EvalAltResult`] are [`None`] and [`NONE`][Position::NONE] and must be set afterwards.
|
||||||
///
|
|
||||||
/// The [`NonZeroU64`] hash is calculated by the function [`calc_native_fn_hash`][crate::calc_native_fn_hash].
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn get_qualified_var(
|
pub(crate) fn get_qualified_var(&self, hash_var: u64) -> Result<&Dynamic, Box<EvalAltResult>> {
|
||||||
&self,
|
|
||||||
hash_var: NonZeroU64,
|
|
||||||
) -> Result<&Dynamic, Box<EvalAltResult>> {
|
|
||||||
self.all_variables.get(&hash_var).ok_or_else(|| {
|
self.all_variables.get(&hash_var).ok_or_else(|| {
|
||||||
EvalAltResult::ErrorVariableNotFound(String::new(), Position::NONE).into()
|
EvalAltResult::ErrorVariableNotFound(String::new(), Position::NONE).into()
|
||||||
})
|
})
|
||||||
@ -460,12 +472,15 @@ impl Module {
|
|||||||
/// If there is an existing function of the same name and number of arguments, it is replaced.
|
/// If there is an existing function of the same name and number of arguments, it is replaced.
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn set_script_fn(&mut self, fn_def: impl Into<Shared<ScriptFnDef>>) -> NonZeroU64 {
|
pub(crate) fn set_script_fn(
|
||||||
|
&mut self,
|
||||||
|
fn_def: impl Into<Shared<crate::ast::ScriptFnDef>>,
|
||||||
|
) -> u64 {
|
||||||
let fn_def = fn_def.into();
|
let fn_def = fn_def.into();
|
||||||
|
|
||||||
// None + function name + number of arguments.
|
// None + function name + number of arguments.
|
||||||
let num_params = fn_def.params.len();
|
let num_params = fn_def.params.len();
|
||||||
let hash_script = crate::calc_script_fn_hash(empty(), &fn_def.name, num_params).unwrap();
|
let hash_script = crate::calc_fn_hash(empty(), &fn_def.name, num_params);
|
||||||
let mut param_names: StaticVec<_> = fn_def.params.iter().cloned().collect();
|
let mut param_names: StaticVec<_> = fn_def.params.iter().cloned().collect();
|
||||||
param_names.push("Dynamic".into());
|
param_names.push("Dynamic".into());
|
||||||
self.functions.insert(
|
self.functions.insert(
|
||||||
@ -493,7 +508,7 @@ impl Module {
|
|||||||
name: &str,
|
name: &str,
|
||||||
num_params: usize,
|
num_params: usize,
|
||||||
public_only: bool,
|
public_only: bool,
|
||||||
) -> Option<&ScriptFnDef> {
|
) -> Option<&crate::ast::ScriptFnDef> {
|
||||||
self.functions
|
self.functions
|
||||||
.values()
|
.values()
|
||||||
.find(
|
.find(
|
||||||
@ -593,8 +608,7 @@ impl Module {
|
|||||||
|
|
||||||
/// Does the particular Rust function exist in the [`Module`]?
|
/// Does the particular Rust function exist in the [`Module`]?
|
||||||
///
|
///
|
||||||
/// The [`NonZeroU64`] hash is calculated by the function [`calc_native_fn_hash`][crate::calc_native_fn_hash].
|
/// The [`u64`] hash is returned by the `set_fn_XXX` calls.
|
||||||
/// It is also returned by the `set_fn_XXX` calls.
|
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -606,7 +620,7 @@ impl Module {
|
|||||||
/// assert!(module.contains_fn(hash, true));
|
/// assert!(module.contains_fn(hash, true));
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn contains_fn(&self, hash_fn: NonZeroU64, public_only: bool) -> bool {
|
pub fn contains_fn(&self, hash_fn: u64, public_only: bool) -> bool {
|
||||||
if public_only {
|
if public_only {
|
||||||
self.functions
|
self.functions
|
||||||
.get(&hash_fn)
|
.get(&hash_fn)
|
||||||
@ -621,9 +635,7 @@ impl Module {
|
|||||||
|
|
||||||
/// Update the metadata (parameter names/types and return type) of a registered function.
|
/// Update the metadata (parameter names/types and return type) of a registered function.
|
||||||
///
|
///
|
||||||
/// The [`NonZeroU64`] hash is calculated either by the function
|
/// The [`u64`] hash is returned by the `set_fn_XXX` calls.
|
||||||
/// [`calc_native_fn_hash`][crate::calc_native_fn_hash] or the function
|
|
||||||
/// [`calc_script_fn_hash`][crate::calc_script_fn_hash].
|
|
||||||
///
|
///
|
||||||
/// ## Parameter Names and Types
|
/// ## Parameter Names and Types
|
||||||
///
|
///
|
||||||
@ -634,7 +646,7 @@ impl Module {
|
|||||||
/// The _last entry_ in the list should be the _return type_ of the function.
|
/// The _last entry_ in the list should be the _return type_ of the function.
|
||||||
/// In other words, the number of entries should be one larger than the number of parameters.
|
/// In other words, the number of entries should be one larger than the number of parameters.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn update_fn_metadata(&mut self, hash_fn: NonZeroU64, arg_names: &[&str]) -> &mut Self {
|
pub fn update_fn_metadata(&mut self, hash_fn: u64, arg_names: &[&str]) -> &mut Self {
|
||||||
if let Some(f) = self.functions.get_mut(&hash_fn) {
|
if let Some(f) = self.functions.get_mut(&hash_fn) {
|
||||||
f.param_names = arg_names.iter().map(|&n| n.into()).collect();
|
f.param_names = arg_names.iter().map(|&n| n.into()).collect();
|
||||||
}
|
}
|
||||||
@ -643,15 +655,9 @@ impl Module {
|
|||||||
|
|
||||||
/// Update the namespace of a registered function.
|
/// Update the namespace of a registered function.
|
||||||
///
|
///
|
||||||
/// The [`NonZeroU64`] hash is calculated either by the function
|
/// The [`u64`] hash is returned by the `set_fn_XXX` calls.
|
||||||
/// [`calc_native_fn_hash`][crate::calc_native_fn_hash] or the function
|
|
||||||
/// [`calc_script_fn_hash`][crate::calc_script_fn_hash].
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn update_fn_namespace(
|
pub fn update_fn_namespace(&mut self, hash_fn: u64, namespace: FnNamespace) -> &mut Self {
|
||||||
&mut self,
|
|
||||||
hash_fn: NonZeroU64,
|
|
||||||
namespace: FnNamespace,
|
|
||||||
) -> &mut Self {
|
|
||||||
if let Some(f) = self.functions.get_mut(&hash_fn) {
|
if let Some(f) = self.functions.get_mut(&hash_fn) {
|
||||||
f.namespace = namespace;
|
f.namespace = namespace;
|
||||||
}
|
}
|
||||||
@ -676,24 +682,34 @@ impl Module {
|
|||||||
arg_names: Option<&[&str]>,
|
arg_names: Option<&[&str]>,
|
||||||
arg_types: &[TypeId],
|
arg_types: &[TypeId],
|
||||||
func: CallableFunction,
|
func: CallableFunction,
|
||||||
) -> NonZeroU64 {
|
) -> u64 {
|
||||||
let name = name.into();
|
let name = name.into();
|
||||||
|
let is_method = func.is_method();
|
||||||
let hash_fn =
|
|
||||||
crate::calc_native_fn_hash(empty(), &name, arg_types.iter().cloned()).unwrap();
|
|
||||||
|
|
||||||
let param_types = arg_types
|
let param_types = arg_types
|
||||||
.into_iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(|id| {
|
.enumerate()
|
||||||
if id == TypeId::of::<&str>() || id == TypeId::of::<String>() {
|
.map(|(i, type_id)| {
|
||||||
|
if !is_method || i > 0 {
|
||||||
|
if type_id == TypeId::of::<&str>() {
|
||||||
|
// Map &str to ImmutableString
|
||||||
|
TypeId::of::<ImmutableString>()
|
||||||
|
} else if type_id == TypeId::of::<String>() {
|
||||||
|
// Map String to ImmutableString
|
||||||
TypeId::of::<ImmutableString>()
|
TypeId::of::<ImmutableString>()
|
||||||
} else {
|
} else {
|
||||||
id
|
type_id
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Do not map &mut parameter
|
||||||
|
type_id
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<StaticVec<_>>();
|
.collect::<StaticVec<_>>();
|
||||||
|
|
||||||
|
let hash_fn = calc_native_fn_hash(empty(), &name, ¶m_types);
|
||||||
|
|
||||||
self.functions.insert(
|
self.functions.insert(
|
||||||
hash_fn,
|
hash_fn,
|
||||||
FuncInfo {
|
FuncInfo {
|
||||||
@ -793,7 +809,7 @@ impl Module {
|
|||||||
func: impl Fn(NativeCallContext, &mut FnCallArgs) -> Result<T, Box<EvalAltResult>>
|
func: impl Fn(NativeCallContext, &mut FnCallArgs) -> Result<T, Box<EvalAltResult>>
|
||||||
+ SendSync
|
+ SendSync
|
||||||
+ 'static,
|
+ 'static,
|
||||||
) -> NonZeroU64 {
|
) -> u64 {
|
||||||
let f =
|
let f =
|
||||||
move |ctx: NativeCallContext, args: &mut FnCallArgs| func(ctx, args).map(Dynamic::from);
|
move |ctx: NativeCallContext, args: &mut FnCallArgs| func(ctx, args).map(Dynamic::from);
|
||||||
|
|
||||||
@ -829,7 +845,7 @@ impl Module {
|
|||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
func: impl Fn() -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
func: impl Fn() -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> NonZeroU64 {
|
) -> u64 {
|
||||||
let f = move |_: NativeCallContext, _: &mut FnCallArgs| func().map(Dynamic::from);
|
let f = move |_: NativeCallContext, _: &mut FnCallArgs| func().map(Dynamic::from);
|
||||||
let arg_types = [];
|
let arg_types = [];
|
||||||
self.set_fn(
|
self.set_fn(
|
||||||
@ -864,7 +880,7 @@ impl Module {
|
|||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
func: impl Fn(A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
func: impl Fn(A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> NonZeroU64 {
|
) -> u64 {
|
||||||
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
func(cast_arg::<A>(&mut args[0])).map(Dynamic::from)
|
func(cast_arg::<A>(&mut args[0])).map(Dynamic::from)
|
||||||
};
|
};
|
||||||
@ -904,7 +920,7 @@ impl Module {
|
|||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
namespace: FnNamespace,
|
namespace: FnNamespace,
|
||||||
func: impl Fn(&mut A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
func: impl Fn(&mut A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> NonZeroU64 {
|
) -> u64 {
|
||||||
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
func(&mut args[0].write_lock::<A>().unwrap()).map(Dynamic::from)
|
func(&mut args[0].write_lock::<A>().unwrap()).map(Dynamic::from)
|
||||||
};
|
};
|
||||||
@ -943,7 +959,7 @@ impl Module {
|
|||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
func: impl Fn(&mut A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
func: impl Fn(&mut A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> NonZeroU64 {
|
) -> u64 {
|
||||||
self.set_fn_1_mut(
|
self.set_fn_1_mut(
|
||||||
crate::engine::make_getter(&name.into()),
|
crate::engine::make_getter(&name.into()),
|
||||||
FnNamespace::Global,
|
FnNamespace::Global,
|
||||||
@ -975,7 +991,7 @@ impl Module {
|
|||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
func: impl Fn(A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
func: impl Fn(A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> NonZeroU64 {
|
) -> u64 {
|
||||||
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
let a = cast_arg::<A>(&mut args[0]);
|
let a = cast_arg::<A>(&mut args[0]);
|
||||||
let b = cast_arg::<B>(&mut args[1]);
|
let b = cast_arg::<B>(&mut args[1]);
|
||||||
@ -1022,7 +1038,7 @@ impl Module {
|
|||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
namespace: FnNamespace,
|
namespace: FnNamespace,
|
||||||
func: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
func: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> NonZeroU64 {
|
) -> u64 {
|
||||||
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
let b = cast_arg::<B>(&mut args[1]);
|
let b = cast_arg::<B>(&mut args[1]);
|
||||||
let a = &mut args[0].write_lock::<A>().unwrap();
|
let a = &mut args[0].write_lock::<A>().unwrap();
|
||||||
@ -1068,7 +1084,7 @@ impl Module {
|
|||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
func: impl Fn(&mut A, B) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
func: impl Fn(&mut A, B) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> NonZeroU64 {
|
) -> u64 {
|
||||||
self.set_fn_2_mut(
|
self.set_fn_2_mut(
|
||||||
crate::engine::make_setter(&name.into()),
|
crate::engine::make_setter(&name.into()),
|
||||||
FnNamespace::Global,
|
FnNamespace::Global,
|
||||||
@ -1107,7 +1123,7 @@ impl Module {
|
|||||||
pub fn set_indexer_get_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
pub fn set_indexer_get_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
func: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
func: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> NonZeroU64 {
|
) -> u64 {
|
||||||
if TypeId::of::<A>() == TypeId::of::<Array>() {
|
if TypeId::of::<A>() == TypeId::of::<Array>() {
|
||||||
panic!("Cannot register indexer for arrays.");
|
panic!("Cannot register indexer for arrays.");
|
||||||
}
|
}
|
||||||
@ -1154,7 +1170,7 @@ impl Module {
|
|||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
func: impl Fn(A, B, C) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
func: impl Fn(A, B, C) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> NonZeroU64 {
|
) -> u64 {
|
||||||
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
let a = cast_arg::<A>(&mut args[0]);
|
let a = cast_arg::<A>(&mut args[0]);
|
||||||
let b = cast_arg::<B>(&mut args[1]);
|
let b = cast_arg::<B>(&mut args[1]);
|
||||||
@ -1207,7 +1223,7 @@ impl Module {
|
|||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
namespace: FnNamespace,
|
namespace: FnNamespace,
|
||||||
func: impl Fn(&mut A, B, C) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
func: impl Fn(&mut A, B, C) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> NonZeroU64 {
|
) -> u64 {
|
||||||
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
let b = cast_arg::<B>(&mut args[2]);
|
let b = cast_arg::<B>(&mut args[2]);
|
||||||
let c = cast_arg::<C>(&mut args[3]);
|
let c = cast_arg::<C>(&mut args[3]);
|
||||||
@ -1258,7 +1274,7 @@ impl Module {
|
|||||||
pub fn set_indexer_set_fn<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone>(
|
pub fn set_indexer_set_fn<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
func: impl Fn(&mut A, B, C) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
func: impl Fn(&mut A, B, C) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> NonZeroU64 {
|
) -> u64 {
|
||||||
if TypeId::of::<A>() == TypeId::of::<Array>() {
|
if TypeId::of::<A>() == TypeId::of::<Array>() {
|
||||||
panic!("Cannot register indexer for arrays.");
|
panic!("Cannot register indexer for arrays.");
|
||||||
}
|
}
|
||||||
@ -1330,7 +1346,7 @@ impl Module {
|
|||||||
&mut self,
|
&mut self,
|
||||||
get_fn: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
get_fn: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
set_fn: impl Fn(&mut A, B, T) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
set_fn: impl Fn(&mut A, B, T) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> (NonZeroU64, NonZeroU64) {
|
) -> (u64, u64) {
|
||||||
(
|
(
|
||||||
self.set_indexer_get_fn(get_fn),
|
self.set_indexer_get_fn(get_fn),
|
||||||
self.set_indexer_set_fn(set_fn),
|
self.set_indexer_set_fn(set_fn),
|
||||||
@ -1367,7 +1383,7 @@ impl Module {
|
|||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
func: impl Fn(A, B, C, D) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
func: impl Fn(A, B, C, D) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> NonZeroU64 {
|
) -> u64 {
|
||||||
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
let a = cast_arg::<A>(&mut args[0]);
|
let a = cast_arg::<A>(&mut args[0]);
|
||||||
let b = cast_arg::<B>(&mut args[1]);
|
let b = cast_arg::<B>(&mut args[1]);
|
||||||
@ -1427,7 +1443,7 @@ impl Module {
|
|||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
namespace: FnNamespace,
|
namespace: FnNamespace,
|
||||||
func: impl Fn(&mut A, B, C, D) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
func: impl Fn(&mut A, B, C, D) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> NonZeroU64 {
|
) -> u64 {
|
||||||
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
let b = cast_arg::<B>(&mut args[1]);
|
let b = cast_arg::<B>(&mut args[1]);
|
||||||
let c = cast_arg::<C>(&mut args[2]);
|
let c = cast_arg::<C>(&mut args[2]);
|
||||||
@ -1454,14 +1470,9 @@ impl Module {
|
|||||||
|
|
||||||
/// Get a Rust function.
|
/// Get a Rust function.
|
||||||
///
|
///
|
||||||
/// The [`NonZeroU64`] hash is calculated by the function [`calc_native_fn_hash`][crate::calc_native_fn_hash].
|
/// The [`u64`] hash is returned by the `set_fn_XXX` calls.
|
||||||
/// It is also returned by the `set_fn_XXX` calls.
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn get_fn(
|
pub(crate) fn get_fn(&self, hash_fn: u64, public_only: bool) -> Option<&CallableFunction> {
|
||||||
&self,
|
|
||||||
hash_fn: NonZeroU64,
|
|
||||||
public_only: bool,
|
|
||||||
) -> Option<&CallableFunction> {
|
|
||||||
self.functions
|
self.functions
|
||||||
.get(&hash_fn)
|
.get(&hash_fn)
|
||||||
.and_then(|FuncInfo { access, func, .. }| match access {
|
.and_then(|FuncInfo { access, func, .. }| match access {
|
||||||
@ -1473,24 +1484,17 @@ impl Module {
|
|||||||
|
|
||||||
/// Does the particular namespace-qualified function exist in the [`Module`]?
|
/// Does the particular namespace-qualified function exist in the [`Module`]?
|
||||||
///
|
///
|
||||||
/// The [`NonZeroU64`] hash is calculated by the function
|
/// The [`u64`] hash is calculated by [`build_index`][Module::build_index].
|
||||||
/// [`calc_native_fn_hash`][crate::calc_native_fn_hash] and must match
|
|
||||||
/// the hash calculated by [`build_index`][Module::build_index].
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn contains_qualified_fn(&self, hash_fn: NonZeroU64) -> bool {
|
pub fn contains_qualified_fn(&self, hash_fn: u64) -> bool {
|
||||||
self.all_functions.contains_key(&hash_fn)
|
self.all_functions.contains_key(&hash_fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a namespace-qualified function.
|
/// Get a namespace-qualified function.
|
||||||
///
|
///
|
||||||
/// The [`NonZeroU64`] hash is calculated by the function
|
/// The [`u64`] hash is calculated by [`build_index`][Module::build_index].
|
||||||
/// [`calc_native_fn_hash`][crate::calc_native_fn_hash] and must match
|
|
||||||
/// the hash calculated by [`build_index`][Module::build_index].
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn get_qualified_fn(
|
pub(crate) fn get_qualified_fn(&self, hash_qualified_fn: u64) -> Option<&CallableFunction> {
|
||||||
&self,
|
|
||||||
hash_qualified_fn: NonZeroU64,
|
|
||||||
) -> Option<&CallableFunction> {
|
|
||||||
self.all_functions.get(&hash_qualified_fn)
|
self.all_functions.get(&hash_qualified_fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1692,7 +1696,8 @@ impl Module {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn iter_script_fn(
|
pub(crate) fn iter_script_fn(
|
||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<Item = (FnNamespace, FnAccess, &str, usize, &ScriptFnDef)> + '_ {
|
) -> impl Iterator<Item = (FnNamespace, FnAccess, &str, usize, &crate::ast::ScriptFnDef)> + '_
|
||||||
|
{
|
||||||
self.functions.values().filter(|f| f.func.is_script()).map(
|
self.functions.values().filter(|f| f.func.is_script()).map(
|
||||||
|FuncInfo {
|
|FuncInfo {
|
||||||
namespace,
|
namespace,
|
||||||
@ -1751,7 +1756,7 @@ impl Module {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn iter_script_fn_info(
|
pub fn iter_script_fn_info(
|
||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<Item = (FnNamespace, FnAccess, &str, usize, &ScriptFnDef)> {
|
) -> impl Iterator<Item = (FnNamespace, FnAccess, &str, usize, &crate::ast::ScriptFnDef)> {
|
||||||
self.iter_script_fn()
|
self.iter_script_fn()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1853,8 +1858,8 @@ impl Module {
|
|||||||
fn index_module<'a>(
|
fn index_module<'a>(
|
||||||
module: &'a Module,
|
module: &'a Module,
|
||||||
qualifiers: &mut Vec<&'a str>,
|
qualifiers: &mut Vec<&'a str>,
|
||||||
variables: &mut HashMap<NonZeroU64, Dynamic, StraightHasherBuilder>,
|
variables: &mut HashMap<u64, Dynamic, StraightHasherBuilder>,
|
||||||
functions: &mut HashMap<NonZeroU64, CallableFunction, StraightHasherBuilder>,
|
functions: &mut HashMap<u64, CallableFunction, StraightHasherBuilder>,
|
||||||
type_iterators: &mut HashMap<TypeId, IteratorFn>,
|
type_iterators: &mut HashMap<TypeId, IteratorFn>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let mut contains_indexed_global_functions = false;
|
let mut contains_indexed_global_functions = false;
|
||||||
@ -1870,8 +1875,7 @@ impl Module {
|
|||||||
|
|
||||||
// Index all variables
|
// Index all variables
|
||||||
module.variables.iter().for_each(|(var_name, value)| {
|
module.variables.iter().for_each(|(var_name, value)| {
|
||||||
let hash_var =
|
let hash_var = crate::calc_fn_hash(qualifiers.iter().map(|&v| v), var_name, 0);
|
||||||
crate::calc_script_fn_hash(qualifiers.iter().map(|&v| v), var_name, 0).unwrap();
|
|
||||||
variables.insert(hash_var, value.clone());
|
variables.insert(hash_var, value.clone());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1908,26 +1912,13 @@ impl Module {
|
|||||||
FnAccess::Private => return, // Do not index private functions
|
FnAccess::Private => return, // Do not index private functions
|
||||||
}
|
}
|
||||||
|
|
||||||
let hash_qualified_script =
|
|
||||||
crate::calc_script_fn_hash(qualifiers.iter().cloned(), name, *params)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if !func.is_script() {
|
if !func.is_script() {
|
||||||
assert_eq!(*params, param_types.len());
|
let hash_qualified_fn =
|
||||||
|
calc_native_fn_hash(qualifiers.iter().cloned(), name, param_types);
|
||||||
// Namespace-qualified Rust functions are indexed in two steps:
|
|
||||||
// 1) Calculate a hash in a similar manner to script-defined functions,
|
|
||||||
// i.e. qualifiers + function name + number of arguments.
|
|
||||||
// 2) Calculate a second hash with no qualifiers, empty function name,
|
|
||||||
// and the actual list of argument [`TypeId`]'.s
|
|
||||||
let hash_fn_args =
|
|
||||||
crate::calc_native_fn_hash(empty(), "", param_types.iter().cloned())
|
|
||||||
.unwrap();
|
|
||||||
// 3) The two hashes are combined.
|
|
||||||
let hash_qualified_fn = combine_hashes(hash_qualified_script, hash_fn_args);
|
|
||||||
|
|
||||||
functions.insert(hash_qualified_fn, func.clone());
|
functions.insert(hash_qualified_fn, func.clone());
|
||||||
} else if cfg!(not(feature = "no_function")) {
|
} else if cfg!(not(feature = "no_function")) {
|
||||||
|
let hash_qualified_script =
|
||||||
|
crate::calc_fn_hash(qualifiers.iter().cloned(), name, *params);
|
||||||
functions.insert(hash_qualified_script, func.clone());
|
functions.insert(hash_qualified_script, func.clone());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2015,7 +2006,7 @@ impl Module {
|
|||||||
/// _(INTERNALS)_ A chain of [module][Module] names to namespace-qualify a variable or function call.
|
/// _(INTERNALS)_ A chain of [module][Module] names to namespace-qualify a variable or function call.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// A [`NonZeroU64`] offset to the current [`Scope`][crate::Scope] is cached for quick search purposes.
|
/// A [`u64`] offset to the current [`Scope`][crate::Scope] is cached for quick search purposes.
|
||||||
///
|
///
|
||||||
/// A [`StaticVec`] is used because most namespace-qualified access contains only one level,
|
/// A [`StaticVec`] is used because most namespace-qualified access contains only one level,
|
||||||
/// and it is wasteful to always allocate a [`Vec`] with one element.
|
/// and it is wasteful to always allocate a [`Vec`] with one element.
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
//! Module implementing the [`AST`] optimizer.
|
//! Module implementing the [`AST`] optimizer.
|
||||||
|
|
||||||
use crate::ast::{Expr, ScriptFnDef, Stmt};
|
use crate::ast::{Expr, Stmt};
|
||||||
use crate::dynamic::AccessMode;
|
use crate::dynamic::AccessMode;
|
||||||
use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF};
|
use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF};
|
||||||
use crate::fn_builtin::get_builtin_binary_op_fn;
|
use crate::fn_builtin::get_builtin_binary_op_fn;
|
||||||
use crate::parser::map_dynamic_to_expr;
|
use crate::parser::map_dynamic_to_expr;
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
|
any::TypeId,
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
iter::empty,
|
iter::empty,
|
||||||
@ -16,7 +17,10 @@ use crate::stdlib::{
|
|||||||
};
|
};
|
||||||
use crate::token::is_valid_identifier;
|
use crate::token::is_valid_identifier;
|
||||||
use crate::utils::get_hasher;
|
use crate::utils::get_hasher;
|
||||||
use crate::{calc_native_fn_hash, Dynamic, Engine, Module, Position, Scope, StaticVec, AST};
|
use crate::{
|
||||||
|
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, Module, Position, Scope,
|
||||||
|
StaticVec, AST,
|
||||||
|
};
|
||||||
|
|
||||||
/// Level of optimization performed.
|
/// Level of optimization performed.
|
||||||
#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)]
|
#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)]
|
||||||
@ -126,15 +130,25 @@ impl<'a> State<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Has a system function a Rust-native override?
|
||||||
|
fn has_native_fn(state: &State, hash_script: u64, arg_types: &[TypeId]) -> bool {
|
||||||
|
let hash_params = calc_fn_params_hash(arg_types.iter().cloned());
|
||||||
|
let hash = combine_hashes(hash_script, hash_params);
|
||||||
|
|
||||||
|
// First check registered functions
|
||||||
|
state.engine.global_namespace.contains_fn(hash, false)
|
||||||
|
// Then check packages
|
||||||
|
|| state.engine.global_modules.iter().any(|m| m.contains_fn(hash, false))
|
||||||
|
// Then check sub-modules
|
||||||
|
|| state.engine.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash))
|
||||||
|
}
|
||||||
|
|
||||||
/// Call a registered function
|
/// Call a registered function
|
||||||
fn call_fn_with_constant_arguments(
|
fn call_fn_with_constant_arguments(
|
||||||
state: &State,
|
state: &State,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
arg_values: &mut [Dynamic],
|
arg_values: &mut [Dynamic],
|
||||||
) -> Option<Dynamic> {
|
) -> Option<Dynamic> {
|
||||||
// Search built-in's and external functions
|
|
||||||
let hash_fn = calc_native_fn_hash(empty(), fn_name, arg_values.iter().map(|a| a.type_id()));
|
|
||||||
|
|
||||||
state
|
state
|
||||||
.engine
|
.engine
|
||||||
.call_native_fn(
|
.call_native_fn(
|
||||||
@ -142,7 +156,7 @@ fn call_fn_with_constant_arguments(
|
|||||||
&mut Default::default(),
|
&mut Default::default(),
|
||||||
state.lib,
|
state.lib,
|
||||||
fn_name,
|
fn_name,
|
||||||
hash_fn.unwrap(),
|
calc_fn_hash(empty(), fn_name, arg_values.len()),
|
||||||
arg_values.iter_mut().collect::<StaticVec<_>>().as_mut(),
|
arg_values.iter_mut().collect::<StaticVec<_>>().as_mut(),
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
@ -295,10 +309,10 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
match stmt {
|
match stmt {
|
||||||
// expr op= expr
|
// expr op= expr
|
||||||
Stmt::Assignment(x, _) => match x.0 {
|
Stmt::Assignment(x, _) => match x.0 {
|
||||||
Expr::Variable(_) => optimize_expr(&mut x.2, state),
|
Expr::Variable(_) => optimize_expr(&mut x.1, state),
|
||||||
_ => {
|
_ => {
|
||||||
optimize_expr(&mut x.0, state);
|
optimize_expr(&mut x.0, state);
|
||||||
optimize_expr(&mut x.2, state);
|
optimize_expr(&mut x.1, state);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -393,25 +407,32 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// while false { block } -> Noop
|
// while false { block } -> Noop
|
||||||
Stmt::While(Expr::BoolConstant(false, pos), _, _) => {
|
Stmt::While(Some(Expr::BoolConstant(false, pos)), _, _) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*stmt = Stmt::Noop(*pos)
|
*stmt = Stmt::Noop(*pos)
|
||||||
}
|
}
|
||||||
// while expr { block }
|
// while expr { block }
|
||||||
Stmt::While(condition, block, _) => {
|
Stmt::While(condition, block, _) => {
|
||||||
optimize_stmt(block, state, false);
|
optimize_stmt(block, state, false);
|
||||||
|
|
||||||
|
if let Some(condition) = condition {
|
||||||
optimize_expr(condition, state);
|
optimize_expr(condition, state);
|
||||||
|
}
|
||||||
|
|
||||||
match **block {
|
match **block {
|
||||||
// while expr { break; } -> { expr; }
|
// while expr { break; } -> { expr; }
|
||||||
Stmt::Break(pos) => {
|
Stmt::Break(pos) => {
|
||||||
// Only a single break statement - turn into running the guard expression once
|
// Only a single break statement - turn into running the guard expression once
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
|
if let Some(condition) = condition {
|
||||||
let mut statements = vec![Stmt::Expr(mem::take(condition))];
|
let mut statements = vec![Stmt::Expr(mem::take(condition))];
|
||||||
if preserve_result {
|
if preserve_result {
|
||||||
statements.push(Stmt::Noop(pos))
|
statements.push(Stmt::Noop(pos))
|
||||||
}
|
}
|
||||||
*stmt = Stmt::Block(statements, pos);
|
*stmt = Stmt::Block(statements, pos);
|
||||||
|
} else {
|
||||||
|
*stmt = Stmt::Noop(pos);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
@ -511,7 +532,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
|||||||
Expr::Dot(x, _) => match (&mut x.lhs, &mut x.rhs) {
|
Expr::Dot(x, _) => match (&mut x.lhs, &mut x.rhs) {
|
||||||
// map.string
|
// map.string
|
||||||
(Expr::Map(m, pos), Expr::Property(p)) if m.iter().all(|(_, x)| x.is_pure()) => {
|
(Expr::Map(m, pos), Expr::Property(p)) if m.iter().all(|(_, x)| x.is_pure()) => {
|
||||||
let prop = &p.2.name;
|
let prop = &p.4.name;
|
||||||
// Map literal where everything is pure - promote the indexed item.
|
// Map literal where everything is pure - promote the indexed item.
|
||||||
// All other items can be thrown away.
|
// All other items can be thrown away.
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
@ -669,7 +690,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
|||||||
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
|
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
|
||||||
|
|
||||||
// Search for overloaded operators (can override built-in).
|
// Search for overloaded operators (can override built-in).
|
||||||
if !state.engine.has_override_by_name_and_arguments(Some(&Default::default()), &mut Default::default(), state.lib, x.name.as_ref(), arg_types.as_ref()) {
|
if !has_native_fn(state, x.hash.native_hash(), arg_types.as_ref()) {
|
||||||
if let Some(result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1])
|
if let Some(result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1])
|
||||||
.and_then(|f| {
|
.and_then(|f| {
|
||||||
let ctx = (state.engine, x.name.as_ref(), state.lib).into();
|
let ctx = (state.engine, x.name.as_ref(), state.lib).into();
|
||||||
@ -865,7 +886,7 @@ pub fn optimize_into_ast(
|
|||||||
engine: &Engine,
|
engine: &Engine,
|
||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
mut statements: Vec<Stmt>,
|
mut statements: Vec<Stmt>,
|
||||||
_functions: Vec<ScriptFnDef>,
|
_functions: Vec<crate::ast::ScriptFnDef>,
|
||||||
level: OptimizationLevel,
|
level: OptimizationLevel,
|
||||||
) -> AST {
|
) -> AST {
|
||||||
let level = if cfg!(feature = "no_optimize") {
|
let level = if cfg!(feature = "no_optimize") {
|
||||||
@ -884,7 +905,7 @@ pub fn optimize_into_ast(
|
|||||||
|
|
||||||
_functions
|
_functions
|
||||||
.iter()
|
.iter()
|
||||||
.map(|fn_def| ScriptFnDef {
|
.map(|fn_def| crate::ast::ScriptFnDef {
|
||||||
name: fn_def.name.clone(),
|
name: fn_def.name.clone(),
|
||||||
access: fn_def.access,
|
access: fn_def.access,
|
||||||
body: Default::default(),
|
body: Default::default(),
|
||||||
|
@ -1,11 +1,6 @@
|
|||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::{def_package, FnPtr, ImmutableString, NativeCallContext};
|
use crate::{def_package, FnPtr, ImmutableString, NativeCallContext};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
use crate::{ast::ScriptFnDef, stdlib::collections::HashMap, Array, Map};
|
|
||||||
|
|
||||||
def_package!(crate:BasicFnPackage:"Basic Fn functions.", lib, {
|
def_package!(crate:BasicFnPackage:"Basic Fn functions.", lib, {
|
||||||
combine_with_exported_module!(lib, "FnPtr", fn_ptr_functions);
|
combine_with_exported_module!(lib, "FnPtr", fn_ptr_functions);
|
||||||
});
|
});
|
||||||
@ -29,7 +24,7 @@ mod fn_ptr_functions {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
pub mod functions_and_maps {
|
pub mod functions_and_maps {
|
||||||
pub fn get_fn_metadata_list(ctx: NativeCallContext) -> Array {
|
pub fn get_fn_metadata_list(ctx: NativeCallContext) -> crate::Array {
|
||||||
collect_fn_metadata(ctx)
|
collect_fn_metadata(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -38,7 +33,9 @@ mod fn_ptr_functions {
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
fn collect_fn_metadata(ctx: NativeCallContext) -> Array {
|
fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array {
|
||||||
|
use crate::{ast::ScriptFnDef, stdlib::collections::HashMap, Array, Map};
|
||||||
|
|
||||||
// Create a metadata record for a function.
|
// Create a metadata record for a function.
|
||||||
fn make_metadata(
|
fn make_metadata(
|
||||||
dict: &HashMap<&str, ImmutableString>,
|
dict: &HashMap<&str, ImmutableString>,
|
||||||
@ -76,22 +73,6 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> Array {
|
|||||||
map.into()
|
map.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recursively scan modules for script-defined functions.
|
|
||||||
fn scan_module(
|
|
||||||
list: &mut Array,
|
|
||||||
dict: &HashMap<&str, ImmutableString>,
|
|
||||||
namespace: ImmutableString,
|
|
||||||
module: &Module,
|
|
||||||
) {
|
|
||||||
module.iter_script_fn().for_each(|(_, _, _, _, f)| {
|
|
||||||
list.push(make_metadata(dict, Some(namespace.clone()), f).into())
|
|
||||||
});
|
|
||||||
module.iter_sub_modules().for_each(|(ns, m)| {
|
|
||||||
let ns: ImmutableString = format!("{}::{}", namespace, ns).into();
|
|
||||||
scan_module(list, dict, ns, m.as_ref())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intern strings
|
// Intern strings
|
||||||
let mut dict = HashMap::<&str, ImmutableString>::with_capacity(8);
|
let mut dict = HashMap::<&str, ImmutableString>::with_capacity(8);
|
||||||
[
|
[
|
||||||
@ -115,8 +96,26 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> Array {
|
|||||||
.for_each(|(_, _, _, _, f)| list.push(make_metadata(&dict, None, f).into()));
|
.for_each(|(_, _, _, _, f)| list.push(make_metadata(&dict, None, f).into()));
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
{
|
||||||
|
// Recursively scan modules for script-defined functions.
|
||||||
|
fn scan_module(
|
||||||
|
list: &mut Array,
|
||||||
|
dict: &HashMap<&str, ImmutableString>,
|
||||||
|
namespace: ImmutableString,
|
||||||
|
module: &Module,
|
||||||
|
) {
|
||||||
|
module.iter_script_fn().for_each(|(_, _, _, _, f)| {
|
||||||
|
list.push(make_metadata(dict, Some(namespace.clone()), f).into())
|
||||||
|
});
|
||||||
|
module.iter_sub_modules().for_each(|(ns, m)| {
|
||||||
|
let ns: ImmutableString = format!("{}::{}", namespace, ns).into();
|
||||||
|
scan_module(list, dict, ns, m.as_ref())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
ctx.iter_imports_raw()
|
ctx.iter_imports_raw()
|
||||||
.for_each(|(ns, m)| scan_module(&mut list, &dict, ns.clone(), m.as_ref()));
|
.for_each(|(ns, m)| scan_module(&mut list, &dict, ns.clone(), m.as_ref()));
|
||||||
|
}
|
||||||
|
|
||||||
list
|
list
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str
|
|||||||
// Register string iterator
|
// Register string iterator
|
||||||
lib.set_iter(
|
lib.set_iter(
|
||||||
TypeId::of::<ImmutableString>(),
|
TypeId::of::<ImmutableString>(),
|
||||||
|string: Dynamic| Box::new(string.cast::<ImmutableString>().chars().collect::<Vec<_>>().into_iter().map(Into::into))
|
|string| Box::new(string.cast::<ImmutableString>().chars().collect::<Vec<_>>().into_iter().map(Into::into))
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -42,6 +42,10 @@ mod string_functions {
|
|||||||
pub fn len(string: &str) -> INT {
|
pub fn len(string: &str) -> INT {
|
||||||
string.chars().count() as INT
|
string.chars().count() as INT
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(name = "bytes", get = "bytes")]
|
||||||
|
pub fn bytes(string: &str) -> INT {
|
||||||
|
string.len() as INT
|
||||||
|
}
|
||||||
pub fn remove(string: &mut ImmutableString, sub_string: ImmutableString) {
|
pub fn remove(string: &mut ImmutableString, sub_string: ImmutableString) {
|
||||||
*string -= sub_string;
|
*string -= sub_string;
|
||||||
}
|
}
|
||||||
|
202
src/parser.rs
202
src/parser.rs
@ -1,6 +1,9 @@
|
|||||||
//! Main module defining the lexer and parser.
|
//! Main module defining the lexer and parser.
|
||||||
|
|
||||||
use crate::ast::{BinaryExpr, CustomExpr, Expr, FnCallExpr, Ident, ReturnType, ScriptFnDef, Stmt};
|
use crate::ast::{
|
||||||
|
BinaryExpr, CustomExpr, Expr, FnCallExpr, FnHash, Ident, OpAssignment, ReturnType, ScriptFnDef,
|
||||||
|
Stmt,
|
||||||
|
};
|
||||||
use crate::dynamic::{AccessMode, Union};
|
use crate::dynamic::{AccessMode, Union};
|
||||||
use crate::engine::KEYWORD_THIS;
|
use crate::engine::KEYWORD_THIS;
|
||||||
use crate::module::NamespaceRef;
|
use crate::module::NamespaceRef;
|
||||||
@ -13,7 +16,7 @@ use crate::stdlib::{
|
|||||||
format,
|
format,
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
iter::empty,
|
iter::empty,
|
||||||
num::{NonZeroU64, NonZeroUsize},
|
num::NonZeroUsize,
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
vec,
|
vec,
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
@ -22,8 +25,8 @@ use crate::syntax::{CustomSyntax, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT};
|
|||||||
use crate::token::{is_keyword_function, is_valid_identifier, Token, TokenStream};
|
use crate::token::{is_keyword_function, is_valid_identifier, Token, TokenStream};
|
||||||
use crate::utils::{get_hasher, StraightHasherBuilder};
|
use crate::utils::{get_hasher, StraightHasherBuilder};
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_script_fn_hash, Dynamic, Engine, ImmutableString, LexError, ParseError, ParseErrorType,
|
calc_fn_hash, Dynamic, Engine, ImmutableString, LexError, ParseError, ParseErrorType, Position,
|
||||||
Position, Scope, StaticVec, AST,
|
Scope, StaticVec, AST,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
@ -34,7 +37,7 @@ use crate::FnAccess;
|
|||||||
|
|
||||||
type PERR = ParseErrorType;
|
type PERR = ParseErrorType;
|
||||||
|
|
||||||
type FunctionsLib = HashMap<NonZeroU64, ScriptFnDef, StraightHasherBuilder>;
|
type FunctionsLib = HashMap<u64, ScriptFnDef, StraightHasherBuilder>;
|
||||||
|
|
||||||
/// A type that encapsulates the current state of the parser.
|
/// A type that encapsulates the current state of the parser.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -236,8 +239,11 @@ impl Expr {
|
|||||||
Self::Variable(x) if x.1.is_none() => {
|
Self::Variable(x) if x.1.is_none() => {
|
||||||
let ident = x.2;
|
let ident = x.2;
|
||||||
let getter = state.get_interned_string(crate::engine::make_getter(&ident.name));
|
let getter = state.get_interned_string(crate::engine::make_getter(&ident.name));
|
||||||
|
let hash_get = calc_fn_hash(empty(), &getter, 1);
|
||||||
let setter = state.get_interned_string(crate::engine::make_setter(&ident.name));
|
let setter = state.get_interned_string(crate::engine::make_setter(&ident.name));
|
||||||
Self::Property(Box::new((getter, setter, ident.into())))
|
let hash_set = calc_fn_hash(empty(), &setter, 2);
|
||||||
|
|
||||||
|
Self::Property(Box::new((getter, hash_get, setter, hash_set, ident.into())))
|
||||||
}
|
}
|
||||||
_ => self,
|
_ => self,
|
||||||
}
|
}
|
||||||
@ -334,33 +340,25 @@ fn parse_fn_call(
|
|||||||
Token::RightParen => {
|
Token::RightParen => {
|
||||||
eat_token(input, Token::RightParen);
|
eat_token(input, Token::RightParen);
|
||||||
|
|
||||||
let mut hash_script = if let Some(ref mut modules) = namespace {
|
let hash = if let Some(ref mut modules) = namespace {
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
modules.set_index(state.find_module(&modules[0].name));
|
modules.set_index(state.find_module(&modules[0].name));
|
||||||
|
|
||||||
// Rust functions are indexed in two steps:
|
calc_fn_hash(modules.iter().map(|m| m.name.as_str()), &id, 0)
|
||||||
// 1) Calculate a hash in a similar manner to script-defined functions,
|
|
||||||
// i.e. qualifiers + function name + number of arguments.
|
|
||||||
// 2) Calculate a second hash with no qualifiers, empty function name,
|
|
||||||
// zero number of arguments, and the actual list of argument `TypeId`'s.
|
|
||||||
// 3) The final hash is the XOR of the two hashes.
|
|
||||||
let qualifiers = modules.iter().map(|m| m.name.as_str());
|
|
||||||
calc_script_fn_hash(qualifiers, &id, 0)
|
|
||||||
} else {
|
} else {
|
||||||
calc_script_fn_hash(empty(), &id, 0)
|
calc_fn_hash(empty(), &id, 0)
|
||||||
};
|
};
|
||||||
|
|
||||||
// script functions can only be valid identifiers
|
|
||||||
if !is_valid_identifier(id.chars()) {
|
|
||||||
hash_script = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(Expr::FnCall(
|
return Ok(Expr::FnCall(
|
||||||
Box::new(FnCallExpr {
|
Box::new(FnCallExpr {
|
||||||
name: id.to_string().into(),
|
name: id.to_string().into(),
|
||||||
capture,
|
capture,
|
||||||
namespace,
|
namespace,
|
||||||
hash_script,
|
hash: if is_valid_identifier(id.chars()) {
|
||||||
|
FnHash::from_script(hash)
|
||||||
|
} else {
|
||||||
|
FnHash::from_native(hash)
|
||||||
|
},
|
||||||
args,
|
args,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
@ -385,33 +383,25 @@ fn parse_fn_call(
|
|||||||
(Token::RightParen, _) => {
|
(Token::RightParen, _) => {
|
||||||
eat_token(input, Token::RightParen);
|
eat_token(input, Token::RightParen);
|
||||||
|
|
||||||
let mut hash_script = if let Some(modules) = namespace.as_mut() {
|
let hash = if let Some(modules) = namespace.as_mut() {
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
modules.set_index(state.find_module(&modules[0].name));
|
modules.set_index(state.find_module(&modules[0].name));
|
||||||
|
|
||||||
// Rust functions are indexed in two steps:
|
calc_fn_hash(modules.iter().map(|m| m.name.as_str()), &id, args.len())
|
||||||
// 1) Calculate a hash in a similar manner to script-defined functions,
|
|
||||||
// i.e. qualifiers + function name + number of arguments.
|
|
||||||
// 2) Calculate a second hash with no qualifiers, empty function name,
|
|
||||||
// zero number of arguments, and the actual list of argument `TypeId`'s.
|
|
||||||
// 3) The final hash is the XOR of the two hashes.
|
|
||||||
let qualifiers = modules.iter().map(|m| m.name.as_str());
|
|
||||||
calc_script_fn_hash(qualifiers, &id, args.len())
|
|
||||||
} else {
|
} else {
|
||||||
calc_script_fn_hash(empty(), &id, args.len())
|
calc_fn_hash(empty(), &id, args.len())
|
||||||
};
|
};
|
||||||
|
|
||||||
// script functions can only be valid identifiers
|
|
||||||
if !is_valid_identifier(id.chars()) {
|
|
||||||
hash_script = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(Expr::FnCall(
|
return Ok(Expr::FnCall(
|
||||||
Box::new(FnCallExpr {
|
Box::new(FnCallExpr {
|
||||||
name: id.to_string().into(),
|
name: id.to_string().into(),
|
||||||
capture,
|
capture,
|
||||||
namespace,
|
namespace,
|
||||||
hash_script,
|
hash: if is_valid_identifier(id.chars()) {
|
||||||
|
FnHash::from_script(hash)
|
||||||
|
} else {
|
||||||
|
FnHash::from_native(hash)
|
||||||
|
},
|
||||||
args,
|
args,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
@ -948,7 +938,7 @@ fn parse_primary(
|
|||||||
}
|
}
|
||||||
Token::True => Expr::BoolConstant(true, settings.pos),
|
Token::True => Expr::BoolConstant(true, settings.pos),
|
||||||
Token::False => Expr::BoolConstant(false, settings.pos),
|
Token::False => Expr::BoolConstant(false, settings.pos),
|
||||||
t => unreachable!("unexpected token: {:?}", t),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Token::FloatConstant(x) => {
|
Token::FloatConstant(x) => {
|
||||||
@ -1013,10 +1003,8 @@ fn parse_primary(
|
|||||||
state.access_var(closure, *pos);
|
state.access_var(closure, *pos);
|
||||||
});
|
});
|
||||||
|
|
||||||
lib.insert(
|
let hash_script = calc_fn_hash(empty(), &func.name, func.params.len());
|
||||||
calc_script_fn_hash(empty(), &func.name, func.params.len()).unwrap(),
|
lib.insert(hash_script, func);
|
||||||
func,
|
|
||||||
);
|
|
||||||
|
|
||||||
expr
|
expr
|
||||||
}
|
}
|
||||||
@ -1033,7 +1021,7 @@ fn parse_primary(
|
|||||||
Token::Identifier(_) => {
|
Token::Identifier(_) => {
|
||||||
let s = match input.next().unwrap().0 {
|
let s = match input.next().unwrap().0 {
|
||||||
Token::Identifier(s) => s,
|
Token::Identifier(s) => s,
|
||||||
t => unreachable!("expecting Token::Identifier, but gets {:?}", t),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
match input.peek().unwrap().0 {
|
match input.peek().unwrap().0 {
|
||||||
@ -1080,7 +1068,7 @@ fn parse_primary(
|
|||||||
Token::Reserved(_) => {
|
Token::Reserved(_) => {
|
||||||
let s = match input.next().unwrap().0 {
|
let s = match input.next().unwrap().0 {
|
||||||
Token::Reserved(s) => s,
|
Token::Reserved(s) => s,
|
||||||
t => unreachable!("expecting Token::Reserved, but gets {:?}", t),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
match input.peek().unwrap().0 {
|
match input.peek().unwrap().0 {
|
||||||
@ -1115,7 +1103,7 @@ fn parse_primary(
|
|||||||
Token::LexError(_) => {
|
Token::LexError(_) => {
|
||||||
let err = match input.next().unwrap().0 {
|
let err = match input.next().unwrap().0 {
|
||||||
Token::LexError(err) => err,
|
Token::LexError(err) => err,
|
||||||
t => unreachable!("expecting Token::LexError, but gets {:?}", t),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
return Err(err.into_err(settings.pos));
|
return Err(err.into_err(settings.pos));
|
||||||
@ -1183,7 +1171,7 @@ fn parse_primary(
|
|||||||
} else {
|
} else {
|
||||||
let mut ns: NamespaceRef = Default::default();
|
let mut ns: NamespaceRef = Default::default();
|
||||||
ns.push(var_name_def);
|
ns.push(var_name_def);
|
||||||
let index = NonZeroU64::new(42).unwrap(); // Dummy
|
let index = 42; // Dummy
|
||||||
namespace = Some((index, ns));
|
namespace = Some((index, ns));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1243,8 +1231,7 @@ fn parse_primary(
|
|||||||
}
|
}
|
||||||
.map(|x| match x.as_mut() {
|
.map(|x| match x.as_mut() {
|
||||||
(_, Some((ref mut hash, ref mut namespace)), Ident { name, .. }) => {
|
(_, Some((ref mut hash, ref mut namespace)), Ident { name, .. }) => {
|
||||||
*hash =
|
*hash = calc_fn_hash(namespace.iter().map(|v| v.name.as_str()), name, 0);
|
||||||
calc_script_fn_hash(namespace.iter().map(|v| v.name.as_str()), name, 0).unwrap();
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
namespace.set_index(state.find_module(&namespace[0].name));
|
namespace.set_index(state.find_module(&namespace[0].name));
|
||||||
@ -1300,6 +1287,7 @@ fn parse_unary(
|
|||||||
Ok(Expr::FnCall(
|
Ok(Expr::FnCall(
|
||||||
Box::new(FnCallExpr {
|
Box::new(FnCallExpr {
|
||||||
name: op.into(),
|
name: op.into(),
|
||||||
|
hash: FnHash::from_native(calc_fn_hash(empty(), op, 1)),
|
||||||
args,
|
args,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
@ -1326,6 +1314,7 @@ fn parse_unary(
|
|||||||
Ok(Expr::FnCall(
|
Ok(Expr::FnCall(
|
||||||
Box::new(FnCallExpr {
|
Box::new(FnCallExpr {
|
||||||
name: op.into(),
|
name: op.into(),
|
||||||
|
hash: FnHash::from_native(calc_fn_hash(empty(), op, 1)),
|
||||||
args,
|
args,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
@ -1346,6 +1335,7 @@ fn parse_unary(
|
|||||||
Ok(Expr::FnCall(
|
Ok(Expr::FnCall(
|
||||||
Box::new(FnCallExpr {
|
Box::new(FnCallExpr {
|
||||||
name: op.into(),
|
name: op.into(),
|
||||||
|
hash: FnHash::from_native(calc_fn_hash(empty(), op, 1)),
|
||||||
args,
|
args,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
@ -1361,7 +1351,7 @@ fn parse_unary(
|
|||||||
|
|
||||||
/// Make an assignment statement.
|
/// Make an assignment statement.
|
||||||
fn make_assignment_stmt<'a>(
|
fn make_assignment_stmt<'a>(
|
||||||
fn_name: Cow<'static, str>,
|
op: Cow<'static, str>,
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
lhs: Expr,
|
lhs: Expr,
|
||||||
rhs: Expr,
|
rhs: Expr,
|
||||||
@ -1384,24 +1374,34 @@ fn make_assignment_stmt<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let op_info = if op.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let op2 = &op[..op.len() - 1]; // extract operator without =
|
||||||
|
|
||||||
|
Some(OpAssignment {
|
||||||
|
hash_op_assign: calc_fn_hash(empty(), &op, 2),
|
||||||
|
hash_op: calc_fn_hash(empty(), op2, 2),
|
||||||
|
op,
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
match &lhs {
|
match &lhs {
|
||||||
// const_expr = rhs
|
// const_expr = rhs
|
||||||
expr if expr.is_constant() => {
|
expr if expr.is_constant() => {
|
||||||
Err(PERR::AssignmentToConstant("".into()).into_err(lhs.position()))
|
Err(PERR::AssignmentToConstant("".into()).into_err(lhs.position()))
|
||||||
}
|
}
|
||||||
// var (non-indexed) = rhs
|
// var (non-indexed) = rhs
|
||||||
Expr::Variable(x) if x.0.is_none() => Ok(Stmt::Assignment(
|
Expr::Variable(x) if x.0.is_none() => {
|
||||||
Box::new((lhs, fn_name.into(), rhs)),
|
Ok(Stmt::Assignment(Box::new((lhs, rhs, op_info)), op_pos))
|
||||||
op_pos,
|
}
|
||||||
)),
|
|
||||||
// var (indexed) = rhs
|
// var (indexed) = rhs
|
||||||
Expr::Variable(x) => {
|
Expr::Variable(x) => {
|
||||||
let (index, _, Ident { name, pos }) = x.as_ref();
|
let (index, _, Ident { name, pos }) = x.as_ref();
|
||||||
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
|
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
|
||||||
AccessMode::ReadWrite => Ok(Stmt::Assignment(
|
AccessMode::ReadWrite => {
|
||||||
Box::new((lhs, fn_name.into(), rhs)),
|
Ok(Stmt::Assignment(Box::new((lhs, rhs, op_info)), op_pos))
|
||||||
op_pos,
|
}
|
||||||
)),
|
|
||||||
// Constant values cannot be assigned to
|
// Constant values cannot be assigned to
|
||||||
AccessMode::ReadOnly => {
|
AccessMode::ReadOnly => {
|
||||||
Err(PERR::AssignmentToConstant(name.to_string()).into_err(*pos))
|
Err(PERR::AssignmentToConstant(name.to_string()).into_err(*pos))
|
||||||
@ -1413,18 +1413,16 @@ fn make_assignment_stmt<'a>(
|
|||||||
match check_lvalue(&x.rhs, matches!(lhs, Expr::Dot(_, _))) {
|
match check_lvalue(&x.rhs, matches!(lhs, Expr::Dot(_, _))) {
|
||||||
Position::NONE => match &x.lhs {
|
Position::NONE => match &x.lhs {
|
||||||
// var[???] (non-indexed) = rhs, var.??? (non-indexed) = rhs
|
// var[???] (non-indexed) = rhs, var.??? (non-indexed) = rhs
|
||||||
Expr::Variable(x) if x.0.is_none() => Ok(Stmt::Assignment(
|
Expr::Variable(x) if x.0.is_none() => {
|
||||||
Box::new((lhs, fn_name.into(), rhs)),
|
Ok(Stmt::Assignment(Box::new((lhs, rhs, op_info)), op_pos))
|
||||||
op_pos,
|
}
|
||||||
)),
|
|
||||||
// var[???] (indexed) = rhs, var.??? (indexed) = rhs
|
// var[???] (indexed) = rhs, var.??? (indexed) = rhs
|
||||||
Expr::Variable(x) => {
|
Expr::Variable(x) => {
|
||||||
let (index, _, Ident { name, pos }) = x.as_ref();
|
let (index, _, Ident { name, pos }) = x.as_ref();
|
||||||
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
|
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
|
||||||
AccessMode::ReadWrite => Ok(Stmt::Assignment(
|
AccessMode::ReadWrite => {
|
||||||
Box::new((lhs, fn_name.into(), rhs)),
|
Ok(Stmt::Assignment(Box::new((lhs, rhs, op_info)), op_pos))
|
||||||
op_pos,
|
}
|
||||||
)),
|
|
||||||
// Constant values cannot be assigned to
|
// Constant values cannot be assigned to
|
||||||
AccessMode::ReadOnly => {
|
AccessMode::ReadOnly => {
|
||||||
Err(PERR::AssignmentToConstant(name.to_string()).into_err(*pos))
|
Err(PERR::AssignmentToConstant(name.to_string()).into_err(*pos))
|
||||||
@ -1506,8 +1504,11 @@ fn make_dot_expr(
|
|||||||
(lhs, Expr::Variable(x)) if x.1.is_none() => {
|
(lhs, Expr::Variable(x)) if x.1.is_none() => {
|
||||||
let ident = x.2;
|
let ident = x.2;
|
||||||
let getter = state.get_interned_string(crate::engine::make_getter(&ident.name));
|
let getter = state.get_interned_string(crate::engine::make_getter(&ident.name));
|
||||||
|
let hash_get = calc_fn_hash(empty(), &getter, 1);
|
||||||
let setter = state.get_interned_string(crate::engine::make_setter(&ident.name));
|
let setter = state.get_interned_string(crate::engine::make_setter(&ident.name));
|
||||||
let rhs = Expr::Property(Box::new((getter, setter, ident)));
|
let hash_set = calc_fn_hash(empty(), &setter, 2);
|
||||||
|
|
||||||
|
let rhs = Expr::Property(Box::new((getter, hash_get, setter, hash_set, ident)));
|
||||||
|
|
||||||
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
|
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
|
||||||
}
|
}
|
||||||
@ -1521,7 +1522,7 @@ fn make_dot_expr(
|
|||||||
}
|
}
|
||||||
// lhs.dot_lhs.dot_rhs
|
// lhs.dot_lhs.dot_rhs
|
||||||
(lhs, Expr::Dot(x, pos)) => match x.lhs {
|
(lhs, Expr::Dot(x, pos)) => match x.lhs {
|
||||||
Expr::Variable(_) | Expr::Property(_) | Expr::FnCall(_, _) => {
|
Expr::Variable(_) | Expr::Property(_) => {
|
||||||
let rhs = Expr::Dot(
|
let rhs = Expr::Dot(
|
||||||
Box::new(BinaryExpr {
|
Box::new(BinaryExpr {
|
||||||
lhs: x.lhs.into_property(state),
|
lhs: x.lhs.into_property(state),
|
||||||
@ -1531,6 +1532,22 @@ fn make_dot_expr(
|
|||||||
);
|
);
|
||||||
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
|
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
|
||||||
}
|
}
|
||||||
|
Expr::FnCall(mut func, func_pos) => {
|
||||||
|
// Recalculate hash
|
||||||
|
func.hash = FnHash::from_script_and_native(
|
||||||
|
calc_fn_hash(empty(), &func.name, func.args.len()),
|
||||||
|
calc_fn_hash(empty(), &func.name, func.args.len() + 1),
|
||||||
|
);
|
||||||
|
|
||||||
|
let rhs = Expr::Dot(
|
||||||
|
Box::new(BinaryExpr {
|
||||||
|
lhs: Expr::FnCall(func, func_pos),
|
||||||
|
rhs: x.rhs,
|
||||||
|
}),
|
||||||
|
pos,
|
||||||
|
);
|
||||||
|
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
|
||||||
|
}
|
||||||
_ => unreachable!("invalid dot expression: {:?}", x.lhs),
|
_ => unreachable!("invalid dot expression: {:?}", x.lhs),
|
||||||
},
|
},
|
||||||
// lhs.idx_lhs[idx_rhs]
|
// lhs.idx_lhs[idx_rhs]
|
||||||
@ -1544,6 +1561,10 @@ fn make_dot_expr(
|
|||||||
);
|
);
|
||||||
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
|
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
|
||||||
}
|
}
|
||||||
|
// lhs.nnn::func(...)
|
||||||
|
(_, Expr::FnCall(x, _)) if x.namespace.is_some() => {
|
||||||
|
unreachable!("method call should not be namespace-qualified")
|
||||||
|
}
|
||||||
// lhs.Fn() or lhs.eval()
|
// lhs.Fn() or lhs.eval()
|
||||||
(_, Expr::FnCall(x, pos))
|
(_, Expr::FnCall(x, pos))
|
||||||
if x.args.len() == 0
|
if x.args.len() == 0
|
||||||
@ -1567,8 +1588,14 @@ fn make_dot_expr(
|
|||||||
.into_err(pos))
|
.into_err(pos))
|
||||||
}
|
}
|
||||||
// lhs.func(...)
|
// lhs.func(...)
|
||||||
(lhs, func @ Expr::FnCall(_, _)) => {
|
(lhs, Expr::FnCall(mut func, func_pos)) => {
|
||||||
Expr::Dot(Box::new(BinaryExpr { lhs, rhs: func }), op_pos)
|
// Recalculate hash
|
||||||
|
func.hash = FnHash::from_script_and_native(
|
||||||
|
calc_fn_hash(empty(), &func.name, func.args.len()),
|
||||||
|
calc_fn_hash(empty(), &func.name, func.args.len() + 1),
|
||||||
|
);
|
||||||
|
let rhs = Expr::FnCall(func, func_pos);
|
||||||
|
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
|
||||||
}
|
}
|
||||||
// lhs.rhs
|
// lhs.rhs
|
||||||
(_, rhs) => return Err(PERR::PropertyExpected.into_err(rhs.position())),
|
(_, rhs) => return Err(PERR::PropertyExpected.into_err(rhs.position())),
|
||||||
@ -1792,9 +1819,11 @@ fn parse_binary_op(
|
|||||||
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
||||||
|
|
||||||
let op = op_token.syntax();
|
let op = op_token.syntax();
|
||||||
|
let hash = calc_fn_hash(empty(), &op, 2);
|
||||||
|
|
||||||
let op_base = FnCallExpr {
|
let op_base = FnCallExpr {
|
||||||
name: op,
|
name: op,
|
||||||
|
hash: FnHash::from_native(hash),
|
||||||
capture: false,
|
capture: false,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
@ -1863,16 +1892,15 @@ fn parse_binary_op(
|
|||||||
.get(&s)
|
.get(&s)
|
||||||
.map_or(false, Option::is_some) =>
|
.map_or(false, Option::is_some) =>
|
||||||
{
|
{
|
||||||
let hash_script = if is_valid_identifier(s.chars()) {
|
let hash = calc_fn_hash(empty(), &s, 2);
|
||||||
// Accept non-native functions for custom operators
|
|
||||||
calc_script_fn_hash(empty(), &s, 2)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
Expr::FnCall(
|
Expr::FnCall(
|
||||||
Box::new(FnCallExpr {
|
Box::new(FnCallExpr {
|
||||||
hash_script,
|
hash: if is_valid_identifier(s.chars()) {
|
||||||
|
FnHash::from_script(hash)
|
||||||
|
} else {
|
||||||
|
FnHash::from_native(hash)
|
||||||
|
},
|
||||||
args,
|
args,
|
||||||
..op_base
|
..op_base
|
||||||
}),
|
}),
|
||||||
@ -2122,10 +2150,11 @@ fn parse_while_loop(
|
|||||||
let (guard, token_pos) = match input.next().unwrap() {
|
let (guard, token_pos) = match input.next().unwrap() {
|
||||||
(Token::While, pos) => {
|
(Token::While, pos) => {
|
||||||
ensure_not_statement_expr(input, "a boolean")?;
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
(parse_expr(input, state, lib, settings.level_up())?, pos)
|
let expr = parse_expr(input, state, lib, settings.level_up())?;
|
||||||
|
(Some(expr), pos)
|
||||||
}
|
}
|
||||||
(Token::Loop, pos) => (Expr::BoolConstant(true, pos), pos),
|
(Token::Loop, pos) => (None, pos),
|
||||||
(t, _) => unreachable!("expecting Token::While or Token::Loop, but gets {:?}", t),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
settings.pos = token_pos;
|
settings.pos = token_pos;
|
||||||
|
|
||||||
@ -2543,7 +2572,7 @@ fn parse_stmt(
|
|||||||
_ => return Err(PERR::WrongDocComment.into_err(comments_pos)),
|
_ => return Err(PERR::WrongDocComment.into_err(comments_pos)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t => unreachable!("expecting Token::Comment, but gets {:?}", t),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2603,7 +2632,7 @@ fn parse_stmt(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let func = parse_fn(input, &mut new_state, lib, access, settings, _comments)?;
|
let func = parse_fn(input, &mut new_state, lib, access, settings, _comments)?;
|
||||||
let hash = calc_script_fn_hash(empty(), &func.name, func.params.len()).unwrap();
|
let hash = calc_fn_hash(empty(), &func.name, func.params.len());
|
||||||
|
|
||||||
if lib.contains_key(&hash) {
|
if lib.contains_key(&hash) {
|
||||||
return Err(PERR::FnDuplicatedDefinition(
|
return Err(PERR::FnDuplicatedDefinition(
|
||||||
@ -2650,10 +2679,7 @@ fn parse_stmt(
|
|||||||
match token {
|
match token {
|
||||||
Token::Return => ReturnType::Return,
|
Token::Return => ReturnType::Return,
|
||||||
Token::Throw => ReturnType::Exception,
|
Token::Throw => ReturnType::Exception,
|
||||||
t => unreachable!(
|
_ => unreachable!(),
|
||||||
"expecting Token::Return or Token::Throw, but gets {:?}",
|
|
||||||
t
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
pos,
|
pos,
|
||||||
)
|
)
|
||||||
@ -2873,12 +2899,10 @@ fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec<Ident>, pos: Po
|
|||||||
|
|
||||||
let curry_func = crate::engine::KEYWORD_FN_PTR_CURRY;
|
let curry_func = crate::engine::KEYWORD_FN_PTR_CURRY;
|
||||||
|
|
||||||
let hash_script = calc_script_fn_hash(empty(), curry_func, num_externals + 1);
|
|
||||||
|
|
||||||
let expr = Expr::FnCall(
|
let expr = Expr::FnCall(
|
||||||
Box::new(FnCallExpr {
|
Box::new(FnCallExpr {
|
||||||
name: curry_func.into(),
|
name: curry_func.into(),
|
||||||
hash_script,
|
hash: FnHash::from_native(calc_fn_hash(empty(), curry_func, num_externals + 1)),
|
||||||
args,
|
args,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
|
@ -147,7 +147,7 @@ impl From<&crate::module::FuncInfo> for FnMetadata {
|
|||||||
doc_comments: if info.func.is_script() {
|
doc_comments: if info.func.is_script() {
|
||||||
#[cfg(feature = "no_function")]
|
#[cfg(feature = "no_function")]
|
||||||
{
|
{
|
||||||
unreachable!()
|
unreachable!("scripted functions should not exist under no_function")
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
{
|
{
|
||||||
@ -160,8 +160,9 @@ impl From<&crate::module::FuncInfo> for FnMetadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<crate::ScriptFnMetadata<'_>> for FnMetadata {
|
#[cfg(not(feature = "no_function"))]
|
||||||
fn from(info: crate::ScriptFnMetadata) -> Self {
|
impl From<crate::ast::ScriptFnMetadata<'_>> for FnMetadata {
|
||||||
|
fn from(info: crate::ast::ScriptFnMetadata) -> Self {
|
||||||
Self {
|
Self {
|
||||||
namespace: FnNamespace::Global,
|
namespace: FnNamespace::Global,
|
||||||
access: info.access.into(),
|
access: info.access.into(),
|
||||||
|
@ -480,7 +480,7 @@ impl Token {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
As => "as",
|
As => "as",
|
||||||
EOF => "{EOF}",
|
EOF => "{EOF}",
|
||||||
_ => unreachable!("operator should be matched in outer scope"),
|
t => unreachable!("operator should be matched in outer scope: {:?}", t),
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
}
|
}
|
||||||
@ -892,7 +892,7 @@ pub fn parse_string_literal(
|
|||||||
'x' => 2,
|
'x' => 2,
|
||||||
'u' => 4,
|
'u' => 4,
|
||||||
'U' => 8,
|
'U' => 8,
|
||||||
_ => unreachable!("expecting 'x', 'u' or 'U', but gets {}", ch),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
for _ in 0..len {
|
for _ in 0..len {
|
||||||
@ -1190,14 +1190,14 @@ fn get_next_token_inner(
|
|||||||
'x' | 'X' => is_hex_digit,
|
'x' | 'X' => is_hex_digit,
|
||||||
'o' | 'O' => is_numeric_digit,
|
'o' | 'O' => is_numeric_digit,
|
||||||
'b' | 'B' => is_numeric_digit,
|
'b' | 'B' => is_numeric_digit,
|
||||||
_ => unreachable!("expecting 'x', 'o' or 'b', but gets {}", ch),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
radix_base = Some(match ch {
|
radix_base = Some(match ch {
|
||||||
'x' | 'X' => 16,
|
'x' | 'X' => 16,
|
||||||
'o' | 'O' => 8,
|
'o' | 'O' => 8,
|
||||||
'b' | 'B' => 2,
|
'b' | 'B' => 2,
|
||||||
_ => unreachable!("expecting 'x', 'o' or 'b', but gets {}", ch),
|
_ => unreachable!(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
92
src/utils.rs
92
src/utils.rs
@ -10,8 +10,7 @@ use crate::stdlib::{
|
|||||||
fmt,
|
fmt,
|
||||||
fmt::{Debug, Display},
|
fmt::{Debug, Display},
|
||||||
hash::{BuildHasher, Hash, Hasher},
|
hash::{BuildHasher, Hash, Hasher},
|
||||||
iter::{empty, FromIterator},
|
iter::FromIterator,
|
||||||
num::NonZeroU64,
|
|
||||||
ops::{Add, AddAssign, Deref, DerefMut, Sub, SubAssign},
|
ops::{Add, AddAssign, Deref, DerefMut, Sub, SubAssign},
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
@ -19,18 +18,18 @@ use crate::stdlib::{
|
|||||||
};
|
};
|
||||||
use crate::Shared;
|
use crate::Shared;
|
||||||
|
|
||||||
/// A hasher that only takes one single [`NonZeroU64`] and returns it as a hash key.
|
/// A hasher that only takes one single [`u64`] and returns it as a hash key.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics when hashing any data type other than a [`NonZeroU64`].
|
/// Panics when hashing any data type other than a [`u64`].
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||||
pub struct StraightHasher(NonZeroU64);
|
pub struct StraightHasher(u64);
|
||||||
|
|
||||||
impl Hasher for StraightHasher {
|
impl Hasher for StraightHasher {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn finish(&self) -> u64 {
|
fn finish(&self) -> u64 {
|
||||||
self.0.get()
|
self.0
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn write(&mut self, bytes: &[u8]) {
|
fn write(&mut self, bytes: &[u8]) {
|
||||||
@ -39,9 +38,7 @@ impl Hasher for StraightHasher {
|
|||||||
let mut key = [0_u8; 8];
|
let mut key = [0_u8; 8];
|
||||||
key.copy_from_slice(bytes);
|
key.copy_from_slice(bytes);
|
||||||
|
|
||||||
// HACK - If it so happens to hash directly to zero (OMG!) then change it to 42...
|
self.0 = u64::from_ne_bytes(key);
|
||||||
self.0 = NonZeroU64::new(u64::from_ne_bytes(key))
|
|
||||||
.unwrap_or_else(|| NonZeroU64::new(42).unwrap());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,7 +51,7 @@ impl BuildHasher for StraightHasherBuilder {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn build_hasher(&self) -> Self::Hasher {
|
fn build_hasher(&self) -> Self::Hasher {
|
||||||
StraightHasher(NonZeroU64::new(42).unwrap())
|
StraightHasher(42)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,26 +61,7 @@ pub fn get_hasher() -> ahash::AHasher {
|
|||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _(INTERNALS)_ Calculate a [`NonZeroU64`] hash key from a namespace-qualified function name and
|
/// _(INTERNALS)_ Calculate a [`u64`] hash key from a namespace-qualified function name
|
||||||
/// parameter types.
|
|
||||||
/// Exported under the `internals` feature only.
|
|
||||||
///
|
|
||||||
/// Module names are passed in via `&str` references from an iterator.
|
|
||||||
/// Parameter types are passed in via [`TypeId`] values from an iterator.
|
|
||||||
///
|
|
||||||
/// # Note
|
|
||||||
///
|
|
||||||
/// The first module name is skipped. Hashing starts from the _second_ module in the chain.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn calc_native_fn_hash<'a>(
|
|
||||||
modules: impl Iterator<Item = &'a str>,
|
|
||||||
fn_name: &str,
|
|
||||||
params: impl Iterator<Item = TypeId>,
|
|
||||||
) -> Option<NonZeroU64> {
|
|
||||||
calc_fn_hash(modules, fn_name, None, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// _(INTERNALS)_ Calculate a [`NonZeroU64`] hash key from a namespace-qualified function name
|
|
||||||
/// and the number of parameters, but no parameter types.
|
/// and the number of parameters, but no parameter types.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
@ -94,29 +72,11 @@ pub fn calc_native_fn_hash<'a>(
|
|||||||
///
|
///
|
||||||
/// The first module name is skipped. Hashing starts from the _second_ module in the chain.
|
/// The first module name is skipped. Hashing starts from the _second_ module in the chain.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn calc_script_fn_hash<'a>(
|
pub fn calc_fn_hash<'a>(
|
||||||
modules: impl Iterator<Item = &'a str>,
|
|
||||||
fn_name: &str,
|
|
||||||
num: usize,
|
|
||||||
) -> Option<NonZeroU64> {
|
|
||||||
calc_fn_hash(modules, fn_name, Some(num), empty())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calculate a [`NonZeroU64`] hash key from a namespace-qualified function name and parameter types.
|
|
||||||
///
|
|
||||||
/// Module names are passed in via `&str` references from an iterator.
|
|
||||||
/// Parameter types are passed in via [`TypeId`] values from an iterator.
|
|
||||||
///
|
|
||||||
/// # Note
|
|
||||||
///
|
|
||||||
/// The first module name is skipped. Hashing starts from the _second_ module in the chain.
|
|
||||||
#[inline(always)]
|
|
||||||
fn calc_fn_hash<'a>(
|
|
||||||
mut modules: impl Iterator<Item = &'a str>,
|
mut modules: impl Iterator<Item = &'a str>,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
num: Option<usize>,
|
num: usize,
|
||||||
params: impl Iterator<Item = TypeId>,
|
) -> u64 {
|
||||||
) -> Option<NonZeroU64> {
|
|
||||||
let s = &mut get_hasher();
|
let s = &mut get_hasher();
|
||||||
|
|
||||||
// Hash a boolean indicating whether the hash is namespace-qualified.
|
// Hash a boolean indicating whether the hash is namespace-qualified.
|
||||||
@ -124,20 +84,30 @@ fn calc_fn_hash<'a>(
|
|||||||
// We always skip the first module
|
// We always skip the first module
|
||||||
modules.for_each(|m| m.hash(s));
|
modules.for_each(|m| m.hash(s));
|
||||||
fn_name.hash(s);
|
fn_name.hash(s);
|
||||||
if let Some(num) = num {
|
|
||||||
num.hash(s);
|
num.hash(s);
|
||||||
} else {
|
s.finish()
|
||||||
params.for_each(|t| t.hash(s));
|
|
||||||
}
|
|
||||||
// HACK - If it so happens to hash directly to zero (OMG!) then change it to 42...
|
|
||||||
NonZeroU64::new(s.finish()).or_else(|| NonZeroU64::new(42))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Combine two [`NonZeroU64`] hashes by taking the XOR of them.
|
/// _(INTERNALS)_ Calculate a [`u64`] hash key from a list of parameter types.
|
||||||
|
/// Exported under the `internals` feature only.
|
||||||
|
///
|
||||||
|
/// Parameter types are passed in via [`TypeId`] values from an iterator.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn combine_hashes(a: NonZeroU64, b: NonZeroU64) -> NonZeroU64 {
|
pub fn calc_fn_params_hash(params: impl Iterator<Item = TypeId>) -> u64 {
|
||||||
// HACK - If it so happens to hash directly to zero (OMG!) then change it to 42...
|
let s = &mut get_hasher();
|
||||||
NonZeroU64::new(a.get() ^ b.get()).unwrap_or_else(|| NonZeroU64::new(42).unwrap())
|
let mut len = 0;
|
||||||
|
params.for_each(|t| {
|
||||||
|
t.hash(s);
|
||||||
|
len += 1;
|
||||||
|
});
|
||||||
|
len.hash(s);
|
||||||
|
s.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Combine two [`u64`] hashes by taking the XOR of them.
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn combine_hashes(a: u64, b: u64) -> u64 {
|
||||||
|
a ^ b
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _(INTERNALS)_ A type that wraps a [`HashMap`] and implements [`Hash`].
|
/// _(INTERNALS)_ A type that wraps a [`HashMap`] and implements [`Hash`].
|
||||||
|
@ -179,6 +179,51 @@ fn test_closures() -> Result<(), Box<EvalAltResult>> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
fn test_closures_sharing() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
engine.register_fn("foo", |x: INT, s: &str| s.len() as INT + x);
|
||||||
|
engine.register_fn("bar", |x: INT, s: String| s.len() as INT + x);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
r#"
|
||||||
|
let s = "hello";
|
||||||
|
let f = || s;
|
||||||
|
foo(1, s)
|
||||||
|
"#
|
||||||
|
)?,
|
||||||
|
6
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<String>(
|
||||||
|
r#"
|
||||||
|
let s = "hello";
|
||||||
|
let f = || s;
|
||||||
|
let n = foo(1, s);
|
||||||
|
s
|
||||||
|
"#
|
||||||
|
)?,
|
||||||
|
"hello"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
r#"
|
||||||
|
let s = "hello";
|
||||||
|
let f = || s;
|
||||||
|
bar(1, s)
|
||||||
|
"#
|
||||||
|
)?,
|
||||||
|
6
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
@ -395,14 +395,14 @@ fn test_module_export() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_module_str() -> Result<(), Box<EvalAltResult>> {
|
fn test_module_str() -> Result<(), Box<EvalAltResult>> {
|
||||||
fn test_fn(_input: ImmutableString) -> Result<INT, Box<EvalAltResult>> {
|
fn test_fn(input: ImmutableString) -> Result<INT, Box<EvalAltResult>> {
|
||||||
Ok(42)
|
Ok(input.len() as INT)
|
||||||
}
|
}
|
||||||
fn test_fn2(_input: &str) -> Result<INT, Box<EvalAltResult>> {
|
fn test_fn2(input: &str) -> Result<INT, Box<EvalAltResult>> {
|
||||||
Ok(42)
|
Ok(input.len() as INT)
|
||||||
}
|
}
|
||||||
fn test_fn3(_input: String) -> Result<INT, Box<EvalAltResult>> {
|
fn test_fn3(input: String) -> Result<INT, Box<EvalAltResult>> {
|
||||||
Ok(42)
|
Ok(input.len() as INT)
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut engine = rhai::Engine::new();
|
let mut engine = rhai::Engine::new();
|
||||||
@ -417,15 +417,15 @@ fn test_module_str() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(r#"import "test" as test; test::test("test");"#)?,
|
engine.eval::<INT>(r#"import "test" as test; test::test("test");"#)?,
|
||||||
42
|
4
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(r#"import "test" as test; test::test2("test");"#)?,
|
engine.eval::<INT>(r#"import "test" as test; test::test2("test");"#)?,
|
||||||
42
|
4
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(r#"import "test" as test; test::test3("test");"#)?,
|
engine.eval::<INT>(r#"import "test" as test; test::test3("test");"#)?,
|
||||||
42
|
4
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -64,6 +64,25 @@ fn test_string_dynamic() -> Result<(), Box<EvalAltResult>> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_string_mut() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
engine.register_fn("foo", |s: &str| s.len() as INT);
|
||||||
|
engine.register_fn("bar", |s: String| s.len() as INT);
|
||||||
|
engine.register_fn("baz", |s: &mut String| s.len());
|
||||||
|
|
||||||
|
assert_eq!(engine.eval::<INT>(r#"foo("hello")"#)?, 5);
|
||||||
|
assert_eq!(engine.eval::<INT>(r#"bar("hello")"#)?, 5);
|
||||||
|
assert!(
|
||||||
|
matches!(*engine.eval::<INT>(r#"baz("hello")"#).expect_err("should error"),
|
||||||
|
EvalAltResult::ErrorFunctionNotFound(f, _) if f == "baz (&str | ImmutableString | String)"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_string_substring() -> Result<(), Box<EvalAltResult>> {
|
fn test_string_substring() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
Loading…
Reference in New Issue
Block a user