Merge pull request #674 from schungx/master
Clippy and other refactors.
This commit is contained in:
commit
e78c3239f0
@ -4,6 +4,11 @@ Rhai Release Notes
|
||||
Version 1.12.0
|
||||
==============
|
||||
|
||||
Buf fixes
|
||||
---------
|
||||
|
||||
* Integer numbers that are too large to deserialize into `INT` now fall back to `Decimal` or `FLOAT` instead of silently truncating.
|
||||
|
||||
Net features
|
||||
------------
|
||||
|
||||
@ -20,6 +25,7 @@ Enhancements
|
||||
* The `TypeBuilder` type and `CustomType` trait are no longer marked as volatile.
|
||||
* `FuncArgs` is also implemented for arrays.
|
||||
* `Engine::set_XXX` API can now be chained.
|
||||
* `EvalContext::scope_mut` now returns `&mut Scope` instead of `&mut &mut Scope`.
|
||||
|
||||
|
||||
Version 1.11.0
|
||||
|
@ -1,3 +1,3 @@
|
||||
//! This file is automatically recreated during build time by `build.rs` from `build.template`.
|
||||
|
||||
pub(crate) const AHASH_SEED: Option<[u64; 4]> = {{AHASH_SEED}};
|
||||
pub const AHASH_SEED: Option<[u64; 4]> = {{AHASH_SEED}};
|
||||
|
@ -5,6 +5,8 @@ edition = "2018"
|
||||
resolver = "2"
|
||||
authors = ["jhwgh1968", "Stephen Chung"]
|
||||
description = "Procedural macros support package for Rhai, a scripting language and engine for Rust"
|
||||
keywords = ["rhai", "scripting", "scripting-engine", "scripting-language", "embedded", "plugin", "macros", "code-generation"]
|
||||
categories = ["no-std", "embedded", "wasm", "parser-implementations"]
|
||||
homepage = "https://rhai.rs/book/plugins/index.html"
|
||||
repository = "https://github.com/rhaiscript/rhai"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
@ -313,7 +313,7 @@ impl Parse for ExportedFn {
|
||||
}
|
||||
}
|
||||
|
||||
let skip_slots = if pass_context { 1 } else { 0 };
|
||||
let skip_slots = usize::from(pass_context);
|
||||
|
||||
// Determine whether function generates a special calling convention for a mutable receiver.
|
||||
let mut_receiver = match fn_all.sig.inputs.iter().nth(skip_slots) {
|
||||
@ -485,12 +485,12 @@ impl ExportedFn {
|
||||
}
|
||||
|
||||
pub fn arg_list(&self) -> impl Iterator<Item = &syn::FnArg> {
|
||||
let skip = if self.pass_context { 1 } else { 0 };
|
||||
let skip = usize::from(self.pass_context);
|
||||
self.signature.inputs.iter().skip(skip)
|
||||
}
|
||||
|
||||
pub fn arg_count(&self) -> usize {
|
||||
let skip = if self.pass_context { 1 } else { 0 };
|
||||
let skip = usize::from(self.pass_context);
|
||||
self.signature.inputs.len() - skip
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ pub fn main() {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
pub fn main() {
|
||||
use rhai::{CallFnOptions, Dynamic, Engine, ImmutableString, Map, Scope, AST};
|
||||
use rhai::{CallFnOptions, Dynamic, Engine, Map, Scope, AST};
|
||||
use std::io::{stdin, stdout, Write};
|
||||
|
||||
const SCRIPT_FILE: &str = "event_handler_js/script.rhai";
|
||||
|
@ -18,6 +18,7 @@ use std::{
|
||||
/// Options for calling a script-defined function via [`Engine::call_fn_with_options`].
|
||||
#[derive(Debug, Hash)]
|
||||
#[non_exhaustive]
|
||||
#[must_use]
|
||||
pub struct CallFnOptions<'t> {
|
||||
/// A value for binding to the `this` pointer (if any).
|
||||
pub this_ptr: Option<&'t mut Dynamic>,
|
||||
@ -120,7 +121,7 @@ impl Engine {
|
||||
name: impl AsRef<str>,
|
||||
args: impl FuncArgs,
|
||||
) -> RhaiResultOf<T> {
|
||||
self.call_fn_with_options(Default::default(), scope, ast, name, args)
|
||||
self.call_fn_with_options(CallFnOptions::default(), scope, ast, name, args)
|
||||
}
|
||||
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
||||
///
|
||||
@ -255,25 +256,28 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
crate::func::ensure_no_data_race(name, args, false).map(|_| Dynamic::UNIT)?;
|
||||
|
||||
if let Some(fn_def) = ast.shared_lib().get_script_fn(name, args.len()) {
|
||||
self.call_script_fn(
|
||||
global,
|
||||
caches,
|
||||
scope,
|
||||
this_ptr,
|
||||
fn_def,
|
||||
args,
|
||||
rewind_scope,
|
||||
Position::NONE,
|
||||
ast.shared_lib()
|
||||
.get_script_fn(name, args.len())
|
||||
.map_or_else(
|
||||
|| Err(ERR::ErrorFunctionNotFound(name.into(), Position::NONE).into()),
|
||||
|fn_def| {
|
||||
self.call_script_fn(
|
||||
global,
|
||||
caches,
|
||||
scope,
|
||||
this_ptr,
|
||||
fn_def,
|
||||
args,
|
||||
rewind_scope,
|
||||
Position::NONE,
|
||||
)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
Err(ERR::ErrorFunctionNotFound(name.into(), Position::NONE).into())
|
||||
}
|
||||
});
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
if self.debugger.is_some() {
|
||||
global.debugger.status = crate::eval::DebuggerStatus::Terminate;
|
||||
global.debugger_mut().status = crate::eval::DebuggerStatus::Terminate;
|
||||
let node = &crate::ast::Stmt::Noop(Position::NONE);
|
||||
self.run_debugger(global, caches, scope, this_ptr, node)?;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
//! Module that defines the public compilation API of [`Engine`].
|
||||
|
||||
use crate::func::native::locked_write;
|
||||
use crate::parser::{ParseResult, ParseState};
|
||||
use crate::{Engine, OptimizationLevel, Scope, AST};
|
||||
#[cfg(feature = "no_std")]
|
||||
@ -221,7 +222,8 @@ impl Engine {
|
||||
scripts.as_ref(),
|
||||
self.token_mapper.as_ref().map(<_>::as_ref),
|
||||
);
|
||||
let mut state = ParseState::new(self, scope, Default::default(), tokenizer_control);
|
||||
let interned_strings = &mut *locked_write(&self.interned_strings);
|
||||
let mut state = ParseState::new(scope, interned_strings, tokenizer_control);
|
||||
let mut _ast = self.parse(&mut stream.peekable(), &mut state, optimization_level)?;
|
||||
#[cfg(feature = "metadata")]
|
||||
_ast.set_doc(state.tokenizer_control.borrow().global_comments.join("\n"));
|
||||
@ -294,7 +296,8 @@ impl Engine {
|
||||
self.lex_raw(&scripts, self.token_mapper.as_ref().map(<_>::as_ref));
|
||||
|
||||
let mut peekable = stream.peekable();
|
||||
let mut state = ParseState::new(self, scope, Default::default(), tokenizer_control);
|
||||
let interned_strings = &mut *locked_write(&self.interned_strings);
|
||||
let mut state = ParseState::new(scope, interned_strings, tokenizer_control);
|
||||
self.parse_global_expr(&mut peekable, &mut state, |_| {}, self.optimization_level)
|
||||
}
|
||||
}
|
||||
|
@ -217,6 +217,7 @@ impl Engine {
|
||||
scope_may_be_changed: bool,
|
||||
func: impl Fn(&mut EvalContext, &[Expression]) -> RhaiResult + SendSync + 'static,
|
||||
) -> ParseResult<&mut Self> {
|
||||
#[allow(clippy::wildcard_imports)]
|
||||
use markers::*;
|
||||
|
||||
let mut segments = StaticVec::<ImmutableString>::new();
|
||||
@ -256,19 +257,29 @@ impl Engine {
|
||||
// Standard or reserved keyword/symbol not in first position
|
||||
_ if !segments.is_empty() && token.is_some() => {
|
||||
// Make it a custom keyword/symbol if it is disabled or reserved
|
||||
if ((!self.disabled_symbols.is_empty() && self.disabled_symbols.contains(s))
|
||||
if (self
|
||||
.disabled_symbols
|
||||
.as_ref()
|
||||
.map_or(false, |m| m.contains(s))
|
||||
|| token.map_or(false, |v| v.is_reserved()))
|
||||
&& (self.custom_keywords.is_empty()
|
||||
|| !self.custom_keywords.contains_key(s))
|
||||
&& !self
|
||||
.custom_keywords
|
||||
.as_ref()
|
||||
.map_or(false, |m| m.contains_key(s))
|
||||
{
|
||||
self.custom_keywords.insert(s.into(), None);
|
||||
self.custom_keywords
|
||||
.get_or_insert_with(Default::default)
|
||||
.insert(s.into(), None);
|
||||
}
|
||||
s.into()
|
||||
}
|
||||
// Standard keyword in first position but not disabled
|
||||
_ if segments.is_empty()
|
||||
&& token.as_ref().map_or(false, Token::is_standard_keyword)
|
||||
&& (self.disabled_symbols.is_empty() || !self.disabled_symbols.contains(s)) =>
|
||||
&& !self
|
||||
.disabled_symbols
|
||||
.as_ref()
|
||||
.map_or(false, |m| m.contains(s)) =>
|
||||
{
|
||||
return Err(LexError::ImproperSymbol(
|
||||
s.to_string(),
|
||||
@ -282,12 +293,19 @@ impl Engine {
|
||||
// Identifier in first position
|
||||
_ if segments.is_empty() && is_valid_identifier(s) => {
|
||||
// Make it a custom keyword/symbol if it is disabled or reserved
|
||||
if (!self.disabled_symbols.is_empty() && self.disabled_symbols.contains(s))
|
||||
|| token.map_or(false, |v| v.is_reserved())
|
||||
&& self.custom_keywords.is_empty()
|
||||
|| !self.custom_keywords.contains_key(s)
|
||||
if self
|
||||
.disabled_symbols
|
||||
.as_ref()
|
||||
.map_or(false, |m| m.contains(s))
|
||||
|| (token.map_or(false, |v| v.is_reserved())
|
||||
&& !self
|
||||
.custom_keywords
|
||||
.as_ref()
|
||||
.map_or(false, |m| m.contains_key(s)))
|
||||
{
|
||||
self.custom_keywords.insert(s.into(), None);
|
||||
self.custom_keywords
|
||||
.get_or_insert_with(Default::default)
|
||||
.insert(s.into(), None);
|
||||
}
|
||||
s.into()
|
||||
}
|
||||
@ -372,14 +390,16 @@ impl Engine {
|
||||
scope_may_be_changed: bool,
|
||||
func: impl Fn(&mut EvalContext, &[Expression], &Dynamic) -> RhaiResult + SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
self.custom_syntax.insert(
|
||||
key.into(),
|
||||
CustomSyntax {
|
||||
parse: Box::new(parse),
|
||||
func: Box::new(func),
|
||||
scope_may_be_changed,
|
||||
},
|
||||
);
|
||||
self.custom_syntax
|
||||
.get_or_insert_with(Default::default)
|
||||
.insert(
|
||||
key.into(),
|
||||
CustomSyntax {
|
||||
parse: Box::new(parse),
|
||||
func: Box::new(func),
|
||||
scope_may_be_changed,
|
||||
},
|
||||
);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
#![cfg(feature = "internals")]
|
||||
#![cfg(feature = "metadata")]
|
||||
|
||||
use crate::module::FuncInfo;
|
||||
use crate::module::{FuncInfo, ModuleFlags};
|
||||
use crate::tokenizer::{is_valid_function_name, Token};
|
||||
use crate::{Engine, FnAccess, FnPtr, Module, Scope, INT};
|
||||
|
||||
@ -77,7 +77,6 @@ pub struct DefinitionsConfig {
|
||||
|
||||
impl Default for DefinitionsConfig {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
write_headers: false,
|
||||
@ -105,13 +104,13 @@ impl Definitions<'_> {
|
||||
/// Headers are always present in content that is expected to be written to a file
|
||||
/// (i.e. `write_to*` and `*_file` methods).
|
||||
#[inline(always)]
|
||||
pub fn with_headers(mut self, headers: bool) -> Self {
|
||||
pub const fn with_headers(mut self, headers: bool) -> Self {
|
||||
self.config.write_headers = headers;
|
||||
self
|
||||
}
|
||||
/// Include standard packages when writing definition files.
|
||||
#[inline(always)]
|
||||
pub fn include_standard_packages(mut self, include_standard_packages: bool) -> Self {
|
||||
pub const fn include_standard_packages(mut self, include_standard_packages: bool) -> Self {
|
||||
self.config.include_standard_packages = include_standard_packages;
|
||||
self
|
||||
}
|
||||
@ -129,7 +128,6 @@ impl Definitions<'_> {
|
||||
}
|
||||
/// Get the configuration.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) const fn config(&self) -> &DefinitionsConfig {
|
||||
&self.config
|
||||
}
|
||||
@ -177,19 +175,19 @@ impl Definitions<'_> {
|
||||
let mut def_file = String::from("module static;\n\n");
|
||||
|
||||
if config.include_standard_packages {
|
||||
def_file += &self.builtin_functions_operators_impl(&config);
|
||||
def_file += &Self::builtin_functions_operators_impl(config);
|
||||
def_file += "\n";
|
||||
def_file += &self.builtin_functions_impl(&config);
|
||||
def_file += &Self::builtin_functions_impl(config);
|
||||
def_file += "\n";
|
||||
}
|
||||
def_file += &self.static_module_impl(&config);
|
||||
def_file += &self.static_module_impl(config);
|
||||
def_file += "\n";
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
{
|
||||
use std::fmt::Write;
|
||||
|
||||
for (module_name, module_def) in self.modules_impl(&config) {
|
||||
for (module_name, module_def) in self.modules_impl(config) {
|
||||
write!(
|
||||
&mut def_file,
|
||||
"\nmodule {module_name} {{\n{module_def}\n}}\n"
|
||||
@ -199,7 +197,7 @@ impl Definitions<'_> {
|
||||
def_file += "\n";
|
||||
}
|
||||
|
||||
def_file += &self.scope_items_impl(&config);
|
||||
def_file += &self.scope_items_impl(config);
|
||||
|
||||
def_file += "\n";
|
||||
|
||||
@ -220,11 +218,11 @@ impl Definitions<'_> {
|
||||
vec![
|
||||
(
|
||||
"__builtin__.d.rhai".to_string(),
|
||||
self.builtin_functions_impl(&config),
|
||||
Self::builtin_functions_impl(config),
|
||||
),
|
||||
(
|
||||
"__builtin-operators__.d.rhai".to_string(),
|
||||
self.builtin_functions_operators_impl(&config),
|
||||
Self::builtin_functions_operators_impl(config),
|
||||
),
|
||||
]
|
||||
} else {
|
||||
@ -233,18 +231,18 @@ impl Definitions<'_> {
|
||||
.into_iter()
|
||||
.chain(std::iter::once((
|
||||
"__static__.d.rhai".to_string(),
|
||||
self.static_module_impl(&config),
|
||||
self.static_module_impl(config),
|
||||
)))
|
||||
.chain(self.scope.iter().map(move |_| {
|
||||
(
|
||||
"__scope__.d.rhai".to_string(),
|
||||
self.scope_items_impl(&config),
|
||||
self.scope_items_impl(config),
|
||||
)
|
||||
}))
|
||||
.chain(
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
{
|
||||
self.modules_impl(&config)
|
||||
self.modules_impl(config)
|
||||
.map(|(name, def)| (format!("{name}.d.rhai"), def))
|
||||
},
|
||||
#[cfg(feature = "no_module")]
|
||||
@ -258,12 +256,12 @@ impl Definitions<'_> {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn builtin_functions(&self) -> String {
|
||||
self.builtin_functions_impl(&self.config)
|
||||
Self::builtin_functions_impl(self.config)
|
||||
}
|
||||
|
||||
/// Return definitions for all builtin functions.
|
||||
#[must_use]
|
||||
fn builtin_functions_impl(&self, config: &DefinitionsConfig) -> String {
|
||||
fn builtin_functions_impl(config: DefinitionsConfig) -> String {
|
||||
let def = include_str!("builtin-functions.d.rhai");
|
||||
|
||||
if config.write_headers {
|
||||
@ -277,12 +275,12 @@ impl Definitions<'_> {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn builtin_functions_operators(&self) -> String {
|
||||
self.builtin_functions_operators_impl(&self.config)
|
||||
Self::builtin_functions_operators_impl(self.config)
|
||||
}
|
||||
|
||||
/// Return definitions for all builtin operators.
|
||||
#[must_use]
|
||||
fn builtin_functions_operators_impl(&self, config: &DefinitionsConfig) -> String {
|
||||
fn builtin_functions_operators_impl(config: DefinitionsConfig) -> String {
|
||||
let def = include_str!("builtin-operators.d.rhai");
|
||||
|
||||
if config.write_headers {
|
||||
@ -296,22 +294,28 @@ impl Definitions<'_> {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn static_module(&self) -> String {
|
||||
self.static_module_impl(&self.config)
|
||||
self.static_module_impl(self.config)
|
||||
}
|
||||
|
||||
/// Return definitions for all globally available functions and constants.
|
||||
#[must_use]
|
||||
fn static_module_impl(&self, config: &DefinitionsConfig) -> String {
|
||||
fn static_module_impl(&self, config: DefinitionsConfig) -> String {
|
||||
let mut s = if config.write_headers {
|
||||
String::from("module static;\n\n")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
let exclude_flags = if self.config.include_standard_packages {
|
||||
ModuleFlags::empty()
|
||||
} else {
|
||||
ModuleFlags::STANDARD_LIB
|
||||
};
|
||||
|
||||
self.engine
|
||||
.global_modules
|
||||
.iter()
|
||||
.filter(|m| self.config.include_standard_packages || !m.standard)
|
||||
.filter(|m| !m.flags.contains(exclude_flags))
|
||||
.enumerate()
|
||||
.for_each(|(i, m)| {
|
||||
if i > 0 {
|
||||
@ -327,12 +331,12 @@ impl Definitions<'_> {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn scope_items(&self) -> String {
|
||||
self.scope_items_impl(&self.config)
|
||||
self.scope_items_impl(self.config)
|
||||
}
|
||||
|
||||
/// Return definitions for all items inside the [`Scope`], if any.
|
||||
#[must_use]
|
||||
fn scope_items_impl(&self, config: &DefinitionsConfig) -> String {
|
||||
fn scope_items_impl(&self, config: DefinitionsConfig) -> String {
|
||||
let mut s = if config.write_headers {
|
||||
String::from("module static;\n\n")
|
||||
} else {
|
||||
@ -352,19 +356,20 @@ impl Definitions<'_> {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[inline(always)]
|
||||
pub fn modules(&self) -> impl Iterator<Item = (String, String)> + '_ {
|
||||
self.modules_impl(&self.config)
|
||||
self.modules_impl(self.config)
|
||||
}
|
||||
|
||||
/// Return a (module name, definitions) pair for each registered static [module][Module].
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
fn modules_impl(
|
||||
&self,
|
||||
config: &DefinitionsConfig,
|
||||
config: DefinitionsConfig,
|
||||
) -> impl Iterator<Item = (String, String)> + '_ {
|
||||
let mut m = self
|
||||
.engine
|
||||
.global_sub_modules
|
||||
.iter()
|
||||
.flat_map(|m| m.iter())
|
||||
.map(move |(name, module)| {
|
||||
(
|
||||
name.to_string(),
|
||||
@ -425,10 +430,11 @@ impl Module {
|
||||
}
|
||||
|
||||
let mut func_infos = self.iter_fn().collect::<Vec<_>>();
|
||||
func_infos.sort_by(|a, b| match a.name.cmp(&b.name) {
|
||||
Ordering::Equal => match a.num_params.cmp(&b.num_params) {
|
||||
Ordering::Equal => (a.params_info.join("") + a.return_type.as_str())
|
||||
.cmp(&(b.params_info.join("") + b.return_type.as_str())),
|
||||
func_infos.sort_by(|a, b| match a.metadata.name.cmp(&b.metadata.name) {
|
||||
Ordering::Equal => match a.metadata.num_params.cmp(&b.metadata.num_params) {
|
||||
Ordering::Equal => (a.metadata.params_info.join("")
|
||||
+ a.metadata.return_type.as_str())
|
||||
.cmp(&(b.metadata.params_info.join("") + b.metadata.return_type.as_str())),
|
||||
o => o,
|
||||
},
|
||||
o => o,
|
||||
@ -440,13 +446,17 @@ impl Module {
|
||||
}
|
||||
first = false;
|
||||
|
||||
if f.access != FnAccess::Private {
|
||||
#[cfg(not(feature = "no_custom_syntax"))]
|
||||
let operator = def.engine.custom_keywords.contains_key(f.name.as_str())
|
||||
|| (!f.name.contains('$') && !is_valid_function_name(f.name.as_str()));
|
||||
if f.metadata.access != FnAccess::Private {
|
||||
let operator =
|
||||
!f.metadata.name.contains('$') && !is_valid_function_name(&f.metadata.name);
|
||||
|
||||
#[cfg(feature = "no_custom_syntax")]
|
||||
let operator = !f.name.contains('$') && !is_valid_function_name(&f.name);
|
||||
#[cfg(not(feature = "no_custom_syntax"))]
|
||||
let operator = operator
|
||||
|| def
|
||||
.engine
|
||||
.custom_keywords
|
||||
.as_ref()
|
||||
.map_or(false, |m| m.contains_key(f.metadata.name.as_str()));
|
||||
|
||||
f.write_definition(writer, def, operator)?;
|
||||
}
|
||||
@ -464,7 +474,7 @@ impl FuncInfo {
|
||||
def: &Definitions,
|
||||
operator: bool,
|
||||
) -> fmt::Result {
|
||||
for comment in &*self.comments {
|
||||
for comment in &*self.metadata.comments {
|
||||
writeln!(writer, "{comment}")?;
|
||||
}
|
||||
|
||||
@ -474,29 +484,33 @@ impl FuncInfo {
|
||||
writer.write_str("fn ")?;
|
||||
}
|
||||
|
||||
if let Some(name) = self.name.strip_prefix("get$") {
|
||||
if let Some(name) = self.metadata.name.strip_prefix("get$") {
|
||||
write!(writer, "get {name}(")?;
|
||||
} else if let Some(name) = self.name.strip_prefix("set$") {
|
||||
} else if let Some(name) = self.metadata.name.strip_prefix("set$") {
|
||||
write!(writer, "set {name}(")?;
|
||||
} else {
|
||||
write!(writer, "{}(", self.name)?;
|
||||
write!(writer, "{}(", self.metadata.name)?;
|
||||
}
|
||||
|
||||
let mut first = true;
|
||||
for i in 0..self.num_params {
|
||||
for i in 0..self.metadata.num_params {
|
||||
if !first {
|
||||
writer.write_str(", ")?;
|
||||
}
|
||||
first = false;
|
||||
|
||||
let (param_name, param_type) = self.params_info.get(i).map_or(("_", "?".into()), |s| {
|
||||
let mut s = s.splitn(2, ':');
|
||||
(
|
||||
s.next().unwrap_or("_").split(' ').last().unwrap(),
|
||||
s.next()
|
||||
.map_or(Cow::Borrowed("?"), |ty| def_type_name(ty, def.engine)),
|
||||
)
|
||||
});
|
||||
let (param_name, param_type) =
|
||||
self.metadata
|
||||
.params_info
|
||||
.get(i)
|
||||
.map_or(("_", "?".into()), |s| {
|
||||
let mut s = s.splitn(2, ':');
|
||||
(
|
||||
s.next().unwrap_or("_").split(' ').last().unwrap(),
|
||||
s.next()
|
||||
.map_or(Cow::Borrowed("?"), |ty| def_type_name(ty, def.engine)),
|
||||
)
|
||||
});
|
||||
|
||||
if operator {
|
||||
write!(writer, "{param_type}")?;
|
||||
@ -508,7 +522,7 @@ impl FuncInfo {
|
||||
write!(
|
||||
writer,
|
||||
") -> {};",
|
||||
def_type_name(&self.return_type, def.engine)
|
||||
def_type_name(&self.metadata.return_type, def.engine)
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
|
@ -3,8 +3,8 @@
|
||||
use crate::func::RegisterNativeFunction;
|
||||
use crate::types::dynamic::Variant;
|
||||
use crate::{
|
||||
Dynamic, Engine, EvalAltResult, FnPtr, Identifier, ImmutableString, NativeCallContext,
|
||||
Position, RhaiResult, RhaiResultOf, Scope, SharedModule, AST,
|
||||
Dynamic, Engine, EvalAltResult, FnPtr, Identifier, ImmutableString, Module, NativeCallContext,
|
||||
Position, RhaiResult, RhaiResultOf, Scope, SharedModule, TypeBuilder, AST,
|
||||
};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@ -534,7 +534,7 @@ impl Position {
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<'a, T: Variant + Clone> crate::TypeBuilder<'a, T> {
|
||||
impl<'a, T: Variant + Clone> TypeBuilder<'a, T> {
|
||||
/// Register a custom fallible function.
|
||||
///
|
||||
/// # Deprecated
|
||||
@ -642,3 +642,19 @@ impl<'a, T: Variant + Clone> crate::TypeBuilder<'a, T> {
|
||||
self.with_indexer_set(set_fn)
|
||||
}
|
||||
}
|
||||
|
||||
impl Module {
|
||||
/// Create a new [`Module`] with a pre-sized capacity for functions.
|
||||
///
|
||||
/// # Deprecated
|
||||
///
|
||||
/// This method is deprecated. Use `new` instead.
|
||||
///
|
||||
/// This method will be removed in the next major version.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
#[deprecated(since = "1.12.0", note = "use `new` instead")]
|
||||
pub fn with_capacity(_capacity: usize) -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! Module that defines the public evaluation API of [`Engine`].
|
||||
|
||||
use crate::eval::{Caches, GlobalRuntimeState};
|
||||
use crate::func::native::locked_write;
|
||||
use crate::parser::ParseState;
|
||||
use crate::types::dynamic::Variant;
|
||||
use crate::{
|
||||
@ -69,7 +70,7 @@ impl Engine {
|
||||
) -> RhaiResultOf<T> {
|
||||
let ast = self.compile_with_scope_and_optimization_level(
|
||||
scope,
|
||||
&[script],
|
||||
[script],
|
||||
self.optimization_level,
|
||||
)?;
|
||||
self.eval_ast_with_scope(scope, &ast)
|
||||
@ -117,20 +118,25 @@ impl Engine {
|
||||
script: &str,
|
||||
) -> RhaiResultOf<T> {
|
||||
let scripts = [script];
|
||||
let (stream, tokenizer_control) =
|
||||
self.lex_raw(&scripts, self.token_mapper.as_ref().map(<_>::as_ref));
|
||||
let mut state = ParseState::new(self, scope, Default::default(), tokenizer_control);
|
||||
let ast = {
|
||||
let interned_strings = &mut *locked_write(&self.interned_strings);
|
||||
|
||||
// No need to optimize a lone expression
|
||||
let ast = self.parse_global_expr(
|
||||
&mut stream.peekable(),
|
||||
&mut state,
|
||||
|_| {},
|
||||
#[cfg(not(feature = "no_optimize"))]
|
||||
OptimizationLevel::None,
|
||||
#[cfg(feature = "no_optimize")]
|
||||
OptimizationLevel::default(),
|
||||
)?;
|
||||
let (stream, tokenizer_control) =
|
||||
self.lex_raw(&scripts, self.token_mapper.as_ref().map(<_>::as_ref));
|
||||
|
||||
let mut state = ParseState::new(scope, interned_strings, tokenizer_control);
|
||||
|
||||
// No need to optimize a lone expression
|
||||
self.parse_global_expr(
|
||||
&mut stream.peekable(),
|
||||
&mut state,
|
||||
|_| {},
|
||||
#[cfg(not(feature = "no_optimize"))]
|
||||
OptimizationLevel::None,
|
||||
#[cfg(feature = "no_optimize")]
|
||||
OptimizationLevel::default(),
|
||||
)?
|
||||
};
|
||||
|
||||
self.eval_ast_with_scope(scope, &ast)
|
||||
}
|
||||
@ -238,7 +244,7 @@ impl Engine {
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
if self.debugger.is_some() {
|
||||
global.debugger.status = crate::eval::DebuggerStatus::Terminate;
|
||||
global.debugger_mut().status = crate::eval::DebuggerStatus::Terminate;
|
||||
let mut this = Dynamic::NULL;
|
||||
let node = &crate::ast::Stmt::Noop(Position::NONE);
|
||||
|
||||
|
@ -360,7 +360,7 @@ impl Engine {
|
||||
+ SendSync
|
||||
+ 'static,
|
||||
) -> &mut Self {
|
||||
self.debugger = Some((Box::new(init), Box::new(callback)));
|
||||
self.debugger = Some(Box::new((Box::new(init), Box::new(callback))));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
//! Module that defines JSON manipulation functions for [`Engine`].
|
||||
#![cfg(not(feature = "no_object"))]
|
||||
|
||||
use crate::parser::ParseState;
|
||||
use crate::func::native::locked_write;
|
||||
use crate::parser::{ParseSettingFlags, ParseState};
|
||||
use crate::tokenizer::Token;
|
||||
use crate::{Engine, LexError, Map, OptimizationLevel, RhaiResultOf, Scope};
|
||||
#[cfg(feature = "no_std")]
|
||||
@ -116,18 +117,21 @@ impl Engine {
|
||||
},
|
||||
);
|
||||
|
||||
let scope = Scope::new();
|
||||
let mut state = ParseState::new(self, &scope, Default::default(), tokenizer_control);
|
||||
let ast = {
|
||||
let scope = Scope::new();
|
||||
let interned_strings = &mut *locked_write(&self.interned_strings);
|
||||
let mut state = ParseState::new(&scope, interned_strings, tokenizer_control);
|
||||
|
||||
let ast = self.parse_global_expr(
|
||||
&mut stream.peekable(),
|
||||
&mut state,
|
||||
|s| s.allow_unquoted_map_properties = false,
|
||||
#[cfg(not(feature = "no_optimize"))]
|
||||
OptimizationLevel::None,
|
||||
#[cfg(feature = "no_optimize")]
|
||||
OptimizationLevel::default(),
|
||||
)?;
|
||||
self.parse_global_expr(
|
||||
&mut stream.peekable(),
|
||||
&mut state,
|
||||
|s| s.flags |= ParseSettingFlags::DISALLOW_UNQUOTED_MAP_PROPERTIES,
|
||||
#[cfg(not(feature = "no_optimize"))]
|
||||
OptimizationLevel::None,
|
||||
#[cfg(feature = "no_optimize")]
|
||||
OptimizationLevel::default(),
|
||||
)?
|
||||
};
|
||||
|
||||
self.eval_ast(&ast)
|
||||
}
|
||||
@ -165,7 +169,7 @@ pub fn format_map_as_json(map: &Map) -> String {
|
||||
result.push(':');
|
||||
|
||||
if let Some(val) = value.read_lock::<Map>() {
|
||||
result.push_str(&format_map_as_json(&*val));
|
||||
result.push_str(&format_map_as_json(&val));
|
||||
} else if value.is_unit() {
|
||||
result.push_str("null");
|
||||
} else {
|
||||
|
@ -107,7 +107,9 @@ impl Engine {
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn disable_symbol(&mut self, symbol: impl Into<Identifier>) -> &mut Self {
|
||||
self.disabled_symbols.insert(symbol.into());
|
||||
self.disabled_symbols
|
||||
.get_or_insert_with(Default::default)
|
||||
.insert(symbol.into());
|
||||
self
|
||||
}
|
||||
|
||||
@ -163,24 +165,30 @@ impl Engine {
|
||||
// Active standard keywords cannot be made custom
|
||||
// Disabled keywords are OK
|
||||
Some(token) if token.is_standard_keyword() => {
|
||||
if self.disabled_symbols.is_empty()
|
||||
|| !self.disabled_symbols.contains(&*token.syntax())
|
||||
if !self
|
||||
.disabled_symbols
|
||||
.as_ref()
|
||||
.map_or(false, |m| m.contains(token.literal_syntax()))
|
||||
{
|
||||
return Err(format!("'{keyword}' is a reserved keyword"));
|
||||
}
|
||||
}
|
||||
// Active standard symbols cannot be made custom
|
||||
Some(token) if token.is_standard_symbol() => {
|
||||
if self.disabled_symbols.is_empty()
|
||||
|| !self.disabled_symbols.contains(&*token.syntax())
|
||||
if !self
|
||||
.disabled_symbols
|
||||
.as_ref()
|
||||
.map_or(false, |m| m.contains(token.literal_syntax()))
|
||||
{
|
||||
return Err(format!("'{keyword}' is a reserved operator"));
|
||||
}
|
||||
}
|
||||
// Active standard symbols cannot be made custom
|
||||
Some(token)
|
||||
if self.disabled_symbols.is_empty()
|
||||
|| !self.disabled_symbols.contains(&*token.syntax()) =>
|
||||
if !self
|
||||
.disabled_symbols
|
||||
.as_ref()
|
||||
.map_or(false, |m| m.contains(token.literal_syntax())) =>
|
||||
{
|
||||
return Err(format!("'{keyword}' is a reserved symbol"))
|
||||
}
|
||||
@ -190,6 +198,7 @@ impl Engine {
|
||||
|
||||
// Add to custom keywords
|
||||
self.custom_keywords
|
||||
.get_or_insert_with(Default::default)
|
||||
.insert(keyword.into(), Some(precedence));
|
||||
|
||||
Ok(self)
|
||||
@ -198,7 +207,7 @@ impl Engine {
|
||||
/// Get the default value of the custom state for each evaluation run.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn default_tag(&self) -> &Dynamic {
|
||||
pub const fn default_tag(&self) -> &Dynamic {
|
||||
&self.def_tag
|
||||
}
|
||||
/// Get a mutable reference to the default value of the custom state for each evaluation run.
|
||||
|
@ -683,13 +683,14 @@ impl Engine {
|
||||
name: impl AsRef<str>,
|
||||
module: SharedModule,
|
||||
) -> &mut Self {
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
fn register_static_module_raw(
|
||||
root: &mut std::collections::BTreeMap<Identifier, SharedModule>,
|
||||
root: &mut BTreeMap<Identifier, SharedModule>,
|
||||
name: &str,
|
||||
module: SharedModule,
|
||||
) {
|
||||
let separator = crate::tokenizer::Token::DoubleColon.syntax();
|
||||
let separator = separator.as_ref();
|
||||
let separator = crate::tokenizer::Token::DoubleColon.literal_syntax();
|
||||
|
||||
if name.contains(separator) {
|
||||
let mut iter = name.splitn(2, separator);
|
||||
@ -718,7 +719,11 @@ impl Engine {
|
||||
}
|
||||
}
|
||||
|
||||
register_static_module_raw(&mut self.global_sub_modules, name.as_ref(), module);
|
||||
register_static_module_raw(
|
||||
self.global_sub_modules.get_or_insert_with(Default::default),
|
||||
name.as_ref(),
|
||||
module,
|
||||
);
|
||||
self
|
||||
}
|
||||
/// _(metadata)_ Generate a list of all registered functions.
|
||||
@ -738,15 +743,21 @@ impl Engine {
|
||||
signatures.extend(self.global_namespace().gen_fn_signatures());
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
for (name, m) in &self.global_sub_modules {
|
||||
for (name, m) in self.global_sub_modules.iter().flat_map(|m| m.iter()) {
|
||||
signatures.extend(m.gen_fn_signatures().map(|f| format!("{name}::{f}")));
|
||||
}
|
||||
|
||||
let exclude_flags = if include_packages {
|
||||
crate::module::ModuleFlags::INTERNAL
|
||||
} else {
|
||||
crate::module::ModuleFlags::INTERNAL | crate::module::ModuleFlags::STANDARD_LIB
|
||||
};
|
||||
|
||||
signatures.extend(
|
||||
self.global_modules
|
||||
.iter()
|
||||
.skip(1)
|
||||
.filter(|m| !m.internal && (include_packages || !m.standard))
|
||||
.filter(|m| !m.flags.contains(exclude_flags))
|
||||
.flat_map(|m| m.gen_fn_signatures()),
|
||||
);
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! Module that defines the public evaluation API of [`Engine`].
|
||||
|
||||
use crate::eval::{Caches, GlobalRuntimeState};
|
||||
use crate::func::native::locked_write;
|
||||
use crate::parser::ParseState;
|
||||
use crate::{Engine, RhaiResultOf, Scope, AST};
|
||||
#[cfg(feature = "no_std")]
|
||||
@ -56,10 +57,16 @@ impl Engine {
|
||||
#[inline]
|
||||
pub fn run_with_scope(&self, scope: &mut Scope, script: &str) -> RhaiResultOf<()> {
|
||||
let scripts = [script];
|
||||
let (stream, tokenizer_control) =
|
||||
self.lex_raw(&scripts, self.token_mapper.as_ref().map(<_>::as_ref));
|
||||
let mut state = ParseState::new(self, scope, Default::default(), tokenizer_control);
|
||||
let ast = self.parse(&mut stream.peekable(), &mut state, self.optimization_level)?;
|
||||
let ast = {
|
||||
let interned_strings = &mut *locked_write(&self.interned_strings);
|
||||
|
||||
let (stream, tokenizer_control) =
|
||||
self.lex_raw(&scripts, self.token_mapper.as_ref().map(<_>::as_ref));
|
||||
|
||||
let mut state = ParseState::new(scope, interned_strings, tokenizer_control);
|
||||
|
||||
self.parse(&mut stream.peekable(), &mut state, self.optimization_level)?
|
||||
};
|
||||
self.run_ast_with_scope(scope, &ast)
|
||||
}
|
||||
/// Evaluate an [`AST`].
|
||||
@ -130,7 +137,7 @@ impl Engine {
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
if self.debugger.is_some() {
|
||||
global.debugger.status = crate::eval::DebuggerStatus::Terminate;
|
||||
global.debugger_mut().status = crate::eval::DebuggerStatus::Terminate;
|
||||
let mut this = crate::Dynamic::NULL;
|
||||
let node = &crate::ast::Stmt::Noop(crate::Position::NONE);
|
||||
self.run_debugger(global, caches, scope, &mut this, node)?;
|
||||
|
@ -139,8 +139,8 @@ pub fn format_type(typ: &str, is_return_type: bool) -> std::borrow::Cow<str> {
|
||||
} else {
|
||||
format!("&mut {r}").into()
|
||||
};
|
||||
} else if typ.contains(" ") {
|
||||
let typ = typ.replace(" ", "");
|
||||
} else if typ.contains(' ') {
|
||||
let typ = typ.replace(' ', "");
|
||||
let r = format_type(&typ, is_return_type);
|
||||
return r.into_owned().into();
|
||||
}
|
||||
@ -205,6 +205,7 @@ impl Engine {
|
||||
return self
|
||||
.global_sub_modules
|
||||
.iter()
|
||||
.flat_map(|m| m.iter())
|
||||
.find_map(|(_, m)| m.get_custom_type(name));
|
||||
#[cfg(feature = "no_module")]
|
||||
return None;
|
||||
@ -238,6 +239,7 @@ impl Engine {
|
||||
return self
|
||||
.global_sub_modules
|
||||
.iter()
|
||||
.flat_map(|m| m.iter())
|
||||
.find_map(|(_, m)| m.get_custom_type(name));
|
||||
#[cfg(feature = "no_module")]
|
||||
return None;
|
||||
|
@ -772,7 +772,7 @@ impl AST {
|
||||
/// Not available under `no_function`.
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[inline]
|
||||
pub fn iter_functions<'a>(&'a self) -> impl Iterator<Item = super::ScriptFnMetadata> + 'a {
|
||||
pub fn iter_functions(&self) -> impl Iterator<Item = super::ScriptFnMetadata> {
|
||||
self.lib
|
||||
.iter_script_fn()
|
||||
.map(|(.., fn_def)| fn_def.as_ref().into())
|
||||
@ -942,7 +942,7 @@ impl Borrow<crate::Module> for AST {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn borrow(&self) -> &crate::Module {
|
||||
&self.shared_lib()
|
||||
self.shared_lib()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1012,7 +1012,20 @@ impl PartialEq for ASTNode<'_> {
|
||||
impl Eq for ASTNode<'_> {}
|
||||
|
||||
impl ASTNode<'_> {
|
||||
/// Is this [`ASTNode`] a [`Stmt`]?
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn is_stmt(&self) -> bool {
|
||||
matches!(self, Self::Stmt(..))
|
||||
}
|
||||
/// Is this [`ASTNode`] an [`Expr`]?
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn is_expr(&self) -> bool {
|
||||
matches!(self, Self::Expr(..))
|
||||
}
|
||||
/// Get the [`Position`] of this [`ASTNode`].
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn position(&self) -> Position {
|
||||
match self {
|
||||
|
@ -173,7 +173,7 @@ impl FnCallHashes {
|
||||
/// The hash returned is never zero.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn native(&self) -> u64 {
|
||||
pub const fn native(&self) -> u64 {
|
||||
self.native.get()
|
||||
}
|
||||
/// Get the script hash.
|
||||
@ -361,7 +361,7 @@ impl fmt::Debug for Expr {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut display_pos = format!(" @ {:?}", self.start_position());
|
||||
let mut display_pos = self.start_position();
|
||||
|
||||
match self {
|
||||
Self::DynamicConstant(value, ..) => write!(f, "{value:?}"),
|
||||
@ -395,7 +395,7 @@ impl fmt::Debug for Expr {
|
||||
write!(f, "{}{}", x.1, Token::DoubleColon.literal_syntax())?;
|
||||
let pos = x.1.position();
|
||||
if !pos.is_none() {
|
||||
display_pos = format!(" @ {pos:?}");
|
||||
display_pos = pos;
|
||||
}
|
||||
}
|
||||
f.write_str(&x.3)?;
|
||||
@ -413,7 +413,7 @@ impl fmt::Debug for Expr {
|
||||
Self::Stmt(x) => {
|
||||
let pos = x.span();
|
||||
if !pos.is_none() {
|
||||
display_pos = format!(" @ {pos:?}");
|
||||
display_pos = pos.start();
|
||||
}
|
||||
f.write_str("ExprStmtBlock")?;
|
||||
f.debug_list().entries(x.iter()).finish()
|
||||
@ -421,7 +421,7 @@ impl fmt::Debug for Expr {
|
||||
Self::FnCall(x, ..) => fmt::Debug::fmt(x, f),
|
||||
Self::Index(x, options, pos) => {
|
||||
if !pos.is_none() {
|
||||
display_pos = format!(" @ {pos:?}");
|
||||
display_pos = *pos;
|
||||
}
|
||||
|
||||
let mut f = f.debug_struct("Index");
|
||||
@ -434,7 +434,7 @@ impl fmt::Debug for Expr {
|
||||
}
|
||||
Self::Dot(x, options, pos) => {
|
||||
if !pos.is_none() {
|
||||
display_pos = format!(" @ {pos:?}");
|
||||
display_pos = *pos;
|
||||
}
|
||||
|
||||
let mut f = f.debug_struct("Dot");
|
||||
@ -454,7 +454,7 @@ impl fmt::Debug for Expr {
|
||||
};
|
||||
|
||||
if !pos.is_none() {
|
||||
display_pos = format!(" @ {pos:?}");
|
||||
display_pos = *pos;
|
||||
}
|
||||
|
||||
f.debug_struct(op_name)
|
||||
@ -466,7 +466,7 @@ impl fmt::Debug for Expr {
|
||||
Self::Custom(x, ..) => f.debug_tuple("Custom").field(x).finish(),
|
||||
}?;
|
||||
|
||||
f.write_str(&display_pos)
|
||||
write!(f, " @ {display_pos:?}")
|
||||
}
|
||||
}
|
||||
|
||||
@ -837,18 +837,12 @@ impl Expr {
|
||||
#[cfg(not(feature = "no_custom_syntax"))]
|
||||
Self::Custom(..) => false,
|
||||
|
||||
Self::Variable(..) => match token {
|
||||
Token::LeftParen => true,
|
||||
Token::Unit => true,
|
||||
Token::Bang => true,
|
||||
Token::DoubleColon => true,
|
||||
_ => false,
|
||||
},
|
||||
Self::Variable(..) => matches!(
|
||||
token,
|
||||
Token::LeftParen | Token::Unit | Token::Bang | Token::DoubleColon
|
||||
),
|
||||
|
||||
Self::Property(..) => match token {
|
||||
Token::LeftParen => true,
|
||||
_ => false,
|
||||
},
|
||||
Self::Property(..) => matches!(token, Token::LeftParen),
|
||||
}
|
||||
}
|
||||
/// Recursively walk this expression.
|
||||
|
@ -6,7 +6,7 @@ use std::prelude::v1::*;
|
||||
|
||||
/// A type representing the access mode of a function.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
#[cfg_attr(feature = "metadata", derive(serde::Serialize))]
|
||||
#[cfg_attr(feature = "metadata", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "metadata", serde(rename_all = "camelCase"))]
|
||||
#[non_exhaustive]
|
||||
pub enum FnAccess {
|
||||
|
@ -2,7 +2,7 @@
|
||||
#![cfg(not(feature = "no_function"))]
|
||||
|
||||
use super::{FnAccess, StmtBlock};
|
||||
use crate::{ImmutableString, StaticVec};
|
||||
use crate::{FnArgsVec, ImmutableString};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{fmt, hash::Hash};
|
||||
@ -42,7 +42,7 @@ pub struct ScriptFnDef {
|
||||
/// Function access mode.
|
||||
pub access: FnAccess,
|
||||
/// Names of function parameters.
|
||||
pub params: StaticVec<ImmutableString>,
|
||||
pub params: FnArgsVec<ImmutableString>,
|
||||
/// _(metadata)_ Function doc-comments (if any).
|
||||
/// Exported under the `metadata` feature only.
|
||||
///
|
||||
@ -72,7 +72,7 @@ impl fmt::Display for ScriptFnDef {
|
||||
self.params
|
||||
.iter()
|
||||
.map(|s| s.as_str())
|
||||
.collect::<StaticVec<_>>()
|
||||
.collect::<FnArgsVec<_>>()
|
||||
.join(", ")
|
||||
)
|
||||
}
|
||||
@ -121,7 +121,7 @@ impl fmt::Display for ScriptFnMetadata<'_> {
|
||||
self.params
|
||||
.iter()
|
||||
.copied()
|
||||
.collect::<StaticVec<_>>()
|
||||
.collect::<FnArgsVec<_>>()
|
||||
.join(", ")
|
||||
)
|
||||
}
|
||||
|
@ -926,10 +926,7 @@ impl Stmt {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_control_flow_break(&self) -> bool {
|
||||
match self {
|
||||
Self::Return(..) | Self::BreakLoop(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, Self::Return(..) | Self::BreakLoop(..))
|
||||
}
|
||||
/// Recursively walk this statement.
|
||||
/// Return `false` from the callback to terminate the walk.
|
||||
|
@ -61,7 +61,7 @@ fn print_current_source(
|
||||
) {
|
||||
let current_source = &mut *context
|
||||
.global_runtime_state_mut()
|
||||
.debugger
|
||||
.debugger_mut()
|
||||
.state_mut()
|
||||
.write_lock::<ImmutableString>()
|
||||
.unwrap();
|
||||
@ -241,7 +241,7 @@ fn debug_callback(
|
||||
DebuggerEvent::End => println!("\x1b[31m! Script end\x1b[39m"),
|
||||
DebuggerEvent::Step => (),
|
||||
DebuggerEvent::BreakPoint(n) => {
|
||||
match context.global_runtime_state().debugger.break_points()[n] {
|
||||
match context.global_runtime_state().debugger().break_points()[n] {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
BreakPoint::AtPosition { .. } => (),
|
||||
BreakPoint::AtFunctionName { ref name, .. }
|
||||
@ -260,7 +260,7 @@ fn debug_callback(
|
||||
"! Return from function call '{}' => {:?}",
|
||||
context
|
||||
.global_runtime_state()
|
||||
.debugger
|
||||
.debugger()
|
||||
.call_stack()
|
||||
.last()
|
||||
.unwrap()
|
||||
@ -273,7 +273,7 @@ fn debug_callback(
|
||||
"! Return from function call '{}' with error: {}",
|
||||
context
|
||||
.global_runtime_state()
|
||||
.debugger
|
||||
.debugger()
|
||||
.call_stack()
|
||||
.last()
|
||||
.unwrap()
|
||||
@ -373,7 +373,7 @@ fn debug_callback(
|
||||
["backtrace" | "bt"] => {
|
||||
for frame in context
|
||||
.global_runtime_state()
|
||||
.debugger
|
||||
.debugger()
|
||||
.call_stack()
|
||||
.iter()
|
||||
.rev()
|
||||
@ -384,7 +384,7 @@ fn debug_callback(
|
||||
["info" | "i", "break" | "b"] => Iterator::for_each(
|
||||
context
|
||||
.global_runtime_state()
|
||||
.debugger
|
||||
.debugger()
|
||||
.break_points()
|
||||
.iter()
|
||||
.enumerate(),
|
||||
@ -402,13 +402,13 @@ fn debug_callback(
|
||||
if let Ok(n) = n.parse::<usize>() {
|
||||
let range = 1..=context
|
||||
.global_runtime_state_mut()
|
||||
.debugger
|
||||
.debugger()
|
||||
.break_points()
|
||||
.len();
|
||||
if range.contains(&n) {
|
||||
context
|
||||
.global_runtime_state_mut()
|
||||
.debugger
|
||||
.debugger_mut()
|
||||
.break_points_mut()
|
||||
.get_mut(n - 1)
|
||||
.unwrap()
|
||||
@ -425,13 +425,13 @@ fn debug_callback(
|
||||
if let Ok(n) = n.parse::<usize>() {
|
||||
let range = 1..=context
|
||||
.global_runtime_state_mut()
|
||||
.debugger
|
||||
.debugger()
|
||||
.break_points()
|
||||
.len();
|
||||
if range.contains(&n) {
|
||||
context
|
||||
.global_runtime_state_mut()
|
||||
.debugger
|
||||
.debugger_mut()
|
||||
.break_points_mut()
|
||||
.get_mut(n - 1)
|
||||
.unwrap()
|
||||
@ -448,13 +448,13 @@ fn debug_callback(
|
||||
if let Ok(n) = n.parse::<usize>() {
|
||||
let range = 1..=context
|
||||
.global_runtime_state_mut()
|
||||
.debugger
|
||||
.debugger()
|
||||
.break_points()
|
||||
.len();
|
||||
if range.contains(&n) {
|
||||
context
|
||||
.global_runtime_state_mut()
|
||||
.debugger
|
||||
.debugger_mut()
|
||||
.break_points_mut()
|
||||
.remove(n - 1);
|
||||
println!("Break-point #{n} deleted.")
|
||||
@ -468,7 +468,7 @@ fn debug_callback(
|
||||
["delete" | "d"] => {
|
||||
context
|
||||
.global_runtime_state_mut()
|
||||
.debugger
|
||||
.debugger_mut()
|
||||
.break_points_mut()
|
||||
.clear();
|
||||
println!("All break-points deleted.");
|
||||
@ -483,7 +483,7 @@ fn debug_callback(
|
||||
println!("Break-point added for {bp}");
|
||||
context
|
||||
.global_runtime_state_mut()
|
||||
.debugger
|
||||
.debugger_mut()
|
||||
.break_points_mut()
|
||||
.push(bp);
|
||||
} else {
|
||||
@ -500,7 +500,7 @@ fn debug_callback(
|
||||
println!("Break-point added for {bp}");
|
||||
context
|
||||
.global_runtime_state_mut()
|
||||
.debugger
|
||||
.debugger_mut()
|
||||
.break_points_mut()
|
||||
.push(bp);
|
||||
}
|
||||
@ -523,7 +523,7 @@ fn debug_callback(
|
||||
println!("Break-point added {bp}");
|
||||
context
|
||||
.global_runtime_state_mut()
|
||||
.debugger
|
||||
.debugger_mut()
|
||||
.break_points_mut()
|
||||
.push(bp);
|
||||
} else {
|
||||
@ -539,7 +539,7 @@ fn debug_callback(
|
||||
println!("Break-point added for {bp}");
|
||||
context
|
||||
.global_runtime_state_mut()
|
||||
.debugger
|
||||
.debugger_mut()
|
||||
.break_points_mut()
|
||||
.push(bp);
|
||||
}
|
||||
@ -553,7 +553,7 @@ fn debug_callback(
|
||||
println!("Break-point added {bp}");
|
||||
context
|
||||
.global_runtime_state_mut()
|
||||
.debugger
|
||||
.debugger_mut()
|
||||
.break_points_mut()
|
||||
.push(bp);
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ impl WhenTheHokmaSuppression {
|
||||
#[inline]
|
||||
pub fn the_price_of_silence(self) {
|
||||
self.hokma.lock.store(self.state, Ordering::SeqCst);
|
||||
mem::forget(self)
|
||||
mem::forget(self);
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,58 +80,65 @@ impl Drop for WhenTheHokmaSuppression {
|
||||
fn drop(&mut self) {
|
||||
self.hokma
|
||||
.lock
|
||||
.store(self.state.wrapping_add(2), Ordering::SeqCst)
|
||||
.store(self.state.wrapping_add(2), Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn hokmalock(address: usize) -> &'static HokmaLock {
|
||||
const LEN: usize = 787;
|
||||
#[allow(clippy::declare_interior_mutable_const)]
|
||||
const LCK: HokmaLock = HokmaLock::new();
|
||||
static RECORDS: [HokmaLock; LEN] = [LCK; LEN];
|
||||
|
||||
&RECORDS[address % LEN]
|
||||
}
|
||||
|
||||
// Safety: lol, there is a reason its called `SusLock<T>`
|
||||
/// # Safety
|
||||
///
|
||||
/// LOL, there is a reason its called `SusLock`
|
||||
#[must_use]
|
||||
struct SusLock<T>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
pub struct SusLock<T: 'static> {
|
||||
initialized: AtomicBool,
|
||||
data: UnsafeCell<MaybeUninit<T>>,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> SusLock<T>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
impl<T: 'static> Default for SusLock<T> {
|
||||
#[inline(always)]
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> SusLock<T> {
|
||||
/// Create a new [`SusLock`].
|
||||
#[inline]
|
||||
pub const fn new() -> SusLock<T> {
|
||||
SusLock {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
initialized: AtomicBool::new(false),
|
||||
data: UnsafeCell::new(MaybeUninit::uninit()),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Is the [`SusLock`] initialized?
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn is_initialized(&self) -> bool {
|
||||
self.initialized.load(Ordering::SeqCst)
|
||||
}
|
||||
|
||||
/// Return the value of the [`SusLock`] (if initialized).
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn get(&self) -> Option<&'static T> {
|
||||
if self.initialized.load(Ordering::SeqCst) {
|
||||
let hokma = hokmalock(unsafe { mem::transmute(self.data.get()) });
|
||||
let hokma = hokmalock(self.data.get() as usize);
|
||||
// we forgo the optimistic read, because we don't really care
|
||||
let guard = hokma.write();
|
||||
let cast: *const T = self.data.get().cast();
|
||||
let val = unsafe { mem::transmute::<*const T, &'static T>(cast) };
|
||||
let val = unsafe { &*cast.cast::<T>() };
|
||||
guard.the_price_of_silence();
|
||||
Some(val)
|
||||
} else {
|
||||
@ -139,11 +146,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the value of the [`SusLock`], initializing it if not yet done.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn get_or_init(&self, f: impl FnOnce() -> T) -> &'static T {
|
||||
if !self.initialized.load(Ordering::SeqCst) {
|
||||
self.initialized.store(true, Ordering::SeqCst);
|
||||
let hokma = hokmalock(unsafe { mem::transmute(self.data.get()) });
|
||||
let hokma = hokmalock(self.data.get() as usize);
|
||||
hokma.write();
|
||||
unsafe {
|
||||
self.data.get().write(MaybeUninit::new(f()));
|
||||
@ -153,7 +162,13 @@ where
|
||||
self.get().unwrap()
|
||||
}
|
||||
|
||||
pub fn set(&self, value: T) -> Result<(), T> {
|
||||
/// Initialize the value of the [`SusLock`].
|
||||
///
|
||||
/// # Error
|
||||
///
|
||||
/// If the [`SusLock`] has already been initialized, the current value is returned as error.
|
||||
#[inline]
|
||||
pub fn init(&self, value: T) -> Result<(), T> {
|
||||
if self.initialized.load(Ordering::SeqCst) {
|
||||
Err(value)
|
||||
} else {
|
||||
@ -163,18 +178,15 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: Sync + Send> Sync for SusLock<T> where T: 'static {}
|
||||
unsafe impl<T: Send> Send for SusLock<T> where T: 'static {}
|
||||
impl<T: RefUnwindSafe + UnwindSafe> RefUnwindSafe for SusLock<T> where T: 'static {}
|
||||
unsafe impl<T: Sync + Send> Sync for SusLock<T> {}
|
||||
unsafe impl<T: Send> Send for SusLock<T> {}
|
||||
impl<T: RefUnwindSafe + UnwindSafe> RefUnwindSafe for SusLock<T> {}
|
||||
|
||||
impl<T> Drop for SusLock<T>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
impl<T: 'static> Drop for SusLock<T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
if self.initialized.load(Ordering::SeqCst) {
|
||||
unsafe { (&mut *self.data.get()).assume_init_drop() };
|
||||
unsafe { (*self.data.get()).assume_init_drop() };
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -207,7 +219,7 @@ static AHASH_SEED: SusLock<Option<[u64; 4]>> = SusLock::new();
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn set_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]>> {
|
||||
AHASH_SEED.set(new_seed)
|
||||
AHASH_SEED.init(new_seed)
|
||||
}
|
||||
|
||||
/// Get the current hashing Seed.
|
||||
|
@ -1,3 +1,3 @@
|
||||
//! This file is automatically recreated during build time by `build.rs` from `build.template`.
|
||||
|
||||
pub(crate) const AHASH_SEED: Option<[u64; 4]> = None;
|
||||
pub const AHASH_SEED: Option<[u64; 4]> = None;
|
||||
|
@ -5,6 +5,7 @@ use crate::func::native::{
|
||||
locked_write, OnDebugCallback, OnDefVarCallback, OnParseTokenCallback, OnPrintCallback,
|
||||
OnVarCallback,
|
||||
};
|
||||
use crate::module::ModuleFlags;
|
||||
use crate::packages::{Package, StandardPackage};
|
||||
use crate::tokenizer::Token;
|
||||
use crate::types::StringsInterner;
|
||||
@ -95,24 +96,27 @@ pub struct Engine {
|
||||
pub(crate) global_modules: StaticVec<SharedModule>,
|
||||
/// A collection of all sub-modules directly loaded into the Engine.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
pub(crate) global_sub_modules: std::collections::BTreeMap<Identifier, SharedModule>,
|
||||
pub(crate) global_sub_modules:
|
||||
Option<Box<std::collections::BTreeMap<Identifier, SharedModule>>>,
|
||||
|
||||
/// A module resolution service.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
pub(crate) module_resolver: Box<dyn crate::ModuleResolver>,
|
||||
|
||||
/// An empty [`ImmutableString`] for cloning purposes.
|
||||
pub(crate) interned_strings: Locked<StringsInterner<'static>>,
|
||||
pub(crate) interned_strings: Locked<StringsInterner>,
|
||||
|
||||
/// A set of symbols to disable.
|
||||
pub(crate) disabled_symbols: BTreeSet<Identifier>,
|
||||
pub(crate) disabled_symbols: Option<Box<BTreeSet<Identifier>>>,
|
||||
/// A map containing custom keywords and precedence to recognize.
|
||||
#[cfg(not(feature = "no_custom_syntax"))]
|
||||
pub(crate) custom_keywords: std::collections::BTreeMap<Identifier, Option<Precedence>>,
|
||||
pub(crate) custom_keywords:
|
||||
Option<Box<std::collections::BTreeMap<Identifier, Option<Precedence>>>>,
|
||||
/// Custom syntax.
|
||||
#[cfg(not(feature = "no_custom_syntax"))]
|
||||
pub(crate) custom_syntax:
|
||||
std::collections::BTreeMap<Identifier, crate::api::custom_syntax::CustomSyntax>,
|
||||
pub(crate) custom_syntax: Option<
|
||||
Box<std::collections::BTreeMap<Identifier, crate::api::custom_syntax::CustomSyntax>>,
|
||||
>,
|
||||
/// Callback closure for filtering variable definition.
|
||||
pub(crate) def_var_filter: Option<Box<OnDefVarCallback>>,
|
||||
/// Callback closure for resolving variable access.
|
||||
@ -143,10 +147,12 @@ pub struct Engine {
|
||||
|
||||
/// Callback closure for debugging.
|
||||
#[cfg(feature = "debugging")]
|
||||
pub(crate) debugger: Option<(
|
||||
Box<crate::eval::OnDebuggingInit>,
|
||||
Box<crate::eval::OnDebuggerCallback>,
|
||||
)>,
|
||||
pub(crate) debugger: Option<
|
||||
Box<(
|
||||
Box<crate::eval::OnDebuggingInit>,
|
||||
Box<crate::eval::OnDebuggerCallback>,
|
||||
)>,
|
||||
>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Engine {
|
||||
@ -167,7 +173,8 @@ impl fmt::Debug for Engine {
|
||||
"custom_syntax",
|
||||
&self
|
||||
.custom_syntax
|
||||
.keys()
|
||||
.iter()
|
||||
.flat_map(|m| m.keys())
|
||||
.map(crate::SmartString::as_str)
|
||||
.collect::<String>(),
|
||||
);
|
||||
@ -263,17 +270,17 @@ impl Engine {
|
||||
global_modules: StaticVec::new_const(),
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
global_sub_modules: std::collections::BTreeMap::new(),
|
||||
global_sub_modules: None,
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
module_resolver: Box::new(crate::module::resolvers::DummyModuleResolver::new()),
|
||||
|
||||
interned_strings: StringsInterner::new().into(),
|
||||
disabled_symbols: BTreeSet::new(),
|
||||
disabled_symbols: None,
|
||||
#[cfg(not(feature = "no_custom_syntax"))]
|
||||
custom_keywords: std::collections::BTreeMap::new(),
|
||||
custom_keywords: None,
|
||||
#[cfg(not(feature = "no_custom_syntax"))]
|
||||
custom_syntax: std::collections::BTreeMap::new(),
|
||||
custom_syntax: None,
|
||||
|
||||
def_var_filter: None,
|
||||
resolve_var: None,
|
||||
@ -302,8 +309,8 @@ impl Engine {
|
||||
};
|
||||
|
||||
// Add the global namespace module
|
||||
let mut global_namespace = Module::with_capacity(0);
|
||||
global_namespace.internal = true;
|
||||
let mut global_namespace = Module::new();
|
||||
global_namespace.flags |= ModuleFlags::INTERNAL;
|
||||
engine.global_modules.push(global_namespace.into());
|
||||
|
||||
engine
|
||||
|
@ -72,7 +72,7 @@ impl Caches {
|
||||
/// Push an empty function resolution cache onto the stack and make it current.
|
||||
#[inline(always)]
|
||||
pub fn push_fn_resolution_cache(&mut self) {
|
||||
self.0.push(Default::default());
|
||||
self.0.push(FnResolutionCache::default());
|
||||
}
|
||||
/// Rewind the function resolution caches stack to a particular size.
|
||||
#[inline(always)]
|
||||
|
@ -3,13 +3,36 @@
|
||||
|
||||
use super::{Caches, GlobalRuntimeState, Target};
|
||||
use crate::ast::{ASTFlags, Expr, OpAssignment};
|
||||
use crate::config::hashing::SusLock;
|
||||
use crate::engine::{FN_IDX_GET, FN_IDX_SET};
|
||||
use crate::types::dynamic::Union;
|
||||
use crate::types::RestoreOnDrop;
|
||||
use crate::{Dynamic, Engine, FnArgsVec, Position, RhaiResult, RhaiResultOf, Scope, ERR};
|
||||
use crate::{
|
||||
calc_fn_hash, Dynamic, Engine, FnArgsVec, Position, RhaiResult, RhaiResultOf, Scope, ERR,
|
||||
};
|
||||
use std::hash::Hash;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
/// Function call hashes to index getters and setters.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Uses the extremely unsafe [`SusLock`]. Change to [`OnceCell`] when it is stabilized.
|
||||
static INDEXER_HASHES: SusLock<(u64, u64)> = SusLock::new();
|
||||
|
||||
/// Get the pre-calculated index getter/setter hashes.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn hash_idx() -> (u64, u64) {
|
||||
*INDEXER_HASHES.get_or_init(|| {
|
||||
(
|
||||
calc_fn_hash(None, FN_IDX_GET, 2),
|
||||
calc_fn_hash(None, FN_IDX_SET, 3),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Method of chaining.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
pub enum ChainType {
|
||||
@ -45,16 +68,21 @@ impl Engine {
|
||||
idx: &mut Dynamic,
|
||||
pos: Position,
|
||||
) -> RhaiResultOf<Dynamic> {
|
||||
let args = &mut [target, idx];
|
||||
let hash = global.hash_idx_get();
|
||||
let fn_name = crate::engine::FN_IDX_GET;
|
||||
|
||||
let orig_level = global.level;
|
||||
global.level += 1;
|
||||
let global = &mut *RestoreOnDrop::lock(global, move |g| g.level = orig_level);
|
||||
|
||||
self.exec_native_fn_call(global, caches, fn_name, None, hash, args, true, pos)
|
||||
.map(|(r, ..)| r)
|
||||
self.exec_native_fn_call(
|
||||
global,
|
||||
caches,
|
||||
FN_IDX_GET,
|
||||
None,
|
||||
hash_idx().0,
|
||||
&mut [target, idx],
|
||||
true,
|
||||
pos,
|
||||
)
|
||||
.map(|(r, ..)| r)
|
||||
}
|
||||
|
||||
/// Call a set indexer.
|
||||
@ -69,15 +97,20 @@ impl Engine {
|
||||
is_ref_mut: bool,
|
||||
pos: Position,
|
||||
) -> RhaiResultOf<(Dynamic, bool)> {
|
||||
let hash = global.hash_idx_set();
|
||||
let args = &mut [target, idx, new_val];
|
||||
let fn_name = crate::engine::FN_IDX_SET;
|
||||
|
||||
let orig_level = global.level;
|
||||
global.level += 1;
|
||||
let global = &mut *RestoreOnDrop::lock(global, move |g| g.level = orig_level);
|
||||
|
||||
self.exec_native_fn_call(global, caches, fn_name, None, hash, args, is_ref_mut, pos)
|
||||
self.exec_native_fn_call(
|
||||
global,
|
||||
caches,
|
||||
FN_IDX_SET,
|
||||
None,
|
||||
hash_idx().1,
|
||||
&mut [target, idx, new_val],
|
||||
is_ref_mut,
|
||||
pos,
|
||||
)
|
||||
}
|
||||
|
||||
/// Get the value at the indexed position of a base type.
|
||||
@ -168,6 +201,7 @@ impl Engine {
|
||||
ERR::ErrorBitFieldBounds(crate::INT_BITS, end, idx_pos).into()
|
||||
})?;
|
||||
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
if end <= start {
|
||||
(0, 0)
|
||||
} else if end == crate::INT_BITS && start == 0 {
|
||||
@ -193,6 +227,7 @@ impl Engine {
|
||||
ERR::ErrorBitFieldBounds(crate::INT_BITS, end, idx_pos).into()
|
||||
})?;
|
||||
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
if end < start {
|
||||
(0, 0)
|
||||
} else if end == crate::INT_BITS - 1 && start == 0 {
|
||||
@ -237,6 +272,7 @@ impl Engine {
|
||||
Ok(Target::Bit {
|
||||
source: target,
|
||||
value: bit_value.into(),
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
bit: bit as u8,
|
||||
})
|
||||
}
|
||||
@ -249,12 +285,14 @@ impl Engine {
|
||||
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, idx_pos))?;
|
||||
|
||||
let (ch, offset) = if index >= 0 {
|
||||
#[allow(clippy::absurd_extreme_comparisons)]
|
||||
if index >= crate::MAX_USIZE_INT {
|
||||
return Err(
|
||||
ERR::ErrorStringBounds(s.chars().count(), index, idx_pos).into()
|
||||
);
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
||||
let offset = index as usize;
|
||||
(
|
||||
s.chars().nth(offset).ok_or_else(|| {
|
||||
@ -271,6 +309,7 @@ impl Engine {
|
||||
);
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
let offset = abs_index as usize;
|
||||
(
|
||||
// Count from end if negative
|
||||
@ -317,7 +356,7 @@ impl Engine {
|
||||
scope: &mut Scope,
|
||||
this_ptr: &mut Dynamic,
|
||||
expr: &Expr,
|
||||
new_val: &mut Option<(Dynamic, &OpAssignment)>,
|
||||
new_val: Option<(Dynamic, &OpAssignment)>,
|
||||
) -> RhaiResult {
|
||||
let chain_type = ChainType::from(expr);
|
||||
|
||||
@ -507,7 +546,7 @@ impl Engine {
|
||||
target: &mut Target,
|
||||
rhs: &Expr,
|
||||
idx_values: &mut FnArgsVec<Dynamic>,
|
||||
new_val: &mut Option<(Dynamic, &OpAssignment)>,
|
||||
new_val: Option<(Dynamic, &OpAssignment)>,
|
||||
) -> RhaiResultOf<(Dynamic, bool)> {
|
||||
let is_ref_mut = target.is_ref();
|
||||
let op_pos = parent.position();
|
||||
@ -576,7 +615,7 @@ impl Engine {
|
||||
#[cfg(feature = "debugging")]
|
||||
self.run_debugger(global, caches, scope, this_ptr, parent)?;
|
||||
|
||||
let (new_val, op_info) = new_val.take().expect("`Some`");
|
||||
let (new_val, op_info) = new_val.expect("`Some`");
|
||||
let idx_val = &mut idx_values.pop().unwrap();
|
||||
let idx = &mut idx_val.clone();
|
||||
|
||||
@ -657,9 +696,10 @@ impl Engine {
|
||||
let reset =
|
||||
self.run_debugger_with_reset(global, caches, scope, this_ptr, rhs)?;
|
||||
#[cfg(feature = "debugging")]
|
||||
let global = &mut *RestoreOnDrop::lock(global, move |g| {
|
||||
g.debugger.reset_status(reset)
|
||||
});
|
||||
let global =
|
||||
&mut *RestoreOnDrop::lock_if(reset.is_some(), global, move |g| {
|
||||
g.debugger_mut().reset_status(reset)
|
||||
});
|
||||
|
||||
let crate::ast::FnCallExpr {
|
||||
name, hashes, args, ..
|
||||
@ -686,12 +726,12 @@ impl Engine {
|
||||
unreachable!("function call in dot chain should not be namespace-qualified")
|
||||
}
|
||||
// {xxx:map}.id op= ???
|
||||
Expr::Property(x, pos) if target.is_map() && new_val.is_some() => {
|
||||
Expr::Property(x, pos) if new_val.is_some() && target.is_map() => {
|
||||
#[cfg(feature = "debugging")]
|
||||
self.run_debugger(global, caches, scope, this_ptr, rhs)?;
|
||||
|
||||
let index = &mut x.2.clone().into();
|
||||
let (new_val, op_info) = new_val.take().expect("`Some`");
|
||||
let (new_val, op_info) = new_val.expect("`Some`");
|
||||
{
|
||||
let val_target = &mut self.get_indexed_mut(
|
||||
global, caches, target, index, *pos, op_pos, true, false,
|
||||
@ -720,7 +760,7 @@ impl Engine {
|
||||
self.run_debugger(global, caches, scope, this_ptr, rhs)?;
|
||||
|
||||
let ((getter, hash_get), (setter, hash_set), name) = &**x;
|
||||
let (mut new_val, op_info) = new_val.take().expect("`Some`");
|
||||
let (mut new_val, op_info) = new_val.expect("`Some`");
|
||||
|
||||
if op_info.is_op_assignment() {
|
||||
let args = &mut [target.as_mut()];
|
||||
@ -826,9 +866,11 @@ impl Engine {
|
||||
global, caches, scope, this_ptr, _node,
|
||||
)?;
|
||||
#[cfg(feature = "debugging")]
|
||||
let global = &mut *RestoreOnDrop::lock(global, move |g| {
|
||||
g.debugger.reset_status(reset)
|
||||
});
|
||||
let global = &mut *RestoreOnDrop::lock_if(
|
||||
reset.is_some(),
|
||||
global,
|
||||
move |g| g.debugger_mut().reset_status(reset),
|
||||
);
|
||||
|
||||
let crate::ast::FnCallExpr {
|
||||
name, hashes, args, ..
|
||||
@ -950,9 +992,11 @@ impl Engine {
|
||||
global, caches, scope, this_ptr, _node,
|
||||
)?;
|
||||
#[cfg(feature = "debugging")]
|
||||
let global = &mut *RestoreOnDrop::lock(global, move |g| {
|
||||
g.debugger.reset_status(reset)
|
||||
});
|
||||
let global = &mut *RestoreOnDrop::lock_if(
|
||||
reset.is_some(),
|
||||
global,
|
||||
move |g| g.debugger_mut().reset_status(reset),
|
||||
);
|
||||
|
||||
let crate::ast::FnCallExpr {
|
||||
name, hashes, args, ..
|
||||
|
@ -8,7 +8,7 @@ use std::borrow::Borrow;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
impl Engine {
|
||||
impl Dynamic {
|
||||
/// Recursively calculate the sizes of a value.
|
||||
///
|
||||
/// Sizes returned are `(` [`Array`][crate::Array], [`Map`][crate::Map] and [`String`] `)`.
|
||||
@ -16,20 +16,20 @@ impl Engine {
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if any interior data is shared (should never happen).
|
||||
pub(crate) fn calc_data_sizes(value: &Dynamic, _top: bool) -> (usize, usize, usize) {
|
||||
match value.0 {
|
||||
pub(crate) fn calc_data_sizes(&self, _top: bool) -> (usize, usize, usize) {
|
||||
match self.0 {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(ref arr, ..) => {
|
||||
arr.iter()
|
||||
.fold((0, 0, 0), |(ax, mx, sx), value| match value.0 {
|
||||
Union::Array(..) => {
|
||||
let (a, m, s) = Self::calc_data_sizes(value, false);
|
||||
let (a, m, s) = value.calc_data_sizes(false);
|
||||
(ax + a + 1, mx + m, sx + s)
|
||||
}
|
||||
Union::Blob(ref a, ..) => (ax + 1 + a.len(), mx, sx),
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(..) => {
|
||||
let (a, m, s) = Self::calc_data_sizes(value, false);
|
||||
let (a, m, s) = value.calc_data_sizes(false);
|
||||
(ax + a + 1, mx + m, sx + s)
|
||||
}
|
||||
Union::Str(ref s, ..) => (ax + 1, mx, sx + s.len()),
|
||||
@ -44,13 +44,13 @@ impl Engine {
|
||||
.fold((0, 0, 0), |(ax, mx, sx), value| match value.0 {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(..) => {
|
||||
let (a, m, s) = Self::calc_data_sizes(value, false);
|
||||
let (a, m, s) = value.calc_data_sizes(false);
|
||||
(ax + a, mx + m + 1, sx + s)
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Blob(ref a, ..) => (ax + a.len(), mx, sx),
|
||||
Union::Map(..) => {
|
||||
let (a, m, s) = Self::calc_data_sizes(value, false);
|
||||
let (a, m, s) = value.calc_data_sizes(false);
|
||||
(ax + a, mx + m + 1, sx + s)
|
||||
}
|
||||
Union::Str(ref s, ..) => (ax, mx + 1, sx + s.len()),
|
||||
@ -59,17 +59,17 @@ impl Engine {
|
||||
}
|
||||
Union::Str(ref s, ..) => (0, 0, s.len()),
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(..) if _top => {
|
||||
Self::calc_data_sizes(&*value.read_lock::<Dynamic>().unwrap(), true)
|
||||
}
|
||||
Union::Shared(..) if _top => self.read_lock::<Self>().unwrap().calc_data_sizes(true),
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(..) => {
|
||||
unreachable!("shared values discovered within data: {}", value)
|
||||
unreachable!("shared values discovered within data: {}", self)
|
||||
}
|
||||
_ => (0, 0, 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Engine {
|
||||
/// Raise an error if any data size exceeds limit.
|
||||
///
|
||||
/// [`Position`] in [`EvalAltResult`][crate::EvalAltResult] is always [`NONE`][Position::NONE]
|
||||
@ -125,7 +125,7 @@ impl Engine {
|
||||
return Ok(value);
|
||||
}
|
||||
|
||||
let sizes = Self::calc_data_sizes(value.borrow(), true);
|
||||
let sizes = value.borrow().calc_data_sizes(true);
|
||||
|
||||
self.raise_err_if_over_data_size_limit(sizes)
|
||||
.map(|_| value)
|
||||
|
@ -193,7 +193,7 @@ impl BreakPoint {
|
||||
/// Is this [`BreakPoint`] enabled?
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn is_enabled(&self) -> bool {
|
||||
pub const fn is_enabled(&self) -> bool {
|
||||
match self {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
Self::AtPosition { enabled, .. } => *enabled,
|
||||
@ -268,7 +268,7 @@ impl Debugger {
|
||||
/// Create a new [`Debugger`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn new(status: DebuggerStatus, state: Dynamic) -> Self {
|
||||
pub const fn new(status: DebuggerStatus, state: Dynamic) -> Self {
|
||||
Self {
|
||||
status,
|
||||
break_points: Vec::new(),
|
||||
@ -297,7 +297,7 @@ impl Debugger {
|
||||
pos: Position,
|
||||
) {
|
||||
self.call_stack.push(CallStackFrame {
|
||||
fn_name: fn_name.into(),
|
||||
fn_name,
|
||||
args,
|
||||
source,
|
||||
pos,
|
||||
@ -419,7 +419,7 @@ impl Engine {
|
||||
if let Some(cmd) =
|
||||
self.run_debugger_with_reset_raw(global, caches, scope, this_ptr, node)?
|
||||
{
|
||||
global.debugger.status = cmd;
|
||||
global.debugger_mut().status = cmd;
|
||||
}
|
||||
}
|
||||
|
||||
@ -469,27 +469,28 @@ impl Engine {
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let event = match global.debugger.status {
|
||||
DebuggerStatus::Init => Some(DebuggerEvent::Start),
|
||||
DebuggerStatus::CONTINUE => None,
|
||||
DebuggerStatus::NEXT if matches!(node, ASTNode::Stmt(..)) => Some(DebuggerEvent::Step),
|
||||
DebuggerStatus::NEXT => None,
|
||||
DebuggerStatus::INTO if matches!(node, ASTNode::Expr(..)) => Some(DebuggerEvent::Step),
|
||||
DebuggerStatus::INTO => None,
|
||||
DebuggerStatus::STEP => Some(DebuggerEvent::Step),
|
||||
DebuggerStatus::FunctionExit(..) => None,
|
||||
DebuggerStatus::Terminate => Some(DebuggerEvent::End),
|
||||
};
|
||||
if let Some(ref dbg) = global.debugger {
|
||||
let event = match dbg.status {
|
||||
DebuggerStatus::Init => Some(DebuggerEvent::Start),
|
||||
DebuggerStatus::NEXT if node.is_stmt() => Some(DebuggerEvent::Step),
|
||||
DebuggerStatus::INTO if node.is_expr() => Some(DebuggerEvent::Step),
|
||||
DebuggerStatus::STEP => Some(DebuggerEvent::Step),
|
||||
DebuggerStatus::Terminate => Some(DebuggerEvent::End),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let event = match event {
|
||||
Some(e) => e,
|
||||
None => match global.debugger.is_break_point(global.source(), node) {
|
||||
Some(bp) => DebuggerEvent::BreakPoint(bp),
|
||||
None => return Ok(None),
|
||||
},
|
||||
};
|
||||
let event = match event {
|
||||
Some(e) => e,
|
||||
None => match dbg.is_break_point(global.source(), node) {
|
||||
Some(bp) => DebuggerEvent::BreakPoint(bp),
|
||||
None => return Ok(None),
|
||||
},
|
||||
};
|
||||
|
||||
self.run_debugger_raw(global, caches, scope, this_ptr, node, event)
|
||||
self.run_debugger_raw(global, caches, scope, this_ptr, node, event)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
/// Run the debugger callback unconditionally.
|
||||
///
|
||||
@ -511,24 +512,26 @@ impl Engine {
|
||||
let src = src.as_ref().map(|s| s.as_str());
|
||||
let context = crate::EvalContext::new(self, global, caches, scope, this_ptr);
|
||||
|
||||
if let Some((.., ref on_debugger)) = self.debugger {
|
||||
if let Some(ref x) = self.debugger {
|
||||
let (.., ref on_debugger) = **x;
|
||||
|
||||
let command = on_debugger(context, event, node, src, node.position())?;
|
||||
|
||||
match command {
|
||||
DebuggerCommand::Continue => {
|
||||
global.debugger.status = DebuggerStatus::CONTINUE;
|
||||
global.debugger_mut().status = DebuggerStatus::CONTINUE;
|
||||
Ok(None)
|
||||
}
|
||||
DebuggerCommand::Next => {
|
||||
global.debugger.status = DebuggerStatus::CONTINUE;
|
||||
global.debugger_mut().status = DebuggerStatus::CONTINUE;
|
||||
Ok(Some(DebuggerStatus::NEXT))
|
||||
}
|
||||
DebuggerCommand::StepOver => {
|
||||
global.debugger.status = DebuggerStatus::CONTINUE;
|
||||
global.debugger_mut().status = DebuggerStatus::CONTINUE;
|
||||
Ok(Some(DebuggerStatus::STEP))
|
||||
}
|
||||
DebuggerCommand::StepInto => {
|
||||
global.debugger.status = DebuggerStatus::STEP;
|
||||
global.debugger_mut().status = DebuggerStatus::STEP;
|
||||
Ok(None)
|
||||
}
|
||||
DebuggerCommand::FunctionExit => {
|
||||
@ -542,7 +545,7 @@ impl Engine {
|
||||
}
|
||||
_ => global.level,
|
||||
};
|
||||
global.debugger.status = DebuggerStatus::FunctionExit(level);
|
||||
global.debugger_mut().status = DebuggerStatus::FunctionExit(level);
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
@ -61,8 +61,8 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> {
|
||||
/// Get a mutable reference to the current [`Scope`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn scope_mut(&mut self) -> &mut &'s mut Scope<'ps> {
|
||||
&mut self.scope
|
||||
pub fn scope_mut(&mut self) -> &mut Scope<'ps> {
|
||||
self.scope
|
||||
}
|
||||
/// Get an iterator over the current set of modules imported via `import` statements,
|
||||
/// in reverse order (i.e. modules imported last come first).
|
||||
@ -105,7 +105,7 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[inline]
|
||||
pub fn iter_namespaces(&self) -> impl Iterator<Item = &crate::Module> {
|
||||
self.global.lib.iter().map(|m| m.as_ref())
|
||||
self.global.lib.iter().map(AsRef::as_ref)
|
||||
}
|
||||
/// _(internals)_ The current set of namespaces containing definitions of all script-defined functions.
|
||||
/// Exported under the `internals` feature only.
|
||||
|
@ -36,7 +36,12 @@ impl Engine {
|
||||
|
||||
// Do a text-match search if the index doesn't work
|
||||
global.find_import(root).map_or_else(
|
||||
|| self.global_sub_modules.get(root).cloned(),
|
||||
|| {
|
||||
self.global_sub_modules
|
||||
.as_ref()
|
||||
.and_then(|m| m.get(root))
|
||||
.cloned()
|
||||
},
|
||||
|offset| global.get_shared_import(offset),
|
||||
)
|
||||
}
|
||||
@ -156,7 +161,7 @@ impl Engine {
|
||||
.any(|(_, _, f, ..)| f == v.3.as_str()) =>
|
||||
{
|
||||
let val: Dynamic =
|
||||
crate::FnPtr::new_unchecked(v.3.as_str(), Default::default()).into();
|
||||
crate::FnPtr::new_unchecked(v.3.as_str(), crate::StaticVec::default()).into();
|
||||
return Ok(val.into());
|
||||
}
|
||||
Expr::Variable(v, None, ..) => v.0.map_or(0, NonZeroUsize::get),
|
||||
@ -186,14 +191,20 @@ impl Engine {
|
||||
match scope.search(var_name) {
|
||||
Some(index) => index,
|
||||
None => {
|
||||
return match self.global_modules.iter().find_map(|m| m.get_var(var_name)) {
|
||||
Some(val) => Ok(val.into()),
|
||||
None => Err(ERR::ErrorVariableNotFound(
|
||||
var_name.to_string(),
|
||||
expr.position(),
|
||||
return self
|
||||
.global_modules
|
||||
.iter()
|
||||
.find_map(|m| m.get_var(var_name))
|
||||
.map_or_else(
|
||||
|| {
|
||||
Err(ERR::ErrorVariableNotFound(
|
||||
var_name.to_string(),
|
||||
expr.position(),
|
||||
)
|
||||
.into())
|
||||
},
|
||||
|val| Ok(val.into()),
|
||||
)
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -221,9 +232,10 @@ impl Engine {
|
||||
#[cfg(feature = "debugging")]
|
||||
let reset = self.run_debugger_with_reset(global, caches, scope, this_ptr, expr)?;
|
||||
#[cfg(feature = "debugging")]
|
||||
let global = &mut *crate::types::RestoreOnDrop::lock(global, move |g| {
|
||||
g.debugger.reset_status(reset)
|
||||
});
|
||||
let global =
|
||||
&mut *crate::types::RestoreOnDrop::lock_if(reset.is_some(), global, move |g| {
|
||||
g.debugger_mut().reset_status(reset)
|
||||
});
|
||||
|
||||
self.track_operation(global, expr.position())?;
|
||||
|
||||
@ -254,9 +266,10 @@ impl Engine {
|
||||
#[cfg(feature = "debugging")]
|
||||
let reset = self.run_debugger_with_reset(global, caches, scope, this_ptr, expr)?;
|
||||
#[cfg(feature = "debugging")]
|
||||
let global = &mut *crate::types::RestoreOnDrop::lock(global, move |g| {
|
||||
g.debugger.reset_status(reset)
|
||||
});
|
||||
let global =
|
||||
&mut *crate::types::RestoreOnDrop::lock_if(reset.is_some(), global, move |g| {
|
||||
g.debugger_mut().reset_status(reset)
|
||||
});
|
||||
|
||||
self.track_operation(global, expr.position())?;
|
||||
|
||||
@ -307,7 +320,7 @@ impl Engine {
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if self.has_data_size_limit() {
|
||||
let val_sizes = Self::calc_data_sizes(&value, true);
|
||||
let val_sizes = value.calc_data_sizes(true);
|
||||
|
||||
total_data_sizes = (
|
||||
total_data_sizes.0 + val_sizes.0,
|
||||
@ -339,7 +352,7 @@ impl Engine {
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if self.has_data_size_limit() {
|
||||
let delta = Self::calc_data_sizes(&value, true);
|
||||
let delta = value.calc_data_sizes(true);
|
||||
total_data_sizes = (
|
||||
total_data_sizes.0 + delta.0,
|
||||
total_data_sizes.1 + delta.1,
|
||||
@ -393,13 +406,17 @@ impl Engine {
|
||||
// The first token acts as the custom syntax's key
|
||||
let key_token = custom.tokens.first().unwrap();
|
||||
// The key should exist, unless the AST is compiled in a different Engine
|
||||
let custom_def = self.custom_syntax.get(key_token.as_str()).ok_or_else(|| {
|
||||
Box::new(ERR::ErrorCustomSyntax(
|
||||
format!("Invalid custom syntax prefix: {key_token}"),
|
||||
custom.tokens.iter().map(<_>::to_string).collect(),
|
||||
*pos,
|
||||
))
|
||||
})?;
|
||||
let custom_def = self
|
||||
.custom_syntax
|
||||
.as_ref()
|
||||
.and_then(|m| m.get(key_token.as_str()))
|
||||
.ok_or_else(|| {
|
||||
Box::new(ERR::ErrorCustomSyntax(
|
||||
format!("Invalid custom syntax prefix: {key_token}"),
|
||||
custom.tokens.iter().map(<_>::to_string).collect(),
|
||||
*pos,
|
||||
))
|
||||
})?;
|
||||
let mut context = EvalContext::new(self, global, caches, scope, this_ptr);
|
||||
|
||||
(custom_def.func)(&mut context, &expressions, &custom.state)
|
||||
@ -411,13 +428,11 @@ impl Engine {
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Expr::Index(..) => {
|
||||
self.eval_dot_index_chain(global, caches, scope, this_ptr, expr, &mut None)
|
||||
self.eval_dot_index_chain(global, caches, scope, this_ptr, expr, None)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Dot(..) => {
|
||||
self.eval_dot_index_chain(global, caches, scope, this_ptr, expr, &mut None)
|
||||
}
|
||||
Expr::Dot(..) => self.eval_dot_index_chain(global, caches, scope, this_ptr, expr, None),
|
||||
|
||||
_ => unreachable!("expression cannot be evaluated: {:?}", expr),
|
||||
}
|
||||
|
@ -25,10 +25,11 @@ pub type GlobalConstants =
|
||||
pub struct GlobalRuntimeState {
|
||||
/// Names of imported [modules][crate::Module].
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
imports: crate::StaticVec<ImmutableString>,
|
||||
imports: Option<Box<crate::StaticVec<ImmutableString>>>,
|
||||
/// Stack of imported [modules][crate::Module].
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
modules: crate::StaticVec<crate::SharedModule>,
|
||||
modules: Option<Box<crate::StaticVec<crate::SharedModule>>>,
|
||||
|
||||
/// The current stack of loaded [modules][crate::Module] containing script-defined functions.
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub lib: crate::StaticVec<crate::SharedModule>,
|
||||
@ -57,9 +58,6 @@ pub struct GlobalRuntimeState {
|
||||
///
|
||||
/// When that happens, this flag is turned on.
|
||||
pub always_search_scope: bool,
|
||||
/// Function call hashes to index getters and setters.
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
fn_hash_indexing: (u64, u64),
|
||||
/// Embedded [module][crate::Module] resolver.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
pub embedded_module_resolver:
|
||||
@ -74,7 +72,7 @@ pub struct GlobalRuntimeState {
|
||||
pub tag: Dynamic,
|
||||
/// Debugging interface.
|
||||
#[cfg(feature = "debugging")]
|
||||
pub debugger: super::Debugger,
|
||||
pub(crate) debugger: Option<super::Debugger>,
|
||||
}
|
||||
|
||||
impl GlobalRuntimeState {
|
||||
@ -84,9 +82,9 @@ impl GlobalRuntimeState {
|
||||
pub fn new(engine: &Engine) -> Self {
|
||||
Self {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
imports: crate::StaticVec::new_const(),
|
||||
imports: None,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
modules: crate::StaticVec::new_const(),
|
||||
modules: None,
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
lib: crate::StaticVec::new_const(),
|
||||
source: None,
|
||||
@ -98,11 +96,6 @@ impl GlobalRuntimeState {
|
||||
always_search_scope: false,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
embedded_module_resolver: None,
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
fn_hash_indexing: (
|
||||
crate::calc_fn_hash(None, crate::engine::FN_IDX_GET, 2),
|
||||
crate::calc_fn_hash(None, crate::engine::FN_IDX_SET, 3),
|
||||
),
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
constants: None,
|
||||
@ -110,36 +103,28 @@ impl GlobalRuntimeState {
|
||||
tag: engine.default_tag().clone(),
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
debugger: crate::eval::Debugger::new(
|
||||
if engine.debugger.is_some() {
|
||||
crate::eval::DebuggerStatus::Init
|
||||
} else {
|
||||
crate::eval::DebuggerStatus::CONTINUE
|
||||
},
|
||||
match engine.debugger {
|
||||
Some((ref init, ..)) => init(engine),
|
||||
None => Dynamic::UNIT,
|
||||
},
|
||||
),
|
||||
debugger: engine.debugger.as_ref().map(|x| {
|
||||
crate::eval::Debugger::new(crate::eval::DebuggerStatus::Init, (x.0)(engine))
|
||||
}),
|
||||
}
|
||||
}
|
||||
/// Get the length of the stack of globally-imported [modules][crate::Module].
|
||||
///
|
||||
/// Not available under `no_module`.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn num_imports(&self) -> usize {
|
||||
self.modules.len()
|
||||
self.modules.as_ref().map_or(0, |m| m.len())
|
||||
}
|
||||
/// Get the globally-imported [module][crate::Module] at a particular index.
|
||||
///
|
||||
/// Not available under `no_module`.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn get_shared_import(&self, index: usize) -> Option<crate::SharedModule> {
|
||||
self.modules.get(index).cloned()
|
||||
self.modules.as_ref().and_then(|m| m.get(index).cloned())
|
||||
}
|
||||
/// Get a mutable reference to the globally-imported [module][crate::Module] at a
|
||||
/// particular index.
|
||||
@ -147,13 +132,13 @@ impl GlobalRuntimeState {
|
||||
/// Not available under `no_module`.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[allow(dead_code)]
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub(crate) fn get_shared_import_mut(
|
||||
&mut self,
|
||||
index: usize,
|
||||
) -> Option<&mut crate::SharedModule> {
|
||||
self.modules.get_mut(index)
|
||||
self.modules.as_mut().and_then(|m| m.get_mut(index))
|
||||
}
|
||||
/// Get the index of a globally-imported [module][crate::Module] by name.
|
||||
///
|
||||
@ -162,33 +147,45 @@ impl GlobalRuntimeState {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn find_import(&self, name: &str) -> Option<usize> {
|
||||
self.imports
|
||||
.iter()
|
||||
.rev()
|
||||
.position(|key| key.as_str() == name)
|
||||
.map(|i| self.imports.len() - 1 - i)
|
||||
self.imports.as_ref().and_then(|imports| {
|
||||
imports
|
||||
.iter()
|
||||
.rev()
|
||||
.position(|key| key.as_str() == name)
|
||||
.map(|i| imports.len() - 1 - i)
|
||||
})
|
||||
}
|
||||
/// Push an imported [module][crate::Module] onto the stack.
|
||||
///
|
||||
/// Not available under `no_module`.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub fn push_import(
|
||||
&mut self,
|
||||
name: impl Into<ImmutableString>,
|
||||
module: impl Into<crate::SharedModule>,
|
||||
) {
|
||||
self.imports.push(name.into());
|
||||
self.modules.push(module.into());
|
||||
self.imports
|
||||
.get_or_insert_with(|| crate::StaticVec::new_const().into())
|
||||
.push(name.into());
|
||||
|
||||
self.modules
|
||||
.get_or_insert_with(|| crate::StaticVec::new_const().into())
|
||||
.push(module.into());
|
||||
}
|
||||
/// Truncate the stack of globally-imported [modules][crate::Module] to a particular length.
|
||||
///
|
||||
/// Not available under `no_module`.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub fn truncate_imports(&mut self, size: usize) {
|
||||
self.imports.truncate(size);
|
||||
self.modules.truncate(size);
|
||||
if size == 0 {
|
||||
self.imports = None;
|
||||
self.modules = None;
|
||||
} else if self.imports.is_some() {
|
||||
self.imports.as_mut().unwrap().truncate(size);
|
||||
self.modules.as_mut().unwrap().truncate(size);
|
||||
}
|
||||
}
|
||||
/// Get an iterator to the stack of globally-imported [modules][crate::Module] in reverse order.
|
||||
///
|
||||
@ -198,8 +195,9 @@ impl GlobalRuntimeState {
|
||||
pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &crate::Module)> {
|
||||
self.imports
|
||||
.iter()
|
||||
.zip(self.modules.iter())
|
||||
.flat_map(|x| x.iter())
|
||||
.rev()
|
||||
.zip(self.modules.iter().flat_map(|x| x.iter()).rev())
|
||||
.map(|(name, module)| (name.as_str(), &**module))
|
||||
}
|
||||
/// Get an iterator to the stack of globally-imported [modules][crate::Module] in reverse order.
|
||||
@ -210,7 +208,11 @@ impl GlobalRuntimeState {
|
||||
pub(crate) fn iter_imports_raw(
|
||||
&self,
|
||||
) -> impl Iterator<Item = (&ImmutableString, &crate::SharedModule)> {
|
||||
self.imports.iter().zip(self.modules.iter()).rev()
|
||||
self.imports
|
||||
.iter()
|
||||
.flat_map(|x| x.iter())
|
||||
.rev()
|
||||
.zip(self.modules.iter().flat_map(|x| x.iter()).rev())
|
||||
}
|
||||
/// Get an iterator to the stack of globally-imported [modules][crate::Module] in forward order.
|
||||
///
|
||||
@ -220,18 +222,21 @@ impl GlobalRuntimeState {
|
||||
pub fn scan_imports_raw(
|
||||
&self,
|
||||
) -> impl Iterator<Item = (&ImmutableString, &crate::SharedModule)> {
|
||||
self.imports.iter().zip(self.modules.iter())
|
||||
self.imports
|
||||
.iter()
|
||||
.flat_map(|x| x.iter())
|
||||
.zip(self.modules.iter().flat_map(|x| x.iter()))
|
||||
}
|
||||
/// Can the particular function with [`Dynamic`] parameter(s) exist in the stack of
|
||||
/// globally-imported [modules][crate::Module]?
|
||||
///
|
||||
/// Not available under `no_module`.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn may_contain_dynamic_fn(&self, hash_script: u64) -> bool {
|
||||
self.modules
|
||||
.iter()
|
||||
.any(|m| m.may_contain_dynamic_fn(hash_script))
|
||||
self.modules.as_ref().map_or(false, |m| {
|
||||
m.iter().any(|m| m.may_contain_dynamic_fn(hash_script))
|
||||
})
|
||||
}
|
||||
/// Does the specified function hash key exist in the stack of globally-imported
|
||||
/// [modules][crate::Module]?
|
||||
@ -242,7 +247,9 @@ impl GlobalRuntimeState {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn contains_qualified_fn(&self, hash: u64) -> bool {
|
||||
self.modules.iter().any(|m| m.contains_qualified_fn(hash))
|
||||
self.modules
|
||||
.as_ref()
|
||||
.map_or(false, |m| m.iter().any(|m| m.contains_qualified_fn(hash)))
|
||||
}
|
||||
/// Get the specified function via its hash key from the stack of globally-imported
|
||||
/// [modules][crate::Module].
|
||||
@ -255,10 +262,11 @@ impl GlobalRuntimeState {
|
||||
&self,
|
||||
hash: u64,
|
||||
) -> Option<(&crate::func::CallableFunction, Option<&ImmutableString>)> {
|
||||
self.modules
|
||||
.iter()
|
||||
.rev()
|
||||
.find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw())))
|
||||
self.modules.as_ref().and_then(|m| {
|
||||
m.iter()
|
||||
.rev()
|
||||
.find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw())))
|
||||
})
|
||||
}
|
||||
/// Does the specified [`TypeId`][std::any::TypeId] iterator exist in the stack of
|
||||
/// globally-imported [modules][crate::Module]?
|
||||
@ -269,7 +277,9 @@ impl GlobalRuntimeState {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn contains_iter(&self, id: std::any::TypeId) -> bool {
|
||||
self.modules.iter().any(|m| m.contains_qualified_iter(id))
|
||||
self.modules
|
||||
.as_ref()
|
||||
.map_or(false, |m| m.iter().any(|m| m.contains_qualified_iter(id)))
|
||||
}
|
||||
/// Get the specified [`TypeId`][std::any::TypeId] iterator from the stack of globally-imported
|
||||
/// [modules][crate::Module].
|
||||
@ -280,9 +290,8 @@ impl GlobalRuntimeState {
|
||||
#[must_use]
|
||||
pub fn get_iter(&self, id: std::any::TypeId) -> Option<&crate::func::IteratorFn> {
|
||||
self.modules
|
||||
.iter()
|
||||
.rev()
|
||||
.find_map(|m| m.get_qualified_iter(id))
|
||||
.as_ref()
|
||||
.and_then(|m| m.iter().rev().find_map(|m| m.get_qualified_iter(id)))
|
||||
}
|
||||
/// Get the current source.
|
||||
#[inline(always)]
|
||||
@ -297,19 +306,24 @@ impl GlobalRuntimeState {
|
||||
pub(crate) const fn source_raw(&self) -> Option<&ImmutableString> {
|
||||
self.source.as_ref()
|
||||
}
|
||||
/// Get the pre-calculated index getter hash.
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) fn hash_idx_get(&mut self) -> u64 {
|
||||
self.fn_hash_indexing.0
|
||||
|
||||
/// Return a reference to the debugging interface.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the debugging interface is not set.
|
||||
#[cfg(feature = "debugging")]
|
||||
pub fn debugger(&self) -> &super::Debugger {
|
||||
self.debugger.as_ref().unwrap()
|
||||
}
|
||||
/// Get the pre-calculated index setter hash.
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) fn hash_idx_set(&mut self) -> u64 {
|
||||
self.fn_hash_indexing.1
|
||||
/// Return a mutable reference to the debugging interface.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the debugging interface is not set.
|
||||
#[cfg(feature = "debugging")]
|
||||
pub fn debugger_mut(&mut self) -> &mut super::Debugger {
|
||||
self.debugger.as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@ -317,9 +331,16 @@ impl GlobalRuntimeState {
|
||||
impl<K: Into<ImmutableString>, M: Into<crate::SharedModule>> Extend<(K, M)> for GlobalRuntimeState {
|
||||
#[inline]
|
||||
fn extend<T: IntoIterator<Item = (K, M)>>(&mut self, iter: T) {
|
||||
let imports = self
|
||||
.imports
|
||||
.get_or_insert_with(|| crate::StaticVec::new_const().into());
|
||||
let modules = self
|
||||
.modules
|
||||
.get_or_insert_with(|| crate::StaticVec::new_const().into());
|
||||
|
||||
for (k, m) in iter {
|
||||
self.imports.push(k.into());
|
||||
self.modules.push(m.into());
|
||||
imports.push(k.into());
|
||||
modules.push(m.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -344,9 +365,6 @@ impl fmt::Debug for GlobalRuntimeState {
|
||||
.field("scope_level", &self.scope_level)
|
||||
.field("always_search_scope", &self.always_search_scope);
|
||||
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
f.field("fn_hash_indexing", &self.fn_hash_indexing);
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
f.field("constants", &self.constants);
|
||||
|
@ -21,7 +21,9 @@ pub use eval_context::EvalContext;
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub use global_state::GlobalConstants;
|
||||
pub use global_state::GlobalRuntimeState;
|
||||
pub use target::{calc_index, calc_offset_len, Target};
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
pub use target::calc_offset_len;
|
||||
pub use target::{calc_index, Target};
|
||||
|
||||
#[cfg(feature = "unchecked")]
|
||||
mod unchecked {
|
||||
|
@ -194,7 +194,9 @@ impl Engine {
|
||||
#[cfg(feature = "debugging")]
|
||||
let reset = self.run_debugger_with_reset(global, caches, scope, this_ptr, stmt)?;
|
||||
#[cfg(feature = "debugging")]
|
||||
let global = &mut *RestoreOnDrop::lock(global, move |g| g.debugger.reset_status(reset));
|
||||
let global = &mut *RestoreOnDrop::lock_if(reset.is_some(), global, move |g| {
|
||||
g.debugger_mut().reset_status(reset)
|
||||
});
|
||||
|
||||
// Coded this way for better branch prediction.
|
||||
// Popular branches are lifted out of the `match` statement into their own branches.
|
||||
@ -258,7 +260,7 @@ impl Engine {
|
||||
rhs_val = self.get_interned_string(value).into();
|
||||
}
|
||||
|
||||
let _new_val = &mut Some((rhs_val, op_info));
|
||||
let _new_val = Some((rhs_val, op_info));
|
||||
|
||||
// Must be either `var[index] op= val` or `var.prop op= val`
|
||||
match lhs {
|
||||
@ -500,7 +502,8 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
let func = func.or_else(|| global.get_iter(iter_type)).or_else(|| {
|
||||
self.global_sub_modules
|
||||
.values()
|
||||
.iter()
|
||||
.flat_map(|m| m.values())
|
||||
.find_map(|m| m.get_qualified_iter(iter_type))
|
||||
});
|
||||
|
||||
@ -533,6 +536,7 @@ impl Engine {
|
||||
let index_value = x as INT;
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
#[allow(clippy::absurd_extreme_comparisons)]
|
||||
if index_value > crate::MAX_USIZE_INT {
|
||||
return Err(ERR::ErrorArithmetic(
|
||||
format!("for-loop counter overflow: {x}"),
|
||||
@ -764,6 +768,8 @@ impl Engine {
|
||||
// Import statement
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
Stmt::Import(x, _pos) => {
|
||||
use crate::ModuleResolver;
|
||||
|
||||
let (expr, export) = &**x;
|
||||
|
||||
// Guard against too many modules
|
||||
@ -777,8 +783,6 @@ impl Engine {
|
||||
self.make_type_mismatch_err::<crate::ImmutableString>(typ, expr.position())
|
||||
})?;
|
||||
|
||||
use crate::ModuleResolver;
|
||||
|
||||
let path_pos = expr.start_position();
|
||||
|
||||
let resolver = global.embedded_module_resolver.clone();
|
||||
@ -799,10 +803,10 @@ impl Engine {
|
||||
Err(ERR::ErrorModuleNotFound(path.to_string(), path_pos).into())
|
||||
})?;
|
||||
|
||||
let (export, must_be_indexed) = if !export.is_empty() {
|
||||
(export.name.clone(), true)
|
||||
} else {
|
||||
let (export, must_be_indexed) = if export.is_empty() {
|
||||
(self.const_empty_string(), false)
|
||||
} else {
|
||||
(export.name.clone(), true)
|
||||
};
|
||||
|
||||
if !must_be_indexed || module.is_indexed() {
|
||||
@ -824,13 +828,14 @@ impl Engine {
|
||||
Stmt::Export(x, ..) => {
|
||||
let (Ident { name, pos, .. }, Ident { name: alias, .. }) = &**x;
|
||||
// Mark scope variables as public
|
||||
if let Some(index) = scope.search(name) {
|
||||
let alias = if alias.is_empty() { name } else { alias }.clone();
|
||||
scope.add_alias_by_index(index, alias.into());
|
||||
Ok(Dynamic::UNIT)
|
||||
} else {
|
||||
Err(ERR::ErrorVariableNotFound(name.to_string(), *pos).into())
|
||||
}
|
||||
scope.search(name).map_or_else(
|
||||
|| Err(ERR::ErrorVariableNotFound(name.to_string(), *pos).into()),
|
||||
|index| {
|
||||
let alias = if alias.is_empty() { name } else { alias }.clone();
|
||||
scope.add_alias_by_index(index, alias);
|
||||
Ok(Dynamic::UNIT)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// Share statement
|
||||
@ -838,20 +843,21 @@ impl Engine {
|
||||
Stmt::Share(x) => {
|
||||
x.iter()
|
||||
.try_for_each(|(name, index, pos)| {
|
||||
if let Some(index) = index
|
||||
index
|
||||
.map(|n| scope.len() - n.get())
|
||||
.or_else(|| scope.search(name))
|
||||
{
|
||||
let val = scope.get_mut_by_index(index);
|
||||
.map_or_else(
|
||||
|| Err(ERR::ErrorVariableNotFound(name.to_string(), *pos).into()),
|
||||
|index| {
|
||||
let val = scope.get_mut_by_index(index);
|
||||
|
||||
if !val.is_shared() {
|
||||
// Replace the variable with a shared value.
|
||||
*val = std::mem::take(val).into_shared();
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ERR::ErrorVariableNotFound(name.to_string(), *pos).into())
|
||||
}
|
||||
if !val.is_shared() {
|
||||
// Replace the variable with a shared value.
|
||||
*val = std::mem::take(val).into_shared();
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
})
|
||||
.map(|_| Dynamic::UNIT)
|
||||
}
|
||||
|
@ -13,8 +13,13 @@ use std::{
|
||||
/// Negative starting positions count from the end.
|
||||
///
|
||||
/// Values going over bounds are limited to the actual length.
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
#[allow(
|
||||
clippy::cast_sign_loss,
|
||||
clippy::absurd_extreme_comparisons,
|
||||
clippy::cast_possible_truncation
|
||||
)]
|
||||
pub fn calc_offset_len(length: usize, start: crate::INT, len: crate::INT) -> (usize, usize) {
|
||||
let start = if start < 0 {
|
||||
let abs_start = start.unsigned_abs();
|
||||
@ -47,22 +52,26 @@ pub fn calc_offset_len(length: usize, start: crate::INT, len: crate::INT) -> (us
|
||||
/// Values going over bounds call the provided closure to return a default value or an error.
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
#[allow(
|
||||
clippy::cast_sign_loss,
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::absurd_extreme_comparisons
|
||||
)]
|
||||
pub fn calc_index<E>(
|
||||
length: usize,
|
||||
start: crate::INT,
|
||||
negative_count_from_end: bool,
|
||||
err_func: impl FnOnce() -> Result<usize, E>,
|
||||
) -> Result<usize, E> {
|
||||
if start < 0 {
|
||||
if negative_count_from_end {
|
||||
let abs_start = start.unsigned_abs() as usize;
|
||||
|
||||
// Count from end if negative
|
||||
if abs_start <= length {
|
||||
return Ok(length - abs_start);
|
||||
}
|
||||
}
|
||||
if start < 0 && negative_count_from_end {
|
||||
let abs_start = start.unsigned_abs();
|
||||
return Ok(if abs_start as u64 > crate::MAX_USIZE_INT as u64 {
|
||||
0
|
||||
} else {
|
||||
length - usize::min(abs_start as usize, length)
|
||||
});
|
||||
}
|
||||
|
||||
if start <= crate::MAX_USIZE_INT && (start as usize) < length {
|
||||
return Ok(start as usize);
|
||||
}
|
||||
@ -315,7 +324,10 @@ impl<'a> Target<'a> {
|
||||
|
||||
let value = &mut *source.write_lock::<crate::Blob>().expect("`Blob`");
|
||||
|
||||
value[*index] = (new_byte & 0x00ff) as u8;
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
{
|
||||
value[*index] = (new_byte & 0x00ff) as u8;
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::StringChar {
|
||||
|
@ -1,14 +1,18 @@
|
||||
//! Built-in implementations for common operators.
|
||||
|
||||
#![allow(clippy::float_cmp)]
|
||||
|
||||
use super::call::FnCallArgs;
|
||||
use super::native::FnBuiltin;
|
||||
#[allow(clippy::enum_glob_use)]
|
||||
use crate::tokenizer::{Token, Token::*};
|
||||
use crate::{
|
||||
Dynamic, ExclusiveRange, ImmutableString, InclusiveRange, NativeCallContext, RhaiResult, INT,
|
||||
Dynamic, ExclusiveRange, ImmutableString, InclusiveRange, NativeCallContext, RhaiResult,
|
||||
SmartString, INT,
|
||||
};
|
||||
use std::any::TypeId;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{any::TypeId, fmt::Write};
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::FLOAT;
|
||||
@ -67,13 +71,11 @@ fn is_numeric(type_id: TypeId) -> bool {
|
||||
|
||||
/// A function that returns `true`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn const_true_fn(_: NativeCallContext, _: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
Ok(Dynamic::TRUE)
|
||||
}
|
||||
/// A function that returns `false`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn const_false_fn(_: NativeCallContext, _: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
Ok(Dynamic::FALSE)
|
||||
}
|
||||
@ -142,6 +144,7 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
|
||||
if type1 == type2 {
|
||||
if type1 == TypeId::of::<INT>() {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
#[allow(clippy::wildcard_imports)]
|
||||
use crate::packages::arithmetic::arith_basic::INT::functions::*;
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
@ -241,7 +244,9 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
|
||||
let x = args[0].as_char().expect(BUILTIN);
|
||||
let y = args[1].as_char().expect(BUILTIN);
|
||||
|
||||
let result = format!("{x}{y}");
|
||||
let mut result = SmartString::new_const();
|
||||
result.push(x);
|
||||
result.push(y);
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
_ctx.engine()
|
||||
@ -337,6 +342,7 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
|
||||
($x:ty, $xx:ident, $y:ty, $yy:ident) => {
|
||||
if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
#[allow(clippy::wildcard_imports)]
|
||||
use crate::packages::arithmetic::decimal_functions::builtin::*;
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
@ -399,7 +405,10 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
|
||||
Plus => Some(|_ctx, args| {
|
||||
let x = args[0].as_char().expect(BUILTIN);
|
||||
let y = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||
let result = format!("{x}{y}");
|
||||
|
||||
let mut result = SmartString::new_const();
|
||||
result.push(x);
|
||||
result.push_str(y);
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
_ctx.engine()
|
||||
@ -523,24 +532,20 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
|
||||
}
|
||||
|
||||
// Handle ranges here because ranges are implemented as custom type
|
||||
if type1 == TypeId::of::<ExclusiveRange>() {
|
||||
if type1 == type2 {
|
||||
return match op {
|
||||
EqualsTo => Some(impl_op!(ExclusiveRange == ExclusiveRange)),
|
||||
NotEqualsTo => Some(impl_op!(ExclusiveRange != ExclusiveRange)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
if type1 == TypeId::of::<ExclusiveRange>() && type1 == type2 {
|
||||
return match op {
|
||||
EqualsTo => Some(impl_op!(ExclusiveRange == ExclusiveRange)),
|
||||
NotEqualsTo => Some(impl_op!(ExclusiveRange != ExclusiveRange)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
||||
if type1 == TypeId::of::<InclusiveRange>() {
|
||||
if type1 == type2 {
|
||||
return match op {
|
||||
EqualsTo => Some(impl_op!(InclusiveRange == InclusiveRange)),
|
||||
NotEqualsTo => Some(impl_op!(InclusiveRange != InclusiveRange)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
if type1 == TypeId::of::<InclusiveRange>() && type1 == type2 {
|
||||
return match op {
|
||||
EqualsTo => Some(impl_op!(InclusiveRange == InclusiveRange)),
|
||||
NotEqualsTo => Some(impl_op!(InclusiveRange != InclusiveRange)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
||||
// One of the operands is a custom type, so it is never built-in
|
||||
@ -630,6 +635,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
|
||||
if type1 == type2 {
|
||||
if type1 == TypeId::of::<INT>() {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
#[allow(clippy::wildcard_imports)]
|
||||
use crate::packages::arithmetic::arith_basic::INT::functions::*;
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
@ -679,7 +685,12 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
|
||||
PlusAssign => Some(|_, args| {
|
||||
let y = args[1].as_char().expect(BUILTIN);
|
||||
let x = &mut *args[0].write_lock::<Dynamic>().expect(BUILTIN);
|
||||
Ok((*x = format!("{x}{y}").into()).into())
|
||||
|
||||
let mut buf = SmartString::new_const();
|
||||
write!(&mut buf, "{y}").unwrap();
|
||||
buf.push(y);
|
||||
|
||||
Ok((*x = buf.into()).into())
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
@ -713,6 +724,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
if type1 == TypeId::of::<crate::Array>() {
|
||||
#[allow(clippy::wildcard_imports)]
|
||||
use crate::packages::array_basic::array_functions::*;
|
||||
use crate::Array;
|
||||
|
||||
@ -744,6 +756,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
if type1 == TypeId::of::<crate::Blob>() {
|
||||
#[allow(clippy::wildcard_imports)]
|
||||
use crate::packages::blob_basic::blob_functions::*;
|
||||
use crate::Blob;
|
||||
|
||||
@ -794,6 +807,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
|
||||
($x:ident, $xx:ident, $y:ty, $yy:ident) => {
|
||||
if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
#[allow(clippy::wildcard_imports)]
|
||||
use crate::packages::arithmetic::decimal_functions::builtin::*;
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
@ -880,6 +894,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
|
||||
// array op= any
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
if type1 == TypeId::of::<crate::Array>() {
|
||||
#[allow(clippy::wildcard_imports)]
|
||||
use crate::packages::array_basic::array_functions::*;
|
||||
use crate::Array;
|
||||
|
||||
@ -909,6 +924,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
|
||||
|
||||
// blob op= int
|
||||
if (type1, type2) == (TypeId::of::<Blob>(), TypeId::of::<INT>()) {
|
||||
#[allow(clippy::wildcard_imports)]
|
||||
use crate::packages::blob_basic::blob_functions::*;
|
||||
|
||||
return match op {
|
||||
@ -928,6 +944,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
|
||||
|
||||
// blob op= char
|
||||
if (type1, type2) == (TypeId::of::<Blob>(), TypeId::of::<char>()) {
|
||||
#[allow(clippy::wildcard_imports)]
|
||||
use crate::packages::blob_basic::blob_functions::*;
|
||||
|
||||
return match op {
|
||||
@ -947,6 +964,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
|
||||
|
||||
// blob op= string
|
||||
if (type1, type2) == (TypeId::of::<Blob>(), TypeId::of::<ImmutableString>()) {
|
||||
#[allow(clippy::wildcard_imports)]
|
||||
use crate::packages::blob_basic::blob_functions::*;
|
||||
|
||||
return match op {
|
||||
|
@ -90,11 +90,7 @@ impl<'a> ArgBackup<'a> {
|
||||
/// exiting the current scope. Otherwise it is undefined behavior as the shorter lifetime will leak.
|
||||
#[inline(always)]
|
||||
pub fn restore_first_arg(&mut self, args: &mut FnCallArgs<'a>) {
|
||||
if let Some(p) = self.orig_mut.take() {
|
||||
args[0] = p;
|
||||
} else {
|
||||
unreachable!("`Some`");
|
||||
}
|
||||
args[0] = self.orig_mut.take().expect("`Some`");
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,7 +112,7 @@ pub fn ensure_no_data_race(fn_name: &str, args: &FnCallArgs, is_ref_mut: bool) -
|
||||
if let Some((n, ..)) = args
|
||||
.iter()
|
||||
.enumerate()
|
||||
.skip(if is_ref_mut { 1 } else { 0 })
|
||||
.skip(usize::from(is_ref_mut))
|
||||
.find(|(.., a)| a.is_locked())
|
||||
{
|
||||
return Err(ERR::ErrorDataRace(
|
||||
@ -216,7 +212,8 @@ impl Engine {
|
||||
} else {
|
||||
func.or_else(|| _global.get_qualified_fn(hash)).or_else(|| {
|
||||
self.global_sub_modules
|
||||
.values()
|
||||
.iter()
|
||||
.flat_map(|m| m.values())
|
||||
.find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw())))
|
||||
})
|
||||
};
|
||||
@ -256,7 +253,8 @@ impl Engine {
|
||||
|| _global.may_contain_dynamic_fn(hash_base)
|
||||
|| self
|
||||
.global_sub_modules
|
||||
.values()
|
||||
.iter()
|
||||
.flat_map(|m| m.values())
|
||||
.any(|m| m.may_contain_dynamic_fn(hash_base));
|
||||
|
||||
// Set maximum bitmask when there are dynamic versions of the function
|
||||
@ -277,7 +275,7 @@ impl Engine {
|
||||
Some(token) if token.is_op_assignment() => {
|
||||
let (first_arg, rest_args) = args.split_first().unwrap();
|
||||
|
||||
get_builtin_op_assignment_fn(token, *first_arg, rest_args[0])
|
||||
get_builtin_op_assignment_fn(token, first_arg, rest_args[0])
|
||||
.map(|f| FnResolutionCacheEntry {
|
||||
func: CallableFunction::Method(Shared::new(f)),
|
||||
source: None,
|
||||
@ -344,7 +342,7 @@ impl Engine {
|
||||
name: &str,
|
||||
op_token: Option<&Token>,
|
||||
hash: u64,
|
||||
mut args: &mut FnCallArgs,
|
||||
args: &mut FnCallArgs,
|
||||
is_ref_mut: bool,
|
||||
pos: Position,
|
||||
) -> RhaiResultOf<(Dynamic, bool)> {
|
||||
@ -368,7 +366,10 @@ impl Engine {
|
||||
|
||||
// Push a new call stack frame
|
||||
#[cfg(feature = "debugging")]
|
||||
let orig_call_stack_len = global.debugger.call_stack().len();
|
||||
let orig_call_stack_len = global
|
||||
.debugger
|
||||
.as_ref()
|
||||
.map_or(0, |dbg| dbg.call_stack().len());
|
||||
|
||||
let backup = &mut ArgBackup::new();
|
||||
|
||||
@ -381,14 +382,16 @@ impl Engine {
|
||||
}
|
||||
|
||||
let args =
|
||||
&mut *RestoreOnDrop::lock_if(swap, &mut args, move |a| backup.restore_first_arg(a));
|
||||
&mut *RestoreOnDrop::lock_if(swap, args, move |a| backup.restore_first_arg(a));
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
if self.debugger.is_some() {
|
||||
global.debugger.push_call_stack_frame(
|
||||
let source = source.clone().or_else(|| global.source.clone());
|
||||
|
||||
global.debugger_mut().push_call_stack_frame(
|
||||
self.get_interned_string(name),
|
||||
args.iter().map(|v| (*v).clone()).collect(),
|
||||
source.clone().or_else(|| global.source.clone()),
|
||||
source,
|
||||
pos,
|
||||
);
|
||||
}
|
||||
@ -414,16 +417,16 @@ impl Engine {
|
||||
};
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
{
|
||||
if self.debugger.is_some() {
|
||||
use crate::eval::{DebuggerEvent, DebuggerStatus};
|
||||
|
||||
let trigger = match global.debugger.status {
|
||||
let trigger = match global.debugger().status {
|
||||
DebuggerStatus::FunctionExit(n) => n >= global.level,
|
||||
DebuggerStatus::Next(.., true) => true,
|
||||
_ => false,
|
||||
};
|
||||
if trigger {
|
||||
let scope = &mut &mut Scope::new();
|
||||
let scope = &mut Scope::new();
|
||||
let mut this = Dynamic::NULL;
|
||||
let node = crate::ast::Stmt::Noop(pos);
|
||||
let node = (&node).into();
|
||||
@ -440,7 +443,7 @@ impl Engine {
|
||||
}
|
||||
|
||||
// Pop the call stack
|
||||
global.debugger.rewind_call_stack(orig_call_stack_len);
|
||||
global.debugger_mut().rewind_call_stack(orig_call_stack_len);
|
||||
}
|
||||
|
||||
let result = _result?;
|
||||
@ -597,12 +600,13 @@ impl Engine {
|
||||
let num_params = _args[1].as_int().expect("`INT`");
|
||||
|
||||
return Ok((
|
||||
if num_params < 0 || num_params > crate::MAX_USIZE_INT {
|
||||
false
|
||||
} else {
|
||||
if (0..=crate::MAX_USIZE_INT).contains(&num_params) {
|
||||
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
||||
let hash_script =
|
||||
calc_fn_hash(None, fn_name.as_str(), num_params as usize);
|
||||
self.has_script_fn(global, caches, hash_script)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
.into(),
|
||||
false,
|
||||
@ -647,12 +651,11 @@ impl Engine {
|
||||
}
|
||||
|
||||
let mut empty_scope;
|
||||
let scope = match _scope {
|
||||
Some(scope) => scope,
|
||||
None => {
|
||||
empty_scope = Scope::new();
|
||||
&mut empty_scope
|
||||
}
|
||||
let scope = if let Some(scope) = _scope {
|
||||
scope
|
||||
} else {
|
||||
empty_scope = Scope::new();
|
||||
&mut empty_scope
|
||||
};
|
||||
|
||||
let orig_source = mem::replace(&mut global.source, source.clone());
|
||||
@ -676,7 +679,7 @@ impl Engine {
|
||||
backup.change_first_arg_to_copy(_args);
|
||||
}
|
||||
|
||||
let args = &mut *RestoreOnDrop::lock_if(swap, &mut _args, move |a| {
|
||||
let args = &mut *RestoreOnDrop::lock_if(swap, _args, move |a| {
|
||||
backup.restore_first_arg(a)
|
||||
});
|
||||
|
||||
@ -718,11 +721,15 @@ impl Engine {
|
||||
|
||||
// Do not match function exit for arguments
|
||||
#[cfg(feature = "debugging")]
|
||||
let reset = global.debugger.clear_status_if(|status| {
|
||||
matches!(status, crate::eval::DebuggerStatus::FunctionExit(..))
|
||||
let reset = global.debugger.as_mut().and_then(|dbg| {
|
||||
dbg.clear_status_if(|status| {
|
||||
matches!(status, crate::eval::DebuggerStatus::FunctionExit(..))
|
||||
})
|
||||
});
|
||||
#[cfg(feature = "debugging")]
|
||||
let global = &mut *RestoreOnDrop::lock(global, move |g| g.debugger.reset_status(reset));
|
||||
let global = &mut *RestoreOnDrop::lock_if(reset.is_some(), global, move |g| {
|
||||
g.debugger_mut().reset_status(reset)
|
||||
});
|
||||
|
||||
self.eval_expr(global, caches, scope, this_ptr, arg_expr)
|
||||
.map(|r| (r, arg_expr.start_position()))
|
||||
@ -901,7 +908,7 @@ impl Engine {
|
||||
call_args = &mut _arg_values;
|
||||
}
|
||||
// Recalculate the hash based on the new function name and new arguments
|
||||
hash = if !is_anon && !is_valid_function_name(&fn_name) {
|
||||
hash = if !is_anon && !is_valid_function_name(fn_name) {
|
||||
FnCallHashes::from_native(calc_fn_hash(
|
||||
None,
|
||||
fn_name,
|
||||
@ -963,7 +970,7 @@ impl Engine {
|
||||
) -> RhaiResult {
|
||||
let mut first_arg = first_arg;
|
||||
let mut a_expr = args_expr;
|
||||
let mut total_args = if first_arg.is_some() { 1 } else { 0 } + a_expr.len();
|
||||
let mut total_args = usize::from(first_arg.is_some()) + a_expr.len();
|
||||
let mut curry = FnArgsVec::new_const();
|
||||
let mut name = fn_name;
|
||||
let mut hashes = hashes;
|
||||
@ -1077,9 +1084,10 @@ impl Engine {
|
||||
.as_int()
|
||||
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, arg_pos))?;
|
||||
|
||||
return Ok(if num_params < 0 || num_params > crate::MAX_USIZE_INT {
|
||||
return Ok(if !(0..=crate::MAX_USIZE_INT).contains(&num_params) {
|
||||
false
|
||||
} else {
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
let hash_script = calc_fn_hash(None, &fn_name, num_params as usize);
|
||||
self.has_script_fn(global, caches, hash_script)
|
||||
}
|
||||
@ -1437,7 +1445,7 @@ impl Engine {
|
||||
// No optimizations because we only run it once
|
||||
let ast = self.compile_with_scope_and_optimization_level(
|
||||
&Scope::new(),
|
||||
&[script],
|
||||
[script],
|
||||
#[cfg(not(feature = "no_optimize"))]
|
||||
OptimizationLevel::None,
|
||||
#[cfg(feature = "no_optimize")]
|
||||
|
@ -132,9 +132,7 @@ impl CallableFunction {
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
match self {
|
||||
Self::Pure(..) | Self::Method(..) => true,
|
||||
Self::Plugin(..) => true,
|
||||
Self::Iterator(..) => true,
|
||||
Self::Pure(..) | Self::Method(..) | Self::Plugin(..) | Self::Iterator(..) => true,
|
||||
Self::Script(..) => false,
|
||||
}
|
||||
}
|
||||
@ -147,8 +145,9 @@ impl CallableFunction {
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
match self {
|
||||
Self::Plugin(..) => FnAccess::Public,
|
||||
Self::Pure(..) | Self::Method(..) | Self::Iterator(..) => FnAccess::Public,
|
||||
Self::Plugin(..) | Self::Pure(..) | Self::Method(..) | Self::Iterator(..) => {
|
||||
FnAccess::Public
|
||||
}
|
||||
Self::Script(f) => f.access,
|
||||
}
|
||||
}
|
||||
|
@ -80,7 +80,6 @@ pub trait Func<ARGS, RET> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[must_use]
|
||||
fn create_from_script(self, script: &str, entry_point: &str) -> ParseResult<Self::Output>;
|
||||
}
|
||||
|
||||
|
@ -151,7 +151,7 @@ impl<'a> NativeCallContext<'a> {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn new_with_all_fields(
|
||||
pub const fn new_with_all_fields(
|
||||
engine: &'a Engine,
|
||||
fn_name: &'a str,
|
||||
source: Option<&'a str>,
|
||||
@ -182,7 +182,7 @@ impl<'a> NativeCallContext<'a> {
|
||||
Self {
|
||||
engine,
|
||||
fn_name: &context.fn_name,
|
||||
source: context.source.as_ref().map(String::as_str),
|
||||
source: context.source.as_deref(),
|
||||
global: &context.global,
|
||||
pos: context.pos,
|
||||
}
|
||||
@ -240,7 +240,7 @@ impl<'a> NativeCallContext<'a> {
|
||||
/// Custom state kept in a [`Dynamic`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn tag(&self) -> Option<&Dynamic> {
|
||||
pub const fn tag(&self) -> Option<&Dynamic> {
|
||||
Some(&self.global.tag)
|
||||
}
|
||||
/// Get an iterator over the current set of modules imported via `import` statements
|
||||
@ -278,7 +278,7 @@ impl<'a> NativeCallContext<'a> {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[inline]
|
||||
pub fn iter_namespaces(&self) -> impl Iterator<Item = &crate::Module> {
|
||||
self.global.lib.iter().map(|m| m.as_ref())
|
||||
self.global.lib.iter().map(AsRef::as_ref)
|
||||
}
|
||||
/// _(internals)_ The current stack of namespaces containing definitions of all script-defined functions.
|
||||
/// Exported under the `internals` feature only.
|
||||
|
@ -83,7 +83,10 @@ impl Engine {
|
||||
let orig_imports_len = global.num_imports();
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
let orig_call_stack_len = global.debugger.call_stack().len();
|
||||
let orig_call_stack_len = global
|
||||
.debugger
|
||||
.as_ref()
|
||||
.map_or(0, |dbg| dbg.call_stack().len());
|
||||
|
||||
// Put arguments into scope as variables
|
||||
scope.extend(fn_def.params.iter().cloned().zip(args.iter_mut().map(|v| {
|
||||
@ -94,10 +97,12 @@ impl Engine {
|
||||
// Push a new call stack frame
|
||||
#[cfg(feature = "debugging")]
|
||||
if self.debugger.is_some() {
|
||||
global.debugger.push_call_stack_frame(
|
||||
let source = global.source.clone();
|
||||
|
||||
global.debugger_mut().push_call_stack_frame(
|
||||
fn_def.name.clone(),
|
||||
scope.iter().skip(orig_scope_len).map(|(.., v)| v).collect(),
|
||||
global.source.clone(),
|
||||
source,
|
||||
pos,
|
||||
);
|
||||
}
|
||||
@ -126,7 +131,7 @@ impl Engine {
|
||||
};
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
{
|
||||
if self.debugger.is_some() {
|
||||
let node = crate::ast::Stmt::Noop(fn_def.body.position());
|
||||
self.run_debugger(global, caches, scope, this_ptr, &node)?;
|
||||
}
|
||||
@ -156,12 +161,13 @@ impl Engine {
|
||||
});
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
{
|
||||
let trigger = match global.debugger.status {
|
||||
if self.debugger.is_some() {
|
||||
let trigger = match global.debugger_mut().status {
|
||||
crate::eval::DebuggerStatus::FunctionExit(n) => n >= global.level,
|
||||
crate::eval::DebuggerStatus::Next(.., true) => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if trigger {
|
||||
let node = crate::ast::Stmt::Noop(fn_def.body.end_position().or_else(pos));
|
||||
let node = (&node).into();
|
||||
@ -176,7 +182,11 @@ impl Engine {
|
||||
}
|
||||
|
||||
// Pop the call stack
|
||||
global.debugger.rewind_call_stack(orig_call_stack_len);
|
||||
global
|
||||
.debugger
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.rewind_call_stack(orig_call_stack_len);
|
||||
}
|
||||
|
||||
// Remove all local variables and imported modules
|
||||
@ -226,7 +236,7 @@ impl Engine {
|
||||
// Then check imported modules
|
||||
global.contains_qualified_fn(hash_script)
|
||||
// Then check sub-modules
|
||||
|| self.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash_script));
|
||||
|| self.global_sub_modules.iter().flat_map(|m| m.values()).any(|m| m.contains_qualified_fn(hash_script));
|
||||
|
||||
if !result && !cache.filter.is_absent_and_set(hash_script) {
|
||||
// Do not cache "one-hit wonders"
|
||||
|
@ -65,11 +65,15 @@
|
||||
// #![warn(clippy::undocumented_unsafe_blocks)]
|
||||
#![allow(clippy::unit_arg)]
|
||||
#![allow(clippy::missing_errors_doc)]
|
||||
#![allow(clippy::missing_panics_doc)]
|
||||
#![allow(clippy::used_underscore_binding)]
|
||||
#![allow(clippy::inline_always)]
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
#![allow(clippy::negative_feature_names)]
|
||||
#![allow(clippy::module_inception)]
|
||||
#![allow(clippy::box_collection)]
|
||||
#![allow(clippy::too_many_arguments)]
|
||||
#![allow(clippy::upper_case_acronyms)]
|
||||
|
||||
#[cfg(feature = "no_std")]
|
||||
extern crate alloc;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -8,6 +8,7 @@ use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT,
|
||||
use crate::eval::{Caches, GlobalRuntimeState};
|
||||
use crate::func::builtin::get_builtin_binary_op_fn;
|
||||
use crate::func::hashing::get_hasher;
|
||||
use crate::module::ModuleFlags;
|
||||
use crate::tokenizer::Token;
|
||||
use crate::types::dynamic::AccessMode;
|
||||
use crate::{
|
||||
@ -168,7 +169,7 @@ fn has_native_fn_override(
|
||||
if engine
|
||||
.global_modules
|
||||
.iter()
|
||||
.filter(|m| !m.standard)
|
||||
.filter(|m| !m.flags.contains(ModuleFlags::STANDARD_LIB))
|
||||
.any(|m| m.contains_fn(hash))
|
||||
{
|
||||
return true;
|
||||
@ -178,7 +179,8 @@ fn has_native_fn_override(
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
if engine
|
||||
.global_sub_modules
|
||||
.values()
|
||||
.iter()
|
||||
.flat_map(|m| m.values())
|
||||
.any(|m| m.contains_qualified_fn(hash))
|
||||
{
|
||||
return true;
|
||||
|
@ -1,5 +1,6 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use crate::module::ModuleFlags;
|
||||
use crate::plugin::*;
|
||||
use crate::{def_package, Position, RhaiError, RhaiResultOf, ERR, INT};
|
||||
#[cfg(feature = "no_std")]
|
||||
@ -192,7 +193,7 @@ macro_rules! reg_functions {
|
||||
def_package! {
|
||||
/// Basic arithmetic package.
|
||||
pub ArithmeticPackage(lib) {
|
||||
lib.standard = true;
|
||||
lib.flags |= ModuleFlags::STANDARD_LIB;
|
||||
|
||||
combine_with_exported_module!(lib, "int", int_functions);
|
||||
reg_functions!(lib += signed_basic; INT);
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
use crate::engine::OP_EQUALS;
|
||||
use crate::eval::{calc_index, calc_offset_len};
|
||||
use crate::module::ModuleFlags;
|
||||
use crate::plugin::*;
|
||||
use crate::{
|
||||
def_package, Array, Dynamic, ExclusiveRange, FnPtr, InclusiveRange, NativeCallContext,
|
||||
@ -14,7 +15,7 @@ use std::{any::TypeId, cmp::Ordering, mem};
|
||||
def_package! {
|
||||
/// Package of basic array utilities.
|
||||
pub BasicArrayPackage(lib) {
|
||||
lib.standard = true;
|
||||
lib.flags |= ModuleFlags::STANDARD_LIB;
|
||||
|
||||
combine_with_exported_module!(lib, "array", array_functions);
|
||||
|
||||
@ -229,6 +230,8 @@ pub mod array_functions {
|
||||
// Check if array will be over max size limit
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
{
|
||||
use crate::types::dynamic::Union;
|
||||
|
||||
if _ctx.engine().max_array_size() > 0 && len > _ctx.engine().max_array_size() {
|
||||
return Err(
|
||||
ERR::ErrorDataTooLarge("Size of array".to_string(), Position::NONE).into(),
|
||||
@ -236,10 +239,10 @@ pub mod array_functions {
|
||||
}
|
||||
|
||||
let check_sizes = match item.0 {
|
||||
crate::types::dynamic::Union::Str(..) => true,
|
||||
crate::types::dynamic::Union::Array(..) => true,
|
||||
Union::Str(..) => true,
|
||||
Union::Array(..) => true,
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
crate::types::dynamic::Union::Map(..) => true,
|
||||
Union::Map(..) => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
@ -247,8 +250,8 @@ pub mod array_functions {
|
||||
let mut arr_len = array.len();
|
||||
let mut arr = Dynamic::from_array(mem::take(array));
|
||||
|
||||
let (mut a1, mut m1, mut s1) = crate::Engine::calc_data_sizes(&arr, true);
|
||||
let (a2, m2, s2) = crate::Engine::calc_data_sizes(&item, true);
|
||||
let (mut a1, mut m1, mut s1) = arr.calc_data_sizes(true);
|
||||
let (a2, m2, s2) = item.calc_data_sizes(true);
|
||||
|
||||
{
|
||||
let mut guard = arr.write_lock::<Array>().unwrap();
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::eval::calc_index;
|
||||
use crate::module::ModuleFlags;
|
||||
use crate::plugin::*;
|
||||
use crate::{
|
||||
def_package, ExclusiveRange, InclusiveRange, Position, RhaiResultOf, ERR, INT, INT_BITS,
|
||||
@ -10,7 +11,7 @@ use std::prelude::v1::*;
|
||||
def_package! {
|
||||
/// Package of basic bit-field utilities.
|
||||
pub BitFieldPackage(lib) {
|
||||
lib.standard = true;
|
||||
lib.flags |= ModuleFlags::STANDARD_LIB;
|
||||
|
||||
combine_with_exported_module!(lib, "bit_field", bit_field_functions);
|
||||
}
|
||||
@ -142,7 +143,7 @@ mod bit_field_functions {
|
||||
}
|
||||
|
||||
// 2^bits - 1
|
||||
let mask = ((2 as UNSIGNED_INT).pow(bits as u32) - 1) as crate::INT;
|
||||
let mask = ((2 as UNSIGNED_INT).pow(bits as u32) - 1) as INT;
|
||||
|
||||
Ok(((value & (mask << bit)) >> bit) & mask)
|
||||
}
|
||||
@ -229,7 +230,7 @@ mod bit_field_functions {
|
||||
}
|
||||
|
||||
// 2^bits - 1
|
||||
let mask = ((2 as UNSIGNED_INT).pow(bits as u32) - 1) as crate::INT;
|
||||
let mask = ((2 as UNSIGNED_INT).pow(bits as u32) - 1) as INT;
|
||||
|
||||
*value &= !(mask << bit);
|
||||
*value |= (new_value & mask) << bit;
|
||||
|
@ -1,10 +1,11 @@
|
||||
#![cfg(not(feature = "no_index"))]
|
||||
|
||||
use crate::eval::{calc_index, calc_offset_len};
|
||||
use crate::module::ModuleFlags;
|
||||
use crate::plugin::*;
|
||||
use crate::{
|
||||
def_package, Array, Blob, Dynamic, ExclusiveRange, InclusiveRange, NativeCallContext, Position,
|
||||
RhaiResultOf, INT, INT_BYTES, MAX_USIZE_INT,
|
||||
RhaiResultOf, ERR, INT, INT_BYTES, MAX_USIZE_INT,
|
||||
};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@ -16,7 +17,7 @@ use crate::{FLOAT, FLOAT_BYTES};
|
||||
def_package! {
|
||||
/// Package of basic BLOB utilities.
|
||||
pub BasicBlobPackage(lib) {
|
||||
lib.standard = true;
|
||||
lib.flags |= ModuleFlags::STANDARD_LIB;
|
||||
|
||||
combine_with_exported_module!(lib, "blob", blob_functions);
|
||||
combine_with_exported_module!(lib, "parse_int", parse_int_functions);
|
||||
@ -362,9 +363,7 @@ pub mod blob_functions {
|
||||
|
||||
// Check if blob will be over max size limit
|
||||
if _ctx.engine().max_array_size() > 0 && len > _ctx.engine().max_array_size() {
|
||||
return Err(
|
||||
crate::ERR::ErrorDataTooLarge("Size of BLOB".to_string(), Position::NONE).into(),
|
||||
);
|
||||
return Err(ERR::ErrorDataTooLarge("Size of BLOB".to_string(), Position::NONE).into());
|
||||
}
|
||||
|
||||
if len > blob.len() {
|
||||
|
@ -1,6 +1,7 @@
|
||||
#![cfg(feature = "debugging")]
|
||||
|
||||
use crate::def_package;
|
||||
use crate::module::ModuleFlags;
|
||||
use crate::plugin::*;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@ -17,7 +18,7 @@ use crate::Map;
|
||||
def_package! {
|
||||
/// Package of basic debugging utilities.
|
||||
pub DebuggingPackage(lib) {
|
||||
lib.standard = true;
|
||||
lib.flags |= ModuleFlags::STANDARD_LIB;
|
||||
|
||||
combine_with_exported_module!(lib, "debugging", debugging_functions);
|
||||
}
|
||||
@ -33,47 +34,57 @@ mod debugging_functions {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
pub fn back_trace(ctx: NativeCallContext) -> Array {
|
||||
ctx.global_runtime_state()
|
||||
.debugger
|
||||
.call_stack()
|
||||
.iter()
|
||||
.rev()
|
||||
.filter(|crate::debugger::CallStackFrame { fn_name, args, .. }| {
|
||||
fn_name.as_str() != "back_trace" || !args.is_empty()
|
||||
})
|
||||
.map(
|
||||
|frame @ crate::debugger::CallStackFrame {
|
||||
fn_name: _fn_name,
|
||||
args: _args,
|
||||
source: _source,
|
||||
pos: _pos,
|
||||
}| {
|
||||
let display = frame.to_string();
|
||||
use crate::debugger::CallStackFrame;
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
{
|
||||
let mut map = Map::new();
|
||||
map.insert("display".into(), display.into());
|
||||
map.insert("fn_name".into(), _fn_name.into());
|
||||
if !_args.is_empty() {
|
||||
map.insert("args".into(), Dynamic::from_array(_args.clone().to_vec()));
|
||||
if let Some(ref debugger) = ctx.global_runtime_state().debugger {
|
||||
debugger
|
||||
.call_stack()
|
||||
.iter()
|
||||
.rev()
|
||||
.filter(|CallStackFrame { fn_name, args, .. }| {
|
||||
fn_name.as_str() != "back_trace" || !args.is_empty()
|
||||
})
|
||||
.map(
|
||||
|frame @ CallStackFrame {
|
||||
fn_name: _fn_name,
|
||||
args: _args,
|
||||
source: _source,
|
||||
pos: _pos,
|
||||
}| {
|
||||
let display = frame.to_string();
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
{
|
||||
use crate::INT;
|
||||
|
||||
let mut map = Map::new();
|
||||
map.insert("display".into(), display.into());
|
||||
map.insert("fn_name".into(), _fn_name.into());
|
||||
if !_args.is_empty() {
|
||||
map.insert(
|
||||
"args".into(),
|
||||
Dynamic::from_array(_args.clone().to_vec()),
|
||||
);
|
||||
}
|
||||
if let Some(source) = _source {
|
||||
map.insert("source".into(), source.into());
|
||||
}
|
||||
if !_pos.is_none() {
|
||||
map.insert("line".into(), (_pos.line().unwrap() as INT).into());
|
||||
map.insert(
|
||||
"position".into(),
|
||||
(_pos.position().unwrap_or(0) as INT).into(),
|
||||
);
|
||||
}
|
||||
Dynamic::from_map(map)
|
||||
}
|
||||
if let Some(source) = _source {
|
||||
map.insert("source".into(), source.into());
|
||||
}
|
||||
if !_pos.is_none() {
|
||||
map.insert("line".into(), (_pos.line().unwrap() as crate::INT).into());
|
||||
map.insert(
|
||||
"position".into(),
|
||||
(_pos.position().unwrap_or(0) as crate::INT).into(),
|
||||
);
|
||||
}
|
||||
Dynamic::from_map(map)
|
||||
}
|
||||
#[cfg(feature = "no_object")]
|
||||
display.into()
|
||||
},
|
||||
)
|
||||
.collect()
|
||||
#[cfg(feature = "no_object")]
|
||||
display.into()
|
||||
},
|
||||
)
|
||||
.collect()
|
||||
} else {
|
||||
Array::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::module::ModuleFlags;
|
||||
use crate::plugin::*;
|
||||
use crate::{def_package, FnPtr, ImmutableString, NativeCallContext};
|
||||
#[cfg(feature = "no_std")]
|
||||
@ -6,7 +7,7 @@ use std::prelude::v1::*;
|
||||
def_package! {
|
||||
/// Package of basic function pointer utilities.
|
||||
pub BasicFnPackage(lib) {
|
||||
lib.standard = true;
|
||||
lib.flags |= ModuleFlags::STANDARD_LIB;
|
||||
|
||||
combine_with_exported_module!(lib, "FnPtr", fn_ptr_functions);
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
use crate::eval::calc_index;
|
||||
use crate::module::ModuleFlags;
|
||||
use crate::plugin::*;
|
||||
use crate::{
|
||||
def_package, ExclusiveRange, InclusiveRange, RhaiResultOf, INT, INT_BITS, MAX_USIZE_INT,
|
||||
def_package, ExclusiveRange, InclusiveRange, RhaiResultOf, ERR, INT, INT_BITS, MAX_USIZE_INT,
|
||||
};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@ -65,14 +66,11 @@ impl<T: Copy + PartialOrd> StepRange<T> {
|
||||
if let Some(n) = add(from, step) {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if n == from {
|
||||
return Err(crate::ERR::ErrorInFunctionCall(
|
||||
return Err(ERR::ErrorInFunctionCall(
|
||||
"range".to_string(),
|
||||
String::new(),
|
||||
crate::ERR::ErrorArithmetic(
|
||||
"step value cannot be zero".to_string(),
|
||||
Position::NONE,
|
||||
)
|
||||
.into(),
|
||||
ERR::ErrorArithmetic("step value cannot be zero".to_string(), Position::NONE)
|
||||
.into(),
|
||||
Position::NONE,
|
||||
)
|
||||
.into());
|
||||
@ -127,7 +125,7 @@ pub struct BitRange(INT, usize);
|
||||
impl BitRange {
|
||||
pub fn new(value: INT, from: INT, len: INT) -> RhaiResultOf<Self> {
|
||||
let from = calc_index(INT_BITS, from, true, || {
|
||||
crate::ERR::ErrorBitFieldBounds(INT_BITS, from, Position::NONE).into()
|
||||
ERR::ErrorBitFieldBounds(INT_BITS, from, Position::NONE).into()
|
||||
})?;
|
||||
|
||||
let len = if len < 0 {
|
||||
@ -329,7 +327,7 @@ macro_rules! reg_range {
|
||||
def_package! {
|
||||
/// Package of basic range iterators
|
||||
pub BasicIteratorPackage(lib) {
|
||||
lib.standard = true;
|
||||
lib.flags |= ModuleFlags::STANDARD_LIB;
|
||||
|
||||
reg_range!(lib | "range" => INT);
|
||||
|
||||
|
@ -1,14 +1,18 @@
|
||||
use crate::def_package;
|
||||
use crate::module::ModuleFlags;
|
||||
use crate::plugin::*;
|
||||
use crate::types::dynamic::Tag;
|
||||
use crate::{Dynamic, RhaiResultOf, ERR, INT};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::FLOAT;
|
||||
|
||||
def_package! {
|
||||
/// Package of core language features.
|
||||
pub LanguageCorePackage(lib) {
|
||||
lib.standard = true;
|
||||
lib.flags |= ModuleFlags::STANDARD_LIB;
|
||||
|
||||
combine_with_exported_module!(lib, "core", core_functions);
|
||||
|
||||
@ -75,10 +79,17 @@ mod core_functions {
|
||||
}
|
||||
|
||||
/// Block the current thread for a particular number of `seconds`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// // Do nothing for 10 seconds!
|
||||
/// sleep(10.0);
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[rhai_fn(name = "sleep")]
|
||||
pub fn sleep_float(seconds: crate::FLOAT) {
|
||||
pub fn sleep_float(seconds: FLOAT) {
|
||||
if seconds <= 0.0 {
|
||||
return;
|
||||
}
|
||||
@ -89,6 +100,13 @@ mod core_functions {
|
||||
std::thread::sleep(std::time::Duration::from_secs_f32(seconds));
|
||||
}
|
||||
/// Block the current thread for a particular number of `seconds`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// // Do nothing for 10 seconds!
|
||||
/// sleep(10);
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
pub fn sleep(seconds: INT) {
|
||||
if seconds <= 0 {
|
||||
@ -120,17 +138,24 @@ mod core_functions {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[export_module]
|
||||
mod reflection_functions {
|
||||
pub fn get_fn_metadata_list(ctx: NativeCallContext) -> crate::Array {
|
||||
use crate::Array;
|
||||
|
||||
/// Return an array of object maps containing metadata of all script-defined functions.
|
||||
pub fn get_fn_metadata_list(ctx: NativeCallContext) -> Array {
|
||||
collect_fn_metadata(ctx, |_, _, _, _, _| true)
|
||||
}
|
||||
/// Return an array of object maps containing metadata of all script-defined functions
|
||||
/// matching the specified name.
|
||||
#[rhai_fn(name = "get_fn_metadata_list")]
|
||||
pub fn get_fn_metadata(ctx: NativeCallContext, name: &str) -> crate::Array {
|
||||
pub fn get_fn_metadata(ctx: NativeCallContext, name: &str) -> Array {
|
||||
collect_fn_metadata(ctx, |_, _, n, _, _| n == name)
|
||||
}
|
||||
/// Return an array of object maps containing metadata of all script-defined functions
|
||||
/// matching the specified name and arity (number of parameters).
|
||||
#[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) -> Array {
|
||||
if params < 0 || params > crate::MAX_USIZE_INT {
|
||||
crate::Array::new()
|
||||
Array::new()
|
||||
} else {
|
||||
collect_fn_metadata(ctx, |_, _, n, p, _| p == (params as usize) && n == name)
|
||||
}
|
||||
@ -145,38 +170,47 @@ fn collect_fn_metadata(
|
||||
filter: impl Fn(FnNamespace, FnAccess, &str, usize, &crate::Shared<crate::ast::ScriptFnDef>) -> bool
|
||||
+ Copy,
|
||||
) -> crate::Array {
|
||||
use crate::{ast::ScriptFnDef, Array, Map};
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
use crate::Identifier;
|
||||
use crate::{ast::ScriptFnDef, engine::FN_ANONYMOUS, Array, Map};
|
||||
|
||||
// Create a metadata record for a function.
|
||||
fn make_metadata(
|
||||
dict: &mut crate::types::StringsInterner,
|
||||
#[cfg(not(feature = "no_module"))] namespace: crate::Identifier,
|
||||
engine: &Engine,
|
||||
#[cfg(not(feature = "no_module"))] namespace: Identifier,
|
||||
func: &ScriptFnDef,
|
||||
) -> Map {
|
||||
let mut map = Map::new();
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
if !namespace.is_empty() {
|
||||
map.insert("namespace".into(), dict.get(namespace).into());
|
||||
map.insert(
|
||||
"namespace".into(),
|
||||
engine.get_interned_string(namespace).into(),
|
||||
);
|
||||
}
|
||||
map.insert("name".into(), dict.get(func.name.as_str()).into());
|
||||
map.insert(
|
||||
"name".into(),
|
||||
engine.get_interned_string(func.name.clone()).into(),
|
||||
);
|
||||
map.insert(
|
||||
"access".into(),
|
||||
dict.get(match func.access {
|
||||
FnAccess::Public => "public",
|
||||
FnAccess::Private => "private",
|
||||
})
|
||||
.into(),
|
||||
engine
|
||||
.get_interned_string(match func.access {
|
||||
FnAccess::Public => "public",
|
||||
FnAccess::Private => "private",
|
||||
})
|
||||
.into(),
|
||||
);
|
||||
map.insert(
|
||||
"is_anonymous".into(),
|
||||
func.name.starts_with(crate::engine::FN_ANONYMOUS).into(),
|
||||
func.name.starts_with(FN_ANONYMOUS).into(),
|
||||
);
|
||||
map.insert(
|
||||
"params".into(),
|
||||
func.params
|
||||
.iter()
|
||||
.map(|p| dict.get(p.as_str()).into())
|
||||
.map(|p| engine.get_interned_string(p.clone()).into())
|
||||
.collect::<Array>()
|
||||
.into(),
|
||||
);
|
||||
@ -186,7 +220,7 @@ fn collect_fn_metadata(
|
||||
"comments".into(),
|
||||
func.comments
|
||||
.iter()
|
||||
.map(|s| dict.get(s.as_ref()).into())
|
||||
.map(|s| engine.get_interned_string(s.as_ref()).into())
|
||||
.collect::<Array>()
|
||||
.into(),
|
||||
);
|
||||
@ -195,7 +229,7 @@ fn collect_fn_metadata(
|
||||
map
|
||||
}
|
||||
|
||||
let dict = &mut crate::types::StringsInterner::new();
|
||||
let engine = ctx.engine();
|
||||
let mut list = Array::new();
|
||||
|
||||
ctx.iter_namespaces()
|
||||
@ -204,9 +238,9 @@ fn collect_fn_metadata(
|
||||
.for_each(|(.., f)| {
|
||||
list.push(
|
||||
make_metadata(
|
||||
dict,
|
||||
engine,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
crate::Identifier::new_const(),
|
||||
Identifier::new_const(),
|
||||
f,
|
||||
)
|
||||
.into(),
|
||||
@ -221,9 +255,9 @@ fn collect_fn_metadata(
|
||||
.for_each(|(.., f)| {
|
||||
list.push(
|
||||
make_metadata(
|
||||
dict,
|
||||
engine,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
crate::Identifier::new_const(),
|
||||
Identifier::new_const(),
|
||||
f,
|
||||
)
|
||||
.into(),
|
||||
@ -233,15 +267,16 @@ fn collect_fn_metadata(
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
ctx.engine()
|
||||
.global_sub_modules
|
||||
.values()
|
||||
.iter()
|
||||
.flat_map(|m| m.values())
|
||||
.flat_map(|m| m.iter_script_fn())
|
||||
.filter(|(ns, a, n, p, f)| filter(*ns, *a, n, *p, f))
|
||||
.for_each(|(.., f)| {
|
||||
list.push(
|
||||
make_metadata(
|
||||
dict,
|
||||
engine,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
crate::Identifier::new_const(),
|
||||
Identifier::new_const(),
|
||||
f,
|
||||
)
|
||||
.into(),
|
||||
@ -250,36 +285,31 @@ fn collect_fn_metadata(
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
{
|
||||
use crate::{tokenizer::Token::DoubleColon, Shared, SmartString};
|
||||
|
||||
// Recursively scan modules for script-defined functions.
|
||||
fn scan_module(
|
||||
dict: &mut crate::types::StringsInterner,
|
||||
engine: &Engine,
|
||||
list: &mut Array,
|
||||
namespace: &str,
|
||||
module: &Module,
|
||||
filter: impl Fn(
|
||||
FnNamespace,
|
||||
FnAccess,
|
||||
&str,
|
||||
usize,
|
||||
&crate::Shared<crate::ast::ScriptFnDef>,
|
||||
) -> bool
|
||||
+ Copy,
|
||||
filter: impl Fn(FnNamespace, FnAccess, &str, usize, &Shared<ScriptFnDef>) -> bool + Copy,
|
||||
) {
|
||||
module
|
||||
.iter_script_fn()
|
||||
.filter(|(s, a, n, p, f)| filter(*s, *a, n, *p, f))
|
||||
.for_each(|(.., f)| list.push(make_metadata(dict, namespace.into(), f).into()));
|
||||
for (ns, m) in module.iter_sub_modules() {
|
||||
let ns = format!(
|
||||
"{namespace}{}{ns}",
|
||||
crate::tokenizer::Token::DoubleColon.literal_syntax()
|
||||
);
|
||||
scan_module(dict, list, &ns, &**m, filter);
|
||||
.for_each(|(.., f)| list.push(make_metadata(engine, namespace.into(), f).into()));
|
||||
for (name, m) in module.iter_sub_modules() {
|
||||
use std::fmt::Write;
|
||||
|
||||
let mut ns = SmartString::new_const();
|
||||
write!(&mut ns, "{namespace}{}{name}", DoubleColon.literal_syntax()).unwrap();
|
||||
scan_module(engine, list, &ns, m, filter);
|
||||
}
|
||||
}
|
||||
|
||||
for (ns, m) in ctx.iter_imports_raw() {
|
||||
scan_module(dict, &mut list, ns, &**m, filter);
|
||||
scan_module(engine, &mut list, ns, m, filter);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::def_package;
|
||||
use crate::module::ModuleFlags;
|
||||
use crate::plugin::*;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@ -38,7 +39,7 @@ macro_rules! reg_functions {
|
||||
def_package! {
|
||||
/// Package of basic logic operators.
|
||||
pub LogicPackage(lib) {
|
||||
lib.standard = true;
|
||||
lib.flags |= ModuleFlags::STANDARD_LIB;
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
@ -92,6 +93,7 @@ mod logic_functions {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
#[export_module]
|
||||
mod f32_functions {
|
||||
use crate::INT;
|
||||
@ -147,6 +149,7 @@ mod f32_functions {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
#[export_module]
|
||||
mod f64_functions {
|
||||
use crate::INT;
|
||||
|
@ -1,6 +1,7 @@
|
||||
#![cfg(not(feature = "no_object"))]
|
||||
|
||||
use crate::engine::OP_EQUALS;
|
||||
use crate::module::ModuleFlags;
|
||||
use crate::plugin::*;
|
||||
use crate::{def_package, Dynamic, ImmutableString, Map, NativeCallContext, RhaiResultOf, INT};
|
||||
#[cfg(feature = "no_std")]
|
||||
@ -12,7 +13,7 @@ use crate::Array;
|
||||
def_package! {
|
||||
/// Package of basic object map utilities.
|
||||
pub BasicMapPackage(lib) {
|
||||
lib.standard = true;
|
||||
lib.flags |= ModuleFlags::STANDARD_LIB;
|
||||
|
||||
combine_with_exported_module!(lib, "map", map_functions);
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use crate::module::ModuleFlags;
|
||||
use crate::plugin::*;
|
||||
use crate::{def_package, Position, RhaiResultOf, ERR, INT};
|
||||
#[cfg(feature = "no_std")]
|
||||
@ -54,7 +55,7 @@ macro_rules! reg_functions {
|
||||
def_package! {
|
||||
/// Basic mathematical package.
|
||||
pub BasicMathPackage(lib) {
|
||||
lib.standard = true;
|
||||
lib.flags |= ModuleFlags::STANDARD_LIB;
|
||||
|
||||
// Integer functions
|
||||
combine_with_exported_module!(lib, "int", int_functions);
|
||||
@ -144,6 +145,7 @@ mod int_functions {
|
||||
);
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
||||
INT::from_str_radix(string.trim(), radix as u32).map_err(|err| {
|
||||
ERR::ErrorArithmetic(
|
||||
format!("Error parsing integer number '{string}': {err}"),
|
||||
@ -157,8 +159,6 @@ mod int_functions {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[export_module]
|
||||
mod trig_functions {
|
||||
use crate::FLOAT;
|
||||
|
||||
/// Return the sine of the floating-point number in radians.
|
||||
pub fn sin(x: FLOAT) -> FLOAT {
|
||||
x.sin()
|
||||
@ -221,8 +221,6 @@ mod trig_functions {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[export_module]
|
||||
mod float_functions {
|
||||
use crate::FLOAT;
|
||||
|
||||
/// Return the natural number _e_.
|
||||
#[rhai_fn(name = "E")]
|
||||
pub fn e() -> FLOAT {
|
||||
@ -312,6 +310,7 @@ mod float_functions {
|
||||
/// Convert the floating-point number into an integer.
|
||||
#[rhai_fn(name = "to_int", return_raw)]
|
||||
pub fn f32_to_int(x: f32) -> RhaiResultOf<INT> {
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
if cfg!(not(feature = "unchecked")) && (x > (INT::MAX as f32) || x < (INT::MIN as f32)) {
|
||||
Err(
|
||||
ERR::ErrorArithmetic(format!("Integer overflow: to_int({x})"), Position::NONE)
|
||||
@ -324,6 +323,7 @@ mod float_functions {
|
||||
/// Convert the floating-point number into an integer.
|
||||
#[rhai_fn(name = "to_int", return_raw)]
|
||||
pub fn f64_to_int(x: f64) -> RhaiResultOf<INT> {
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
if cfg!(not(feature = "unchecked")) && (x > (INT::MAX as f64) || x < (INT::MIN as f64)) {
|
||||
Err(
|
||||
ERR::ErrorArithmetic(format!("Integer overflow: to_int({x})"), Position::NONE)
|
||||
@ -356,7 +356,7 @@ mod float_functions {
|
||||
#[cfg(not(feature = "f32_float"))]
|
||||
#[rhai_fn(name = "to_float")]
|
||||
pub fn f32_to_f64(x: f32) -> f64 {
|
||||
x as f64
|
||||
x.into()
|
||||
}
|
||||
}
|
||||
|
||||
@ -477,6 +477,7 @@ mod decimal_functions {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
Ok(x.round_dp(digits as u32))
|
||||
}
|
||||
/// Round the decimal number to the specified number of `digits` after the decimal point and return it.
|
||||
@ -494,6 +495,7 @@ mod decimal_functions {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
Ok(x.round_dp_with_strategy(digits as u32, RoundingStrategy::AwayFromZero))
|
||||
}
|
||||
/// Round the decimal number to the specified number of `digits` after the decimal point and return it.
|
||||
@ -511,6 +513,7 @@ mod decimal_functions {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
Ok(x.round_dp_with_strategy(digits as u32, RoundingStrategy::ToZero))
|
||||
}
|
||||
/// Round the decimal number to the specified number of `digits` after the decimal point and return it.
|
||||
@ -528,6 +531,7 @@ mod decimal_functions {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
Ok(x.round_dp_with_strategy(digits as u32, RoundingStrategy::MidpointAwayFromZero))
|
||||
}
|
||||
/// Round the decimal number to the specified number of `digits` after the decimal point and return it.
|
||||
@ -545,6 +549,7 @@ mod decimal_functions {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
Ok(x.round_dp_with_strategy(digits as u32, RoundingStrategy::MidpointTowardZero))
|
||||
}
|
||||
/// Convert the decimal number into an integer.
|
||||
@ -562,14 +567,15 @@ mod decimal_functions {
|
||||
return Some(n);
|
||||
});
|
||||
|
||||
match n {
|
||||
Some(n) => Ok(n),
|
||||
_ => Err(ERR::ErrorArithmetic(
|
||||
format!("Integer overflow: to_int({x})"),
|
||||
Position::NONE,
|
||||
)
|
||||
.into()),
|
||||
}
|
||||
n.map_or_else(
|
||||
|| {
|
||||
Err(
|
||||
ERR::ErrorArithmetic(format!("Integer overflow: to_int({x})"), Position::NONE)
|
||||
.into(),
|
||||
)
|
||||
},
|
||||
Ok,
|
||||
)
|
||||
}
|
||||
/// Return the integral part of the decimal number.
|
||||
#[rhai_fn(name = "int", get = "int")]
|
||||
|
@ -3,6 +3,7 @@ use std::prelude::v1::*;
|
||||
|
||||
use super::*;
|
||||
use crate::def_package;
|
||||
use crate::module::ModuleFlags;
|
||||
|
||||
def_package! {
|
||||
/// Core package containing basic facilities.
|
||||
@ -23,6 +24,6 @@ def_package! {
|
||||
BasicFnPackage,
|
||||
#[cfg(feature = "debugging")] DebuggingPackage
|
||||
{
|
||||
lib.standard = true;
|
||||
lib.flags |= ModuleFlags::STANDARD_LIB;
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ use std::prelude::v1::*;
|
||||
|
||||
use super::*;
|
||||
use crate::def_package;
|
||||
use crate::module::ModuleFlags;
|
||||
|
||||
def_package! {
|
||||
/// Standard package containing all built-in features.
|
||||
@ -29,6 +30,6 @@ def_package! {
|
||||
#[cfg(not(feature = "no_time"))] BasicTimePackage,
|
||||
MoreStringPackage
|
||||
{
|
||||
lib.standard = true;
|
||||
lib.flags |= ModuleFlags::STANDARD_LIB;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
use crate::module::ModuleFlags;
|
||||
use crate::plugin::*;
|
||||
use crate::{def_package, FnPtr, INT};
|
||||
use crate::{def_package, FnPtr, ImmutableString, SmartString, INT};
|
||||
use std::any::TypeId;
|
||||
use std::fmt::{Binary, LowerHex, Octal};
|
||||
use std::fmt::{Binary, LowerHex, Octal, Write};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
@ -17,7 +18,7 @@ pub const FUNC_TO_DEBUG: &str = "to_debug";
|
||||
def_package! {
|
||||
/// Package of basic string utilities (e.g. printing)
|
||||
pub BasicStringPackage(lib) {
|
||||
lib.standard = true;
|
||||
lib.flags |= ModuleFlags::STANDARD_LIB;
|
||||
|
||||
combine_with_exported_module!(lib, "print_debug", print_debug_functions);
|
||||
combine_with_exported_module!(lib, "number_formatting", number_formatting);
|
||||
@ -25,7 +26,7 @@ def_package! {
|
||||
// Register characters iterator
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
lib.set_iter(TypeId::of::<ImmutableString>(), |value| Box::new(
|
||||
value.cast::<ImmutableString>().chars().map(Into::into).collect::<crate::Array>().into_iter()
|
||||
value.cast::<ImmutableString>().chars().map(Into::into).collect::<Array>().into_iter()
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -37,7 +38,7 @@ pub fn print_with_func(
|
||||
fn_name: &str,
|
||||
ctx: &NativeCallContext,
|
||||
value: &mut Dynamic,
|
||||
) -> crate::ImmutableString {
|
||||
) -> ImmutableString {
|
||||
match ctx.call_native_fn_raw(fn_name, true, &mut [value]) {
|
||||
Ok(result) if result.is_string() => {
|
||||
result.into_immutable_string().expect("`ImmutableString`")
|
||||
@ -49,8 +50,6 @@ pub fn print_with_func(
|
||||
|
||||
#[export_module]
|
||||
mod print_debug_functions {
|
||||
use crate::ImmutableString;
|
||||
|
||||
/// Convert the value of the `item` into a string.
|
||||
#[rhai_fn(name = "print", pure)]
|
||||
pub fn print_generic(ctx: NativeCallContext, item: &mut Dynamic) -> ImmutableString {
|
||||
@ -69,7 +68,9 @@ mod print_debug_functions {
|
||||
/// Convert the value of the `item` into a string in debug format.
|
||||
#[rhai_fn(name = "to_debug", pure)]
|
||||
pub fn to_debug_generic(ctx: NativeCallContext, item: &mut Dynamic) -> ImmutableString {
|
||||
ctx.engine().map_type_name(&format!("{item:?}")).into()
|
||||
let mut buf = SmartString::new_const();
|
||||
write!(&mut buf, "{item:?}").unwrap();
|
||||
ctx.engine().map_type_name(&buf).into()
|
||||
}
|
||||
|
||||
/// Return the empty string.
|
||||
@ -86,7 +87,9 @@ mod print_debug_functions {
|
||||
/// Convert the string into debug format.
|
||||
#[rhai_fn(name = "debug", name = "to_debug", pure)]
|
||||
pub fn debug_string(string: &mut ImmutableString) -> ImmutableString {
|
||||
format!("{string:?}").into()
|
||||
let mut buf = SmartString::new_const();
|
||||
write!(&mut buf, "{string:?}").unwrap();
|
||||
buf.into()
|
||||
}
|
||||
|
||||
/// Return the character into a string.
|
||||
@ -97,7 +100,9 @@ mod print_debug_functions {
|
||||
/// Convert the string into debug format.
|
||||
#[rhai_fn(name = "debug", name = "to_debug")]
|
||||
pub fn debug_char(character: char) -> ImmutableString {
|
||||
format!("{character:?}").into()
|
||||
let mut buf = SmartString::new_const();
|
||||
buf.push(character);
|
||||
buf.into()
|
||||
}
|
||||
|
||||
/// Convert the function pointer into a string in debug format.
|
||||
@ -114,7 +119,9 @@ mod print_debug_functions {
|
||||
/// Convert the boolean value into a string in debug format.
|
||||
#[rhai_fn(name = "debug", name = "to_debug")]
|
||||
pub fn debug_bool(value: bool) -> ImmutableString {
|
||||
format!("{value:?}").into()
|
||||
let mut buf = SmartString::new_const();
|
||||
write!(&mut buf, "{value:?}").unwrap();
|
||||
buf.into()
|
||||
}
|
||||
|
||||
/// Return the empty string.
|
||||
@ -147,14 +154,18 @@ mod print_debug_functions {
|
||||
#[rhai_fn(name = "debug", name = "to_debug")]
|
||||
pub fn debug_f64(number: f64) -> ImmutableString {
|
||||
let number = crate::types::FloatWrapper::new(number);
|
||||
format!("{number:?}").into()
|
||||
let mut buf = SmartString::new_const();
|
||||
write!(&mut buf, "{number:?}").unwrap();
|
||||
buf.into()
|
||||
}
|
||||
/// Convert the value of `number` into a string.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[rhai_fn(name = "debug", name = "to_debug")]
|
||||
pub fn debug_f32(number: f32) -> ImmutableString {
|
||||
let number = crate::types::FloatWrapper::new(number);
|
||||
format!("{number:?}").into()
|
||||
let mut buf = SmartString::new_const();
|
||||
write!(&mut buf, "{number:?}").unwrap();
|
||||
buf.into()
|
||||
}
|
||||
|
||||
/// Convert the array into a string.
|
||||
@ -217,13 +228,19 @@ mod print_debug_functions {
|
||||
#[export_module]
|
||||
mod number_formatting {
|
||||
fn to_hex<T: LowerHex>(value: T) -> ImmutableString {
|
||||
format!("{value:x}").into()
|
||||
let mut buf = SmartString::new_const();
|
||||
write!(&mut buf, "{value:x}").unwrap();
|
||||
buf.into()
|
||||
}
|
||||
fn to_octal<T: Octal>(value: T) -> ImmutableString {
|
||||
format!("{value:o}").into()
|
||||
let mut buf = SmartString::new_const();
|
||||
write!(&mut buf, "{value:o}").unwrap();
|
||||
buf.into()
|
||||
}
|
||||
fn to_binary<T: Binary>(value: T) -> ImmutableString {
|
||||
format!("{value:b}").into()
|
||||
let mut buf = SmartString::new_const();
|
||||
write!(&mut buf, "{value:b}").unwrap();
|
||||
buf.into()
|
||||
}
|
||||
|
||||
/// Convert the `value` into a string in hex format.
|
||||
|
@ -1,7 +1,8 @@
|
||||
use crate::module::ModuleFlags;
|
||||
use crate::plugin::*;
|
||||
use crate::{
|
||||
def_package, Dynamic, ExclusiveRange, InclusiveRange, RhaiResultOf, StaticVec, INT,
|
||||
MAX_USIZE_INT,
|
||||
def_package, Dynamic, ExclusiveRange, ImmutableString, InclusiveRange, Position, RhaiResultOf,
|
||||
SmartString, StaticVec, ERR, INT, MAX_USIZE_INT,
|
||||
};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@ -12,7 +13,7 @@ use super::string_basic::{print_with_func, FUNC_TO_STRING};
|
||||
def_package! {
|
||||
/// Package of additional string utilities over [`BasicStringPackage`][super::BasicStringPackage]
|
||||
pub MoreStringPackage(lib) {
|
||||
lib.standard = true;
|
||||
lib.flags |= ModuleFlags::STANDARD_LIB;
|
||||
|
||||
combine_with_exported_module!(lib, "string", string_functions);
|
||||
}
|
||||
@ -20,8 +21,6 @@ def_package! {
|
||||
|
||||
#[export_module]
|
||||
mod string_functions {
|
||||
use crate::{ImmutableString, SmartString};
|
||||
|
||||
#[rhai_fn(name = "+", pure)]
|
||||
pub fn add_append(
|
||||
ctx: NativeCallContext,
|
||||
@ -33,7 +32,9 @@ mod string_functions {
|
||||
if s.is_empty() {
|
||||
string.clone()
|
||||
} else {
|
||||
format!("{string}{s}").into()
|
||||
let mut buf = SmartString::from(string.as_str());
|
||||
buf.push_str(&s);
|
||||
buf.into()
|
||||
}
|
||||
}
|
||||
#[rhai_fn(name = "+=", name = "append")]
|
||||
@ -41,7 +42,9 @@ mod string_functions {
|
||||
let s = print_with_func(FUNC_TO_STRING, &ctx, &mut item);
|
||||
|
||||
if !s.is_empty() {
|
||||
*string = format!("{string}{s}").into();
|
||||
let mut buf = SmartString::from(string.as_str());
|
||||
buf.push_str(&s);
|
||||
*string = buf.into();
|
||||
}
|
||||
}
|
||||
#[rhai_fn(name = "+", pure)]
|
||||
@ -74,7 +77,10 @@ mod string_functions {
|
||||
}
|
||||
#[rhai_fn(name = "+")]
|
||||
pub fn add_prepend_char(character: char, string: &str) -> ImmutableString {
|
||||
format!("{character}{string}").into()
|
||||
let mut buf = SmartString::new_const();
|
||||
buf.push(character);
|
||||
buf.push_str(string);
|
||||
buf.into()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "+")]
|
||||
@ -127,7 +133,7 @@ mod string_functions {
|
||||
}
|
||||
#[rhai_fn(name = "+=", name = "append")]
|
||||
pub fn add(string: &mut ImmutableString, utf8: Blob) {
|
||||
let mut s = crate::SmartString::from(string.as_str());
|
||||
let mut s = SmartString::from(string.as_str());
|
||||
if !utf8.is_empty() {
|
||||
s.push_str(&String::from_utf8_lossy(&utf8));
|
||||
*string = s.into();
|
||||
@ -147,6 +153,25 @@ mod string_functions {
|
||||
|
||||
s.into()
|
||||
}
|
||||
|
||||
/// Convert the string into an UTF-8 encoded byte-stream as a BLOB.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "朝には紅顔ありて夕べには白骨となる";
|
||||
///
|
||||
/// let bytes = text.to_blob();
|
||||
///
|
||||
/// print(bytes.len()); // prints 51
|
||||
/// ```
|
||||
pub fn to_blob(string: &str) -> Blob {
|
||||
if string.is_empty() {
|
||||
Blob::new()
|
||||
} else {
|
||||
string.as_bytes().into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the length of the string, in number of characters.
|
||||
@ -188,25 +213,6 @@ mod string_functions {
|
||||
string.len() as INT
|
||||
}
|
||||
}
|
||||
/// Convert the string into an UTF-8 encoded byte-stream as a BLOB.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "朝には紅顔ありて夕べには白骨となる";
|
||||
///
|
||||
/// let bytes = text.to_blob();
|
||||
///
|
||||
/// print(bytes.len()); // prints 51
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
pub fn to_blob(string: &str) -> crate::Blob {
|
||||
if string.is_empty() {
|
||||
crate::Blob::new()
|
||||
} else {
|
||||
string.as_bytes().into()
|
||||
}
|
||||
}
|
||||
/// Remove all occurrences of a sub-string from the string.
|
||||
///
|
||||
/// # Example
|
||||
@ -239,9 +245,10 @@ mod string_functions {
|
||||
/// Clear the string, making it empty.
|
||||
pub fn clear(string: &mut ImmutableString) {
|
||||
if !string.is_empty() {
|
||||
match string.get_mut() {
|
||||
Some(s) => s.clear(),
|
||||
_ => *string = ImmutableString::new(),
|
||||
if let Some(s) = string.get_mut() {
|
||||
s.clear()
|
||||
} else {
|
||||
*string = ImmutableString::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -265,6 +272,7 @@ mod string_functions {
|
||||
/// ```
|
||||
pub fn truncate(string: &mut ImmutableString, len: INT) {
|
||||
if len > 0 {
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
let len = len.min(MAX_USIZE_INT) as usize;
|
||||
let chars: StaticVec<_> = string.chars().collect();
|
||||
let copy = string.make_mut();
|
||||
@ -286,20 +294,17 @@ mod string_functions {
|
||||
/// print(text); // prints "hello"
|
||||
/// ```
|
||||
pub fn trim(string: &mut ImmutableString) {
|
||||
match string.get_mut() {
|
||||
Some(s) => {
|
||||
let trimmed = s.trim();
|
||||
if let Some(s) = string.get_mut() {
|
||||
let trimmed = s.trim();
|
||||
|
||||
if trimmed != s {
|
||||
*s = trimmed.into();
|
||||
}
|
||||
if trimmed != s {
|
||||
*s = trimmed.into();
|
||||
}
|
||||
None => {
|
||||
let trimmed = string.trim();
|
||||
} else {
|
||||
let trimmed = string.trim();
|
||||
|
||||
if trimmed != string {
|
||||
*string = trimmed.into();
|
||||
}
|
||||
if trimmed != string {
|
||||
*string = trimmed.into();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -536,7 +541,7 @@ mod string_functions {
|
||||
/// ```
|
||||
#[rhai_fn(name = "contains")]
|
||||
pub fn contains_char(string: &str, character: char) -> bool {
|
||||
string.contains(character).into()
|
||||
string.contains(character)
|
||||
}
|
||||
|
||||
/// Return `true` if the string starts with a specified string.
|
||||
@ -1219,11 +1224,9 @@ mod string_functions {
|
||||
|
||||
// Check if string will be over max size limit
|
||||
if _ctx.engine().max_string_size() > 0 && len > _ctx.engine().max_string_size() {
|
||||
return Err(crate::ERR::ErrorDataTooLarge(
|
||||
"Length of string".to_string(),
|
||||
crate::Position::NONE,
|
||||
)
|
||||
.into());
|
||||
return Err(
|
||||
ERR::ErrorDataTooLarge("Length of string".to_string(), Position::NONE).into(),
|
||||
);
|
||||
}
|
||||
|
||||
let orig_len = string.chars().count();
|
||||
@ -1237,11 +1240,9 @@ mod string_functions {
|
||||
|
||||
if _ctx.engine().max_string_size() > 0 && string.len() > _ctx.engine().max_string_size()
|
||||
{
|
||||
return Err(crate::ERR::ErrorDataTooLarge(
|
||||
"Length of string".to_string(),
|
||||
crate::Position::NONE,
|
||||
)
|
||||
.into());
|
||||
return Err(
|
||||
ERR::ErrorDataTooLarge("Length of string".to_string(), Position::NONE).into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1279,11 +1280,9 @@ mod string_functions {
|
||||
|
||||
// Check if string will be over max size limit
|
||||
if _ctx.engine().max_string_size() > 0 && len > _ctx.engine().max_string_size() {
|
||||
return Err(crate::ERR::ErrorDataTooLarge(
|
||||
"Length of string".to_string(),
|
||||
crate::Position::NONE,
|
||||
)
|
||||
.into());
|
||||
return Err(
|
||||
ERR::ErrorDataTooLarge("Length of string".to_string(), Position::NONE).into(),
|
||||
);
|
||||
}
|
||||
|
||||
let mut str_len = string.chars().count();
|
||||
@ -1304,11 +1303,9 @@ mod string_functions {
|
||||
|
||||
if _ctx.engine().max_string_size() > 0 && string.len() > _ctx.engine().max_string_size()
|
||||
{
|
||||
return Err(crate::ERR::ErrorDataTooLarge(
|
||||
"Length of string".to_string(),
|
||||
crate::Position::NONE,
|
||||
)
|
||||
.into());
|
||||
return Err(
|
||||
ERR::ErrorDataTooLarge("Length of string".to_string(), Position::NONE).into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1317,7 +1314,7 @@ mod string_functions {
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
pub mod arrays {
|
||||
use crate::{Array, ImmutableString};
|
||||
use crate::Array;
|
||||
|
||||
/// Split the string into two at the specified `index` position and return it both strings
|
||||
/// as an array.
|
||||
|
@ -1,6 +1,7 @@
|
||||
#![cfg(not(feature = "no_time"))]
|
||||
|
||||
use super::arithmetic::make_err as make_arithmetic_err;
|
||||
use crate::module::ModuleFlags;
|
||||
use crate::plugin::*;
|
||||
use crate::{def_package, Dynamic, RhaiResult, RhaiResultOf, INT};
|
||||
|
||||
@ -16,7 +17,7 @@ use instant::{Duration, Instant};
|
||||
def_package! {
|
||||
/// Package of basic timing utilities.
|
||||
pub BasicTimePackage(lib) {
|
||||
lib.standard = true;
|
||||
lib.flags |= ModuleFlags::STANDARD_LIB;
|
||||
|
||||
// Register date/time functions
|
||||
combine_with_exported_module!(lib, "time", time_functions);
|
||||
|
1011
src/parser.rs
1011
src/parser.rs
File diff suppressed because it is too large
Load Diff
@ -47,7 +47,7 @@ impl<'de> DynamicDeserializer<'de> {
|
||||
)
|
||||
}
|
||||
#[inline(always)]
|
||||
fn deserialize_int<V: Visitor<'de>>(self, v: crate::INT, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
fn deserialize_int<V: Visitor<'de>>(v: crate::INT, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
return visitor.visit_i64(v);
|
||||
#[cfg(feature = "only_i32")]
|
||||
@ -185,7 +185,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
|
||||
|
||||
fn deserialize_i8<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
if let Ok(v) = self.0.as_int() {
|
||||
self.deserialize_int(v, visitor)
|
||||
Self::deserialize_int(v, visitor)
|
||||
} else {
|
||||
self.0
|
||||
.downcast_ref::<i8>()
|
||||
@ -195,7 +195,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
|
||||
|
||||
fn deserialize_i16<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
if let Ok(v) = self.0.as_int() {
|
||||
self.deserialize_int(v, visitor)
|
||||
Self::deserialize_int(v, visitor)
|
||||
} else {
|
||||
self.0
|
||||
.downcast_ref::<i16>()
|
||||
@ -205,7 +205,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
|
||||
|
||||
fn deserialize_i32<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
if let Ok(v) = self.0.as_int() {
|
||||
self.deserialize_int(v, visitor)
|
||||
Self::deserialize_int(v, visitor)
|
||||
} else if cfg!(feature = "only_i32") {
|
||||
self.type_error()
|
||||
} else {
|
||||
@ -217,7 +217,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
|
||||
|
||||
fn deserialize_i64<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
if let Ok(v) = self.0.as_int() {
|
||||
self.deserialize_int(v, visitor)
|
||||
Self::deserialize_int(v, visitor)
|
||||
} else if cfg!(not(feature = "only_i32")) {
|
||||
self.type_error()
|
||||
} else {
|
||||
@ -229,7 +229,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
|
||||
|
||||
fn deserialize_i128<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
if let Ok(v) = self.0.as_int() {
|
||||
self.deserialize_int(v, visitor)
|
||||
Self::deserialize_int(v, visitor)
|
||||
} else if cfg!(not(feature = "only_i32")) {
|
||||
self.type_error()
|
||||
} else {
|
||||
@ -241,7 +241,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
|
||||
|
||||
fn deserialize_u8<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
if let Ok(v) = self.0.as_int() {
|
||||
self.deserialize_int(v, visitor)
|
||||
Self::deserialize_int(v, visitor)
|
||||
} else {
|
||||
self.0
|
||||
.downcast_ref::<u8>()
|
||||
@ -251,7 +251,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
|
||||
|
||||
fn deserialize_u16<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
if let Ok(v) = self.0.as_int() {
|
||||
self.deserialize_int(v, visitor)
|
||||
Self::deserialize_int(v, visitor)
|
||||
} else {
|
||||
self.0
|
||||
.downcast_ref::<u16>()
|
||||
@ -261,7 +261,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
|
||||
|
||||
fn deserialize_u32<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
if let Ok(v) = self.0.as_int() {
|
||||
self.deserialize_int(v, visitor)
|
||||
Self::deserialize_int(v, visitor)
|
||||
} else {
|
||||
self.0
|
||||
.downcast_ref::<u32>()
|
||||
@ -271,7 +271,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
|
||||
|
||||
fn deserialize_u64<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
if let Ok(v) = self.0.as_int() {
|
||||
self.deserialize_int(v, visitor)
|
||||
Self::deserialize_int(v, visitor)
|
||||
} else {
|
||||
self.0
|
||||
.downcast_ref::<u64>()
|
||||
@ -281,7 +281,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
|
||||
|
||||
fn deserialize_u128<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
if let Ok(v) = self.0.as_int() {
|
||||
self.deserialize_int(v, visitor)
|
||||
Self::deserialize_int(v, visitor)
|
||||
} else {
|
||||
self.0
|
||||
.downcast_ref::<u128>()
|
||||
@ -296,21 +296,21 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
|
||||
.downcast_ref::<f32>()
|
||||
.map_or_else(|| self.type_error(), |&x| _visitor.visit_f32(x));
|
||||
|
||||
#[cfg(feature = "no_float")]
|
||||
#[cfg(feature = "decimal")]
|
||||
#[allow(unreachable_code)]
|
||||
{
|
||||
use rust_decimal::prelude::ToPrimitive;
|
||||
#[cfg(feature = "decimal")]
|
||||
{
|
||||
use rust_decimal::prelude::ToPrimitive;
|
||||
|
||||
return self
|
||||
.0
|
||||
.downcast_ref::<rust_decimal::Decimal>()
|
||||
.and_then(|&x| x.to_f32())
|
||||
.map_or_else(|| self.type_error(), |v| _visitor.visit_f32(v));
|
||||
return self
|
||||
.0
|
||||
.downcast_ref::<rust_decimal::Decimal>()
|
||||
.and_then(|&x| x.to_f32())
|
||||
.map_or_else(|| self.type_error(), |v| _visitor.visit_f32(v));
|
||||
}
|
||||
|
||||
self.type_error_str("f32")
|
||||
}
|
||||
|
||||
#[cfg(feature = "no_float")]
|
||||
#[cfg(not(feature = "decimal"))]
|
||||
return self.type_error_str("f32");
|
||||
}
|
||||
|
||||
fn deserialize_f64<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> {
|
||||
@ -320,21 +320,21 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
|
||||
.downcast_ref::<f64>()
|
||||
.map_or_else(|| self.type_error(), |&x| _visitor.visit_f64(x));
|
||||
|
||||
#[cfg(feature = "no_float")]
|
||||
#[cfg(feature = "decimal")]
|
||||
#[allow(unreachable_code)]
|
||||
{
|
||||
use rust_decimal::prelude::ToPrimitive;
|
||||
#[cfg(feature = "decimal")]
|
||||
{
|
||||
use rust_decimal::prelude::ToPrimitive;
|
||||
|
||||
return self
|
||||
.0
|
||||
.downcast_ref::<rust_decimal::Decimal>()
|
||||
.and_then(|&x| x.to_f64())
|
||||
.map_or_else(|| self.type_error(), |v| _visitor.visit_f64(v));
|
||||
return self
|
||||
.0
|
||||
.downcast_ref::<rust_decimal::Decimal>()
|
||||
.and_then(|&x| x.to_f64())
|
||||
.map_or_else(|| self.type_error(), |v| _visitor.visit_f64(v));
|
||||
}
|
||||
|
||||
self.type_error_str("f64")
|
||||
}
|
||||
|
||||
#[cfg(feature = "no_float")]
|
||||
#[cfg(not(feature = "decimal"))]
|
||||
return self.type_error_str("f64");
|
||||
}
|
||||
|
||||
fn deserialize_char<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
@ -517,10 +517,9 @@ impl<'de, ITER: Iterator<Item = &'de Dynamic>> serde::de::SeqAccess<'de>
|
||||
seed: T,
|
||||
) -> RhaiResultOf<Option<T::Value>> {
|
||||
// Deserialize each item coming out of the iterator.
|
||||
match self.iter.next() {
|
||||
Some(item) => seed.deserialize(item.into_deserializer()).map(Some),
|
||||
None => Ok(None),
|
||||
}
|
||||
self.iter.next().map_or(Ok(None), |item| {
|
||||
seed.deserialize(item.into_deserializer()).map(Some)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -553,10 +552,10 @@ impl<'de, K: Iterator<Item = &'de str>, V: Iterator<Item = &'de Dynamic>> serde:
|
||||
seed: S,
|
||||
) -> RhaiResultOf<Option<S::Value>> {
|
||||
// Deserialize each `Identifier` key coming out of the keys iterator.
|
||||
match self.keys.next().map(<_>::into_deserializer) {
|
||||
Some(d) => seed.deserialize(d).map(Some),
|
||||
None => Ok(None),
|
||||
}
|
||||
self.keys
|
||||
.next()
|
||||
.map(<_>::into_deserializer)
|
||||
.map_or(Ok(None), |d| seed.deserialize(d).map(Some))
|
||||
}
|
||||
|
||||
fn next_value_seed<S: serde::de::DeserializeSeed<'de>>(
|
||||
|
@ -9,6 +9,9 @@ use std::fmt;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
struct DynamicVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for DynamicVisitor {
|
||||
@ -38,14 +41,43 @@ impl<'de> Visitor<'de> for DynamicVisitor {
|
||||
#[inline]
|
||||
fn visit_i64<E: Error>(self, v: i64) -> Result<Self::Value, E> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
{
|
||||
Ok(v.into())
|
||||
}
|
||||
return Ok(v.into());
|
||||
|
||||
#[cfg(feature = "only_i32")]
|
||||
if v > i32::MAX as i64 {
|
||||
Ok(Dynamic::from(v))
|
||||
} else {
|
||||
self.visit_i32(v as i32)
|
||||
if v <= INT::MAX as i64 {
|
||||
return Ok(Dynamic::from(v as INT));
|
||||
}
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
{
|
||||
#[cfg(feature = "decimal")]
|
||||
if let Some(n) = rust_decimal::Decimal::from_i64(v) {
|
||||
return Ok(Dynamic::from_decimal(n));
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
return Ok(Dynamic::from_float(v as crate::FLOAT));
|
||||
|
||||
Err(Error::custom(format!("integer number too large: {v}")))
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn visit_i128<E: Error>(self, v: i128) -> Result<Self::Value, E> {
|
||||
if v <= i128::from(INT::MAX) {
|
||||
return Ok(Dynamic::from(v as INT));
|
||||
}
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
{
|
||||
#[cfg(feature = "decimal")]
|
||||
if let Some(n) = rust_decimal::Decimal::from_i128(v) {
|
||||
return Ok(Dynamic::from_decimal(n));
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
return Ok(Dynamic::from_float(v as crate::FLOAT));
|
||||
|
||||
Err(Error::custom(format!("integer number too large: {v}")))
|
||||
}
|
||||
}
|
||||
#[inline(always)]
|
||||
@ -59,57 +91,105 @@ impl<'de> Visitor<'de> for DynamicVisitor {
|
||||
#[inline]
|
||||
fn visit_u32<E: Error>(self, v: u32) -> Result<Self::Value, E> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
{
|
||||
Ok(INT::from(v).into())
|
||||
}
|
||||
return Ok(Dynamic::from(v as INT));
|
||||
|
||||
#[cfg(feature = "only_i32")]
|
||||
if v > i32::MAX as u32 {
|
||||
Ok(Dynamic::from(v))
|
||||
} else {
|
||||
self.visit_i32(v as i32)
|
||||
if v <= INT::MAX as u32 {
|
||||
return Ok(Dynamic::from(v as INT));
|
||||
}
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
{
|
||||
#[cfg(feature = "decimal")]
|
||||
if let Some(n) = rust_decimal::Decimal::from_u32(v) {
|
||||
return Ok(Dynamic::from_decimal(n));
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
return Ok(Dynamic::from_float(v as crate::FLOAT));
|
||||
|
||||
Err(Error::custom(format!("integer number too large: {v}")))
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn visit_u64<E: Error>(self, v: u64) -> Result<Self::Value, E> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
if v > i64::MAX as u64 {
|
||||
Ok(Dynamic::from(v))
|
||||
} else {
|
||||
self.visit_i64(v as i64)
|
||||
if v <= INT::MAX as u64 {
|
||||
return Ok(Dynamic::from(v as INT));
|
||||
}
|
||||
#[cfg(feature = "only_i32")]
|
||||
if v > i32::MAX as u64 {
|
||||
Ok(Dynamic::from(v))
|
||||
} else {
|
||||
self.visit_i32(v as i32)
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
if let Some(n) = rust_decimal::Decimal::from_u64(v) {
|
||||
return Ok(Dynamic::from_decimal(n));
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
return Ok(Dynamic::from_float(v as crate::FLOAT));
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
Err(Error::custom(format!("integer number too large: {v}")))
|
||||
}
|
||||
#[inline]
|
||||
fn visit_u128<E: Error>(self, v: u128) -> Result<Self::Value, E> {
|
||||
if v <= INT::MAX as u128 {
|
||||
return Ok(Dynamic::from(v as INT));
|
||||
}
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
if let Some(n) = rust_decimal::Decimal::from_u128(v) {
|
||||
return Ok(Dynamic::from_decimal(n));
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
return Ok(Dynamic::from_float(v as crate::FLOAT));
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
Err(Error::custom(format!("integer number too large: {v}")))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[inline(always)]
|
||||
fn visit_f32<E: Error>(self, v: f32) -> Result<Self::Value, E> {
|
||||
#[cfg(not(feature = "f32_float"))]
|
||||
return self.visit_f64(v as f64);
|
||||
#[cfg(feature = "f32_float")]
|
||||
return Ok(v.into());
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
return Ok((v as crate::FLOAT).into());
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
{
|
||||
#[cfg(feature = "decimal")]
|
||||
if let Some(n) = rust_decimal::Decimal::from_f32(v) {
|
||||
return Ok(Dynamic::from_decimal(n));
|
||||
}
|
||||
|
||||
Err(Error::custom(format!(
|
||||
"floating-point number is not supported: {v}"
|
||||
)))
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[inline(always)]
|
||||
fn visit_f64<E: Error>(self, v: f64) -> Result<Self::Value, E> {
|
||||
#[cfg(not(feature = "f32_float"))]
|
||||
return Ok(v.into());
|
||||
#[cfg(feature = "f32_float")]
|
||||
return self.visit_f32(v as f32);
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
return Ok((v as crate::FLOAT).into());
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
{
|
||||
#[cfg(feature = "decimal")]
|
||||
if let Some(n) = rust_decimal::Decimal::from_f64(v) {
|
||||
return Ok(Dynamic::from_decimal(n));
|
||||
}
|
||||
|
||||
Err(Error::custom(format!(
|
||||
"floating-point number is not supported: {v}"
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "no_float")]
|
||||
#[cfg(feature = "decimal")]
|
||||
#[inline]
|
||||
fn visit_f32<E: Error>(self, v: f32) -> Result<Self::Value, E> {
|
||||
use rust_decimal::Decimal;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
Decimal::try_from(v)
|
||||
rust_decimal::Decimal::try_from(v)
|
||||
.map(|v| v.into())
|
||||
.map_err(Error::custom)
|
||||
}
|
||||
@ -117,10 +197,9 @@ impl<'de> Visitor<'de> for DynamicVisitor {
|
||||
#[cfg(feature = "decimal")]
|
||||
#[inline]
|
||||
fn visit_f64<E: Error>(self, v: f64) -> Result<Self::Value, E> {
|
||||
use rust_decimal::Decimal;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
Decimal::try_from(v)
|
||||
rust_decimal::Decimal::try_from(v)
|
||||
.map(|v| v.into())
|
||||
.map_err(Error::custom)
|
||||
}
|
||||
@ -216,10 +295,9 @@ impl<'de> Deserialize<'de> for Scope<'de> {
|
||||
where
|
||||
A: SeqAccess<'de>,
|
||||
{
|
||||
let mut scope = match access.size_hint() {
|
||||
Some(size) => Scope::with_capacity(size),
|
||||
None => Scope::new(),
|
||||
};
|
||||
let mut scope = access
|
||||
.size_hint()
|
||||
.map_or_else(Scope::new, Scope::with_capacity);
|
||||
|
||||
while let Some(ScopeEntry {
|
||||
name,
|
||||
|
@ -2,7 +2,7 @@
|
||||
#![cfg(feature = "metadata")]
|
||||
|
||||
use crate::api::type_names::format_type;
|
||||
use crate::module::{calc_native_fn_hash, FuncInfo};
|
||||
use crate::module::{calc_native_fn_hash, FuncInfo, ModuleFlags};
|
||||
use crate::{calc_fn_hash, Engine, FnAccess, SmartString, StaticVec, AST};
|
||||
use serde::Serialize;
|
||||
#[cfg(feature = "no_std")]
|
||||
@ -66,13 +66,13 @@ impl Ord for FnMetadata<'_> {
|
||||
|
||||
impl<'a> From<&'a FuncInfo> for FnMetadata<'a> {
|
||||
fn from(info: &'a FuncInfo) -> Self {
|
||||
let base_hash = calc_fn_hash(None, &info.name, info.num_params);
|
||||
let base_hash = calc_fn_hash(None, &info.metadata.name, info.metadata.num_params);
|
||||
let (typ, full_hash) = if info.func.is_script() {
|
||||
(FnType::Script, base_hash)
|
||||
} else {
|
||||
(
|
||||
FnType::Native,
|
||||
calc_native_fn_hash(None, &info.name, &info.param_types),
|
||||
calc_native_fn_hash(None, &info.metadata.name, &info.metadata.param_types),
|
||||
)
|
||||
};
|
||||
|
||||
@ -80,12 +80,13 @@ impl<'a> From<&'a FuncInfo> for FnMetadata<'a> {
|
||||
base_hash,
|
||||
full_hash,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
namespace: info.namespace,
|
||||
access: info.access,
|
||||
name: &info.name,
|
||||
namespace: info.metadata.namespace,
|
||||
access: info.metadata.access,
|
||||
name: &info.metadata.name,
|
||||
typ,
|
||||
num_params: info.num_params,
|
||||
num_params: info.metadata.num_params,
|
||||
params: info
|
||||
.metadata
|
||||
.params_info
|
||||
.iter()
|
||||
.map(|s| {
|
||||
@ -99,7 +100,7 @@ impl<'a> From<&'a FuncInfo> for FnMetadata<'a> {
|
||||
})
|
||||
.collect(),
|
||||
_dummy: None,
|
||||
return_type: format_type(&info.return_type, true),
|
||||
return_type: format_type(&info.metadata.return_type, true),
|
||||
signature: info.gen_signature().into(),
|
||||
doc_comments: if info.func.is_script() {
|
||||
#[cfg(feature = "no_function")]
|
||||
@ -114,7 +115,7 @@ impl<'a> From<&'a FuncInfo> for FnMetadata<'a> {
|
||||
.map(<_>::as_ref)
|
||||
.collect()
|
||||
} else {
|
||||
info.comments.iter().map(<_>::as_ref).collect()
|
||||
info.metadata.comments.iter().map(<_>::as_ref).collect()
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -170,14 +171,20 @@ pub fn gen_metadata_to_json(
|
||||
let mut global = ModuleMetadata::new();
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
for (name, m) in &engine.global_sub_modules {
|
||||
for (name, m) in engine.global_sub_modules.iter().flat_map(|m| m.iter()) {
|
||||
global.modules.insert(name, m.as_ref().into());
|
||||
}
|
||||
|
||||
let exclude_flags = if include_standard_packages {
|
||||
ModuleFlags::empty()
|
||||
} else {
|
||||
ModuleFlags::STANDARD_LIB
|
||||
};
|
||||
|
||||
engine
|
||||
.global_modules
|
||||
.iter()
|
||||
.filter(|m| include_standard_packages || !m.standard)
|
||||
.filter(|m| !m.flags.contains(exclude_flags))
|
||||
.flat_map(|m| m.iter_fn())
|
||||
.for_each(|f| {
|
||||
#[allow(unused_mut)]
|
||||
|
204
src/serde/ser.rs
204
src/serde/ser.rs
@ -1,6 +1,6 @@
|
||||
//! Implement serialization support of [`Dynamic`][crate::Dynamic] for [`serde`].
|
||||
|
||||
use crate::{Dynamic, Identifier, Position, RhaiError, RhaiResult, RhaiResultOf, ERR};
|
||||
use crate::{Dynamic, Identifier, Position, RhaiError, RhaiResult, RhaiResultOf, ERR, INT};
|
||||
use serde::ser::{
|
||||
Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple, SerializeTupleStruct,
|
||||
};
|
||||
@ -9,6 +9,9 @@ use std::fmt;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
/// Serializer for [`Dynamic`][crate::Dynamic].
|
||||
pub struct DynamicSerializer {
|
||||
/// Buffer to hold a temporary key.
|
||||
@ -37,8 +40,9 @@ impl DynamicSerializer {
|
||||
/// # #[cfg(not(feature = "no_index"))]
|
||||
/// # #[cfg(not(feature = "no_object"))]
|
||||
/// # #[cfg(not(feature = "no_float"))]
|
||||
/// # #[cfg(not(feature = "f32_float"))]
|
||||
/// # {
|
||||
/// use rhai::{Dynamic, Array, Map, INT};
|
||||
/// use rhai::{Dynamic, Array, Map};
|
||||
/// use rhai::serde::to_dynamic;
|
||||
/// use serde::Serialize;
|
||||
///
|
||||
@ -112,151 +116,166 @@ impl Serializer for &mut DynamicSerializer {
|
||||
|
||||
#[inline(always)]
|
||||
fn serialize_i8(self, v: i8) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
return self.serialize_i64(i64::from(v));
|
||||
#[cfg(feature = "only_i32")]
|
||||
return self.serialize_i32(i32::from(v));
|
||||
Ok(INT::from(v).into())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn serialize_i16(self, v: i16) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
return self.serialize_i64(i64::from(v));
|
||||
#[cfg(feature = "only_i32")]
|
||||
return self.serialize_i32(i32::from(v));
|
||||
Ok(INT::from(v).into())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn serialize_i32(self, v: i32) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
return self.serialize_i64(i64::from(v));
|
||||
#[cfg(feature = "only_i32")]
|
||||
return Ok(v.into());
|
||||
Ok(INT::from(v).into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_i64(self, v: i64) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
{
|
||||
Ok(v.into())
|
||||
}
|
||||
return Ok(v.into());
|
||||
|
||||
#[cfg(feature = "only_i32")]
|
||||
if v > i32::MAX as i64 {
|
||||
Ok(Dynamic::from(v))
|
||||
} else {
|
||||
self.serialize_i32(v as i32)
|
||||
if v <= INT::MAX as i64 {
|
||||
return Ok(Dynamic::from(v as INT));
|
||||
}
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
{
|
||||
#[cfg(feature = "decimal")]
|
||||
if let Some(n) = rust_decimal::Decimal::from_i64(v) {
|
||||
return Ok(Dynamic::from_decimal(n));
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
return Ok(Dynamic::from_float(v as crate::FLOAT));
|
||||
|
||||
Err(Error::custom(format!("integer number too large: {v}")))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_i128(self, v: i128) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
if v > i64::MAX as i128 {
|
||||
Ok(Dynamic::from(v))
|
||||
} else {
|
||||
self.serialize_i64(v as i64)
|
||||
if v <= i128::from(INT::MAX) {
|
||||
return Ok(Dynamic::from(v as INT));
|
||||
}
|
||||
#[cfg(feature = "only_i32")]
|
||||
if v > i32::MAX as i128 {
|
||||
Ok(Dynamic::from(v))
|
||||
} else {
|
||||
self.serialize_i32(v as i32)
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
{
|
||||
#[cfg(feature = "decimal")]
|
||||
if let Some(n) = rust_decimal::Decimal::from_i128(v) {
|
||||
return Ok(Dynamic::from_decimal(n));
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
return Ok(Dynamic::from_float(v as crate::FLOAT));
|
||||
|
||||
Err(Error::custom(format!("integer number too large: {v}")))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn serialize_u8(self, v: u8) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
return self.serialize_i64(i64::from(v));
|
||||
#[cfg(feature = "only_i32")]
|
||||
return self.serialize_i32(i32::from(v));
|
||||
Ok(INT::from(v).into())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn serialize_u16(self, v: u16) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
return self.serialize_i64(i64::from(v));
|
||||
#[cfg(feature = "only_i32")]
|
||||
return self.serialize_i32(i32::from(v));
|
||||
Ok(INT::from(v).into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_u32(self, v: u32) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
{
|
||||
self.serialize_i64(i64::from(v))
|
||||
}
|
||||
return Ok(Dynamic::from(v as INT));
|
||||
|
||||
#[cfg(feature = "only_i32")]
|
||||
if v > i32::MAX as u32 {
|
||||
Ok(Dynamic::from(v))
|
||||
} else {
|
||||
self.serialize_i32(v as i32)
|
||||
if v <= INT::MAX as u32 {
|
||||
return Ok(Dynamic::from(v as INT));
|
||||
}
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
{
|
||||
#[cfg(feature = "decimal")]
|
||||
if let Some(n) = rust_decimal::Decimal::from_u32(v) {
|
||||
return Ok(Dynamic::from_decimal(n));
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
return Ok(Dynamic::from_float(v as crate::FLOAT));
|
||||
|
||||
Err(Error::custom(format!("integer number too large: {v}")))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_u64(self, v: u64) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
if v > i64::MAX as u64 {
|
||||
Ok(Dynamic::from(v))
|
||||
} else {
|
||||
self.serialize_i64(v as i64)
|
||||
if v <= INT::MAX as u64 {
|
||||
return Ok(Dynamic::from(v as INT));
|
||||
}
|
||||
#[cfg(feature = "only_i32")]
|
||||
if v > i32::MAX as u64 {
|
||||
Ok(Dynamic::from(v))
|
||||
} else {
|
||||
self.serialize_i32(v as i32)
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
if let Some(n) = rust_decimal::Decimal::from_u64(v) {
|
||||
return Ok(Dynamic::from_decimal(n));
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
return Ok(Dynamic::from_float(v as crate::FLOAT));
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
Err(Error::custom(format!("integer number too large: {v}")))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_u128(self, v: u128) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
if v > i64::MAX as u128 {
|
||||
Ok(Dynamic::from(v))
|
||||
} else {
|
||||
self.serialize_i64(v as i64)
|
||||
if v <= INT::MAX as u128 {
|
||||
return Ok(Dynamic::from(v as INT));
|
||||
}
|
||||
#[cfg(feature = "only_i32")]
|
||||
if v > i32::MAX as u128 {
|
||||
Ok(Dynamic::from(v))
|
||||
} else {
|
||||
self.serialize_i32(v as i32)
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
if let Some(n) = rust_decimal::Decimal::from_u128(v) {
|
||||
return Ok(Dynamic::from_decimal(n));
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
return Ok(Dynamic::from_float(v as crate::FLOAT));
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
Err(Error::custom(format!("integer number too large: {v}")))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
fn serialize_f32(self, v: f32) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(any(not(feature = "no_float"), not(feature = "decimal")))]
|
||||
return Ok(Dynamic::from(v));
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
return Ok((v as crate::FLOAT).into());
|
||||
|
||||
#[cfg(feature = "no_float")]
|
||||
#[cfg(feature = "decimal")]
|
||||
#[allow(unreachable_code)]
|
||||
{
|
||||
use rust_decimal::Decimal;
|
||||
use std::convert::TryFrom;
|
||||
#[cfg(feature = "decimal")]
|
||||
if let Some(n) = rust_decimal::Decimal::from_f32(v) {
|
||||
return Ok(Dynamic::from_decimal(n));
|
||||
}
|
||||
|
||||
Decimal::try_from(v)
|
||||
.map(|v| v.into())
|
||||
.map_err(Error::custom)
|
||||
Err(Error::custom(format!(
|
||||
"floating-point number is not supported: {v}"
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
fn serialize_f64(self, v: f64) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(any(not(feature = "no_float"), not(feature = "decimal")))]
|
||||
return Ok(Dynamic::from(v));
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
return Ok((v as crate::FLOAT).into());
|
||||
|
||||
#[cfg(feature = "no_float")]
|
||||
#[cfg(feature = "decimal")]
|
||||
#[allow(unreachable_code)]
|
||||
{
|
||||
use rust_decimal::Decimal;
|
||||
use std::convert::TryFrom;
|
||||
#[cfg(feature = "decimal")]
|
||||
if let Some(n) = rust_decimal::Decimal::from_f64(v) {
|
||||
return Ok(Dynamic::from_decimal(n));
|
||||
}
|
||||
|
||||
Decimal::try_from(v)
|
||||
.map(|v| v.into())
|
||||
.map_err(Error::custom)
|
||||
Err(Error::custom(format!(
|
||||
"floating-point number is not supported: {v}"
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -332,10 +351,7 @@ impl Serializer for &mut DynamicSerializer {
|
||||
_value: &T,
|
||||
) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
{
|
||||
let content = to_dynamic(_value)?;
|
||||
make_variant(_variant, content)
|
||||
}
|
||||
return Ok(make_variant(_variant, to_dynamic(_value)?));
|
||||
#[cfg(feature = "no_object")]
|
||||
return Err(ERR::ErrorMismatchDataType(
|
||||
"".into(),
|
||||
@ -688,7 +704,7 @@ impl serde::ser::SerializeTupleVariant for TupleVariantSerializer {
|
||||
|
||||
#[inline]
|
||||
fn end(self) -> RhaiResultOf<Self::Ok> {
|
||||
make_variant(self.variant, self.array.into())
|
||||
Ok(make_variant(self.variant, self.array.into()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -716,14 +732,14 @@ impl serde::ser::SerializeStructVariant for StructVariantSerializer {
|
||||
|
||||
#[inline]
|
||||
fn end(self) -> RhaiResultOf<Self::Ok> {
|
||||
make_variant(self.variant, self.map.into())
|
||||
Ok(make_variant(self.variant, self.map.into()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[inline]
|
||||
fn make_variant(variant: &'static str, value: Dynamic) -> RhaiResult {
|
||||
fn make_variant(variant: &'static str, value: Dynamic) -> Dynamic {
|
||||
let mut map = crate::Map::new();
|
||||
map.insert(variant.into(), value);
|
||||
Ok(map.into())
|
||||
map.into()
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ impl Serialize for Dynamic {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(ref a, ..) => (**a).serialize(ser),
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Blob(ref a, ..) => ser.serialize_bytes(&**a),
|
||||
Union::Blob(ref a, ..) => ser.serialize_bytes(a),
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(ref m, ..) => {
|
||||
let mut map = ser.serialize_map(Some(m.len()))?;
|
||||
|
174
src/tokenizer.rs
174
src/tokenizer.rs
@ -9,7 +9,6 @@ use crate::{Engine, Identifier, LexError, SmartString, StaticVec, INT, UNSIGNED_
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
cell::RefCell,
|
||||
char, fmt,
|
||||
iter::{FusedIterator, Peekable},
|
||||
@ -591,14 +590,67 @@ pub enum Token {
|
||||
impl fmt::Display for Token {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(&self.syntax())
|
||||
#[allow(clippy::enum_glob_use)]
|
||||
use Token::*;
|
||||
|
||||
match self {
|
||||
IntegerConstant(i) => write!(f, "{i}"),
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
FloatConstant(v) => write!(f, "{v}"),
|
||||
#[cfg(feature = "decimal")]
|
||||
DecimalConstant(d) => write!(f, "{d}"),
|
||||
StringConstant(s) => write!(f, r#""{s}""#),
|
||||
InterpolatedString(..) => f.write_str("string"),
|
||||
CharConstant(c) => write!(f, "{c}"),
|
||||
Identifier(s) => f.write_str(s),
|
||||
Reserved(s) => f.write_str(s),
|
||||
#[cfg(not(feature = "no_custom_syntax"))]
|
||||
Custom(s) => f.write_str(s),
|
||||
LexError(err) => write!(f, "{err}"),
|
||||
Comment(s) => f.write_str(s),
|
||||
|
||||
EOF => f.write_str("{EOF}"),
|
||||
|
||||
token => f.write_str(token.literal_syntax()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Token {
|
||||
/// Is the token a literal symbol?
|
||||
#[must_use]
|
||||
pub const fn is_literal(&self) -> bool {
|
||||
#[allow(clippy::enum_glob_use)]
|
||||
use Token::*;
|
||||
|
||||
match self {
|
||||
IntegerConstant(..) => false,
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
FloatConstant(..) => false,
|
||||
#[cfg(feature = "decimal")]
|
||||
DecimalConstant(..) => false,
|
||||
StringConstant(..)
|
||||
| InterpolatedString(..)
|
||||
| CharConstant(..)
|
||||
| Identifier(..)
|
||||
| Reserved(..) => false,
|
||||
#[cfg(not(feature = "no_custom_syntax"))]
|
||||
Custom(..) => false,
|
||||
LexError(..) | Comment(..) => false,
|
||||
|
||||
EOF => false,
|
||||
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
/// Get the literal syntax of the token.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the token is not a literal symbol.
|
||||
#[must_use]
|
||||
pub const fn literal_syntax(&self) -> &'static str {
|
||||
#[allow(clippy::enum_glob_use)]
|
||||
use Token::*;
|
||||
|
||||
match self {
|
||||
@ -690,34 +742,7 @@ impl Token {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
As => "as",
|
||||
|
||||
_ => "ERROR: NOT A KEYWORD",
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the syntax of the token.
|
||||
#[must_use]
|
||||
pub fn syntax(&self) -> Cow<'static, str> {
|
||||
use Token::*;
|
||||
|
||||
match self {
|
||||
IntegerConstant(i) => i.to_string().into(),
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
FloatConstant(f) => f.to_string().into(),
|
||||
#[cfg(feature = "decimal")]
|
||||
DecimalConstant(d) => d.to_string().into(),
|
||||
StringConstant(s) => format!("\"{s}\"").into(),
|
||||
InterpolatedString(..) => "string".into(),
|
||||
CharConstant(c) => c.to_string().into(),
|
||||
Identifier(s) => s.to_string().into(),
|
||||
Reserved(s) => s.to_string().into(),
|
||||
#[cfg(not(feature = "no_custom_syntax"))]
|
||||
Custom(s) => s.to_string().into(),
|
||||
LexError(err) => err.to_string().into(),
|
||||
Comment(s) => s.to_string().into(),
|
||||
|
||||
EOF => "{EOF}".into(),
|
||||
|
||||
token => token.literal_syntax().into(),
|
||||
_ => panic!("token is not a literal symbol"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -802,6 +827,7 @@ impl Token {
|
||||
/// Reverse lookup a symbol token from a piece of syntax.
|
||||
#[must_use]
|
||||
pub fn lookup_symbol_from_syntax(syntax: &str) -> Option<Self> {
|
||||
#[allow(clippy::enum_glob_use)]
|
||||
use Token::*;
|
||||
|
||||
Some(match syntax {
|
||||
@ -941,21 +967,20 @@ impl Token {
|
||||
/// (not sure about `fn` name).
|
||||
#[must_use]
|
||||
pub const fn is_next_unary(&self) -> bool {
|
||||
#[allow(clippy::enum_glob_use)]
|
||||
use Token::*;
|
||||
|
||||
match self {
|
||||
LexError(..) |
|
||||
SemiColon | // ; - is unary
|
||||
Colon | // #{ foo: - is unary
|
||||
Comma | // ( ... , -expr ) - is unary
|
||||
//Period |
|
||||
//Elvis |
|
||||
//DoubleQuestion |
|
||||
//QuestionBracket |
|
||||
ExclusiveRange | // .. - is unary
|
||||
//Period |
|
||||
//Elvis |
|
||||
DoubleQuestion | // ?? - is unary
|
||||
ExclusiveRange | // .. - is unary
|
||||
InclusiveRange | // ..= - is unary
|
||||
LeftBrace | // { -expr } - is unary
|
||||
// RightBrace | { expr } - expr not unary & is closing
|
||||
// RightBrace | // { expr } - expr not unary & is closing
|
||||
LeftParen | // ( -expr ) - is unary
|
||||
// RightParen | // ( expr ) - expr not unary & is closing
|
||||
LeftBracket | // [ -expr ] - is unary
|
||||
@ -989,7 +1014,7 @@ impl Token {
|
||||
Pipe |
|
||||
Ampersand |
|
||||
If |
|
||||
//Do |
|
||||
//Do |
|
||||
While |
|
||||
Until |
|
||||
In |
|
||||
@ -1000,15 +1025,21 @@ impl Token {
|
||||
XOr |
|
||||
XOrAssign |
|
||||
Return |
|
||||
Throw => true,
|
||||
Throw => true,
|
||||
|
||||
_ => false,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
QuestionBracket => true, // ?[ - is unary
|
||||
|
||||
LexError(..) => true,
|
||||
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the precedence number of the token.
|
||||
#[must_use]
|
||||
pub const fn precedence(&self) -> Option<Precedence> {
|
||||
#[allow(clippy::enum_glob_use)]
|
||||
use Token::*;
|
||||
|
||||
Precedence::new(match self {
|
||||
@ -1041,6 +1072,7 @@ impl Token {
|
||||
/// Does an expression bind to the right (instead of left)?
|
||||
#[must_use]
|
||||
pub const fn is_bind_right(&self) -> bool {
|
||||
#[allow(clippy::enum_glob_use)]
|
||||
use Token::*;
|
||||
|
||||
match self {
|
||||
@ -1054,6 +1086,7 @@ impl Token {
|
||||
/// Is this token a standard symbol used in the language?
|
||||
#[must_use]
|
||||
pub const fn is_standard_symbol(&self) -> bool {
|
||||
#[allow(clippy::enum_glob_use)]
|
||||
use Token::*;
|
||||
|
||||
match self {
|
||||
@ -1080,6 +1113,7 @@ impl Token {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_standard_keyword(&self) -> bool {
|
||||
#[allow(clippy::enum_glob_use)]
|
||||
use Token::*;
|
||||
|
||||
match self {
|
||||
@ -1127,7 +1161,7 @@ impl Token {
|
||||
impl From<Token> for String {
|
||||
#[inline(always)]
|
||||
fn from(token: Token) -> Self {
|
||||
token.syntax().into()
|
||||
token.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1476,13 +1510,13 @@ pub fn get_next_token(
|
||||
|
||||
/// Test if the given character is a hex character.
|
||||
#[inline(always)]
|
||||
fn is_hex_digit(c: char) -> bool {
|
||||
const fn is_hex_digit(c: char) -> bool {
|
||||
matches!(c, 'a'..='f' | 'A'..='F' | '0'..='9')
|
||||
}
|
||||
|
||||
/// Test if the given character is a numeric digit.
|
||||
#[inline(always)]
|
||||
fn is_numeric_digit(c: char) -> bool {
|
||||
const fn is_numeric_digit(c: char) -> bool {
|
||||
matches!(c, '0'..='9')
|
||||
}
|
||||
|
||||
@ -1662,21 +1696,8 @@ fn get_next_token_inner(
|
||||
});
|
||||
|
||||
// Parse number
|
||||
return Some((
|
||||
if let Some(radix) = radix_base {
|
||||
let result = &result[2..];
|
||||
|
||||
UNSIGNED_INT::from_str_radix(&result, radix)
|
||||
.map(|v| v as INT)
|
||||
.map_or_else(
|
||||
|_| {
|
||||
Token::LexError(
|
||||
LERR::MalformedNumber(result.to_string()).into(),
|
||||
)
|
||||
},
|
||||
Token::IntegerConstant,
|
||||
)
|
||||
} else {
|
||||
let token = radix_base.map_or_else(
|
||||
|| {
|
||||
let num = INT::from_str(&result).map(Token::IntegerConstant);
|
||||
|
||||
// If integer parsing is unnecessary, try float instead
|
||||
@ -1705,8 +1726,23 @@ fn get_next_token_inner(
|
||||
Token::LexError(LERR::MalformedNumber(result.to_string()).into())
|
||||
})
|
||||
},
|
||||
num_pos,
|
||||
));
|
||||
|radix| {
|
||||
let result = &result[2..];
|
||||
|
||||
UNSIGNED_INT::from_str_radix(result, radix)
|
||||
.map(|v| v as INT)
|
||||
.map_or_else(
|
||||
|_| {
|
||||
Token::LexError(
|
||||
LERR::MalformedNumber(result.to_string()).into(),
|
||||
)
|
||||
},
|
||||
Token::IntegerConstant,
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
return Some((token, num_pos));
|
||||
}
|
||||
|
||||
// letter or underscore ...
|
||||
@ -1735,7 +1771,7 @@ fn get_next_token_inner(
|
||||
Some('\r') => {
|
||||
eat_next(stream, pos);
|
||||
// `\r\n
|
||||
if let Some('\n') = stream.peek_next() {
|
||||
if stream.peek_next() == Some('\n') {
|
||||
eat_next(stream, pos);
|
||||
}
|
||||
pos.new_line();
|
||||
@ -1763,7 +1799,7 @@ fn get_next_token_inner(
|
||||
// ' - character literal
|
||||
('\'', '\'') => {
|
||||
return Some((
|
||||
Token::LexError(LERR::MalformedChar("".to_string()).into()),
|
||||
Token::LexError(LERR::MalformedChar(String::new()).into()),
|
||||
start_pos,
|
||||
))
|
||||
}
|
||||
@ -1916,7 +1952,7 @@ fn get_next_token_inner(
|
||||
while let Some(c) = stream.get_next() {
|
||||
if c == '\r' {
|
||||
// \r\n
|
||||
if let Some('\n') = stream.peek_next() {
|
||||
if stream.peek_next() == Some('\n') {
|
||||
eat_next(stream, pos);
|
||||
}
|
||||
pos.new_line();
|
||||
@ -2399,7 +2435,7 @@ impl<'a> Iterator for TokenIterator<'a> {
|
||||
Some((Token::Reserved(s), pos)) => (match
|
||||
(s.as_str(),
|
||||
#[cfg(not(feature = "no_custom_syntax"))]
|
||||
(!self.engine.custom_keywords.is_empty() && self.engine.custom_keywords.contains_key(&*s)),
|
||||
self.engine.custom_keywords.as_ref().map_or(false, |m| m.contains_key(&*s)),
|
||||
#[cfg(feature = "no_custom_syntax")]
|
||||
false
|
||||
)
|
||||
@ -2436,7 +2472,7 @@ impl<'a> Iterator for TokenIterator<'a> {
|
||||
#[cfg(feature = "no_custom_syntax")]
|
||||
(.., true) => unreachable!("no custom operators"),
|
||||
// Reserved keyword that is not custom and disabled.
|
||||
(token, false) if !self.engine.disabled_symbols.is_empty() && self.engine.disabled_symbols.contains(token) => {
|
||||
(token, false) if self.engine.disabled_symbols.as_ref().map_or(false,|m| m.contains(token)) => {
|
||||
let msg = format!("reserved {} '{token}' is disabled", if is_valid_identifier(token) { "keyword"} else {"symbol"});
|
||||
Token::LexError(LERR::ImproperSymbol(s.to_string(), msg).into())
|
||||
},
|
||||
@ -2445,13 +2481,13 @@ impl<'a> Iterator for TokenIterator<'a> {
|
||||
}, pos),
|
||||
// Custom keyword
|
||||
#[cfg(not(feature = "no_custom_syntax"))]
|
||||
Some((Token::Identifier(s), pos)) if !self.engine.custom_keywords.is_empty() && self.engine.custom_keywords.contains_key(&*s) => {
|
||||
Some((Token::Identifier(s), pos)) if self.engine.custom_keywords.as_ref().map_or(false,|m| m.contains_key(&*s)) => {
|
||||
(Token::Custom(s), pos)
|
||||
}
|
||||
// Custom keyword/symbol - must be disabled
|
||||
#[cfg(not(feature = "no_custom_syntax"))]
|
||||
Some((token, pos)) if !self.engine.custom_keywords.is_empty() && self.engine.custom_keywords.contains_key(token.literal_syntax()) => {
|
||||
if !self.engine.disabled_symbols.is_empty() && self.engine.disabled_symbols.contains(token.literal_syntax()) {
|
||||
Some((token, pos)) if token.is_literal() && self.engine.custom_keywords.as_ref().map_or(false,|m| m.contains_key(token.literal_syntax())) => {
|
||||
if self.engine.disabled_symbols.as_ref().map_or(false,|m| m.contains(token.literal_syntax())) {
|
||||
// Disabled standard keyword/symbol
|
||||
(Token::Custom(Box::new(token.literal_syntax().into())), pos)
|
||||
} else {
|
||||
@ -2460,7 +2496,7 @@ impl<'a> Iterator for TokenIterator<'a> {
|
||||
}
|
||||
}
|
||||
// Disabled symbol
|
||||
Some((token, pos)) if !self.engine.disabled_symbols.is_empty() && self.engine.disabled_symbols.contains(token.literal_syntax()) => {
|
||||
Some((token, pos)) if token.is_literal() && self.engine.disabled_symbols.as_ref().map_or(false,|m| m.contains(token.literal_syntax())) => {
|
||||
(Token::Reserved(Box::new(token.literal_syntax().into())), pos)
|
||||
}
|
||||
// Normal symbol
|
||||
|
@ -105,7 +105,7 @@ impl Add<BloomFilterU64> for &BloomFilterU64 {
|
||||
impl AddAssign<Self> for BloomFilterU64 {
|
||||
#[inline(always)]
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
*self += &rhs
|
||||
*self += &rhs;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@ pub struct CustomTypeInfo {
|
||||
|
||||
/// _(internals)_ A collection of custom types.
|
||||
/// Exported under the `internals` feature only.
|
||||
#[derive(Clone, Hash, Default)]
|
||||
#[derive(Clone, Hash)]
|
||||
pub struct CustomTypesCollection(BTreeMap<Identifier, CustomTypeInfo>);
|
||||
|
||||
impl fmt::Debug for CustomTypesCollection {
|
||||
@ -25,6 +25,13 @@ impl fmt::Debug for CustomTypesCollection {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CustomTypesCollection {
|
||||
#[inline(always)]
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl CustomTypesCollection {
|
||||
/// Create a new [`CustomTypesCollection`].
|
||||
#[inline(always)]
|
||||
|
@ -158,7 +158,7 @@ impl<'d, T: Any + Clone> Deref for DynamicWriteLock<'d, T> {
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self.0 {
|
||||
DynamicWriteLockInner::Reference(ref reference) => *reference,
|
||||
DynamicWriteLockInner::Reference(ref reference) => reference,
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
DynamicWriteLockInner::Guard(ref guard) => guard.downcast_ref().expect(CHECKED),
|
||||
}
|
||||
@ -169,7 +169,7 @@ impl<'d, T: Any + Clone> DerefMut for DynamicWriteLock<'d, T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
match self.0 {
|
||||
DynamicWriteLockInner::Reference(ref mut reference) => *reference,
|
||||
DynamicWriteLockInner::Reference(ref mut reference) => reference,
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
DynamicWriteLockInner::Guard(ref mut guard) => guard.downcast_mut().expect(CHECKED),
|
||||
}
|
||||
@ -640,6 +640,7 @@ impl fmt::Debug for Dynamic {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::enum_glob_use)]
|
||||
use AccessMode::*;
|
||||
|
||||
impl Clone for Dynamic {
|
||||
@ -1088,7 +1089,7 @@ impl Dynamic {
|
||||
pub fn from<T: Variant + Clone>(value: T) -> Self {
|
||||
// Coded this way in order to maximally leverage potentials for dead-code removal.
|
||||
|
||||
reify!(value, |v: Dynamic| return v);
|
||||
reify!(value, |v: Self| return v);
|
||||
reify!(value, |v: INT| return v.into());
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
@ -1187,7 +1188,7 @@ impl Dynamic {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
self.flatten_in_place();
|
||||
|
||||
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||
if TypeId::of::<T>() == TypeId::of::<Self>() {
|
||||
return Some(reify!(self => T));
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<()>() {
|
||||
@ -1309,7 +1310,7 @@ impl Dynamic {
|
||||
#[must_use]
|
||||
pub fn cast<T: Any + Clone>(self) -> T {
|
||||
// Bail out early if the return type needs no cast
|
||||
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||
if TypeId::of::<T>() == TypeId::of::<Self>() {
|
||||
return reify!(self => T);
|
||||
}
|
||||
|
||||
@ -1710,10 +1711,9 @@ impl Dynamic {
|
||||
match self.0 {
|
||||
Union::Unit(..) => true,
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
|
||||
Union::Unit(..) => true,
|
||||
_ => false,
|
||||
},
|
||||
Union::Shared(ref cell, ..) => {
|
||||
matches!(crate::func::locked_read(cell).0, Union::Unit(..))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -1724,10 +1724,9 @@ impl Dynamic {
|
||||
match self.0 {
|
||||
Union::Int(..) => true,
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
|
||||
Union::Int(..) => true,
|
||||
_ => false,
|
||||
},
|
||||
Union::Shared(ref cell, ..) => {
|
||||
matches!(crate::func::locked_read(cell).0, Union::Int(..))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -1741,10 +1740,9 @@ impl Dynamic {
|
||||
match self.0 {
|
||||
Union::Float(..) => true,
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
|
||||
Union::Float(..) => true,
|
||||
_ => false,
|
||||
},
|
||||
Union::Shared(ref cell, ..) => {
|
||||
matches!(crate::func::locked_read(cell).0, Union::Float(..))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -1758,10 +1756,9 @@ impl Dynamic {
|
||||
match self.0 {
|
||||
Union::Decimal(..) => true,
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
|
||||
Union::Decimal(..) => true,
|
||||
_ => false,
|
||||
},
|
||||
Union::Shared(ref cell, ..) => {
|
||||
matches!(crate::func::locked_read(cell).0, Union::Decimal(..))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -1772,10 +1769,9 @@ impl Dynamic {
|
||||
match self.0 {
|
||||
Union::Bool(..) => true,
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
|
||||
Union::Bool(..) => true,
|
||||
_ => false,
|
||||
},
|
||||
Union::Shared(ref cell, ..) => {
|
||||
matches!(crate::func::locked_read(cell).0, Union::Bool(..))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -1786,10 +1782,9 @@ impl Dynamic {
|
||||
match self.0 {
|
||||
Union::Char(..) => true,
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
|
||||
Union::Char(..) => true,
|
||||
_ => false,
|
||||
},
|
||||
Union::Shared(ref cell, ..) => {
|
||||
matches!(crate::func::locked_read(cell).0, Union::Char(..))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -1800,10 +1795,9 @@ impl Dynamic {
|
||||
match self.0 {
|
||||
Union::Str(..) => true,
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
|
||||
Union::Str(..) => true,
|
||||
_ => false,
|
||||
},
|
||||
Union::Shared(ref cell, ..) => {
|
||||
matches!(crate::func::locked_read(cell).0, Union::Str(..))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -1817,10 +1811,9 @@ impl Dynamic {
|
||||
match self.0 {
|
||||
Union::Array(..) => true,
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
|
||||
Union::Array(..) => true,
|
||||
_ => false,
|
||||
},
|
||||
Union::Shared(ref cell, ..) => {
|
||||
matches!(crate::func::locked_read(cell).0, Union::Array(..))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -1834,10 +1827,9 @@ impl Dynamic {
|
||||
match self.0 {
|
||||
Union::Blob(..) => true,
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
|
||||
Union::Blob(..) => true,
|
||||
_ => false,
|
||||
},
|
||||
Union::Shared(ref cell, ..) => {
|
||||
matches!(crate::func::locked_read(cell).0, Union::Blob(..))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -1851,10 +1843,9 @@ impl Dynamic {
|
||||
match self.0 {
|
||||
Union::Map(..) => true,
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
|
||||
Union::Map(..) => true,
|
||||
_ => false,
|
||||
},
|
||||
Union::Shared(ref cell, ..) => {
|
||||
matches!(crate::func::locked_read(cell).0, Union::Map(..))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -1865,10 +1856,9 @@ impl Dynamic {
|
||||
match self.0 {
|
||||
Union::FnPtr(..) => true,
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
|
||||
Union::FnPtr(..) => true,
|
||||
_ => false,
|
||||
},
|
||||
Union::Shared(ref cell, ..) => {
|
||||
matches!(crate::func::locked_read(cell).0, Union::FnPtr(..))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -1882,10 +1872,9 @@ impl Dynamic {
|
||||
match self.0 {
|
||||
Union::TimeStamp(..) => true,
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
|
||||
Union::TimeStamp(..) => true,
|
||||
_ => false,
|
||||
},
|
||||
Union::Shared(ref cell, ..) => {
|
||||
matches!(crate::func::locked_read(cell).0, Union::TimeStamp(..))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -299,10 +299,7 @@ impl EvalAltResult {
|
||||
#[inline(never)]
|
||||
#[must_use]
|
||||
pub const fn is_pseudo_error(&self) -> bool {
|
||||
match self {
|
||||
Self::LoopBreak(..) | Self::Return(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, Self::LoopBreak(..) | Self::Return(..))
|
||||
}
|
||||
/// Can this error be caught?
|
||||
#[cold]
|
||||
@ -357,20 +354,17 @@ impl EvalAltResult {
|
||||
#[inline(never)]
|
||||
#[must_use]
|
||||
pub const fn is_system_exception(&self) -> bool {
|
||||
match self {
|
||||
Self::ErrorSystem(..) => true,
|
||||
Self::ErrorParsing(..) => true,
|
||||
|
||||
Self::ErrorCustomSyntax(..)
|
||||
| Self::ErrorTooManyOperations(..)
|
||||
| Self::ErrorTooManyModules(..)
|
||||
| Self::ErrorStackOverflow(..)
|
||||
| Self::ErrorDataTooLarge(..) => true,
|
||||
|
||||
Self::ErrorTerminated(..) => true,
|
||||
|
||||
_ => false,
|
||||
}
|
||||
matches!(
|
||||
self,
|
||||
Self::ErrorSystem(..)
|
||||
| Self::ErrorParsing(..)
|
||||
| Self::ErrorCustomSyntax(..)
|
||||
| Self::ErrorTooManyOperations(..)
|
||||
| Self::ErrorTooManyModules(..)
|
||||
| Self::ErrorStackOverflow(..)
|
||||
| Self::ErrorDataTooLarge(..)
|
||||
| Self::ErrorTerminated(..)
|
||||
)
|
||||
}
|
||||
/// Get the [position][Position] of this error.
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
@ -459,7 +453,6 @@ impl EvalAltResult {
|
||||
/// Unwrap this error and get the very base error.
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
#[must_use]
|
||||
pub fn unwrap_inner(&self) -> &Self {
|
||||
match self {
|
||||
Self::ErrorInFunctionCall(.., err, _) | Self::ErrorInModule(.., err, _) => {
|
||||
|
@ -14,7 +14,7 @@ use num_traits::float::FloatCore as Float;
|
||||
/// A type that wraps a floating-point number and implements [`Hash`].
|
||||
///
|
||||
/// Not available under `no_float`.
|
||||
#[derive(Clone, Copy, PartialEq, PartialOrd)]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, PartialOrd)]
|
||||
pub struct FloatWrapper<F>(F);
|
||||
|
||||
impl Hash for FloatWrapper<crate::FLOAT> {
|
||||
|
@ -147,7 +147,6 @@ impl FromStr for ImmutableString {
|
||||
type Err = ();
|
||||
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let s: SmartString = s.into();
|
||||
Ok(Self(s.into()))
|
||||
|
@ -12,7 +12,6 @@ use std::prelude::v1::*;
|
||||
use std::{
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
marker::PhantomData,
|
||||
ops::AddAssign,
|
||||
};
|
||||
|
||||
@ -24,28 +23,23 @@ pub const MAX_STRING_LEN: usize = 24;
|
||||
|
||||
/// _(internals)_ A cache for interned strings.
|
||||
/// Exported under the `internals` feature only.
|
||||
pub struct StringsInterner<'a> {
|
||||
/// Maximum number of strings interned.
|
||||
pub capacity: usize,
|
||||
/// Maximum string length.
|
||||
pub max_string_len: usize,
|
||||
#[derive(Clone)]
|
||||
#[must_use]
|
||||
pub struct StringsInterner {
|
||||
/// Cached strings.
|
||||
cache: StraightHashMap<ImmutableString>,
|
||||
/// Bloom filter to avoid caching "one-hit wonders".
|
||||
filter: BloomFilterU64,
|
||||
/// Take care of the lifetime parameter.
|
||||
dummy: PhantomData<&'a ()>,
|
||||
bloom_filter: BloomFilterU64,
|
||||
}
|
||||
|
||||
impl Default for StringsInterner<'_> {
|
||||
impl Default for StringsInterner {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for StringsInterner<'_> {
|
||||
impl fmt::Debug for StringsInterner {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
@ -53,17 +47,13 @@ impl fmt::Debug for StringsInterner<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl StringsInterner<'_> {
|
||||
impl StringsInterner {
|
||||
/// Create a new [`StringsInterner`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
capacity: MAX_INTERNED_STRINGS,
|
||||
max_string_len: MAX_STRING_LEN,
|
||||
cache: StraightHashMap::default(),
|
||||
filter: BloomFilterU64::new(),
|
||||
dummy: PhantomData,
|
||||
bloom_filter: BloomFilterU64::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,22 +69,26 @@ impl StringsInterner<'_> {
|
||||
#[must_use]
|
||||
pub fn get_with_mapper<S: AsRef<str>>(
|
||||
&mut self,
|
||||
id: &str,
|
||||
category: &str,
|
||||
mapper: impl FnOnce(S) -> ImmutableString,
|
||||
text: S,
|
||||
) -> ImmutableString {
|
||||
let key = text.as_ref();
|
||||
|
||||
let hasher = &mut get_hasher();
|
||||
id.hash(hasher);
|
||||
category.hash(hasher);
|
||||
key.hash(hasher);
|
||||
let hash = hasher.finish();
|
||||
|
||||
// Cache long strings only on the second try to avoid caching "one-hit wonders".
|
||||
if key.len() > MAX_STRING_LEN && self.filter.is_absent_and_set(hash) {
|
||||
if key.len() > MAX_STRING_LEN && self.bloom_filter.is_absent_and_set(hash) {
|
||||
return mapper(text);
|
||||
}
|
||||
|
||||
if self.cache.is_empty() {
|
||||
self.cache.reserve(MAX_INTERNED_STRINGS);
|
||||
}
|
||||
|
||||
let result = match self.cache.entry(hash) {
|
||||
Entry::Occupied(e) => return e.get().clone(),
|
||||
Entry::Vacant(e) => {
|
||||
@ -114,26 +108,22 @@ impl StringsInterner<'_> {
|
||||
}
|
||||
|
||||
/// If the interner is over capacity, remove the longest entry that has the lowest count
|
||||
fn throttle_cache(&mut self, hash: u64) {
|
||||
if self.cache.len() <= self.capacity {
|
||||
#[inline]
|
||||
fn throttle_cache(&mut self, skip_hash: u64) {
|
||||
if self.cache.len() <= MAX_INTERNED_STRINGS {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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
|
||||
// that has just been inserted.
|
||||
let max = if self.capacity < 5 {
|
||||
2
|
||||
} else {
|
||||
self.capacity - 3
|
||||
};
|
||||
|
||||
while self.cache.len() > max {
|
||||
while self.cache.len() > MAX_INTERNED_STRINGS - 3 {
|
||||
let (_, _, n) = self
|
||||
.cache
|
||||
.iter()
|
||||
.fold((0, usize::MAX, 0), |(x, c, n), (&k, v)| {
|
||||
if k != hash && (v.strong_count() < c || (v.strong_count() == c && v.len() > x))
|
||||
if k != skip_hash
|
||||
&& (v.strong_count() < c || (v.strong_count() == c && v.len() > x))
|
||||
{
|
||||
(v.len(), v.strong_count(), k)
|
||||
} else {
|
||||
@ -169,14 +159,14 @@ impl StringsInterner<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<Self> for StringsInterner<'_> {
|
||||
impl AddAssign<Self> for StringsInterner {
|
||||
#[inline(always)]
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
self.cache.extend(rhs.cache.into_iter());
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<&Self> for StringsInterner<'_> {
|
||||
impl AddAssign<&Self> for StringsInterner {
|
||||
#[inline(always)]
|
||||
fn add_assign(&mut self, rhs: &Self) {
|
||||
self.cache
|
||||
|
@ -175,7 +175,6 @@ impl ParseErrorType {
|
||||
/// Make a [`ParseError`] using the current type and position.
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
#[must_use]
|
||||
pub(crate) fn into_err(self, pos: Position) -> ParseError {
|
||||
ParseError(self.into(), pos)
|
||||
}
|
||||
@ -299,7 +298,6 @@ impl ParseError {
|
||||
/// Get the [type][ParseErrorType] of this parse error.
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
#[must_use]
|
||||
pub const fn err_type(&self) -> &ParseErrorType {
|
||||
&self.0
|
||||
}
|
||||
@ -316,7 +314,7 @@ impl From<ParseErrorType> for RhaiError {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn from(err: ParseErrorType) -> Self {
|
||||
Box::new(err.into())
|
||||
Self::new(err.into())
|
||||
}
|
||||
}
|
||||
|
||||
@ -332,7 +330,7 @@ impl From<ParseError> for RhaiError {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn from(err: ParseError) -> Self {
|
||||
Box::new(err.into())
|
||||
Self::new(err.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,12 +6,12 @@ use std::prelude::v1::*;
|
||||
|
||||
/// Run custom restoration logic upon the end of scope.
|
||||
#[must_use]
|
||||
pub struct RestoreOnDrop<'a, T, R: FnOnce(&mut T)> {
|
||||
pub struct RestoreOnDrop<'a, T: ?Sized, R: FnOnce(&mut T)> {
|
||||
value: &'a mut T,
|
||||
restore: Option<R>,
|
||||
}
|
||||
|
||||
impl<'a, T, R: FnOnce(&mut T)> RestoreOnDrop<'a, T, R> {
|
||||
impl<'a, T: ?Sized, R: FnOnce(&mut T)> RestoreOnDrop<'a, T, R> {
|
||||
/// Create a new [`RestoreOnDrop`] that locks a mutable reference and runs restoration logic at
|
||||
/// the end of scope only when `need_restore` is `true`.
|
||||
///
|
||||
@ -39,7 +39,7 @@ impl<'a, T, R: FnOnce(&mut T)> RestoreOnDrop<'a, T, R> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, R: FnOnce(&mut T)> Drop for RestoreOnDrop<'a, T, R> {
|
||||
impl<'a, T: ?Sized, R: FnOnce(&mut T)> Drop for RestoreOnDrop<'a, T, R> {
|
||||
#[inline(always)]
|
||||
fn drop(&mut self) {
|
||||
if let Some(restore) = self.restore.take() {
|
||||
@ -48,7 +48,7 @@ impl<'a, T, R: FnOnce(&mut T)> Drop for RestoreOnDrop<'a, T, R> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, R: FnOnce(&mut T)> Deref for RestoreOnDrop<'a, T, R> {
|
||||
impl<'a, T: ?Sized, R: FnOnce(&mut T)> Deref for RestoreOnDrop<'a, T, R> {
|
||||
type Target = T;
|
||||
|
||||
#[inline(always)]
|
||||
@ -57,7 +57,7 @@ impl<'a, T, R: FnOnce(&mut T)> Deref for RestoreOnDrop<'a, T, R> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, R: FnOnce(&mut T)> DerefMut for RestoreOnDrop<'a, T, R> {
|
||||
impl<'a, T: ?Sized, R: FnOnce(&mut T)> DerefMut for RestoreOnDrop<'a, T, R> {
|
||||
#[inline(always)]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.value
|
||||
|
@ -353,6 +353,43 @@ impl Scope<'_> {
|
||||
self.values.push(value);
|
||||
self
|
||||
}
|
||||
/// Remove the last entry from the [`Scope`].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics is the [`Scope`] is empty.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Scope;
|
||||
///
|
||||
/// let mut my_scope = Scope::new();
|
||||
///
|
||||
/// my_scope.push("x", 42_i64);
|
||||
/// my_scope.push("y", 123_i64);
|
||||
/// assert!(my_scope.contains("x"));
|
||||
/// assert!(my_scope.contains("y"));
|
||||
/// assert_eq!(my_scope.len(), 2);
|
||||
///
|
||||
/// my_scope.pop();
|
||||
/// assert!(my_scope.contains("x"));
|
||||
/// assert!(!my_scope.contains("y"));
|
||||
/// assert_eq!(my_scope.len(), 1);
|
||||
///
|
||||
/// my_scope.pop();
|
||||
/// assert!(!my_scope.contains("x"));
|
||||
/// assert!(!my_scope.contains("y"));
|
||||
/// assert_eq!(my_scope.len(), 0);
|
||||
/// assert!(my_scope.is_empty());
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn pop(&mut self) -> &mut Self {
|
||||
self.names.pop().expect("`Scope` must not be empty");
|
||||
self.values.pop().expect("`Scope` must not be empty");
|
||||
self.aliases.pop().expect("`Scope` must not be empty");
|
||||
self
|
||||
}
|
||||
/// Truncate (rewind) the [`Scope`] to a previous size.
|
||||
///
|
||||
/// # Example
|
||||
|
@ -14,7 +14,7 @@ fn test_fn_ptr_curry_call() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
engine.register_raw_fn(
|
||||
"call_with_arg",
|
||||
&[TypeId::of::<FnPtr>(), TypeId::of::<INT>()],
|
||||
[TypeId::of::<FnPtr>(), TypeId::of::<INT>()],
|
||||
|context, args| {
|
||||
let fn_ptr = std::mem::take(args[0]).cast::<FnPtr>();
|
||||
fn_ptr.call_raw(&context, None, [std::mem::take(args[1])])
|
||||
@ -165,7 +165,7 @@ fn test_closures() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
engine.register_raw_fn(
|
||||
"custom_call",
|
||||
&[TypeId::of::<INT>(), TypeId::of::<FnPtr>()],
|
||||
[TypeId::of::<INT>(), TypeId::of::<FnPtr>()],
|
||||
|context, args| {
|
||||
let func = take(args[1]).cast::<FnPtr>();
|
||||
|
||||
@ -348,7 +348,7 @@ fn test_closures_shared_obj() -> Result<(), Box<EvalAltResult>> {
|
||||
let p1 = Rc::new(RefCell::new(41));
|
||||
let p2 = Rc::new(RefCell::new(1));
|
||||
|
||||
f(p1.clone(), p2.clone())?;
|
||||
f(p1.clone(), p2)?;
|
||||
|
||||
assert_eq!(*p1.borrow(), 42);
|
||||
|
||||
|
@ -266,17 +266,18 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
|
||||
*state = Dynamic::FALSE;
|
||||
Ok(Some("$ident$".into()))
|
||||
}
|
||||
2 => match stream[1].as_str() {
|
||||
"world" if state.as_bool().unwrap_or(false) => Ok(Some("$$world".into())),
|
||||
"world" => Ok(Some("$$hello".into())),
|
||||
"kitty" => {
|
||||
*state = (42 as INT).into();
|
||||
Ok(None)
|
||||
2 => {
|
||||
match stream[1].as_str() {
|
||||
"world" if state.as_bool().unwrap_or(false) => Ok(Some("$$world".into())),
|
||||
"world" => Ok(Some("$$hello".into())),
|
||||
"kitty" => {
|
||||
*state = (42 as INT).into();
|
||||
Ok(None)
|
||||
}
|
||||
s => Err(LexError::ImproperSymbol(s.to_string(), String::new())
|
||||
.into_err(Position::NONE)),
|
||||
}
|
||||
s => Err(LexError::ImproperSymbol(s.to_string(), String::new())
|
||||
.into_err(Position::NONE)
|
||||
.into()),
|
||||
},
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
true,
|
||||
|
@ -59,13 +59,13 @@ fn test_debugger_state() -> Result<(), Box<EvalAltResult>> {
|
||||
// Print debugger state - which is an object map
|
||||
println!(
|
||||
"Current state = {}",
|
||||
context.global_runtime_state_mut().debugger.state()
|
||||
context.global_runtime_state_mut().debugger().state()
|
||||
);
|
||||
|
||||
// Modify state
|
||||
let mut state = context
|
||||
.global_runtime_state_mut()
|
||||
.debugger
|
||||
.debugger_mut()
|
||||
.state_mut()
|
||||
.write_lock::<Map>()
|
||||
.unwrap();
|
||||
|
@ -13,22 +13,20 @@ use rhai::Array;
|
||||
use rhai::Map;
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use rhai::FLOAT;
|
||||
#[cfg(feature = "no_float")]
|
||||
#[cfg(feature = "decimal")]
|
||||
use rust_decimal::Decimal;
|
||||
|
||||
#[test]
|
||||
fn test_serde_ser_primary_types() -> Result<(), Box<EvalAltResult>> {
|
||||
assert!(to_dynamic(42_u64)?.is_int());
|
||||
assert!(to_dynamic(u64::MAX)?.is::<u64>());
|
||||
assert!(to_dynamic(42 as INT)?.is_int());
|
||||
assert!(to_dynamic(true)?.is_bool());
|
||||
assert!(to_dynamic(())?.is_unit());
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
{
|
||||
assert!(to_dynamic(123.456_f64)?.is::<f64>());
|
||||
assert!(to_dynamic(123.456_f32)?.is::<f32>());
|
||||
assert!(to_dynamic(123.456_f64)?.is::<FLOAT>());
|
||||
assert!(to_dynamic(123.456_f32)?.is::<FLOAT>());
|
||||
}
|
||||
|
||||
#[cfg(feature = "no_float")]
|
||||
@ -749,6 +747,31 @@ fn test_serde_json() -> serde_json::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "metadata")]
|
||||
#[cfg(feature = "decimal")]
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
fn test_serde_json_numbers() -> serde_json::Result<()> {
|
||||
use std::str::FromStr;
|
||||
|
||||
let d: Dynamic = serde_json::from_str("100000000000")?;
|
||||
assert!(d.is::<INT>());
|
||||
assert_eq!(d.as_int().unwrap(), 100000000000);
|
||||
|
||||
let d: Dynamic = serde_json::from_str("10000000000000000000")?;
|
||||
assert!(d.is::<Decimal>());
|
||||
assert_eq!(
|
||||
d.as_decimal().unwrap(),
|
||||
Decimal::from_str("10000000000000000000").unwrap()
|
||||
);
|
||||
|
||||
let d: Dynamic = serde_json::from_str("10000000000000000000000000")?;
|
||||
assert!(d.is::<FLOAT>());
|
||||
assert_eq!(d.as_float().unwrap(), 10000000000000000000000000.0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
fn test_serde_optional() -> Result<(), Box<EvalAltResult>> {
|
||||
@ -825,7 +848,14 @@ fn test_serde_blob() -> Result<(), Box<EvalAltResult>> {
|
||||
fn test_serde_json_borrowed_string() {
|
||||
let value = json!({ "a": "b" });
|
||||
println!("value: {value:?}");
|
||||
let _: Dynamic = serde_json::from_value(value).unwrap();
|
||||
|
||||
let result: Dynamic = serde_json::from_value(value.clone()).unwrap();
|
||||
println!("result: {result:?}");
|
||||
|
||||
let value2 = serde_json::to_value(&result).unwrap();
|
||||
println!("value2: {value2:?}");
|
||||
|
||||
assert_eq!(value, value2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
Loading…
Reference in New Issue
Block a user