commit
3a5757d3e1
12
CHANGELOG.md
12
CHANGELOG.md
@ -1,6 +1,18 @@
|
|||||||
Rhai Release Notes
|
Rhai Release Notes
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
Version 1.11.0
|
||||||
|
==============
|
||||||
|
|
||||||
|
New features
|
||||||
|
------------
|
||||||
|
|
||||||
|
### Custom syntax with state
|
||||||
|
|
||||||
|
* [`Engine::register_custom_syntax_with_state_raw`] is added. The custom syntax parser and implementation functions take on an additional parameter that holds a user-defined custom _state_ which should substantially simplify writing some custom parsers.
|
||||||
|
* [`Engine::register_custom_syntax_raw`] is deprecated.
|
||||||
|
|
||||||
|
|
||||||
Version 1.10.0
|
Version 1.10.0
|
||||||
==============
|
==============
|
||||||
|
|
||||||
|
1
doc/rhai-1.10.0-sync.json
Normal file
1
doc/rhai-1.10.0-sync.json
Normal file
File diff suppressed because one or more lines are too long
1
doc/rhai-1.10.0.json
Normal file
1
doc/rhai-1.10.0.json
Normal file
File diff suppressed because one or more lines are too long
1
doc/rhai-1.8.0-sync.json
Normal file
1
doc/rhai-1.8.0-sync.json
Normal file
File diff suppressed because one or more lines are too long
1
doc/rhai-1.9.1.json
Normal file
1
doc/rhai-1.9.1.json
Normal file
File diff suppressed because one or more lines are too long
@ -7,8 +7,8 @@ use crate::parser::ParseResult;
|
|||||||
use crate::tokenizer::{is_valid_identifier, Token};
|
use crate::tokenizer::{is_valid_identifier, Token};
|
||||||
use crate::types::dynamic::Variant;
|
use crate::types::dynamic::Variant;
|
||||||
use crate::{
|
use crate::{
|
||||||
reify, Engine, EvalContext, Identifier, ImmutableString, LexError, Position, RhaiResult,
|
reify, Dynamic, Engine, EvalContext, Identifier, ImmutableString, LexError, Position,
|
||||||
StaticVec,
|
RhaiResult, StaticVec,
|
||||||
};
|
};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
@ -39,19 +39,21 @@ pub mod markers {
|
|||||||
|
|
||||||
/// A general expression evaluation trait object.
|
/// A general expression evaluation trait object.
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub type FnCustomSyntaxEval = dyn Fn(&mut EvalContext, &[Expression]) -> RhaiResult;
|
pub type FnCustomSyntaxEval = dyn Fn(&mut EvalContext, &[Expression], &Dynamic) -> RhaiResult;
|
||||||
/// A general expression evaluation trait object.
|
/// A general expression evaluation trait object.
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub type FnCustomSyntaxEval = dyn Fn(&mut EvalContext, &[Expression]) -> RhaiResult + Send + Sync;
|
pub type FnCustomSyntaxEval =
|
||||||
|
dyn Fn(&mut EvalContext, &[Expression], &Dynamic) -> RhaiResult + Send + Sync;
|
||||||
|
|
||||||
/// A general expression parsing trait object.
|
/// A general expression parsing trait object.
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub type FnCustomSyntaxParse =
|
pub type FnCustomSyntaxParse =
|
||||||
dyn Fn(&[ImmutableString], &str) -> ParseResult<Option<ImmutableString>>;
|
dyn Fn(&[ImmutableString], &str, &mut Dynamic) -> ParseResult<Option<ImmutableString>>;
|
||||||
/// A general expression parsing trait object.
|
/// A general expression parsing trait object.
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub type FnCustomSyntaxParse =
|
pub type FnCustomSyntaxParse = dyn Fn(&[ImmutableString], &str, &mut Dynamic) -> ParseResult<Option<ImmutableString>>
|
||||||
dyn Fn(&[ImmutableString], &str) -> ParseResult<Option<ImmutableString>> + Send + Sync;
|
+ Send
|
||||||
|
+ Sync;
|
||||||
|
|
||||||
/// An expression sub-tree in an [`AST`][crate::AST].
|
/// An expression sub-tree in an [`AST`][crate::AST].
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -297,10 +299,10 @@ impl Engine {
|
|||||||
// The first keyword is the discriminator
|
// The first keyword is the discriminator
|
||||||
let key = segments[0].clone();
|
let key = segments[0].clone();
|
||||||
|
|
||||||
self.register_custom_syntax_raw(
|
self.register_custom_syntax_with_state_raw(
|
||||||
key,
|
key,
|
||||||
// Construct the parsing function
|
// Construct the parsing function
|
||||||
move |stream, _| {
|
move |stream, _, _| {
|
||||||
if stream.len() >= segments.len() {
|
if stream.len() >= segments.len() {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
@ -308,12 +310,12 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
scope_may_be_changed,
|
scope_may_be_changed,
|
||||||
func,
|
move |context, expressions, _| func(context, expressions),
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
/// Register a custom syntax with the [`Engine`].
|
/// Register a custom syntax with the [`Engine`] with custom user-defined state.
|
||||||
///
|
///
|
||||||
/// Not available under `no_custom_syntax`.
|
/// Not available under `no_custom_syntax`.
|
||||||
///
|
///
|
||||||
@ -328,30 +330,31 @@ impl Engine {
|
|||||||
/// All custom keywords used as symbols must be manually registered via [`Engine::register_custom_operator`].
|
/// All custom keywords used as symbols must be manually registered via [`Engine::register_custom_operator`].
|
||||||
/// Otherwise, they won't be recognized.
|
/// Otherwise, they won't be recognized.
|
||||||
///
|
///
|
||||||
/// # Implementation Function Signature
|
/// # Parsing Function Signature
|
||||||
///
|
///
|
||||||
/// The implementation function has the following signature:
|
/// The parsing function has the following signature:
|
||||||
///
|
///
|
||||||
/// `Fn(symbols: &[ImmutableString], look_ahead: &str) -> Result<Option<ImmutableString>, ParseError>`
|
/// `Fn(symbols: &[ImmutableString], look_ahead: &str, state: &mut Dynamic) -> Result<Option<ImmutableString>, ParseError>`
|
||||||
///
|
///
|
||||||
/// where:
|
/// where:
|
||||||
/// * `symbols`: a slice of symbols that have been parsed so far, possibly containing `$expr$` and/or `$block$`;
|
/// * `symbols`: a slice of symbols that have been parsed so far, possibly containing `$expr$` and/or `$block$`;
|
||||||
/// `$ident$` and other literal markers are replaced by the actual text
|
/// `$ident$` and other literal markers are replaced by the actual text
|
||||||
/// * `look_ahead`: a string slice containing the next symbol that is about to be read
|
/// * `look_ahead`: a string slice containing the next symbol that is about to be read
|
||||||
|
/// * `state`: a [`Dynamic`] value that contains a user-defined state
|
||||||
///
|
///
|
||||||
/// ## Return value
|
/// ## Return value
|
||||||
///
|
///
|
||||||
/// * `Ok(None)`: parsing complete and there are no more symbols to match.
|
/// * `Ok(None)`: parsing complete and there are no more symbols to match.
|
||||||
/// * `Ok(Some(symbol))`: the next symbol to match, which can also be `$expr$`, `$ident$` or `$block$`.
|
/// * `Ok(Some(symbol))`: the next symbol to match, which can also be `$expr$`, `$ident$` or `$block$`.
|
||||||
/// * `Err(ParseError)`: error that is reflected back to the [`Engine`], normally `ParseError(ParseErrorType::BadInput(LexError::ImproperSymbol(message)), Position::NONE)` to indicate a syntax error, but it can be any [`ParseError`][crate::ParseError].
|
/// * `Err(ParseError)`: error that is reflected back to the [`Engine`], normally `ParseError(ParseErrorType::BadInput(LexError::ImproperSymbol(message)), Position::NONE)` to indicate a syntax error, but it can be any [`ParseError`][crate::ParseError].
|
||||||
pub fn register_custom_syntax_raw(
|
pub fn register_custom_syntax_with_state_raw(
|
||||||
&mut self,
|
&mut self,
|
||||||
key: impl Into<Identifier>,
|
key: impl Into<Identifier>,
|
||||||
parse: impl Fn(&[ImmutableString], &str) -> ParseResult<Option<ImmutableString>>
|
parse: impl Fn(&[ImmutableString], &str, &mut Dynamic) -> ParseResult<Option<ImmutableString>>
|
||||||
+ SendSync
|
+ SendSync
|
||||||
+ 'static,
|
+ 'static,
|
||||||
scope_may_be_changed: bool,
|
scope_may_be_changed: bool,
|
||||||
func: impl Fn(&mut EvalContext, &[Expression]) -> RhaiResult + SendSync + 'static,
|
func: impl Fn(&mut EvalContext, &[Expression], &Dynamic) -> RhaiResult + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.custom_syntax.insert(
|
self.custom_syntax.insert(
|
||||||
key.into(),
|
key.into(),
|
||||||
|
@ -258,6 +258,68 @@ impl Engine {
|
|||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.register_indexer_set(set_fn)
|
self.register_indexer_set(set_fn)
|
||||||
}
|
}
|
||||||
|
/// Register a custom syntax with the [`Engine`].
|
||||||
|
///
|
||||||
|
/// Not available under `no_custom_syntax`.
|
||||||
|
///
|
||||||
|
/// # Deprecated
|
||||||
|
///
|
||||||
|
/// This method is deprecated.
|
||||||
|
/// Use [`register_custom_syntax_with_state_raw`][Engine::register_custom_syntax_with_state_raw] instead.
|
||||||
|
///
|
||||||
|
/// This method will be removed in the next major version.
|
||||||
|
///
|
||||||
|
/// # WARNING - Low Level API
|
||||||
|
///
|
||||||
|
/// This function is very low level.
|
||||||
|
///
|
||||||
|
/// * `scope_may_be_changed` specifies variables have been added/removed by this custom syntax.
|
||||||
|
/// * `parse` is the parsing function.
|
||||||
|
/// * `func` is the implementation function.
|
||||||
|
///
|
||||||
|
/// All custom keywords used as symbols must be manually registered via [`Engine::register_custom_operator`].
|
||||||
|
/// Otherwise, they won't be recognized.
|
||||||
|
///
|
||||||
|
/// # Parsing Function Signature
|
||||||
|
///
|
||||||
|
/// The parsing function has the following signature:
|
||||||
|
///
|
||||||
|
/// `Fn(symbols: &[ImmutableString], look_ahead: &str) -> Result<Option<ImmutableString>, ParseError>`
|
||||||
|
///
|
||||||
|
/// where:
|
||||||
|
/// * `symbols`: a slice of symbols that have been parsed so far, possibly containing `$expr$` and/or `$block$`;
|
||||||
|
/// `$ident$` and other literal markers are replaced by the actual text
|
||||||
|
/// * `look_ahead`: a string slice containing the next symbol that is about to be read
|
||||||
|
///
|
||||||
|
/// ## Return value
|
||||||
|
///
|
||||||
|
/// * `Ok(None)`: parsing complete and there are no more symbols to match.
|
||||||
|
/// * `Ok(Some(symbol))`: the next symbol to match, which can also be `$expr$`, `$ident$` or `$block$`.
|
||||||
|
/// * `Err(ParseError)`: error that is reflected back to the [`Engine`], normally `ParseError(ParseErrorType::BadInput(LexError::ImproperSymbol(message)), Position::NONE)` to indicate a syntax error, but it can be any [`ParseError`][crate::ParseError].
|
||||||
|
#[deprecated(
|
||||||
|
since = "1.11.0",
|
||||||
|
note = "use `register_custom_syntax_with_state_raw` instead"
|
||||||
|
)]
|
||||||
|
#[inline(always)]
|
||||||
|
#[cfg(not(feature = "no_custom_syntax"))]
|
||||||
|
pub fn register_custom_syntax_raw(
|
||||||
|
&mut self,
|
||||||
|
key: impl Into<Identifier>,
|
||||||
|
parse: impl Fn(&[ImmutableString], &str) -> crate::parser::ParseResult<Option<ImmutableString>>
|
||||||
|
+ crate::func::SendSync
|
||||||
|
+ 'static,
|
||||||
|
scope_may_be_changed: bool,
|
||||||
|
func: impl Fn(&mut crate::EvalContext, &[crate::Expression]) -> RhaiResult
|
||||||
|
+ crate::func::SendSync
|
||||||
|
+ 'static,
|
||||||
|
) -> &mut Self {
|
||||||
|
self.register_custom_syntax_with_state_raw(
|
||||||
|
key,
|
||||||
|
move |keywords, look_ahead, _| parse(keywords, look_ahead),
|
||||||
|
scope_may_be_changed,
|
||||||
|
move |context, expressions, _| func(context, expressions),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dynamic {
|
impl Dynamic {
|
||||||
|
@ -170,11 +170,8 @@ impl Engine {
|
|||||||
keyword: impl AsRef<str>,
|
keyword: impl AsRef<str>,
|
||||||
precedence: u8,
|
precedence: u8,
|
||||||
) -> Result<&mut Self, String> {
|
) -> Result<&mut Self, String> {
|
||||||
let precedence = Precedence::new(precedence);
|
let precedence =
|
||||||
|
Precedence::new(precedence).ok_or_else(|| "precedence cannot be zero".to_string())?;
|
||||||
if precedence.is_none() {
|
|
||||||
return Err("precedence cannot be zero".into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let keyword = keyword.as_ref();
|
let keyword = keyword.as_ref();
|
||||||
|
|
||||||
@ -213,7 +210,8 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add to custom keywords
|
// Add to custom keywords
|
||||||
self.custom_keywords.insert(keyword.into(), precedence);
|
self.custom_keywords
|
||||||
|
.insert(keyword.into(), Some(precedence));
|
||||||
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
@ -695,13 +695,13 @@ impl Engine {
|
|||||||
|
|
||||||
if root.is_empty() || !root.contains_key(sub_module) {
|
if root.is_empty() || !root.contains_key(sub_module) {
|
||||||
let mut m = Module::new();
|
let mut m = Module::new();
|
||||||
register_static_module_raw(m.sub_modules_mut(), remainder, module);
|
register_static_module_raw(m.get_sub_modules_mut(), remainder, module);
|
||||||
m.build_index();
|
m.build_index();
|
||||||
root.insert(sub_module.into(), m.into());
|
root.insert(sub_module.into(), m.into());
|
||||||
} else {
|
} else {
|
||||||
let m = root.remove(sub_module).expect("contains sub-module");
|
let m = root.remove(sub_module).expect("contains sub-module");
|
||||||
let mut m = crate::func::shared_take_or_clone(m);
|
let mut m = crate::func::shared_take_or_clone(m);
|
||||||
register_static_module_raw(m.sub_modules_mut(), remainder, module);
|
register_static_module_raw(m.get_sub_modules_mut(), remainder, module);
|
||||||
m.build_index();
|
m.build_index();
|
||||||
root.insert(sub_module.into(), m.into());
|
root.insert(sub_module.into(), m.into());
|
||||||
}
|
}
|
||||||
|
@ -238,6 +238,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Make a `Box<`[`EvalAltResult<ErrorMismatchDataType>`][ERR::ErrorMismatchDataType]`>`.
|
/// Make a `Box<`[`EvalAltResult<ErrorMismatchDataType>`][ERR::ErrorMismatchDataType]`>`.
|
||||||
|
#[cold]
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn make_type_mismatch_err<T>(&self, typ: &str, pos: Position) -> RhaiError {
|
pub(crate) fn make_type_mismatch_err<T>(&self, typ: &str, pos: Position) -> RhaiError {
|
||||||
|
@ -61,6 +61,8 @@ pub struct CustomExpr {
|
|||||||
pub inputs: StaticVec<Expr>,
|
pub inputs: StaticVec<Expr>,
|
||||||
/// List of tokens actually parsed.
|
/// List of tokens actually parsed.
|
||||||
pub tokens: StaticVec<ImmutableString>,
|
pub tokens: StaticVec<ImmutableString>,
|
||||||
|
/// State value.
|
||||||
|
pub state: Dynamic,
|
||||||
/// Is the current [`Scope`][crate::Scope] possibly modified by this custom statement
|
/// Is the current [`Scope`][crate::Scope] possibly modified by this custom statement
|
||||||
/// (e.g. introducing a new variable)?
|
/// (e.g. introducing a new variable)?
|
||||||
pub scope_may_be_changed: bool,
|
pub scope_may_be_changed: bool,
|
||||||
|
@ -304,7 +304,7 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Add the global namespace module
|
// Add the global namespace module
|
||||||
let mut global_namespace = Module::new();
|
let mut global_namespace = Module::with_capacity(0);
|
||||||
global_namespace.internal = true;
|
global_namespace.internal = true;
|
||||||
engine.global_modules.push(global_namespace.into());
|
engine.global_modules.push(global_namespace.into());
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
//! System caches.
|
//! System caches.
|
||||||
|
|
||||||
use crate::func::{CallableFunction, StraightHashMap};
|
use crate::func::{CallableFunction, StraightHashMap};
|
||||||
|
use crate::types::BloomFilterU64;
|
||||||
use crate::{Identifier, StaticVec};
|
use crate::{Identifier, StaticVec};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
@ -16,12 +17,27 @@ pub struct FnResolutionCacheEntry {
|
|||||||
pub source: Option<Box<Identifier>>,
|
pub source: Option<Box<Identifier>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _(internals)_ A function resolution cache.
|
/// _(internals)_ A function resolution cache with a bloom filter.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// [`FnResolutionCacheEntry`] is [`Box`]ed in order to pack as many entries inside a single B-Tree
|
/// The bloom filter is used to rapidly check whether a function hash has never been encountered.
|
||||||
/// level as possible.
|
/// It enables caching a hash only during the second encounter to avoid "one-hit wonders".
|
||||||
pub type FnResolutionCache = StraightHashMap<u64, Option<FnResolutionCacheEntry>>;
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct FnResolutionCache {
|
||||||
|
/// Hash map containing cached functions.
|
||||||
|
pub map: StraightHashMap<Option<FnResolutionCacheEntry>>,
|
||||||
|
/// Bloom filter to avoid caching "one-hit wonders".
|
||||||
|
pub filter: BloomFilterU64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FnResolutionCache {
|
||||||
|
/// Clear the [`FnResolutionCache`].
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.map.clear();
|
||||||
|
self.filter.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// _(internals)_ A type containing system-wide caches.
|
/// _(internals)_ A type containing system-wide caches.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
@ -31,7 +47,7 @@ pub type FnResolutionCache = StraightHashMap<u64, Option<FnResolutionCacheEntry>
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Caches<'a> {
|
pub struct Caches<'a> {
|
||||||
/// Stack of [function resolution caches][FnResolutionCache].
|
/// Stack of [function resolution caches][FnResolutionCache].
|
||||||
fn_resolution: StaticVec<FnResolutionCache>,
|
stack: StaticVec<FnResolutionCache>,
|
||||||
/// Take care of the lifetime parameter.
|
/// Take care of the lifetime parameter.
|
||||||
dummy: PhantomData<&'a ()>,
|
dummy: PhantomData<&'a ()>,
|
||||||
}
|
}
|
||||||
@ -42,7 +58,7 @@ impl Caches<'_> {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
fn_resolution: StaticVec::new_const(),
|
stack: StaticVec::new_const(),
|
||||||
dummy: PhantomData,
|
dummy: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -50,27 +66,26 @@ impl Caches<'_> {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn fn_resolution_caches_len(&self) -> usize {
|
pub fn fn_resolution_caches_len(&self) -> usize {
|
||||||
self.fn_resolution.len()
|
self.stack.len()
|
||||||
}
|
}
|
||||||
/// Get a mutable reference to the current function resolution cache.
|
/// Get a mutable reference to the current function resolution cache.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn fn_resolution_cache_mut(&mut self) -> &mut FnResolutionCache {
|
pub fn fn_resolution_cache_mut(&mut self) -> &mut FnResolutionCache {
|
||||||
if self.fn_resolution.is_empty() {
|
if self.stack.is_empty() {
|
||||||
// Push a new function resolution cache if the stack is empty
|
// Push a new function resolution cache if the stack is empty
|
||||||
self.push_fn_resolution_cache();
|
self.push_fn_resolution_cache();
|
||||||
}
|
}
|
||||||
self.fn_resolution.last_mut().unwrap()
|
self.stack.last_mut().unwrap()
|
||||||
}
|
}
|
||||||
/// Push an empty function resolution cache onto the stack and make it current.
|
/// Push an empty function resolution cache onto the stack and make it current.
|
||||||
#[allow(dead_code)]
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn push_fn_resolution_cache(&mut self) {
|
pub fn push_fn_resolution_cache(&mut self) {
|
||||||
self.fn_resolution.push(StraightHashMap::default());
|
self.stack.push(Default::default());
|
||||||
}
|
}
|
||||||
/// Rewind the function resolution caches stack to a particular size.
|
/// Rewind the function resolution caches stack to a particular size.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn rewind_fn_resolution_caches(&mut self, len: usize) {
|
pub fn rewind_fn_resolution_caches(&mut self, len: usize) {
|
||||||
self.fn_resolution.truncate(len);
|
self.stack.truncate(len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -257,8 +257,9 @@ impl Engine {
|
|||||||
let hash = combine_hashes(hashes.native, hash);
|
let hash = combine_hashes(hashes.native, hash);
|
||||||
|
|
||||||
let cache = caches.fn_resolution_cache_mut();
|
let cache = caches.fn_resolution_cache_mut();
|
||||||
|
let local_entry: CallableFunction;
|
||||||
|
|
||||||
let func = match cache.entry(hash) {
|
let func = match cache.map.entry(hash) {
|
||||||
Entry::Vacant(entry) => {
|
Entry::Vacant(entry) => {
|
||||||
let func = if args.len() == 2 {
|
let func = if args.len() == 2 {
|
||||||
get_builtin_binary_op_fn(name, operands[0], operands[1])
|
get_builtin_binary_op_fn(name, operands[0], operands[1])
|
||||||
@ -267,14 +268,21 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(f) = func {
|
if let Some(f) = func {
|
||||||
&entry
|
if cache.filter.is_absent_and_set(hash) {
|
||||||
.insert(Some(FnResolutionCacheEntry {
|
// Do not cache "one-hit wonders"
|
||||||
func: CallableFunction::from_fn_builtin(f),
|
local_entry = CallableFunction::from_fn_builtin(f);
|
||||||
source: None,
|
&local_entry
|
||||||
}))
|
} else {
|
||||||
.as_ref()
|
// Cache repeated calls
|
||||||
.unwrap()
|
&entry
|
||||||
.func
|
.insert(Some(FnResolutionCacheEntry {
|
||||||
|
func: CallableFunction::from_fn_builtin(f),
|
||||||
|
source: None,
|
||||||
|
}))
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.func
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let result = self.exec_fn_call(
|
let result = self.exec_fn_call(
|
||||||
None, global, caches, lib, name, *hashes, operands, false, false, pos,
|
None, global, caches, lib, name, *hashes, operands, false, false, pos,
|
||||||
@ -596,7 +604,7 @@ impl Engine {
|
|||||||
let mut context =
|
let mut context =
|
||||||
EvalContext::new(self, scope, global, Some(caches), lib, this_ptr, level);
|
EvalContext::new(self, scope, global, Some(caches), lib, this_ptr, level);
|
||||||
|
|
||||||
let result = (custom_def.func)(&mut context, &expressions);
|
let result = (custom_def.func)(&mut context, &expressions, &custom.state);
|
||||||
|
|
||||||
self.check_return_value(result, expr.start_position())
|
self.check_return_value(result, expr.start_position())
|
||||||
}
|
}
|
||||||
|
@ -867,13 +867,14 @@ impl Engine {
|
|||||||
&& access == AccessMode::ReadOnly
|
&& access == AccessMode::ReadOnly
|
||||||
&& lib.iter().any(|&m| !m.is_empty())
|
&& lib.iter().any(|&m| !m.is_empty())
|
||||||
{
|
{
|
||||||
if global.constants.is_none() {
|
crate::func::locked_write(global.constants.get_or_insert_with(
|
||||||
global.constants = Some(crate::Shared::new(
|
|| {
|
||||||
crate::Locked::new(std::collections::BTreeMap::new()),
|
crate::Shared::new(crate::Locked::new(
|
||||||
));
|
std::collections::BTreeMap::new(),
|
||||||
}
|
))
|
||||||
crate::func::locked_write(global.constants.as_ref().unwrap())
|
},
|
||||||
.insert(var_name.name.clone(), value.clone());
|
))
|
||||||
|
.insert(var_name.name.clone(), value.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
if export {
|
if export {
|
||||||
|
@ -25,32 +25,42 @@ const BUILTIN: &str = "data type was checked";
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn is_numeric(type_id: TypeId) -> bool {
|
fn is_numeric(type_id: TypeId) -> bool {
|
||||||
let result = false;
|
if type_id == TypeId::of::<INT>() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i64"))]
|
#[cfg(not(feature = "only_i64"))]
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
let result = result
|
if type_id == TypeId::of::<u8>()
|
||||||
|| type_id == TypeId::of::<u8>()
|
|
||||||
|| type_id == TypeId::of::<u16>()
|
|| type_id == TypeId::of::<u16>()
|
||||||
|| type_id == TypeId::of::<u32>()
|
|| type_id == TypeId::of::<u32>()
|
||||||
|| type_id == TypeId::of::<u64>()
|
|| type_id == TypeId::of::<u64>()
|
||||||
|| type_id == TypeId::of::<i8>()
|
|| type_id == TypeId::of::<i8>()
|
||||||
|| type_id == TypeId::of::<i16>()
|
|| type_id == TypeId::of::<i16>()
|
||||||
|| type_id == TypeId::of::<i32>()
|
|| type_id == TypeId::of::<i32>()
|
||||||
|| type_id == TypeId::of::<i64>();
|
|| type_id == TypeId::of::<i64>()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i64"))]
|
#[cfg(not(feature = "only_i64"))]
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
#[cfg(not(target_family = "wasm"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
let result = result || type_id == TypeId::of::<u128>() || type_id == TypeId::of::<i128>();
|
if type_id == TypeId::of::<u128>() || type_id == TypeId::of::<i128>() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
let result = result || type_id == TypeId::of::<f32>() || type_id == TypeId::of::<f64>();
|
if type_id == TypeId::of::<f32>() || type_id == TypeId::of::<f64>() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
let result = result || type_id == TypeId::of::<rust_decimal::Decimal>();
|
if type_id == TypeId::of::<rust_decimal::Decimal>() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
result
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build in common binary operator implementations to avoid the cost of calling a registered function.
|
/// Build in common binary operator implementations to avoid the cost of calling a registered function.
|
||||||
@ -61,8 +71,6 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
|
|||||||
let type1 = x.type_id();
|
let type1 = x.type_id();
|
||||||
let type2 = y.type_id();
|
let type2 = y.type_id();
|
||||||
|
|
||||||
let types_pair = (type1, type2);
|
|
||||||
|
|
||||||
macro_rules! impl_op {
|
macro_rules! impl_op {
|
||||||
($xx:ident $op:tt $yy:ident) => { |_, args| {
|
($xx:ident $op:tt $yy:ident) => { |_, args| {
|
||||||
let x = &*args[0].read_lock::<$xx>().expect(BUILTIN);
|
let x = &*args[0].read_lock::<$xx>().expect(BUILTIN);
|
||||||
@ -255,7 +263,7 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
macro_rules! impl_float {
|
macro_rules! impl_float {
|
||||||
($x:ty, $xx:ident, $y:ty, $yy:ident) => {
|
($x:ty, $xx:ident, $y:ty, $yy:ident) => {
|
||||||
if types_pair == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
||||||
return match op {
|
return match op {
|
||||||
"+" => Some(impl_op!(FLOAT => $xx + $yy)),
|
"+" => Some(impl_op!(FLOAT => $xx + $yy)),
|
||||||
"-" => Some(impl_op!(FLOAT => $xx - $yy)),
|
"-" => Some(impl_op!(FLOAT => $xx - $yy)),
|
||||||
@ -285,7 +293,7 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
|
|||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
macro_rules! impl_decimal {
|
macro_rules! impl_decimal {
|
||||||
($x:ty, $xx:ident, $y:ty, $yy:ident) => {
|
($x:ty, $xx:ident, $y:ty, $yy:ident) => {
|
||||||
if types_pair == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
use crate::packages::arithmetic::decimal_functions::*;
|
use crate::packages::arithmetic::decimal_functions::*;
|
||||||
|
|
||||||
@ -335,7 +343,7 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
|
|||||||
}
|
}
|
||||||
|
|
||||||
// char op string
|
// char op string
|
||||||
if types_pair == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
|
if (type1, type2) == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
|
||||||
fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) {
|
fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) {
|
||||||
let x = args[0].as_char().expect(BUILTIN);
|
let x = args[0].as_char().expect(BUILTIN);
|
||||||
let y = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN);
|
let y = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||||
@ -361,7 +369,7 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
// string op char
|
// string op char
|
||||||
if types_pair == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
|
if (type1, type2) == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
|
||||||
fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) {
|
fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) {
|
||||||
let x = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
let x = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||||
let y = args[1].as_char().expect(BUILTIN);
|
let y = args[1].as_char().expect(BUILTIN);
|
||||||
@ -397,7 +405,7 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
// () op string
|
// () op string
|
||||||
if types_pair == (TypeId::of::<()>(), TypeId::of::<ImmutableString>()) {
|
if (type1, type2) == (TypeId::of::<()>(), TypeId::of::<ImmutableString>()) {
|
||||||
return match op {
|
return match op {
|
||||||
"+" => Some(|_, args| Ok(args[1].clone())),
|
"+" => Some(|_, args| Ok(args[1].clone())),
|
||||||
"==" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)),
|
"==" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)),
|
||||||
@ -406,7 +414,7 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
// string op ()
|
// string op ()
|
||||||
if types_pair == (TypeId::of::<ImmutableString>(), TypeId::of::<()>()) {
|
if (type1, type2) == (TypeId::of::<ImmutableString>(), TypeId::of::<()>()) {
|
||||||
return match op {
|
return match op {
|
||||||
"+" => Some(|_, args| Ok(args[0].clone())),
|
"+" => Some(|_, args| Ok(args[0].clone())),
|
||||||
"==" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)),
|
"==" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)),
|
||||||
@ -446,7 +454,7 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
|
|||||||
|
|
||||||
// map op string
|
// map op string
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
if types_pair == (TypeId::of::<crate::Map>(), TypeId::of::<ImmutableString>()) {
|
if (type1, type2) == (TypeId::of::<crate::Map>(), TypeId::of::<ImmutableString>()) {
|
||||||
use crate::Map;
|
use crate::Map;
|
||||||
|
|
||||||
return match op {
|
return match op {
|
||||||
@ -456,12 +464,12 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Non-compatible ranges
|
// Non-compatible ranges
|
||||||
if types_pair
|
if (type1, type2)
|
||||||
== (
|
== (
|
||||||
TypeId::of::<ExclusiveRange>(),
|
TypeId::of::<ExclusiveRange>(),
|
||||||
TypeId::of::<InclusiveRange>(),
|
TypeId::of::<InclusiveRange>(),
|
||||||
)
|
)
|
||||||
|| types_pair
|
|| (type1, type2)
|
||||||
== (
|
== (
|
||||||
TypeId::of::<InclusiveRange>(),
|
TypeId::of::<InclusiveRange>(),
|
||||||
TypeId::of::<ExclusiveRange>(),
|
TypeId::of::<ExclusiveRange>(),
|
||||||
@ -554,8 +562,6 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
|
|||||||
let type1 = x.type_id();
|
let type1 = x.type_id();
|
||||||
let type2 = y.type_id();
|
let type2 = y.type_id();
|
||||||
|
|
||||||
let types_pair = (type1, type2);
|
|
||||||
|
|
||||||
macro_rules! impl_op {
|
macro_rules! impl_op {
|
||||||
($x:ty = x $op:tt $yy:ident) => { |_, args| {
|
($x:ty = x $op:tt $yy:ident) => { |_, args| {
|
||||||
let x = args[0].$yy().expect(BUILTIN);
|
let x = args[0].$yy().expect(BUILTIN);
|
||||||
@ -691,7 +697,7 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
macro_rules! impl_float {
|
macro_rules! impl_float {
|
||||||
($x:ident, $xx:ident, $y:ty, $yy:ident) => {
|
($x:ident, $xx:ident, $y:ty, $yy:ident) => {
|
||||||
if types_pair == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
||||||
return match op {
|
return match op {
|
||||||
"+=" => Some(impl_op!($x += $yy)),
|
"+=" => Some(impl_op!($x += $yy)),
|
||||||
"-=" => Some(impl_op!($x -= $yy)),
|
"-=" => Some(impl_op!($x -= $yy)),
|
||||||
@ -714,7 +720,7 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
|
|||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
macro_rules! impl_decimal {
|
macro_rules! impl_decimal {
|
||||||
($x:ident, $xx:ident, $y:ty, $yy:ident) => {
|
($x:ident, $xx:ident, $y:ty, $yy:ident) => {
|
||||||
if types_pair == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
use crate::packages::arithmetic::decimal_functions::*;
|
use crate::packages::arithmetic::decimal_functions::*;
|
||||||
|
|
||||||
@ -753,7 +759,7 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
|
|||||||
}
|
}
|
||||||
|
|
||||||
// string op= char
|
// string op= char
|
||||||
if types_pair == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
|
if (type1, type2) == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
|
||||||
return match op {
|
return match op {
|
||||||
"+=" => Some(impl_op!(ImmutableString += as_char as char)),
|
"+=" => Some(impl_op!(ImmutableString += as_char as char)),
|
||||||
"-=" => Some(impl_op!(ImmutableString -= as_char as char)),
|
"-=" => Some(impl_op!(ImmutableString -= as_char as char)),
|
||||||
@ -761,7 +767,7 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
// char op= string
|
// char op= string
|
||||||
if types_pair == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
|
if (type1, type2) == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
|
||||||
return match op {
|
return match op {
|
||||||
"+=" => Some(|_, args| {
|
"+=" => Some(|_, args| {
|
||||||
let mut ch = args[0].as_char().expect(BUILTIN).to_string();
|
let mut ch = args[0].as_char().expect(BUILTIN).to_string();
|
||||||
@ -810,7 +816,7 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
|
|||||||
use crate::Blob;
|
use crate::Blob;
|
||||||
|
|
||||||
// blob op= int
|
// blob op= int
|
||||||
if types_pair == (TypeId::of::<Blob>(), TypeId::of::<INT>()) {
|
if (type1, type2) == (TypeId::of::<Blob>(), TypeId::of::<INT>()) {
|
||||||
return match op {
|
return match op {
|
||||||
"+=" => Some(|_, args| {
|
"+=" => Some(|_, args| {
|
||||||
let x = args[1].as_int().expect("`INT`");
|
let x = args[1].as_int().expect("`INT`");
|
||||||
@ -822,7 +828,7 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
|
|||||||
}
|
}
|
||||||
|
|
||||||
// blob op= char
|
// blob op= char
|
||||||
if types_pair == (TypeId::of::<Blob>(), TypeId::of::<char>()) {
|
if (type1, type2) == (TypeId::of::<Blob>(), TypeId::of::<char>()) {
|
||||||
return match op {
|
return match op {
|
||||||
"+=" => Some(|_, args| {
|
"+=" => Some(|_, args| {
|
||||||
let x = args[1].as_char().expect("`char`");
|
let x = args[1].as_char().expect("`char`");
|
||||||
@ -834,7 +840,7 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
|
|||||||
}
|
}
|
||||||
|
|
||||||
// blob op= string
|
// blob op= string
|
||||||
if types_pair == (TypeId::of::<Blob>(), TypeId::of::<ImmutableString>()) {
|
if (type1, type2) == (TypeId::of::<Blob>(), TypeId::of::<ImmutableString>()) {
|
||||||
return match op {
|
return match op {
|
||||||
"+=" => Some(|_, args| {
|
"+=" => Some(|_, args| {
|
||||||
let s = std::mem::take(args[1]).cast::<ImmutableString>();
|
let s = std::mem::take(args[1]).cast::<ImmutableString>();
|
||||||
|
@ -185,6 +185,7 @@ impl Engine {
|
|||||||
&self,
|
&self,
|
||||||
_global: &GlobalRuntimeState,
|
_global: &GlobalRuntimeState,
|
||||||
caches: &'s mut Caches,
|
caches: &'s mut Caches,
|
||||||
|
local_entry: &'s mut Option<FnResolutionCacheEntry>,
|
||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
hash_base: u64,
|
hash_base: u64,
|
||||||
@ -203,7 +204,9 @@ impl Engine {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
match caches.fn_resolution_cache_mut().entry(hash) {
|
let cache = caches.fn_resolution_cache_mut();
|
||||||
|
|
||||||
|
match cache.map.entry(hash) {
|
||||||
Entry::Occupied(entry) => entry.into_mut().as_ref(),
|
Entry::Occupied(entry) => entry.into_mut().as_ref(),
|
||||||
Entry::Vacant(entry) => {
|
Entry::Vacant(entry) => {
|
||||||
let num_args = args.as_ref().map_or(0, |a| a.len());
|
let num_args = args.as_ref().map_or(0, |a| a.len());
|
||||||
@ -229,12 +232,19 @@ impl Engine {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if let Some((f, s)) = func {
|
if let Some((f, s)) = func {
|
||||||
// Specific version found - insert into cache and return it
|
// Specific version found
|
||||||
let new_entry = FnResolutionCacheEntry {
|
let new_entry = Some(FnResolutionCacheEntry {
|
||||||
func: f.clone(),
|
func: f.clone(),
|
||||||
source: s.map(|s| Box::new(s.into())),
|
source: s.map(|s| Box::new(s.into())),
|
||||||
|
});
|
||||||
|
return if cache.filter.is_absent_and_set(hash) {
|
||||||
|
// Do not cache "one-hit wonders"
|
||||||
|
*local_entry = new_entry;
|
||||||
|
local_entry.as_ref()
|
||||||
|
} else {
|
||||||
|
// Cache entry
|
||||||
|
entry.insert(new_entry).as_ref()
|
||||||
};
|
};
|
||||||
return entry.insert(Some(new_entry)).as_ref();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check `Dynamic` parameters for functions with parameters
|
// Check `Dynamic` parameters for functions with parameters
|
||||||
@ -288,7 +298,14 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return entry.insert(builtin).as_ref();
|
return if cache.filter.is_absent_and_set(hash) {
|
||||||
|
// Do not cache "one-hit wonders"
|
||||||
|
*local_entry = builtin;
|
||||||
|
local_entry.as_ref()
|
||||||
|
} else {
|
||||||
|
// Cache entry
|
||||||
|
entry.insert(builtin).as_ref()
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try all permutations with `Dynamic` wildcards
|
// Try all permutations with `Dynamic` wildcards
|
||||||
@ -345,9 +362,12 @@ impl Engine {
|
|||||||
let parent_source = global.source.clone();
|
let parent_source = global.source.clone();
|
||||||
|
|
||||||
// Check if function access already in the cache
|
// Check if function access already in the cache
|
||||||
|
let local_entry = &mut None;
|
||||||
|
|
||||||
let func = self.resolve_fn(
|
let func = self.resolve_fn(
|
||||||
global,
|
global,
|
||||||
caches,
|
caches,
|
||||||
|
local_entry,
|
||||||
lib,
|
lib,
|
||||||
name,
|
name,
|
||||||
hash,
|
hash,
|
||||||
@ -619,11 +639,15 @@ impl Engine {
|
|||||||
let level = level + 1;
|
let level = level + 1;
|
||||||
|
|
||||||
// Script-defined function call?
|
// Script-defined function call?
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
let local_entry = &mut None;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
if let Some(FnResolutionCacheEntry { func, ref source }) = self
|
if let Some(FnResolutionCacheEntry { func, ref source }) = self
|
||||||
.resolve_fn(
|
.resolve_fn(
|
||||||
global,
|
global,
|
||||||
caches,
|
caches,
|
||||||
|
local_entry,
|
||||||
lib,
|
lib,
|
||||||
fn_name,
|
fn_name,
|
||||||
hashes.script,
|
hashes.script,
|
||||||
|
@ -8,10 +8,10 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
pub type StraightHashMap<K, V> = hashbrown::HashMap<K, V, StraightHasherBuilder>;
|
pub type StraightHashMap<V> = hashbrown::HashMap<u64, V, StraightHasherBuilder>;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
pub type StraightHashMap<K, V> = std::collections::HashMap<K, V, StraightHasherBuilder>;
|
pub type StraightHashMap<V> = std::collections::HashMap<u64, V, StraightHasherBuilder>;
|
||||||
|
|
||||||
/// Dummy hash value to map zeros to. This value can be anything.
|
/// Dummy hash value to map zeros to. This value can be anything.
|
||||||
///
|
///
|
||||||
|
@ -64,7 +64,7 @@ pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T {
|
|||||||
///
|
///
|
||||||
/// # Type Parameters
|
/// # Type Parameters
|
||||||
///
|
///
|
||||||
/// * `ARGS` - a tuple containing parameter types, with `&mut T` represented by [`Mut<T>`].
|
/// * `ARGS` - a tuple containing parameter types, with `&mut T` represented by `Mut<T>`.
|
||||||
/// * `RET` - return type of the function; if the function returns `Result`, it is the unwrapped inner value type.
|
/// * `RET` - return type of the function; if the function returns `Result`, it is the unwrapped inner value type.
|
||||||
pub trait RegisterNativeFunction<ARGS, RET, RESULT> {
|
pub trait RegisterNativeFunction<ARGS, RET, RESULT> {
|
||||||
/// Convert this function into a [`CallableFunction`].
|
/// Convert this function into a [`CallableFunction`].
|
||||||
|
@ -35,6 +35,7 @@ impl Engine {
|
|||||||
pos: Position,
|
pos: Position,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
|
#[cold]
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
fn make_error(
|
fn make_error(
|
||||||
name: String,
|
name: String,
|
||||||
@ -234,7 +235,7 @@ impl Engine {
|
|||||||
) -> bool {
|
) -> bool {
|
||||||
let cache = caches.fn_resolution_cache_mut();
|
let cache = caches.fn_resolution_cache_mut();
|
||||||
|
|
||||||
if let Some(result) = cache.get(&hash_script).map(Option::is_some) {
|
if let Some(result) = cache.map.get(&hash_script).map(Option::is_some) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,7 +252,11 @@ impl Engine {
|
|||||||
|| self.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash_script));
|
|| self.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash_script));
|
||||||
|
|
||||||
if !result {
|
if !result {
|
||||||
cache.insert(hash_script, None);
|
if cache.filter.is_absent(hash_script) {
|
||||||
|
cache.filter.mark(hash_script);
|
||||||
|
} else {
|
||||||
|
cache.map.insert(hash_script, None);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
|
@ -16,7 +16,7 @@ use crate::{
|
|||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{
|
use std::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
collections::{BTreeMap, BTreeSet},
|
collections::BTreeMap,
|
||||||
fmt,
|
fmt,
|
||||||
ops::{Add, AddAssign},
|
ops::{Add, AddAssign},
|
||||||
};
|
};
|
||||||
@ -170,24 +170,24 @@ pub struct Module {
|
|||||||
/// Is this module part of a standard library?
|
/// Is this module part of a standard library?
|
||||||
pub(crate) standard: bool,
|
pub(crate) standard: bool,
|
||||||
/// Custom types.
|
/// Custom types.
|
||||||
custom_types: CustomTypesCollection,
|
custom_types: Option<CustomTypesCollection>,
|
||||||
/// Sub-modules.
|
/// Sub-modules.
|
||||||
modules: BTreeMap<Identifier, Shared<Module>>,
|
modules: Option<BTreeMap<Identifier, Shared<Module>>>,
|
||||||
/// [`Module`] variables.
|
/// [`Module`] variables.
|
||||||
variables: BTreeMap<Identifier, Dynamic>,
|
variables: Option<BTreeMap<Identifier, 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: StraightHashMap<u64, Dynamic>,
|
all_variables: Option<StraightHashMap<Dynamic>>,
|
||||||
/// Functions (both native Rust and scripted).
|
/// Functions (both native Rust and scripted).
|
||||||
functions: StraightHashMap<u64, Box<FuncInfo>>,
|
functions: StraightHashMap<FuncInfo>,
|
||||||
/// Flattened collection of all functions, native Rust and scripted.
|
/// Flattened collection of all functions, native Rust and scripted.
|
||||||
/// including those in sub-modules.
|
/// including those in sub-modules.
|
||||||
all_functions: StraightHashMap<u64, CallableFunction>,
|
all_functions: Option<StraightHashMap<CallableFunction>>,
|
||||||
/// Native Rust functions (in scripted hash format) that contain [`Dynamic`] parameters.
|
/// Native Rust functions (in scripted hash format) that contain [`Dynamic`] parameters.
|
||||||
dynamic_functions: BloomFilterU64,
|
dynamic_functions: BloomFilterU64,
|
||||||
/// Iterator functions, keyed by the type producing the iterator.
|
/// Iterator functions, keyed by the type producing the iterator.
|
||||||
type_iterators: BTreeMap<TypeId, Shared<IteratorFn>>,
|
type_iterators: Option<BTreeMap<TypeId, Shared<IteratorFn>>>,
|
||||||
/// Flattened collection of iterator functions, including those in sub-modules.
|
/// Flattened collection of iterator functions, including those in sub-modules.
|
||||||
all_type_iterators: BTreeMap<TypeId, Shared<IteratorFn>>,
|
all_type_iterators: Option<BTreeMap<TypeId, Shared<IteratorFn>>>,
|
||||||
/// Is the [`Module`] indexed?
|
/// Is the [`Module`] indexed?
|
||||||
indexed: bool,
|
indexed: bool,
|
||||||
/// Does the [`Module`] contain indexed functions that have been exposed to the global namespace?
|
/// Does the [`Module`] contain indexed functions that have been exposed to the global namespace?
|
||||||
@ -210,9 +210,10 @@ impl fmt::Debug for Module {
|
|||||||
"modules",
|
"modules",
|
||||||
&self
|
&self
|
||||||
.modules
|
.modules
|
||||||
.keys()
|
.iter()
|
||||||
|
.flat_map(|m| m.keys())
|
||||||
.map(SmartString::as_str)
|
.map(SmartString::as_str)
|
||||||
.collect::<BTreeSet<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
)
|
)
|
||||||
.field("vars", &self.variables)
|
.field("vars", &self.variables)
|
||||||
.field(
|
.field(
|
||||||
@ -220,7 +221,7 @@ impl fmt::Debug for Module {
|
|||||||
&self
|
&self
|
||||||
.iter_fn()
|
.iter_fn()
|
||||||
.map(|f| f.func.to_string())
|
.map(|f| f.func.to_string())
|
||||||
.collect::<BTreeSet<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
@ -269,24 +270,39 @@ impl Module {
|
|||||||
/// module.set_var("answer", 42_i64);
|
/// module.set_var("answer", 42_i64);
|
||||||
/// assert_eq!(module.get_var_value::<i64>("answer").expect("answer should exist"), 42);
|
/// assert_eq!(module.get_var_value::<i64>("answer").expect("answer should exist"), 42);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
|
Self::with_capacity(16)
|
||||||
|
}
|
||||||
|
/// Create a new [`Module`] with a pre-sized capacity for functions.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use rhai::Module;
|
||||||
|
/// let mut module = Module::with_capacity(10);
|
||||||
|
/// module.set_var("answer", 42_i64);
|
||||||
|
/// assert_eq!(module.get_var_value::<i64>("answer").expect("answer should exist"), 42);
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_capacity(capacity: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: Identifier::new_const(),
|
id: Identifier::new_const(),
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
doc: crate::SmartString::new_const(),
|
doc: crate::SmartString::new_const(),
|
||||||
internal: false,
|
internal: false,
|
||||||
standard: false,
|
standard: false,
|
||||||
custom_types: CustomTypesCollection::new(),
|
custom_types: None,
|
||||||
modules: BTreeMap::new(),
|
modules: None,
|
||||||
variables: BTreeMap::new(),
|
variables: None,
|
||||||
all_variables: StraightHashMap::default(),
|
all_variables: None,
|
||||||
functions: StraightHashMap::default(),
|
functions: StraightHashMap::with_capacity_and_hasher(capacity, Default::default()),
|
||||||
all_functions: StraightHashMap::default(),
|
all_functions: None,
|
||||||
dynamic_functions: BloomFilterU64::new(),
|
dynamic_functions: BloomFilterU64::new(),
|
||||||
type_iterators: BTreeMap::new(),
|
type_iterators: None,
|
||||||
all_type_iterators: BTreeMap::new(),
|
all_type_iterators: None,
|
||||||
indexed: true,
|
indexed: true,
|
||||||
contains_indexed_global_functions: false,
|
contains_indexed_global_functions: false,
|
||||||
}
|
}
|
||||||
@ -420,15 +436,15 @@ impl Module {
|
|||||||
self.doc.clear();
|
self.doc.clear();
|
||||||
self.internal = false;
|
self.internal = false;
|
||||||
self.standard = false;
|
self.standard = false;
|
||||||
self.custom_types.clear();
|
self.custom_types = None;
|
||||||
self.modules.clear();
|
self.modules = None;
|
||||||
self.variables.clear();
|
self.variables = None;
|
||||||
self.all_variables.clear();
|
self.all_variables = None;
|
||||||
self.functions.clear();
|
self.functions.clear();
|
||||||
self.all_functions.clear();
|
self.all_functions = None;
|
||||||
self.dynamic_functions.clear();
|
self.dynamic_functions.clear();
|
||||||
self.type_iterators.clear();
|
self.type_iterators = None;
|
||||||
self.all_type_iterators.clear();
|
self.all_type_iterators = None;
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
self.contains_indexed_global_functions = false;
|
self.contains_indexed_global_functions = false;
|
||||||
}
|
}
|
||||||
@ -452,7 +468,9 @@ impl Module {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_custom_type<T>(&mut self, name: &str) -> &mut Self {
|
pub fn set_custom_type<T>(&mut self, name: &str) -> &mut Self {
|
||||||
self.custom_types.add_type::<T>(name);
|
self.custom_types
|
||||||
|
.get_or_insert_with(CustomTypesCollection::new)
|
||||||
|
.add_type::<T>(name);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Map a custom type to a friendly display name.
|
/// Map a custom type to a friendly display name.
|
||||||
@ -476,7 +494,9 @@ impl Module {
|
|||||||
type_name: impl Into<Identifier>,
|
type_name: impl Into<Identifier>,
|
||||||
name: impl Into<Identifier>,
|
name: impl Into<Identifier>,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.custom_types.add(type_name, name);
|
self.custom_types
|
||||||
|
.get_or_insert_with(CustomTypesCollection::new)
|
||||||
|
.add(type_name, name);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Get the display name of a registered custom type.
|
/// Get the display name of a registered custom type.
|
||||||
@ -499,7 +519,10 @@ impl Module {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_custom_type(&self, key: &str) -> Option<&str> {
|
pub fn get_custom_type(&self, key: &str) -> Option<&str> {
|
||||||
self.custom_types.get(key).map(|t| t.display_name.as_str())
|
self.custom_types
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|c| c.get(key))
|
||||||
|
.map(|t| t.display_name.as_str())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if this [`Module`] contains no items.
|
/// Returns `true` if this [`Module`] contains no items.
|
||||||
@ -517,12 +540,15 @@ impl Module {
|
|||||||
self.indexed
|
self.indexed
|
||||||
&& !self.contains_indexed_global_functions
|
&& !self.contains_indexed_global_functions
|
||||||
&& self.functions.is_empty()
|
&& self.functions.is_empty()
|
||||||
&& self.all_functions.is_empty()
|
&& self.all_functions.as_ref().map_or(true, |m| m.is_empty())
|
||||||
&& self.variables.is_empty()
|
&& self.variables.as_ref().map_or(true, |m| m.is_empty())
|
||||||
&& self.all_variables.is_empty()
|
&& self.all_variables.as_ref().map_or(true, |m| m.is_empty())
|
||||||
&& self.modules.is_empty()
|
&& self.modules.as_ref().map_or(true, |m| m.is_empty())
|
||||||
&& self.type_iterators.is_empty()
|
&& self.type_iterators.as_ref().map_or(true, |t| t.is_empty())
|
||||||
&& self.all_type_iterators.is_empty()
|
&& self
|
||||||
|
.all_type_iterators
|
||||||
|
.as_ref()
|
||||||
|
.map_or(true, |m| m.is_empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the [`Module`] indexed?
|
/// Is the [`Module`] indexed?
|
||||||
@ -577,11 +603,9 @@ impl Module {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn contains_var(&self, name: &str) -> bool {
|
pub fn contains_var(&self, name: &str) -> bool {
|
||||||
if self.variables.is_empty() {
|
self.variables
|
||||||
false
|
.as_ref()
|
||||||
} else {
|
.map_or(false, |m| m.contains_key(name))
|
||||||
self.variables.contains_key(name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the value of a [`Module`] variable.
|
/// Get the value of a [`Module`] variable.
|
||||||
@ -613,11 +637,7 @@ impl Module {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_var(&self, name: &str) -> Option<Dynamic> {
|
pub fn get_var(&self, name: &str) -> Option<Dynamic> {
|
||||||
if self.variables.is_empty() {
|
self.variables.as_ref().and_then(|m| m.get(name)).cloned()
|
||||||
None
|
|
||||||
} else {
|
|
||||||
self.variables.get(name).cloned()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a variable into the [`Module`].
|
/// Set a variable into the [`Module`].
|
||||||
@ -643,9 +663,13 @@ impl Module {
|
|||||||
|
|
||||||
if self.indexed {
|
if self.indexed {
|
||||||
let hash_var = crate::calc_qualified_var_hash(Some(""), &ident);
|
let hash_var = crate::calc_qualified_var_hash(Some(""), &ident);
|
||||||
self.all_variables.insert(hash_var, value.clone());
|
self.all_variables
|
||||||
|
.get_or_insert_with(|| Default::default())
|
||||||
|
.insert(hash_var, value.clone());
|
||||||
}
|
}
|
||||||
self.variables.insert(ident, value);
|
self.variables
|
||||||
|
.get_or_insert_with(|| Default::default())
|
||||||
|
.insert(ident, value);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -653,11 +677,9 @@ impl Module {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn get_qualified_var(&self, hash_var: u64) -> Option<Dynamic> {
|
pub(crate) fn get_qualified_var(&self, hash_var: u64) -> Option<Dynamic> {
|
||||||
if self.all_variables.is_empty() {
|
self.all_variables
|
||||||
None
|
.as_ref()
|
||||||
} else {
|
.and_then(|c| c.get(&hash_var).cloned())
|
||||||
self.all_variables.get(&hash_var).cloned()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a script-defined function into the [`Module`].
|
/// Set a script-defined function into the [`Module`].
|
||||||
@ -688,8 +710,7 @@ impl Module {
|
|||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
comments: Box::default(),
|
comments: Box::default(),
|
||||||
func: fn_def.into(),
|
func: fn_def.into(),
|
||||||
}
|
},
|
||||||
.into(),
|
|
||||||
);
|
);
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
self.contains_indexed_global_functions = false;
|
self.contains_indexed_global_functions = false;
|
||||||
@ -717,7 +738,8 @@ impl Module {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a mutable reference to the underlying [`BTreeMap`] of sub-modules.
|
/// Get a mutable reference to the underlying [`BTreeMap`] of sub-modules,
|
||||||
|
/// creating one if empty.
|
||||||
///
|
///
|
||||||
/// # WARNING
|
/// # WARNING
|
||||||
///
|
///
|
||||||
@ -726,16 +748,16 @@ impl Module {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn sub_modules_mut(&mut self) -> &mut BTreeMap<Identifier, Shared<Module>> {
|
pub(crate) fn get_sub_modules_mut(&mut self) -> &mut BTreeMap<Identifier, Shared<Module>> {
|
||||||
// We must assume that the user has changed the sub-modules
|
// We must assume that the user has changed the sub-modules
|
||||||
// (otherwise why take a mutable reference?)
|
// (otherwise why take a mutable reference?)
|
||||||
self.all_functions.clear();
|
self.all_functions = None;
|
||||||
self.all_variables.clear();
|
self.all_variables = None;
|
||||||
self.all_type_iterators.clear();
|
self.all_type_iterators = None;
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
self.contains_indexed_global_functions = false;
|
self.contains_indexed_global_functions = false;
|
||||||
|
|
||||||
&mut self.modules
|
self.modules.get_or_insert_with(|| Default::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Does a sub-module exist in the [`Module`]?
|
/// Does a sub-module exist in the [`Module`]?
|
||||||
@ -752,11 +774,9 @@ impl Module {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn contains_sub_module(&self, name: &str) -> bool {
|
pub fn contains_sub_module(&self, name: &str) -> bool {
|
||||||
if self.modules.is_empty() {
|
self.modules
|
||||||
false
|
.as_ref()
|
||||||
} else {
|
.map_or(false, |m| m.contains_key(name))
|
||||||
self.modules.contains_key(name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a sub-module in the [`Module`].
|
/// Get a sub-module in the [`Module`].
|
||||||
@ -773,11 +793,10 @@ impl Module {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_sub_module(&self, name: &str) -> Option<&Module> {
|
pub fn get_sub_module(&self, name: &str) -> Option<&Module> {
|
||||||
if self.modules.is_empty() {
|
self.modules
|
||||||
None
|
.as_ref()
|
||||||
} else {
|
.and_then(|m| m.get(name))
|
||||||
self.modules.get(name).map(|m| &**m)
|
.map(|m| &**m)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a sub-module into the [`Module`].
|
/// Set a sub-module into the [`Module`].
|
||||||
@ -799,7 +818,9 @@ impl Module {
|
|||||||
name: impl Into<Identifier>,
|
name: impl Into<Identifier>,
|
||||||
sub_module: impl Into<Shared<Module>>,
|
sub_module: impl Into<Shared<Module>>,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.modules.insert(name.into(), sub_module.into());
|
self.modules
|
||||||
|
.get_or_insert_with(|| Default::default())
|
||||||
|
.insert(name.into(), sub_module.into());
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
self.contains_indexed_global_functions = false;
|
self.contains_indexed_global_functions = false;
|
||||||
self
|
self
|
||||||
@ -1022,8 +1043,7 @@ impl Module {
|
|||||||
return_type: return_type_name,
|
return_type: return_type_name,
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
comments: Box::default(),
|
comments: Box::default(),
|
||||||
}
|
},
|
||||||
.into(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
@ -1535,11 +1555,9 @@ impl Module {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn contains_qualified_fn(&self, hash_fn: u64) -> bool {
|
pub fn contains_qualified_fn(&self, hash_fn: u64) -> bool {
|
||||||
if self.all_functions.is_empty() {
|
self.all_functions
|
||||||
false
|
.as_ref()
|
||||||
} else {
|
.map_or(false, |m| m.contains_key(&hash_fn))
|
||||||
self.all_functions.contains_key(&hash_fn)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a namespace-qualified function.
|
/// Get a namespace-qualified function.
|
||||||
@ -1549,25 +1567,41 @@ impl Module {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn get_qualified_fn(&self, hash_qualified_fn: u64) -> Option<&CallableFunction> {
|
pub(crate) fn get_qualified_fn(&self, hash_qualified_fn: u64) -> Option<&CallableFunction> {
|
||||||
if self.all_functions.is_empty() {
|
self.all_functions
|
||||||
None
|
.as_ref()
|
||||||
} else {
|
.and_then(|m| m.get(&hash_qualified_fn))
|
||||||
self.all_functions.get(&hash_qualified_fn)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Combine another [`Module`] into this [`Module`].
|
/// Combine another [`Module`] into this [`Module`].
|
||||||
/// The other [`Module`] is _consumed_ to merge into this [`Module`].
|
/// The other [`Module`] is _consumed_ to merge into this [`Module`].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn combine(&mut self, other: Self) -> &mut Self {
|
pub fn combine(&mut self, other: Self) -> &mut Self {
|
||||||
self.modules.extend(other.modules.into_iter());
|
match self.modules {
|
||||||
self.variables.extend(other.variables.into_iter());
|
Some(ref mut m) if other.modules.is_some() => {
|
||||||
|
m.extend(other.modules.unwrap().into_iter())
|
||||||
|
}
|
||||||
|
Some(_) => (),
|
||||||
|
None => self.modules = other.modules,
|
||||||
|
}
|
||||||
|
match self.variables {
|
||||||
|
Some(ref mut m) if other.variables.is_some() => {
|
||||||
|
m.extend(other.variables.unwrap().into_iter())
|
||||||
|
}
|
||||||
|
Some(_) => (),
|
||||||
|
None => self.variables = other.variables,
|
||||||
|
}
|
||||||
self.functions.extend(other.functions.into_iter());
|
self.functions.extend(other.functions.into_iter());
|
||||||
self.dynamic_functions += &other.dynamic_functions;
|
self.dynamic_functions += &other.dynamic_functions;
|
||||||
self.type_iterators.extend(other.type_iterators.into_iter());
|
match self.type_iterators {
|
||||||
self.all_functions.clear();
|
Some(ref mut m) if other.type_iterators.is_some() => {
|
||||||
self.all_variables.clear();
|
m.extend(other.type_iterators.unwrap().into_iter())
|
||||||
self.all_type_iterators.clear();
|
}
|
||||||
|
Some(_) => (),
|
||||||
|
None => self.type_iterators = other.type_iterators,
|
||||||
|
}
|
||||||
|
self.all_functions = None;
|
||||||
|
self.all_variables = None;
|
||||||
|
self.all_type_iterators = None;
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
self.contains_indexed_global_functions = false;
|
self.contains_indexed_global_functions = false;
|
||||||
|
|
||||||
@ -1587,16 +1621,30 @@ impl Module {
|
|||||||
/// Sub-modules are flattened onto the root [`Module`], with higher level overriding lower level.
|
/// Sub-modules are flattened onto the root [`Module`], with higher level overriding lower level.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn combine_flatten(&mut self, other: Self) -> &mut Self {
|
pub fn combine_flatten(&mut self, other: Self) -> &mut Self {
|
||||||
for (.., m) in other.modules {
|
if let Some(modules) = other.modules {
|
||||||
self.combine_flatten(shared_take_or_clone(m));
|
for m in modules.into_values() {
|
||||||
|
self.combine_flatten(shared_take_or_clone(m));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match self.variables {
|
||||||
|
Some(ref mut m) if other.variables.is_some() => {
|
||||||
|
m.extend(other.variables.unwrap().into_iter())
|
||||||
|
}
|
||||||
|
Some(_) => (),
|
||||||
|
None => self.variables = other.variables,
|
||||||
}
|
}
|
||||||
self.variables.extend(other.variables.into_iter());
|
|
||||||
self.functions.extend(other.functions.into_iter());
|
self.functions.extend(other.functions.into_iter());
|
||||||
self.dynamic_functions += &other.dynamic_functions;
|
self.dynamic_functions += &other.dynamic_functions;
|
||||||
self.type_iterators.extend(other.type_iterators.into_iter());
|
match self.type_iterators {
|
||||||
self.all_functions.clear();
|
Some(ref mut m) if other.type_iterators.is_some() => {
|
||||||
self.all_variables.clear();
|
m.extend(other.type_iterators.unwrap().into_iter())
|
||||||
self.all_type_iterators.clear();
|
}
|
||||||
|
Some(_) => (),
|
||||||
|
None => self.type_iterators = other.type_iterators,
|
||||||
|
}
|
||||||
|
self.all_functions = None;
|
||||||
|
self.all_variables = None;
|
||||||
|
self.all_type_iterators = None;
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
self.contains_indexed_global_functions = false;
|
self.contains_indexed_global_functions = false;
|
||||||
|
|
||||||
@ -1615,26 +1663,40 @@ impl Module {
|
|||||||
/// Only items not existing in this [`Module`] are added.
|
/// Only items not existing in this [`Module`] are added.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn fill_with(&mut self, other: &Self) -> &mut Self {
|
pub fn fill_with(&mut self, other: &Self) -> &mut Self {
|
||||||
for (k, v) in &other.modules {
|
if let Some(ref modules) = other.modules {
|
||||||
if !self.modules.contains_key(k) {
|
let m = self.modules.get_or_insert_with(|| Default::default());
|
||||||
self.modules.insert(k.clone(), v.clone());
|
|
||||||
|
for (k, v) in modules {
|
||||||
|
if !m.contains_key(k) {
|
||||||
|
m.insert(k.clone(), v.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (k, v) in &other.variables {
|
if let Some(ref variables) = other.variables {
|
||||||
if !self.variables.contains_key(k) {
|
for (k, v) in variables {
|
||||||
self.variables.insert(k.clone(), v.clone());
|
let m = self.variables.get_or_insert_with(|| Default::default());
|
||||||
|
|
||||||
|
if !m.contains_key(k) {
|
||||||
|
m.insert(k.clone(), v.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (&k, v) in &other.functions {
|
for (&k, v) in &other.functions {
|
||||||
self.functions.entry(k).or_insert_with(|| v.clone());
|
self.functions.entry(k).or_insert_with(|| v.clone());
|
||||||
}
|
}
|
||||||
self.dynamic_functions += &other.dynamic_functions;
|
self.dynamic_functions += &other.dynamic_functions;
|
||||||
for (&k, v) in &other.type_iterators {
|
if let Some(ref type_iterators) = other.type_iterators {
|
||||||
self.type_iterators.entry(k).or_insert_with(|| v.clone());
|
let t = self
|
||||||
|
.type_iterators
|
||||||
|
.get_or_insert_with(|| Default::default());
|
||||||
|
|
||||||
|
for (&k, v) in type_iterators {
|
||||||
|
t.entry(k).or_insert_with(|| v.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.all_functions.clear();
|
self.all_functions = None;
|
||||||
self.all_variables.clear();
|
self.all_variables = None;
|
||||||
self.all_type_iterators.clear();
|
self.all_type_iterators = None;
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
self.contains_indexed_global_functions = false;
|
self.contains_indexed_global_functions = false;
|
||||||
|
|
||||||
@ -1661,17 +1723,27 @@ impl Module {
|
|||||||
other: &Self,
|
other: &Self,
|
||||||
_filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool + Copy,
|
_filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool + Copy,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
for (k, v) in &other.modules {
|
if let Some(ref modules) = other.modules {
|
||||||
let mut m = Self::new();
|
for (k, v) in modules {
|
||||||
m.merge_filtered(v, _filter);
|
let mut m = Self::new();
|
||||||
self.set_sub_module(k.clone(), m);
|
m.merge_filtered(v, _filter);
|
||||||
|
self.set_sub_module(k.clone(), m);
|
||||||
|
}
|
||||||
|
#[cfg(feature = "no_function")]
|
||||||
|
if let Some(ref mut m) = self.modules {
|
||||||
|
m.extend(modules.iter().map(|(k, v)| (k.clone(), v.clone())));
|
||||||
|
} else {
|
||||||
|
self.modules = Some(modules.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#[cfg(feature = "no_function")]
|
|
||||||
self.modules
|
|
||||||
.extend(other.modules.iter().map(|(k, v)| (k.clone(), v.clone())));
|
|
||||||
|
|
||||||
self.variables
|
if let Some(ref variables) = other.variables {
|
||||||
.extend(other.variables.iter().map(|(k, v)| (k.clone(), v.clone())));
|
if let Some(ref mut m) = self.variables {
|
||||||
|
m.extend(variables.iter().map(|(k, v)| (k.clone(), v.clone())));
|
||||||
|
} else {
|
||||||
|
self.variables = other.variables.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.functions.extend(
|
self.functions.extend(
|
||||||
other
|
other
|
||||||
@ -1690,11 +1762,16 @@ impl Module {
|
|||||||
);
|
);
|
||||||
self.dynamic_functions += &other.dynamic_functions;
|
self.dynamic_functions += &other.dynamic_functions;
|
||||||
|
|
||||||
self.type_iterators
|
if let Some(ref type_iterators) = other.type_iterators {
|
||||||
.extend(other.type_iterators.iter().map(|(&k, v)| (k, v.clone())));
|
if let Some(ref mut t) = self.type_iterators {
|
||||||
self.all_functions.clear();
|
t.extend(type_iterators.iter().map(|(&k, v)| (k, v.clone())));
|
||||||
self.all_variables.clear();
|
} else {
|
||||||
self.all_type_iterators.clear();
|
self.type_iterators = other.type_iterators.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.all_functions = None;
|
||||||
|
self.all_variables = None;
|
||||||
|
self.all_type_iterators = None;
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
self.contains_indexed_global_functions = false;
|
self.contains_indexed_global_functions = false;
|
||||||
|
|
||||||
@ -1727,10 +1804,10 @@ impl Module {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
self.all_functions.clear();
|
|
||||||
self.dynamic_functions.clear();
|
self.dynamic_functions.clear();
|
||||||
self.all_variables.clear();
|
self.all_functions = None;
|
||||||
self.all_type_iterators.clear();
|
self.all_variables = None;
|
||||||
|
self.all_type_iterators = None;
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
self.contains_indexed_global_functions = false;
|
self.contains_indexed_global_functions = false;
|
||||||
self
|
self
|
||||||
@ -1741,29 +1818,33 @@ impl Module {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn count(&self) -> (usize, usize, usize) {
|
pub fn count(&self) -> (usize, usize, usize) {
|
||||||
(
|
(
|
||||||
self.variables.len(),
|
self.variables.as_ref().map_or(0, |m| m.len()),
|
||||||
self.functions.len(),
|
self.functions.len(),
|
||||||
self.type_iterators.len(),
|
self.type_iterators.as_ref().map_or(0, |t| t.len()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an iterator to the sub-modules in the [`Module`].
|
/// Get an iterator to the sub-modules in the [`Module`].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn iter_sub_modules(&self) -> impl Iterator<Item = (&str, &Shared<Module>)> {
|
pub fn iter_sub_modules(&self) -> impl Iterator<Item = (&str, &Shared<Module>)> {
|
||||||
self.modules.iter().map(|(k, m)| (k.as_str(), m))
|
self.modules
|
||||||
|
.iter()
|
||||||
|
.flat_map(|m| m.iter().map(|(k, m)| (k.as_str(), m)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an iterator to the variables in the [`Module`].
|
/// Get an iterator to the variables in the [`Module`].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn iter_var(&self) -> impl Iterator<Item = (&str, &Dynamic)> {
|
pub fn iter_var(&self) -> impl Iterator<Item = (&str, &Dynamic)> {
|
||||||
self.variables.iter().map(|(k, v)| (k.as_str(), v))
|
self.variables
|
||||||
|
.iter()
|
||||||
|
.flat_map(|m| m.iter().map(|(k, v)| (k.as_str(), v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an iterator to the functions in the [`Module`].
|
/// Get an iterator to the functions in the [`Module`].
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) fn iter_fn(&self) -> impl Iterator<Item = &FuncInfo> {
|
pub(crate) fn iter_fn(&self) -> impl Iterator<Item = &FuncInfo> {
|
||||||
self.functions.values().map(<_>::as_ref)
|
self.functions.values()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an iterator over all script-defined functions in the [`Module`].
|
/// Get an iterator over all script-defined functions in the [`Module`].
|
||||||
@ -2025,31 +2106,37 @@ impl Module {
|
|||||||
fn index_module<'a>(
|
fn index_module<'a>(
|
||||||
module: &'a Module,
|
module: &'a Module,
|
||||||
path: &mut Vec<&'a str>,
|
path: &mut Vec<&'a str>,
|
||||||
variables: &mut StraightHashMap<u64, Dynamic>,
|
variables: &mut StraightHashMap<Dynamic>,
|
||||||
functions: &mut StraightHashMap<u64, CallableFunction>,
|
functions: &mut StraightHashMap<CallableFunction>,
|
||||||
type_iterators: &mut BTreeMap<TypeId, Shared<IteratorFn>>,
|
type_iterators: &mut BTreeMap<TypeId, Shared<IteratorFn>>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let mut contains_indexed_global_functions = false;
|
let mut contains_indexed_global_functions = false;
|
||||||
|
|
||||||
for (name, m) in &module.modules {
|
if let Some(ref modules) = module.modules {
|
||||||
// Index all the sub-modules first.
|
for (name, m) in modules {
|
||||||
path.push(name);
|
// Index all the sub-modules first.
|
||||||
if index_module(m, path, variables, functions, type_iterators) {
|
path.push(name);
|
||||||
contains_indexed_global_functions = true;
|
if index_module(m, path, variables, functions, type_iterators) {
|
||||||
|
contains_indexed_global_functions = true;
|
||||||
|
}
|
||||||
|
path.pop();
|
||||||
}
|
}
|
||||||
path.pop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Index all variables
|
// Index all variables
|
||||||
for (var_name, value) in &module.variables {
|
if let Some(ref v) = module.variables {
|
||||||
let hash_var = crate::calc_qualified_var_hash(path.iter().copied(), var_name);
|
for (var_name, value) in v {
|
||||||
variables.insert(hash_var, value.clone());
|
let hash_var = crate::calc_qualified_var_hash(path.iter().copied(), var_name);
|
||||||
|
variables.insert(hash_var, value.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Index type iterators
|
// Index type iterators
|
||||||
for (&type_id, func) in &module.type_iterators {
|
if let Some(ref t) = module.type_iterators {
|
||||||
type_iterators.insert(type_id, func.clone());
|
for (&type_id, func) in t {
|
||||||
contains_indexed_global_functions = true;
|
type_iterators.insert(type_id, func.clone());
|
||||||
|
contains_indexed_global_functions = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Index all Rust functions
|
// Index all Rust functions
|
||||||
@ -2097,9 +2184,22 @@ impl Module {
|
|||||||
&mut type_iterators,
|
&mut type_iterators,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.all_variables = variables;
|
self.all_variables = if variables.is_empty() {
|
||||||
self.all_functions = functions;
|
None
|
||||||
self.all_type_iterators = type_iterators;
|
} else {
|
||||||
|
Some(variables)
|
||||||
|
};
|
||||||
|
self.all_functions = if functions.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(functions)
|
||||||
|
};
|
||||||
|
self.all_type_iterators = if type_iterators.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(type_iterators)
|
||||||
|
};
|
||||||
|
|
||||||
self.indexed = true;
|
self.indexed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2110,22 +2210,18 @@ impl Module {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn contains_qualified_iter(&self, id: TypeId) -> bool {
|
pub fn contains_qualified_iter(&self, id: TypeId) -> bool {
|
||||||
if self.all_type_iterators.is_empty() {
|
self.all_type_iterators
|
||||||
false
|
.as_ref()
|
||||||
} else {
|
.map_or(false, |t| t.contains_key(&id))
|
||||||
self.all_type_iterators.contains_key(&id)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Does a type iterator exist in the module?
|
/// Does a type iterator exist in the module?
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn contains_iter(&self, id: TypeId) -> bool {
|
pub fn contains_iter(&self, id: TypeId) -> bool {
|
||||||
if self.type_iterators.is_empty() {
|
self.type_iterators
|
||||||
false
|
.as_ref()
|
||||||
} else {
|
.map_or(false, |t| t.contains_key(&id))
|
||||||
self.type_iterators.contains_key(&id)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a type iterator into the [`Module`].
|
/// Set a type iterator into the [`Module`].
|
||||||
@ -2149,10 +2245,14 @@ impl Module {
|
|||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
let func = Shared::new(func);
|
let func = Shared::new(func);
|
||||||
if self.indexed {
|
if self.indexed {
|
||||||
self.all_type_iterators.insert(type_id, func.clone());
|
self.all_type_iterators
|
||||||
|
.get_or_insert_with(|| Default::default())
|
||||||
|
.insert(type_id, func.clone());
|
||||||
self.contains_indexed_global_functions = true;
|
self.contains_indexed_global_functions = true;
|
||||||
}
|
}
|
||||||
self.type_iterators.insert(type_id, func);
|
self.type_iterators
|
||||||
|
.get_or_insert_with(|| Default::default())
|
||||||
|
.insert(type_id, func);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2209,22 +2309,20 @@ impl Module {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn get_qualified_iter(&self, id: TypeId) -> Option<&IteratorFn> {
|
pub(crate) fn get_qualified_iter(&self, id: TypeId) -> Option<&IteratorFn> {
|
||||||
if self.all_type_iterators.is_empty() {
|
self.all_type_iterators
|
||||||
None
|
.as_ref()
|
||||||
} else {
|
.and_then(|t| t.get(&id))
|
||||||
self.all_type_iterators.get(&id).map(|f| &**f)
|
.map(|f| &**f)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the specified type iterator.
|
/// Get the specified type iterator.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn get_iter(&self, id: TypeId) -> Option<&IteratorFn> {
|
pub(crate) fn get_iter(&self, id: TypeId) -> Option<&IteratorFn> {
|
||||||
if self.type_iterators.is_empty() {
|
self.type_iterators
|
||||||
None
|
.as_ref()
|
||||||
} else {
|
.and_then(|t| t.get(&id))
|
||||||
self.type_iterators.get(&id).map(|f| &**f)
|
.map(|f| &**f)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
use crate::eval::GlobalRuntimeState;
|
use crate::eval::GlobalRuntimeState;
|
||||||
use crate::func::{locked_read, locked_write};
|
use crate::func::{locked_read, locked_write};
|
||||||
use crate::{
|
use crate::{
|
||||||
Engine, Identifier, Module, ModuleResolver, Position, RhaiResultOf, Scope, Shared, ERR,
|
Engine, Identifier, Locked, Module, ModuleResolver, Position, RhaiResultOf, Scope, Shared, ERR,
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
@ -51,11 +51,7 @@ pub struct FileModuleResolver {
|
|||||||
extension: Identifier,
|
extension: Identifier,
|
||||||
cache_enabled: bool,
|
cache_enabled: bool,
|
||||||
scope: Scope<'static>,
|
scope: Scope<'static>,
|
||||||
|
cache: Locked<BTreeMap<PathBuf, Shared<Module>>>,
|
||||||
#[cfg(not(feature = "sync"))]
|
|
||||||
cache: std::cell::RefCell<BTreeMap<PathBuf, Shared<Module>>>,
|
|
||||||
#[cfg(feature = "sync")]
|
|
||||||
cache: std::sync::RwLock<BTreeMap<PathBuf, Shared<Module>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FileModuleResolver {
|
impl Default for FileModuleResolver {
|
||||||
|
@ -9,7 +9,8 @@ use std::prelude::v1::*;
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
use num_traits::Float;
|
use num_traits::Float;
|
||||||
|
|
||||||
#[inline]
|
#[cold]
|
||||||
|
#[inline(never)]
|
||||||
pub fn make_err(msg: impl Into<String>) -> RhaiError {
|
pub fn make_err(msg: impl Into<String>) -> RhaiError {
|
||||||
ERR::ErrorArithmetic(msg.into(), Position::NONE).into()
|
ERR::ErrorArithmetic(msg.into(), Position::NONE).into()
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::def_package;
|
use crate::def_package;
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::types::dynamic::Tag;
|
use crate::types::dynamic::Tag;
|
||||||
use crate::{Dynamic, RhaiResultOf, ERR, INT, MAX_USIZE_INT};
|
use crate::{Dynamic, RhaiResultOf, ERR, INT};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
@ -113,7 +113,7 @@ mod reflection_functions {
|
|||||||
}
|
}
|
||||||
#[rhai_fn(name = "get_fn_metadata_list")]
|
#[rhai_fn(name = "get_fn_metadata_list")]
|
||||||
pub fn get_fn_metadata2(ctx: NativeCallContext, name: &str, params: INT) -> crate::Array {
|
pub fn get_fn_metadata2(ctx: NativeCallContext, name: &str, params: INT) -> crate::Array {
|
||||||
if params < 0 || params > MAX_USIZE_INT {
|
if params < 0 || params > crate::MAX_USIZE_INT {
|
||||||
crate::Array::new()
|
crate::Array::new()
|
||||||
} else {
|
} else {
|
||||||
collect_fn_metadata(ctx, |_, _, n, p, _| p == (params as usize) && n == name)
|
collect_fn_metadata(ctx, |_, _, n, p, _| p == (params as usize) && n == name)
|
||||||
|
@ -32,7 +32,7 @@ use std::{
|
|||||||
|
|
||||||
pub type ParseResult<T> = Result<T, ParseError>;
|
pub type ParseResult<T> = Result<T, ParseError>;
|
||||||
|
|
||||||
type FnLib = StraightHashMap<u64, Shared<ScriptFnDef>>;
|
type FnLib = StraightHashMap<Shared<ScriptFnDef>>;
|
||||||
|
|
||||||
const KEYWORD_SEMICOLON: &str = Token::SemiColon.literal_syntax();
|
const KEYWORD_SEMICOLON: &str = Token::SemiColon.literal_syntax();
|
||||||
|
|
||||||
@ -2477,6 +2477,7 @@ impl Engine {
|
|||||||
state.stack.push(marker, ());
|
state.stack.push(marker, ());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut user_state = Dynamic::UNIT;
|
||||||
let parse_func = &*syntax.parse;
|
let parse_func = &*syntax.parse;
|
||||||
let mut required_token: ImmutableString = key.into();
|
let mut required_token: ImmutableString = key.into();
|
||||||
|
|
||||||
@ -2488,7 +2489,7 @@ impl Engine {
|
|||||||
settings.pos = *fwd_pos;
|
settings.pos = *fwd_pos;
|
||||||
let settings = settings.level_up();
|
let settings = settings.level_up();
|
||||||
|
|
||||||
required_token = match parse_func(&segments, &*fwd_token.syntax()) {
|
required_token = match parse_func(&segments, &*fwd_token.syntax(), &mut user_state) {
|
||||||
Ok(Some(seg))
|
Ok(Some(seg))
|
||||||
if seg.starts_with(CUSTOM_SYNTAX_MARKER_SYNTAX_VARIANT)
|
if seg.starts_with(CUSTOM_SYNTAX_MARKER_SYNTAX_VARIANT)
|
||||||
&& seg.len() > CUSTOM_SYNTAX_MARKER_SYNTAX_VARIANT.len() =>
|
&& seg.len() > CUSTOM_SYNTAX_MARKER_SYNTAX_VARIANT.len() =>
|
||||||
@ -2624,6 +2625,7 @@ impl Engine {
|
|||||||
crate::ast::CustomExpr {
|
crate::ast::CustomExpr {
|
||||||
inputs,
|
inputs,
|
||||||
tokens,
|
tokens,
|
||||||
|
state: user_state,
|
||||||
scope_may_be_changed: syntax.scope_may_be_changed,
|
scope_may_be_changed: syntax.scope_may_be_changed,
|
||||||
self_terminated,
|
self_terminated,
|
||||||
}
|
}
|
||||||
|
@ -1545,14 +1545,17 @@ fn get_next_token_inner(
|
|||||||
|
|
||||||
// digit ...
|
// digit ...
|
||||||
('0'..='9', ..) => {
|
('0'..='9', ..) => {
|
||||||
let mut result = smallvec::SmallVec::<[char; 16]>::new();
|
let mut result = SmartString::new_const();
|
||||||
let mut radix_base: Option<u32> = None;
|
let mut radix_base: Option<u32> = None;
|
||||||
let mut valid: fn(char) -> bool = is_numeric_digit;
|
let mut valid: fn(char) -> bool = is_numeric_digit;
|
||||||
result.push(c);
|
result.push(c);
|
||||||
|
|
||||||
while let Some(next_char) = stream.peek_next() {
|
while let Some(next_char) = stream.peek_next() {
|
||||||
match next_char {
|
match next_char {
|
||||||
ch if valid(ch) || ch == NUMBER_SEPARATOR => {
|
NUMBER_SEPARATOR => {
|
||||||
|
eat_next(stream, pos);
|
||||||
|
}
|
||||||
|
ch if valid(ch) => {
|
||||||
result.push(next_char);
|
result.push(next_char);
|
||||||
eat_next(stream, pos);
|
eat_next(stream, pos);
|
||||||
}
|
}
|
||||||
@ -1649,49 +1652,42 @@ fn get_next_token_inner(
|
|||||||
// Parse number
|
// Parse number
|
||||||
return Some((
|
return Some((
|
||||||
if let Some(radix) = radix_base {
|
if let Some(radix) = radix_base {
|
||||||
let out: String = result
|
let result = &result[2..];
|
||||||
.iter()
|
|
||||||
.skip(2)
|
|
||||||
.filter(|&&c| c != NUMBER_SEPARATOR)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
UNSIGNED_INT::from_str_radix(&out, radix)
|
UNSIGNED_INT::from_str_radix(&result, radix)
|
||||||
.map(|v| v as INT)
|
.map(|v| v as INT)
|
||||||
.map_or_else(
|
.map_or_else(
|
||||||
|_| {
|
|_| {
|
||||||
Token::LexError(
|
Token::LexError(
|
||||||
LERR::MalformedNumber(result.into_iter().collect()).into(),
|
LERR::MalformedNumber(result.to_string()).into(),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
Token::IntegerConstant,
|
Token::IntegerConstant,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let out: String =
|
let num = INT::from_str(&result).map(Token::IntegerConstant);
|
||||||
result.iter().filter(|&&c| c != NUMBER_SEPARATOR).collect();
|
|
||||||
let num = INT::from_str(&out).map(Token::IntegerConstant);
|
|
||||||
|
|
||||||
// If integer parsing is unnecessary, try float instead
|
// If integer parsing is unnecessary, try float instead
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
let num = num.or_else(|_| {
|
let num = num.or_else(|_| {
|
||||||
crate::ast::FloatWrapper::from_str(&out).map(Token::FloatConstant)
|
crate::ast::FloatWrapper::from_str(&result).map(Token::FloatConstant)
|
||||||
});
|
});
|
||||||
|
|
||||||
// Then try decimal
|
// Then try decimal
|
||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
let num = num.or_else(|_| {
|
let num = num.or_else(|_| {
|
||||||
rust_decimal::Decimal::from_str(&out).map(Token::DecimalConstant)
|
rust_decimal::Decimal::from_str(&result).map(Token::DecimalConstant)
|
||||||
});
|
});
|
||||||
|
|
||||||
// Then try decimal in scientific notation
|
// Then try decimal in scientific notation
|
||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
let num = num.or_else(|_| {
|
let num = num.or_else(|_| {
|
||||||
rust_decimal::Decimal::from_scientific(&out).map(Token::DecimalConstant)
|
rust_decimal::Decimal::from_scientific(&result)
|
||||||
|
.map(Token::DecimalConstant)
|
||||||
});
|
});
|
||||||
|
|
||||||
num.unwrap_or_else(|_| {
|
num.unwrap_or_else(|_| {
|
||||||
Token::LexError(
|
Token::LexError(LERR::MalformedNumber(result.to_string()).into())
|
||||||
LERR::MalformedNumber(result.into_iter().collect()).into(),
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
num_pos,
|
num_pos,
|
||||||
@ -2179,22 +2175,20 @@ fn get_identifier(
|
|||||||
start_pos: Position,
|
start_pos: Position,
|
||||||
first_char: char,
|
first_char: char,
|
||||||
) -> (Token, Position) {
|
) -> (Token, Position) {
|
||||||
let mut result = smallvec::SmallVec::<[char; 8]>::new();
|
let mut identifier = SmartString::new_const();
|
||||||
result.push(first_char);
|
identifier.push(first_char);
|
||||||
|
|
||||||
while let Some(next_char) = stream.peek_next() {
|
while let Some(next_char) = stream.peek_next() {
|
||||||
match next_char {
|
match next_char {
|
||||||
x if is_id_continue(x) => {
|
x if is_id_continue(x) => {
|
||||||
result.push(x);
|
identifier.push(x);
|
||||||
eat_next(stream, pos);
|
eat_next(stream, pos);
|
||||||
}
|
}
|
||||||
_ => break,
|
_ => break,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let is_valid_identifier = is_valid_identifier(result.iter().copied());
|
let is_valid_identifier = is_valid_identifier(identifier.chars());
|
||||||
|
|
||||||
let identifier: String = result.into_iter().collect();
|
|
||||||
|
|
||||||
if let Some(token) = Token::lookup_from_syntax(&identifier) {
|
if let Some(token) = Token::lookup_from_syntax(&identifier) {
|
||||||
return (token, start_pos);
|
return (token, start_pos);
|
||||||
@ -2202,12 +2196,12 @@ fn get_identifier(
|
|||||||
|
|
||||||
if !is_valid_identifier {
|
if !is_valid_identifier {
|
||||||
return (
|
return (
|
||||||
Token::LexError(LERR::MalformedIdentifier(identifier).into()),
|
Token::LexError(LERR::MalformedIdentifier(identifier.to_string()).into()),
|
||||||
start_pos,
|
start_pos,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
(Token::Identifier(identifier.into()), start_pos)
|
(Token::Identifier(identifier), start_pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is a keyword allowed as a function?
|
/// Is a keyword allowed as a function?
|
||||||
|
@ -10,7 +10,7 @@ use std::{
|
|||||||
/// Number of `usize` values required for 256 bits.
|
/// Number of `usize` values required for 256 bits.
|
||||||
const SIZE: usize = (256 / 8) / mem::size_of::<usize>();
|
const SIZE: usize = (256 / 8) / mem::size_of::<usize>();
|
||||||
|
|
||||||
/// A simple bloom filter implementation for `u64` hash values only - i.e., all 64 bits are assumed
|
/// A simple bloom filter implementation for `u64` hash values only - i.e. all 64 bits are assumed
|
||||||
/// to be relatively random.
|
/// to be relatively random.
|
||||||
///
|
///
|
||||||
/// For this reason, the implementation is simplistic - it just looks at the least significant byte
|
/// For this reason, the implementation is simplistic - it just looks at the least significant byte
|
||||||
@ -23,7 +23,8 @@ pub struct BloomFilterU64([usize; SIZE]);
|
|||||||
impl BloomFilterU64 {
|
impl BloomFilterU64 {
|
||||||
/// Get the bit position of a `u64` hash value.
|
/// Get the bit position of a `u64` hash value.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
const fn hash(value: u64) -> (usize, usize) {
|
#[must_use]
|
||||||
|
const fn calc_hash(value: u64) -> (usize, usize) {
|
||||||
let hash = (value & 0x00ff) as usize;
|
let hash = (value & 0x00ff) as usize;
|
||||||
(hash / 64, 0x01 << (hash % 64))
|
(hash / 64, 0x01 << (hash % 64))
|
||||||
}
|
}
|
||||||
@ -37,7 +38,7 @@ impl BloomFilterU64 {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.0.iter().all(|&v| v == 0)
|
self.0 == [0; SIZE]
|
||||||
}
|
}
|
||||||
/// Clear this [`BloomFilterU64`].
|
/// Clear this [`BloomFilterU64`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -46,18 +47,29 @@ impl BloomFilterU64 {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Mark a `u64` hash into this [`BloomFilterU64`].
|
/// Mark a `u64` hash into this [`BloomFilterU64`].
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
pub fn mark(&mut self, hash: u64) -> &mut Self {
|
pub fn mark(&mut self, hash: u64) -> &mut Self {
|
||||||
let (offset, mask) = Self::hash(hash);
|
let (offset, mask) = Self::calc_hash(hash);
|
||||||
self.0[offset] |= mask;
|
self.0[offset] |= mask;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Is a `u64` hash definitely absent from this [`BloomFilterU64`]?
|
/// Is a `u64` hash definitely absent from this [`BloomFilterU64`]?
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
pub const fn is_absent(&self, hash: u64) -> bool {
|
pub const fn is_absent(&self, hash: u64) -> bool {
|
||||||
let (offset, mask) = Self::hash(hash);
|
let (offset, mask) = Self::calc_hash(hash);
|
||||||
(self.0[offset] & mask) == 0
|
(self.0[offset] & mask) == 0
|
||||||
}
|
}
|
||||||
|
/// If a `u64` hash is absent from this [`BloomFilterU64`], return `true` and then mark it.
|
||||||
|
/// Otherwise return `false`.
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_absent_and_set(&mut self, hash: u64) -> bool {
|
||||||
|
let (offset, mask) = Self::calc_hash(hash);
|
||||||
|
let result = (self.0[offset] & mask) == 0;
|
||||||
|
self.0[offset] |= mask;
|
||||||
|
result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Add for &BloomFilterU64 {
|
impl Add for &BloomFilterU64 {
|
||||||
|
@ -29,11 +29,6 @@ impl CustomTypesCollection {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self(BTreeMap::new())
|
Self(BTreeMap::new())
|
||||||
}
|
}
|
||||||
/// Clear the [`CustomTypesCollection`].
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
self.0.clear();
|
|
||||||
}
|
|
||||||
/// Register a custom type.
|
/// Register a custom type.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn add(&mut self, type_name: impl Into<Identifier>, name: impl Into<Identifier>) {
|
pub fn add(&mut self, type_name: impl Into<Identifier>, name: impl Into<Identifier>) {
|
||||||
|
@ -1115,12 +1115,12 @@ impl Dynamic {
|
|||||||
/// Beware that you need to pass in an [`Array`][crate::Array] type for it to be recognized as
|
/// Beware that you need to pass in an [`Array`][crate::Array] type for it to be recognized as
|
||||||
/// an [`Array`][crate::Array]. A [`Vec<T>`][Vec] does not get automatically converted to an
|
/// an [`Array`][crate::Array]. A [`Vec<T>`][Vec] does not get automatically converted to an
|
||||||
/// [`Array`][crate::Array], but will be a custom type instead (stored as a trait object). Use
|
/// [`Array`][crate::Array], but will be a custom type instead (stored as a trait object). Use
|
||||||
/// `Into<Dynamic>` to convert a [`Vec<T>`][Vec] into a [`Dynamic`] as an
|
/// [`Dynamic::from_array`] to convert a [`Vec<T>`][Vec] into a [`Dynamic`] as an
|
||||||
/// [`Array`][crate::Array] value.
|
/// [`Array`][crate::Array] value.
|
||||||
///
|
///
|
||||||
/// Similarly, passing in a [`HashMap<String, T>`][std::collections::HashMap] or
|
/// Similarly, passing in a [`HashMap<String, T>`][std::collections::HashMap] or
|
||||||
/// [`BTreeMap<String, T>`][std::collections::BTreeMap] will not get a [`Map`][crate::Map] but a
|
/// [`BTreeMap<String, T>`][std::collections::BTreeMap] will not get a [`Map`][crate::Map] but a
|
||||||
/// custom type. Again, use `Into<Dynamic>` to get a [`Dynamic`] with a [`Map`][crate::Map]
|
/// custom type. Again, use [`Dynamic::from_map`] to get a [`Dynamic`] with a [`Map`][crate::Map]
|
||||||
/// value.
|
/// value.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
@ -249,6 +249,7 @@ impl fmt::Display for EvalAltResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: AsRef<str>> From<T> for EvalAltResult {
|
impl<T: AsRef<str>> From<T> for EvalAltResult {
|
||||||
|
#[cold]
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
fn from(err: T) -> Self {
|
fn from(err: T) -> Self {
|
||||||
Self::ErrorRuntime(err.as_ref().to_string().into(), Position::NONE)
|
Self::ErrorRuntime(err.as_ref().to_string().into(), Position::NONE)
|
||||||
@ -256,6 +257,7 @@ impl<T: AsRef<str>> From<T> for EvalAltResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: AsRef<str>> From<T> for Box<EvalAltResult> {
|
impl<T: AsRef<str>> From<T> for Box<EvalAltResult> {
|
||||||
|
#[cold]
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
fn from(err: T) -> Self {
|
fn from(err: T) -> Self {
|
||||||
EvalAltResult::ErrorRuntime(err.as_ref().to_string().into(), Position::NONE).into()
|
EvalAltResult::ErrorRuntime(err.as_ref().to_string().into(), Position::NONE).into()
|
||||||
@ -266,6 +268,8 @@ impl EvalAltResult {
|
|||||||
/// Is this a pseudo error? A pseudo error is one that does not occur naturally.
|
/// Is this a pseudo error? A pseudo error is one that does not occur naturally.
|
||||||
///
|
///
|
||||||
/// [`LoopBreak`][EvalAltResult::LoopBreak] and [`Return`][EvalAltResult::Return] are pseudo errors.
|
/// [`LoopBreak`][EvalAltResult::LoopBreak] and [`Return`][EvalAltResult::Return] are pseudo errors.
|
||||||
|
#[cold]
|
||||||
|
#[inline(never)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn is_pseudo_error(&self) -> bool {
|
pub const fn is_pseudo_error(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
@ -274,6 +278,8 @@ impl EvalAltResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Can this error be caught?
|
/// Can this error be caught?
|
||||||
|
#[cold]
|
||||||
|
#[inline(never)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn is_catchable(&self) -> bool {
|
pub const fn is_catchable(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
@ -319,6 +325,8 @@ impl EvalAltResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Is this error a system exception?
|
/// Is this error a system exception?
|
||||||
|
#[cold]
|
||||||
|
#[inline(never)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn is_system_exception(&self) -> bool {
|
pub const fn is_system_exception(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
@ -338,6 +346,8 @@ impl EvalAltResult {
|
|||||||
}
|
}
|
||||||
/// Get the [position][Position] of this error.
|
/// Get the [position][Position] of this error.
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
#[cold]
|
||||||
|
#[inline(never)]
|
||||||
pub(crate) fn dump_fields(&self, map: &mut crate::Map) {
|
pub(crate) fn dump_fields(&self, map: &mut crate::Map) {
|
||||||
map.insert(
|
map.insert(
|
||||||
"error".into(),
|
"error".into(),
|
||||||
@ -419,6 +429,8 @@ impl EvalAltResult {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
/// Unwrap this error and get the very base error.
|
/// Unwrap this error and get the very base error.
|
||||||
|
#[cold]
|
||||||
|
#[inline(never)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn unwrap_inner(&self) -> &Self {
|
pub fn unwrap_inner(&self) -> &Self {
|
||||||
match self {
|
match self {
|
||||||
@ -429,6 +441,8 @@ impl EvalAltResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get the [position][Position] of this error.
|
/// Get the [position][Position] of this error.
|
||||||
|
#[cold]
|
||||||
|
#[inline(never)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn position(&self) -> Position {
|
pub const fn position(&self) -> Position {
|
||||||
match self {
|
match self {
|
||||||
@ -470,18 +484,24 @@ impl EvalAltResult {
|
|||||||
/// Remove the [position][Position] information from this error.
|
/// Remove the [position][Position] information from this error.
|
||||||
///
|
///
|
||||||
/// The [position][Position] of this error is set to [`NONE`][Position::NONE] afterwards.
|
/// The [position][Position] of this error is set to [`NONE`][Position::NONE] afterwards.
|
||||||
|
#[cold]
|
||||||
|
#[inline(never)]
|
||||||
pub fn clear_position(&mut self) -> &mut Self {
|
pub fn clear_position(&mut self) -> &mut Self {
|
||||||
self.set_position(Position::NONE)
|
self.set_position(Position::NONE)
|
||||||
}
|
}
|
||||||
/// Remove the [position][Position] information from this error and return it.
|
/// Remove the [position][Position] information from this error and return it.
|
||||||
///
|
///
|
||||||
/// The [position][Position] of this error is set to [`NONE`][Position::NONE] afterwards.
|
/// The [position][Position] of this error is set to [`NONE`][Position::NONE] afterwards.
|
||||||
|
#[cold]
|
||||||
|
#[inline(never)]
|
||||||
pub fn take_position(&mut self) -> Position {
|
pub fn take_position(&mut self) -> Position {
|
||||||
let pos = self.position();
|
let pos = self.position();
|
||||||
self.set_position(Position::NONE);
|
self.set_position(Position::NONE);
|
||||||
pos
|
pos
|
||||||
}
|
}
|
||||||
/// Override the [position][Position] of this error.
|
/// Override the [position][Position] of this error.
|
||||||
|
#[cold]
|
||||||
|
#[inline(never)]
|
||||||
pub fn set_position(&mut self, new_position: Position) -> &mut Self {
|
pub fn set_position(&mut self, new_position: Position) -> &mut Self {
|
||||||
match self {
|
match self {
|
||||||
Self::ErrorSystem(..) => (),
|
Self::ErrorSystem(..) => (),
|
||||||
@ -522,6 +542,7 @@ impl EvalAltResult {
|
|||||||
}
|
}
|
||||||
/// Consume the current [`EvalAltResult`] and return a new one with the specified [`Position`]
|
/// Consume the current [`EvalAltResult`] and return a new one with the specified [`Position`]
|
||||||
/// if the current position is [`Position::NONE`].
|
/// if the current position is [`Position::NONE`].
|
||||||
|
#[cold]
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn fill_position(mut self: Box<Self>, new_position: Position) -> Box<Self> {
|
pub(crate) fn fill_position(mut self: Box<Self>, new_position: Position) -> Box<Self> {
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
use crate::func::{hashing::get_hasher, StraightHashMap};
|
use crate::func::{hashing::get_hasher, StraightHashMap};
|
||||||
use crate::ImmutableString;
|
use crate::ImmutableString;
|
||||||
|
#[cfg(feature = "no_std")]
|
||||||
|
use hashbrown::hash_map::Entry;
|
||||||
|
#[cfg(not(feature = "no_std"))]
|
||||||
|
use std::collections::hash_map::Entry;
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{
|
use std::{
|
||||||
@ -20,14 +23,13 @@ pub const MAX_STRING_LEN: usize = 24;
|
|||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// Normal identifiers, property getters and setters are interned separately.
|
/// Normal identifiers, property getters and setters are interned separately.
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct StringsInterner<'a> {
|
pub struct StringsInterner<'a> {
|
||||||
/// Maximum number of strings interned.
|
/// Maximum number of strings interned.
|
||||||
pub capacity: usize,
|
pub capacity: usize,
|
||||||
/// Maximum string length.
|
/// Maximum string length.
|
||||||
pub max_string_len: usize,
|
pub max_string_len: usize,
|
||||||
/// Normal strings.
|
/// Normal strings.
|
||||||
strings: StraightHashMap<u64, ImmutableString>,
|
strings: StraightHashMap<ImmutableString>,
|
||||||
/// Take care of the lifetime parameter.
|
/// Take care of the lifetime parameter.
|
||||||
dummy: PhantomData<&'a ()>,
|
dummy: PhantomData<&'a ()>,
|
||||||
}
|
}
|
||||||
@ -71,7 +73,7 @@ impl StringsInterner<'_> {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_with_mapper<S: AsRef<str>>(
|
pub fn get_with_mapper<S: AsRef<str>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
mapper: fn(S) -> ImmutableString,
|
mapper: impl Fn(S) -> ImmutableString,
|
||||||
text: S,
|
text: S,
|
||||||
) -> ImmutableString {
|
) -> ImmutableString {
|
||||||
let key = text.as_ref();
|
let key = text.as_ref();
|
||||||
@ -84,19 +86,20 @@ impl StringsInterner<'_> {
|
|||||||
key.hash(hasher);
|
key.hash(hasher);
|
||||||
let key = hasher.finish();
|
let key = hasher.finish();
|
||||||
|
|
||||||
if !self.strings.is_empty() && self.strings.contains_key(&key) {
|
let result = match self.strings.entry(key) {
|
||||||
return self.strings.get(&key).unwrap().clone();
|
Entry::Occupied(e) => return e.get().clone(),
|
||||||
}
|
Entry::Vacant(e) => {
|
||||||
|
let value = mapper(text);
|
||||||
|
|
||||||
let value = mapper(text);
|
if value.strong_count() > 1 {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
if value.strong_count() > 1 {
|
e.insert(value).clone()
|
||||||
return value;
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
self.strings.insert(key, value.clone());
|
// If the interner is over capacity, remove the longest entry that has the lowest count
|
||||||
|
|
||||||
// If the interner is over capacity, remove the longest entry
|
|
||||||
if self.strings.len() > self.capacity {
|
if self.strings.len() > self.capacity {
|
||||||
// Leave some buffer to grow when shrinking the cache.
|
// Leave some buffer to grow when shrinking the cache.
|
||||||
// We leave at least two entries, one for the empty string, and one for the string
|
// We leave at least two entries, one for the empty string, and one for the string
|
||||||
@ -108,19 +111,24 @@ impl StringsInterner<'_> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
while self.strings.len() > max {
|
while self.strings.len() > max {
|
||||||
let (_, n) = self.strings.iter().fold((0, 0), |(x, n), (&k, v)| {
|
let (_, _, n) =
|
||||||
if k != key && v.len() > x {
|
self.strings
|
||||||
(v.len(), k)
|
.iter()
|
||||||
} else {
|
.fold((0, usize::MAX, 0), |(x, c, n), (&k, v)| {
|
||||||
(x, n)
|
if k != key
|
||||||
}
|
&& (v.strong_count() < c || (v.strong_count() == c && v.len() > x))
|
||||||
});
|
{
|
||||||
|
(v.len(), v.strong_count(), k)
|
||||||
|
} else {
|
||||||
|
(x, c, n)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
self.strings.remove(&n);
|
self.strings.remove(&n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
value
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Number of strings interned.
|
/// Number of strings interned.
|
||||||
|
@ -263,6 +263,7 @@ impl fmt::Display for ParseErrorType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl From<LexError> for ParseErrorType {
|
impl From<LexError> for ParseErrorType {
|
||||||
|
#[cold]
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
fn from(err: LexError) -> Self {
|
fn from(err: LexError) -> Self {
|
||||||
match err {
|
match err {
|
||||||
@ -300,13 +301,15 @@ impl fmt::Display for ParseError {
|
|||||||
|
|
||||||
impl ParseError {
|
impl ParseError {
|
||||||
/// Get the [type][ParseErrorType] of this parse error.
|
/// Get the [type][ParseErrorType] of this parse error.
|
||||||
#[inline(always)]
|
#[cold]
|
||||||
|
#[inline(never)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn err_type(&self) -> &ParseErrorType {
|
pub const fn err_type(&self) -> &ParseErrorType {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
/// Get the [position][Position] of this parse error.
|
/// Get the [position][Position] of this parse error.
|
||||||
#[inline(always)]
|
#[cold]
|
||||||
|
#[inline(never)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn position(&self) -> Position {
|
pub const fn position(&self) -> Position {
|
||||||
self.1
|
self.1
|
||||||
@ -314,28 +317,32 @@ impl ParseError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl From<ParseErrorType> for RhaiError {
|
impl From<ParseErrorType> for RhaiError {
|
||||||
#[inline(always)]
|
#[cold]
|
||||||
|
#[inline(never)]
|
||||||
fn from(err: ParseErrorType) -> Self {
|
fn from(err: ParseErrorType) -> Self {
|
||||||
Box::new(err.into())
|
Box::new(err.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ParseErrorType> for ERR {
|
impl From<ParseErrorType> for ERR {
|
||||||
#[inline(always)]
|
#[cold]
|
||||||
|
#[inline(never)]
|
||||||
fn from(err: ParseErrorType) -> Self {
|
fn from(err: ParseErrorType) -> Self {
|
||||||
Self::ErrorParsing(err, Position::NONE)
|
Self::ErrorParsing(err, Position::NONE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ParseError> for RhaiError {
|
impl From<ParseError> for RhaiError {
|
||||||
#[inline(always)]
|
#[cold]
|
||||||
|
#[inline(never)]
|
||||||
fn from(err: ParseError) -> Self {
|
fn from(err: ParseError) -> Self {
|
||||||
Box::new(err.into())
|
Box::new(err.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ParseError> for ERR {
|
impl From<ParseError> for ERR {
|
||||||
#[inline(always)]
|
#[cold]
|
||||||
|
#[inline(never)]
|
||||||
fn from(err: ParseError) -> Self {
|
fn from(err: ParseError) -> Self {
|
||||||
Self::ErrorParsing(*err.0, err.1)
|
Self::ErrorParsing(*err.0, err.1)
|
||||||
}
|
}
|
||||||
|
@ -254,14 +254,17 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
|||||||
fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
|
fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
engine.register_custom_syntax_raw(
|
engine.register_custom_syntax_with_state_raw(
|
||||||
"hello",
|
"hello",
|
||||||
|stream, _| match stream.len() {
|
|stream, _, state| match stream.len() {
|
||||||
0 => unreachable!(),
|
0 => unreachable!(),
|
||||||
1 => Ok(Some("$ident$".into())),
|
1 => Ok(Some("$ident$".into())),
|
||||||
2 => match stream[1].as_str() {
|
2 => match stream[1].as_str() {
|
||||||
"world" => Ok(Some("$$hello".into())),
|
"world" => Ok(Some("$$hello".into())),
|
||||||
"kitty" => Ok(None),
|
"kitty" => {
|
||||||
|
*state = (42 as INT).into();
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
s => Err(LexError::ImproperSymbol(s.to_string(), String::new())
|
s => Err(LexError::ImproperSymbol(s.to_string(), String::new())
|
||||||
.into_err(Position::NONE)
|
.into_err(Position::NONE)
|
||||||
.into()),
|
.into()),
|
||||||
@ -269,7 +272,7 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
|
|||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
|context, inputs| {
|
|context, inputs, state| {
|
||||||
context.scope_mut().push("foo", 999 as INT);
|
context.scope_mut().push("foo", 999 as INT);
|
||||||
|
|
||||||
Ok(match inputs[0].get_string_value().unwrap() {
|
Ok(match inputs[0].get_string_value().unwrap() {
|
||||||
@ -277,14 +280,14 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
|
|||||||
if inputs
|
if inputs
|
||||||
.last()
|
.last()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get_literal_value::<ImmutableString>()
|
.get_string_value()
|
||||||
.map_or(false, |s| s == "$$hello") =>
|
.map_or(false, |s| s == "$$hello") =>
|
||||||
{
|
{
|
||||||
0 as INT
|
0 as INT
|
||||||
}
|
}
|
||||||
"world" => 123 as INT,
|
"world" => 123 as INT,
|
||||||
"kitty" if inputs.len() > 1 => 999 as INT,
|
"kitty" if inputs.len() > 1 => 999 as INT,
|
||||||
"kitty" => 42 as INT,
|
"kitty" => state.as_int().unwrap(),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
.into())
|
.into())
|
||||||
@ -313,9 +316,9 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
|
|||||||
fn test_custom_syntax_raw2() -> Result<(), Box<EvalAltResult>> {
|
fn test_custom_syntax_raw2() -> Result<(), Box<EvalAltResult>> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
engine.register_custom_syntax_raw(
|
engine.register_custom_syntax_with_state_raw(
|
||||||
"#",
|
"#",
|
||||||
|symbols, lookahead| match symbols.len() {
|
|symbols, lookahead, _| match symbols.len() {
|
||||||
1 if lookahead == "-" => Ok(Some("$symbol$".into())),
|
1 if lookahead == "-" => Ok(Some("$symbol$".into())),
|
||||||
1 => Ok(Some("$int$".into())),
|
1 => Ok(Some("$int$".into())),
|
||||||
2 if symbols[1] == "-" => Ok(Some("$int$".into())),
|
2 if symbols[1] == "-" => Ok(Some("$int$".into())),
|
||||||
@ -324,7 +327,7 @@ fn test_custom_syntax_raw2() -> Result<(), Box<EvalAltResult>> {
|
|||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
move |_, inputs| {
|
move |_, inputs, _| {
|
||||||
let id = if inputs.len() == 2 {
|
let id = if inputs.len() == 2 {
|
||||||
-inputs[1].get_literal_value::<INT>().unwrap()
|
-inputs[1].get_literal_value::<INT>().unwrap()
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user