commit
252567f84d
29
CHANGELOG.md
29
CHANGELOG.md
@ -4,14 +4,37 @@ Rhai Release Notes
|
||||
Version 1.12.0
|
||||
==============
|
||||
|
||||
Buf fixes
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
* Integer numbers that are too large to deserialize into `INT` now fall back to `Decimal` or `FLOAT` instead of silently truncating.
|
||||
* Parsing deeply-nested closures (e.g. `||{||{||{||{||{||{||{...}}}}}}}`) no longer panics but will be confined to the nesting limit.
|
||||
|
||||
Breaking API changes
|
||||
--------------------
|
||||
|
||||
* The callback for initializing a debugger instance has changed to `Fn(&Engine, Debugger) -> Debugger`. This allows more control over the initial setup of the debugger.
|
||||
* The internal macro `reify!` is no longer available publicly.
|
||||
|
||||
Deprecated API's
|
||||
----------------
|
||||
|
||||
* `Module::with_capacity` is deprecated.
|
||||
* The internal method `Engine::eval_statements_raw` is deprecated.
|
||||
|
||||
Speed improvements
|
||||
------------------
|
||||
|
||||
* The functions registration mechanism is revamped to take advantage of constant generics, among others.
|
||||
* This yields a 10-20% speed improvements on certain real-life, function-call-heavy workloads.
|
||||
|
||||
Net features
|
||||
------------
|
||||
|
||||
### `!in`
|
||||
|
||||
* A new operator `!in` is added which maps to `!(... in ...)`.
|
||||
|
||||
### `Engine::call_fn_with_options`
|
||||
|
||||
* `Engine::call_fn_raw` is deprecated in favor of `Engine::call_fn_with_options` which allows setting options for the function call.
|
||||
@ -21,11 +44,15 @@ Net features
|
||||
Enhancements
|
||||
------------
|
||||
|
||||
* Optimizations have been done to key data structures to minimize size and creation time, which involves turning rarely-used fields into `Option<Box<T>>`. This resulted in some speed improvements.
|
||||
* `CallableFunction` is exported under `internals`.
|
||||
* 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`.
|
||||
* Line-style doc-comments are now merged into a single string to avoid creating many strings. Block-style doc-comments continue to be independent strings.
|
||||
* Block-style doc-comments are now "un-indented" for better formatting.
|
||||
* Doc-comments on plugin modules are now captured in the module's `doc` field.
|
||||
|
||||
|
||||
Version 1.11.0
|
||||
|
@ -23,7 +23,7 @@ ahash = { version = "0.8.2", default-features = false, features = ["compile-time
|
||||
num-traits = { version = "0.2", default-features = false }
|
||||
bitflags = { version = "1", default-features = false }
|
||||
smartstring = { version = "1", default-features = false }
|
||||
rhai_codegen = { version = "1.4.1", path = "codegen", default-features = false }
|
||||
rhai_codegen = { version = "1.5.0", path = "codegen", default-features = false }
|
||||
|
||||
no-std-compat = { version = "0.4", default-features = false, features = ["alloc"], optional = true }
|
||||
libm = { version = "0.2", default-features = false, optional = true }
|
||||
@ -43,7 +43,7 @@ serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
|
||||
[features]
|
||||
default = ["std", "ahash/runtime-rng"] # ahash/runtime-rng trumps ahash/compile-time-rng
|
||||
std = ["ahash/std", "num-traits/std", "smartstring/std"]
|
||||
unchecked = [] # unchecked arithmetic
|
||||
unchecked = [] # disable safety checks
|
||||
sync = [] # restrict to only types that implement Send + Sync
|
||||
no_position = [] # do not track position in the parser
|
||||
no_optimize = [] # no script optimizer
|
||||
|
@ -33,7 +33,7 @@ Standard features
|
||||
-----------------
|
||||
|
||||
* Simple language similar to JavaScript+Rust with [dynamic](https://rhai.rs/book/language/dynamic.html) typing.
|
||||
* Fairly efficient evaluation (1 million iterations in 0.23 sec on a single-core, 2.3 GHz Linux VM).
|
||||
* Fairly efficient evaluation (1 million iterations in 0.14 sec on a single-core 2.6 GHz Linux VM).
|
||||
* Tight integration with native Rust [functions](https://rhai.rs/book/rust/functions.html) and [types](https://rhai.rs/book/rust/custom-types.html), including [getters/setters](https://rhai.rs/book/rust/getters-setters.html), [methods](https://rhai.rs/book/rust/methods.html) and [indexers](https://rhai.rs/book/rust/indexers.html).
|
||||
* Freely pass Rust values into a script as [variables](https://rhai.rs/book/language/variables.html)/[constants](https://rhai.rs/book/language/constants.html) via an external [`Scope`](https://rhai.rs/book/engine/scope.html) - all clonable Rust types are supported; no need to implement any special trait. Or tap directly into the [variable resolution process](https://rhai.rs/book/engine/var.html).
|
||||
* Built-in support for most common [data types](https://rhai.rs/book/language/values-and-types.html) including booleans, [integers](https://rhai.rs/book/language/numbers.html), [floating-point numbers](https://rhai.rs/book/language/numbers.html) (including [`Decimal`](https://crates.io/crates/rust_decimal)), [strings](https://rhai.rs/book/language/strings-chars.html), [Unicode characters](https://rhai.rs/book/language/strings-chars.html), [arrays](https://rhai.rs/book/language/arrays.html) (including packed [byte arrays](https://rhai.rs/book/language/blobs.html)) and [object maps](https://rhai.rs/book/language/object-maps.html).
|
||||
|
@ -1,11 +1,11 @@
|
||||
[package]
|
||||
name = "rhai_codegen"
|
||||
version = "1.4.3"
|
||||
version = "1.5.0"
|
||||
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"]
|
||||
keywords = ["scripting", "scripting-engine", "scripting-language", "embedded", "plugin"]
|
||||
categories = ["no-std", "embedded", "wasm", "parser-implementations"]
|
||||
homepage = "https://rhai.rs/book/plugins/index.html"
|
||||
repository = "https://github.com/rhaiscript/rhai"
|
||||
@ -24,5 +24,5 @@ syn = { version = "1.0", features = ["full", "parsing", "printing", "proc-macro"
|
||||
quote = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
rhai = { path = "..", version = "1.11", features = ["metadata"] }
|
||||
rhai = { path = "..", version = "1.12", features = ["metadata"] }
|
||||
trybuild = "1"
|
||||
|
@ -145,6 +145,7 @@ pub fn inner_item_attributes<T: ExportedParams>(
|
||||
pub fn doc_attributes(attrs: &[syn::Attribute]) -> syn::Result<Vec<String>> {
|
||||
// Find the #[doc] attribute which will turn be read for function documentation.
|
||||
let mut comments = Vec::new();
|
||||
let mut buf = String::new();
|
||||
|
||||
for attr in attrs {
|
||||
if let Some(i) = attr.path.get_ident() {
|
||||
@ -158,17 +159,28 @@ pub fn doc_attributes(attrs: &[syn::Attribute]) -> syn::Result<Vec<String>> {
|
||||
|
||||
if line.contains('\n') {
|
||||
// Must be a block comment `/** ... */`
|
||||
if !buf.is_empty() {
|
||||
comments.push(buf.clone());
|
||||
buf.clear();
|
||||
}
|
||||
line.insert_str(0, "/**");
|
||||
line.push_str("*/");
|
||||
comments.push(line);
|
||||
} else {
|
||||
// Single line - assume it is `///`
|
||||
line.insert_str(0, "///");
|
||||
if !buf.is_empty() {
|
||||
buf.push('\n');
|
||||
}
|
||||
buf.push_str("///");
|
||||
buf.push_str(&line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
comments.push(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
if !buf.is_empty() {
|
||||
comments.push(buf);
|
||||
}
|
||||
|
||||
Ok(comments)
|
||||
|
@ -674,6 +674,7 @@ impl ExportedFn {
|
||||
let arg_count = self.arg_count();
|
||||
let is_method_call = self.mutable_receiver();
|
||||
let is_pure = !self.mutable_receiver() || self.params().pure.is_some();
|
||||
let pass_context = self.pass_context;
|
||||
|
||||
let mut unpack_statements = Vec::new();
|
||||
let mut unpack_exprs = Vec::new();
|
||||
@ -689,7 +690,7 @@ impl ExportedFn {
|
||||
let skip_first_arg;
|
||||
|
||||
if self.pass_context {
|
||||
unpack_exprs.push(syn::parse2::<syn::Expr>(quote! { context }).unwrap());
|
||||
unpack_exprs.push(syn::parse2::<syn::Expr>(quote! { context.unwrap() }).unwrap());
|
||||
}
|
||||
|
||||
// Handle the first argument separately if the function has a "method like" receiver
|
||||
@ -860,13 +861,14 @@ impl ExportedFn {
|
||||
#(#cfg_attrs)*
|
||||
impl PluginFunction for #type_name {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
#(#unpack_statements)*
|
||||
#return_expr
|
||||
}
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { #is_method_call }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { #is_pure }
|
||||
#[inline(always)] fn has_context(&self) -> bool { #pass_context }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -106,6 +106,7 @@ impl Module {
|
||||
impl Parse for Module {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let mut mod_all: syn::ItemMod = input.parse()?;
|
||||
|
||||
let fns: Vec<_>;
|
||||
let mut consts = Vec::new();
|
||||
let mut custom_types = Vec::new();
|
||||
@ -269,11 +270,17 @@ impl Module {
|
||||
let (.., orig_content) = mod_all.content.take().unwrap();
|
||||
let mod_attrs = mem::take(&mut mod_all.attrs);
|
||||
|
||||
#[cfg(feature = "metadata")]
|
||||
let mod_doc = crate::attrs::doc_attributes(&mod_attrs)?.join("\n");
|
||||
#[cfg(not(feature = "metadata"))]
|
||||
let mod_doc = String::new();
|
||||
|
||||
if !params.skip {
|
||||
// Generate new module items.
|
||||
//
|
||||
// This is done before inner module recursive generation, because that is destructive.
|
||||
let mod_gen = crate::rhai_module::generate_body(
|
||||
&mod_doc,
|
||||
&mut fns,
|
||||
&consts,
|
||||
&custom_types,
|
||||
|
@ -26,6 +26,7 @@ pub struct ExportedType {
|
||||
}
|
||||
|
||||
pub fn generate_body(
|
||||
doc: &str,
|
||||
fns: &mut [ExportedFn],
|
||||
consts: &[ExportedConst],
|
||||
custom_types: &[ExportedType],
|
||||
@ -230,7 +231,7 @@ pub fn generate_body(
|
||||
syn::parse2::<syn::Stmt>(quote! {
|
||||
#(#cfg_attrs)*
|
||||
m.set_fn_with_comments(#fn_literal, FnNamespace::#ns_str, FnAccess::Public,
|
||||
#param_names, &[#(#fn_input_types),*], &[#(#comments),*], #fn_token_name().into());
|
||||
#param_names, [#(#fn_input_types),*], [#(#comments),*], #fn_token_name().into());
|
||||
})
|
||||
.unwrap()
|
||||
});
|
||||
@ -246,6 +247,17 @@ pub fn generate_body(
|
||||
gen_fn_tokens.push(function.generate_impl(&fn_token_name.to_string()));
|
||||
}
|
||||
|
||||
let module_docs = if doc.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
syn::parse2::<syn::Stmt>(quote! {
|
||||
m.set_doc(#doc);
|
||||
})
|
||||
.unwrap(),
|
||||
)
|
||||
};
|
||||
|
||||
let mut generate_fn_call = syn::parse2::<syn::ItemMod>(quote! {
|
||||
pub mod generate_info {
|
||||
#[allow(unused_imports)]
|
||||
@ -254,6 +266,7 @@ pub fn generate_body(
|
||||
#[doc(hidden)]
|
||||
pub fn rhai_module_generate() -> Module {
|
||||
let mut m = Module::new();
|
||||
#module_docs
|
||||
rhai_generate_into_module(&mut m, false);
|
||||
m.build_index();
|
||||
m
|
||||
|
@ -280,12 +280,13 @@ mod generate_tests {
|
||||
#[inline(always)] pub fn param_types() -> [TypeId; 0usize] { [] }
|
||||
}
|
||||
impl PluginFunction for Token {
|
||||
#[inline(always)] fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
#[inline(always)] fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
Ok(Dynamic::from(do_nothing()))
|
||||
}
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
#[allow(unused)]
|
||||
#[doc(hidden)]
|
||||
@ -318,13 +319,14 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for Token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg0 = mem::take(args[0usize]).cast::<usize>();
|
||||
Ok(Dynamic::from(do_something(arg0)))
|
||||
}
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
#[allow(unused)]
|
||||
#[doc(hidden)]
|
||||
@ -357,13 +359,14 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for Token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg0 = mem::take(args[0usize]).cast::<usize>();
|
||||
Ok(Dynamic::from(do_something(context, arg0)))
|
||||
Ok(Dynamic::from(do_something(context.unwrap(), arg0)))
|
||||
}
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
||||
#[inline(always)] fn has_context(&self) -> bool { true }
|
||||
}
|
||||
#[allow(unused)]
|
||||
#[doc(hidden)]
|
||||
@ -399,12 +402,13 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for Token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
Ok(Dynamic::from(return_dynamic()))
|
||||
}
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
#[allow(unused)]
|
||||
#[doc(hidden)]
|
||||
@ -432,13 +436,14 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for TestStruct {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg0 = mem::take(args[0usize]).cast::<usize>();
|
||||
Ok(Dynamic::from(do_something(arg0)))
|
||||
}
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
};
|
||||
|
||||
@ -465,7 +470,7 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for Token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg0 = mem::take(args[0usize]).cast::<usize>();
|
||||
let arg1 = mem::take(args[1usize]).cast::<usize>();
|
||||
Ok(Dynamic::from(add_together(arg0, arg1)))
|
||||
@ -473,6 +478,7 @@ mod generate_tests {
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
#[allow(unused)]
|
||||
#[doc(hidden)]
|
||||
@ -505,7 +511,7 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for Token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg1 = mem::take(args[1usize]).cast::<usize>();
|
||||
let arg0 = &mut args[0usize].write_lock::<usize>().unwrap();
|
||||
Ok(Dynamic::from(increment(arg0, arg1)))
|
||||
@ -513,6 +519,7 @@ mod generate_tests {
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { false }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
#[allow(unused)]
|
||||
#[doc(hidden)]
|
||||
@ -546,13 +553,14 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for Token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg0 = mem::take(args[0usize]).into_immutable_string().unwrap();
|
||||
Ok(Dynamic::from(special_print(&arg0)))
|
||||
}
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
#[allow(unused)]
|
||||
#[doc(hidden)]
|
||||
|
@ -92,10 +92,11 @@ mod module_tests {
|
||||
.cloned()
|
||||
.collect::<Vec<_>>(),
|
||||
vec![
|
||||
"/// This is a doc-comment.",
|
||||
"/// Another line.",
|
||||
"/// block doc-comment ",
|
||||
"/// Final line.",
|
||||
"\
|
||||
/// This is a doc-comment.\n\
|
||||
/// Another line.\n\
|
||||
/// block doc-comment \n\
|
||||
/// Final line.",
|
||||
"/** doc-comment\n in multiple lines\n */"
|
||||
]
|
||||
);
|
||||
@ -385,12 +386,13 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for get_mystic_number_token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
Ok(Dynamic::from(get_mystic_number()))
|
||||
}
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -402,6 +404,12 @@ mod generate_tests {
|
||||
#[test]
|
||||
fn one_factory_fn_with_comments_module() {
|
||||
let input_tokens: TokenStream = quote! {
|
||||
/// This is the one_fn module!
|
||||
/** block doc-comment
|
||||
* multi-line
|
||||
*/
|
||||
/// Another line!
|
||||
/// Final line!
|
||||
pub mod one_fn {
|
||||
/// This is a doc-comment.
|
||||
/// Another line.
|
||||
@ -418,6 +426,12 @@ mod generate_tests {
|
||||
};
|
||||
|
||||
let expected_tokens = quote! {
|
||||
/// This is the one_fn module!
|
||||
/** block doc-comment
|
||||
* multi-line
|
||||
*/
|
||||
/// Another line!
|
||||
/// Final line!
|
||||
pub mod one_fn {
|
||||
/// This is a doc-comment.
|
||||
/// Another line.
|
||||
@ -436,6 +450,7 @@ mod generate_tests {
|
||||
#[doc(hidden)]
|
||||
pub fn rhai_module_generate() -> Module {
|
||||
let mut m = Module::new();
|
||||
m.set_doc("/// This is the one_fn module!\n/** block doc-comment\n * multi-line\n */\n/// Another line!\n/// Final line!");
|
||||
rhai_generate_into_module(&mut m, false);
|
||||
m.build_index();
|
||||
m
|
||||
@ -444,11 +459,8 @@ mod generate_tests {
|
||||
#[doc(hidden)]
|
||||
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
|
||||
m.set_fn_with_comments("get_mystic_number", FnNamespace::Internal, FnAccess::Public,
|
||||
Some(get_mystic_number_token::PARAM_NAMES), &[], &[
|
||||
"/// This is a doc-comment.",
|
||||
"/// Another line.",
|
||||
"/// block doc-comment ",
|
||||
"/// Final line.",
|
||||
Some(get_mystic_number_token::PARAM_NAMES), [], [
|
||||
"/// This is a doc-comment.\n/// Another line.\n/// block doc-comment \n/// Final line.",
|
||||
"/** doc-comment\n in multiple lines\n */"
|
||||
], get_mystic_number_token().into());
|
||||
if flatten {} else {}
|
||||
@ -463,12 +475,13 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for get_mystic_number_token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
Ok(Dynamic::from(get_mystic_number()))
|
||||
}
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -521,13 +534,14 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for add_one_to_token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||
Ok(Dynamic::from(add_one_to(arg0)))
|
||||
}
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -579,13 +593,14 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for add_one_to_token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||
Ok(Dynamic::from(add_one_to(arg0)))
|
||||
}
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -651,13 +666,14 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for add_one_to_token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||
Ok(Dynamic::from(add_one_to(arg0)))
|
||||
}
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
@ -670,7 +686,7 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for add_n_to_token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||
let arg1 = mem::take(args[1usize]).cast::<INT>();
|
||||
Ok(Dynamic::from(add_n_to(arg0, arg1)))
|
||||
@ -678,6 +694,7 @@ mod generate_tests {
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -729,7 +746,7 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for add_together_token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||
let arg1 = mem::take(args[1usize]).cast::<INT>();
|
||||
Ok(Dynamic::from(add_together(arg0, arg1)))
|
||||
@ -737,6 +754,7 @@ mod generate_tests {
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -795,7 +813,7 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for add_together_token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||
let arg1 = mem::take(args[1usize]).cast::<INT>();
|
||||
Ok(Dynamic::from(add_together(arg0, arg1)))
|
||||
@ -803,6 +821,7 @@ mod generate_tests {
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -871,13 +890,14 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for get_mystic_number_token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg0 = &mut args[0usize].write_lock::<Hello>().unwrap();
|
||||
Ok(Dynamic::from(get_mystic_number(arg0)))
|
||||
}
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { false }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1081,12 +1101,13 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for get_mystic_number_token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
Ok(Dynamic::from(get_mystic_number()))
|
||||
}
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1171,13 +1192,14 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for print_out_to_token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg0 = mem::take(args[0usize]).into_immutable_string().unwrap();
|
||||
Ok(Dynamic::from(print_out_to(&arg0)))
|
||||
}
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1229,13 +1251,14 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for print_out_to_token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg0 = mem::take(args[0usize]).into_string().unwrap();
|
||||
Ok(Dynamic::from(print_out_to(arg0)))
|
||||
}
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1288,7 +1311,7 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for foo_token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg1 = mem::take(args[1usize]).cast::<INT>();
|
||||
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
|
||||
Ok(Dynamic::from(foo(arg0, arg1)))
|
||||
@ -1296,6 +1319,7 @@ mod generate_tests {
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1347,13 +1371,14 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for increment_token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
|
||||
Ok(Dynamic::from(increment(arg0)))
|
||||
}
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { false }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1408,13 +1433,14 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for increment_token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
|
||||
Ok(Dynamic::from(increment(arg0)))
|
||||
}
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { false }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
}
|
||||
#[allow(unused_imports)]
|
||||
@ -1492,13 +1518,14 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for increment_token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
|
||||
Ok(Dynamic::from(increment(arg0)))
|
||||
}
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { false }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
}
|
||||
#[allow(unused_imports)]
|
||||
@ -1577,13 +1604,14 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for int_foo_token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
|
||||
Ok(Dynamic::from(int_foo(arg0)))
|
||||
}
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { false }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1639,13 +1667,14 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for int_foo_token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
|
||||
Ok(Dynamic::from(int_foo(arg0)))
|
||||
}
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { false }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1698,7 +1727,7 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for int_foo_token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
|
||||
Ok(Dynamic::from(int_foo(arg0, arg1)))
|
||||
@ -1706,6 +1735,7 @@ mod generate_tests {
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { false }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1761,7 +1791,7 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for int_foo_token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
|
||||
Ok(Dynamic::from(int_foo(arg0, arg1)))
|
||||
@ -1769,6 +1799,7 @@ mod generate_tests {
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { false }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1821,7 +1852,7 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for get_by_index_token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||
let arg0 = &mut args[0usize].write_lock::<MyCollection>().unwrap();
|
||||
Ok(Dynamic::from(get_by_index(arg0, arg1)))
|
||||
@ -1829,6 +1860,7 @@ mod generate_tests {
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { false }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1889,7 +1921,7 @@ mod generate_tests {
|
||||
#[cfg(hello)]
|
||||
impl PluginFunction for get_by_index_token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||
let arg0 = &mut args[0usize].write_lock::<MyCollection>().unwrap();
|
||||
Ok(Dynamic::from(get_by_index(arg0, arg1)))
|
||||
@ -1897,6 +1929,7 @@ mod generate_tests {
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { false }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1952,7 +1985,7 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for get_by_index_token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||
let arg0 = &mut args[0usize].write_lock::<MyCollection>().unwrap();
|
||||
Ok(Dynamic::from(get_by_index(arg0, arg1)))
|
||||
@ -1960,6 +1993,7 @@ mod generate_tests {
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { false }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -2012,7 +2046,7 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for set_by_index_token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||
let arg2 = mem::take(args[2usize]).cast::<FLOAT>();
|
||||
let arg0 = &mut args[0usize].write_lock::<MyCollection>().unwrap();
|
||||
@ -2021,6 +2055,7 @@ mod generate_tests {
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { false }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -2076,7 +2111,7 @@ mod generate_tests {
|
||||
}
|
||||
impl PluginFunction for set_by_index_token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||
let arg2 = mem::take(args[2usize]).cast::<FLOAT>();
|
||||
let arg0 = &mut args[0usize].write_lock::<MyCollection>().unwrap();
|
||||
@ -2085,6 +2120,7 @@ mod generate_tests {
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
||||
#[inline(always)] fn is_pure(&self) -> bool { false }
|
||||
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -4,7 +4,7 @@
|
||||
fn init() {
|
||||
// Can detect system-provided default states!
|
||||
// Add 'bool_state' as new state variable if one does not exist
|
||||
if !("bool_state" in this) {
|
||||
if "bool_state" !in this {
|
||||
this.bool_state = false;
|
||||
}
|
||||
// Add 'value' as new state variable (overwrites any existing)
|
||||
|
@ -4,7 +4,7 @@
|
||||
/// State is stored inside an object map bound to 'state'.
|
||||
fn init() {
|
||||
// Add 'bool_state' as new state variable if one does not exist
|
||||
if !("bool_state" in state) {
|
||||
if "bool_state" !in state {
|
||||
state.bool_state = false;
|
||||
}
|
||||
// Add 'obj_state' as new state variable (overwrites any existing)
|
||||
@ -37,7 +37,7 @@ fn start(data) {
|
||||
|
||||
/// 'end' event handler
|
||||
fn end(data) {
|
||||
if !state.bool_state || !("start_mode" in state) {
|
||||
if !state.bool_state || "start_mode" !in state {
|
||||
throw "Not yet started!";
|
||||
}
|
||||
if state.value > 0 {
|
||||
|
@ -88,6 +88,7 @@ impl Engine {
|
||||
///
|
||||
/// To define a pretty-print name, call [`with_name`][`TypeBuilder::with_name`],
|
||||
/// to use [`Engine::register_type_with_name`] instead.
|
||||
#[must_use]
|
||||
pub struct TypeBuilder<'a, T: Variant + Clone> {
|
||||
engine: &'a mut Engine,
|
||||
name: Option<&'static str>,
|
||||
@ -101,7 +102,7 @@ impl<'a, T: Variant + Clone> TypeBuilder<'a, T> {
|
||||
Self {
|
||||
engine,
|
||||
name: None,
|
||||
_marker: PhantomData::default(),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -116,10 +117,10 @@ impl<'a, T: Variant + Clone> TypeBuilder<'a, T> {
|
||||
|
||||
/// Register a custom function.
|
||||
#[inline(always)]
|
||||
pub fn with_fn<A, R, S>(
|
||||
pub fn with_fn<A: 'static, const N: usize, const C: bool, R: Variant + Clone, const L: bool>(
|
||||
&mut self,
|
||||
name: impl AsRef<str> + Into<Identifier>,
|
||||
method: impl RegisterNativeFunction<A, R, S>,
|
||||
method: impl RegisterNativeFunction<A, N, C, R, L>,
|
||||
) -> &mut Self {
|
||||
self.engine.register_fn(name, method);
|
||||
self
|
||||
@ -148,10 +149,10 @@ impl<'a, T: Variant + Clone> TypeBuilder<'a, T> {
|
||||
///
|
||||
/// Not available under `no_object`.
|
||||
#[inline(always)]
|
||||
pub fn with_get<V: Variant + Clone, S>(
|
||||
pub fn with_get<const C: bool, V: Variant + Clone, const L: bool>(
|
||||
&mut self,
|
||||
name: impl AsRef<str>,
|
||||
get_fn: impl RegisterNativeFunction<(Mut<T>,), V, S> + crate::func::SendSync + 'static,
|
||||
get_fn: impl RegisterNativeFunction<(Mut<T>,), 1, C, V, L> + crate::func::SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
self.engine.register_get(name, get_fn);
|
||||
self
|
||||
@ -161,10 +162,10 @@ impl<'a, T: Variant + Clone> TypeBuilder<'a, T> {
|
||||
///
|
||||
/// Not available under `no_object`.
|
||||
#[inline(always)]
|
||||
pub fn with_set<V: Variant + Clone, S>(
|
||||
pub fn with_set<const C: bool, V: Variant + Clone, const L: bool>(
|
||||
&mut self,
|
||||
name: impl AsRef<str>,
|
||||
set_fn: impl RegisterNativeFunction<(Mut<T>, V), (), S> + crate::func::SendSync + 'static,
|
||||
set_fn: impl RegisterNativeFunction<(Mut<T>, V), 2, C, (), L> + crate::func::SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
self.engine.register_set(name, set_fn);
|
||||
self
|
||||
@ -176,11 +177,19 @@ impl<'a, T: Variant + Clone> TypeBuilder<'a, T> {
|
||||
///
|
||||
/// Not available under `no_object`.
|
||||
#[inline(always)]
|
||||
pub fn with_get_set<V: Variant + Clone, S1, S2>(
|
||||
pub fn with_get_set<
|
||||
const C1: bool,
|
||||
const C2: bool,
|
||||
V: Variant + Clone,
|
||||
const L1: bool,
|
||||
const L2: bool,
|
||||
>(
|
||||
&mut self,
|
||||
name: impl AsRef<str>,
|
||||
get_fn: impl RegisterNativeFunction<(Mut<T>,), V, S1> + crate::func::SendSync + 'static,
|
||||
set_fn: impl RegisterNativeFunction<(Mut<T>, V), (), S2> + crate::func::SendSync + 'static,
|
||||
get_fn: impl RegisterNativeFunction<(Mut<T>,), 1, C1, V, L1> + crate::func::SendSync + 'static,
|
||||
set_fn: impl RegisterNativeFunction<(Mut<T>, V), 2, C2, (), L2>
|
||||
+ crate::func::SendSync
|
||||
+ 'static,
|
||||
) -> &mut Self {
|
||||
self.engine.register_get_set(name, get_fn, set_fn);
|
||||
self
|
||||
@ -195,9 +204,14 @@ impl<'a, T: Variant + Clone> TypeBuilder<'a, T> {
|
||||
///
|
||||
/// Not available under both `no_index` and `no_object`.
|
||||
#[inline(always)]
|
||||
pub fn with_indexer_get<X: Variant + Clone, V: Variant + Clone, S>(
|
||||
pub fn with_indexer_get<
|
||||
X: Variant + Clone,
|
||||
const C: bool,
|
||||
V: Variant + Clone,
|
||||
const L: bool,
|
||||
>(
|
||||
&mut self,
|
||||
get_fn: impl RegisterNativeFunction<(Mut<T>, X), V, S> + crate::func::SendSync + 'static,
|
||||
get_fn: impl RegisterNativeFunction<(Mut<T>, X), 2, C, V, L> + crate::func::SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
self.engine.register_indexer_get(get_fn);
|
||||
self
|
||||
@ -207,9 +221,16 @@ impl<'a, T: Variant + Clone> TypeBuilder<'a, T> {
|
||||
///
|
||||
/// Not available under both `no_index` and `no_object`.
|
||||
#[inline(always)]
|
||||
pub fn with_indexer_set<X: Variant + Clone, V: Variant + Clone, S>(
|
||||
pub fn with_indexer_set<
|
||||
X: Variant + Clone,
|
||||
const C: bool,
|
||||
V: Variant + Clone,
|
||||
const L: bool,
|
||||
>(
|
||||
&mut self,
|
||||
set_fn: impl RegisterNativeFunction<(Mut<T>, X, V), (), S> + crate::func::SendSync + 'static,
|
||||
set_fn: impl RegisterNativeFunction<(Mut<T>, X, V), 3, C, (), L>
|
||||
+ crate::func::SendSync
|
||||
+ 'static,
|
||||
) -> &mut Self {
|
||||
self.engine.register_indexer_set(set_fn);
|
||||
self
|
||||
@ -219,10 +240,19 @@ impl<'a, T: Variant + Clone> TypeBuilder<'a, T> {
|
||||
///
|
||||
/// Not available under both `no_index` and `no_object`.
|
||||
#[inline(always)]
|
||||
pub fn with_indexer_get_set<X: Variant + Clone, V: Variant + Clone, S1, S2>(
|
||||
pub fn with_indexer_get_set<
|
||||
X: Variant + Clone,
|
||||
const C1: bool,
|
||||
const C2: bool,
|
||||
V: Variant + Clone,
|
||||
const L1: bool,
|
||||
const L2: bool,
|
||||
>(
|
||||
&mut self,
|
||||
get_fn: impl RegisterNativeFunction<(Mut<T>, X), V, S1> + crate::func::SendSync + 'static,
|
||||
set_fn: impl RegisterNativeFunction<(Mut<T>, X, V), (), S2> + crate::func::SendSync + 'static,
|
||||
get_fn: impl RegisterNativeFunction<(Mut<T>, X), 2, C1, V, L1> + crate::func::SendSync + 'static,
|
||||
set_fn: impl RegisterNativeFunction<(Mut<T>, X, V), 3, C2, (), L2>
|
||||
+ crate::func::SendSync
|
||||
+ 'static,
|
||||
) -> &mut Self {
|
||||
self.engine.register_indexer_get_set(get_fn, set_fn);
|
||||
self
|
||||
|
@ -3,10 +3,8 @@
|
||||
|
||||
use crate::eval::{Caches, GlobalRuntimeState};
|
||||
use crate::types::dynamic::Variant;
|
||||
use crate::types::RestoreOnDrop;
|
||||
use crate::{
|
||||
reify, Dynamic, Engine, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, AST,
|
||||
ERR,
|
||||
Dynamic, Engine, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, AST, ERR,
|
||||
};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@ -81,7 +79,7 @@ impl Engine {
|
||||
///
|
||||
/// The [`AST`] is evaluated before calling the function.
|
||||
/// This allows a script to load the necessary modules.
|
||||
/// This is usually desired. If not, use [`call_fn_with_options`] instead.
|
||||
/// This is usually desired. If not, use [`call_fn_with_options`][Engine::call_fn_with_options] instead.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -240,10 +238,10 @@ impl Engine {
|
||||
let rewind_scope = options.rewind_scope;
|
||||
|
||||
let result = if options.eval_ast && !statements.is_empty() {
|
||||
auto_restore! {
|
||||
scope if rewind_scope => rewind;
|
||||
let orig_scope_len = scope.len();
|
||||
let scope = &mut *RestoreOnDrop::lock_if(rewind_scope, scope, move |s| {
|
||||
s.rewind(orig_scope_len);
|
||||
});
|
||||
}
|
||||
|
||||
self.eval_global_statements(global, caches, scope, statements)
|
||||
} else {
|
||||
@ -254,7 +252,7 @@ impl Engine {
|
||||
|
||||
// Check for data race.
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
crate::func::ensure_no_data_race(name, args, false).map(|_| Dynamic::UNIT)?;
|
||||
crate::func::ensure_no_data_race(name, args, false)?;
|
||||
|
||||
ast.shared_lib()
|
||||
.get_script_fn(name, args.len())
|
||||
@ -276,7 +274,7 @@ impl Engine {
|
||||
});
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
if self.debugger.is_some() {
|
||||
if self.is_debugger_registered() {
|
||||
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)?;
|
||||
|
@ -218,15 +218,12 @@ impl Engine {
|
||||
scripts: impl AsRef<[S]>,
|
||||
optimization_level: OptimizationLevel,
|
||||
) -> ParseResult<AST> {
|
||||
let (stream, tokenizer_control) = self.lex_raw(
|
||||
scripts.as_ref(),
|
||||
self.token_mapper.as_ref().map(<_>::as_ref),
|
||||
);
|
||||
let (stream, tc) = self.lex_raw(scripts.as_ref(), self.token_mapper.as_deref());
|
||||
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)?;
|
||||
let state = &mut ParseState::new(scope, interned_strings, tc);
|
||||
let mut _ast = self.parse(stream.peekable(), state, optimization_level)?;
|
||||
#[cfg(feature = "metadata")]
|
||||
_ast.set_doc(state.tokenizer_control.borrow().global_comments.join("\n"));
|
||||
_ast.set_doc(&state.tokenizer_control.borrow().global_comments);
|
||||
Ok(_ast)
|
||||
}
|
||||
/// Compile a string containing an expression into an [`AST`],
|
||||
@ -292,12 +289,9 @@ impl Engine {
|
||||
script: impl AsRef<str>,
|
||||
) -> ParseResult<AST> {
|
||||
let scripts = [script];
|
||||
let (stream, tokenizer_control) =
|
||||
self.lex_raw(&scripts, self.token_mapper.as_ref().map(<_>::as_ref));
|
||||
|
||||
let mut peekable = stream.peekable();
|
||||
let (stream, t) = self.lex_raw(&scripts, self.token_mapper.as_deref());
|
||||
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)
|
||||
let state = &mut ParseState::new(scope, interned_strings, t);
|
||||
self.parse_global_expr(stream.peekable(), state, |_| {}, self.optimization_level)
|
||||
}
|
||||
}
|
||||
|
@ -4,11 +4,11 @@
|
||||
use crate::ast::Expr;
|
||||
use crate::func::SendSync;
|
||||
use crate::parser::ParseResult;
|
||||
use crate::tokenizer::{is_valid_identifier, Token};
|
||||
use crate::tokenizer::{is_valid_identifier, Token, NO_TOKEN};
|
||||
use crate::types::dynamic::Variant;
|
||||
use crate::{
|
||||
reify, Dynamic, Engine, EvalContext, Identifier, ImmutableString, LexError, Position,
|
||||
RhaiResult, StaticVec,
|
||||
Dynamic, Engine, EvalContext, Identifier, ImmutableString, LexError, Position, RhaiResult,
|
||||
StaticVec,
|
||||
};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@ -166,6 +166,7 @@ impl Deref for Expression<'_> {
|
||||
type Target = Expr;
|
||||
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0
|
||||
}
|
||||
@ -230,11 +231,11 @@ impl Engine {
|
||||
continue;
|
||||
}
|
||||
|
||||
let token = Token::lookup_symbol_from_syntax(s).or_else(|| {
|
||||
let token = Token::lookup_symbol_from_syntax(s).unwrap_or_else(|| {
|
||||
if Token::is_reserved_keyword(s) {
|
||||
Some(Token::Reserved(Box::new(s.into())))
|
||||
Token::Reserved(Box::new(s.into()))
|
||||
} else {
|
||||
None
|
||||
NO_TOKEN
|
||||
}
|
||||
});
|
||||
|
||||
@ -255,16 +256,16 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
CUSTOM_SYNTAX_MARKER_FLOAT if !segments.is_empty() => s.into(),
|
||||
// Standard or reserved keyword/symbol not in first position
|
||||
_ if !segments.is_empty() && token.is_some() => {
|
||||
_ if !segments.is_empty() && token != NO_TOKEN => {
|
||||
// Make it a custom keyword/symbol if it is disabled or reserved
|
||||
if (self
|
||||
.disabled_symbols
|
||||
.as_ref()
|
||||
.as_deref()
|
||||
.map_or(false, |m| m.contains(s))
|
||||
|| token.map_or(false, |v| v.is_reserved()))
|
||||
|| token.is_reserved())
|
||||
&& !self
|
||||
.custom_keywords
|
||||
.as_ref()
|
||||
.as_deref()
|
||||
.map_or(false, |m| m.contains_key(s))
|
||||
{
|
||||
self.custom_keywords
|
||||
@ -275,10 +276,10 @@ impl Engine {
|
||||
}
|
||||
// Standard keyword in first position but not disabled
|
||||
_ if segments.is_empty()
|
||||
&& token.as_ref().map_or(false, Token::is_standard_keyword)
|
||||
&& token.is_standard_keyword()
|
||||
&& !self
|
||||
.disabled_symbols
|
||||
.as_ref()
|
||||
.as_deref()
|
||||
.map_or(false, |m| m.contains(s)) =>
|
||||
{
|
||||
return Err(LexError::ImproperSymbol(
|
||||
@ -295,12 +296,12 @@ impl Engine {
|
||||
// Make it a custom keyword/symbol if it is disabled or reserved
|
||||
if self
|
||||
.disabled_symbols
|
||||
.as_ref()
|
||||
.as_deref()
|
||||
.map_or(false, |m| m.contains(s))
|
||||
|| (token.map_or(false, |v| v.is_reserved())
|
||||
|| (token.is_reserved()
|
||||
&& !self
|
||||
.custom_keywords
|
||||
.as_ref()
|
||||
.as_deref()
|
||||
.map_or(false, |m| m.contains_key(s)))
|
||||
{
|
||||
self.custom_keywords
|
||||
|
@ -29,6 +29,7 @@ impl Engine {
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn definitions(&self) -> Definitions {
|
||||
Definitions {
|
||||
engine: self,
|
||||
@ -55,6 +56,7 @@ impl Engine {
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn definitions_with_scope<'e>(&'e self, scope: &'e Scope<'e>) -> Definitions<'e> {
|
||||
Definitions {
|
||||
engine: self,
|
||||
@ -67,7 +69,6 @@ impl Engine {
|
||||
/// Internal configuration for module generation.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
#[non_exhaustive]
|
||||
#[must_use]
|
||||
pub struct DefinitionsConfig {
|
||||
/// Write `module ...` headers in definition files (default `false`).
|
||||
pub write_headers: bool,
|
||||
@ -77,6 +78,7 @@ pub struct DefinitionsConfig {
|
||||
|
||||
impl Default for DefinitionsConfig {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
write_headers: false,
|
||||
@ -89,7 +91,6 @@ impl Default for DefinitionsConfig {
|
||||
/// contents of an [`Engine`].
|
||||
/// Exported under the `internals` and `metadata` feature only.
|
||||
#[derive(Debug, Clone)]
|
||||
#[must_use]
|
||||
pub struct Definitions<'e> {
|
||||
/// The [`Engine`].
|
||||
engine: &'e Engine,
|
||||
@ -104,12 +105,14 @@ 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)]
|
||||
#[must_use]
|
||||
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)]
|
||||
#[must_use]
|
||||
pub const fn include_standard_packages(mut self, include_standard_packages: bool) -> Self {
|
||||
self.config.include_standard_packages = include_standard_packages;
|
||||
self
|
||||
@ -128,6 +131,7 @@ impl Definitions<'_> {
|
||||
}
|
||||
/// Get the configuration.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) const fn config(&self) -> &DefinitionsConfig {
|
||||
&self.config
|
||||
}
|
||||
@ -368,8 +372,9 @@ impl Definitions<'_> {
|
||||
let mut m = self
|
||||
.engine
|
||||
.global_sub_modules
|
||||
.iter()
|
||||
.flat_map(|m| m.iter())
|
||||
.as_deref()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(move |(name, module)| {
|
||||
(
|
||||
name.to_string(),
|
||||
@ -391,6 +396,7 @@ impl Definitions<'_> {
|
||||
impl Module {
|
||||
/// Return definitions for all items inside the [`Module`].
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[must_use]
|
||||
fn definition(&self, def: &Definitions) -> String {
|
||||
let mut s = String::new();
|
||||
self.write_definition(&mut s, def).unwrap();
|
||||
@ -455,7 +461,7 @@ impl Module {
|
||||
|| def
|
||||
.engine
|
||||
.custom_keywords
|
||||
.as_ref()
|
||||
.as_deref()
|
||||
.map_or(false, |m| m.contains_key(f.metadata.name.as_str()));
|
||||
|
||||
f.write_definition(writer, def, operator)?;
|
||||
@ -536,6 +542,7 @@ impl FuncInfo {
|
||||
/// It tries to flatten types, removing `&` and `&mut`, and paths, while keeping generics.
|
||||
///
|
||||
/// Associated generic types are also rewritten into regular generic type parameters.
|
||||
#[must_use]
|
||||
fn def_type_name<'a>(ty: &'a str, engine: &'a Engine) -> Cow<'a, str> {
|
||||
let ty = engine.format_type_name(ty).replace("crate::", "");
|
||||
let ty = ty.strip_prefix("&mut").unwrap_or(&*ty).trim();
|
||||
|
@ -26,7 +26,7 @@ impl Engine {
|
||||
///
|
||||
/// This method is deprecated. Use [`run_file`][Engine::run_file] instead.
|
||||
///
|
||||
/// This method will be removed in the next majocd cr version.
|
||||
/// This method will be removed in the next major version.
|
||||
#[deprecated(since = "1.1.0", note = "use `run_file` instead")]
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
@ -137,12 +137,6 @@ impl Engine {
|
||||
}
|
||||
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
||||
///
|
||||
/// The following options are available:
|
||||
///
|
||||
/// * whether to evaluate the [`AST`] to load necessary modules before calling the function
|
||||
/// * whether to rewind the [`Scope`] after the function call
|
||||
/// * a value for binding to the `this` pointer (if any)
|
||||
///
|
||||
/// Not available under `no_function`.
|
||||
///
|
||||
/// # Deprecated
|
||||
@ -191,10 +185,10 @@ impl Engine {
|
||||
/// This method will be removed in the next major version.
|
||||
#[deprecated(since = "1.9.1", note = "use `register_fn` instead")]
|
||||
#[inline(always)]
|
||||
pub fn register_result_fn<A, R, S>(
|
||||
pub fn register_result_fn<A: 'static, const N: usize, const C: bool, R: Variant + Clone>(
|
||||
&mut self,
|
||||
name: impl AsRef<str> + Into<Identifier>,
|
||||
func: impl RegisterNativeFunction<A, R, RhaiResultOf<S>>,
|
||||
func: impl RegisterNativeFunction<A, N, C, R, true>,
|
||||
) -> &mut Self {
|
||||
self.register_fn(name, func)
|
||||
}
|
||||
@ -212,12 +206,10 @@ impl Engine {
|
||||
#[deprecated(since = "1.9.1", note = "use `register_get` instead")]
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[inline(always)]
|
||||
pub fn register_get_result<T: Variant + Clone, V: Variant + Clone, S>(
|
||||
pub fn register_get_result<T: Variant + Clone, const C: bool, V: Variant + Clone>(
|
||||
&mut self,
|
||||
name: impl AsRef<str>,
|
||||
get_fn: impl RegisterNativeFunction<(Mut<T>,), V, RhaiResultOf<S>>
|
||||
+ crate::func::SendSync
|
||||
+ 'static,
|
||||
get_fn: impl RegisterNativeFunction<(Mut<T>,), 1, C, V, true> + crate::func::SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
self.register_get(name, get_fn)
|
||||
}
|
||||
@ -233,10 +225,10 @@ impl Engine {
|
||||
#[deprecated(since = "1.9.1", note = "use `register_set` instead")]
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[inline(always)]
|
||||
pub fn register_set_result<T: Variant + Clone, V: Variant + Clone, S>(
|
||||
pub fn register_set_result<T: Variant + Clone, V: Variant + Clone, const C: bool, S>(
|
||||
&mut self,
|
||||
name: impl AsRef<str>,
|
||||
set_fn: impl RegisterNativeFunction<(Mut<T>, V), (), RhaiResultOf<S>>
|
||||
set_fn: impl RegisterNativeFunction<(Mut<T>, V), 2, C, (), true>
|
||||
+ crate::func::SendSync
|
||||
+ 'static,
|
||||
) -> &mut Self {
|
||||
@ -253,12 +245,6 @@ impl Engine {
|
||||
/// This method is deprecated. Use [`register_indexer_get`][Engine::register_indexer_get] instead.
|
||||
///
|
||||
/// This method will be removed in the next major version.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the type is [`Array`][crate::Array], [`Map`][crate::Map], [`String`],
|
||||
/// [`ImmutableString`][crate::ImmutableString], `&str` or [`INT`][crate::INT].
|
||||
/// Indexers for arrays, object maps, strings and integers cannot be registered.
|
||||
#[deprecated(since = "1.9.1", note = "use `register_indexer_get` instead")]
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
#[inline(always)]
|
||||
@ -266,10 +252,10 @@ impl Engine {
|
||||
T: Variant + Clone,
|
||||
X: Variant + Clone,
|
||||
V: Variant + Clone,
|
||||
S,
|
||||
const C: bool,
|
||||
>(
|
||||
&mut self,
|
||||
get_fn: impl RegisterNativeFunction<(Mut<T>, X), V, RhaiResultOf<S>>
|
||||
get_fn: impl RegisterNativeFunction<(Mut<T>, X), 2, C, V, true>
|
||||
+ crate::func::SendSync
|
||||
+ 'static,
|
||||
) -> &mut Self {
|
||||
@ -284,12 +270,6 @@ impl Engine {
|
||||
/// This method is deprecated. Use [`register_indexer_set`][Engine::register_indexer_set] instead.
|
||||
///
|
||||
/// This method will be removed in the next major version.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the type is [`Array`][crate::Array], [`Map`][crate::Map], [`String`],
|
||||
/// [`ImmutableString`][crate::ImmutableString], `&str` or [`INT`][crate::INT].
|
||||
/// Indexers for arrays, object maps, strings and integers cannot be registered.
|
||||
#[deprecated(since = "1.9.1", note = "use `register_indexer_set` instead")]
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
#[inline(always)]
|
||||
@ -297,10 +277,10 @@ impl Engine {
|
||||
T: Variant + Clone,
|
||||
X: Variant + Clone,
|
||||
V: Variant + Clone,
|
||||
S,
|
||||
const C: bool,
|
||||
>(
|
||||
&mut self,
|
||||
set_fn: impl RegisterNativeFunction<(Mut<T>, X, V), (), RhaiResultOf<S>>
|
||||
set_fn: impl RegisterNativeFunction<(Mut<T>, X, V), 3, C, (), true>
|
||||
+ crate::func::SendSync
|
||||
+ 'static,
|
||||
) -> &mut Self {
|
||||
@ -316,34 +296,6 @@ impl Engine {
|
||||
/// Use [`register_custom_syntax_with_state_raw`][Engine::register_custom_syntax_with_state_raw] instead.
|
||||
///
|
||||
/// This method will be removed in the next major version.
|
||||
///
|
||||
/// # WARNING - Low Level API
|
||||
///
|
||||
/// This function is very low level.
|
||||
///
|
||||
/// * `scope_may_be_changed` specifies variables have been added/removed by this custom syntax.
|
||||
/// * `parse` is the parsing function.
|
||||
/// * `func` is the implementation function.
|
||||
///
|
||||
/// All custom keywords used as symbols must be manually registered via [`Engine::register_custom_operator`].
|
||||
/// Otherwise, they won't be recognized.
|
||||
///
|
||||
/// # Parsing Function Signature
|
||||
///
|
||||
/// The parsing function has the following signature:
|
||||
///
|
||||
/// `Fn(symbols: &[ImmutableString], look_ahead: &str) -> Result<Option<ImmutableString>, ParseError>`
|
||||
///
|
||||
/// where:
|
||||
/// * `symbols`: a slice of symbols that have been parsed so far, possibly containing `$expr$` and/or `$block$`;
|
||||
/// `$ident$` and other literal markers are replaced by the actual text
|
||||
/// * `look_ahead`: a string slice containing the next symbol that is about to be read
|
||||
///
|
||||
/// ## Return value
|
||||
///
|
||||
/// * `Ok(None)`: parsing complete and there are no more symbols to match.
|
||||
/// * `Ok(Some(symbol))`: the next symbol to match, which can also be `$expr$`, `$ident$` or `$block$`.
|
||||
/// * `Err(ParseError)`: error that is reflected back to the [`Engine`], normally `ParseError(ParseErrorType::BadInput(LexError::ImproperSymbol(message)), Position::NONE)` to indicate a syntax error, but it can be any [`ParseError`][crate::ParseError].
|
||||
#[deprecated(
|
||||
since = "1.11.0",
|
||||
note = "use `register_custom_syntax_with_state_raw` instead"
|
||||
@ -368,6 +320,24 @@ impl Engine {
|
||||
move |context, expressions, _| func(context, expressions),
|
||||
)
|
||||
}
|
||||
/// _(internals)_ Evaluate a list of statements with no `this` pointer.
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
/// # Deprecated
|
||||
///
|
||||
/// This method is deprecated. It will be removed in the next major version.
|
||||
#[cfg(feature = "internals")]
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "1.12.0")]
|
||||
pub fn eval_statements_raw(
|
||||
&self,
|
||||
global: &mut crate::eval::GlobalRuntimeState,
|
||||
caches: &mut crate::eval::Caches,
|
||||
scope: &mut Scope,
|
||||
statements: &[crate::ast::Stmt],
|
||||
) -> RhaiResult {
|
||||
self.eval_global_statements(global, caches, scope, statements)
|
||||
}
|
||||
}
|
||||
|
||||
impl Dynamic {
|
||||
@ -544,10 +514,15 @@ impl<'a, T: Variant + Clone> TypeBuilder<'a, T> {
|
||||
/// This method will be removed in the next major version.
|
||||
#[deprecated(since = "1.9.1", note = "use `with_fn` instead")]
|
||||
#[inline(always)]
|
||||
pub fn with_result_fn<N, A, F, R, S>(&mut self, name: N, method: F) -> &mut Self
|
||||
pub fn with_result_fn<S, A: 'static, const N: usize, const C: bool, R, F>(
|
||||
&mut self,
|
||||
name: S,
|
||||
method: F,
|
||||
) -> &mut Self
|
||||
where
|
||||
N: AsRef<str> + Into<Identifier>,
|
||||
F: RegisterNativeFunction<A, R, RhaiResultOf<S>>,
|
||||
S: AsRef<str> + Into<Identifier>,
|
||||
R: Variant + Clone,
|
||||
F: RegisterNativeFunction<A, N, C, R, true>,
|
||||
{
|
||||
self.with_fn(name, method)
|
||||
}
|
||||
@ -566,12 +541,10 @@ impl<'a, T: Variant + Clone> TypeBuilder<'a, T> {
|
||||
#[deprecated(since = "1.9.1", note = "use `with_get` instead")]
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[inline(always)]
|
||||
pub fn with_get_result<V: Variant + Clone, S>(
|
||||
pub fn with_get_result<V: Variant + Clone, const C: bool>(
|
||||
&mut self,
|
||||
name: impl AsRef<str>,
|
||||
get_fn: impl RegisterNativeFunction<(Mut<T>,), V, RhaiResultOf<S>>
|
||||
+ crate::func::SendSync
|
||||
+ 'static,
|
||||
get_fn: impl RegisterNativeFunction<(Mut<T>,), 1, C, V, true> + crate::func::SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
self.with_get(name, get_fn)
|
||||
}
|
||||
@ -588,10 +561,10 @@ impl<'a, T: Variant + Clone> TypeBuilder<'a, T> {
|
||||
#[deprecated(since = "1.9.1", note = "use `with_set` instead")]
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[inline(always)]
|
||||
pub fn with_set_result<V: Variant + Clone, S>(
|
||||
pub fn with_set_result<V: Variant + Clone, const C: bool>(
|
||||
&mut self,
|
||||
name: impl AsRef<str>,
|
||||
set_fn: impl RegisterNativeFunction<(Mut<T>, V), (), RhaiResultOf<S>>
|
||||
set_fn: impl RegisterNativeFunction<(Mut<T>, V), 2, C, (), true>
|
||||
+ crate::func::SendSync
|
||||
+ 'static,
|
||||
) -> &mut Self {
|
||||
@ -612,9 +585,9 @@ impl<'a, T: Variant + Clone> TypeBuilder<'a, T> {
|
||||
#[deprecated(since = "1.9.1", note = "use `with_indexer_get` instead")]
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
#[inline(always)]
|
||||
pub fn with_indexer_get_result<X: Variant + Clone, V: Variant + Clone, S>(
|
||||
pub fn with_indexer_get_result<X: Variant + Clone, V: Variant + Clone, const C: bool>(
|
||||
&mut self,
|
||||
get_fn: impl RegisterNativeFunction<(Mut<T>, X), V, RhaiResultOf<S>>
|
||||
get_fn: impl RegisterNativeFunction<(Mut<T>, X), 2, C, V, true>
|
||||
+ crate::func::SendSync
|
||||
+ 'static,
|
||||
) -> &mut Self {
|
||||
@ -633,9 +606,9 @@ impl<'a, T: Variant + Clone> TypeBuilder<'a, T> {
|
||||
#[deprecated(since = "1.9.1", note = "use `with_indexer_set` instead")]
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
#[inline(always)]
|
||||
pub fn with_indexer_set_result<X: Variant + Clone, V: Variant + Clone, S>(
|
||||
pub fn with_indexer_set_result<X: Variant + Clone, V: Variant + Clone, const C: bool>(
|
||||
&mut self,
|
||||
set_fn: impl RegisterNativeFunction<(Mut<T>, X, V), (), RhaiResultOf<S>>
|
||||
set_fn: impl RegisterNativeFunction<(Mut<T>, X, V), 3, C, (), true>
|
||||
+ crate::func::SendSync
|
||||
+ 'static,
|
||||
) -> &mut Self {
|
||||
|
@ -5,7 +5,7 @@ use crate::func::native::locked_write;
|
||||
use crate::parser::ParseState;
|
||||
use crate::types::dynamic::Variant;
|
||||
use crate::{
|
||||
reify, Dynamic, Engine, OptimizationLevel, Position, RhaiResult, RhaiResultOf, Scope, AST, ERR,
|
||||
Dynamic, Engine, OptimizationLevel, Position, RhaiResult, RhaiResultOf, Scope, AST, ERR,
|
||||
};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@ -121,15 +121,14 @@ impl Engine {
|
||||
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 (stream, tc) = self.lex_raw(&scripts, self.token_mapper.as_deref());
|
||||
|
||||
let mut state = ParseState::new(scope, interned_strings, tokenizer_control);
|
||||
let state = &mut ParseState::new(scope, interned_strings, tc);
|
||||
|
||||
// No need to optimize a lone expression
|
||||
self.parse_global_expr(
|
||||
&mut stream.peekable(),
|
||||
&mut state,
|
||||
stream.peekable(),
|
||||
state,
|
||||
|_| {},
|
||||
#[cfg(not(feature = "no_optimize"))]
|
||||
OptimizationLevel::None,
|
||||
@ -243,7 +242,7 @@ impl Engine {
|
||||
let result = self.eval_global_statements(global, caches, scope, statements);
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
if self.debugger.is_some() {
|
||||
if self.is_debugger_registered() {
|
||||
global.debugger_mut().status = crate::eval::DebuggerStatus::Terminate;
|
||||
let mut this = Dynamic::NULL;
|
||||
let node = &crate::ast::Stmt::Noop(Position::NONE);
|
||||
@ -263,25 +262,6 @@ impl Engine {
|
||||
|
||||
result
|
||||
}
|
||||
/// _(internals)_ Evaluate a list of statements with no `this` pointer.
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
/// This is commonly used to evaluate a list of statements in an [`AST`] or a script function body.
|
||||
///
|
||||
/// # WARNING - Low Level API
|
||||
///
|
||||
/// This function is very low level.
|
||||
#[cfg(feature = "internals")]
|
||||
#[inline(always)]
|
||||
pub fn eval_statements_raw(
|
||||
&self,
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
scope: &mut Scope,
|
||||
statements: &[crate::ast::Stmt],
|
||||
) -> RhaiResult {
|
||||
self.eval_global_statements(global, caches, scope, statements)
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluate a string as a script, returning the result value or an error.
|
||||
|
@ -349,7 +349,9 @@ impl Engine {
|
||||
#[inline(always)]
|
||||
pub fn register_debugger(
|
||||
&mut self,
|
||||
init: impl Fn(&Self) -> Dynamic + SendSync + 'static,
|
||||
init: impl Fn(&Self, crate::debugger::Debugger) -> crate::debugger::Debugger
|
||||
+ SendSync
|
||||
+ 'static,
|
||||
callback: impl Fn(
|
||||
EvalContext,
|
||||
crate::eval::DebuggerEvent,
|
||||
@ -360,7 +362,7 @@ impl Engine {
|
||||
+ SendSync
|
||||
+ 'static,
|
||||
) -> &mut Self {
|
||||
self.debugger = Some(Box::new((Box::new(init), Box::new(callback))));
|
||||
self.debugger_interface = Some(Box::new((Box::new(init), Box::new(callback))));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -64,8 +64,8 @@ impl Engine {
|
||||
|
||||
let (stream, tokenizer_control) = self.lex_raw(
|
||||
&scripts,
|
||||
if has_null {
|
||||
Some(&|token, _, _| {
|
||||
Some(if has_null {
|
||||
&|token, _, _| {
|
||||
match token {
|
||||
// `null` => `()`
|
||||
Token::Reserved(s) if &*s == "null" => Token::Unit,
|
||||
@ -86,9 +86,9 @@ impl Engine {
|
||||
// All others
|
||||
_ => token,
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
Some(&|token, _, _| {
|
||||
&|token, _, _| {
|
||||
match token {
|
||||
Token::Reserved(s) if &*s == "null" => Token::LexError(
|
||||
LexError::ImproperSymbol("null".to_string(), String::new()).into(),
|
||||
@ -97,34 +97,31 @@ impl Engine {
|
||||
Token::LeftBrace => Token::MapStart,
|
||||
// Disallowed syntax
|
||||
t @ (Token::Unit | Token::MapStart) => Token::LexError(
|
||||
LexError::ImproperSymbol(
|
||||
t.literal_syntax().to_string(),
|
||||
"Invalid JSON syntax".to_string(),
|
||||
)
|
||||
LexError::ImproperSymbol(t.literal_syntax().to_string(), String::new())
|
||||
.into(),
|
||||
),
|
||||
Token::InterpolatedString(..) => Token::LexError(
|
||||
LexError::ImproperSymbol(
|
||||
"interpolated string".to_string(),
|
||||
"Invalid JSON syntax".to_string(),
|
||||
String::new(),
|
||||
)
|
||||
.into(),
|
||||
),
|
||||
// All others
|
||||
_ => token,
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
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 state = &mut ParseState::new(&scope, interned_strings, tokenizer_control);
|
||||
|
||||
self.parse_global_expr(
|
||||
&mut stream.peekable(),
|
||||
&mut state,
|
||||
stream.peekable(),
|
||||
state,
|
||||
|s| s.flags |= ParseSettingFlags::DISALLOW_UNQUOTED_MAP_PROPERTIES,
|
||||
#[cfg(not(feature = "no_optimize"))]
|
||||
OptimizationLevel::None,
|
||||
|
@ -6,23 +6,20 @@ use std::num::{NonZeroU64, NonZeroUsize};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
pub mod default_limits {
|
||||
#[cfg(debug_assertions)]
|
||||
pub mod default_limits {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub const MAX_CALL_STACK_DEPTH: usize = 8;
|
||||
#[cfg(debug_assertions)]
|
||||
pub const MAX_EXPR_DEPTH: usize = 32;
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[cfg(debug_assertions)]
|
||||
pub const MAX_FUNCTION_EXPR_DEPTH: usize = 16;
|
||||
|
||||
}
|
||||
#[cfg(not(debug_assertions))]
|
||||
pub mod default_limits {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub const MAX_CALL_STACK_DEPTH: usize = 64;
|
||||
#[cfg(not(debug_assertions))]
|
||||
pub const MAX_EXPR_DEPTH: usize = 64;
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[cfg(not(debug_assertions))]
|
||||
pub const MAX_FUNCTION_EXPR_DEPTH: usize = 32;
|
||||
}
|
||||
|
||||
@ -55,7 +52,7 @@ pub struct Limits {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
pub max_modules: usize,
|
||||
/// Maximum length of a [string][crate::ImmutableString].
|
||||
pub max_string_size: Option<NonZeroUsize>,
|
||||
pub max_string_len: Option<NonZeroUsize>,
|
||||
/// Maximum length of an [array][crate::Array].
|
||||
///
|
||||
/// Not available under `no_index`.
|
||||
@ -83,7 +80,7 @@ impl Limits {
|
||||
max_operations: None,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
max_modules: usize::MAX,
|
||||
max_string_size: None,
|
||||
max_string_len: None,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
max_array_size: None,
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
@ -104,7 +101,7 @@ impl Engine {
|
||||
/// Is there a data size limit set?
|
||||
#[inline(always)]
|
||||
pub(crate) const fn has_data_size_limit(&self) -> bool {
|
||||
self.limits.max_string_size.is_some()
|
||||
self.limits.max_string_len.is_some()
|
||||
|| {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
{
|
||||
@ -222,19 +219,19 @@ impl Engine {
|
||||
#[cfg(feature = "no_function")]
|
||||
return 0;
|
||||
}
|
||||
/// Set the maximum length of [strings][crate::ImmutableString] (0 for unlimited).
|
||||
/// Set the maximum length, in bytes, of [strings][crate::ImmutableString] (0 for unlimited).
|
||||
///
|
||||
/// Not available under `unchecked`.
|
||||
#[inline(always)]
|
||||
pub fn set_max_string_size(&mut self, max_size: usize) -> &mut Self {
|
||||
self.limits.max_string_size = NonZeroUsize::new(max_size);
|
||||
pub fn set_max_string_size(&mut self, max_len: usize) -> &mut Self {
|
||||
self.limits.max_string_len = NonZeroUsize::new(max_len);
|
||||
self
|
||||
}
|
||||
/// The maximum length of [strings][crate::ImmutableString] (0 for unlimited).
|
||||
/// The maximum length, in bytes, of [strings][crate::ImmutableString] (0 for unlimited).
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn max_string_size(&self) -> usize {
|
||||
match self.limits.max_string_size {
|
||||
match self.limits.max_string_len {
|
||||
Some(n) => n.get(),
|
||||
None => 0,
|
||||
}
|
||||
|
71
src/api/limits_unchecked.rs
Normal file
71
src/api/limits_unchecked.rs
Normal file
@ -0,0 +1,71 @@
|
||||
//! Placeholder settings for [`Engine`]'s limitations.
|
||||
#![cfg(feature = "unchecked")]
|
||||
|
||||
use crate::Engine;
|
||||
|
||||
impl Engine {
|
||||
/// The maximum levels of function calls allowed for a script.
|
||||
///
|
||||
/// Always returns [`usize::MAX`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_call_levels(&self) -> usize {
|
||||
usize::MAX
|
||||
}
|
||||
/// The maximum number of operations allowed for a script to run (0 for unlimited).
|
||||
///
|
||||
/// Always returns zero.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_operations(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
/// The maximum number of imported [modules][crate::Module] allowed for a script.
|
||||
///
|
||||
/// Always returns [`usize::MAX`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_modules(&self) -> usize {
|
||||
usize::MAX
|
||||
}
|
||||
/// The depth limit for expressions (0 for unlimited).
|
||||
///
|
||||
/// Always returns zero.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_expr_depth(&self) -> usize {
|
||||
0
|
||||
}
|
||||
/// The depth limit for expressions in functions (0 for unlimited).
|
||||
///
|
||||
/// Always returns zero.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_function_expr_depth(&self) -> usize {
|
||||
0
|
||||
}
|
||||
/// The maximum length of [strings][crate::ImmutableString] (0 for unlimited).
|
||||
///
|
||||
/// Always returns zero.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_string_size(&self) -> usize {
|
||||
0
|
||||
}
|
||||
/// The maximum length of [arrays][crate::Array] (0 for unlimited).
|
||||
///
|
||||
/// Always returns zero.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_array_size(&self) -> usize {
|
||||
0
|
||||
}
|
||||
/// The maximum size of [object maps][crate::Map] (0 for unlimited).
|
||||
///
|
||||
/// Always returns zero.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_map_size(&self) -> usize {
|
||||
0
|
||||
}
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
//! Module defining the public API of the Rhai engine.
|
||||
|
||||
pub mod type_names;
|
||||
|
||||
pub mod eval;
|
||||
|
||||
pub mod run;
|
||||
@ -21,18 +19,21 @@ pub mod options;
|
||||
pub mod optimize;
|
||||
|
||||
pub mod limits;
|
||||
pub mod limits_unchecked;
|
||||
|
||||
pub mod events;
|
||||
|
||||
pub mod custom_syntax;
|
||||
pub mod type_names;
|
||||
|
||||
pub mod deprecated;
|
||||
pub mod custom_syntax;
|
||||
|
||||
pub mod build_type;
|
||||
|
||||
#[cfg(feature = "metadata")]
|
||||
pub mod definitions;
|
||||
|
||||
pub mod deprecated;
|
||||
|
||||
use crate::{Dynamic, Engine, Identifier};
|
||||
|
||||
#[cfg(feature = "no_std")]
|
||||
@ -167,7 +168,7 @@ impl Engine {
|
||||
Some(token) if token.is_standard_keyword() => {
|
||||
if !self
|
||||
.disabled_symbols
|
||||
.as_ref()
|
||||
.as_deref()
|
||||
.map_or(false, |m| m.contains(token.literal_syntax()))
|
||||
{
|
||||
return Err(format!("'{keyword}' is a reserved keyword"));
|
||||
@ -177,7 +178,7 @@ impl Engine {
|
||||
Some(token) if token.is_standard_symbol() => {
|
||||
if !self
|
||||
.disabled_symbols
|
||||
.as_ref()
|
||||
.as_deref()
|
||||
.map_or(false, |m| m.contains(token.literal_syntax()))
|
||||
{
|
||||
return Err(format!("'{keyword}' is a reserved operator"));
|
||||
@ -187,7 +188,7 @@ impl Engine {
|
||||
Some(token)
|
||||
if !self
|
||||
.disabled_symbols
|
||||
.as_ref()
|
||||
.as_deref()
|
||||
.map_or(false, |m| m.contains(token.literal_syntax())) =>
|
||||
{
|
||||
return Err(format!("'{keyword}' is a reserved symbol"))
|
||||
@ -223,71 +224,3 @@ impl Engine {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unchecked")]
|
||||
impl Engine {
|
||||
/// The maximum levels of function calls allowed for a script.
|
||||
///
|
||||
/// Always returns [`usize::MAX`] under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_call_levels(&self) -> usize {
|
||||
usize::MAX
|
||||
}
|
||||
/// The maximum number of operations allowed for a script to run (0 for unlimited).
|
||||
///
|
||||
/// Always returns zero under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_operations(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
/// The maximum number of imported [modules][crate::Module] allowed for a script.
|
||||
///
|
||||
/// Always returns [`usize::MAX`] under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_modules(&self) -> usize {
|
||||
usize::MAX
|
||||
}
|
||||
/// The depth limit for expressions (0 for unlimited).
|
||||
///
|
||||
/// Always returns zero under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_expr_depth(&self) -> usize {
|
||||
0
|
||||
}
|
||||
/// The depth limit for expressions in functions (0 for unlimited).
|
||||
///
|
||||
/// Always returns zero under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_function_expr_depth(&self) -> usize {
|
||||
0
|
||||
}
|
||||
/// The maximum length of [strings][crate::ImmutableString] (0 for unlimited).
|
||||
///
|
||||
/// Always returns zero under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_string_size(&self) -> usize {
|
||||
0
|
||||
}
|
||||
/// The maximum length of [arrays][crate::Array] (0 for unlimited).
|
||||
///
|
||||
/// Always returns zero under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_array_size(&self) -> usize {
|
||||
0
|
||||
}
|
||||
/// The maximum size of [object maps][crate::Map] (0 for unlimited).
|
||||
///
|
||||
/// Always returns zero under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_map_size(&self) -> usize {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
@ -51,20 +51,14 @@ impl Engine {
|
||||
) -> AST {
|
||||
let mut ast = ast;
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
let functions = ast
|
||||
.shared_lib()
|
||||
.iter_fn()
|
||||
.filter(|f| f.func.is_script())
|
||||
.map(|f| f.func.get_script_fn_def().unwrap().clone())
|
||||
.collect();
|
||||
|
||||
let mut _new_ast = crate::optimizer::optimize_into_ast(
|
||||
self,
|
||||
let mut _new_ast = self.optimize_into_ast(
|
||||
scope,
|
||||
ast.take_statements(),
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
functions,
|
||||
ast.shared_lib()
|
||||
.iter_fn()
|
||||
.map(|f| f.func.get_script_fn_def().cloned().expect("`ScriptFnDef"))
|
||||
.collect(),
|
||||
optimization_level,
|
||||
);
|
||||
|
||||
|
@ -56,7 +56,14 @@ impl Engine {
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn register_fn<A, R, S, F: RegisterNativeFunction<A, R, S>>(
|
||||
pub fn register_fn<
|
||||
A: 'static,
|
||||
const N: usize,
|
||||
const C: bool,
|
||||
R: Variant + Clone,
|
||||
const L: bool,
|
||||
F: RegisterNativeFunction<A, N, C, R, L>,
|
||||
>(
|
||||
&mut self,
|
||||
name: impl AsRef<str> + Into<Identifier>,
|
||||
func: F,
|
||||
@ -83,13 +90,24 @@ impl Engine {
|
||||
#[cfg(not(feature = "metadata"))]
|
||||
let param_type_names: Option<&[&str]> = None;
|
||||
|
||||
let fn_name = name.as_ref();
|
||||
let no_const = false;
|
||||
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
let no_const = no_const || (F::num_params() == 3 && fn_name == crate::engine::FN_IDX_SET);
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
let no_const =
|
||||
no_const || (F::num_params() == 2 && fn_name.starts_with(crate::engine::FN_SET));
|
||||
|
||||
let func = func.into_callable_function(fn_name.into(), no_const);
|
||||
|
||||
self.global_namespace_mut().set_fn(
|
||||
name,
|
||||
FnNamespace::Global,
|
||||
FnAccess::Public,
|
||||
param_type_names,
|
||||
param_types,
|
||||
func.into_callable_function(),
|
||||
func,
|
||||
);
|
||||
self
|
||||
}
|
||||
@ -299,10 +317,10 @@ impl Engine {
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[inline(always)]
|
||||
pub fn register_get<T: Variant + Clone, V: Variant + Clone, S>(
|
||||
pub fn register_get<T: Variant + Clone, const C: bool, V: Variant + Clone, const L: bool>(
|
||||
&mut self,
|
||||
name: impl AsRef<str>,
|
||||
get_fn: impl RegisterNativeFunction<(Mut<T>,), V, S> + SendSync + 'static,
|
||||
get_fn: impl RegisterNativeFunction<(Mut<T>,), 1, C, V, L> + SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
self.register_fn(crate::engine::make_getter(name.as_ref()).as_str(), get_fn)
|
||||
}
|
||||
@ -349,10 +367,10 @@ impl Engine {
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[inline(always)]
|
||||
pub fn register_set<T: Variant + Clone, V: Variant + Clone, S>(
|
||||
pub fn register_set<T: Variant + Clone, const C: bool, V: Variant + Clone, const L: bool>(
|
||||
&mut self,
|
||||
name: impl AsRef<str>,
|
||||
set_fn: impl RegisterNativeFunction<(Mut<T>, V), (), S> + SendSync + 'static,
|
||||
set_fn: impl RegisterNativeFunction<(Mut<T>, V), 2, C, (), L> + SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
self.register_fn(crate::engine::make_setter(name.as_ref()).as_str(), set_fn)
|
||||
}
|
||||
@ -403,11 +421,18 @@ impl Engine {
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[inline(always)]
|
||||
pub fn register_get_set<T: Variant + Clone, V: Variant + Clone, S1, S2>(
|
||||
pub fn register_get_set<
|
||||
T: Variant + Clone,
|
||||
const C1: bool,
|
||||
const C2: bool,
|
||||
V: Variant + Clone,
|
||||
const L1: bool,
|
||||
const L2: bool,
|
||||
>(
|
||||
&mut self,
|
||||
name: impl AsRef<str>,
|
||||
get_fn: impl RegisterNativeFunction<(Mut<T>,), V, S1> + SendSync + 'static,
|
||||
set_fn: impl RegisterNativeFunction<(Mut<T>, V), (), S2> + SendSync + 'static,
|
||||
get_fn: impl RegisterNativeFunction<(Mut<T>,), 1, C1, V, L1> + SendSync + 'static,
|
||||
set_fn: impl RegisterNativeFunction<(Mut<T>, V), 2, C2, (), L2> + SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
self.register_get(&name, get_fn).register_set(&name, set_fn)
|
||||
}
|
||||
@ -462,9 +487,15 @@ impl Engine {
|
||||
/// ```
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
#[inline]
|
||||
pub fn register_indexer_get<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone, S>(
|
||||
pub fn register_indexer_get<
|
||||
T: Variant + Clone,
|
||||
X: Variant + Clone,
|
||||
const C: bool,
|
||||
V: Variant + Clone,
|
||||
const L: bool,
|
||||
>(
|
||||
&mut self,
|
||||
get_fn: impl RegisterNativeFunction<(Mut<T>, X), V, S> + SendSync + 'static,
|
||||
get_fn: impl RegisterNativeFunction<(Mut<T>, X), 2, C, V, L> + SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<crate::Array>() {
|
||||
@ -537,9 +568,15 @@ impl Engine {
|
||||
/// ```
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
#[inline]
|
||||
pub fn register_indexer_set<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone, S>(
|
||||
pub fn register_indexer_set<
|
||||
T: Variant + Clone,
|
||||
X: Variant + Clone,
|
||||
const C: bool,
|
||||
V: Variant + Clone,
|
||||
const L: bool,
|
||||
>(
|
||||
&mut self,
|
||||
set_fn: impl RegisterNativeFunction<(Mut<T>, X, V), (), S> + SendSync + 'static,
|
||||
set_fn: impl RegisterNativeFunction<(Mut<T>, X, V), 3, C, (), L> + SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<crate::Array>() {
|
||||
@ -616,13 +653,15 @@ impl Engine {
|
||||
pub fn register_indexer_get_set<
|
||||
T: Variant + Clone,
|
||||
X: Variant + Clone,
|
||||
const C1: bool,
|
||||
const C2: bool,
|
||||
V: Variant + Clone,
|
||||
S1,
|
||||
S2,
|
||||
const L1: bool,
|
||||
const L2: bool,
|
||||
>(
|
||||
&mut self,
|
||||
get_fn: impl RegisterNativeFunction<(Mut<T>, X), V, S1> + SendSync + 'static,
|
||||
set_fn: impl RegisterNativeFunction<(Mut<T>, X, V), (), S2> + SendSync + 'static,
|
||||
get_fn: impl RegisterNativeFunction<(Mut<T>, X), 2, C1, V, L1> + SendSync + 'static,
|
||||
set_fn: impl RegisterNativeFunction<(Mut<T>, X, V), 3, C2, (), L2> + SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
self.register_indexer_get(get_fn)
|
||||
.register_indexer_set(set_fn)
|
||||
@ -743,7 +782,7 @@ impl Engine {
|
||||
signatures.extend(self.global_namespace().gen_fn_signatures());
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
for (name, m) in self.global_sub_modules.iter().flat_map(|m| m.iter()) {
|
||||
for (name, m) in self.global_sub_modules.as_deref().into_iter().flatten() {
|
||||
signatures.extend(m.gen_fn_signatures().map(|f| format!("{name}::{f}")));
|
||||
}
|
||||
|
||||
|
@ -58,14 +58,10 @@ impl Engine {
|
||||
pub fn run_with_scope(&self, scope: &mut Scope, script: &str) -> RhaiResultOf<()> {
|
||||
let scripts = [script];
|
||||
let ast = {
|
||||
let (stream, tc) = self.lex_raw(&scripts, self.token_mapper.as_deref());
|
||||
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)?
|
||||
let state = &mut ParseState::new(scope, interned_strings, tc);
|
||||
self.parse(stream.peekable(), state, self.optimization_level)?
|
||||
};
|
||||
self.run_ast_with_scope(scope, &ast)
|
||||
}
|
||||
@ -131,19 +127,23 @@ impl Engine {
|
||||
}
|
||||
|
||||
let statements = ast.statements();
|
||||
if !statements.is_empty() {
|
||||
self.eval_global_statements(global, caches, scope, statements)?;
|
||||
}
|
||||
|
||||
let result = if !statements.is_empty() {
|
||||
self.eval_global_statements(global, caches, scope, statements)
|
||||
.map(|_| ())
|
||||
} else {
|
||||
Ok(())
|
||||
};
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
if self.debugger.is_some() {
|
||||
if self.is_debugger_registered() {
|
||||
global.debugger_mut().status = crate::eval::DebuggerStatus::Terminate;
|
||||
let mut this = crate::Dynamic::NULL;
|
||||
let node = &crate::ast::Stmt::Noop(crate::Position::NONE);
|
||||
self.run_debugger(global, caches, scope, &mut this, node)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -204,8 +204,9 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
return self
|
||||
.global_sub_modules
|
||||
.iter()
|
||||
.flat_map(|m| m.iter())
|
||||
.as_deref()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.find_map(|(_, m)| m.get_custom_type(name));
|
||||
#[cfg(feature = "no_module")]
|
||||
return None;
|
||||
@ -238,8 +239,9 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
return self
|
||||
.global_sub_modules
|
||||
.iter()
|
||||
.flat_map(|m| m.iter())
|
||||
.as_deref()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.find_map(|(_, m)| m.get_custom_type(name));
|
||||
#[cfg(feature = "no_module")]
|
||||
return None;
|
||||
|
@ -1,9 +1,9 @@
|
||||
//! Module defining script expressions.
|
||||
|
||||
use super::{ASTFlags, ASTNode, Ident, Stmt, StmtBlock};
|
||||
use super::{ASTFlags, ASTNode, Ident, Namespace, Stmt, StmtBlock};
|
||||
use crate::engine::{KEYWORD_FN_PTR, OP_EXCLUSIVE_RANGE, OP_INCLUSIVE_RANGE};
|
||||
use crate::func::hashing::ALT_ZERO_HASH;
|
||||
use crate::tokenizer::Token;
|
||||
use crate::tokenizer::{Token, NO_TOKEN};
|
||||
use crate::types::dynamic::Union;
|
||||
use crate::{
|
||||
calc_fn_hash, Dynamic, FnPtr, Identifier, ImmutableString, Position, SmartString, StaticVec,
|
||||
@ -197,8 +197,7 @@ impl FnCallHashes {
|
||||
#[derive(Clone, Hash)]
|
||||
pub struct FnCallExpr {
|
||||
/// Namespace of the function, if any.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
pub namespace: super::Namespace,
|
||||
pub namespace: Namespace,
|
||||
/// Function name.
|
||||
pub name: ImmutableString,
|
||||
/// Pre-calculated hashes.
|
||||
@ -208,7 +207,8 @@ pub struct FnCallExpr {
|
||||
/// Does this function call capture the parent scope?
|
||||
pub capture_parent_scope: bool,
|
||||
/// Is this function call a native operator?
|
||||
pub op_token: Option<Token>,
|
||||
/// Otherwise set to [`Token::NONE`].
|
||||
pub op_token: Token,
|
||||
}
|
||||
|
||||
impl fmt::Debug for FnCallExpr {
|
||||
@ -216,15 +216,14 @@ impl fmt::Debug for FnCallExpr {
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut ff = f.debug_struct("FnCallExpr");
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
if !self.namespace.is_empty() {
|
||||
ff.field("namespace", &self.namespace);
|
||||
}
|
||||
ff.field("hash", &self.hashes)
|
||||
.field("name", &self.name)
|
||||
.field("args", &self.args);
|
||||
if let Some(ref token) = self.op_token {
|
||||
ff.field("op_token", token);
|
||||
if self.op_token != NO_TOKEN {
|
||||
ff.field("op_token", &self.op_token);
|
||||
}
|
||||
if self.capture_parent_scope {
|
||||
ff.field("capture_parent_scope", &self.capture_parent_scope);
|
||||
@ -240,10 +239,7 @@ impl FnCallExpr {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn is_qualified(&self) -> bool {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
return !self.namespace.is_empty();
|
||||
#[cfg(feature = "no_module")]
|
||||
return false;
|
||||
!self.namespace.is_empty()
|
||||
}
|
||||
/// Convert this into an [`Expr::FnCall`].
|
||||
#[inline(always)]
|
||||
@ -303,9 +299,7 @@ pub enum Expr {
|
||||
/// majority of cases (unless there are more than 255 variables defined!).
|
||||
/// This is to avoid reading a pointer redirection during each variable access.
|
||||
Variable(
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
Box<(Option<NonZeroUsize>, super::Namespace, u64, ImmutableString)>,
|
||||
#[cfg(feature = "no_module")] Box<(Option<NonZeroUsize>, (), u64, ImmutableString)>,
|
||||
Box<(Option<NonZeroUsize>, Namespace, u64, ImmutableString)>,
|
||||
Option<NonZeroU8>,
|
||||
Position,
|
||||
),
|
||||
@ -583,13 +577,12 @@ impl Expr {
|
||||
|
||||
Union::FnPtr(f, ..) if !f.is_curried() => Self::FnCall(
|
||||
FnCallExpr {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
namespace: super::Namespace::NONE,
|
||||
namespace: Namespace::NONE,
|
||||
name: KEYWORD_FN_PTR.into(),
|
||||
hashes: calc_fn_hash(None, f.fn_name(), 1).into(),
|
||||
args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(),
|
||||
capture_parent_scope: false,
|
||||
op_token: None,
|
||||
op_token: NO_TOKEN,
|
||||
}
|
||||
.into(),
|
||||
pos,
|
||||
|
@ -5,6 +5,7 @@ pub mod expr;
|
||||
pub mod flags;
|
||||
pub mod ident;
|
||||
pub mod namespace;
|
||||
pub mod namespace_none;
|
||||
pub mod script_fn;
|
||||
pub mod stmt;
|
||||
|
||||
@ -16,6 +17,8 @@ pub use flags::{ASTFlags, FnAccess};
|
||||
pub use ident::Ident;
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
pub use namespace::Namespace;
|
||||
#[cfg(feature = "no_module")]
|
||||
pub use namespace_none::Namespace;
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub use script_fn::EncapsulatedEnviron;
|
||||
|
22
src/ast/namespace_none.rs
Normal file
22
src/ast/namespace_none.rs
Normal file
@ -0,0 +1,22 @@
|
||||
//! Namespace reference type.
|
||||
#![cfg(feature = "no_module")]
|
||||
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
/// _(internals)_ A chain of [module][crate::Module] names to namespace-qualify a variable or function call.
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
/// Not available under `no_module`.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Default, Hash)]
|
||||
pub struct Namespace;
|
||||
|
||||
impl Namespace {
|
||||
/// Constant for no namespace.
|
||||
pub const NONE: Self = Self;
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn is_empty(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
@ -16,7 +16,6 @@ use std::{fmt, hash::Hash};
|
||||
///
|
||||
/// Not available under `no_module` or `no_function`.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EncapsulatedEnviron {
|
||||
/// Functions defined within the same [`AST`][crate::AST].
|
||||
@ -24,7 +23,7 @@ pub struct EncapsulatedEnviron {
|
||||
/// Imported [modules][crate::Module].
|
||||
pub imports: Box<[(ImmutableString, crate::SharedModule)]>,
|
||||
/// Globally-defined constants.
|
||||
pub constants: Option<crate::eval::GlobalConstants>,
|
||||
pub constants: Option<crate::eval::SharedGlobalConstants>,
|
||||
}
|
||||
|
||||
/// _(internals)_ A type containing information on a script-defined function.
|
||||
@ -35,7 +34,6 @@ pub struct ScriptFnDef {
|
||||
pub body: StmtBlock,
|
||||
/// Encapsulated AST environment, if any.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub environ: Option<crate::Shared<EncapsulatedEnviron>>,
|
||||
/// Function name.
|
||||
pub name: ImmutableString,
|
||||
@ -51,12 +49,14 @@ pub struct ScriptFnDef {
|
||||
///
|
||||
/// Block doc-comments are kept in a single string slice with line-breaks within.
|
||||
///
|
||||
/// Line doc-comments are kept in one string slice per line without the termination line-break.
|
||||
/// Line doc-comments are merged, with line-breaks, into a single string slice without a termination line-break.
|
||||
///
|
||||
/// Leading white-spaces are stripped, and each string slice always starts with the
|
||||
/// corresponding doc-comment leader: `///` or `/**`.
|
||||
///
|
||||
/// Each line in non-block doc-comments starts with `///`.
|
||||
#[cfg(feature = "metadata")]
|
||||
pub comments: Box<[Box<str>]>,
|
||||
pub comments: Box<[crate::Identifier]>,
|
||||
}
|
||||
|
||||
impl fmt::Display for ScriptFnDef {
|
||||
@ -100,10 +100,12 @@ pub struct ScriptFnMetadata<'a> {
|
||||
///
|
||||
/// Block doc-comments are kept in a single string slice with line-breaks within.
|
||||
///
|
||||
/// Line doc-comments are kept in one string slice per line without the termination line-break.
|
||||
/// Line doc-comments are merged, with line-breaks, into a single string slice without a termination line-break.
|
||||
///
|
||||
/// Leading white-spaces are stripped, and each string slice always starts with the
|
||||
/// corresponding doc-comment leader: `///` or `/**`.
|
||||
///
|
||||
/// Each line in non-block doc-comments starts with `///`.
|
||||
#[cfg(feature = "metadata")]
|
||||
pub comments: Vec<&'a str>,
|
||||
}
|
||||
|
@ -2,7 +2,8 @@
|
||||
|
||||
use super::{ASTFlags, ASTNode, BinaryExpr, Expr, FnCallExpr, Ident};
|
||||
use crate::engine::KEYWORD_EVAL;
|
||||
use crate::tokenizer::{Span, Token};
|
||||
use crate::tokenizer::Token;
|
||||
use crate::types::Span;
|
||||
use crate::{calc_fn_hash, Position, StaticVec, INT};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@ -61,10 +62,8 @@ impl OpAssignment {
|
||||
#[must_use]
|
||||
#[inline(always)]
|
||||
pub fn new_op_assignment(name: &str, pos: Position) -> Self {
|
||||
Self::new_op_assignment_from_token(
|
||||
&Token::lookup_symbol_from_syntax(name).expect("operator"),
|
||||
pos,
|
||||
)
|
||||
let op = Token::lookup_symbol_from_syntax(name).expect("operator");
|
||||
Self::new_op_assignment_from_token(op, pos)
|
||||
}
|
||||
/// Create a new [`OpAssignment`] from a [`Token`].
|
||||
///
|
||||
@ -72,10 +71,11 @@ impl OpAssignment {
|
||||
///
|
||||
/// Panics if the token is not an op-assignment operator.
|
||||
#[must_use]
|
||||
pub fn new_op_assignment_from_token(op: &Token, pos: Position) -> Self {
|
||||
pub fn new_op_assignment_from_token(op: Token, pos: Position) -> Self {
|
||||
let op_raw = op
|
||||
.get_base_op_from_assignment()
|
||||
.expect("op-assignment operator");
|
||||
|
||||
Self {
|
||||
hash_op_assign: calc_fn_hash(None, op.literal_syntax(), 2),
|
||||
hash_op: calc_fn_hash(None, op_raw.literal_syntax(), 2),
|
||||
@ -92,10 +92,8 @@ impl OpAssignment {
|
||||
#[must_use]
|
||||
#[inline(always)]
|
||||
pub fn new_op_assignment_from_base(name: &str, pos: Position) -> Self {
|
||||
Self::new_op_assignment_from_base_token(
|
||||
&Token::lookup_symbol_from_syntax(name).expect("operator"),
|
||||
pos,
|
||||
)
|
||||
let op = Token::lookup_symbol_from_syntax(name).expect("operator");
|
||||
Self::new_op_assignment_from_base_token(op, pos)
|
||||
}
|
||||
/// Convert a [`Token`] into a new [`OpAssignment`].
|
||||
///
|
||||
@ -104,8 +102,8 @@ impl OpAssignment {
|
||||
/// Panics if the token is cannot be converted into an op-assignment operator.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn new_op_assignment_from_base_token(op: &Token, pos: Position) -> Self {
|
||||
Self::new_op_assignment_from_token(&op.convert_to_op_assignment().expect("operator"), pos)
|
||||
pub fn new_op_assignment_from_base_token(op: Token, pos: Position) -> Self {
|
||||
Self::new_op_assignment_from_token(op.convert_to_op_assignment().expect("operator"), pos)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,19 +100,21 @@ fn print_error(input: &str, mut err: EvalAltResult) {
|
||||
// Print error position
|
||||
if pos.is_none() {
|
||||
// No position
|
||||
println!("{err}");
|
||||
println!("\x1b[31m{err}\x1b[39m");
|
||||
} else {
|
||||
// Specific position - print line text
|
||||
println!("{line_no}{}", lines[pos.line().unwrap() - 1]);
|
||||
|
||||
for (i, err_line) in err.to_string().split('\n').enumerate() {
|
||||
// Display position marker
|
||||
println!(
|
||||
"{0:>1$} {err}",
|
||||
"^",
|
||||
line_no.len() + pos.position().unwrap(),
|
||||
"\x1b[31m{0:>1$}{err_line}\x1b[39m",
|
||||
if i > 0 { "| " } else { "^ " },
|
||||
line_no.len() + pos.position().unwrap() + 1,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Print debug help.
|
||||
fn print_debug_help() {
|
||||
@ -237,7 +239,13 @@ fn debug_callback(
|
||||
) -> Result<DebuggerCommand, Box<EvalAltResult>> {
|
||||
// Check event
|
||||
match event {
|
||||
DebuggerEvent::Start if source.is_some() => {
|
||||
println!("\x1b[32m! Script '{}' start\x1b[39m", source.unwrap())
|
||||
}
|
||||
DebuggerEvent::Start => println!("\x1b[32m! Script start\x1b[39m"),
|
||||
DebuggerEvent::End if source.is_some() => {
|
||||
println!("\x1b[31m! Script '{}' end\x1b[39m", source.unwrap())
|
||||
}
|
||||
DebuggerEvent::End => println!("\x1b[31m! Script end\x1b[39m"),
|
||||
DebuggerEvent::Step => (),
|
||||
DebuggerEvent::BreakPoint(n) => {
|
||||
@ -572,7 +580,7 @@ fn debug_callback(
|
||||
break Err(EvalAltResult::ErrorRuntime(msg.trim().into(), pos).into());
|
||||
}
|
||||
["run" | "r"] => {
|
||||
println!("Restarting script...");
|
||||
println!("Terminating current run...");
|
||||
break Err(EvalAltResult::ErrorTerminated(Dynamic::UNIT, pos).into());
|
||||
}
|
||||
_ => eprintln!(
|
||||
@ -604,7 +612,10 @@ fn main() {
|
||||
#[allow(deprecated)]
|
||||
engine.register_debugger(
|
||||
// Store the current source in the debugger state
|
||||
|_| "".into(),
|
||||
|engine, mut debugger| {
|
||||
debugger.set_state(engine.const_empty_string());
|
||||
debugger
|
||||
},
|
||||
// Main debugging interface
|
||||
move |context, event, node, source, pos| {
|
||||
debug_callback(context, event, node, source, pos, &lines)
|
||||
@ -627,10 +638,13 @@ fn main() {
|
||||
while let Err(err) = engine.run_ast_with_scope(&mut Scope::new(), &ast) {
|
||||
match *err {
|
||||
// Loop back to restart
|
||||
EvalAltResult::ErrorTerminated(..) => (),
|
||||
EvalAltResult::ErrorTerminated(..) => {
|
||||
println!("Restarting script...");
|
||||
}
|
||||
// Break evaluation
|
||||
_ => {
|
||||
print_error(&script, *err);
|
||||
println!();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -31,14 +31,16 @@ fn print_error(input: &str, mut err: EvalAltResult) {
|
||||
// Specific position - print line text
|
||||
println!("{line_no}{}", lines[pos.line().unwrap() - 1]);
|
||||
|
||||
for (i, err_line) in err.to_string().split('\n').enumerate() {
|
||||
// Display position marker
|
||||
println!(
|
||||
"{0:>1$} {err}",
|
||||
"^",
|
||||
line_no.len() + pos.position().unwrap(),
|
||||
"{0:>1$}{err_line}",
|
||||
if i > 0 { "| " } else { "^ " },
|
||||
line_no.len() + pos.position().unwrap() + 1,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Print help text.
|
||||
fn print_help() {
|
||||
@ -245,6 +247,7 @@ fn setup_editor() -> Editor<()> {
|
||||
rl
|
||||
}
|
||||
|
||||
/// Module containing sample functions.
|
||||
#[export_module]
|
||||
mod sample_functions {
|
||||
/// This is a sample function.
|
||||
|
@ -8,11 +8,15 @@ fn eprint_error(input: &str, mut err: EvalAltResult) {
|
||||
let line_no = format!("{line}: ");
|
||||
|
||||
eprintln!("{line_no}{}", lines[line - 1]);
|
||||
eprintln!(
|
||||
"{:>1$} {err_msg}",
|
||||
"^",
|
||||
line_no.len() + pos.position().unwrap(),
|
||||
|
||||
for (i, err_line) in err_msg.to_string().split('\n').enumerate() {
|
||||
// Display position marker
|
||||
println!(
|
||||
"{0:>1$}{err_line}",
|
||||
if i > 0 { "| " } else { "^ " },
|
||||
line_no.len() + pos.position().unwrap() + 1,
|
||||
);
|
||||
}
|
||||
eprintln!();
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,7 @@ pub struct Engine {
|
||||
|
||||
/// Callback closure for debugging.
|
||||
#[cfg(feature = "debugging")]
|
||||
pub(crate) debugger: Option<
|
||||
pub(crate) debugger_interface: Option<
|
||||
Box<(
|
||||
Box<crate::eval::OnDebuggingInit>,
|
||||
Box<crate::eval::OnDebuggerCallback>,
|
||||
@ -191,6 +191,9 @@ impl fmt::Debug for Engine {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
f.field("limits", &self.limits);
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
f.field("debugger_interface", &self.debugger_interface.is_some());
|
||||
|
||||
f.finish()
|
||||
}
|
||||
}
|
||||
@ -305,7 +308,7 @@ impl Engine {
|
||||
limits: crate::api::limits::Limits::new(),
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
debugger: None,
|
||||
debugger_interface: None,
|
||||
};
|
||||
|
||||
// Add the global namespace module
|
||||
@ -348,4 +351,12 @@ impl Engine {
|
||||
pub fn const_empty_string(&self) -> ImmutableString {
|
||||
self.get_interned_string("")
|
||||
}
|
||||
|
||||
/// Is there a debugger interface registered with this [`Engine`]?
|
||||
#[cfg(feature = "debugging")]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) const fn is_debugger_registered(&self) -> bool {
|
||||
self.debugger_interface.is_some()
|
||||
}
|
||||
}
|
||||
|
@ -5,8 +5,8 @@ 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::tokenizer::NO_TOKEN;
|
||||
use crate::types::dynamic::Union;
|
||||
use crate::types::RestoreOnDrop;
|
||||
use crate::{
|
||||
calc_fn_hash, Dynamic, Engine, FnArgsVec, Position, RhaiResult, RhaiResultOf, Scope, ERR,
|
||||
};
|
||||
@ -68,20 +68,12 @@ impl Engine {
|
||||
idx: &mut Dynamic,
|
||||
pos: Position,
|
||||
) -> RhaiResultOf<Dynamic> {
|
||||
let orig_level = global.level;
|
||||
global.level += 1;
|
||||
let global = &mut *RestoreOnDrop::lock(global, move |g| g.level = orig_level);
|
||||
auto_restore! { let orig_level = global.level; global.level += 1 }
|
||||
|
||||
self.exec_native_fn_call(
|
||||
global,
|
||||
caches,
|
||||
FN_IDX_GET,
|
||||
None,
|
||||
hash_idx().0,
|
||||
&mut [target, idx],
|
||||
true,
|
||||
pos,
|
||||
)
|
||||
let hash = hash_idx().0;
|
||||
let args = &mut [target, idx];
|
||||
|
||||
self.exec_native_fn_call(global, caches, FN_IDX_GET, NO_TOKEN, hash, args, true, pos)
|
||||
.map(|(r, ..)| r)
|
||||
}
|
||||
|
||||
@ -97,19 +89,13 @@ impl Engine {
|
||||
is_ref_mut: bool,
|
||||
pos: Position,
|
||||
) -> RhaiResultOf<(Dynamic, bool)> {
|
||||
let orig_level = global.level;
|
||||
global.level += 1;
|
||||
let global = &mut *RestoreOnDrop::lock(global, move |g| g.level = orig_level);
|
||||
auto_restore! { let orig_level = global.level; global.level += 1 }
|
||||
|
||||
let hash = hash_idx().1;
|
||||
let args = &mut [target, idx, new_val];
|
||||
|
||||
self.exec_native_fn_call(
|
||||
global,
|
||||
caches,
|
||||
FN_IDX_SET,
|
||||
None,
|
||||
hash_idx().1,
|
||||
&mut [target, idx, new_val],
|
||||
is_ref_mut,
|
||||
pos,
|
||||
global, caches, FN_IDX_SET, NO_TOKEN, hash, args, is_ref_mut, pos,
|
||||
)
|
||||
}
|
||||
|
||||
@ -599,7 +585,9 @@ impl Engine {
|
||||
// Try to call index setter if value is changed
|
||||
let idx = &mut idx_val_for_setter;
|
||||
let new_val = &mut new_val;
|
||||
self.call_indexer_set(
|
||||
// The return value of a indexer setter (usually `()`) is thrown away and not used.
|
||||
let _ = self
|
||||
.call_indexer_set(
|
||||
global, caches, target, idx, new_val, is_ref_mut, op_pos,
|
||||
)
|
||||
.or_else(|e| match *e {
|
||||
@ -659,8 +647,8 @@ impl Engine {
|
||||
|
||||
// Try to call index setter
|
||||
let new_val = &mut new_val;
|
||||
|
||||
self.call_indexer_set(
|
||||
// The return value of a indexer setter (usually `()`) is thrown away and not used.
|
||||
let _ = self.call_indexer_set(
|
||||
global, caches, target, idx_val, new_val, is_ref_mut, op_pos,
|
||||
)?;
|
||||
}
|
||||
@ -696,19 +684,17 @@ impl Engine {
|
||||
let reset =
|
||||
self.run_debugger_with_reset(global, caches, scope, this_ptr, rhs)?;
|
||||
#[cfg(feature = "debugging")]
|
||||
let global =
|
||||
&mut *RestoreOnDrop::lock_if(reset.is_some(), global, move |g| {
|
||||
g.debugger_mut().reset_status(reset)
|
||||
});
|
||||
auto_restore!(global if reset.is_some() => move |g| g.debugger_mut().reset_status(reset));
|
||||
|
||||
let crate::ast::FnCallExpr {
|
||||
name, hashes, args, ..
|
||||
} = &**x;
|
||||
|
||||
// Truncate the index values upon exit
|
||||
auto_restore! {
|
||||
idx_values => truncate;
|
||||
let offset = idx_values.len() - args.len();
|
||||
let idx_values =
|
||||
&mut *RestoreOnDrop::lock(idx_values, move |v| v.truncate(offset));
|
||||
}
|
||||
|
||||
let call_args = &mut idx_values[offset..];
|
||||
let arg1_pos = args.get(0).map_or(Position::NONE, Expr::position);
|
||||
@ -764,9 +750,11 @@ impl Engine {
|
||||
|
||||
if op_info.is_op_assignment() {
|
||||
let args = &mut [target.as_mut()];
|
||||
|
||||
let (mut orig_val, ..) = self
|
||||
.exec_native_fn_call(
|
||||
global, caches, getter, None, *hash_get, args, is_ref_mut, *pos,
|
||||
global, caches, getter, NO_TOKEN, *hash_get, args, is_ref_mut,
|
||||
*pos,
|
||||
)
|
||||
.or_else(|err| match *err {
|
||||
// Try an indexer if property does not exist
|
||||
@ -798,8 +786,9 @@ impl Engine {
|
||||
}
|
||||
|
||||
let args = &mut [target.as_mut(), &mut new_val];
|
||||
|
||||
self.exec_native_fn_call(
|
||||
global, caches, setter, None, *hash_set, args, is_ref_mut, *pos,
|
||||
global, caches, setter, NO_TOKEN, *hash_set, args, is_ref_mut, *pos,
|
||||
)
|
||||
.or_else(|err| match *err {
|
||||
// Try an indexer if property does not exist
|
||||
@ -824,8 +813,9 @@ impl Engine {
|
||||
|
||||
let ((getter, hash_get), _, name) = &**x;
|
||||
let args = &mut [target.as_mut()];
|
||||
|
||||
self.exec_native_fn_call(
|
||||
global, caches, getter, None, *hash_get, args, is_ref_mut, *pos,
|
||||
global, caches, getter, NO_TOKEN, *hash_get, args, is_ref_mut, *pos,
|
||||
)
|
||||
.map_or_else(
|
||||
|err| match *err {
|
||||
@ -866,21 +856,17 @@ impl Engine {
|
||||
global, caches, scope, this_ptr, _node,
|
||||
)?;
|
||||
#[cfg(feature = "debugging")]
|
||||
let global = &mut *RestoreOnDrop::lock_if(
|
||||
reset.is_some(),
|
||||
global,
|
||||
move |g| g.debugger_mut().reset_status(reset),
|
||||
);
|
||||
auto_restore!(global if reset.is_some() => move |g| g.debugger_mut().reset_status(reset));
|
||||
|
||||
let crate::ast::FnCallExpr {
|
||||
name, hashes, args, ..
|
||||
} = &**x;
|
||||
|
||||
// Truncate the index values upon exit
|
||||
auto_restore! {
|
||||
idx_values => truncate;
|
||||
let offset = idx_values.len() - args.len();
|
||||
let idx_values = &mut *RestoreOnDrop::lock(idx_values, move |v| {
|
||||
v.truncate(offset)
|
||||
});
|
||||
}
|
||||
|
||||
let call_args = &mut idx_values[offset..];
|
||||
let arg1_pos = args.get(0).map_or(Position::NONE, Expr::position);
|
||||
@ -915,14 +901,13 @@ impl Engine {
|
||||
self.run_debugger(global, caches, scope, this_ptr, _node)?;
|
||||
|
||||
let ((getter, hash_get), (setter, hash_set), name) = &**p;
|
||||
let mut arg_values = [target.as_mut(), &mut Dynamic::UNIT.clone()];
|
||||
let args = &mut arg_values[..1];
|
||||
let args = &mut [target.as_mut()];
|
||||
|
||||
// Assume getters are always pure
|
||||
let (mut val, ..) = self
|
||||
.exec_native_fn_call(
|
||||
global, caches, getter, None, *hash_get, args, is_ref_mut,
|
||||
pos,
|
||||
global, caches, getter, NO_TOKEN, *hash_get, args,
|
||||
is_ref_mut, pos,
|
||||
)
|
||||
.or_else(|err| match *err {
|
||||
// Try an indexer if property does not exist
|
||||
@ -952,14 +937,15 @@ impl Engine {
|
||||
// Feed the value back via a setter just in case it has been updated
|
||||
if may_be_changed {
|
||||
// Re-use args because the first &mut parameter will not be consumed
|
||||
let mut arg_values = [target.as_mut(), val.as_mut()];
|
||||
let args = &mut arg_values;
|
||||
self.exec_native_fn_call(
|
||||
global, caches, setter, None, *hash_set, args, is_ref_mut,
|
||||
pos,
|
||||
let args = &mut [target.as_mut(), val.as_mut()];
|
||||
|
||||
// The return value is thrown away and not used.
|
||||
let _ = self
|
||||
.exec_native_fn_call(
|
||||
global, caches, setter, NO_TOKEN, *hash_set, args,
|
||||
is_ref_mut, pos,
|
||||
)
|
||||
.or_else(
|
||||
|err| match *err {
|
||||
.or_else(|err| match *err {
|
||||
// Try an indexer if property does not exist
|
||||
ERR::ErrorDotExpr(..) => {
|
||||
let idx = &mut name.into();
|
||||
@ -978,8 +964,7 @@ impl Engine {
|
||||
})
|
||||
}
|
||||
_ => Err(err),
|
||||
},
|
||||
)?;
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok((result, may_be_changed))
|
||||
@ -992,22 +977,17 @@ impl Engine {
|
||||
global, caches, scope, this_ptr, _node,
|
||||
)?;
|
||||
#[cfg(feature = "debugging")]
|
||||
let global = &mut *RestoreOnDrop::lock_if(
|
||||
reset.is_some(),
|
||||
global,
|
||||
move |g| g.debugger_mut().reset_status(reset),
|
||||
);
|
||||
auto_restore!(global if reset.is_some() => move |g| g.debugger_mut().reset_status(reset));
|
||||
|
||||
let crate::ast::FnCallExpr {
|
||||
name, hashes, args, ..
|
||||
} = &**f;
|
||||
|
||||
// Truncate the index values upon exit
|
||||
auto_restore! {
|
||||
idx_values => truncate;
|
||||
let offset = idx_values.len() - args.len();
|
||||
let idx_values =
|
||||
&mut *RestoreOnDrop::lock(idx_values, move |v| {
|
||||
v.truncate(offset)
|
||||
});
|
||||
}
|
||||
|
||||
let call_args = &mut idx_values[offset..];
|
||||
let pos1 = args.get(0).map_or(Position::NONE, Expr::position);
|
||||
|
@ -74,13 +74,10 @@ impl Engine {
|
||||
///
|
||||
/// [`Position`] in [`EvalAltResult`][crate::EvalAltResult] is always [`NONE`][Position::NONE]
|
||||
/// and should be set afterwards.
|
||||
pub(crate) fn raise_err_if_over_data_size_limit(
|
||||
&self,
|
||||
(_arr, _map, s): (usize, usize, usize),
|
||||
) -> RhaiResultOf<()> {
|
||||
pub(crate) fn throw_on_size(&self, (_arr, _map, s): (usize, usize, usize)) -> RhaiResultOf<()> {
|
||||
if self
|
||||
.limits
|
||||
.max_string_size
|
||||
.max_string_len
|
||||
.map_or(false, |max| s > max.get())
|
||||
{
|
||||
return Err(
|
||||
@ -127,9 +124,10 @@ impl Engine {
|
||||
|
||||
let sizes = value.borrow().calc_data_sizes(true);
|
||||
|
||||
self.raise_err_if_over_data_size_limit(sizes)
|
||||
.map(|_| value)
|
||||
.map_err(|err| err.fill_position(pos))
|
||||
self.throw_on_size(sizes)
|
||||
.map_err(|err| err.fill_position(pos))?;
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
/// Raise an error if the size of a [`Dynamic`] is out of limits (if any).
|
||||
|
@ -10,10 +10,10 @@ use std::{fmt, iter::repeat, mem};
|
||||
|
||||
/// Callback function to initialize the debugger.
|
||||
#[cfg(not(feature = "sync"))]
|
||||
pub type OnDebuggingInit = dyn Fn(&Engine) -> Dynamic;
|
||||
pub type OnDebuggingInit = dyn Fn(&Engine, Debugger) -> Debugger;
|
||||
/// Callback function to initialize the debugger.
|
||||
#[cfg(feature = "sync")]
|
||||
pub type OnDebuggingInit = dyn Fn(&Engine) -> Dynamic + Send + Sync;
|
||||
pub type OnDebuggingInit = dyn Fn(&Engine, Debugger) -> Debugger + Send + Sync;
|
||||
|
||||
/// Callback function for debugging.
|
||||
#[cfg(not(feature = "sync"))]
|
||||
@ -268,12 +268,12 @@ impl Debugger {
|
||||
/// Create a new [`Debugger`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn new(status: DebuggerStatus, state: Dynamic) -> Self {
|
||||
pub const fn new(status: DebuggerStatus) -> Self {
|
||||
Self {
|
||||
status,
|
||||
break_points: Vec::new(),
|
||||
call_stack: Vec::new(),
|
||||
state,
|
||||
state: Dynamic::UNIT,
|
||||
}
|
||||
}
|
||||
/// Get the current call stack.
|
||||
@ -415,7 +415,7 @@ impl Engine {
|
||||
this_ptr: &mut Dynamic,
|
||||
node: impl Into<ASTNode<'a>>,
|
||||
) -> RhaiResultOf<()> {
|
||||
if self.debugger.is_some() {
|
||||
if self.is_debugger_registered() {
|
||||
if let Some(cmd) =
|
||||
self.run_debugger_with_reset_raw(global, caches, scope, this_ptr, node)?
|
||||
{
|
||||
@ -440,7 +440,7 @@ impl Engine {
|
||||
this_ptr: &mut Dynamic,
|
||||
node: impl Into<ASTNode<'a>>,
|
||||
) -> RhaiResultOf<Option<DebuggerStatus>> {
|
||||
if self.debugger.is_some() {
|
||||
if self.is_debugger_registered() {
|
||||
self.run_debugger_with_reset_raw(global, caches, scope, this_ptr, node)
|
||||
} else {
|
||||
Ok(None)
|
||||
@ -508,11 +508,10 @@ impl Engine {
|
||||
node: ASTNode<'a>,
|
||||
event: DebuggerEvent,
|
||||
) -> Result<Option<DebuggerStatus>, Box<crate::EvalAltResult>> {
|
||||
if let Some(ref x) = self.debugger_interface {
|
||||
let src = global.source_raw().cloned();
|
||||
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 x) = self.debugger {
|
||||
let context = EvalContext::new(self, global, caches, scope, this_ptr);
|
||||
let (.., ref on_debugger) = **x;
|
||||
|
||||
let command = on_debugger(context, event, node, src, node.position())?;
|
||||
|
@ -6,7 +6,6 @@ use crate::{Dynamic, Engine, Scope};
|
||||
use std::prelude::v1::*;
|
||||
|
||||
/// Context of a script evaluation process.
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub struct EvalContext<'a, 's, 'ps, 'g, 'c, 't> {
|
||||
/// The current [`Engine`].
|
||||
|
@ -62,25 +62,22 @@ impl Engine {
|
||||
}
|
||||
Expr::Variable(v, None, ..) => match &**v {
|
||||
// Normal variable access
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
(_, ns, ..) if ns.is_empty() => {
|
||||
self.search_scope_only(global, caches, scope, this_ptr, expr)
|
||||
}
|
||||
#[cfg(feature = "no_module")]
|
||||
(_, (), ..) => self.search_scope_only(global, caches, scope, this_ptr, expr),
|
||||
|
||||
// Qualified variable access
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
(_, namespace, hash_var, var_name) => {
|
||||
(_, ns, hash_var, var_name) => {
|
||||
// foo:bar::baz::VARIABLE
|
||||
if let Some(module) = self.search_imports(global, namespace) {
|
||||
if let Some(module) = self.search_imports(global, ns) {
|
||||
return module.get_qualified_var(*hash_var).map_or_else(
|
||||
|| {
|
||||
let sep = crate::tokenizer::Token::DoubleColon.literal_syntax();
|
||||
|
||||
Err(ERR::ErrorVariableNotFound(
|
||||
format!("{namespace}{sep}{var_name}"),
|
||||
namespace.position(),
|
||||
format!("{ns}{sep}{var_name}"),
|
||||
ns.position(),
|
||||
)
|
||||
.into())
|
||||
},
|
||||
@ -94,7 +91,7 @@ impl Engine {
|
||||
|
||||
// global::VARIABLE
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
if namespace.len() == 1 && namespace.root() == crate::engine::KEYWORD_GLOBAL {
|
||||
if ns.len() == 1 && ns.root() == crate::engine::KEYWORD_GLOBAL {
|
||||
if let Some(ref constants) = global.constants {
|
||||
if let Some(value) =
|
||||
crate::func::locked_write(constants).get_mut(var_name.as_str())
|
||||
@ -109,17 +106,17 @@ impl Engine {
|
||||
let sep = crate::tokenizer::Token::DoubleColon.literal_syntax();
|
||||
|
||||
return Err(ERR::ErrorVariableNotFound(
|
||||
format!("{namespace}{sep}{var_name}"),
|
||||
namespace.position(),
|
||||
format!("{ns}{sep}{var_name}"),
|
||||
ns.position(),
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
Err(
|
||||
ERR::ErrorModuleNotFound(namespace.to_string(), namespace.position())
|
||||
.into(),
|
||||
)
|
||||
Err(ERR::ErrorModuleNotFound(ns.to_string(), ns.position()).into())
|
||||
}
|
||||
|
||||
#[cfg(feature = "no_module")]
|
||||
_ => unreachable!("Invalid expression {:?}", expr),
|
||||
},
|
||||
_ => unreachable!("Expr::Variable expected but gets {:?}", expr),
|
||||
}
|
||||
@ -142,14 +139,18 @@ impl Engine {
|
||||
|
||||
let index = match expr {
|
||||
// Check if the variable is `this`
|
||||
Expr::Variable(v, None, ..) if v.0.is_none() && v.3 == KEYWORD_THIS => {
|
||||
Expr::Variable(v, None, ..)
|
||||
if v.0.is_none() && v.1.is_empty() && v.3 == KEYWORD_THIS =>
|
||||
{
|
||||
return if this_ptr.is_null() {
|
||||
Err(ERR::ErrorUnboundThis(expr.position()).into())
|
||||
} else {
|
||||
Ok(this_ptr.into())
|
||||
};
|
||||
}
|
||||
|
||||
_ if global.always_search_scope => 0,
|
||||
|
||||
Expr::Variable(_, Some(i), ..) => i.get() as usize,
|
||||
// Scripted function with the same name
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
@ -165,6 +166,7 @@ impl Engine {
|
||||
return Ok(val.into());
|
||||
}
|
||||
Expr::Variable(v, None, ..) => v.0.map_or(0, NonZeroUsize::get),
|
||||
|
||||
_ => unreachable!("Expr::Variable expected but gets {:?}", expr),
|
||||
};
|
||||
|
||||
@ -232,10 +234,7 @@ 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_if(reset.is_some(), global, move |g| {
|
||||
g.debugger_mut().reset_status(reset)
|
||||
});
|
||||
auto_restore!(global if reset.is_some() => move |g| g.debugger_mut().reset_status(reset));
|
||||
|
||||
self.track_operation(global, expr.position())?;
|
||||
|
||||
@ -266,10 +265,7 @@ 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_if(reset.is_some(), global, move |g| {
|
||||
g.debugger_mut().reset_status(reset)
|
||||
});
|
||||
auto_restore!(global if reset.is_some() => move |g| g.debugger_mut().reset_status(reset));
|
||||
|
||||
self.track_operation(global, expr.position())?;
|
||||
|
||||
@ -327,7 +323,7 @@ impl Engine {
|
||||
total_data_sizes.1 + val_sizes.1,
|
||||
total_data_sizes.2 + val_sizes.2,
|
||||
);
|
||||
self.raise_err_if_over_data_size_limit(total_data_sizes)
|
||||
self.throw_on_size(total_data_sizes)
|
||||
.map_err(|err| err.fill_position(item_expr.position()))?;
|
||||
}
|
||||
|
||||
@ -358,7 +354,7 @@ impl Engine {
|
||||
total_data_sizes.1 + delta.1,
|
||||
total_data_sizes.2 + delta.2,
|
||||
);
|
||||
self.raise_err_if_over_data_size_limit(total_data_sizes)
|
||||
self.throw_on_size(total_data_sizes)
|
||||
.map_err(|err| err.fill_position(value_expr.position()))?;
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ use std::prelude::v1::*;
|
||||
/// Collection of globally-defined constants.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub type GlobalConstants =
|
||||
pub type SharedGlobalConstants =
|
||||
crate::Shared<crate::Locked<std::collections::BTreeMap<ImmutableString, Dynamic>>>;
|
||||
|
||||
/// _(internals)_ Global runtime states.
|
||||
@ -67,12 +67,12 @@ pub struct GlobalRuntimeState {
|
||||
/// Interior mutability is needed because it is shared in order to aid in cloning.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub constants: Option<GlobalConstants>,
|
||||
pub constants: Option<SharedGlobalConstants>,
|
||||
/// Custom state that can be used by the external host.
|
||||
pub tag: Dynamic,
|
||||
/// Debugging interface.
|
||||
#[cfg(feature = "debugging")]
|
||||
pub(crate) debugger: Option<super::Debugger>,
|
||||
pub(crate) debugger: Option<Box<super::Debugger>>,
|
||||
}
|
||||
|
||||
impl GlobalRuntimeState {
|
||||
@ -103,8 +103,9 @@ impl GlobalRuntimeState {
|
||||
tag: engine.default_tag().clone(),
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
debugger: engine.debugger.as_ref().map(|x| {
|
||||
crate::eval::Debugger::new(crate::eval::DebuggerStatus::Init, (x.0)(engine))
|
||||
debugger: engine.debugger_interface.as_ref().map(|x| {
|
||||
let dbg = crate::eval::Debugger::new(crate::eval::DebuggerStatus::Init);
|
||||
(x.0)(engine, dbg).into()
|
||||
}),
|
||||
}
|
||||
}
|
||||
@ -115,7 +116,7 @@ impl GlobalRuntimeState {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn num_imports(&self) -> usize {
|
||||
self.modules.as_ref().map_or(0, |m| m.len())
|
||||
self.modules.as_deref().map_or(0, crate::StaticVec::len)
|
||||
}
|
||||
/// Get the globally-imported [module][crate::Module] at a particular index.
|
||||
///
|
||||
@ -138,7 +139,7 @@ impl GlobalRuntimeState {
|
||||
&mut self,
|
||||
index: usize,
|
||||
) -> Option<&mut crate::SharedModule> {
|
||||
self.modules.as_mut().and_then(|m| m.get_mut(index))
|
||||
self.modules.as_deref_mut().and_then(|m| m.get_mut(index))
|
||||
}
|
||||
/// Get the index of a globally-imported [module][crate::Module] by name.
|
||||
///
|
||||
@ -183,8 +184,8 @@ impl GlobalRuntimeState {
|
||||
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);
|
||||
self.imports.as_deref_mut().unwrap().truncate(size);
|
||||
self.modules.as_deref_mut().unwrap().truncate(size);
|
||||
}
|
||||
}
|
||||
/// Get an iterator to the stack of globally-imported [modules][crate::Module] in reverse order.
|
||||
@ -194,10 +195,11 @@ impl GlobalRuntimeState {
|
||||
#[inline]
|
||||
pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &crate::Module)> {
|
||||
self.imports
|
||||
.iter()
|
||||
.flat_map(|x| x.iter())
|
||||
.as_deref()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.rev()
|
||||
.zip(self.modules.iter().flat_map(|x| x.iter()).rev())
|
||||
.zip(self.modules.as_deref().into_iter().flatten().rev())
|
||||
.map(|(name, module)| (name.as_str(), &**module))
|
||||
}
|
||||
/// Get an iterator to the stack of globally-imported [modules][crate::Module] in reverse order.
|
||||
@ -209,10 +211,11 @@ impl GlobalRuntimeState {
|
||||
&self,
|
||||
) -> impl Iterator<Item = (&ImmutableString, &crate::SharedModule)> {
|
||||
self.imports
|
||||
.iter()
|
||||
.flat_map(|x| x.iter())
|
||||
.as_deref()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.rev()
|
||||
.zip(self.modules.iter().flat_map(|x| x.iter()).rev())
|
||||
.zip(self.modules.as_deref().into_iter().flatten())
|
||||
}
|
||||
/// Get an iterator to the stack of globally-imported [modules][crate::Module] in forward order.
|
||||
///
|
||||
@ -223,9 +226,10 @@ impl GlobalRuntimeState {
|
||||
&self,
|
||||
) -> impl Iterator<Item = (&ImmutableString, &crate::SharedModule)> {
|
||||
self.imports
|
||||
.iter()
|
||||
.flat_map(|x| x.iter())
|
||||
.zip(self.modules.iter().flat_map(|x| x.iter()))
|
||||
.as_deref()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.zip(self.modules.as_deref().into_iter().flatten())
|
||||
}
|
||||
/// Can the particular function with [`Dynamic`] parameter(s) exist in the stack of
|
||||
/// globally-imported [modules][crate::Module]?
|
||||
@ -234,7 +238,7 @@ impl GlobalRuntimeState {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[inline]
|
||||
pub(crate) fn may_contain_dynamic_fn(&self, hash_script: u64) -> bool {
|
||||
self.modules.as_ref().map_or(false, |m| {
|
||||
self.modules.as_deref().map_or(false, |m| {
|
||||
m.iter().any(|m| m.may_contain_dynamic_fn(hash_script))
|
||||
})
|
||||
}
|
||||
@ -323,7 +327,7 @@ impl GlobalRuntimeState {
|
||||
/// 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()
|
||||
self.debugger.as_deref_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,10 +17,10 @@ pub use debugger::{
|
||||
OnDebuggerCallback, OnDebuggingInit,
|
||||
};
|
||||
pub use eval_context::EvalContext;
|
||||
pub use global_state::GlobalRuntimeState;
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub use global_state::GlobalConstants;
|
||||
pub use global_state::GlobalRuntimeState;
|
||||
pub use global_state::SharedGlobalConstants;
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
pub use target::calc_offset_len;
|
||||
pub use target::{calc_index, Target};
|
||||
|
142
src/eval/stmt.rs
142
src/eval/stmt.rs
@ -3,16 +3,28 @@
|
||||
use super::{Caches, EvalContext, GlobalRuntimeState, Target};
|
||||
use crate::api::events::VarDefInfo;
|
||||
use crate::ast::{
|
||||
ASTFlags, BinaryExpr, Expr, Ident, OpAssignment, Stmt, SwitchCasesCollection, TryCatchBlock,
|
||||
ASTFlags, BinaryExpr, Expr, OpAssignment, Stmt, SwitchCasesCollection, TryCatchBlock,
|
||||
};
|
||||
use crate::func::{get_builtin_op_assignment_fn, get_hasher};
|
||||
use crate::types::dynamic::AccessMode;
|
||||
use crate::types::RestoreOnDrop;
|
||||
use crate::types::dynamic::{AccessMode, Union};
|
||||
use crate::{Dynamic, Engine, RhaiResult, RhaiResultOf, Scope, ERR, INT};
|
||||
use std::hash::{Hash, Hasher};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
impl Dynamic {
|
||||
/// If the value is a string, intern it.
|
||||
#[inline(always)]
|
||||
fn intern_string(self, engine: &Engine) -> Self {
|
||||
match self.0 {
|
||||
Union::Str(..) => engine
|
||||
.get_interned_string(self.into_immutable_string().expect("`ImmutableString`"))
|
||||
.into(),
|
||||
_ => self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Engine {
|
||||
/// Evaluate a statements block.
|
||||
pub(crate) fn eval_stmt_block(
|
||||
@ -29,10 +41,10 @@ impl Engine {
|
||||
}
|
||||
|
||||
// Restore scope at end of block if necessary
|
||||
auto_restore! {
|
||||
scope if restore_orig_state => rewind;
|
||||
let orig_scope_len = scope.len();
|
||||
let scope = &mut *RestoreOnDrop::lock_if(restore_orig_state, scope, move |s| {
|
||||
s.rewind(orig_scope_len);
|
||||
});
|
||||
}
|
||||
|
||||
// Restore global state at end of block if necessary
|
||||
let orig_always_search_scope = global.always_search_scope;
|
||||
@ -43,7 +55,7 @@ impl Engine {
|
||||
global.scope_level += 1;
|
||||
}
|
||||
|
||||
let global = &mut *RestoreOnDrop::lock_if(restore_orig_state, global, move |g| {
|
||||
auto_restore!(global if restore_orig_state => move |g| {
|
||||
g.scope_level -= 1;
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
@ -55,10 +67,10 @@ impl Engine {
|
||||
});
|
||||
|
||||
// Pop new function resolution caches at end of block
|
||||
auto_restore! {
|
||||
caches => rewind_fn_resolution_caches;
|
||||
let orig_fn_resolution_caches_len = caches.fn_resolution_caches_len();
|
||||
let caches = &mut *RestoreOnDrop::lock(caches, move |c| {
|
||||
c.rewind_fn_resolution_caches(orig_fn_resolution_caches_len)
|
||||
});
|
||||
}
|
||||
|
||||
// Run the statements
|
||||
statements.iter().try_fold(Dynamic::UNIT, |_, stmt| {
|
||||
@ -129,23 +141,25 @@ impl Engine {
|
||||
let args = &mut [&mut *lock_guard, &mut new_val];
|
||||
|
||||
if self.fast_operators() {
|
||||
if let Some(func) = get_builtin_op_assignment_fn(op_assign_token, args[0], args[1])
|
||||
if let Some((func, ctx)) =
|
||||
get_builtin_op_assignment_fn(op_assign_token.clone(), args[0], args[1])
|
||||
{
|
||||
// Built-in found
|
||||
let op = op_assign_token.literal_syntax();
|
||||
auto_restore! { let orig_level = global.level; global.level += 1 }
|
||||
|
||||
let orig_level = global.level;
|
||||
global.level += 1;
|
||||
let global = &*RestoreOnDrop::lock(global, move |g| g.level = orig_level);
|
||||
|
||||
let context = (self, op, None, global, *op_pos).into();
|
||||
let context = if ctx {
|
||||
Some((self, op, None, &*global, *op_pos).into())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
return func(context, args).map(|_| ());
|
||||
}
|
||||
}
|
||||
|
||||
let op_assign = op_assign_token.literal_syntax();
|
||||
let op = op_token.literal_syntax();
|
||||
let token = Some(op_assign_token);
|
||||
let token = op_assign_token.clone();
|
||||
|
||||
match self
|
||||
.exec_native_fn_call(global, caches, op_assign, token, hash, args, true, *op_pos)
|
||||
@ -154,7 +168,7 @@ impl Engine {
|
||||
Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_assign)) =>
|
||||
{
|
||||
// Expand to `var = var op rhs`
|
||||
let token = Some(op_token);
|
||||
let token = op_token.clone();
|
||||
|
||||
*args[0] = self
|
||||
.exec_native_fn_call(
|
||||
@ -168,14 +182,14 @@ impl Engine {
|
||||
self.check_data_size(&*args[0], root.position())?;
|
||||
} else {
|
||||
// Normal assignment
|
||||
|
||||
// If value is a string, intern it
|
||||
if new_val.is_string() {
|
||||
let value = new_val.into_immutable_string().expect("`ImmutableString`");
|
||||
new_val = self.get_interned_string(value).into();
|
||||
match target {
|
||||
// Lock it again just in case it is shared
|
||||
Target::RefMut(_) | Target::TempValue(_) => {
|
||||
*target.write_lock::<Dynamic>().unwrap() = new_val
|
||||
}
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => **target = new_val,
|
||||
}
|
||||
|
||||
*target.write_lock::<Dynamic>().unwrap() = new_val;
|
||||
}
|
||||
|
||||
target.propagate_changed_value(op_info.pos)
|
||||
@ -194,9 +208,7 @@ 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_if(reset.is_some(), global, move |g| {
|
||||
g.debugger_mut().reset_status(reset)
|
||||
});
|
||||
auto_restore!(global if reset.is_some() => 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.
|
||||
@ -223,14 +235,13 @@ impl Engine {
|
||||
|
||||
let mut target = self.search_namespace(global, caches, scope, this_ptr, lhs)?;
|
||||
|
||||
let is_temp_result = !target.is_ref();
|
||||
let var_name = x.3.as_str();
|
||||
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
// Also handle case where target is a `Dynamic` shared value
|
||||
// (returned by a variable resolver, for example)
|
||||
let is_temp_result = !target.is_ref() && !target.is_shared();
|
||||
#[cfg(feature = "no_closure")]
|
||||
let is_temp_result = !target.is_ref();
|
||||
let is_temp_result = is_temp_result && !target.is_shared();
|
||||
|
||||
// Cannot assign to temp result from expression
|
||||
if is_temp_result {
|
||||
@ -250,19 +261,16 @@ impl Engine {
|
||||
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
{
|
||||
let mut rhs_val = self
|
||||
let rhs_val = self
|
||||
.eval_expr(global, caches, scope, this_ptr, rhs)?
|
||||
.flatten();
|
||||
|
||||
// If value is a string, intern it
|
||||
if rhs_val.is_string() {
|
||||
let value = rhs_val.into_immutable_string().expect("`ImmutableString`");
|
||||
rhs_val = self.get_interned_string(value).into();
|
||||
}
|
||||
.flatten()
|
||||
.intern_string(self);
|
||||
|
||||
let _new_val = Some((rhs_val, op_info));
|
||||
|
||||
// Must be either `var[index] op= val` or `var.prop op= val`
|
||||
// Must be either `var[index] op= val` or `var.prop op= val`.
|
||||
// The return value of any op-assignment (should be `()`) is thrown away and not used.
|
||||
let _ =
|
||||
match lhs {
|
||||
// name op= rhs (handled above)
|
||||
Expr::Variable(..) => {
|
||||
@ -270,14 +278,12 @@ impl Engine {
|
||||
}
|
||||
// idx_lhs[idx_expr] op= rhs
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Expr::Index(..) => {
|
||||
self.eval_dot_index_chain(global, caches, scope, this_ptr, lhs, _new_val)
|
||||
}
|
||||
Expr::Index(..) => self
|
||||
.eval_dot_index_chain(global, caches, scope, this_ptr, lhs, _new_val),
|
||||
// dot_lhs.dot_rhs op= rhs
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Dot(..) => {
|
||||
self.eval_dot_index_chain(global, caches, scope, this_ptr, lhs, _new_val)
|
||||
}
|
||||
Expr::Dot(..) => self
|
||||
.eval_dot_index_chain(global, caches, scope, this_ptr, lhs, _new_val),
|
||||
_ => unreachable!("cannot assign to expression: {:?}", lhs),
|
||||
}?;
|
||||
|
||||
@ -494,27 +500,29 @@ impl Engine {
|
||||
// 2) Global modules - packages
|
||||
// 3) Imported modules - functions marked with global namespace
|
||||
// 4) Global sub-modules - functions marked with global namespace
|
||||
let func = self
|
||||
let iter_func = self
|
||||
.global_modules
|
||||
.iter()
|
||||
.find_map(|m| m.get_iter(iter_type));
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
let func = func.or_else(|| global.get_iter(iter_type)).or_else(|| {
|
||||
let iter_func = iter_func
|
||||
.or_else(|| global.get_iter(iter_type))
|
||||
.or_else(|| {
|
||||
self.global_sub_modules
|
||||
.iter()
|
||||
.flat_map(|m| m.values())
|
||||
.find_map(|m| m.get_qualified_iter(iter_type))
|
||||
.as_deref()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.find_map(|(_, m)| m.get_qualified_iter(iter_type))
|
||||
});
|
||||
|
||||
let func = func.ok_or_else(|| ERR::ErrorFor(expr.start_position()))?;
|
||||
let iter_func = iter_func.ok_or_else(|| ERR::ErrorFor(expr.start_position()))?;
|
||||
|
||||
// Restore scope at end of statement
|
||||
auto_restore! {
|
||||
scope => rewind;
|
||||
let orig_scope_len = scope.len();
|
||||
let scope = &mut *RestoreOnDrop::lock(scope, move |s| {
|
||||
s.rewind(orig_scope_len);
|
||||
});
|
||||
|
||||
}
|
||||
// Add the loop variables
|
||||
let counter_index = if counter.is_empty() {
|
||||
usize::MAX
|
||||
@ -528,7 +536,7 @@ impl Engine {
|
||||
|
||||
let mut result = Dynamic::UNIT;
|
||||
|
||||
for (x, iter_value) in func(iter_obj).enumerate() {
|
||||
for (x, iter_value) in iter_func(iter_obj).enumerate() {
|
||||
// Increment counter
|
||||
if counter_index < usize::MAX {
|
||||
// As the variable increments from 0, this should always work
|
||||
@ -596,10 +604,7 @@ impl Engine {
|
||||
Stmt::TryCatch(x, ..) => {
|
||||
let TryCatchBlock {
|
||||
try_block,
|
||||
catch_var:
|
||||
Ident {
|
||||
name: catch_var, ..
|
||||
},
|
||||
catch_var,
|
||||
catch_block,
|
||||
} = &**x;
|
||||
|
||||
@ -644,14 +649,13 @@ impl Engine {
|
||||
};
|
||||
|
||||
// Restore scope at end of block
|
||||
auto_restore! {
|
||||
scope if !catch_var.is_empty() => rewind;
|
||||
let orig_scope_len = scope.len();
|
||||
let scope =
|
||||
&mut *RestoreOnDrop::lock_if(!catch_var.is_empty(), scope, move |s| {
|
||||
s.rewind(orig_scope_len);
|
||||
});
|
||||
}
|
||||
|
||||
if !catch_var.is_empty() {
|
||||
scope.push(catch_var.clone(), err_value);
|
||||
scope.push(catch_var.name.clone(), err_value);
|
||||
}
|
||||
|
||||
self.eval_stmt_block(global, caches, scope, this_ptr, catch_block, true)
|
||||
@ -721,7 +725,8 @@ impl Engine {
|
||||
// Evaluate initial value
|
||||
let mut value = self
|
||||
.eval_expr(global, caches, scope, this_ptr, expr)?
|
||||
.flatten();
|
||||
.flatten()
|
||||
.intern_string(self);
|
||||
|
||||
let _alias = if !rewind_scope {
|
||||
// Put global constants into global module
|
||||
@ -759,7 +764,7 @@ impl Engine {
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
if let Some(alias) = _alias {
|
||||
scope.add_alias_by_index(scope.len() - 1, alias.name.as_str().into());
|
||||
scope.add_alias_by_index(scope.len() - 1, alias.as_str().into());
|
||||
}
|
||||
|
||||
Ok(Dynamic::UNIT)
|
||||
@ -826,6 +831,7 @@ impl Engine {
|
||||
// Export statement
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
Stmt::Export(x, ..) => {
|
||||
use crate::ast::Ident;
|
||||
let (Ident { name, pos, .. }, Ident { name: alias, .. }) = &**x;
|
||||
// Mark scope variables as public
|
||||
scope.search(name).map_or_else(
|
||||
|
@ -81,6 +81,7 @@ pub fn calc_index<E>(
|
||||
|
||||
/// A type that encapsulates a mutation target for an expression with side effects.
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
pub enum Target<'a> {
|
||||
/// The target is a mutable reference to a [`Dynamic`].
|
||||
RefMut(&'a mut Dynamic),
|
||||
@ -88,9 +89,9 @@ pub enum Target<'a> {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
SharedValue {
|
||||
/// Lock guard to the shared [`Dynamic`].
|
||||
source: crate::types::dynamic::DynamicWriteLock<'a, Dynamic>,
|
||||
/// Copy of the value.
|
||||
value: Dynamic,
|
||||
guard: crate::types::dynamic::DynamicWriteLock<'a, Dynamic>,
|
||||
/// Copy of the shared value.
|
||||
shared_value: Dynamic,
|
||||
},
|
||||
/// The target is a temporary [`Dynamic`] value (i.e. its mutation can cause no side effects).
|
||||
TempValue(Dynamic),
|
||||
@ -177,13 +178,12 @@ impl<'a> Target<'a> {
|
||||
}
|
||||
}
|
||||
/// Is the [`Target`] a shared value?
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn is_shared(&self) -> bool {
|
||||
match self {
|
||||
Self::RefMut(r) => r.is_shared(),
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
return match self {
|
||||
Self::RefMut(r) => r.is_shared(),
|
||||
Self::SharedValue { .. } => true,
|
||||
Self::TempValue(value) => value.is_shared(),
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
@ -191,16 +191,17 @@ impl<'a> Target<'a> {
|
||||
| Self::BitField { .. }
|
||||
| Self::BlobByte { .. }
|
||||
| Self::StringChar { .. } => false,
|
||||
}
|
||||
};
|
||||
#[cfg(feature = "no_closure")]
|
||||
return false;
|
||||
}
|
||||
/// Get the value of the [`Target`] as a [`Dynamic`], cloning a referenced value if necessary.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn take_or_clone(self) -> Dynamic {
|
||||
match self {
|
||||
Self::RefMut(r) => r.clone(), // Referenced value is cloned
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Self::SharedValue { value, .. } => value, // Original shared value is simply taken
|
||||
Self::SharedValue { shared_value, .. } => shared_value, // Original shared value is simply taken
|
||||
Self::TempValue(value) => value, // Owned value is simply taken
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::Bit { value, .. } => value, // boolean is taken
|
||||
@ -223,12 +224,11 @@ impl<'a> Target<'a> {
|
||||
}
|
||||
/// Convert a shared or reference [`Target`] into a target with an owned value.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn into_owned(self) -> Self {
|
||||
match self {
|
||||
Self::RefMut(r) => Self::TempValue(r.clone()),
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Self::SharedValue { value, .. } => Self::TempValue(value),
|
||||
Self::SharedValue { shared_value, .. } => Self::TempValue(shared_value),
|
||||
_ => self,
|
||||
}
|
||||
}
|
||||
@ -240,7 +240,7 @@ impl<'a> Target<'a> {
|
||||
match self {
|
||||
Self::RefMut(r) => r,
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Self::SharedValue { source, .. } => source,
|
||||
Self::SharedValue { guard, .. } => guard,
|
||||
Self::TempValue(value) => value,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::Bit { source, .. } => source,
|
||||
@ -366,9 +366,12 @@ impl<'a> From<&'a mut Dynamic> for Target<'a> {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
if value.is_shared() {
|
||||
// Cloning is cheap for a shared value
|
||||
let val = value.clone();
|
||||
let source = value.write_lock::<Dynamic>().expect("`Dynamic`");
|
||||
return Self::SharedValue { source, value: val };
|
||||
let shared_value = value.clone();
|
||||
let guard = value.write_lock::<Dynamic>().expect("`Dynamic`");
|
||||
return Self::SharedValue {
|
||||
guard,
|
||||
shared_value,
|
||||
};
|
||||
}
|
||||
|
||||
Self::RefMut(value)
|
||||
@ -383,7 +386,7 @@ impl Deref for Target<'_> {
|
||||
match self {
|
||||
Self::RefMut(r) => r,
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Self::SharedValue { source, .. } => source,
|
||||
Self::SharedValue { guard, .. } => guard,
|
||||
Self::TempValue(ref value) => value,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::Bit { ref value, .. }
|
||||
@ -416,7 +419,7 @@ impl DerefMut for Target<'_> {
|
||||
match self {
|
||||
Self::RefMut(r) => r,
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Self::SharedValue { source, .. } => &mut *source,
|
||||
Self::SharedValue { guard, .. } => &mut *guard,
|
||||
Self::TempValue(ref mut value) => value,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::Bit { ref mut value, .. }
|
||||
@ -437,7 +440,6 @@ impl AsMut<Dynamic> for Target<'_> {
|
||||
|
||||
impl<T: Into<Dynamic>> From<T> for Target<'_> {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn from(value: T) -> Self {
|
||||
Self::TempValue(value.into())
|
||||
}
|
||||
|
@ -24,8 +24,8 @@ use num_traits::Float;
|
||||
#[cfg(feature = "decimal")]
|
||||
use rust_decimal::Decimal;
|
||||
|
||||
/// The message: data type was checked
|
||||
const BUILTIN: &str = "data type was checked";
|
||||
/// The `unchecked` feature is not active.
|
||||
const CHECKED_BUILD: bool = cfg!(not(feature = "unchecked"));
|
||||
|
||||
/// Is the type a numeric type?
|
||||
#[inline]
|
||||
@ -71,12 +71,12 @@ fn is_numeric(type_id: TypeId) -> bool {
|
||||
|
||||
/// A function that returns `true`.
|
||||
#[inline(always)]
|
||||
fn const_true_fn(_: NativeCallContext, _: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn const_true_fn(_: Option<NativeCallContext>, _: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
Ok(Dynamic::TRUE)
|
||||
}
|
||||
/// A function that returns `false`.
|
||||
#[inline(always)]
|
||||
fn const_false_fn(_: NativeCallContext, _: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
fn const_false_fn(_: Option<NativeCallContext>, _: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
Ok(Dynamic::FALSE)
|
||||
}
|
||||
|
||||
@ -84,60 +84,60 @@ fn const_false_fn(_: NativeCallContext, _: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
///
|
||||
/// The return function will be registered as a _method_, so the first parameter cannot be consumed.
|
||||
#[must_use]
|
||||
pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<FnBuiltin> {
|
||||
pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option<FnBuiltin> {
|
||||
let type1 = x.type_id();
|
||||
let type2 = y.type_id();
|
||||
|
||||
macro_rules! impl_op {
|
||||
($xx:ident $op:tt $yy:ident) => { |_, args| {
|
||||
let x = &*args[0].read_lock::<$xx>().expect(BUILTIN);
|
||||
let y = &*args[1].read_lock::<$yy>().expect(BUILTIN);
|
||||
($xx:ident $op:tt $yy:ident) => { (|_, args| {
|
||||
let x = &*args[0].read_lock::<$xx>().unwrap();
|
||||
let y = &*args[1].read_lock::<$yy>().unwrap();
|
||||
Ok((x $op y).into())
|
||||
} };
|
||||
($xx:ident . $func:ident ( $yy:ty )) => { |_, args| {
|
||||
let x = &*args[0].read_lock::<$xx>().expect(BUILTIN);
|
||||
let y = &*args[1].read_lock::<$yy>().expect(BUILTIN);
|
||||
}, false) };
|
||||
($xx:ident . $func:ident ( $yy:ty )) => { (|_, args| {
|
||||
let x = &*args[0].read_lock::<$xx>().unwrap();
|
||||
let y = &*args[1].read_lock::<$yy>().unwrap();
|
||||
Ok(x.$func(y).into())
|
||||
} };
|
||||
($xx:ident . $func:ident ( $yy:ident . $yyy:ident () )) => { |_, args| {
|
||||
let x = &*args[0].read_lock::<$xx>().expect(BUILTIN);
|
||||
let y = &*args[1].read_lock::<$yy>().expect(BUILTIN);
|
||||
}, false) };
|
||||
($xx:ident . $func:ident ( $yy:ident . $yyy:ident () )) => { (|_, args| {
|
||||
let x = &*args[0].read_lock::<$xx>().unwrap();
|
||||
let y = &*args[1].read_lock::<$yy>().unwrap();
|
||||
Ok(x.$func(y.$yyy()).into())
|
||||
} };
|
||||
($func:ident ( $op:tt )) => { |_, args| {
|
||||
}, false) };
|
||||
($func:ident ( $op:tt )) => { (|_, args| {
|
||||
let (x, y) = $func(args);
|
||||
Ok((x $op y).into())
|
||||
} };
|
||||
($base:ty => $xx:ident $op:tt $yy:ident) => { |_, args| {
|
||||
let x = args[0].$xx().expect(BUILTIN) as $base;
|
||||
let y = args[1].$yy().expect(BUILTIN) as $base;
|
||||
}, false) };
|
||||
($base:ty => $xx:ident $op:tt $yy:ident) => { (|_, args| {
|
||||
let x = args[0].$xx().unwrap() as $base;
|
||||
let y = args[1].$yy().unwrap() as $base;
|
||||
Ok((x $op y).into())
|
||||
} };
|
||||
($base:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty)) => { |_, args| {
|
||||
let x = args[0].$xx().expect(BUILTIN) as $base;
|
||||
let y = args[1].$yy().expect(BUILTIN) as $base;
|
||||
}, false) };
|
||||
($base:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty)) => { (|_, args| {
|
||||
let x = args[0].$xx().unwrap() as $base;
|
||||
let y = args[1].$yy().unwrap() as $base;
|
||||
Ok(x.$func(y as $yyy).into())
|
||||
} };
|
||||
($base:ty => $func:ident ( $xx:ident, $yy:ident )) => { |_, args| {
|
||||
let x = args[0].$xx().expect(BUILTIN) as $base;
|
||||
let y = args[1].$yy().expect(BUILTIN) as $base;
|
||||
}, false) };
|
||||
($base:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| {
|
||||
let x = args[0].$xx().unwrap() as $base;
|
||||
let y = args[1].$yy().unwrap() as $base;
|
||||
$func(x, y).map(Into::into)
|
||||
} };
|
||||
(from $base:ty => $xx:ident $op:tt $yy:ident) => { |_, args| {
|
||||
let x = <$base>::from(args[0].$xx().expect(BUILTIN));
|
||||
let y = <$base>::from(args[1].$yy().expect(BUILTIN));
|
||||
}, false) };
|
||||
(from $base:ty => $xx:ident $op:tt $yy:ident) => { (|_, args| {
|
||||
let x = <$base>::from(args[0].$xx().unwrap());
|
||||
let y = <$base>::from(args[1].$yy().unwrap());
|
||||
Ok((x $op y).into())
|
||||
} };
|
||||
(from $base:ty => $xx:ident . $func:ident ( $yy:ident )) => { |_, args| {
|
||||
let x = <$base>::from(args[0].$xx().expect(BUILTIN));
|
||||
let y = <$base>::from(args[1].$yy().expect(BUILTIN));
|
||||
}, false) };
|
||||
(from $base:ty => $xx:ident . $func:ident ( $yy:ident )) => { (|_, args| {
|
||||
let x = <$base>::from(args[0].$xx().unwrap());
|
||||
let y = <$base>::from(args[1].$yy().unwrap());
|
||||
Ok(x.$func(y).into())
|
||||
} };
|
||||
(from $base:ty => $func:ident ( $xx:ident, $yy:ident )) => { |_, args| {
|
||||
let x = <$base>::from(args[0].$xx().expect(BUILTIN));
|
||||
let y = <$base>::from(args[1].$yy().expect(BUILTIN));
|
||||
}, false) };
|
||||
(from $base:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| {
|
||||
let x = <$base>::from(args[0].$xx().unwrap());
|
||||
let y = <$base>::from(args[1].$yy().unwrap());
|
||||
$func(x, y).map(Into::into)
|
||||
} };
|
||||
}, false) };
|
||||
}
|
||||
|
||||
// Check for common patterns
|
||||
@ -183,16 +183,8 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
|
||||
Ampersand => Some(impl_op!(INT => as_int & as_int)),
|
||||
Pipe => Some(impl_op!(INT => as_int | as_int)),
|
||||
XOr => Some(impl_op!(INT => as_int ^ as_int)),
|
||||
ExclusiveRange => Some(|_, args| {
|
||||
let x = args[0].as_int().expect(BUILTIN);
|
||||
let y = args[1].as_int().expect(BUILTIN);
|
||||
Ok((x..y).into())
|
||||
}),
|
||||
InclusiveRange => Some(|_, args| {
|
||||
let x = args[0].as_int().expect(BUILTIN);
|
||||
let y = args[1].as_int().expect(BUILTIN);
|
||||
Ok((x..=y).into())
|
||||
}),
|
||||
ExclusiveRange => Some(impl_op!(INT => as_int .. as_int)),
|
||||
InclusiveRange => Some(impl_op!(INT => as_int ..= as_int)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
@ -214,19 +206,20 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
|
||||
|
||||
if type1 == TypeId::of::<ImmutableString>() {
|
||||
return match op {
|
||||
Plus => Some(|_ctx, args| {
|
||||
let s1 = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||
let s2 = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||
Plus => Some((
|
||||
|_ctx, args| {
|
||||
let s1 = &*args[0].read_lock::<ImmutableString>().unwrap();
|
||||
let s2 = &*args[1].read_lock::<ImmutableString>().unwrap();
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if !s1.is_empty() && !s2.is_empty() {
|
||||
let total_len = s1.len() + s2.len();
|
||||
_ctx.engine()
|
||||
.raise_err_if_over_data_size_limit((0, 0, total_len))?;
|
||||
}
|
||||
_ctx.unwrap()
|
||||
.engine()
|
||||
.throw_on_size((0, 0, s1.len() + s2.len()))?;
|
||||
|
||||
Ok((s1 + s2).into())
|
||||
}),
|
||||
},
|
||||
CHECKED_BUILD,
|
||||
)),
|
||||
Minus => Some(impl_op!(ImmutableString - ImmutableString)),
|
||||
EqualsTo => Some(impl_op!(ImmutableString == ImmutableString)),
|
||||
NotEqualsTo => Some(impl_op!(ImmutableString != ImmutableString)),
|
||||
@ -240,20 +233,22 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
|
||||
|
||||
if type1 == TypeId::of::<char>() {
|
||||
return match op {
|
||||
Plus => Some(|_ctx, args| {
|
||||
let x = args[0].as_char().expect(BUILTIN);
|
||||
let y = args[1].as_char().expect(BUILTIN);
|
||||
Plus => Some((
|
||||
|_ctx, args| {
|
||||
let x = args[0].as_char().unwrap();
|
||||
let y = args[1].as_char().unwrap();
|
||||
|
||||
let mut result = SmartString::new_const();
|
||||
result.push(x);
|
||||
result.push(y);
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
_ctx.engine()
|
||||
.raise_err_if_over_data_size_limit((0, 0, result.len()))?;
|
||||
_ctx.unwrap().engine().throw_on_size((0, 0, result.len()))?;
|
||||
|
||||
Ok(result.into())
|
||||
}),
|
||||
},
|
||||
CHECKED_BUILD,
|
||||
)),
|
||||
EqualsTo => Some(impl_op!(char => as_char == as_char)),
|
||||
NotEqualsTo => Some(impl_op!(char => as_char != as_char)),
|
||||
GreaterThan => Some(impl_op!(char => as_char > as_char)),
|
||||
@ -269,27 +264,28 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
|
||||
use crate::Blob;
|
||||
|
||||
return match op {
|
||||
Plus => Some(|_ctx, args| {
|
||||
let blob1 = &*args[0].read_lock::<Blob>().expect(BUILTIN);
|
||||
let blob2 = &*args[1].read_lock::<Blob>().expect(BUILTIN);
|
||||
Plus => Some((
|
||||
|_ctx, args| {
|
||||
let b2 = &*args[1].read_lock::<Blob>().unwrap();
|
||||
if b2.is_empty() {
|
||||
return Ok(args[0].flatten_clone());
|
||||
}
|
||||
let b1 = &*args[0].read_lock::<Blob>().unwrap();
|
||||
if b1.is_empty() {
|
||||
return Ok(args[1].flatten_clone());
|
||||
}
|
||||
|
||||
Ok(Dynamic::from_blob(if blob2.is_empty() {
|
||||
blob1.clone()
|
||||
} else if blob1.is_empty() {
|
||||
blob2.clone()
|
||||
} else {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
_ctx.engine().raise_err_if_over_data_size_limit((
|
||||
blob1.len() + blob2.len(),
|
||||
0,
|
||||
0,
|
||||
))?;
|
||||
_ctx.unwrap()
|
||||
.engine()
|
||||
.throw_on_size((b1.len() + b2.len(), 0, 0))?;
|
||||
|
||||
let mut blob = blob1.clone();
|
||||
blob.extend(blob2);
|
||||
blob
|
||||
}))
|
||||
}),
|
||||
let mut blob = b1.clone();
|
||||
blob.extend(b2);
|
||||
Ok(Dynamic::from_blob(blob))
|
||||
},
|
||||
CHECKED_BUILD,
|
||||
)),
|
||||
EqualsTo => Some(impl_op!(Blob == Blob)),
|
||||
NotEqualsTo => Some(impl_op!(Blob != Blob)),
|
||||
_ => None,
|
||||
@ -298,9 +294,9 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
|
||||
|
||||
if type1 == TypeId::of::<()>() {
|
||||
return match op {
|
||||
EqualsTo => Some(const_true_fn),
|
||||
EqualsTo => Some((const_true_fn, false)),
|
||||
NotEqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => {
|
||||
Some(const_false_fn)
|
||||
Some((const_false_fn, false))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
@ -393,8 +389,8 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
|
||||
// char op string
|
||||
if (type1, type2) == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
|
||||
fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) {
|
||||
let x = args[0].as_char().expect(BUILTIN);
|
||||
let y = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||
let x = args[0].as_char().unwrap();
|
||||
let y = &*args[1].read_lock::<ImmutableString>().unwrap();
|
||||
let s1 = [x, '\0'];
|
||||
let mut y = y.chars();
|
||||
let s2 = [y.next().unwrap_or('\0'), y.next().unwrap_or('\0')];
|
||||
@ -402,20 +398,22 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
|
||||
}
|
||||
|
||||
return match op {
|
||||
Plus => Some(|_ctx, args| {
|
||||
let x = args[0].as_char().expect(BUILTIN);
|
||||
let y = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||
Plus => Some((
|
||||
|_ctx, args| {
|
||||
let x = args[0].as_char().unwrap();
|
||||
let y = &*args[1].read_lock::<ImmutableString>().unwrap();
|
||||
|
||||
let mut result = SmartString::new_const();
|
||||
result.push(x);
|
||||
result.push_str(y);
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
_ctx.engine()
|
||||
.raise_err_if_over_data_size_limit((0, 0, result.len()))?;
|
||||
_ctx.unwrap().engine().throw_on_size((0, 0, result.len()))?;
|
||||
|
||||
Ok(result.into())
|
||||
}),
|
||||
},
|
||||
CHECKED_BUILD,
|
||||
)),
|
||||
EqualsTo => Some(impl_op!(get_s1s2(==))),
|
||||
NotEqualsTo => Some(impl_op!(get_s1s2(!=))),
|
||||
GreaterThan => Some(impl_op!(get_s1s2(>))),
|
||||
@ -428,8 +426,8 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
|
||||
// string op char
|
||||
if (type1, type2) == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
|
||||
fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) {
|
||||
let x = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||
let y = args[1].as_char().expect(BUILTIN);
|
||||
let x = &*args[0].read_lock::<ImmutableString>().unwrap();
|
||||
let y = args[1].as_char().unwrap();
|
||||
let mut x = x.chars();
|
||||
let s1 = [x.next().unwrap_or('\0'), x.next().unwrap_or('\0')];
|
||||
let s2 = [y, '\0'];
|
||||
@ -437,22 +435,27 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
|
||||
}
|
||||
|
||||
return match op {
|
||||
Plus => Some(|_ctx, args| {
|
||||
let x = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||
let y = args[1].as_char().expect(BUILTIN);
|
||||
Plus => Some((
|
||||
|_ctx, args| {
|
||||
let x = &*args[0].read_lock::<ImmutableString>().unwrap();
|
||||
let y = args[1].as_char().unwrap();
|
||||
let result = x + y;
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
_ctx.engine()
|
||||
.raise_err_if_over_data_size_limit((0, 0, result.len()))?;
|
||||
_ctx.unwrap().engine().throw_on_size((0, 0, result.len()))?;
|
||||
|
||||
Ok(result.into())
|
||||
}),
|
||||
Minus => Some(|_, args| {
|
||||
let x = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||
let y = args[1].as_char().expect(BUILTIN);
|
||||
},
|
||||
CHECKED_BUILD,
|
||||
)),
|
||||
Minus => Some((
|
||||
|_, args| {
|
||||
let x = &*args[0].read_lock::<ImmutableString>().unwrap();
|
||||
let y = args[1].as_char().unwrap();
|
||||
Ok((x - y).into())
|
||||
}),
|
||||
},
|
||||
false,
|
||||
)),
|
||||
EqualsTo => Some(impl_op!(get_s1s2(==))),
|
||||
NotEqualsTo => Some(impl_op!(get_s1s2(!=))),
|
||||
GreaterThan => Some(impl_op!(get_s1s2(>))),
|
||||
@ -465,22 +468,22 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
|
||||
// () op string
|
||||
if (type1, type2) == (TypeId::of::<()>(), TypeId::of::<ImmutableString>()) {
|
||||
return match op {
|
||||
Plus => Some(|_, args| Ok(args[1].clone())),
|
||||
Plus => Some((|_, args| Ok(args[1].clone()), false)),
|
||||
EqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => {
|
||||
Some(const_false_fn)
|
||||
Some((const_false_fn, false))
|
||||
}
|
||||
NotEqualsTo => Some(const_true_fn),
|
||||
NotEqualsTo => Some((const_true_fn, false)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
// string op ()
|
||||
if (type1, type2) == (TypeId::of::<ImmutableString>(), TypeId::of::<()>()) {
|
||||
return match op {
|
||||
Plus => Some(|_, args| Ok(args[0].clone())),
|
||||
Plus => Some((|_, args| Ok(args[0].clone()), false)),
|
||||
EqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => {
|
||||
Some(const_false_fn)
|
||||
Some((const_false_fn, false))
|
||||
}
|
||||
NotEqualsTo => Some(const_true_fn),
|
||||
NotEqualsTo => Some((const_true_fn, false)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
@ -492,21 +495,22 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
|
||||
|
||||
if type2 == TypeId::of::<char>() {
|
||||
return match op {
|
||||
Plus => Some(|_ctx, args| {
|
||||
let mut blob = args[0].read_lock::<Blob>().expect(BUILTIN).clone();
|
||||
Plus => Some((
|
||||
|_ctx, args| {
|
||||
let mut blob = args[0].read_lock::<Blob>().unwrap().clone();
|
||||
let mut buf = [0_u8; 4];
|
||||
let x = args[1].as_char().expect(BUILTIN).encode_utf8(&mut buf);
|
||||
let x = args[1].as_char().unwrap().encode_utf8(&mut buf);
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
_ctx.engine().raise_err_if_over_data_size_limit((
|
||||
blob.len() + x.len(),
|
||||
0,
|
||||
0,
|
||||
))?;
|
||||
_ctx.unwrap()
|
||||
.engine()
|
||||
.throw_on_size((blob.len() + x.len(), 0, 0))?;
|
||||
|
||||
blob.extend(x.as_bytes());
|
||||
Ok(Dynamic::from_blob(blob))
|
||||
}),
|
||||
},
|
||||
CHECKED_BUILD,
|
||||
)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
@ -525,8 +529,8 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
|
||||
)
|
||||
{
|
||||
return match op {
|
||||
NotEqualsTo => Some(const_true_fn),
|
||||
Equals => Some(const_false_fn),
|
||||
NotEqualsTo => Some((const_true_fn, false)),
|
||||
Equals => Some((const_false_fn, false)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
@ -556,9 +560,9 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
|
||||
} else if type1 != type2 {
|
||||
// If the types are not the same, default to not compare
|
||||
match op {
|
||||
NotEqualsTo => Some(const_true_fn),
|
||||
NotEqualsTo => Some((const_true_fn, false)),
|
||||
EqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => {
|
||||
Some(const_false_fn)
|
||||
Some((const_false_fn, false))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
@ -571,9 +575,9 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
|
||||
// Default comparison operators for different types
|
||||
if type2 != type1 {
|
||||
return match op {
|
||||
NotEqualsTo => Some(const_true_fn),
|
||||
NotEqualsTo => Some((const_true_fn, false)),
|
||||
EqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => {
|
||||
Some(const_false_fn)
|
||||
Some((const_false_fn, false))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
@ -587,48 +591,48 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
|
||||
///
|
||||
/// The return function is registered as a _method_, so the first parameter cannot be consumed.
|
||||
#[must_use]
|
||||
pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<FnBuiltin> {
|
||||
pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option<FnBuiltin> {
|
||||
let type1 = x.type_id();
|
||||
let type2 = y.type_id();
|
||||
|
||||
macro_rules! impl_op {
|
||||
($x:ty = x $op:tt $yy:ident) => { |_, args| {
|
||||
let x = args[0].$yy().expect(BUILTIN);
|
||||
let y = args[1].$yy().expect(BUILTIN) as $x;
|
||||
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) = x $op y).into())
|
||||
} };
|
||||
($x:ident $op:tt $yy:ident) => { |_, args| {
|
||||
let y = args[1].$yy().expect(BUILTIN) as $x;
|
||||
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) $op y).into())
|
||||
} };
|
||||
($x:ident $op:tt $yy:ident as $yyy:ty) => { |_, args| {
|
||||
let y = args[1].$yy().expect(BUILTIN) as $yyy;
|
||||
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) $op y).into())
|
||||
} };
|
||||
($x:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty )) => { |_, args| {
|
||||
let x = args[0].$xx().expect(BUILTIN);
|
||||
let y = args[1].$yy().expect(BUILTIN) as $x;
|
||||
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) = x.$func(y as $yyy)).into())
|
||||
} };
|
||||
($x:ty => $func:ident ( $xx:ident, $yy:ident )) => { |_, args| {
|
||||
let x = args[0].$xx().expect(BUILTIN);
|
||||
let y = args[1].$yy().expect(BUILTIN) as $x;
|
||||
Ok((*args[0].write_lock().expect(BUILTIN) = $func(x, y)?).into())
|
||||
} };
|
||||
(from $x:ident $op:tt $yy:ident) => { |_, args| {
|
||||
let y = <$x>::from(args[1].$yy().expect(BUILTIN));
|
||||
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) $op y).into())
|
||||
} };
|
||||
(from $x:ty => $xx:ident . $func:ident ( $yy:ident )) => { |_, args| {
|
||||
let x = args[0].$xx().expect(BUILTIN);
|
||||
let y = <$x>::from(args[1].$yy().expect(BUILTIN));
|
||||
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) = x.$func(y)).into())
|
||||
} };
|
||||
(from $x:ty => $func:ident ( $xx:ident, $yy:ident )) => { |_, args| {
|
||||
let x = args[0].$xx().expect(BUILTIN);
|
||||
let y = <$x>::from(args[1].$yy().expect(BUILTIN));
|
||||
Ok((*args[0].write_lock().expect(BUILTIN) = $func(x, y)?).into())
|
||||
} };
|
||||
($x:ty = x $op:tt $yy:ident) => { (|_, args| {
|
||||
let x = args[0].$yy().unwrap();
|
||||
let y = args[1].$yy().unwrap() as $x;
|
||||
Ok((*args[0].write_lock::<$x>().unwrap() = x $op y).into())
|
||||
}, false) };
|
||||
($x:ident $op:tt $yy:ident) => { (|_, args| {
|
||||
let y = args[1].$yy().unwrap() as $x;
|
||||
Ok((*args[0].write_lock::<$x>().unwrap() $op y).into())
|
||||
}, false) };
|
||||
($x:ident $op:tt $yy:ident as $yyy:ty) => { (|_, args| {
|
||||
let y = args[1].$yy().unwrap() as $yyy;
|
||||
Ok((*args[0].write_lock::<$x>().unwrap() $op y).into())
|
||||
}, false) };
|
||||
($x:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty )) => { (|_, args| {
|
||||
let x = args[0].$xx().unwrap();
|
||||
let y = args[1].$yy().unwrap() as $x;
|
||||
Ok((*args[0].write_lock::<$x>().unwrap() = x.$func(y as $yyy)).into())
|
||||
}, false) };
|
||||
($x:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| {
|
||||
let x = args[0].$xx().unwrap();
|
||||
let y = args[1].$yy().unwrap() as $x;
|
||||
Ok((*args[0].write_lock().unwrap() = $func(x, y)?).into())
|
||||
}, false) };
|
||||
(from $x:ident $op:tt $yy:ident) => { (|_, args| {
|
||||
let y = <$x>::from(args[1].$yy().unwrap());
|
||||
Ok((*args[0].write_lock::<$x>().unwrap() $op y).into())
|
||||
}, false) };
|
||||
(from $x:ty => $xx:ident . $func:ident ( $yy:ident )) => { (|_, args| {
|
||||
let x = args[0].$xx().unwrap();
|
||||
let y = <$x>::from(args[1].$yy().unwrap());
|
||||
Ok((*args[0].write_lock::<$x>().unwrap() = x.$func(y)).into())
|
||||
}, false) };
|
||||
(from $x:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| {
|
||||
let x = args[0].$xx().unwrap();
|
||||
let y = <$x>::from(args[1].$yy().unwrap());
|
||||
Ok((*args[0].write_lock().unwrap() = $func(x, y)?).into())
|
||||
}, false) };
|
||||
}
|
||||
|
||||
// Check for common patterns
|
||||
@ -682,42 +686,50 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
|
||||
|
||||
if type1 == TypeId::of::<char>() {
|
||||
return match op {
|
||||
PlusAssign => Some(|_, args| {
|
||||
let y = args[1].as_char().expect(BUILTIN);
|
||||
let x = &mut *args[0].write_lock::<Dynamic>().expect(BUILTIN);
|
||||
PlusAssign => Some((
|
||||
|_, args| {
|
||||
let y = args[1].as_char().unwrap();
|
||||
let x = &mut *args[0].write_lock::<Dynamic>().unwrap();
|
||||
|
||||
let mut buf = SmartString::new_const();
|
||||
write!(&mut buf, "{y}").unwrap();
|
||||
buf.push(y);
|
||||
|
||||
Ok((*x = buf.into()).into())
|
||||
}),
|
||||
},
|
||||
false,
|
||||
)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
||||
if type1 == TypeId::of::<ImmutableString>() {
|
||||
return match op {
|
||||
PlusAssign => Some(|_ctx, args| {
|
||||
let (first, second) = args.split_first_mut().expect(BUILTIN);
|
||||
let x = &mut *first.write_lock::<ImmutableString>().expect(BUILTIN);
|
||||
let y = &*second[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||
PlusAssign => Some((
|
||||
|_ctx, args| {
|
||||
let (first, second) = args.split_first_mut().unwrap();
|
||||
let x = &mut *first.write_lock::<ImmutableString>().unwrap();
|
||||
let y = &*second[0].read_lock::<ImmutableString>().unwrap();
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if !x.is_empty() && !y.is_empty() {
|
||||
let total_len = x.len() + y.len();
|
||||
_ctx.engine()
|
||||
.raise_err_if_over_data_size_limit((0, 0, total_len))?;
|
||||
_ctx.unwrap().engine().throw_on_size((0, 0, total_len))?;
|
||||
}
|
||||
|
||||
Ok((*x += y).into())
|
||||
}),
|
||||
MinusAssign => Some(|_, args| {
|
||||
let (first, second) = args.split_first_mut().expect(BUILTIN);
|
||||
let x = &mut *first.write_lock::<ImmutableString>().expect(BUILTIN);
|
||||
let y = &*second[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||
},
|
||||
CHECKED_BUILD,
|
||||
)),
|
||||
MinusAssign => Some((
|
||||
|_, args| {
|
||||
let (first, second) = args.split_first_mut().unwrap();
|
||||
let x = &mut *first.write_lock::<ImmutableString>().unwrap();
|
||||
let y = &*second[0].read_lock::<ImmutableString>().unwrap();
|
||||
Ok((*x -= y).into())
|
||||
}),
|
||||
},
|
||||
false,
|
||||
)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
@ -729,27 +741,30 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
|
||||
use crate::Array;
|
||||
|
||||
return match op {
|
||||
PlusAssign => Some(|_ctx, args| {
|
||||
let x = std::mem::take(args[1]).into_array().expect(BUILTIN);
|
||||
PlusAssign => Some((
|
||||
|_ctx, args| {
|
||||
let x = std::mem::take(args[1]).into_array().unwrap();
|
||||
|
||||
if x.is_empty() {
|
||||
return Ok(Dynamic::UNIT);
|
||||
}
|
||||
|
||||
let _array_is_empty = args[0].read_lock::<Array>().expect(BUILTIN).is_empty();
|
||||
let _array_is_empty = args[0].read_lock::<Array>().unwrap().is_empty();
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if !_array_is_empty {
|
||||
_ctx.engine().check_data_size(
|
||||
&*args[0].read_lock().expect(BUILTIN),
|
||||
_ctx.unwrap().engine().check_data_size(
|
||||
&*args[0].read_lock().unwrap(),
|
||||
crate::Position::NONE,
|
||||
)?;
|
||||
}
|
||||
|
||||
let array = &mut *args[0].write_lock::<Array>().expect(BUILTIN);
|
||||
let array = &mut *args[0].write_lock::<Array>().unwrap();
|
||||
|
||||
Ok(append(array, x).into())
|
||||
}),
|
||||
},
|
||||
CHECKED_BUILD,
|
||||
)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
@ -761,19 +776,20 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
|
||||
use crate::Blob;
|
||||
|
||||
return match op {
|
||||
PlusAssign => Some(|_ctx, args| {
|
||||
let blob2 = std::mem::take(args[1]).into_blob().expect(BUILTIN);
|
||||
let blob1 = &mut *args[0].write_lock::<Blob>().expect(BUILTIN);
|
||||
PlusAssign => Some((
|
||||
|_ctx, args| {
|
||||
let blob2 = std::mem::take(args[1]).into_blob().unwrap();
|
||||
let blob1 = &mut *args[0].write_lock::<Blob>().unwrap();
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
_ctx.engine().raise_err_if_over_data_size_limit((
|
||||
blob1.len() + blob2.len(),
|
||||
0,
|
||||
0,
|
||||
))?;
|
||||
_ctx.unwrap()
|
||||
.engine()
|
||||
.throw_on_size((blob1.len() + blob2.len(), 0, 0))?;
|
||||
|
||||
Ok(append(blob1, blob2).into())
|
||||
}),
|
||||
},
|
||||
CHECKED_BUILD,
|
||||
)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
@ -847,17 +863,21 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
|
||||
// string op= char
|
||||
if (type1, type2) == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
|
||||
return match op {
|
||||
PlusAssign => Some(|_ctx, args| {
|
||||
PlusAssign => Some((
|
||||
|_ctx, args| {
|
||||
let mut buf = [0_u8; 4];
|
||||
let ch = &*args[1].as_char().expect(BUILTIN).encode_utf8(&mut buf);
|
||||
let mut x = args[0].write_lock::<ImmutableString>().expect(BUILTIN);
|
||||
let ch = &*args[1].as_char().unwrap().encode_utf8(&mut buf);
|
||||
let mut x = args[0].write_lock::<ImmutableString>().unwrap();
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
_ctx.engine()
|
||||
.raise_err_if_over_data_size_limit((0, 0, x.len() + ch.len()))?;
|
||||
_ctx.unwrap()
|
||||
.engine()
|
||||
.throw_on_size((0, 0, x.len() + ch.len()))?;
|
||||
|
||||
Ok((*x += ch).into())
|
||||
}),
|
||||
},
|
||||
CHECKED_BUILD,
|
||||
)),
|
||||
MinusAssign => Some(impl_op!(ImmutableString -= as_char as char)),
|
||||
_ => None,
|
||||
};
|
||||
@ -865,28 +885,32 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
|
||||
// char op= string
|
||||
if (type1, type2) == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
|
||||
return match op {
|
||||
PlusAssign => Some(|_ctx, args| {
|
||||
PlusAssign => Some((
|
||||
|_ctx, args| {
|
||||
let ch = {
|
||||
let s = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||
let s = &*args[1].read_lock::<ImmutableString>().unwrap();
|
||||
|
||||
if s.is_empty() {
|
||||
return Ok(Dynamic::UNIT);
|
||||
}
|
||||
|
||||
let mut ch = args[0].as_char().expect(BUILTIN).to_string();
|
||||
let mut ch = args[0].as_char().unwrap().to_string();
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
_ctx.engine()
|
||||
.raise_err_if_over_data_size_limit((0, 0, ch.len() + s.len()))?;
|
||||
_ctx.unwrap()
|
||||
.engine()
|
||||
.throw_on_size((0, 0, ch.len() + s.len()))?;
|
||||
|
||||
ch.push_str(s);
|
||||
ch
|
||||
};
|
||||
|
||||
*args[0].write_lock::<Dynamic>().expect(BUILTIN) = ch.into();
|
||||
*args[0].write_lock::<Dynamic>().unwrap() = ch.into();
|
||||
|
||||
Ok(Dynamic::UNIT)
|
||||
}),
|
||||
},
|
||||
CHECKED_BUILD,
|
||||
)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
@ -899,21 +923,23 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
|
||||
use crate::Array;
|
||||
|
||||
return match op {
|
||||
PlusAssign => Some(|_ctx, args| {
|
||||
PlusAssign => Some((
|
||||
|_ctx, args| {
|
||||
{
|
||||
let x = std::mem::take(args[1]);
|
||||
let array = &mut *args[0].write_lock::<Array>().expect(BUILTIN);
|
||||
let array = &mut *args[0].write_lock::<Array>().unwrap();
|
||||
push(array, x);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
_ctx.engine().check_data_size(
|
||||
&*args[0].read_lock().expect(BUILTIN),
|
||||
crate::Position::NONE,
|
||||
)?;
|
||||
_ctx.unwrap()
|
||||
.engine()
|
||||
.check_data_size(&*args[0].read_lock().unwrap(), crate::Position::NONE)?;
|
||||
|
||||
Ok(Dynamic::UNIT)
|
||||
}),
|
||||
},
|
||||
CHECKED_BUILD,
|
||||
)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
@ -928,16 +954,20 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
|
||||
use crate::packages::blob_basic::blob_functions::*;
|
||||
|
||||
return match op {
|
||||
PlusAssign => Some(|_ctx, args| {
|
||||
let x = args[1].as_int().expect(BUILTIN);
|
||||
let blob = &mut *args[0].write_lock::<Blob>().expect(BUILTIN);
|
||||
PlusAssign => Some((
|
||||
|_ctx, args| {
|
||||
let x = args[1].as_int().unwrap();
|
||||
let blob = &mut *args[0].write_lock::<Blob>().unwrap();
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
_ctx.engine()
|
||||
.raise_err_if_over_data_size_limit((blob.len() + 1, 0, 0))?;
|
||||
_ctx.unwrap()
|
||||
.engine()
|
||||
.throw_on_size((blob.len() + 1, 0, 0))?;
|
||||
|
||||
Ok(push(blob, x).into())
|
||||
}),
|
||||
},
|
||||
CHECKED_BUILD,
|
||||
)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
@ -948,16 +978,20 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
|
||||
use crate::packages::blob_basic::blob_functions::*;
|
||||
|
||||
return match op {
|
||||
PlusAssign => Some(|_ctx, args| {
|
||||
let x = args[1].as_char().expect(BUILTIN);
|
||||
let blob = &mut *args[0].write_lock::<Blob>().expect(BUILTIN);
|
||||
PlusAssign => Some((
|
||||
|_ctx, args| {
|
||||
let x = args[1].as_char().unwrap();
|
||||
let blob = &mut *args[0].write_lock::<Blob>().unwrap();
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
_ctx.engine()
|
||||
.raise_err_if_over_data_size_limit((blob.len() + 1, 0, 0))?;
|
||||
_ctx.unwrap()
|
||||
.engine()
|
||||
.throw_on_size((blob.len() + 1, 0, 0))?;
|
||||
|
||||
Ok(append_char(blob, x).into())
|
||||
}),
|
||||
},
|
||||
CHECKED_BUILD,
|
||||
)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
@ -968,24 +1002,25 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
|
||||
use crate::packages::blob_basic::blob_functions::*;
|
||||
|
||||
return match op {
|
||||
PlusAssign => Some(|_ctx, args| {
|
||||
let (first, second) = args.split_first_mut().expect(BUILTIN);
|
||||
let blob = &mut *first.write_lock::<Blob>().expect(BUILTIN);
|
||||
let s = &*second[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||
PlusAssign => Some((
|
||||
|_ctx, args| {
|
||||
let (first, second) = args.split_first_mut().unwrap();
|
||||
let blob = &mut *first.write_lock::<Blob>().unwrap();
|
||||
let s = &*second[0].read_lock::<ImmutableString>().unwrap();
|
||||
|
||||
if s.is_empty() {
|
||||
return Ok(Dynamic::UNIT);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
_ctx.engine().raise_err_if_over_data_size_limit((
|
||||
blob.len() + s.len(),
|
||||
0,
|
||||
0,
|
||||
))?;
|
||||
_ctx.unwrap()
|
||||
.engine()
|
||||
.throw_on_size((blob.len() + s.len(), 0, 0))?;
|
||||
|
||||
Ok(append_str(blob, s).into())
|
||||
}),
|
||||
},
|
||||
CHECKED_BUILD,
|
||||
)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
173
src/func/call.rs
173
src/func/call.rs
@ -8,8 +8,7 @@ use crate::engine::{
|
||||
KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF,
|
||||
};
|
||||
use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState};
|
||||
use crate::tokenizer::{is_valid_function_name, Token};
|
||||
use crate::types::RestoreOnDrop;
|
||||
use crate::tokenizer::{is_valid_function_name, Token, NO_TOKEN};
|
||||
use crate::{
|
||||
calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString,
|
||||
OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, Shared, ERR,
|
||||
@ -165,7 +164,7 @@ impl Engine {
|
||||
_global: &GlobalRuntimeState,
|
||||
caches: &'s mut Caches,
|
||||
local_entry: &'s mut Option<FnResolutionCacheEntry>,
|
||||
op_token: Option<&Token>,
|
||||
op_token: Token,
|
||||
hash_base: u64,
|
||||
args: Option<&mut FnCallArgs>,
|
||||
allow_dynamic: bool,
|
||||
@ -174,7 +173,7 @@ impl Engine {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut hash = args.as_ref().map_or(hash_base, |args| {
|
||||
let mut hash = args.as_deref().map_or(hash_base, |args| {
|
||||
calc_fn_hash_full(hash_base, args.iter().map(|a| a.type_id()))
|
||||
});
|
||||
|
||||
@ -183,7 +182,7 @@ impl Engine {
|
||||
match cache.map.entry(hash) {
|
||||
Entry::Occupied(entry) => entry.into_mut().as_ref(),
|
||||
Entry::Vacant(entry) => {
|
||||
let num_args = args.as_ref().map_or(0, |a| a.len());
|
||||
let num_args = args.as_deref().map_or(0, |a| a.len());
|
||||
let mut max_bitmask = 0; // One above maximum bitmask based on number of parameters.
|
||||
// Set later when a specific matching function is not found.
|
||||
let mut bitmask = 1usize; // Bitmask of which parameter to replace with `Dynamic`
|
||||
@ -212,9 +211,12 @@ impl Engine {
|
||||
} else {
|
||||
func.or_else(|| _global.get_qualified_fn(hash)).or_else(|| {
|
||||
self.global_sub_modules
|
||||
.iter()
|
||||
.flat_map(|m| m.values())
|
||||
.find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw())))
|
||||
.as_deref()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.find_map(|(_, m)| {
|
||||
m.get_qualified_fn(hash).map(|f| (f, m.id_raw()))
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
@ -251,11 +253,9 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
let is_dynamic = is_dynamic
|
||||
|| _global.may_contain_dynamic_fn(hash_base)
|
||||
|| self
|
||||
.global_sub_modules
|
||||
.iter()
|
||||
.flat_map(|m| m.values())
|
||||
.any(|m| m.may_contain_dynamic_fn(hash_base));
|
||||
|| self.global_sub_modules.as_deref().map_or(false, |m| {
|
||||
m.values().any(|m| m.may_contain_dynamic_fn(hash_base))
|
||||
});
|
||||
|
||||
// Set maximum bitmask when there are dynamic versions of the function
|
||||
if is_dynamic {
|
||||
@ -272,22 +272,22 @@ impl Engine {
|
||||
// Try to find a built-in version
|
||||
let builtin =
|
||||
args.and_then(|args| match op_token {
|
||||
Some(token) if token.is_op_assignment() => {
|
||||
Token::NONE => None,
|
||||
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])
|
||||
.map(|f| FnResolutionCacheEntry {
|
||||
func: CallableFunction::Method(Shared::new(f)),
|
||||
.map(|(f, ctx)| FnResolutionCacheEntry {
|
||||
func: CallableFunction::Method(Shared::new(f), ctx),
|
||||
source: None,
|
||||
})
|
||||
}
|
||||
Some(token) => get_builtin_binary_op_fn(token, args[0], args[1])
|
||||
.map(|f| FnResolutionCacheEntry {
|
||||
func: CallableFunction::Method(Shared::new(f)),
|
||||
token => get_builtin_binary_op_fn(token, args[0], args[1]).map(
|
||||
|(f, ctx)| FnResolutionCacheEntry {
|
||||
func: CallableFunction::Method(Shared::new(f), ctx),
|
||||
source: None,
|
||||
}),
|
||||
|
||||
None => None,
|
||||
},
|
||||
),
|
||||
});
|
||||
|
||||
return if cache.filter.is_absent_and_set(hash) {
|
||||
@ -324,9 +324,9 @@ impl Engine {
|
||||
}
|
||||
}
|
||||
|
||||
/// # Main Entry-Point
|
||||
/// # Main Entry-Point (Native by Name)
|
||||
///
|
||||
/// Call a native Rust function registered with the [`Engine`].
|
||||
/// Call a native Rust function registered with the [`Engine`] by name.
|
||||
///
|
||||
/// # WARNING
|
||||
///
|
||||
@ -340,7 +340,7 @@ impl Engine {
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
name: &str,
|
||||
op_token: Option<&Token>,
|
||||
op_token: Token,
|
||||
hash: u64,
|
||||
args: &mut FnCallArgs,
|
||||
is_ref_mut: bool,
|
||||
@ -380,12 +380,10 @@ impl Engine {
|
||||
// Clone the first argument
|
||||
backup.change_first_arg_to_copy(args);
|
||||
}
|
||||
|
||||
let args =
|
||||
&mut *RestoreOnDrop::lock_if(swap, args, move |a| backup.restore_first_arg(a));
|
||||
auto_restore!(args if swap => move |a| backup.restore_first_arg(a));
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
if self.debugger.is_some() {
|
||||
if self.is_debugger_registered() {
|
||||
let source = source.clone().or_else(|| global.source.clone());
|
||||
|
||||
global.debugger_mut().push_call_stack_frame(
|
||||
@ -399,25 +397,29 @@ impl Engine {
|
||||
// Run external function
|
||||
let is_method = func.is_method();
|
||||
let src = source.as_ref().map(|s| s.as_str());
|
||||
let context = (self, name, src, &*global, pos).into();
|
||||
|
||||
let mut _result = if func.is_plugin_fn() {
|
||||
let f = func.get_plugin_fn().unwrap();
|
||||
let context = if func.has_context() {
|
||||
Some((self, name, src, &*global, pos).into())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut _result = if let Some(f) = func.get_plugin_fn() {
|
||||
if !f.is_pure() && !args.is_empty() && args[0].is_read_only() {
|
||||
Err(ERR::ErrorNonPureMethodCallOnConstant(name.to_string(), pos).into())
|
||||
} else {
|
||||
f.call(context, args)
|
||||
.and_then(|r| self.check_data_size(r, pos))
|
||||
.map_err(|err| err.fill_position(pos))
|
||||
}
|
||||
} else if let Some(f) = func.get_native_fn() {
|
||||
f(context, args)
|
||||
} else {
|
||||
func.get_native_fn().unwrap()(context, args)
|
||||
unreachable!();
|
||||
}
|
||||
.and_then(|r| self.check_data_size(r, pos))
|
||||
.map_err(|err| err.fill_position(pos))
|
||||
};
|
||||
.map_err(|err| err.fill_position(pos));
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
if self.debugger.is_some() {
|
||||
if self.is_debugger_registered() {
|
||||
use crate::eval::{DebuggerEvent, DebuggerStatus};
|
||||
|
||||
let trigger = match global.debugger().status {
|
||||
@ -542,9 +544,9 @@ impl Engine {
|
||||
}
|
||||
}
|
||||
|
||||
/// # Main Entry-Point
|
||||
/// # Main Entry-Point (By Name)
|
||||
///
|
||||
/// Perform an actual function call, native Rust or scripted, taking care of special functions.
|
||||
/// Perform an actual function call, native Rust or scripted, by name, taking care of special functions.
|
||||
///
|
||||
/// # WARNING
|
||||
///
|
||||
@ -559,7 +561,7 @@ impl Engine {
|
||||
caches: &mut Caches,
|
||||
_scope: Option<&mut Scope>,
|
||||
fn_name: &str,
|
||||
op_token: Option<&Token>,
|
||||
op_token: Token,
|
||||
hashes: FnCallHashes,
|
||||
mut _args: &mut FnCallArgs,
|
||||
is_ref_mut: bool,
|
||||
@ -578,9 +580,7 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
ensure_no_data_race(fn_name, _args, is_ref_mut)?;
|
||||
|
||||
let orig_level = global.level;
|
||||
global.level += 1;
|
||||
let global = &mut *RestoreOnDrop::lock(global, move |g| g.level = orig_level);
|
||||
auto_restore! { let orig_level = global.level; global.level += 1 }
|
||||
|
||||
// These may be redirected from method style calls.
|
||||
if hashes.is_native_only() {
|
||||
@ -638,7 +638,7 @@ impl Engine {
|
||||
let local_entry = &mut None;
|
||||
|
||||
if let Some(FnResolutionCacheEntry { func, ref source }) = self
|
||||
.resolve_fn(global, caches, local_entry, None, hash, None, false)
|
||||
.resolve_fn(global, caches, local_entry, NO_TOKEN, hash, None, false)
|
||||
.cloned()
|
||||
{
|
||||
// Script function call
|
||||
@ -659,7 +659,7 @@ impl Engine {
|
||||
};
|
||||
|
||||
let orig_source = mem::replace(&mut global.source, source.clone());
|
||||
let global = &mut *RestoreOnDrop::lock(global, move |g| g.source = orig_source);
|
||||
auto_restore!(global => move |g| g.source = orig_source);
|
||||
|
||||
return if _is_method_call {
|
||||
// Method call of script function - map first argument to `this`
|
||||
@ -679,9 +679,7 @@ impl Engine {
|
||||
backup.change_first_arg_to_copy(_args);
|
||||
}
|
||||
|
||||
let args = &mut *RestoreOnDrop::lock_if(swap, _args, move |a| {
|
||||
backup.restore_first_arg(a)
|
||||
});
|
||||
auto_restore!(args = (_args) if swap => move |a| backup.restore_first_arg(a));
|
||||
|
||||
let mut this = Dynamic::NULL;
|
||||
|
||||
@ -721,15 +719,13 @@ impl Engine {
|
||||
|
||||
// Do not match function exit for arguments
|
||||
#[cfg(feature = "debugging")]
|
||||
let reset = global.debugger.as_mut().and_then(|dbg| {
|
||||
let reset = global.debugger.as_deref_mut().and_then(|dbg| {
|
||||
dbg.clear_status_if(|status| {
|
||||
matches!(status, crate::eval::DebuggerStatus::FunctionExit(..))
|
||||
})
|
||||
});
|
||||
#[cfg(feature = "debugging")]
|
||||
let global = &mut *RestoreOnDrop::lock_if(reset.is_some(), global, move |g| {
|
||||
g.debugger_mut().reset_status(reset)
|
||||
});
|
||||
auto_restore!(global if reset.is_some() => 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()))
|
||||
@ -782,7 +778,7 @@ impl Engine {
|
||||
caches,
|
||||
None,
|
||||
fn_name,
|
||||
None,
|
||||
NO_TOKEN,
|
||||
new_hash,
|
||||
&mut args,
|
||||
false,
|
||||
@ -836,7 +832,7 @@ impl Engine {
|
||||
caches,
|
||||
None,
|
||||
&fn_name,
|
||||
None,
|
||||
NO_TOKEN,
|
||||
new_hash,
|
||||
&mut args,
|
||||
is_ref_mut,
|
||||
@ -935,7 +931,7 @@ impl Engine {
|
||||
caches,
|
||||
None,
|
||||
fn_name,
|
||||
None,
|
||||
NO_TOKEN,
|
||||
hash,
|
||||
&mut args,
|
||||
is_ref_mut,
|
||||
@ -961,7 +957,7 @@ impl Engine {
|
||||
scope: &mut Scope,
|
||||
this_ptr: &mut Dynamic,
|
||||
fn_name: &str,
|
||||
op_token: Option<&Token>,
|
||||
op_token: Token,
|
||||
first_arg: Option<&Expr>,
|
||||
args_expr: &[Expr],
|
||||
hashes: FnCallHashes,
|
||||
@ -977,7 +973,7 @@ impl Engine {
|
||||
let redirected; // Handle call() - Redirect function call
|
||||
|
||||
match name {
|
||||
_ if op_token.is_some() => (),
|
||||
_ if op_token != NO_TOKEN => (),
|
||||
|
||||
// Handle call()
|
||||
KEYWORD_FN_PTR_CALL if total_args >= 1 => {
|
||||
@ -1208,12 +1204,7 @@ impl Engine {
|
||||
|
||||
self.track_operation(global, first_expr.position())?;
|
||||
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
let target_is_shared = target.is_shared();
|
||||
#[cfg(feature = "no_closure")]
|
||||
let target_is_shared = false;
|
||||
|
||||
if target_is_shared || target.is_temp_value() {
|
||||
if target.is_shared() || target.is_temp_value() {
|
||||
arg_values.insert(0, target.take_or_clone().flatten());
|
||||
} else {
|
||||
// Turn it into a method call only if the object is not shared and not a simple value
|
||||
@ -1370,9 +1361,7 @@ impl Engine {
|
||||
}
|
||||
}
|
||||
|
||||
let orig_level = global.level;
|
||||
global.level += 1;
|
||||
let global = &mut *RestoreOnDrop::lock(global, move |g| g.level = orig_level);
|
||||
auto_restore! { let orig_level = global.level; global.level += 1 }
|
||||
|
||||
match func {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
@ -1382,7 +1371,7 @@ impl Engine {
|
||||
let mut this = Dynamic::NULL;
|
||||
|
||||
let orig_source = mem::replace(&mut global.source, module.id_raw().cloned());
|
||||
let global = &mut *RestoreOnDrop::lock(global, move |g| g.source = orig_source);
|
||||
auto_restore!(global => move |g| g.source = orig_source);
|
||||
|
||||
self.call_script_fn(
|
||||
global, caches, new_scope, &mut this, fn_def, &mut args, true, pos,
|
||||
@ -1390,8 +1379,12 @@ impl Engine {
|
||||
}
|
||||
|
||||
Some(f) if f.is_plugin_fn() => {
|
||||
let context = (self, fn_name, module.id(), &*global, pos).into();
|
||||
let f = f.get_plugin_fn().expect("plugin function");
|
||||
let context = if f.has_context() {
|
||||
Some((self, fn_name, module.id(), &*global, pos).into())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if !f.is_pure() && !args.is_empty() && args[0].is_read_only() {
|
||||
Err(ERR::ErrorNonPureMethodCallOnConstant(fn_name.to_string(), pos).into())
|
||||
} else {
|
||||
@ -1402,7 +1395,11 @@ impl Engine {
|
||||
|
||||
Some(f) if f.is_native() => {
|
||||
let func = f.get_native_fn().expect("native function");
|
||||
let context = (self, fn_name, module.id(), &*global, pos).into();
|
||||
let context = if f.has_context() {
|
||||
Some((self, fn_name, module.id(), &*global, pos).into())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
func(context, &mut args).and_then(|r| self.check_data_size(r, pos))
|
||||
}
|
||||
|
||||
@ -1467,6 +1464,8 @@ impl Engine {
|
||||
self.eval_global_statements(global, caches, scope, statements)
|
||||
}
|
||||
|
||||
/// # Main Entry-Point (`FnCallExpr`)
|
||||
///
|
||||
/// Evaluate a function call expression.
|
||||
pub(crate) fn eval_fn_call_expr(
|
||||
&self,
|
||||
@ -1488,10 +1487,26 @@ impl Engine {
|
||||
..
|
||||
} = expr;
|
||||
|
||||
let op_token = op_token.as_ref();
|
||||
let op_token = op_token.clone();
|
||||
|
||||
// Short-circuit native unary operator call if under Fast Operators mode
|
||||
if op_token == Token::Bang && self.fast_operators() && args.len() == 1 {
|
||||
let mut value = self
|
||||
.get_arg_value(global, caches, scope, this_ptr, &args[0])?
|
||||
.0
|
||||
.flatten();
|
||||
|
||||
return value.as_bool().and_then(|r| Ok((!r).into())).or_else(|_| {
|
||||
let operand = &mut [&mut value];
|
||||
self.exec_fn_call(
|
||||
global, caches, None, name, op_token, *hashes, operand, false, false, pos,
|
||||
)
|
||||
.map(|(v, ..)| v)
|
||||
});
|
||||
}
|
||||
|
||||
// Short-circuit native binary operator call if under Fast Operators mode
|
||||
if op_token.is_some() && self.fast_operators() && args.len() == 2 {
|
||||
if op_token != NO_TOKEN && self.fast_operators() && args.len() == 2 {
|
||||
let mut lhs = self
|
||||
.get_arg_value(global, caches, scope, this_ptr, &args[0])?
|
||||
.0
|
||||
@ -1504,15 +1519,17 @@ impl Engine {
|
||||
|
||||
let operands = &mut [&mut lhs, &mut rhs];
|
||||
|
||||
if let Some(func) =
|
||||
get_builtin_binary_op_fn(op_token.as_ref().unwrap(), operands[0], operands[1])
|
||||
if let Some((func, ctx)) =
|
||||
get_builtin_binary_op_fn(op_token.clone(), operands[0], operands[1])
|
||||
{
|
||||
// Built-in found
|
||||
let orig_level = global.level;
|
||||
global.level += 1;
|
||||
let global = &*RestoreOnDrop::lock(global, move |g| g.level = orig_level);
|
||||
auto_restore! { let orig_level = global.level; global.level += 1 }
|
||||
|
||||
let context = (self, name.as_str(), None, global, pos).into();
|
||||
let context = if ctx {
|
||||
Some((self, name.as_str(), None, &*global, pos).into())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
return func(context, operands);
|
||||
}
|
||||
|
||||
|
@ -14,10 +14,10 @@ use std::prelude::v1::*;
|
||||
#[non_exhaustive]
|
||||
pub enum CallableFunction {
|
||||
/// A pure native Rust function with all arguments passed by value.
|
||||
Pure(Shared<FnAny>),
|
||||
Pure(Shared<FnAny>, bool),
|
||||
/// A native Rust object method with the first argument passed by reference,
|
||||
/// and the rest passed by value.
|
||||
Method(Shared<FnAny>),
|
||||
Method(Shared<FnAny>, bool),
|
||||
/// An iterator function.
|
||||
Iterator(Shared<IteratorFn>),
|
||||
/// A plugin function,
|
||||
@ -136,6 +136,18 @@ impl CallableFunction {
|
||||
Self::Script(..) => false,
|
||||
}
|
||||
}
|
||||
/// Is there a [`NativeCallContext`][crate::NativeCallContext] parameter?
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn has_context(&self) -> bool {
|
||||
match self {
|
||||
Self::Pure(.., ctx) | Self::Method(.., ctx) => *ctx,
|
||||
Self::Plugin(f) => f.has_context(),
|
||||
Self::Iterator(..) => false,
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Self::Script(..) => false,
|
||||
}
|
||||
}
|
||||
/// Get the access mode.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
@ -156,7 +168,7 @@ impl CallableFunction {
|
||||
#[must_use]
|
||||
pub fn get_native_fn(&self) -> Option<&Shared<FnAny>> {
|
||||
match self {
|
||||
Self::Pure(f) | Self::Method(f) => Some(f),
|
||||
Self::Pure(f, ..) | Self::Method(f, ..) => Some(f),
|
||||
Self::Iterator(..) | Self::Plugin(..) => None,
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
@ -204,16 +216,16 @@ impl CallableFunction {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
impl From<crate::ast::ScriptFnDef> for CallableFunction {
|
||||
#[inline(always)]
|
||||
fn from(_func: crate::ast::ScriptFnDef) -> Self {
|
||||
Self::Script(_func.into())
|
||||
fn from(func: crate::ast::ScriptFnDef) -> Self {
|
||||
Self::Script(func.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
impl From<Shared<crate::ast::ScriptFnDef>> for CallableFunction {
|
||||
#[inline(always)]
|
||||
fn from(_func: Shared<crate::ast::ScriptFnDef>) -> Self {
|
||||
Self::Script(_func)
|
||||
fn from(func: Shared<crate::ast::ScriptFnDef>) -> Self {
|
||||
Self::Script(func)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ use std::prelude::v1::*;
|
||||
/// Trait to create a Rust closure from a script.
|
||||
///
|
||||
/// Not available under `no_function`.
|
||||
pub trait Func<ARGS, RET> {
|
||||
pub trait Func<A, R> {
|
||||
/// The closure's output type.
|
||||
type Output;
|
||||
|
||||
@ -91,14 +91,14 @@ macro_rules! def_anonymous_fn {
|
||||
impl<$($par: Variant + Clone,)* RET: Variant + Clone> Func<($($par,)*), RET> for Engine
|
||||
{
|
||||
#[cfg(feature = "sync")]
|
||||
type Output = Box<dyn Fn($($par),*) -> RhaiResultOf<RET> + Send + Sync>;
|
||||
type Output = Box<dyn Fn($($par,)*) -> RhaiResultOf<RET> + Send + Sync>;
|
||||
#[cfg(not(feature = "sync"))]
|
||||
type Output = Box<dyn Fn($($par),*) -> RhaiResultOf<RET>>;
|
||||
type Output = Box<dyn Fn($($par,)*) -> RhaiResultOf<RET>>;
|
||||
|
||||
#[inline]
|
||||
fn create_from_ast(self, ast: AST, entry_point: &str) -> Self::Output {
|
||||
let fn_name: SmartString = entry_point.into();
|
||||
Box::new(move |$($par),*| self.call_fn(&mut Scope::new(), &ast, &fn_name, ($($par,)*)))
|
||||
Box::new(move |$($par,)*| self.call_fn(&mut Scope::new(), &ast, &fn_name, ($($par,)*)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -4,11 +4,11 @@ use super::call::FnCallArgs;
|
||||
use crate::ast::FnCallHashes;
|
||||
use crate::eval::{Caches, GlobalRuntimeState};
|
||||
use crate::plugin::PluginFunction;
|
||||
use crate::tokenizer::{is_valid_function_name, Token, TokenizeState};
|
||||
use crate::tokenizer::{is_valid_function_name, Token, TokenizeState, NO_TOKEN};
|
||||
use crate::types::dynamic::Variant;
|
||||
use crate::{
|
||||
calc_fn_hash, reify, Dynamic, Engine, EvalContext, FuncArgs, Position, RhaiResult,
|
||||
RhaiResultOf, StaticVec, VarDefInfo, ERR,
|
||||
calc_fn_hash, Dynamic, Engine, EvalContext, FuncArgs, Position, RhaiResult, RhaiResultOf,
|
||||
StaticVec, VarDefInfo, ERR,
|
||||
};
|
||||
use std::any::{type_name, TypeId};
|
||||
#[cfg(feature = "no_std")]
|
||||
@ -424,8 +424,7 @@ impl<'a> NativeCallContext<'a> {
|
||||
let caches = &mut Caches::new();
|
||||
|
||||
let fn_name = fn_name.as_ref();
|
||||
let op_token = Token::lookup_symbol_from_syntax(fn_name);
|
||||
let op_token = op_token.as_ref();
|
||||
let op_token = Token::lookup_symbol_from_syntax(fn_name).unwrap_or(NO_TOKEN);
|
||||
let args_len = args.len();
|
||||
|
||||
global.level += 1;
|
||||
@ -547,13 +546,16 @@ pub fn locked_write<T>(value: &Locked<T>) -> LockGuardMut<T> {
|
||||
|
||||
/// General Rust function trail object.
|
||||
#[cfg(not(feature = "sync"))]
|
||||
pub type FnAny = dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult;
|
||||
pub type FnAny = dyn Fn(Option<NativeCallContext>, &mut FnCallArgs) -> RhaiResult;
|
||||
/// General Rust function trail object.
|
||||
#[cfg(feature = "sync")]
|
||||
pub type FnAny = dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult + Send + Sync;
|
||||
pub type FnAny = dyn Fn(Option<NativeCallContext>, &mut FnCallArgs) -> RhaiResult + Send + Sync;
|
||||
|
||||
/// Built-in function trait object.
|
||||
pub type FnBuiltin = fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult;
|
||||
pub type FnBuiltin = (
|
||||
fn(Option<NativeCallContext>, &mut FnCallArgs) -> RhaiResult,
|
||||
bool,
|
||||
);
|
||||
|
||||
/// Function that gets an iterator from a type.
|
||||
#[cfg(not(feature = "sync"))]
|
||||
|
@ -24,12 +24,16 @@ pub use rhai_codegen::{export_fn, register_exported_fn};
|
||||
/// Use the `#[export_module]` and `#[export_fn]` procedural attributes instead.
|
||||
pub trait PluginFunction {
|
||||
/// Call the plugin function with the arguments provided.
|
||||
fn call(&self, context: NativeCallContext, args: &mut FnCallArgs) -> RhaiResult;
|
||||
fn call(&self, context: Option<NativeCallContext>, args: &mut FnCallArgs) -> RhaiResult;
|
||||
|
||||
/// Is this plugin function a method?
|
||||
#[must_use]
|
||||
fn is_method_call(&self) -> bool;
|
||||
|
||||
/// Does this plugin function contain a [`NativeCallContext`] parameter?
|
||||
#[must_use]
|
||||
fn has_context(&self) -> bool;
|
||||
|
||||
/// Is this plugin function pure?
|
||||
///
|
||||
/// This defaults to `true` such that any old implementation that has constant-checking code
|
||||
|
@ -1,15 +1,21 @@
|
||||
//! Module which defines the function registration mechanism.
|
||||
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(unused_mut)]
|
||||
#![allow(unused_variables)]
|
||||
|
||||
use super::call::FnCallArgs;
|
||||
use super::callable_function::CallableFunction;
|
||||
use super::native::{SendSync, Shared};
|
||||
use crate::types::dynamic::{DynamicWriteLock, Variant};
|
||||
use crate::{reify, Dynamic, NativeCallContext, RhaiResultOf};
|
||||
use crate::{Dynamic, Identifier, NativeCallContext, RhaiResultOf};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{any::TypeId, mem};
|
||||
use std::{
|
||||
any::{type_name, TypeId},
|
||||
mem,
|
||||
};
|
||||
|
||||
/// These types are used to build a unique _marker_ tuple type for each combination
|
||||
/// of function parameter types in order to make each trait implementation unique.
|
||||
@ -19,13 +25,13 @@ use std::{any::TypeId, mem};
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// `RegisterNativeFunction<(Mut<A>, B, Ref<C>), R, ()>` = `Fn(&mut A, B, &C) -> R`
|
||||
/// `RegisterNativeFunction<(Mut<A>, B, Ref<C>), 3, false, R, false>` = `Fn(&mut A, B, &C) -> R`
|
||||
///
|
||||
/// `RegisterNativeFunction<(Mut<A>, B, Ref<C>), R, NativeCallContext>` = `Fn(NativeCallContext, &mut A, B, &C) -> R`
|
||||
/// `RegisterNativeFunction<(Mut<A>, B, Ref<C>), 3, true, R, false>` = `Fn(NativeCallContext, &mut A, B, &C) -> R`
|
||||
///
|
||||
/// `RegisterNativeFunction<(Mut<A>, B, Ref<C>), R, RhaiResultOf<()>>` = `Fn(&mut A, B, &C) -> Result<R, Box<EvalAltResult>>`
|
||||
/// `RegisterNativeFunction<(Mut<A>, B, Ref<C>), 3, false, R, true>` = `Fn(&mut A, B, &C) -> Result<R, Box<EvalAltResult>>`
|
||||
///
|
||||
/// `RegisterNativeFunction<(Mut<A>, B, Ref<C>), R, RhaiResultOf<NativeCallContext>>` = `Fn(NativeCallContext, &mut A, B, &C) -> Result<R, Box<EvalAltResult>>`
|
||||
/// `RegisterNativeFunction<(Mut<A>, B, Ref<C>), 3, true, R, true>` = `Fn(NativeCallContext, &mut A, B, &C) -> Result<R, Box<EvalAltResult>>`
|
||||
///
|
||||
/// These types are not actually used anywhere.
|
||||
pub struct Mut<T>(T);
|
||||
@ -64,123 +70,137 @@ pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T {
|
||||
///
|
||||
/// # Type Parameters
|
||||
///
|
||||
/// * `ARGS` - a tuple containing parameter types, with `&mut T` represented by `Mut<T>`.
|
||||
/// * `RET` - return type of the function; if the function returns `Result`, it is the unwrapped inner value type.
|
||||
pub trait RegisterNativeFunction<ARGS, RET, RESULT> {
|
||||
/// * `A` - a tuple containing parameter types, with `&mut T` represented by `Mut<T>`.
|
||||
/// * `N` - a constant generic containing the number of parameters, must be consistent with `ARGS`.
|
||||
/// * `X` - a constant boolean generic indicating whether there is a `NativeCallContext` parameter.
|
||||
/// * `R` - return type of the function; if the function returns `Result`, it is the unwrapped inner value type.
|
||||
/// * `F` - a constant boolean generic indicating whether the function is fallible (i.e. returns `Result<T, Box<EvalAltResult>>`).
|
||||
pub trait RegisterNativeFunction<
|
||||
A: 'static,
|
||||
const N: usize,
|
||||
const X: bool,
|
||||
R: 'static,
|
||||
const F: bool,
|
||||
>
|
||||
{
|
||||
/// Convert this function into a [`CallableFunction`].
|
||||
#[must_use]
|
||||
fn into_callable_function(self) -> CallableFunction;
|
||||
fn into_callable_function(self, name: Identifier, no_const: bool) -> CallableFunction;
|
||||
/// Get the type ID's of this function's parameters.
|
||||
#[must_use]
|
||||
fn param_types() -> Box<[TypeId]>;
|
||||
fn param_types() -> [TypeId; N];
|
||||
/// Get the number of parameters for this function.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn num_params() -> usize {
|
||||
N
|
||||
}
|
||||
/// Is there a [`NativeCallContext`] parameter for this function?
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn has_context() -> bool {
|
||||
X
|
||||
}
|
||||
/// _(metadata)_ Get the type names of this function's parameters.
|
||||
/// Exported under the `metadata` feature only.
|
||||
#[cfg(feature = "metadata")]
|
||||
#[must_use]
|
||||
fn param_names() -> Box<[&'static str]>;
|
||||
fn param_names() -> [&'static str; N];
|
||||
/// _(metadata)_ Get the type ID of this function's return value.
|
||||
/// Exported under the `metadata` feature only.
|
||||
#[cfg(feature = "metadata")]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn return_type() -> TypeId;
|
||||
fn return_type() -> TypeId {
|
||||
if F {
|
||||
TypeId::of::<RhaiResultOf<R>>()
|
||||
} else {
|
||||
TypeId::of::<R>()
|
||||
}
|
||||
}
|
||||
/// _(metadata)_ Get the type name of this function's return value.
|
||||
/// Exported under the `metadata` feature only.
|
||||
#[cfg(feature = "metadata")]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn return_type_name() -> &'static str {
|
||||
std::any::type_name::<RET>()
|
||||
type_name::<R>()
|
||||
}
|
||||
}
|
||||
|
||||
const EXPECT_ARGS: &str = "arguments";
|
||||
|
||||
macro_rules! check_constant {
|
||||
($abi:ident, $ctx:ident, $args:ident) => {
|
||||
($abi:ident, $n:expr, $fn_name:ident, $no_const:ident, $args:ident) => {
|
||||
#[cfg(any(not(feature = "no_object"), not(feature = "no_index")))]
|
||||
if stringify!($abi) == "Method" && !$args.is_empty() {
|
||||
let deny = match $args.len() {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
3 if $ctx.fn_name() == crate::engine::FN_IDX_SET && $args[0].is_read_only() => true,
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
2 if $ctx.fn_name().starts_with(crate::engine::FN_SET)
|
||||
&& $args[0].is_read_only() =>
|
||||
{
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if deny {
|
||||
if stringify!($abi) == "Method" && $no_const && $args[0].is_read_only() {
|
||||
return Err(crate::ERR::ErrorNonPureMethodCallOnConstant(
|
||||
$ctx.fn_name().to_string(),
|
||||
$fn_name.to_string(),
|
||||
crate::Position::NONE,
|
||||
)
|
||||
.into());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! def_register {
|
||||
() => {
|
||||
def_register!(imp Pure :);
|
||||
def_register!(imp Pure : 0;);
|
||||
};
|
||||
(imp $abi:ident : $($par:ident => $arg:expr => $mark:ty => $param:ty => $let:stmt => $clone:expr),*) => {
|
||||
(imp $abi:ident : $n:expr ; $($par:ident => $arg:expr => $mark:ty => $param:ty => $clone:expr),*) => {
|
||||
// ^ function ABI type
|
||||
// ^ number of parameters
|
||||
// ^ function parameter generic type name (A, B, C etc.)
|
||||
// ^ call argument(like A, *B, &mut C etc)
|
||||
// ^ function parameter marker type (A, Ref<B> or Mut<C>)
|
||||
// ^ function parameter actual type (A, &B or &mut C)
|
||||
// ^ argument let statement
|
||||
// ^ parameter access function (by_value or by_ref)
|
||||
|
||||
impl<
|
||||
FN: Fn($($param),*) -> RET + SendSync + 'static,
|
||||
$($par: Variant + Clone,)*
|
||||
RET: Variant + Clone
|
||||
> RegisterNativeFunction<($($mark,)*), RET, ()> for FN {
|
||||
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
|
||||
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(std::any::type_name::<$param>()),*].into_boxed_slice() }
|
||||
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<RET>() }
|
||||
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
||||
CallableFunction::$abi(Shared::new(move |_ctx: NativeCallContext, args: &mut FnCallArgs| {
|
||||
RET: Variant + Clone,
|
||||
> RegisterNativeFunction<($($mark,)*), $n, false, RET, false> for FN {
|
||||
#[inline(always)] fn param_types() -> [TypeId;$n] { [$(TypeId::of::<$par>()),*] }
|
||||
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> [&'static str;$n] { [$(type_name::<$param>()),*] }
|
||||
#[inline(always)] fn into_callable_function(self, fn_name: Identifier, no_const: bool) -> CallableFunction {
|
||||
CallableFunction::$abi(Shared::new(move |_, args: &mut FnCallArgs| {
|
||||
// The arguments are assumed to be of the correct number and types!
|
||||
check_constant!($abi, _ctx, args);
|
||||
check_constant!($abi, $n, fn_name, no_const, args);
|
||||
|
||||
let mut _drain = args.iter_mut();
|
||||
$($let $par = ($clone)(_drain.next().expect(EXPECT_ARGS)); )*
|
||||
let mut drain = args.iter_mut();
|
||||
$(let mut $par = $clone(drain.next().unwrap()); )*
|
||||
|
||||
// Call the function with each argument value
|
||||
let r = self($($arg),*);
|
||||
|
||||
// Map the result
|
||||
Ok(Dynamic::from(r))
|
||||
}))
|
||||
}), false)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
FN: for<'a> Fn(NativeCallContext<'a>, $($param),*) -> RET + SendSync + 'static,
|
||||
$($par: Variant + Clone,)*
|
||||
RET: Variant + Clone
|
||||
> RegisterNativeFunction<($($mark,)*), RET, NativeCallContext<'static>> for FN {
|
||||
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
|
||||
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(std::any::type_name::<$param>()),*].into_boxed_slice() }
|
||||
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<RET>() }
|
||||
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
||||
CallableFunction::$abi(Shared::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
|
||||
// The arguments are assumed to be of the correct number and types!
|
||||
check_constant!($abi, ctx, args);
|
||||
RET: Variant + Clone,
|
||||
> RegisterNativeFunction<($($mark,)*), $n, true, RET, false> for FN {
|
||||
#[inline(always)] fn param_types() -> [TypeId;$n] { [$(TypeId::of::<$par>()),*] }
|
||||
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> [&'static str;$n] { [$(type_name::<$param>()),*] }
|
||||
#[inline(always)] fn into_callable_function(self, fn_name: Identifier, no_const: bool) -> CallableFunction {
|
||||
CallableFunction::$abi(Shared::new(move |ctx: Option<NativeCallContext>, args: &mut FnCallArgs| {
|
||||
let ctx = ctx.unwrap();
|
||||
|
||||
let mut _drain = args.iter_mut();
|
||||
$($let $par = ($clone)(_drain.next().expect(EXPECT_ARGS)); )*
|
||||
// The arguments are assumed to be of the correct number and types!
|
||||
check_constant!($abi, $n, fn_name, no_const, args);
|
||||
|
||||
let mut drain = args.iter_mut();
|
||||
$(let mut $par = $clone(drain.next().unwrap()); )*
|
||||
|
||||
// Call the function with each argument value
|
||||
let r = self(ctx, $($arg),*);
|
||||
|
||||
// Map the result
|
||||
Ok(Dynamic::from(r))
|
||||
}))
|
||||
}), true)
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,22 +208,21 @@ macro_rules! def_register {
|
||||
FN: Fn($($param),*) -> RhaiResultOf<RET> + SendSync + 'static,
|
||||
$($par: Variant + Clone,)*
|
||||
RET: Variant + Clone
|
||||
> RegisterNativeFunction<($($mark,)*), RET, RhaiResultOf<()>> for FN {
|
||||
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
|
||||
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(std::any::type_name::<$param>()),*].into_boxed_slice() }
|
||||
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<RhaiResultOf<RET>>() }
|
||||
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { std::any::type_name::<RhaiResultOf<RET>>() }
|
||||
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
||||
CallableFunction::$abi(Shared::new(move |_ctx: NativeCallContext, args: &mut FnCallArgs| {
|
||||
> RegisterNativeFunction<($($mark,)*), $n, false, RET, true> for FN {
|
||||
#[inline(always)] fn param_types() -> [TypeId;$n] { [$(TypeId::of::<$par>()),*] }
|
||||
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> [&'static str;$n] { [$(type_name::<$param>()),*] }
|
||||
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { type_name::<RhaiResultOf<RET>>() }
|
||||
#[inline(always)] fn into_callable_function(self, fn_name: Identifier, no_const: bool) -> CallableFunction {
|
||||
CallableFunction::$abi(Shared::new(move |_, args: &mut FnCallArgs| {
|
||||
// The arguments are assumed to be of the correct number and types!
|
||||
check_constant!($abi, _ctx, args);
|
||||
check_constant!($abi, $n, fn_name, no_const, args);
|
||||
|
||||
let mut _drain = args.iter_mut();
|
||||
$($let $par = ($clone)(_drain.next().expect(EXPECT_ARGS)); )*
|
||||
let mut drain = args.iter_mut();
|
||||
$(let mut $par = $clone(drain.next().unwrap()); )*
|
||||
|
||||
// Call the function with each argument value
|
||||
self($($arg),*).map(Dynamic::from)
|
||||
}))
|
||||
}), false)
|
||||
}
|
||||
}
|
||||
|
||||
@ -211,40 +230,41 @@ macro_rules! def_register {
|
||||
FN: for<'a> Fn(NativeCallContext<'a>, $($param),*) -> RhaiResultOf<RET> + SendSync + 'static,
|
||||
$($par: Variant + Clone,)*
|
||||
RET: Variant + Clone
|
||||
> RegisterNativeFunction<($($mark,)*), RET, RhaiResultOf<NativeCallContext<'static>>> for FN {
|
||||
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
|
||||
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(std::any::type_name::<$param>()),*].into_boxed_slice() }
|
||||
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<RhaiResultOf<RET>>() }
|
||||
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { std::any::type_name::<RhaiResultOf<RET>>() }
|
||||
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
||||
CallableFunction::$abi(Shared::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
|
||||
// The arguments are assumed to be of the correct number and types!
|
||||
check_constant!($abi, ctx, args);
|
||||
> RegisterNativeFunction<($($mark,)*), $n, true, RET, true> for FN {
|
||||
#[inline(always)] fn param_types() -> [TypeId;$n] { [$(TypeId::of::<$par>()),*] }
|
||||
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> [&'static str;$n] { [$(type_name::<$param>()),*] }
|
||||
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { type_name::<RhaiResultOf<RET>>() }
|
||||
#[inline(always)] fn into_callable_function(self, fn_name: Identifier, no_const: bool) -> CallableFunction {
|
||||
CallableFunction::$abi(Shared::new(move |ctx: Option<NativeCallContext>, args: &mut FnCallArgs| {
|
||||
let ctx = ctx.unwrap();
|
||||
|
||||
let mut _drain = args.iter_mut();
|
||||
$($let $par = ($clone)(_drain.next().expect(EXPECT_ARGS)); )*
|
||||
// The arguments are assumed to be of the correct number and types!
|
||||
check_constant!($abi, $n, fn_name, no_const, args);
|
||||
|
||||
let mut drain = args.iter_mut();
|
||||
$(let mut $par = $clone(drain.next().unwrap()); )*
|
||||
|
||||
// Call the function with each argument value
|
||||
self(ctx, $($arg),*).map(Dynamic::from)
|
||||
}))
|
||||
}), true)
|
||||
}
|
||||
}
|
||||
|
||||
//def_register!(imp_pop $($par => $mark => $param),*);
|
||||
};
|
||||
($p0:ident $(, $p:ident)*) => {
|
||||
def_register!(imp Pure : $p0 => $p0 => $p0 => $p0 => let $p0 => by_value $(, $p => $p => $p => $p => let $p => by_value)*);
|
||||
def_register!(imp Method : $p0 => &mut $p0 => Mut<$p0> => &mut $p0 => let mut $p0 => by_ref $(, $p => $p => $p => $p => let $p => by_value)*);
|
||||
($p0:ident:$n0:expr $(, $p:ident: $n:expr)*) => {
|
||||
def_register!(imp Pure : $n0 ; $p0 => $p0 => $p0 => $p0 => by_value $(, $p => $p => $p => $p => by_value)*);
|
||||
def_register!(imp Method : $n0 ; $p0 => &mut $p0 => Mut<$p0> => &mut $p0 => by_ref $(, $p => $p => $p => $p => by_value)*);
|
||||
// ^ CallableFunction constructor
|
||||
// ^ first parameter passed through
|
||||
// ^ number of arguments ^ first parameter passed through
|
||||
// ^ others passed by value (by_value)
|
||||
|
||||
// Currently does not support first argument which is a reference, as there will be
|
||||
// conflicting implementations since &T: Any and T: Any cannot be distinguished
|
||||
//def_register!(imp $p0 => Ref<$p0> => &$p0 => by_ref $(, $p => $p => $p => by_value)*);
|
||||
|
||||
def_register!($($p),*);
|
||||
def_register!($($p: $n),*);
|
||||
};
|
||||
}
|
||||
|
||||
def_register!(A, B, C, D, E, F, G, H, J, K, L, M, N, P, Q, R, S, T, U, V);
|
||||
def_register!(A:20, B:19, C:18, D:17, E:16, F:15, G:14, H:13, J:12, K:11, L:10, M:9, N:8, P:7, Q:6, R:5, S:4, T:3, U:2, V:1);
|
||||
|
@ -4,7 +4,7 @@
|
||||
use super::call::FnCallArgs;
|
||||
use crate::ast::ScriptFnDef;
|
||||
use crate::eval::{Caches, GlobalRuntimeState};
|
||||
use crate::{Dynamic, Engine, Position, RhaiError, RhaiResult, Scope, ERR};
|
||||
use crate::{Dynamic, Engine, Position, RhaiResult, Scope, ERR};
|
||||
use std::mem;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@ -33,32 +33,6 @@ impl Engine {
|
||||
rewind_scope: bool,
|
||||
pos: Position,
|
||||
) -> RhaiResult {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn make_error(
|
||||
name: String,
|
||||
_fn_def: &ScriptFnDef,
|
||||
global: &GlobalRuntimeState,
|
||||
err: RhaiError,
|
||||
pos: Position,
|
||||
) -> RhaiResult {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
let source = _fn_def
|
||||
.environ
|
||||
.as_ref()
|
||||
.and_then(|environ| environ.lib.id().map(str::to_string));
|
||||
#[cfg(feature = "no_module")]
|
||||
let source = None;
|
||||
|
||||
Err(ERR::ErrorInFunctionCall(
|
||||
name,
|
||||
source.unwrap_or_else(|| global.source().unwrap_or("").to_string()),
|
||||
err,
|
||||
pos,
|
||||
)
|
||||
.into())
|
||||
}
|
||||
|
||||
assert!(fn_def.params.len() == args.len());
|
||||
|
||||
self.track_operation(global, pos)?;
|
||||
@ -69,7 +43,7 @@ impl Engine {
|
||||
}
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
if self.debugger.is_none() && fn_def.body.is_empty() {
|
||||
if self.debugger_interface.is_none() && fn_def.body.is_empty() {
|
||||
return Ok(Dynamic::UNIT);
|
||||
}
|
||||
#[cfg(not(feature = "debugging"))]
|
||||
@ -96,15 +70,14 @@ impl Engine {
|
||||
|
||||
// Push a new call stack frame
|
||||
#[cfg(feature = "debugging")]
|
||||
if self.debugger.is_some() {
|
||||
if self.is_debugger_registered() {
|
||||
let fn_name = fn_def.name.clone();
|
||||
let args = scope.iter().skip(orig_scope_len).map(|(.., v)| v).collect();
|
||||
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(),
|
||||
source,
|
||||
pos,
|
||||
);
|
||||
global
|
||||
.debugger_mut()
|
||||
.push_call_stack_frame(fn_name, args, source, pos);
|
||||
}
|
||||
|
||||
// Merge in encapsulated environment, if any
|
||||
@ -131,37 +104,42 @@ impl Engine {
|
||||
};
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
if self.debugger.is_some() {
|
||||
if self.is_debugger_registered() {
|
||||
let node = crate::ast::Stmt::Noop(fn_def.body.position());
|
||||
self.run_debugger(global, caches, scope, this_ptr, &node)?;
|
||||
}
|
||||
|
||||
// Evaluate the function
|
||||
let mut _result = self
|
||||
let mut _result: RhaiResult = self
|
||||
.eval_stmt_block(global, caches, scope, this_ptr, &fn_def.body, rewind_scope)
|
||||
.or_else(|err| match *err {
|
||||
// Convert return statement to return value
|
||||
ERR::Return(x, ..) => Ok(x),
|
||||
// Error in sub function call
|
||||
ERR::ErrorInFunctionCall(name, src, err, ..) => {
|
||||
let fn_name = if src.is_empty() {
|
||||
format!("{name} < {}", fn_def.name)
|
||||
} else {
|
||||
format!("{name} @ '{src}' < {}", fn_def.name)
|
||||
};
|
||||
make_error(fn_name, fn_def, global, err, pos)
|
||||
}
|
||||
// System errors are passed straight-through
|
||||
mut err if err.is_system_exception() => {
|
||||
err.set_position(pos);
|
||||
Err(err.into())
|
||||
}
|
||||
// Other errors are wrapped in `ErrorInFunctionCall`
|
||||
_ => make_error(fn_def.name.to_string(), fn_def, global, err, pos),
|
||||
_ => Err(ERR::ErrorInFunctionCall(
|
||||
fn_def.name.to_string(),
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
fn_def
|
||||
.environ
|
||||
.as_deref()
|
||||
.and_then(|environ| environ.lib.id())
|
||||
.unwrap_or_else(|| global.source().unwrap_or(""))
|
||||
.to_string(),
|
||||
#[cfg(feature = "no_module")]
|
||||
global.source().unwrap_or("").to_string(),
|
||||
err,
|
||||
pos,
|
||||
)
|
||||
.into()),
|
||||
});
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
if self.debugger.is_some() {
|
||||
if self.is_debugger_registered() {
|
||||
let trigger = match global.debugger_mut().status {
|
||||
crate::eval::DebuggerStatus::FunctionExit(n) => n >= global.level,
|
||||
crate::eval::DebuggerStatus::Next(.., true) => true,
|
||||
@ -236,7 +214,9 @@ impl Engine {
|
||||
// Then check imported modules
|
||||
global.contains_qualified_fn(hash_script)
|
||||
// Then check sub-modules
|
||||
|| self.global_sub_modules.iter().flat_map(|m| m.values()).any(|m| m.contains_qualified_fn(hash_script));
|
||||
|| self.global_sub_modules.as_deref().map_or(false, |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"
|
||||
|
13
src/lib.rs
13
src/lib.rs
@ -85,6 +85,11 @@ extern crate no_std_compat as std;
|
||||
use std::prelude::v1::*;
|
||||
|
||||
// Internal modules
|
||||
#[macro_use]
|
||||
mod reify;
|
||||
#[macro_use]
|
||||
mod types;
|
||||
|
||||
mod api;
|
||||
mod ast;
|
||||
pub mod config;
|
||||
@ -95,10 +100,8 @@ mod module;
|
||||
mod optimizer;
|
||||
pub mod packages;
|
||||
mod parser;
|
||||
mod reify;
|
||||
mod tests;
|
||||
mod tokenizer;
|
||||
mod types;
|
||||
|
||||
/// Error encountered when parsing a script.
|
||||
type PERR = ParseErrorType;
|
||||
@ -207,9 +210,9 @@ pub use engine::{Engine, OP_CONTAINS, OP_EQUALS};
|
||||
pub use eval::EvalContext;
|
||||
pub use func::{NativeCallContext, RegisterNativeFunction};
|
||||
pub use module::{FnNamespace, Module};
|
||||
pub use tokenizer::Position;
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
pub use types::Instant;
|
||||
pub use types::Position;
|
||||
pub use types::{
|
||||
Dynamic, EvalAltResult, FnPtr, ImmutableString, LexError, ParseError, ParseErrorType, Scope,
|
||||
};
|
||||
@ -311,12 +314,12 @@ pub use types::dynamic::{AccessMode, DynamicReadLock, DynamicWriteLock, Variant}
|
||||
pub use types::FloatWrapper;
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
pub use types::StringsInterner;
|
||||
pub use types::{Span, StringsInterner};
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
pub use tokenizer::{
|
||||
get_next_token, is_valid_function_name, is_valid_identifier, parse_string_literal, InputStream,
|
||||
MultiInputsStream, Span, Token, TokenIterator, TokenizeState, TokenizerControl,
|
||||
MultiInputsStream, Token, TokenIterator, TokenizeState, TokenizerControl,
|
||||
TokenizerControlBlock,
|
||||
};
|
||||
|
||||
|
@ -9,8 +9,8 @@ use crate::func::{
|
||||
};
|
||||
use crate::types::{dynamic::Variant, BloomFilterU64, CustomTypesCollection};
|
||||
use crate::{
|
||||
calc_fn_hash, calc_fn_hash_full, Dynamic, Identifier, ImmutableString, NativeCallContext,
|
||||
RhaiResultOf, Shared, SharedModule, SmartString, StaticVec,
|
||||
calc_fn_hash, calc_fn_hash_full, Dynamic, FnArgsVec, Identifier, ImmutableString,
|
||||
NativeCallContext, RhaiResultOf, Shared, SharedModule, SmartString,
|
||||
};
|
||||
use bitflags::bitflags;
|
||||
#[cfg(feature = "no_std")]
|
||||
@ -72,20 +72,20 @@ pub struct FuncInfoMetadata {
|
||||
/// Function access mode.
|
||||
pub access: FnAccess,
|
||||
/// Function name.
|
||||
pub name: ImmutableString,
|
||||
pub name: Identifier,
|
||||
/// Number of parameters.
|
||||
pub num_params: usize,
|
||||
/// Parameter types (if applicable).
|
||||
pub param_types: StaticVec<TypeId>,
|
||||
pub param_types: FnArgsVec<TypeId>,
|
||||
/// Parameter names and types (if available).
|
||||
#[cfg(feature = "metadata")]
|
||||
pub params_info: StaticVec<Identifier>,
|
||||
pub params_info: FnArgsVec<Identifier>,
|
||||
/// Return type name.
|
||||
#[cfg(feature = "metadata")]
|
||||
pub return_type: Identifier,
|
||||
/// Comments.
|
||||
#[cfg(feature = "metadata")]
|
||||
pub comments: Box<[Box<str>]>,
|
||||
pub comments: Box<[Identifier]>,
|
||||
}
|
||||
|
||||
/// A type containing a single registered function.
|
||||
@ -115,7 +115,7 @@ impl FuncInfo {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let params: StaticVec<_> = self
|
||||
let params: FnArgsVec<_> = self
|
||||
.metadata
|
||||
.params_info
|
||||
.iter()
|
||||
@ -231,7 +231,8 @@ impl fmt::Debug for Module {
|
||||
"modules",
|
||||
&self
|
||||
.modules
|
||||
.iter()
|
||||
.as_deref()
|
||||
.into_iter()
|
||||
.flat_map(|m| m.keys())
|
||||
.map(SmartString::as_str)
|
||||
.collect::<Vec<_>>(),
|
||||
@ -392,7 +393,7 @@ impl Module {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn doc(&self) -> &str {
|
||||
self.doc.as_ref().map_or("", |s| s.as_str())
|
||||
self.doc.as_deref().map_or("", SmartString::as_str)
|
||||
}
|
||||
|
||||
/// Set the documentation of the [`Module`].
|
||||
@ -542,16 +543,28 @@ impl Module {
|
||||
#[must_use]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
!self.flags.contains(ModuleFlags::INDEXED_GLOBAL_FUNCTIONS)
|
||||
&& self.functions.as_ref().map_or(true, |m| m.is_empty())
|
||||
&& self.variables.as_ref().map_or(true, |m| m.is_empty())
|
||||
&& self.modules.as_ref().map_or(true, |m| m.is_empty())
|
||||
&& self.type_iterators.as_ref().map_or(true, |t| t.is_empty())
|
||||
&& self.all_functions.as_ref().map_or(true, |m| m.is_empty())
|
||||
&& self.all_variables.as_ref().map_or(true, |m| m.is_empty())
|
||||
&& self
|
||||
.functions
|
||||
.as_ref()
|
||||
.map_or(true, StraightHashMap::is_empty)
|
||||
&& self.variables.as_deref().map_or(true, BTreeMap::is_empty)
|
||||
&& self.modules.as_deref().map_or(true, BTreeMap::is_empty)
|
||||
&& self
|
||||
.type_iterators
|
||||
.as_deref()
|
||||
.map_or(true, BTreeMap::is_empty)
|
||||
&& self
|
||||
.all_functions
|
||||
.as_deref()
|
||||
.map_or(true, StraightHashMap::is_empty)
|
||||
&& self
|
||||
.all_variables
|
||||
.as_deref()
|
||||
.map_or(true, StraightHashMap::is_empty)
|
||||
&& self
|
||||
.all_type_iterators
|
||||
.as_ref()
|
||||
.map_or(true, |m| m.is_empty())
|
||||
.as_deref()
|
||||
.map_or(true, BTreeMap::is_empty)
|
||||
}
|
||||
|
||||
/// Is the [`Module`] indexed?
|
||||
@ -710,7 +723,7 @@ impl Module {
|
||||
namespace: FnNamespace::Internal,
|
||||
access: fn_def.access,
|
||||
num_params,
|
||||
param_types: StaticVec::new_const(),
|
||||
param_types: FnArgsVec::new_const(),
|
||||
#[cfg(feature = "metadata")]
|
||||
params_info,
|
||||
#[cfg(feature = "metadata")]
|
||||
@ -866,16 +879,12 @@ impl Module {
|
||||
/// In other words, the number of entries should be one larger than the number of parameters.
|
||||
#[cfg(feature = "metadata")]
|
||||
#[inline]
|
||||
pub fn update_fn_metadata<S: AsRef<str>>(
|
||||
pub fn update_fn_metadata<S: Into<Identifier>>(
|
||||
&mut self,
|
||||
hash_fn: u64,
|
||||
arg_names: impl AsRef<[S]>,
|
||||
arg_names: impl IntoIterator<Item = S>,
|
||||
) -> &mut Self {
|
||||
let mut param_names: StaticVec<_> = arg_names
|
||||
.as_ref()
|
||||
.iter()
|
||||
.map(|s| s.as_ref().into())
|
||||
.collect();
|
||||
let mut param_names: FnArgsVec<_> = arg_names.into_iter().map(Into::into).collect();
|
||||
|
||||
if let Some(f) = self.functions.as_mut().and_then(|m| m.get_mut(&hash_fn)) {
|
||||
let (param_names, return_type_name) = if param_names.len() > f.metadata.num_params {
|
||||
@ -907,32 +916,30 @@ impl Module {
|
||||
///
|
||||
/// ## Comments
|
||||
///
|
||||
/// Block doc-comments should be kept in a single line.
|
||||
/// Block doc-comments should be kept in a separate string slice.
|
||||
///
|
||||
/// Line doc-comments should be kept in one string slice per line without the termination line-break.
|
||||
/// Line doc-comments should be merged, with line-breaks, into a single string slice without a final termination line-break.
|
||||
///
|
||||
/// Leading white-spaces should be stripped, and each string slice always starts with the corresponding
|
||||
/// doc-comment leader: `///` or `/**`.
|
||||
///
|
||||
/// Each line in non-block doc-comments should start with `///`.
|
||||
#[cfg(feature = "metadata")]
|
||||
#[inline]
|
||||
pub fn update_fn_metadata_with_comments<A: AsRef<str>, C: AsRef<str>>(
|
||||
pub fn update_fn_metadata_with_comments<A: Into<Identifier>, C: Into<Identifier>>(
|
||||
&mut self,
|
||||
hash_fn: u64,
|
||||
arg_names: impl AsRef<[A]>,
|
||||
comments: impl AsRef<[C]>,
|
||||
arg_names: impl IntoIterator<Item = A>,
|
||||
comments: impl IntoIterator<Item = C>,
|
||||
) -> &mut Self {
|
||||
self.update_fn_metadata(hash_fn, arg_names);
|
||||
|
||||
let comments = comments.as_ref();
|
||||
|
||||
if !comments.is_empty() {
|
||||
let f = self
|
||||
.functions
|
||||
self.functions
|
||||
.as_mut()
|
||||
.and_then(|m| m.get_mut(&hash_fn))
|
||||
.unwrap();
|
||||
f.metadata.comments = comments.iter().map(|s| s.as_ref().into()).collect();
|
||||
}
|
||||
.unwrap()
|
||||
.metadata
|
||||
.comments = comments.into_iter().map(Into::into).collect();
|
||||
|
||||
self
|
||||
}
|
||||
@ -997,12 +1004,11 @@ impl Module {
|
||||
let _arg_names = arg_names;
|
||||
let is_method = func.is_method();
|
||||
|
||||
let mut param_types: StaticVec<_> = arg_types
|
||||
let mut param_types: FnArgsVec<_> = arg_types
|
||||
.as_ref()
|
||||
.iter()
|
||||
.copied()
|
||||
.enumerate()
|
||||
.map(|(i, type_id)| Self::map_type(!is_method || i > 0, type_id))
|
||||
.map(|(i, &type_id)| Self::map_type(!is_method || i > 0, type_id))
|
||||
.collect();
|
||||
param_types.shrink_to_fit();
|
||||
|
||||
@ -1013,11 +1019,11 @@ impl Module {
|
||||
#[cfg(feature = "metadata")]
|
||||
let (param_names, return_type_name) = {
|
||||
let mut names = _arg_names
|
||||
.iter()
|
||||
.flat_map(|&p| p.iter())
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(|&s| s.into())
|
||||
.collect::<StaticVec<_>>();
|
||||
let return_type = if names.len() > arg_types.as_ref().len() {
|
||||
.collect::<FnArgsVec<_>>();
|
||||
let return_type = if names.len() > param_types.len() {
|
||||
names.pop().unwrap()
|
||||
} else {
|
||||
crate::SmartString::new_const()
|
||||
@ -1086,12 +1092,14 @@ impl Module {
|
||||
///
|
||||
/// ## Comments
|
||||
///
|
||||
/// Block doc-comments should be kept in a single line.
|
||||
/// Block doc-comments should be kept in a separate string slice.
|
||||
///
|
||||
/// Line doc-comments should be kept in one string slice per line without the termination line-break.
|
||||
/// Line doc-comments should be merged, with line-breaks, into a single string slice without a final termination line-break.
|
||||
///
|
||||
/// Leading white-spaces should be stripped, and each string slice always starts with the corresponding
|
||||
/// doc-comment leader: `///` or `/**`.
|
||||
///
|
||||
/// Each line in non-block doc-comments should start with `///`.
|
||||
#[cfg(feature = "metadata")]
|
||||
#[inline]
|
||||
pub fn set_fn_with_comments<S: AsRef<str>>(
|
||||
@ -1101,17 +1109,18 @@ impl Module {
|
||||
access: FnAccess,
|
||||
arg_names: Option<&[&str]>,
|
||||
arg_types: impl AsRef<[TypeId]>,
|
||||
comments: impl AsRef<[S]>,
|
||||
comments: impl IntoIterator<Item = S>,
|
||||
func: CallableFunction,
|
||||
) -> u64 {
|
||||
let hash = self.set_fn(name, namespace, access, arg_names, arg_types, func);
|
||||
|
||||
let comments = comments.as_ref();
|
||||
|
||||
if !comments.is_empty() {
|
||||
let f = self.functions.as_mut().unwrap().get_mut(&hash).unwrap();
|
||||
f.metadata.comments = comments.iter().map(|s| s.as_ref().into()).collect();
|
||||
}
|
||||
self.functions
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.get_mut(&hash)
|
||||
.unwrap()
|
||||
.metadata
|
||||
.comments = comments.into_iter().map(|s| s.as_ref().into()).collect();
|
||||
|
||||
hash
|
||||
}
|
||||
@ -1191,8 +1200,9 @@ impl Module {
|
||||
arg_types: impl AsRef<[TypeId]>,
|
||||
func: impl Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResultOf<T> + SendSync + 'static,
|
||||
) -> u64 {
|
||||
let f =
|
||||
move |ctx: NativeCallContext, args: &mut FnCallArgs| func(ctx, args).map(Dynamic::from);
|
||||
let f = move |ctx: Option<NativeCallContext>, args: &mut FnCallArgs| {
|
||||
func(ctx.unwrap(), args).map(Dynamic::from)
|
||||
};
|
||||
|
||||
self.set_fn(
|
||||
name,
|
||||
@ -1200,7 +1210,7 @@ impl Module {
|
||||
access,
|
||||
None,
|
||||
arg_types,
|
||||
CallableFunction::Method(Shared::new(f)),
|
||||
CallableFunction::Method(Shared::new(f), true),
|
||||
)
|
||||
}
|
||||
|
||||
@ -1227,22 +1237,33 @@ impl Module {
|
||||
/// assert!(module.contains_fn(hash));
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn set_native_fn<A, T, F, S>(
|
||||
pub fn set_native_fn<A: 'static, const N: usize, const C: bool, T, F>(
|
||||
&mut self,
|
||||
name: impl AsRef<str> + Into<Identifier>,
|
||||
func: F,
|
||||
) -> u64
|
||||
where
|
||||
T: Variant + Clone,
|
||||
F: RegisterNativeFunction<A, T, RhaiResultOf<S>>,
|
||||
F: RegisterNativeFunction<A, N, C, T, true>,
|
||||
{
|
||||
let fn_name = name.into();
|
||||
let no_const = false;
|
||||
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
let no_const = no_const || (F::num_params() == 3 && fn_name == crate::engine::FN_IDX_SET);
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
let no_const =
|
||||
no_const || (F::num_params() == 2 && fn_name.starts_with(crate::engine::FN_SET));
|
||||
|
||||
let func = func.into_callable_function(fn_name.clone(), no_const);
|
||||
|
||||
self.set_fn(
|
||||
name,
|
||||
fn_name,
|
||||
FnNamespace::Internal,
|
||||
FnAccess::Public,
|
||||
None,
|
||||
&F::param_types(),
|
||||
func.into_callable_function(),
|
||||
F::param_types(),
|
||||
func,
|
||||
)
|
||||
}
|
||||
|
||||
@ -1266,19 +1287,22 @@ impl Module {
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[inline(always)]
|
||||
pub fn set_getter_fn<A, T, F, S>(&mut self, name: impl AsRef<str>, func: F) -> u64
|
||||
pub fn set_getter_fn<A, const C: bool, T, F>(&mut self, name: impl AsRef<str>, func: F) -> u64
|
||||
where
|
||||
A: Variant + Clone,
|
||||
T: Variant + Clone,
|
||||
F: RegisterNativeFunction<(Mut<A>,), T, RhaiResultOf<S>> + SendSync + 'static,
|
||||
F: RegisterNativeFunction<(Mut<A>,), 1, C, T, true> + SendSync + 'static,
|
||||
{
|
||||
let fn_name = crate::engine::make_getter(name.as_ref());
|
||||
let func = func.into_callable_function(fn_name.clone(), false);
|
||||
|
||||
self.set_fn(
|
||||
crate::engine::make_getter(name.as_ref()).as_str(),
|
||||
fn_name,
|
||||
FnNamespace::Global,
|
||||
FnAccess::Public,
|
||||
None,
|
||||
&F::param_types(),
|
||||
func.into_callable_function(),
|
||||
F::param_types(),
|
||||
func,
|
||||
)
|
||||
}
|
||||
|
||||
@ -1307,19 +1331,22 @@ impl Module {
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[inline(always)]
|
||||
pub fn set_setter_fn<A, T, F, S>(&mut self, name: impl AsRef<str>, func: F) -> u64
|
||||
pub fn set_setter_fn<A, const C: bool, T, F>(&mut self, name: impl AsRef<str>, func: F) -> u64
|
||||
where
|
||||
A: Variant + Clone,
|
||||
T: Variant + Clone,
|
||||
F: RegisterNativeFunction<(Mut<A>, T), (), RhaiResultOf<S>> + SendSync + 'static,
|
||||
F: RegisterNativeFunction<(Mut<A>, T), 2, C, (), true> + SendSync + 'static,
|
||||
{
|
||||
let fn_name = crate::engine::make_setter(name.as_ref());
|
||||
let func = func.into_callable_function(fn_name.clone(), true);
|
||||
|
||||
self.set_fn(
|
||||
crate::engine::make_setter(name.as_ref()).as_str(),
|
||||
fn_name,
|
||||
FnNamespace::Global,
|
||||
FnAccess::Public,
|
||||
None,
|
||||
&F::param_types(),
|
||||
func.into_callable_function(),
|
||||
F::param_types(),
|
||||
func,
|
||||
)
|
||||
}
|
||||
|
||||
@ -1353,11 +1380,16 @@ impl Module {
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[inline(always)]
|
||||
pub fn set_getter_setter_fn<A: Variant + Clone, T: Variant + Clone, S1, S2>(
|
||||
pub fn set_getter_setter_fn<
|
||||
A: Variant + Clone,
|
||||
const C1: bool,
|
||||
const C2: bool,
|
||||
T: Variant + Clone,
|
||||
>(
|
||||
&mut self,
|
||||
name: impl AsRef<str>,
|
||||
getter: impl RegisterNativeFunction<(Mut<A>,), T, RhaiResultOf<S1>> + SendSync + 'static,
|
||||
setter: impl RegisterNativeFunction<(Mut<A>, T), (), RhaiResultOf<S2>> + SendSync + 'static,
|
||||
getter: impl RegisterNativeFunction<(Mut<A>,), 1, C1, T, true> + SendSync + 'static,
|
||||
setter: impl RegisterNativeFunction<(Mut<A>, T), 2, C2, (), true> + SendSync + 'static,
|
||||
) -> (u64, u64) {
|
||||
(
|
||||
self.set_getter_fn(name.as_ref(), getter),
|
||||
@ -1394,12 +1426,12 @@ impl Module {
|
||||
/// ```
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
#[inline]
|
||||
pub fn set_indexer_get_fn<A, B, T, F, S>(&mut self, func: F) -> u64
|
||||
pub fn set_indexer_get_fn<A, B, const C: bool, T, F>(&mut self, func: F) -> u64
|
||||
where
|
||||
A: Variant + Clone,
|
||||
B: Variant + Clone,
|
||||
T: Variant + Clone,
|
||||
F: RegisterNativeFunction<(Mut<A>, B), T, RhaiResultOf<S>> + SendSync + 'static,
|
||||
F: RegisterNativeFunction<(Mut<A>, B), 2, C, T, true> + SendSync + 'static,
|
||||
{
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
if TypeId::of::<A>() == TypeId::of::<crate::Array>() {
|
||||
@ -1421,8 +1453,8 @@ impl Module {
|
||||
FnNamespace::Global,
|
||||
FnAccess::Public,
|
||||
None,
|
||||
&F::param_types(),
|
||||
func.into_callable_function(),
|
||||
F::param_types(),
|
||||
func.into_callable_function(crate::engine::FN_IDX_GET.into(), false),
|
||||
)
|
||||
}
|
||||
|
||||
@ -1455,12 +1487,12 @@ impl Module {
|
||||
/// ```
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
#[inline]
|
||||
pub fn set_indexer_set_fn<A, B, T, F, S>(&mut self, func: F) -> u64
|
||||
pub fn set_indexer_set_fn<A, B, const C: bool, T, F>(&mut self, func: F) -> u64
|
||||
where
|
||||
A: Variant + Clone,
|
||||
B: Variant + Clone,
|
||||
T: Variant + Clone,
|
||||
F: RegisterNativeFunction<(Mut<A>, B, T), (), RhaiResultOf<S>> + SendSync + 'static,
|
||||
F: RegisterNativeFunction<(Mut<A>, B, T), 3, C, (), true> + SendSync + 'static,
|
||||
{
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
if TypeId::of::<A>() == TypeId::of::<crate::Array>() {
|
||||
@ -1482,8 +1514,8 @@ impl Module {
|
||||
FnNamespace::Global,
|
||||
FnAccess::Public,
|
||||
None,
|
||||
&F::param_types(),
|
||||
func.into_callable_function(),
|
||||
F::param_types(),
|
||||
func.into_callable_function(crate::engine::FN_IDX_SET.into(), true),
|
||||
)
|
||||
}
|
||||
|
||||
@ -1527,13 +1559,13 @@ impl Module {
|
||||
pub fn set_indexer_get_set_fn<
|
||||
A: Variant + Clone,
|
||||
B: Variant + Clone,
|
||||
const C1: bool,
|
||||
const C2: bool,
|
||||
T: Variant + Clone,
|
||||
S1,
|
||||
S2,
|
||||
>(
|
||||
&mut self,
|
||||
get_fn: impl RegisterNativeFunction<(Mut<A>, B), T, RhaiResultOf<S1>> + SendSync + 'static,
|
||||
set_fn: impl RegisterNativeFunction<(Mut<A>, B, T), (), RhaiResultOf<S2>> + SendSync + 'static,
|
||||
get_fn: impl RegisterNativeFunction<(Mut<A>, B), 2, C1, T, true> + SendSync + 'static,
|
||||
set_fn: impl RegisterNativeFunction<(Mut<A>, B, T), 3, C2, (), true> + SendSync + 'static,
|
||||
) -> (u64, u64) {
|
||||
(
|
||||
self.set_indexer_get_fn(get_fn),
|
||||
@ -1633,8 +1665,8 @@ impl Module {
|
||||
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
||||
|
||||
#[cfg(feature = "metadata")]
|
||||
if !other.doc.as_ref().map_or(true, |s| s.is_empty()) {
|
||||
if !self.doc.as_ref().map_or(true, |s| s.is_empty()) {
|
||||
if !other.doc.as_deref().map_or(true, SmartString::is_empty) {
|
||||
if !self.doc.as_deref().map_or(true, SmartString::is_empty) {
|
||||
self.doc.get_or_insert_with(Default::default).push('\n');
|
||||
}
|
||||
self.doc
|
||||
@ -1689,8 +1721,8 @@ impl Module {
|
||||
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
||||
|
||||
#[cfg(feature = "metadata")]
|
||||
if !other.doc.as_ref().map_or(true, |s| s.is_empty()) {
|
||||
if !self.doc.as_ref().map_or(true, |s| s.is_empty()) {
|
||||
if !other.doc.as_deref().map_or(true, SmartString::is_empty) {
|
||||
if !self.doc.as_deref().map_or(true, SmartString::is_empty) {
|
||||
self.doc.get_or_insert_with(Default::default).push('\n');
|
||||
}
|
||||
self.doc
|
||||
@ -1754,8 +1786,8 @@ impl Module {
|
||||
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
||||
|
||||
#[cfg(feature = "metadata")]
|
||||
if !other.doc.as_ref().map_or(true, |s| s.is_empty()) {
|
||||
if !self.doc.as_ref().map_or(true, |s| s.is_empty()) {
|
||||
if !other.doc.as_deref().map_or(true, SmartString::is_empty) {
|
||||
if !self.doc.as_deref().map_or(true, SmartString::is_empty) {
|
||||
self.doc.get_or_insert_with(Default::default).push('\n');
|
||||
}
|
||||
self.doc
|
||||
@ -1837,8 +1869,8 @@ impl Module {
|
||||
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
||||
|
||||
#[cfg(feature = "metadata")]
|
||||
if !other.doc.as_ref().map_or(true, |s| s.is_empty()) {
|
||||
if !self.doc.as_ref().map_or(true, |s| s.is_empty()) {
|
||||
if !other.doc.as_deref().map_or(true, SmartString::is_empty) {
|
||||
if !self.doc.as_deref().map_or(true, SmartString::is_empty) {
|
||||
self.doc.get_or_insert_with(Default::default).push('\n');
|
||||
}
|
||||
self.doc
|
||||
@ -1886,9 +1918,9 @@ impl Module {
|
||||
#[must_use]
|
||||
pub fn count(&self) -> (usize, usize, usize) {
|
||||
(
|
||||
self.variables.as_ref().map_or(0, |m| m.len()),
|
||||
self.functions.as_ref().map_or(0, |m| m.len()),
|
||||
self.type_iterators.as_ref().map_or(0, |t| t.len()),
|
||||
self.variables.as_deref().map_or(0, BTreeMap::len),
|
||||
self.functions.as_ref().map_or(0, StraightHashMap::len),
|
||||
self.type_iterators.as_deref().map_or(0, BTreeMap::len),
|
||||
)
|
||||
}
|
||||
|
||||
@ -1896,16 +1928,20 @@ impl Module {
|
||||
#[inline]
|
||||
pub fn iter_sub_modules(&self) -> impl Iterator<Item = (&str, &SharedModule)> {
|
||||
self.modules
|
||||
.iter()
|
||||
.flat_map(|m| m.iter().map(|(k, m)| (k.as_str(), m)))
|
||||
.as_deref()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(|(k, m)| (k.as_str(), m))
|
||||
}
|
||||
|
||||
/// Get an iterator to the variables in the [`Module`].
|
||||
#[inline]
|
||||
pub fn iter_var(&self) -> impl Iterator<Item = (&str, &Dynamic)> {
|
||||
self.variables
|
||||
.iter()
|
||||
.flat_map(|m| m.iter().map(|(k, v)| (k.as_str(), v)))
|
||||
.as_deref()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(|(k, v)| (k.as_str(), v))
|
||||
}
|
||||
|
||||
/// Get an iterator to the functions in the [`Module`].
|
||||
@ -2065,7 +2101,7 @@ impl Module {
|
||||
let mut module = Module::new();
|
||||
|
||||
// Extra modules left become sub-modules
|
||||
let mut imports = StaticVec::new_const();
|
||||
let mut imports = crate::StaticVec::new_const();
|
||||
|
||||
if result.is_ok() {
|
||||
global
|
||||
@ -2088,7 +2124,8 @@ impl Module {
|
||||
|
||||
global.source = orig_source;
|
||||
|
||||
result?;
|
||||
// The return value is thrown away and not used
|
||||
let _ = result?;
|
||||
|
||||
// Variables with an alias left in the scope become module variables
|
||||
for (_name, value, mut aliases) in scope {
|
||||
@ -2223,7 +2260,7 @@ impl Module {
|
||||
}
|
||||
|
||||
// Index all Rust functions
|
||||
for (&hash, f) in module.functions.iter().flat_map(|m| m.iter()) {
|
||||
for (&hash, f) in module.functions.iter().flatten() {
|
||||
match f.metadata.namespace {
|
||||
FnNamespace::Global => {
|
||||
// Flatten all functions with global namespace
|
||||
@ -2260,11 +2297,11 @@ impl Module {
|
||||
if !self.is_indexed() {
|
||||
let mut path = Vec::with_capacity(4);
|
||||
let mut variables = StraightHashMap::with_capacity_and_hasher(
|
||||
self.variables.as_ref().map_or(0, |m| m.len()),
|
||||
self.variables.as_deref().map_or(0, BTreeMap::len),
|
||||
Default::default(),
|
||||
);
|
||||
let mut functions = StraightHashMap::with_capacity_and_hasher(
|
||||
self.functions.as_ref().map_or(0, |m| m.len()),
|
||||
self.functions.as_ref().map_or(0, StraightHashMap::len),
|
||||
Default::default(),
|
||||
);
|
||||
let mut type_iterators = BTreeMap::new();
|
||||
|
@ -170,7 +170,7 @@ impl FileModuleResolver {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn base_path(&self) -> Option<&Path> {
|
||||
self.base_path.as_ref().map(<_>::as_ref)
|
||||
self.base_path.as_deref()
|
||||
}
|
||||
/// Set the base path for script files.
|
||||
#[inline(always)]
|
||||
|
146
src/optimizer.rs
146
src/optimizer.rs
@ -24,6 +24,9 @@ use std::{
|
||||
mem,
|
||||
};
|
||||
|
||||
/// Standard not operator.
|
||||
const OP_NOT: &str = Token::Bang.literal_syntax();
|
||||
|
||||
/// Level of optimization performed.
|
||||
#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)]
|
||||
#[non_exhaustive]
|
||||
@ -65,18 +68,19 @@ struct OptimizerState<'a> {
|
||||
}
|
||||
|
||||
impl<'a> OptimizerState<'a> {
|
||||
/// Create a new State.
|
||||
/// Create a new [`OptimizerState`].
|
||||
#[inline(always)]
|
||||
pub fn new(
|
||||
engine: &'a Engine,
|
||||
#[cfg(not(feature = "no_function"))] lib: &'a [crate::SharedModule],
|
||||
lib: &'a [crate::SharedModule],
|
||||
optimization_level: OptimizationLevel,
|
||||
) -> Self {
|
||||
let mut _global = GlobalRuntimeState::new(engine);
|
||||
let _lib = lib;
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
{
|
||||
_global.lib = lib.iter().cloned().collect();
|
||||
_global.lib = _lib.iter().cloned().collect();
|
||||
}
|
||||
|
||||
Self {
|
||||
@ -138,7 +142,7 @@ impl<'a> OptimizerState<'a> {
|
||||
pub fn call_fn_with_constant_arguments(
|
||||
&mut self,
|
||||
fn_name: &str,
|
||||
op_token: Option<&Token>,
|
||||
op_token: Token,
|
||||
arg_values: &mut [Dynamic],
|
||||
) -> Dynamic {
|
||||
self.engine
|
||||
@ -156,39 +160,6 @@ impl<'a> OptimizerState<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// Has a system function a Rust-native override?
|
||||
fn has_native_fn_override(
|
||||
engine: &Engine,
|
||||
hash_script: u64,
|
||||
arg_types: impl AsRef<[TypeId]>,
|
||||
) -> bool {
|
||||
let hash = calc_fn_hash_full(hash_script, arg_types.as_ref().iter().copied());
|
||||
|
||||
// First check the global namespace and packages, but skip modules that are standard because
|
||||
// they should never conflict with system functions.
|
||||
if engine
|
||||
.global_modules
|
||||
.iter()
|
||||
.filter(|m| !m.flags.contains(ModuleFlags::STANDARD_LIB))
|
||||
.any(|m| m.contains_fn(hash))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Then check sub-modules
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
if engine
|
||||
.global_sub_modules
|
||||
.iter()
|
||||
.flat_map(|m| m.values())
|
||||
.any(|m| m.contains_qualified_fn(hash))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Optimize a block of [statements][Stmt].
|
||||
fn optimize_stmt_block(
|
||||
mut statements: StmtBlockContainer,
|
||||
@ -1019,7 +990,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
||||
// ``
|
||||
Expr::InterpolatedString(x, pos) if x.is_empty() => {
|
||||
state.set_dirty();
|
||||
*expr = Expr::StringConstant(state.engine.get_interned_string(""), *pos);
|
||||
*expr = Expr::StringConstant(state.engine.const_empty_string(), *pos);
|
||||
}
|
||||
// `... ${const} ...`
|
||||
Expr::InterpolatedString(..) if expr.is_constant() => {
|
||||
@ -1097,6 +1068,20 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
||||
*expr = mem::take(&mut x.lhs);
|
||||
},
|
||||
|
||||
// !true or !false
|
||||
Expr::FnCall(x,..)
|
||||
if x.name == OP_NOT
|
||||
&& x.args.len() == 1
|
||||
&& matches!(x.args[0], Expr::BoolConstant(..))
|
||||
=> {
|
||||
state.set_dirty();
|
||||
if let Expr::BoolConstant(b, pos) = x.args[0] {
|
||||
*expr = Expr::BoolConstant(!b, pos)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
// eval!
|
||||
Expr::FnCall(x, ..) if x.name == KEYWORD_EVAL => {
|
||||
state.propagate_constants = false;
|
||||
@ -1150,10 +1135,14 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
||||
return;
|
||||
}
|
||||
// Overloaded operators can override built-in.
|
||||
_ if x.args.len() == 2 && x.op_token.is_some() && (state.engine.fast_operators() || !has_native_fn_override(state.engine, x.hashes.native(), &arg_types)) => {
|
||||
if let Some(result) = get_builtin_binary_op_fn(x.op_token.as_ref().unwrap(), &arg_values[0], &arg_values[1])
|
||||
.and_then(|f| {
|
||||
let context = (state.engine, x.name.as_str(),None, &state.global, *pos).into();
|
||||
_ if x.args.len() == 2 && x.op_token != Token::NONE && (state.engine.fast_operators() || !state.engine.has_native_fn_override(x.hashes.native(), &arg_types)) => {
|
||||
if let Some(result) = get_builtin_binary_op_fn(x.op_token.clone(), &arg_values[0], &arg_values[1])
|
||||
.and_then(|(f, ctx)| {
|
||||
let context = if ctx {
|
||||
Some((state.engine, x.name.as_str(),None, &state.global, *pos).into())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let (first, second) = arg_values.split_first_mut().unwrap();
|
||||
(f)(context, &mut [ first, &mut second[0] ]).ok()
|
||||
}) {
|
||||
@ -1204,7 +1193,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
||||
KEYWORD_TYPE_OF if arg_values.len() == 1 => state.engine.map_type_name(arg_values[0].type_name()).into(),
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
crate::engine::KEYWORD_IS_SHARED if arg_values.len() == 1 => Dynamic::FALSE,
|
||||
_ => state.call_fn_with_constant_arguments(&x.name, x.op_token.as_ref(), arg_values)
|
||||
_ => state.call_fn_with_constant_arguments(&x.name, x.op_token.clone(), arg_values)
|
||||
};
|
||||
|
||||
if !result.is_null() {
|
||||
@ -1260,14 +1249,45 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
||||
}
|
||||
}
|
||||
|
||||
impl Engine {
|
||||
/// Has a system function a Rust-native override?
|
||||
fn has_native_fn_override(&self, hash_script: u64, arg_types: impl AsRef<[TypeId]>) -> bool {
|
||||
let hash = calc_fn_hash_full(hash_script, arg_types.as_ref().iter().copied());
|
||||
|
||||
// First check the global namespace and packages, but skip modules that are standard because
|
||||
// they should never conflict with system functions.
|
||||
if self
|
||||
.global_modules
|
||||
.iter()
|
||||
.filter(|m| !m.flags.contains(ModuleFlags::STANDARD_LIB))
|
||||
.any(|m| m.contains_fn(hash))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Then check sub-modules
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
if self
|
||||
.global_sub_modules
|
||||
.as_deref()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.any(|(_, m)| m.contains_qualified_fn(hash))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Optimize a block of [statements][Stmt] at top level.
|
||||
///
|
||||
/// Constants and variables from the scope are added.
|
||||
fn optimize_top_level(
|
||||
&self,
|
||||
statements: StmtBlockContainer,
|
||||
engine: &Engine,
|
||||
scope: &Scope,
|
||||
#[cfg(not(feature = "no_function"))] lib: &[crate::SharedModule],
|
||||
lib: &[crate::SharedModule],
|
||||
optimization_level: OptimizationLevel,
|
||||
) -> StmtBlockContainer {
|
||||
let mut statements = statements;
|
||||
@ -1279,20 +1299,10 @@ fn optimize_top_level(
|
||||
}
|
||||
|
||||
// Set up the state
|
||||
let mut state = OptimizerState::new(
|
||||
engine,
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
lib,
|
||||
optimization_level,
|
||||
);
|
||||
let mut state = OptimizerState::new(self, lib, optimization_level);
|
||||
|
||||
// Add constants from global modules
|
||||
for (name, value) in engine
|
||||
.global_modules
|
||||
.iter()
|
||||
.rev()
|
||||
.flat_map(|m| m.iter_var())
|
||||
{
|
||||
for (name, value) in self.global_modules.iter().rev().flat_map(|m| m.iter_var()) {
|
||||
state.push_var(name, AccessMode::ReadOnly, value.clone());
|
||||
}
|
||||
|
||||
@ -1308,9 +1318,9 @@ fn optimize_top_level(
|
||||
optimize_stmt_block(statements, &mut state, true, false, true)
|
||||
}
|
||||
|
||||
/// Optimize an [`AST`].
|
||||
pub fn optimize_into_ast(
|
||||
engine: &Engine,
|
||||
/// Optimize a collection of statements and functions into an [`AST`].
|
||||
pub(crate) fn optimize_into_ast(
|
||||
&self,
|
||||
scope: &Scope,
|
||||
statements: StmtBlockContainer,
|
||||
#[cfg(not(feature = "no_function"))] functions: StaticVec<
|
||||
@ -1350,7 +1360,7 @@ pub fn optimize_into_ast(
|
||||
// Optimize the function body
|
||||
let body = mem::take(&mut *fn_def.body);
|
||||
|
||||
*fn_def.body = optimize_top_level(body, engine, scope, lib2, optimization_level);
|
||||
*fn_def.body = self.optimize_top_level(body, scope, lib2, optimization_level);
|
||||
|
||||
module.set_script_fn(fn_def);
|
||||
}
|
||||
@ -1362,22 +1372,20 @@ pub fn optimize_into_ast(
|
||||
|
||||
module.into()
|
||||
};
|
||||
#[cfg(feature = "no_function")]
|
||||
let lib: crate::Shared<_> = crate::Module::new().into();
|
||||
|
||||
statements.shrink_to_fit();
|
||||
|
||||
AST::new(
|
||||
match optimization_level {
|
||||
OptimizationLevel::None => statements,
|
||||
OptimizationLevel::Simple | OptimizationLevel::Full => optimize_top_level(
|
||||
statements,
|
||||
engine,
|
||||
scope,
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
&[lib.clone()],
|
||||
optimization_level,
|
||||
),
|
||||
OptimizationLevel::Simple | OptimizationLevel::Full => {
|
||||
self.optimize_top_level(statements, scope, &[lib.clone()], optimization_level)
|
||||
}
|
||||
},
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
lib,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -261,8 +261,7 @@ pub mod array_functions {
|
||||
m1 += m2;
|
||||
s1 += s2;
|
||||
|
||||
_ctx.engine()
|
||||
.raise_err_if_over_data_size_limit((a1, m1, s1))?;
|
||||
_ctx.engine().throw_on_size((a1, m1, s1))?;
|
||||
|
||||
guard.push(item.clone());
|
||||
arr_len += 1;
|
||||
|
@ -81,8 +81,7 @@ pub mod blob_functions {
|
||||
|
||||
// Check if blob will be over max size limit
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
_ctx.engine()
|
||||
.raise_err_if_over_data_size_limit((len, 0, 0))?;
|
||||
_ctx.engine().throw_on_size((len, 0, 0))?;
|
||||
|
||||
let mut blob = Blob::new();
|
||||
blob.resize(len, (value & 0x0000_00ff) as u8);
|
||||
|
@ -235,18 +235,18 @@ macro_rules! reg_range {
|
||||
concat!("from: ", stringify!($y)),
|
||||
concat!("to: ", stringify!($y)),
|
||||
concat!("Iterator<", stringify!($y), ">"),
|
||||
], [
|
||||
"/// Return an iterator over the exclusive range of `from..to`.",
|
||||
"/// The value `to` is never included.",
|
||||
"///",
|
||||
"/// # Example",
|
||||
"///",
|
||||
"/// ```rhai",
|
||||
"/// // prints all values from 8 to 17",
|
||||
"/// for n in range(8, 18) {",
|
||||
"/// print(n);",
|
||||
"/// }",
|
||||
"/// ```"
|
||||
], ["\
|
||||
/// Return an iterator over the exclusive range of `from..to`.\n\
|
||||
/// The value `to` is never included.\n\
|
||||
///\n\
|
||||
/// # Example\n\
|
||||
///\n\
|
||||
/// ```rhai\n\
|
||||
/// // prints all values from 8 to 17\n\
|
||||
/// for n in range(8, 18) {\n\
|
||||
/// print(n);\n\
|
||||
/// }\n\
|
||||
/// ```"
|
||||
]);
|
||||
|
||||
$lib.set_iterator::<RangeInclusive<$y>>();
|
||||
@ -269,27 +269,27 @@ macro_rules! reg_range {
|
||||
concat!("to: ", stringify!($y)),
|
||||
concat!("step: ", stringify!($y)),
|
||||
concat!("Iterator<", stringify!($y), ">")
|
||||
], [
|
||||
"/// Return an iterator over the exclusive range of `from..to`, each iteration increasing by `step`.",
|
||||
"/// The value `to` is never included.",
|
||||
"///",
|
||||
"/// If `from` > `to` and `step` < 0, iteration goes backwards.",
|
||||
"///",
|
||||
"/// If `from` > `to` and `step` > 0 or `from` < `to` and `step` < 0, an empty iterator is returned.",
|
||||
"///",
|
||||
"/// # Example",
|
||||
"///",
|
||||
"/// ```rhai",
|
||||
"/// // prints all values from 8 to 17 in steps of 3",
|
||||
"/// for n in range(8, 18, 3) {",
|
||||
"/// print(n);",
|
||||
"/// }",
|
||||
"///",
|
||||
"/// // prints all values down from 18 to 9 in steps of -3",
|
||||
"/// for n in range(18, 8, -3) {",
|
||||
"/// print(n);",
|
||||
"/// }",
|
||||
"/// ```"
|
||||
], ["\
|
||||
/// Return an iterator over the exclusive range of `from..to`, each iteration increasing by `step`.\n\
|
||||
/// The value `to` is never included.\n\
|
||||
///\n\
|
||||
/// If `from` > `to` and `step` < 0, iteration goes backwards.\n\
|
||||
///\n\
|
||||
/// If `from` > `to` and `step` > 0 or `from` < `to` and `step` < 0, an empty iterator is returned.\n\
|
||||
///\n\
|
||||
/// # Example\n\
|
||||
///\n\
|
||||
/// ```rhai\n\
|
||||
/// // prints all values from 8 to 17 in steps of 3\n\
|
||||
/// for n in range(8, 18, 3) {\n\
|
||||
/// print(n);\n\
|
||||
/// }\n\
|
||||
///\n\
|
||||
/// // prints all values down from 18 to 9 in steps of -3\n\
|
||||
/// for n in range(18, 8, -3) {\n\
|
||||
/// print(n);\n\
|
||||
/// }\n\
|
||||
/// ```"
|
||||
]);
|
||||
|
||||
let _hash = $lib.set_native_fn($x, |range: std::ops::Range<$y>, step: $y| StepRange::new(range.start, range.end, step, $add));
|
||||
@ -299,26 +299,26 @@ macro_rules! reg_range {
|
||||
concat!("range: Range<", stringify!($y), ">"),
|
||||
concat!("step: ", stringify!($y)),
|
||||
concat!("Iterator<", stringify!($y), ">")
|
||||
], [
|
||||
"/// Return an iterator over an exclusive range, each iteration increasing by `step`.",
|
||||
"///",
|
||||
"/// If `range` is reversed and `step` < 0, iteration goes backwards.",
|
||||
"///",
|
||||
"/// Otherwise, if `range` is empty, an empty iterator is returned.",
|
||||
"///",
|
||||
"/// # Example",
|
||||
"///",
|
||||
"/// ```rhai",
|
||||
"/// // prints all values from 8 to 17 in steps of 3",
|
||||
"/// for n in range(8..18, 3) {",
|
||||
"/// print(n);",
|
||||
"/// }",
|
||||
"///",
|
||||
"/// // prints all values down from 18 to 9 in steps of -3",
|
||||
"/// for n in range(18..8, -3) {",
|
||||
"/// print(n);",
|
||||
"/// }",
|
||||
"/// ```"
|
||||
], ["\
|
||||
/// Return an iterator over an exclusive range, each iteration increasing by `step`.\n\
|
||||
///\n\
|
||||
/// If `range` is reversed and `step` < 0, iteration goes backwards.\n\
|
||||
///\n\
|
||||
/// Otherwise, if `range` is empty, an empty iterator is returned.\n\
|
||||
///\n\
|
||||
/// # Example\n\
|
||||
///\n\
|
||||
/// ```rhai\n\
|
||||
/// // prints all values from 8 to 17 in steps of 3\n\
|
||||
/// for n in range(8..18, 3) {\n\
|
||||
/// print(n);\n\
|
||||
/// }\n\
|
||||
///\n\
|
||||
/// // prints all values down from 18 to 9 in steps of -3\n\
|
||||
/// for n in range(18..8, -3) {\n\
|
||||
/// print(n);\n\
|
||||
/// }\n\
|
||||
/// ```"
|
||||
]);
|
||||
)*
|
||||
};
|
||||
@ -665,6 +665,7 @@ mod range_functions {
|
||||
}
|
||||
/// Return true if the range contains no items.
|
||||
#[rhai_fn(get = "is_empty", name = "is_empty", pure)]
|
||||
#[allow(unstable_name_collisions)]
|
||||
pub fn is_empty_exclusive(range: &mut ExclusiveRange) -> bool {
|
||||
range.is_empty()
|
||||
}
|
||||
|
@ -220,7 +220,7 @@ fn collect_fn_metadata(
|
||||
"comments".into(),
|
||||
func.comments
|
||||
.iter()
|
||||
.map(|s| engine.get_interned_string(s.as_ref()).into())
|
||||
.map(|s| engine.get_interned_string(s.as_str()).into())
|
||||
.collect::<Array>()
|
||||
.into(),
|
||||
);
|
||||
@ -267,9 +267,10 @@ fn collect_fn_metadata(
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
ctx.engine()
|
||||
.global_sub_modules
|
||||
.iter()
|
||||
.flat_map(|m| m.values())
|
||||
.flat_map(|m| m.iter_script_fn())
|
||||
.as_deref()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.flat_map(|(_, m)| m.iter_script_fn())
|
||||
.filter(|(ns, a, n, p, f)| filter(*ns, *a, n, *p, f))
|
||||
.for_each(|(.., f)| {
|
||||
list.push(
|
||||
|
509
src/parser.rs
509
src/parser.rs
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,6 @@
|
||||
/// * `reify!(`_variable_ or _expression_`,|`_temp-variable_`: `_type_`|` _code_ `)`
|
||||
/// * `reify!(`_variable_ or _expression_ `=>` `Option<`_type_`>` `)`
|
||||
/// * `reify!(`_variable_ or _expression_ `=>` _type_ `)`
|
||||
#[macro_export]
|
||||
macro_rules! reify {
|
||||
($old:ident, |$new:ident : $t:ty| $code:expr, || $fallback:expr) => {{
|
||||
#[allow(clippy::redundant_else)]
|
||||
|
@ -168,10 +168,12 @@ pub fn gen_metadata_to_json(
|
||||
include_standard_packages: bool,
|
||||
) -> serde_json::Result<String> {
|
||||
let _ast = ast;
|
||||
#[cfg(feature = "metadata")]
|
||||
let mut global_doc = String::new();
|
||||
let mut global = ModuleMetadata::new();
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
for (name, m) in engine.global_sub_modules.iter().flat_map(|m| m.iter()) {
|
||||
for (name, m) in engine.global_sub_modules.as_deref().into_iter().flatten() {
|
||||
global.modules.insert(name, m.as_ref().into());
|
||||
}
|
||||
|
||||
@ -185,7 +187,16 @@ pub fn gen_metadata_to_json(
|
||||
.global_modules
|
||||
.iter()
|
||||
.filter(|m| !m.flags.contains(exclude_flags))
|
||||
.flat_map(|m| m.iter_fn())
|
||||
.flat_map(|m| {
|
||||
#[cfg(feature = "metadata")]
|
||||
if !m.doc().is_empty() {
|
||||
if !global_doc.is_empty() {
|
||||
global_doc.push('\n');
|
||||
}
|
||||
global_doc.push_str(m.doc());
|
||||
}
|
||||
m.iter_fn()
|
||||
})
|
||||
.for_each(|f| {
|
||||
#[allow(unused_mut)]
|
||||
let mut meta: FnMetadata = f.into();
|
||||
@ -213,7 +224,17 @@ pub fn gen_metadata_to_json(
|
||||
|
||||
#[cfg(feature = "metadata")]
|
||||
if let Some(ast) = _ast {
|
||||
global.doc = ast.doc();
|
||||
if !ast.doc().is_empty() {
|
||||
if !global_doc.is_empty() {
|
||||
global_doc.push('\n');
|
||||
}
|
||||
global_doc.push_str(ast.doc());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "metadata")]
|
||||
{
|
||||
global.doc = &global_doc;
|
||||
}
|
||||
|
||||
serde_json::to_string_pretty(&global)
|
||||
|
550
src/tokenizer.rs
550
src/tokenizer.rs
@ -5,7 +5,7 @@ use crate::engine::{
|
||||
KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_THIS, KEYWORD_TYPE_OF,
|
||||
};
|
||||
use crate::func::native::OnParseTokenCallback;
|
||||
use crate::{Engine, Identifier, LexError, SmartString, StaticVec, INT, UNSIGNED_INT};
|
||||
use crate::{Engine, Identifier, LexError, Position, SmartString, StaticVec, INT, UNSIGNED_INT};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{
|
||||
@ -13,7 +13,6 @@ use std::{
|
||||
char, fmt,
|
||||
iter::{FusedIterator, Peekable},
|
||||
num::NonZeroUsize,
|
||||
ops::{Add, AddAssign},
|
||||
rc::Rc,
|
||||
str::{Chars, FromStr},
|
||||
};
|
||||
@ -24,9 +23,9 @@ pub struct TokenizerControlBlock {
|
||||
/// Is the current tokenizer position within an interpolated text string?
|
||||
/// This flag allows switching the tokenizer back to _text_ parsing after an interpolation stream.
|
||||
pub is_within_text: bool,
|
||||
/// Collection of global comments.
|
||||
/// Global comments.
|
||||
#[cfg(feature = "metadata")]
|
||||
pub global_comments: Vec<SmartString>,
|
||||
pub global_comments: String,
|
||||
}
|
||||
|
||||
impl TokenizerControlBlock {
|
||||
@ -37,7 +36,7 @@ impl TokenizerControlBlock {
|
||||
Self {
|
||||
is_within_text: false,
|
||||
#[cfg(feature = "metadata")]
|
||||
global_comments: Vec::new(),
|
||||
global_comments: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -50,326 +49,12 @@ type LERR = LexError;
|
||||
/// Separator character for numbers.
|
||||
const NUMBER_SEPARATOR: char = '_';
|
||||
|
||||
/// No token.
|
||||
pub const NO_TOKEN: Token = Token::NONE;
|
||||
|
||||
/// A stream of tokens.
|
||||
pub type TokenStream<'a> = Peekable<TokenIterator<'a>>;
|
||||
|
||||
/// A location (line number + character position) in the input script.
|
||||
///
|
||||
/// # Limitations
|
||||
///
|
||||
/// In order to keep footprint small, both line number and character position have 16-bit resolution,
|
||||
/// meaning they go up to a maximum of 65,535 lines and 65,535 characters per line.
|
||||
///
|
||||
/// Advancing beyond the maximum line length or maximum number of lines is not an error but has no effect.
|
||||
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
|
||||
pub struct Position {
|
||||
/// Line number: 0 = none
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
line: u16,
|
||||
/// Character position: 0 = BOL
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
pos: u16,
|
||||
}
|
||||
|
||||
impl Position {
|
||||
/// A [`Position`] representing no position.
|
||||
pub const NONE: Self = Self {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
line: 0,
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
pos: 0,
|
||||
};
|
||||
/// A [`Position`] representing the first position.
|
||||
pub const START: Self = Self {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
line: 1,
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
pos: 0,
|
||||
};
|
||||
|
||||
/// Create a new [`Position`].
|
||||
///
|
||||
/// `line` must not be zero.
|
||||
///
|
||||
/// If `position` is zero, then it is at the beginning of a line.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `line` is zero.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn new(line: u16, position: u16) -> Self {
|
||||
assert!(line != 0, "line cannot be zero");
|
||||
|
||||
let _pos = position;
|
||||
|
||||
Self {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
line,
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
pos: _pos,
|
||||
}
|
||||
}
|
||||
/// Get the line number (1-based), or [`None`] if there is no position.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn line(self) -> Option<usize> {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
return if self.is_none() {
|
||||
None
|
||||
} else {
|
||||
Some(self.line as usize)
|
||||
};
|
||||
|
||||
#[cfg(feature = "no_position")]
|
||||
return None;
|
||||
}
|
||||
/// Get the character position (1-based), or [`None`] if at beginning of a line.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn position(self) -> Option<usize> {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
return if self.is_none() || self.pos == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(self.pos as usize)
|
||||
};
|
||||
|
||||
#[cfg(feature = "no_position")]
|
||||
return None;
|
||||
}
|
||||
/// Advance by one character position.
|
||||
#[inline]
|
||||
pub(crate) fn advance(&mut self) {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
{
|
||||
assert!(!self.is_none(), "cannot advance Position::none");
|
||||
|
||||
// Advance up to maximum position
|
||||
if self.pos < u16::MAX {
|
||||
self.pos += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Go backwards by one character position.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if already at beginning of a line - cannot rewind to a previous line.
|
||||
#[inline]
|
||||
pub(crate) fn rewind(&mut self) {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
{
|
||||
assert!(!self.is_none(), "cannot rewind Position::none");
|
||||
assert!(self.pos > 0, "cannot rewind at position 0");
|
||||
self.pos -= 1;
|
||||
}
|
||||
}
|
||||
/// Advance to the next line.
|
||||
#[inline]
|
||||
pub(crate) fn new_line(&mut self) {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
{
|
||||
assert!(!self.is_none(), "cannot advance Position::none");
|
||||
|
||||
// Advance up to maximum position
|
||||
if self.line < u16::MAX {
|
||||
self.line += 1;
|
||||
self.pos = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Is this [`Position`] at the beginning of a line?
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_beginning_of_line(self) -> bool {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
return self.pos == 0 && !self.is_none();
|
||||
#[cfg(feature = "no_position")]
|
||||
return false;
|
||||
}
|
||||
/// Is there no [`Position`]?
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_none(self) -> bool {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
return self.line == 0 && self.pos == 0;
|
||||
#[cfg(feature = "no_position")]
|
||||
return true;
|
||||
}
|
||||
/// Returns an fallback [`Position`] if it is [`NONE`][Position::NONE]?
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn or_else(self, pos: Self) -> Self {
|
||||
if self.is_none() {
|
||||
pos
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
/// Print this [`Position`] for debug purposes.
|
||||
#[inline]
|
||||
pub(crate) fn debug_print(self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if !self.is_none() {
|
||||
write!(_f, " @ {:?}", self)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Position {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self::START
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Position {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.is_none() {
|
||||
write!(f, "none")?;
|
||||
} else {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
write!(f, "line {}, position {}", self.line, self.pos)?;
|
||||
#[cfg(feature = "no_position")]
|
||||
unreachable!("no position");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Position {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.is_none() {
|
||||
f.write_str("none")
|
||||
} else {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
if self.is_beginning_of_line() {
|
||||
write!(f, "{}", self.line)
|
||||
} else {
|
||||
write!(f, "{}:{}", self.line, self.pos)
|
||||
}
|
||||
|
||||
#[cfg(feature = "no_position")]
|
||||
unreachable!("no position");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Position {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
if rhs.is_none() {
|
||||
self
|
||||
} else {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
return Self {
|
||||
line: self.line + rhs.line - 1,
|
||||
pos: if rhs.is_beginning_of_line() {
|
||||
self.pos
|
||||
} else {
|
||||
self.pos + rhs.pos - 1
|
||||
},
|
||||
};
|
||||
#[cfg(feature = "no_position")]
|
||||
unreachable!("no position");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for Position {
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
*self = *self + rhs;
|
||||
}
|
||||
}
|
||||
|
||||
/// _(internals)_ A span consisting of a starting and an ending [positions][Position].
|
||||
/// Exported under the `internals` feature only.
|
||||
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
|
||||
pub struct Span {
|
||||
/// Starting [position][Position].
|
||||
start: Position,
|
||||
/// Ending [position][Position].
|
||||
end: Position,
|
||||
}
|
||||
|
||||
impl Default for Span {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self::NONE
|
||||
}
|
||||
}
|
||||
|
||||
impl Span {
|
||||
/// Empty [`Span`].
|
||||
pub const NONE: Self = Self::new(Position::NONE, Position::NONE);
|
||||
|
||||
/// Create a new [`Span`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn new(start: Position, end: Position) -> Self {
|
||||
Self { start, end }
|
||||
}
|
||||
/// Is this [`Span`] non-existent?
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_none(&self) -> bool {
|
||||
self.start.is_none() && self.end.is_none()
|
||||
}
|
||||
/// Get the [`Span`]'s starting [position][Position].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn start(&self) -> Position {
|
||||
self.start
|
||||
}
|
||||
/// Get the [`Span`]'s ending [position][Position].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn end(&self) -> Position {
|
||||
self.end
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Span {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let _f = f;
|
||||
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
match (self.start(), self.end()) {
|
||||
(Position::NONE, Position::NONE) => write!(_f, "{:?}", Position::NONE),
|
||||
(Position::NONE, end) => write!(_f, "..{:?}", end),
|
||||
(start, Position::NONE) => write!(_f, "{:?}", start),
|
||||
(start, end) if start.line() != end.line() => {
|
||||
write!(_f, "{:?}-{:?}", start, end)
|
||||
}
|
||||
(start, end) => write!(
|
||||
_f,
|
||||
"{}:{}-{}",
|
||||
start.line().unwrap(),
|
||||
start.position().unwrap_or(0),
|
||||
end.position().unwrap_or(0)
|
||||
),
|
||||
}
|
||||
|
||||
#[cfg(feature = "no_position")]
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Span {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
/// _(internals)_ A Rhai language token.
|
||||
/// Exported under the `internals` feature only.
|
||||
#[derive(Debug, PartialEq, Clone, Hash)]
|
||||
@ -489,6 +174,8 @@ pub enum Token {
|
||||
For,
|
||||
/// `in`
|
||||
In,
|
||||
/// `!in`
|
||||
NotIn,
|
||||
/// `<`
|
||||
LessThan,
|
||||
/// `>`
|
||||
@ -575,7 +262,7 @@ pub enum Token {
|
||||
/// A lexer error.
|
||||
LexError(Box<LexError>),
|
||||
/// A comment block.
|
||||
Comment(Box<SmartString>),
|
||||
Comment(Box<String>),
|
||||
/// A reserved symbol.
|
||||
Reserved(Box<SmartString>),
|
||||
/// A custom keyword.
|
||||
@ -584,7 +271,10 @@ pub enum Token {
|
||||
#[cfg(not(feature = "no_custom_syntax"))]
|
||||
Custom(Box<SmartString>),
|
||||
/// End of the input stream.
|
||||
/// Used as a placeholder for the end of input.
|
||||
EOF,
|
||||
/// Placeholder to indicate the lack of a token.
|
||||
NONE,
|
||||
}
|
||||
|
||||
impl fmt::Display for Token {
|
||||
@ -610,6 +300,7 @@ impl fmt::Display for Token {
|
||||
Comment(s) => f.write_str(s),
|
||||
|
||||
EOF => f.write_str("{EOF}"),
|
||||
NONE => f.write_str("{NONE}"),
|
||||
|
||||
token => f.write_str(token.literal_syntax()),
|
||||
}
|
||||
@ -638,7 +329,7 @@ impl Token {
|
||||
Custom(..) => false,
|
||||
LexError(..) | Comment(..) => false,
|
||||
|
||||
EOF => false,
|
||||
EOF | NONE => false,
|
||||
|
||||
_ => true,
|
||||
}
|
||||
@ -696,6 +387,7 @@ impl Token {
|
||||
Loop => "loop",
|
||||
For => "for",
|
||||
In => "in",
|
||||
NotIn => "!in",
|
||||
LessThan => "<",
|
||||
GreaterThan => ">",
|
||||
Bang => "!",
|
||||
@ -750,37 +442,43 @@ impl Token {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_op_assignment(&self) -> bool {
|
||||
#[allow(clippy::enum_glob_use)]
|
||||
use Token::*;
|
||||
|
||||
matches!(
|
||||
self,
|
||||
Self::PlusAssign
|
||||
| Self::MinusAssign
|
||||
| Self::MultiplyAssign
|
||||
| Self::DivideAssign
|
||||
| Self::LeftShiftAssign
|
||||
| Self::RightShiftAssign
|
||||
| Self::ModuloAssign
|
||||
| Self::PowerOfAssign
|
||||
| Self::AndAssign
|
||||
| Self::OrAssign
|
||||
| Self::XOrAssign
|
||||
PlusAssign
|
||||
| MinusAssign
|
||||
| MultiplyAssign
|
||||
| DivideAssign
|
||||
| LeftShiftAssign
|
||||
| RightShiftAssign
|
||||
| ModuloAssign
|
||||
| PowerOfAssign
|
||||
| AndAssign
|
||||
| OrAssign
|
||||
| XOrAssign
|
||||
)
|
||||
}
|
||||
|
||||
/// Get the corresponding operator of the token if it is an op-assignment operator.
|
||||
#[must_use]
|
||||
pub const fn get_base_op_from_assignment(&self) -> Option<Self> {
|
||||
#[allow(clippy::enum_glob_use)]
|
||||
use Token::*;
|
||||
|
||||
Some(match self {
|
||||
Self::PlusAssign => Self::Plus,
|
||||
Self::MinusAssign => Self::Minus,
|
||||
Self::MultiplyAssign => Self::Multiply,
|
||||
Self::DivideAssign => Self::Divide,
|
||||
Self::LeftShiftAssign => Self::LeftShift,
|
||||
Self::RightShiftAssign => Self::RightShift,
|
||||
Self::ModuloAssign => Self::Modulo,
|
||||
Self::PowerOfAssign => Self::PowerOf,
|
||||
Self::AndAssign => Self::Ampersand,
|
||||
Self::OrAssign => Self::Pipe,
|
||||
Self::XOrAssign => Self::XOr,
|
||||
PlusAssign => Plus,
|
||||
MinusAssign => Minus,
|
||||
MultiplyAssign => Multiply,
|
||||
DivideAssign => Divide,
|
||||
LeftShiftAssign => LeftShift,
|
||||
RightShiftAssign => RightShift,
|
||||
ModuloAssign => Modulo,
|
||||
PowerOfAssign => PowerOf,
|
||||
AndAssign => Ampersand,
|
||||
OrAssign => Pipe,
|
||||
XOrAssign => XOr,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
@ -789,37 +487,42 @@ impl Token {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn has_op_assignment(&self) -> bool {
|
||||
#[allow(clippy::enum_glob_use)]
|
||||
use Token::*;
|
||||
|
||||
matches!(
|
||||
self,
|
||||
Self::Plus
|
||||
| Self::Minus
|
||||
| Self::Multiply
|
||||
| Self::Divide
|
||||
| Self::LeftShift
|
||||
| Self::RightShift
|
||||
| Self::Modulo
|
||||
| Self::PowerOf
|
||||
| Self::Ampersand
|
||||
| Self::Pipe
|
||||
| Self::XOr
|
||||
Plus | Minus
|
||||
| Multiply
|
||||
| Divide
|
||||
| LeftShift
|
||||
| RightShift
|
||||
| Modulo
|
||||
| PowerOf
|
||||
| Ampersand
|
||||
| Pipe
|
||||
| XOr
|
||||
)
|
||||
}
|
||||
|
||||
/// Get the corresponding op-assignment operator of the token.
|
||||
#[must_use]
|
||||
pub const fn convert_to_op_assignment(&self) -> Option<Self> {
|
||||
#[allow(clippy::enum_glob_use)]
|
||||
use Token::*;
|
||||
|
||||
Some(match self {
|
||||
Self::Plus => Self::PlusAssign,
|
||||
Self::Minus => Self::MinusAssign,
|
||||
Self::Multiply => Self::MultiplyAssign,
|
||||
Self::Divide => Self::DivideAssign,
|
||||
Self::LeftShift => Self::LeftShiftAssign,
|
||||
Self::RightShift => Self::RightShiftAssign,
|
||||
Self::Modulo => Self::ModuloAssign,
|
||||
Self::PowerOf => Self::PowerOfAssign,
|
||||
Self::Ampersand => Self::AndAssign,
|
||||
Self::Pipe => Self::OrAssign,
|
||||
Self::XOr => Self::XOrAssign,
|
||||
Plus => PlusAssign,
|
||||
Minus => MinusAssign,
|
||||
Multiply => MultiplyAssign,
|
||||
Divide => DivideAssign,
|
||||
LeftShift => LeftShiftAssign,
|
||||
RightShift => RightShiftAssign,
|
||||
Modulo => ModuloAssign,
|
||||
PowerOf => PowerOfAssign,
|
||||
Ampersand => AndAssign,
|
||||
Pipe => OrAssign,
|
||||
XOr => XOrAssign,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
@ -871,6 +574,7 @@ impl Token {
|
||||
"loop" => Loop,
|
||||
"for" => For,
|
||||
"in" => In,
|
||||
"!in" => NotIn,
|
||||
"<" => LessThan,
|
||||
">" => GreaterThan,
|
||||
"!" => Bang,
|
||||
@ -956,13 +660,6 @@ impl Token {
|
||||
}
|
||||
}
|
||||
|
||||
/// Is this token [`EOF`][Token::EOF]?
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn is_eof(&self) -> bool {
|
||||
matches!(self, Self::EOF)
|
||||
}
|
||||
|
||||
/// If another operator is after these, it's probably a unary operator
|
||||
/// (not sure about `fn` name).
|
||||
#[must_use]
|
||||
@ -1018,6 +715,7 @@ impl Token {
|
||||
While |
|
||||
Until |
|
||||
In |
|
||||
NotIn |
|
||||
And |
|
||||
AndAssign |
|
||||
Or |
|
||||
@ -1049,7 +747,7 @@ impl Token {
|
||||
|
||||
EqualsTo | NotEqualsTo => 90,
|
||||
|
||||
In => 110,
|
||||
In | NotIn => 110,
|
||||
|
||||
LessThan | LessThanEqualsTo | GreaterThan | GreaterThanEqualsTo => 130,
|
||||
|
||||
@ -1170,7 +868,7 @@ impl From<Token> for String {
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Default)]
|
||||
pub struct TokenizeState {
|
||||
/// Maximum length of a string.
|
||||
pub max_string_size: Option<NonZeroUsize>,
|
||||
pub max_string_len: Option<NonZeroUsize>,
|
||||
/// Can the next token be a unary operator?
|
||||
pub next_token_cannot_be_unary: bool,
|
||||
/// Shared object to allow controlling the tokenizer externally.
|
||||
@ -1197,6 +895,18 @@ pub trait InputStream {
|
||||
fn peek_next(&mut self) -> Option<char>;
|
||||
}
|
||||
|
||||
/// Return error if the string is longer than the maximum length.
|
||||
#[inline]
|
||||
fn ensure_string_len_within_limit(max: Option<NonZeroUsize>, value: &str) -> Result<(), LexError> {
|
||||
if let Some(max) = max {
|
||||
if value.len() > max.get() {
|
||||
return Err(LexError::StringTooLong(max.get()));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// _(internals)_ Parse a string literal ended by a specified termination character.
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
@ -1286,11 +996,8 @@ pub fn parse_string_literal(
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(max) = state.max_string_size {
|
||||
if result.len() > max.get() {
|
||||
return Err((LexError::StringTooLong(max.get()), *pos));
|
||||
}
|
||||
}
|
||||
ensure_string_len_within_limit(state.max_string_len, &result)
|
||||
.map_err(|err| (err, start))?;
|
||||
|
||||
// Close wrapper
|
||||
if termination_char == next_char && escape.is_empty() {
|
||||
@ -1425,11 +1132,7 @@ pub fn parse_string_literal(
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(max) = state.max_string_size {
|
||||
if result.len() > max.get() {
|
||||
return Err((LexError::StringTooLong(max.get()), *pos));
|
||||
}
|
||||
}
|
||||
ensure_string_len_within_limit(state.max_string_len, &result).map_err(|err| (err, start))?;
|
||||
|
||||
Ok((result, interpolated, first_char))
|
||||
}
|
||||
@ -1446,7 +1149,7 @@ fn scan_block_comment(
|
||||
stream: &mut impl InputStream,
|
||||
level: usize,
|
||||
pos: &mut Position,
|
||||
comment: Option<&mut SmartString>,
|
||||
comment: Option<&mut String>,
|
||||
) -> usize {
|
||||
let mut level = level;
|
||||
let mut comment = comment;
|
||||
@ -1541,7 +1244,7 @@ fn get_next_token_inner(
|
||||
if state.comment_level > 0 {
|
||||
let start_pos = *pos;
|
||||
let mut comment = if state.include_comments {
|
||||
Some(SmartString::new_const())
|
||||
Some(String::new())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@ -1748,11 +1451,17 @@ fn get_next_token_inner(
|
||||
// letter or underscore ...
|
||||
#[cfg(not(feature = "unicode-xid-ident"))]
|
||||
('a'..='z' | '_' | 'A'..='Z', ..) => {
|
||||
return Some(get_token_as_identifier(stream, pos, start_pos, c));
|
||||
return Some(
|
||||
parse_identifier_token(stream, pos, start_pos, c)
|
||||
.unwrap_or_else(|err| (Token::LexError(err.into()), start_pos)),
|
||||
);
|
||||
}
|
||||
#[cfg(feature = "unicode-xid-ident")]
|
||||
(ch, ..) if unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_' => {
|
||||
return Some(get_token_as_identifier(stream, pos, start_pos, c));
|
||||
return Some(
|
||||
parse_identifier_token(stream, pos, start_pos, c)
|
||||
.unwrap_or_else(|err| (Token::LexError(err.into()), start_pos)),
|
||||
);
|
||||
}
|
||||
|
||||
// " - string literal
|
||||
@ -1928,7 +1637,7 @@ fn get_next_token_inner(
|
||||
('/', '/') => {
|
||||
eat_next(stream, pos);
|
||||
|
||||
let mut comment: Option<SmartString> = match stream.peek_next() {
|
||||
let mut comment: Option<String> = match stream.peek_next() {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[cfg(feature = "metadata")]
|
||||
Some('/') => {
|
||||
@ -1971,11 +1680,13 @@ fn get_next_token_inner(
|
||||
if let Some(comment) = comment {
|
||||
match comment {
|
||||
#[cfg(feature = "metadata")]
|
||||
_ if comment.starts_with("//!") => state
|
||||
.tokenizer_control
|
||||
.borrow_mut()
|
||||
.global_comments
|
||||
.push(comment),
|
||||
_ if comment.starts_with("//!") => {
|
||||
let g = &mut state.tokenizer_control.borrow_mut().global_comments;
|
||||
if !g.is_empty() {
|
||||
g.push('\n');
|
||||
}
|
||||
g.push_str(&comment);
|
||||
}
|
||||
_ => return Some((Token::Comment(comment.into()), start_pos)),
|
||||
}
|
||||
}
|
||||
@ -1984,7 +1695,7 @@ fn get_next_token_inner(
|
||||
state.comment_level = 1;
|
||||
eat_next(stream, pos);
|
||||
|
||||
let mut comment: Option<SmartString> = match stream.peek_next() {
|
||||
let mut comment: Option<String> = match stream.peek_next() {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[cfg(feature = "metadata")]
|
||||
Some('*') => {
|
||||
@ -2119,6 +1830,15 @@ fn get_next_token_inner(
|
||||
}
|
||||
('>', ..) => return Some((Token::GreaterThan, start_pos)),
|
||||
|
||||
('!', 'i') => {
|
||||
eat_next(stream, pos);
|
||||
if stream.peek_next() == Some('n') {
|
||||
eat_next(stream, pos);
|
||||
return Some((Token::NotIn, start_pos));
|
||||
}
|
||||
stream.unget('i');
|
||||
return Some((Token::Bang, start_pos));
|
||||
}
|
||||
('!', '=') => {
|
||||
eat_next(stream, pos);
|
||||
|
||||
@ -2220,12 +1940,12 @@ fn get_next_token_inner(
|
||||
}
|
||||
|
||||
/// Get the next token, parsing it as an identifier.
|
||||
fn get_token_as_identifier(
|
||||
fn parse_identifier_token(
|
||||
stream: &mut impl InputStream,
|
||||
pos: &mut Position,
|
||||
start_pos: Position,
|
||||
first_char: char,
|
||||
) -> (Token, Position) {
|
||||
) -> Result<(Token, Position), LexError> {
|
||||
let mut identifier = SmartString::new_const();
|
||||
identifier.push(first_char);
|
||||
|
||||
@ -2240,19 +1960,20 @@ fn get_token_as_identifier(
|
||||
}
|
||||
|
||||
if let Some(token) = Token::lookup_symbol_from_syntax(&identifier) {
|
||||
return (token, start_pos);
|
||||
} else if Token::is_reserved_keyword(&identifier) {
|
||||
return (Token::Reserved(Box::new(identifier)), start_pos);
|
||||
return Ok((token, start_pos));
|
||||
}
|
||||
if Token::is_reserved_keyword(&identifier) {
|
||||
return Ok((Token::Reserved(Box::new(identifier)), start_pos));
|
||||
}
|
||||
|
||||
if !is_valid_identifier(&identifier) {
|
||||
return (
|
||||
return Ok((
|
||||
Token::LexError(LERR::MalformedIdentifier(identifier.to_string()).into()),
|
||||
start_pos,
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
(Token::Identifier(identifier.into()), start_pos)
|
||||
Ok((Token::Identifier(identifier.into()), start_pos))
|
||||
}
|
||||
|
||||
/// Is a keyword allowed as a function?
|
||||
@ -2435,7 +2156,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.as_ref().map_or(false, |m| m.contains_key(&*s)),
|
||||
self.engine.custom_keywords.as_deref().map_or(false, |m| m.contains_key(&*s)),
|
||||
#[cfg(feature = "no_custom_syntax")]
|
||||
false
|
||||
)
|
||||
@ -2472,7 +2193,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.as_ref().map_or(false,|m| m.contains(token)) => {
|
||||
(token, false) if self.engine.disabled_symbols.as_deref().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())
|
||||
},
|
||||
@ -2481,13 +2202,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.as_ref().map_or(false,|m| m.contains_key(&*s)) => {
|
||||
Some((Token::Identifier(s), pos)) if self.engine.custom_keywords.as_deref().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 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())) {
|
||||
Some((token, pos)) if token.is_literal() && self.engine.custom_keywords.as_deref().map_or(false,|m| m.contains_key(token.literal_syntax())) => {
|
||||
if self.engine.disabled_symbols.as_deref().map_or(false,|m| m.contains(token.literal_syntax())) {
|
||||
// Disabled standard keyword/symbol
|
||||
(Token::Custom(Box::new(token.literal_syntax().into())), pos)
|
||||
} else {
|
||||
@ -2496,7 +2217,7 @@ impl<'a> Iterator for TokenIterator<'a> {
|
||||
}
|
||||
}
|
||||
// Disabled symbol
|
||||
Some((token, pos)) if token.is_literal() && self.engine.disabled_symbols.as_ref().map_or(false,|m| m.contains(token.literal_syntax())) => {
|
||||
Some((token, pos)) if token.is_literal() && self.engine.disabled_symbols.as_deref().map_or(false,|m| m.contains(token.literal_syntax())) => {
|
||||
(Token::Reserved(Box::new(token.literal_syntax().into())), pos)
|
||||
}
|
||||
// Normal symbol
|
||||
@ -2554,10 +2275,7 @@ impl Engine {
|
||||
TokenIterator {
|
||||
engine: self,
|
||||
state: TokenizeState {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
max_string_size: self.limits.max_string_size,
|
||||
#[cfg(feature = "unchecked")]
|
||||
max_string_size: None,
|
||||
max_string_len: NonZeroUsize::new(self.max_string_size()),
|
||||
next_token_cannot_be_unary: false,
|
||||
tokenizer_control: buffer,
|
||||
comment_level: 0,
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Collection of custom types.
|
||||
|
||||
use crate::Identifier;
|
||||
use std::{any::type_name, collections::BTreeMap, fmt};
|
||||
use std::{any::type_name, collections::BTreeMap};
|
||||
|
||||
/// _(internals)_ Information for a custom type.
|
||||
/// Exported under the `internals` feature only.
|
||||
@ -13,18 +13,9 @@ pub struct CustomTypeInfo {
|
||||
|
||||
/// _(internals)_ A collection of custom types.
|
||||
/// Exported under the `internals` feature only.
|
||||
#[derive(Clone, Hash)]
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub struct CustomTypesCollection(BTreeMap<Identifier, CustomTypeInfo>);
|
||||
|
||||
impl fmt::Debug for CustomTypesCollection {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("CustomTypesCollection ")?;
|
||||
f.debug_map().entries(self.0.iter()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CustomTypesCollection {
|
||||
#[inline(always)]
|
||||
fn default() -> Self {
|
||||
@ -65,6 +56,7 @@ impl CustomTypesCollection {
|
||||
}
|
||||
/// Find a custom type.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn get(&self, key: &str) -> Option<&CustomTypeInfo> {
|
||||
self.0.get(key)
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Helper module which defines the [`Dynamic`] data type.
|
||||
|
||||
use crate::{reify, ExclusiveRange, FnPtr, ImmutableString, InclusiveRange, INT};
|
||||
use crate::{ExclusiveRange, FnPtr, ImmutableString, InclusiveRange, INT};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{
|
||||
@ -49,11 +49,13 @@ pub type Tag = i16;
|
||||
const DEFAULT_TAG_VALUE: Tag = 0;
|
||||
|
||||
/// Dynamic type containing any value.
|
||||
#[must_use]
|
||||
pub struct Dynamic(pub(crate) Union);
|
||||
|
||||
/// Internal [`Dynamic`] representation.
|
||||
///
|
||||
/// Most variants are boxed to reduce the size.
|
||||
#[must_use]
|
||||
pub enum Union {
|
||||
/// An error value which should not exist.
|
||||
Null,
|
||||
@ -107,10 +109,12 @@ pub enum Union {
|
||||
/// This type provides transparent interoperability between normal [`Dynamic`] and shared
|
||||
/// [`Dynamic`] values.
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
pub struct DynamicReadLock<'d, T: Clone>(DynamicReadLockInner<'d, T>);
|
||||
|
||||
/// Different types of read guards for [`DynamicReadLock`].
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
enum DynamicReadLockInner<'d, T: Clone> {
|
||||
/// A simple reference to a non-shared value.
|
||||
Reference(&'d T),
|
||||
@ -139,10 +143,12 @@ impl<'d, T: Any + Clone> Deref for DynamicReadLock<'d, T> {
|
||||
/// This type provides transparent interoperability between normal [`Dynamic`] and shared
|
||||
/// [`Dynamic`] values.
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
pub struct DynamicWriteLock<'d, T: Clone>(DynamicWriteLockInner<'d, T>);
|
||||
|
||||
/// Different types of write guards for [`DynamicReadLock`].
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
enum DynamicWriteLockInner<'d, T: Clone> {
|
||||
/// A simple mutable reference to a non-shared value.
|
||||
Reference(&'d mut T),
|
||||
@ -686,7 +692,6 @@ impl Clone for Dynamic {
|
||||
|
||||
impl Default for Dynamic {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self::UNIT
|
||||
}
|
||||
@ -852,19 +857,16 @@ impl Dynamic {
|
||||
|
||||
/// Create a new [`Dynamic`] from a [`bool`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn from_bool(value: bool) -> Self {
|
||||
Self(Union::Bool(value, DEFAULT_TAG_VALUE, ReadWrite))
|
||||
}
|
||||
/// Create a new [`Dynamic`] from an [`INT`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn from_int(value: INT) -> Self {
|
||||
Self(Union::Int(value, DEFAULT_TAG_VALUE, ReadWrite))
|
||||
}
|
||||
/// Create a new [`Dynamic`] from a [`char`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn from_char(value: char) -> Self {
|
||||
Self(Union::Char(value, DEFAULT_TAG_VALUE, ReadWrite))
|
||||
}
|
||||
@ -873,7 +875,6 @@ impl Dynamic {
|
||||
/// Not available under `no_float`.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn from_float(value: crate::FLOAT) -> Self {
|
||||
Self(Union::Float(
|
||||
super::FloatWrapper::new(value),
|
||||
@ -886,28 +887,24 @@ impl Dynamic {
|
||||
/// Exported under the `decimal` feature only.
|
||||
#[cfg(feature = "decimal")]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn from_decimal(value: rust_decimal::Decimal) -> Self {
|
||||
Self(Union::Decimal(value.into(), DEFAULT_TAG_VALUE, ReadWrite))
|
||||
}
|
||||
/// Create a [`Dynamic`] from an [`Array`][crate::Array].
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn from_array(array: crate::Array) -> Self {
|
||||
Self(Union::Array(array.into(), DEFAULT_TAG_VALUE, ReadWrite))
|
||||
}
|
||||
/// Create a [`Dynamic`] from a [`Blob`][crate::Blob].
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn from_blob(blob: crate::Blob) -> Self {
|
||||
Self(Union::Blob(blob.into(), DEFAULT_TAG_VALUE, ReadWrite))
|
||||
}
|
||||
/// Create a [`Dynamic`] from a [`Map`][crate::Map].
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn from_map(map: crate::Map) -> Self {
|
||||
Self(Union::Map(map.into(), DEFAULT_TAG_VALUE, ReadWrite))
|
||||
}
|
||||
@ -916,7 +913,6 @@ impl Dynamic {
|
||||
/// Not available under `no-std` or `no_time`.
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn from_timestamp(value: Instant) -> Self {
|
||||
Self(Union::TimeStamp(value.into(), DEFAULT_TAG_VALUE, ReadWrite))
|
||||
}
|
||||
@ -991,7 +987,6 @@ impl Dynamic {
|
||||
}
|
||||
/// Make this [`Dynamic`] read-only (i.e. a constant).
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn into_read_only(self) -> Self {
|
||||
let mut value = self;
|
||||
value.set_access_mode(AccessMode::ReadOnly);
|
||||
@ -1085,7 +1080,6 @@ impl Dynamic {
|
||||
/// assert_eq!(new_result.to_string(), "hello");
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn from<T: Variant + Clone>(value: T) -> Self {
|
||||
// Coded this way in order to maximally leverage potentials for dead-code removal.
|
||||
|
||||
@ -1143,7 +1137,6 @@ impl Dynamic {
|
||||
/// If the [`Dynamic`] value is already shared, this method returns itself.
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn into_shared(self) -> Self {
|
||||
let _access = self.access_mode();
|
||||
|
||||
@ -1182,6 +1175,7 @@ impl Dynamic {
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[allow(unused_mut)]
|
||||
pub fn try_cast<T: Any>(mut self) -> Option<T> {
|
||||
// Coded this way in order to maximally leverage potentials for dead-code removal.
|
||||
|
||||
@ -1365,7 +1359,6 @@ impl Dynamic {
|
||||
///
|
||||
/// If the [`Dynamic`] is a shared value, it returns a cloned copy of the shared value.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn flatten_clone(&self) -> Self {
|
||||
match self.0 {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
@ -1380,7 +1373,6 @@ impl Dynamic {
|
||||
/// If the [`Dynamic`] is a shared value, it returns the shared value if there are no
|
||||
/// outstanding references, or a cloned copy.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn flatten(self) -> Self {
|
||||
match self.0 {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
@ -1451,7 +1443,6 @@ impl Dynamic {
|
||||
/// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1).
|
||||
/// Otherwise, this call panics if the data is currently borrowed for write.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn read_lock<T: Any + Clone>(&self) -> Option<DynamicReadLock<T>> {
|
||||
match self.0 {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
@ -1483,7 +1474,6 @@ impl Dynamic {
|
||||
/// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1).
|
||||
/// Otherwise, this call panics if the data is currently borrowed for write.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn write_lock<T: Any + Clone>(&mut self) -> Option<DynamicWriteLock<T>> {
|
||||
match self.0 {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
|
@ -135,22 +135,20 @@ impl fmt::Display for EvalAltResult {
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Self::ErrorInFunctionCall(s, src, err, ..) if crate::parser::is_anonymous_fn(s) => {
|
||||
write!(f, "{err} in call to closure")?;
|
||||
write!(f, "{err}\nin closure call")?;
|
||||
if !src.is_empty() {
|
||||
write!(f, " @ '{src}'")?;
|
||||
}
|
||||
}
|
||||
Self::ErrorInFunctionCall(s, src, err, ..) => {
|
||||
write!(f, "{err} in call to function {s}")?;
|
||||
write!(f, "{err}\nin call to function '{s}'")?;
|
||||
if !src.is_empty() {
|
||||
write!(f, " @ '{src}'")?;
|
||||
}
|
||||
}
|
||||
|
||||
Self::ErrorInModule(s, err, ..) if s.is_empty() => {
|
||||
write!(f, "Error in module > {err}")?
|
||||
}
|
||||
Self::ErrorInModule(s, err, ..) => write!(f, "Error in module '{s}' > {err}")?,
|
||||
Self::ErrorInModule(s, err, ..) if s.is_empty() => write!(f, "{err}\nin module")?,
|
||||
Self::ErrorInModule(s, err, ..) => write!(f, "{err}\nin module '{s}'")?,
|
||||
|
||||
Self::ErrorVariableExists(s, ..) => write!(f, "Variable already defined: {s}")?,
|
||||
Self::ErrorForbiddenVariable(s, ..) => write!(f, "Forbidden variable name: {s}")?,
|
||||
@ -159,16 +157,14 @@ impl fmt::Display for EvalAltResult {
|
||||
Self::ErrorIndexNotFound(s, ..) => write!(f, "Invalid index: {s}")?,
|
||||
Self::ErrorFunctionNotFound(s, ..) => write!(f, "Function not found: {s}")?,
|
||||
Self::ErrorModuleNotFound(s, ..) => write!(f, "Module not found: {s}")?,
|
||||
Self::ErrorDataRace(s, ..) => {
|
||||
write!(f, "Data race detected when accessing variable: {s}")?
|
||||
}
|
||||
Self::ErrorDataRace(s, ..) => write!(f, "Data race detected on variable '{s}'")?,
|
||||
|
||||
Self::ErrorDotExpr(s, ..) if s.is_empty() => f.write_str("Malformed dot expression")?,
|
||||
Self::ErrorDotExpr(s, ..) => f.write_str(s)?,
|
||||
|
||||
Self::ErrorIndexingType(s, ..) => write!(f, "Indexer unavailable: {s}")?,
|
||||
Self::ErrorUnboundThis(..) => f.write_str("'this' not bound")?,
|
||||
Self::ErrorFor(..) => f.write_str("For loop expects an iterable type")?,
|
||||
Self::ErrorFor(..) => f.write_str("For loop expects iterable type")?,
|
||||
Self::ErrorTooManyOperations(..) => f.write_str("Too many operations")?,
|
||||
Self::ErrorTooManyModules(..) => f.write_str("Too many modules imported")?,
|
||||
Self::ErrorStackOverflow(..) => f.write_str("Stack overflow")?,
|
||||
@ -188,31 +184,25 @@ impl fmt::Display for EvalAltResult {
|
||||
if s.starts_with(crate::engine::FN_GET) =>
|
||||
{
|
||||
let prop = &s[crate::engine::FN_GET.len()..];
|
||||
write!(
|
||||
f,
|
||||
"Property {prop} is not pure and cannot be accessed on a constant"
|
||||
)?
|
||||
write!(f, "Non-pure property {prop} cannot be accessed on constant")?
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Self::ErrorNonPureMethodCallOnConstant(s, ..)
|
||||
if s.starts_with(crate::engine::FN_SET) =>
|
||||
{
|
||||
let prop = &s[crate::engine::FN_SET.len()..];
|
||||
write!(f, "Cannot modify property '{prop}' of a constant")?
|
||||
write!(f, "Cannot modify property '{prop}' of constant")?
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::ErrorNonPureMethodCallOnConstant(s, ..) if s == crate::engine::FN_IDX_GET => {
|
||||
write!(
|
||||
f,
|
||||
"Indexer is not pure and cannot be accessed on a constant"
|
||||
)?
|
||||
write!(f, "Non-pure indexer cannot be accessed on constant")?
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::ErrorNonPureMethodCallOnConstant(s, ..) if s == crate::engine::FN_IDX_SET => {
|
||||
write!(f, "Cannot assign to the indexer of a constant")?
|
||||
write!(f, "Cannot assign to indexer of constant")?
|
||||
}
|
||||
Self::ErrorNonPureMethodCallOnConstant(s, ..) => {
|
||||
write!(f, "Non-pure method '{s}' cannot be called on a constant")?
|
||||
write!(f, "Non-pure method '{s}' cannot be called on constant")?
|
||||
}
|
||||
|
||||
Self::ErrorAssignmentToConstant(s, ..) => write!(f, "Cannot modify constant {s}")?,
|
||||
@ -230,8 +220,8 @@ impl fmt::Display for EvalAltResult {
|
||||
Self::ErrorArithmetic(s, ..) if s.is_empty() => f.write_str("Arithmetic error")?,
|
||||
Self::ErrorArithmetic(s, ..) => f.write_str(s)?,
|
||||
|
||||
Self::LoopBreak(true, ..) => f.write_str("'break' must be inside a loop")?,
|
||||
Self::LoopBreak(false, ..) => f.write_str("'continue' must be inside a loop")?,
|
||||
Self::LoopBreak(true, ..) => f.write_str("'break' must be within a loop")?,
|
||||
Self::LoopBreak(false, ..) => f.write_str("'continue' must be within a loop")?,
|
||||
|
||||
Self::Return(..) => f.write_str("NOT AN ERROR - function returns value")?,
|
||||
|
||||
@ -261,7 +251,7 @@ impl fmt::Display for EvalAltResult {
|
||||
f,
|
||||
"Bit-field index {index} out of bounds: only {max} bits in bit-field",
|
||||
)?,
|
||||
Self::ErrorDataTooLarge(typ, ..) => write!(f, "{typ} exceeds maximum limit")?,
|
||||
Self::ErrorDataTooLarge(typ, ..) => write!(f, "{typ} too large")?,
|
||||
|
||||
Self::ErrorCustomSyntax(s, tokens, ..) => write!(f, "{s}: {}", tokens.join(" "))?,
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ use num_traits::float::FloatCore as Float;
|
||||
///
|
||||
/// Not available under `no_float`.
|
||||
#[derive(Clone, Copy, Eq, PartialEq, PartialOrd)]
|
||||
#[must_use]
|
||||
pub struct FloatWrapper<F>(F);
|
||||
|
||||
impl Hash for FloatWrapper<crate::FLOAT> {
|
||||
@ -108,7 +109,6 @@ impl<F: Float> FloatWrapper<F> {
|
||||
|
||||
/// Create a new [`FloatWrapper`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn new(value: F) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
|
@ -4,8 +4,8 @@ use crate::eval::GlobalRuntimeState;
|
||||
use crate::tokenizer::is_valid_function_name;
|
||||
use crate::types::dynamic::Variant;
|
||||
use crate::{
|
||||
reify, Dynamic, Engine, FuncArgs, ImmutableString, NativeCallContext, Position, RhaiError,
|
||||
RhaiResult, RhaiResultOf, StaticVec, AST, ERR,
|
||||
Dynamic, Engine, FuncArgs, ImmutableString, NativeCallContext, Position, RhaiError, RhaiResult,
|
||||
RhaiResultOf, StaticVec, AST, ERR,
|
||||
};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
@ -24,7 +24,6 @@ pub const MAX_STRING_LEN: usize = 24;
|
||||
/// _(internals)_ A cache for interned strings.
|
||||
/// Exported under the `internals` feature only.
|
||||
#[derive(Clone)]
|
||||
#[must_use]
|
||||
pub struct StringsInterner {
|
||||
/// Cached strings.
|
||||
cache: StraightHashMap<ImmutableString>,
|
||||
@ -34,6 +33,7 @@ pub struct StringsInterner {
|
||||
|
||||
impl Default for StringsInterner {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
@ -50,6 +50,7 @@ impl fmt::Debug for StringsInterner {
|
||||
impl StringsInterner {
|
||||
/// Create a new [`StringsInterner`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
cache: StraightHashMap::default(),
|
||||
|
@ -1,5 +1,8 @@
|
||||
//! Module defining Rhai data types.
|
||||
|
||||
#[macro_use]
|
||||
pub mod restore;
|
||||
|
||||
pub mod bloom_filter;
|
||||
pub mod custom_types;
|
||||
pub mod dynamic;
|
||||
@ -9,7 +12,8 @@ pub mod fn_ptr;
|
||||
pub mod immutable_string;
|
||||
pub mod interner;
|
||||
pub mod parse_error;
|
||||
pub mod restore;
|
||||
pub mod position;
|
||||
pub mod position_none;
|
||||
pub mod scope;
|
||||
pub mod variant;
|
||||
|
||||
@ -25,6 +29,12 @@ pub use fn_ptr::FnPtr;
|
||||
pub use immutable_string::ImmutableString;
|
||||
pub use interner::StringsInterner;
|
||||
pub use parse_error::{LexError, ParseError, ParseErrorType};
|
||||
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
pub use position::{Position, Span};
|
||||
#[cfg(feature = "no_position")]
|
||||
pub use position_none::{Position, Span};
|
||||
|
||||
pub use restore::RestoreOnDrop;
|
||||
pub use scope::Scope;
|
||||
pub use variant::Variant;
|
||||
|
@ -19,7 +19,7 @@ pub enum LexError {
|
||||
UnexpectedInput(String),
|
||||
/// A string literal is not terminated before a new-line or EOF.
|
||||
UnterminatedString,
|
||||
/// An identifier is in an invalid format.
|
||||
/// An identifier or string literal is longer than the maximum allowed length.
|
||||
StringTooLong(usize),
|
||||
/// An string/character/numeric escape sequence is in an invalid format.
|
||||
MalformedEscapeSequence(String),
|
||||
@ -44,11 +44,7 @@ impl fmt::Display for LexError {
|
||||
Self::MalformedChar(s) => write!(f, "Invalid character: '{s}'"),
|
||||
Self::MalformedIdentifier(s) => write!(f, "Variable name is not proper: '{s}'"),
|
||||
Self::UnterminatedString => f.write_str("Open string is not terminated"),
|
||||
Self::StringTooLong(max) => write!(
|
||||
f,
|
||||
"Length of string literal exceeds the maximum limit ({})",
|
||||
max
|
||||
),
|
||||
Self::StringTooLong(max) => write!(f, "String is too long (max {max})"),
|
||||
Self::ImproperSymbol(s, d) if d.is_empty() => {
|
||||
write!(f, "Invalid symbol encountered: '{s}'")
|
||||
}
|
||||
@ -262,7 +258,7 @@ impl From<LexError> for ParseErrorType {
|
||||
fn from(err: LexError) -> Self {
|
||||
match err {
|
||||
LexError::StringTooLong(max) => {
|
||||
Self::LiteralTooLarge("Length of string literal".to_string(), max)
|
||||
Self::LiteralTooLarge("Length of string".to_string(), max)
|
||||
}
|
||||
_ => Self::BadInput(err),
|
||||
}
|
||||
|
286
src/types/position.rs
Normal file
286
src/types/position.rs
Normal file
@ -0,0 +1,286 @@
|
||||
//! Script character position type.
|
||||
#![cfg(not(feature = "no_position"))]
|
||||
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{
|
||||
fmt,
|
||||
ops::{Add, AddAssign},
|
||||
};
|
||||
|
||||
/// A location (line number + character position) in the input script.
|
||||
///
|
||||
/// # Limitations
|
||||
///
|
||||
/// In order to keep footprint small, both line number and character position have 16-bit resolution,
|
||||
/// meaning they go up to a maximum of 65,535 lines and 65,535 characters per line.
|
||||
///
|
||||
/// Advancing beyond the maximum line length or maximum number of lines is not an error but has no effect.
|
||||
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
|
||||
pub struct Position {
|
||||
/// Line number: 0 = none
|
||||
line: u16,
|
||||
/// Character position: 0 = BOL
|
||||
pos: u16,
|
||||
}
|
||||
|
||||
impl Position {
|
||||
/// A [`Position`] representing no position.
|
||||
pub const NONE: Self = Self { line: 0, pos: 0 };
|
||||
/// A [`Position`] representing the first position.
|
||||
pub const START: Self = Self { line: 1, pos: 0 };
|
||||
|
||||
/// Create a new [`Position`].
|
||||
///
|
||||
/// `line` must not be zero.
|
||||
///
|
||||
/// If `position` is zero, then it is at the beginning of a line.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `line` is zero.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn new(line: u16, position: u16) -> Self {
|
||||
assert!(line != 0, "line cannot be zero");
|
||||
|
||||
let _pos = position;
|
||||
|
||||
Self { line, pos: _pos }
|
||||
}
|
||||
/// Get the line number (1-based), or [`None`] if there is no position.
|
||||
///
|
||||
/// Always returns [`None`] under `no_position`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn line(self) -> Option<usize> {
|
||||
if self.is_none() {
|
||||
None
|
||||
} else {
|
||||
Some(self.line as usize)
|
||||
}
|
||||
}
|
||||
/// Get the character position (1-based), or [`None`] if at beginning of a line.
|
||||
///
|
||||
/// Always returns [`None`] under `no_position`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn position(self) -> Option<usize> {
|
||||
if self.is_none() || self.pos == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(self.pos as usize)
|
||||
}
|
||||
}
|
||||
/// Advance by one character position.
|
||||
#[inline]
|
||||
pub(crate) fn advance(&mut self) {
|
||||
assert!(!self.is_none(), "cannot advance Position::none");
|
||||
|
||||
// Advance up to maximum position
|
||||
if self.pos < u16::MAX {
|
||||
self.pos += 1;
|
||||
}
|
||||
}
|
||||
/// Go backwards by one character position.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if already at beginning of a line - cannot rewind to a previous line.
|
||||
#[inline]
|
||||
pub(crate) fn rewind(&mut self) {
|
||||
assert!(!self.is_none(), "cannot rewind Position::none");
|
||||
assert!(self.pos > 0, "cannot rewind at position 0");
|
||||
self.pos -= 1;
|
||||
}
|
||||
/// Advance to the next line.
|
||||
#[inline]
|
||||
pub(crate) fn new_line(&mut self) {
|
||||
assert!(!self.is_none(), "cannot advance Position::none");
|
||||
|
||||
// Advance up to maximum position
|
||||
if self.line < u16::MAX {
|
||||
self.line += 1;
|
||||
self.pos = 0;
|
||||
}
|
||||
}
|
||||
/// Is this [`Position`] at the beginning of a line?
|
||||
///
|
||||
/// Always returns `false` under `no_position`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_beginning_of_line(self) -> bool {
|
||||
self.pos == 0 && !self.is_none()
|
||||
}
|
||||
/// Is there no [`Position`]?
|
||||
///
|
||||
/// Always returns `true` under `no_position`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_none(self) -> bool {
|
||||
self.line == 0 && self.pos == 0
|
||||
}
|
||||
/// Returns an fallback [`Position`] if it is [`NONE`][Position::NONE]?
|
||||
///
|
||||
/// Always returns the fallback under `no_position`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn or_else(self, pos: Self) -> Self {
|
||||
if self.is_none() {
|
||||
pos
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
/// Print this [`Position`] for debug purposes.
|
||||
#[inline]
|
||||
pub(crate) fn debug_print(self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if !self.is_none() {
|
||||
write!(_f, " @ {:?}", self)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Position {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self::START
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Position {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.is_none() {
|
||||
write!(f, "none")
|
||||
} else {
|
||||
write!(f, "line {}, position {}", self.line, self.pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Position {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.is_none() {
|
||||
f.write_str("none")
|
||||
} else {
|
||||
if self.is_beginning_of_line() {
|
||||
write!(f, "{}", self.line)
|
||||
} else {
|
||||
write!(f, "{}:{}", self.line, self.pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Position {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
if rhs.is_none() {
|
||||
self
|
||||
} else {
|
||||
Self {
|
||||
line: self.line + rhs.line - 1,
|
||||
pos: if rhs.is_beginning_of_line() {
|
||||
self.pos
|
||||
} else {
|
||||
self.pos + rhs.pos - 1
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for Position {
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
*self = *self + rhs;
|
||||
}
|
||||
}
|
||||
|
||||
/// _(internals)_ A span consisting of a starting and an ending [positions][Position].
|
||||
/// Exported under the `internals` feature only.
|
||||
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
|
||||
pub struct Span {
|
||||
/// Starting [position][Position].
|
||||
start: Position,
|
||||
/// Ending [position][Position].
|
||||
end: Position,
|
||||
}
|
||||
|
||||
impl Default for Span {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self::NONE
|
||||
}
|
||||
}
|
||||
|
||||
impl Span {
|
||||
/// Empty [`Span`].
|
||||
pub const NONE: Self = Self::new(Position::NONE, Position::NONE);
|
||||
|
||||
/// Create a new [`Span`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn new(start: Position, end: Position) -> Self {
|
||||
Self { start, end }
|
||||
}
|
||||
/// Is this [`Span`] non-existent?
|
||||
///
|
||||
/// Always returns `true` under `no_position`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_none(&self) -> bool {
|
||||
self.start.is_none() && self.end.is_none()
|
||||
}
|
||||
/// Get the [`Span`]'s starting [position][Position].
|
||||
///
|
||||
/// Always returns [`Position::NONE`] under `no_position`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn start(&self) -> Position {
|
||||
self.start
|
||||
}
|
||||
/// Get the [`Span`]'s ending [position][Position].
|
||||
///
|
||||
/// Always returns [`Position::NONE`] under `no_position`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn end(&self) -> Position {
|
||||
self.end
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Span {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let _f = f;
|
||||
|
||||
match (self.start(), self.end()) {
|
||||
(Position::NONE, Position::NONE) => write!(_f, "{:?}", Position::NONE),
|
||||
(Position::NONE, end) => write!(_f, "..{:?}", end),
|
||||
(start, Position::NONE) => write!(_f, "{:?}", start),
|
||||
(start, end) if start.line() != end.line() => {
|
||||
write!(_f, "{:?}-{:?}", start, end)
|
||||
}
|
||||
(start, end) => write!(
|
||||
_f,
|
||||
"{}:{}-{}",
|
||||
start.line().unwrap(),
|
||||
start.position().unwrap_or(0),
|
||||
end.position().unwrap_or(0)
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Span {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
182
src/types/position_none.rs
Normal file
182
src/types/position_none.rs
Normal file
@ -0,0 +1,182 @@
|
||||
//! Placeholder script character position type.
|
||||
#![cfg(feature = "no_position")]
|
||||
#![allow(unused_variables)]
|
||||
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{
|
||||
fmt,
|
||||
ops::{Add, AddAssign},
|
||||
};
|
||||
|
||||
/// A location (line number + character position) in the input script.
|
||||
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
|
||||
pub struct Position;
|
||||
|
||||
impl Position {
|
||||
/// A [`Position`] representing no position.
|
||||
pub const NONE: Self = Self;
|
||||
/// A [`Position`] representing the first position.
|
||||
pub const START: Self = Self;
|
||||
|
||||
/// Create a new [`Position`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn new(line: u16, position: u16) -> Self {
|
||||
Self
|
||||
}
|
||||
/// Get the line number (1-based), or [`None`] if there is no position.
|
||||
///
|
||||
/// Always returns [`None`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn line(self) -> Option<usize> {
|
||||
None
|
||||
}
|
||||
/// Get the character position (1-based), or [`None`] if at beginning of a line.
|
||||
///
|
||||
/// Always returns [`None`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn position(self) -> Option<usize> {
|
||||
None
|
||||
}
|
||||
/// Advance by one character position.
|
||||
#[inline(always)]
|
||||
pub(crate) fn advance(&mut self) {}
|
||||
/// Go backwards by one character position.
|
||||
#[inline(always)]
|
||||
pub(crate) fn rewind(&mut self) {}
|
||||
/// Advance to the next line.
|
||||
#[inline(always)]
|
||||
pub(crate) fn new_line(&mut self) {}
|
||||
/// Is this [`Position`] at the beginning of a line?
|
||||
///
|
||||
/// Always returns `false`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn is_beginning_of_line(self) -> bool {
|
||||
false
|
||||
}
|
||||
/// Is there no [`Position`]?
|
||||
///
|
||||
/// Always returns `true`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn is_none(self) -> bool {
|
||||
true
|
||||
}
|
||||
/// Returns an fallback [`Position`] if it is [`NONE`][Position::NONE]?
|
||||
///
|
||||
/// Always returns the fallback.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn or_else(self, pos: Self) -> Self {
|
||||
pos
|
||||
}
|
||||
/// Print this [`Position`] for debug purposes.
|
||||
#[inline(always)]
|
||||
pub(crate) fn debug_print(self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Position {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Position {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "none")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Position {
|
||||
#[cold]
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("none")
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Position {
|
||||
type Output = Self;
|
||||
|
||||
#[inline(always)]
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for Position {
|
||||
#[inline(always)]
|
||||
fn add_assign(&mut self, rhs: Self) {}
|
||||
}
|
||||
|
||||
/// _(internals)_ A span consisting of a starting and an ending [positions][Position].
|
||||
/// Exported under the `internals` feature only.
|
||||
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
|
||||
pub struct Span;
|
||||
|
||||
impl Default for Span {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl Span {
|
||||
/// Empty [`Span`].
|
||||
pub const NONE: Self = Self;
|
||||
|
||||
/// Create a new [`Span`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn new(start: Position, end: Position) -> Self {
|
||||
Self
|
||||
}
|
||||
/// Is this [`Span`] non-existent?
|
||||
///
|
||||
/// Always returns `true`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn is_none(&self) -> bool {
|
||||
true
|
||||
}
|
||||
/// Get the [`Span`]'s starting [position][Position].
|
||||
///
|
||||
/// Always returns [`Position::NONE`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn start(&self) -> Position {
|
||||
Position::NONE
|
||||
}
|
||||
/// Get the [`Span`]'s ending [position][Position].
|
||||
///
|
||||
/// Always returns [`Position::NONE`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn end(&self) -> Position {
|
||||
Position::NONE
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Span {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let f = f;
|
||||
write!(f, "{:?}", Position)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Span {
|
||||
#[cold]
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
@ -4,6 +4,52 @@ use std::ops::{Deref, DerefMut};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
/// Automatically restore state at the end of the scope.
|
||||
macro_rules! auto_restore {
|
||||
(let $temp:ident = $var:ident . $prop:ident; $code:stmt) => {
|
||||
auto_restore!(let $temp = $var.$prop; $code => move |v| v.$prop = $temp);
|
||||
};
|
||||
(let $temp:ident = $var:ident . $prop:ident; $code:stmt => $restore:expr) => {
|
||||
let $temp = $var.$prop;
|
||||
$code
|
||||
auto_restore!($var => $restore);
|
||||
};
|
||||
($var:ident => $restore:ident; let $temp:ident = $save:expr;) => {
|
||||
auto_restore!($var => $restore; let $temp = $save; {});
|
||||
};
|
||||
($var:ident if $guard:expr => $restore:ident; let $temp:ident = $save:expr;) => {
|
||||
auto_restore!($var if $guard => $restore; let $temp = $save; {});
|
||||
};
|
||||
($var:ident => $restore:ident; let $temp:ident = $save:expr; $code:stmt) => {
|
||||
let $temp = $save;
|
||||
$code
|
||||
auto_restore!($var => move |v| { v.$restore($temp); });
|
||||
};
|
||||
($var:ident if $guard:expr => $restore:ident; let $temp:ident = $save:expr; $code:stmt) => {
|
||||
let $temp = $save;
|
||||
$code
|
||||
auto_restore!($var if $guard => move |v| { v.$restore($temp); });
|
||||
};
|
||||
($var:ident => $restore:expr) => {
|
||||
auto_restore!($var = $var => $restore);
|
||||
};
|
||||
($var:ident = $value:expr => $restore:expr) => {
|
||||
let $var = &mut *crate::types::RestoreOnDrop::lock($value, $restore);
|
||||
};
|
||||
($var:ident if $guard:expr => $restore:expr) => {
|
||||
auto_restore!($var = ($var) if $guard => $restore);
|
||||
};
|
||||
($var:ident = ( $value:expr ) if $guard:expr => $restore:expr) => {
|
||||
let mut __rx__;
|
||||
let $var = if $guard {
|
||||
__rx__ = crate::types::RestoreOnDrop::lock($value, $restore);
|
||||
&mut *__rx__
|
||||
} else {
|
||||
&mut *$value
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/// Run custom restoration logic upon the end of scope.
|
||||
#[must_use]
|
||||
pub struct RestoreOnDrop<'a, T: ?Sized, R: FnOnce(&mut T)> {
|
||||
@ -12,19 +58,6 @@ pub struct RestoreOnDrop<'a, T: ?Sized, R: FnOnce(&mut T)> {
|
||||
}
|
||||
|
||||
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`.
|
||||
///
|
||||
/// Beware that the end of scope means the end of its lifetime, not necessarily waiting until
|
||||
/// the current block scope is exited.
|
||||
#[inline(always)]
|
||||
pub fn lock_if(need_restore: bool, value: &'a mut T, restore: R) -> Self {
|
||||
Self {
|
||||
value,
|
||||
restore: if need_restore { Some(restore) } else { None },
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new [`RestoreOnDrop`] that locks a mutable reference and runs restoration logic at
|
||||
/// the end of scope.
|
||||
///
|
||||
@ -42,9 +75,7 @@ impl<'a, T: ?Sized, R: FnOnce(&mut T)> 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() {
|
||||
restore(self.value);
|
||||
}
|
||||
self.restore.take().unwrap()(self.value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -386,7 +386,7 @@ impl Scope<'_> {
|
||||
#[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");
|
||||
let _ = self.values.pop().expect("`Scope` must not be empty");
|
||||
self.aliases.pop().expect("`Scope` must not be empty");
|
||||
self
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ fn test_arrays() -> Result<(), Box<EvalAltResult>> {
|
||||
assert_eq!(engine.eval::<INT>("let y = [1, 2, 3]; y[-1]")?, 3);
|
||||
assert_eq!(engine.eval::<INT>("let y = [1, 2, 3]; y[-3]")?, 1);
|
||||
assert!(engine.eval::<bool>("let y = [1, 2, 3]; 2 in y")?);
|
||||
assert!(engine.eval::<bool>("let y = [1, 2, 3]; 42 !in y")?);
|
||||
assert_eq!(engine.eval::<INT>("let y = [1, 2, 3]; y += 4; y[3]")?, 4);
|
||||
assert_eq!(
|
||||
engine.eval::<INT>("let y = [1, 2, 3]; pad(y, 5, 42); len(y)")?,
|
||||
|
@ -352,7 +352,7 @@ fn test_call_fn_events() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
let mut handler = Handler::new();
|
||||
assert!(!handler.scope.get_value::<bool>("state").unwrap());
|
||||
handler.on_event("update", 999);
|
||||
let _ = handler.on_event("update", 999);
|
||||
assert!(handler.scope.get_value::<bool>("state").unwrap());
|
||||
assert_eq!(handler.on_event("start", 999).as_int().unwrap(), 1041);
|
||||
|
||||
|
@ -75,10 +75,16 @@ fn test_comments_doc() -> Result<(), Box<EvalAltResult>> {
|
||||
",
|
||||
)?;
|
||||
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
assert_eq!(
|
||||
ast.iter_functions().next().unwrap().comments[0],
|
||||
"/** Hello world\n** how are you?\n**/"
|
||||
);
|
||||
#[cfg(feature = "no_position")]
|
||||
assert_eq!(
|
||||
ast.iter_functions().next().unwrap().comments[0],
|
||||
"/** Hello world\n ** how are you?\n **/",
|
||||
);
|
||||
|
||||
assert!(engine
|
||||
.compile(
|
||||
|
@ -17,7 +17,7 @@ fn test_max_string_size() -> Result<(), Box<EvalAltResult>> {
|
||||
.compile(r#"let x = "hello, world!";"#)
|
||||
.expect_err("should error")
|
||||
.err_type(),
|
||||
ParseErrorType::LiteralTooLarge("Length of string literal".to_string(), 10)
|
||||
ParseErrorType::LiteralTooLarge("Length of string".to_string(), 10)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
@ -25,7 +25,7 @@ fn test_max_string_size() -> Result<(), Box<EvalAltResult>> {
|
||||
.compile(r#"let x = "朝に紅顔、暮に白骨";"#)
|
||||
.expect_err("should error")
|
||||
.err_type(),
|
||||
ParseErrorType::LiteralTooLarge("Length of string literal".to_string(), 10)
|
||||
ParseErrorType::LiteralTooLarge("Length of string".to_string(), 10)
|
||||
);
|
||||
|
||||
assert!(matches!(
|
||||
|
@ -12,7 +12,7 @@ fn test_debugging() -> Result<(), Box<EvalAltResult>> {
|
||||
let mut engine = Engine::new();
|
||||
|
||||
engine.register_debugger(
|
||||
|_| Dynamic::UNIT,
|
||||
|_, dbg| dbg,
|
||||
|_, _, _, _, _| Ok(rhai::debugger::DebuggerCommand::Continue),
|
||||
);
|
||||
|
||||
@ -47,19 +47,20 @@ fn test_debugger_state() -> Result<(), Box<EvalAltResult>> {
|
||||
let mut engine = Engine::new();
|
||||
|
||||
engine.register_debugger(
|
||||
|_| {
|
||||
|_, mut debugger| {
|
||||
// Say, use an object map for the debugger state
|
||||
let mut state = Map::new();
|
||||
// Initialize properties
|
||||
state.insert("hello".into(), (42 as INT).into());
|
||||
state.insert("foo".into(), false.into());
|
||||
Dynamic::from_map(state)
|
||||
debugger.set_state(state);
|
||||
debugger
|
||||
},
|
||||
|mut context, _, _, _, _| {
|
||||
// Print debugger state - which is an object map
|
||||
println!(
|
||||
"Current state = {}",
|
||||
context.global_runtime_state_mut().debugger().state()
|
||||
context.global_runtime_state().debugger().state()
|
||||
);
|
||||
|
||||
// Modify state
|
||||
|
@ -34,6 +34,15 @@ fn test_loop() -> Result<(), Box<EvalAltResult>> {
|
||||
ParseErrorType::LoopBreak
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
assert_eq!(
|
||||
*engine
|
||||
.compile("loop { let f = || { break; } }")
|
||||
.expect_err("should error")
|
||||
.err_type(),
|
||||
ParseErrorType::LoopBreak
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
*engine
|
||||
.compile("let x = 0; if x > 0 { continue; }")
|
||||
|
Loading…
Reference in New Issue
Block a user