commit
252567f84d
29
CHANGELOG.md
29
CHANGELOG.md
@ -4,14 +4,37 @@ Rhai Release Notes
|
|||||||
Version 1.12.0
|
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.
|
* 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
|
Net features
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
### `!in`
|
||||||
|
|
||||||
|
* A new operator `!in` is added which maps to `!(... in ...)`.
|
||||||
|
|
||||||
### `Engine::call_fn_with_options`
|
### `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.
|
* `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
|
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`.
|
* `CallableFunction` is exported under `internals`.
|
||||||
* The `TypeBuilder` type and `CustomType` trait are no longer marked as volatile.
|
* The `TypeBuilder` type and `CustomType` trait are no longer marked as volatile.
|
||||||
* `FuncArgs` is also implemented for arrays.
|
* `FuncArgs` is also implemented for arrays.
|
||||||
* `Engine::set_XXX` API can now be chained.
|
* `Engine::set_XXX` API can now be chained.
|
||||||
* `EvalContext::scope_mut` now returns `&mut Scope` instead of `&mut &mut Scope`.
|
* `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
|
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 }
|
num-traits = { version = "0.2", default-features = false }
|
||||||
bitflags = { version = "1", default-features = false }
|
bitflags = { version = "1", default-features = false }
|
||||||
smartstring = { 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 }
|
no-std-compat = { version = "0.4", default-features = false, features = ["alloc"], optional = true }
|
||||||
libm = { version = "0.2", default-features = false, 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]
|
[features]
|
||||||
default = ["std", "ahash/runtime-rng"] # ahash/runtime-rng trumps ahash/compile-time-rng
|
default = ["std", "ahash/runtime-rng"] # ahash/runtime-rng trumps ahash/compile-time-rng
|
||||||
std = ["ahash/std", "num-traits/std", "smartstring/std"]
|
std = ["ahash/std", "num-traits/std", "smartstring/std"]
|
||||||
unchecked = [] # unchecked arithmetic
|
unchecked = [] # disable safety checks
|
||||||
sync = [] # restrict to only types that implement Send + Sync
|
sync = [] # restrict to only types that implement Send + Sync
|
||||||
no_position = [] # do not track position in the parser
|
no_position = [] # do not track position in the parser
|
||||||
no_optimize = [] # no script optimizer
|
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.
|
* 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).
|
* 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).
|
* 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).
|
* 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]
|
[package]
|
||||||
name = "rhai_codegen"
|
name = "rhai_codegen"
|
||||||
version = "1.4.3"
|
version = "1.5.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
authors = ["jhwgh1968", "Stephen Chung"]
|
authors = ["jhwgh1968", "Stephen Chung"]
|
||||||
description = "Procedural macros support package for Rhai, a scripting language and engine for Rust"
|
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"]
|
categories = ["no-std", "embedded", "wasm", "parser-implementations"]
|
||||||
homepage = "https://rhai.rs/book/plugins/index.html"
|
homepage = "https://rhai.rs/book/plugins/index.html"
|
||||||
repository = "https://github.com/rhaiscript/rhai"
|
repository = "https://github.com/rhaiscript/rhai"
|
||||||
@ -24,5 +24,5 @@ syn = { version = "1.0", features = ["full", "parsing", "printing", "proc-macro"
|
|||||||
quote = "1"
|
quote = "1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rhai = { path = "..", version = "1.11", features = ["metadata"] }
|
rhai = { path = "..", version = "1.12", features = ["metadata"] }
|
||||||
trybuild = "1"
|
trybuild = "1"
|
||||||
|
@ -145,6 +145,7 @@ pub fn inner_item_attributes<T: ExportedParams>(
|
|||||||
pub fn doc_attributes(attrs: &[syn::Attribute]) -> syn::Result<Vec<String>> {
|
pub fn doc_attributes(attrs: &[syn::Attribute]) -> syn::Result<Vec<String>> {
|
||||||
// Find the #[doc] attribute which will turn be read for function documentation.
|
// Find the #[doc] attribute which will turn be read for function documentation.
|
||||||
let mut comments = Vec::new();
|
let mut comments = Vec::new();
|
||||||
|
let mut buf = String::new();
|
||||||
|
|
||||||
for attr in attrs {
|
for attr in attrs {
|
||||||
if let Some(i) = attr.path.get_ident() {
|
if let Some(i) = attr.path.get_ident() {
|
||||||
@ -158,19 +159,30 @@ pub fn doc_attributes(attrs: &[syn::Attribute]) -> syn::Result<Vec<String>> {
|
|||||||
|
|
||||||
if line.contains('\n') {
|
if line.contains('\n') {
|
||||||
// Must be a block comment `/** ... */`
|
// Must be a block comment `/** ... */`
|
||||||
|
if !buf.is_empty() {
|
||||||
|
comments.push(buf.clone());
|
||||||
|
buf.clear();
|
||||||
|
}
|
||||||
line.insert_str(0, "/**");
|
line.insert_str(0, "/**");
|
||||||
line.push_str("*/");
|
line.push_str("*/");
|
||||||
|
comments.push(line);
|
||||||
} else {
|
} else {
|
||||||
// Single line - assume it is `///`
|
// 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)
|
Ok(comments)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -674,6 +674,7 @@ impl ExportedFn {
|
|||||||
let arg_count = self.arg_count();
|
let arg_count = self.arg_count();
|
||||||
let is_method_call = self.mutable_receiver();
|
let is_method_call = self.mutable_receiver();
|
||||||
let is_pure = !self.mutable_receiver() || self.params().pure.is_some();
|
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_statements = Vec::new();
|
||||||
let mut unpack_exprs = Vec::new();
|
let mut unpack_exprs = Vec::new();
|
||||||
@ -689,7 +690,7 @@ impl ExportedFn {
|
|||||||
let skip_first_arg;
|
let skip_first_arg;
|
||||||
|
|
||||||
if self.pass_context {
|
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
|
// Handle the first argument separately if the function has a "method like" receiver
|
||||||
@ -860,13 +861,14 @@ impl ExportedFn {
|
|||||||
#(#cfg_attrs)*
|
#(#cfg_attrs)*
|
||||||
impl PluginFunction for #type_name {
|
impl PluginFunction for #type_name {
|
||||||
#[inline(always)]
|
#[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)*
|
#(#unpack_statements)*
|
||||||
#return_expr
|
#return_expr
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)] fn is_method_call(&self) -> bool { #is_method_call }
|
#[inline(always)] fn is_method_call(&self) -> bool { #is_method_call }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { #is_pure }
|
#[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 {
|
impl Parse for Module {
|
||||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
let mut mod_all: syn::ItemMod = input.parse()?;
|
let mut mod_all: syn::ItemMod = input.parse()?;
|
||||||
|
|
||||||
let fns: Vec<_>;
|
let fns: Vec<_>;
|
||||||
let mut consts = Vec::new();
|
let mut consts = Vec::new();
|
||||||
let mut custom_types = Vec::new();
|
let mut custom_types = Vec::new();
|
||||||
@ -269,11 +270,17 @@ impl Module {
|
|||||||
let (.., orig_content) = mod_all.content.take().unwrap();
|
let (.., orig_content) = mod_all.content.take().unwrap();
|
||||||
let mod_attrs = mem::take(&mut mod_all.attrs);
|
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 {
|
if !params.skip {
|
||||||
// Generate new module items.
|
// Generate new module items.
|
||||||
//
|
//
|
||||||
// This is done before inner module recursive generation, because that is destructive.
|
// This is done before inner module recursive generation, because that is destructive.
|
||||||
let mod_gen = crate::rhai_module::generate_body(
|
let mod_gen = crate::rhai_module::generate_body(
|
||||||
|
&mod_doc,
|
||||||
&mut fns,
|
&mut fns,
|
||||||
&consts,
|
&consts,
|
||||||
&custom_types,
|
&custom_types,
|
||||||
|
@ -26,6 +26,7 @@ pub struct ExportedType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_body(
|
pub fn generate_body(
|
||||||
|
doc: &str,
|
||||||
fns: &mut [ExportedFn],
|
fns: &mut [ExportedFn],
|
||||||
consts: &[ExportedConst],
|
consts: &[ExportedConst],
|
||||||
custom_types: &[ExportedType],
|
custom_types: &[ExportedType],
|
||||||
@ -230,7 +231,7 @@ pub fn generate_body(
|
|||||||
syn::parse2::<syn::Stmt>(quote! {
|
syn::parse2::<syn::Stmt>(quote! {
|
||||||
#(#cfg_attrs)*
|
#(#cfg_attrs)*
|
||||||
m.set_fn_with_comments(#fn_literal, FnNamespace::#ns_str, FnAccess::Public,
|
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()
|
.unwrap()
|
||||||
});
|
});
|
||||||
@ -246,6 +247,17 @@ pub fn generate_body(
|
|||||||
gen_fn_tokens.push(function.generate_impl(&fn_token_name.to_string()));
|
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! {
|
let mut generate_fn_call = syn::parse2::<syn::ItemMod>(quote! {
|
||||||
pub mod generate_info {
|
pub mod generate_info {
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
@ -254,6 +266,7 @@ pub fn generate_body(
|
|||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn rhai_module_generate() -> Module {
|
pub fn rhai_module_generate() -> Module {
|
||||||
let mut m = Module::new();
|
let mut m = Module::new();
|
||||||
|
#module_docs
|
||||||
rhai_generate_into_module(&mut m, false);
|
rhai_generate_into_module(&mut m, false);
|
||||||
m.build_index();
|
m.build_index();
|
||||||
m
|
m
|
||||||
|
@ -280,12 +280,13 @@ mod generate_tests {
|
|||||||
#[inline(always)] pub fn param_types() -> [TypeId; 0usize] { [] }
|
#[inline(always)] pub fn param_types() -> [TypeId; 0usize] { [] }
|
||||||
}
|
}
|
||||||
impl PluginFunction for Token {
|
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()))
|
Ok(Dynamic::from(do_nothing()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
#[inline(always)] fn is_pure(&self) -> bool { true }
|
||||||
|
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||||
}
|
}
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
@ -318,13 +319,14 @@ mod generate_tests {
|
|||||||
}
|
}
|
||||||
impl PluginFunction for Token {
|
impl PluginFunction for Token {
|
||||||
#[inline(always)]
|
#[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 arg0 = mem::take(args[0usize]).cast::<usize>();
|
||||||
Ok(Dynamic::from(do_something(arg0)))
|
Ok(Dynamic::from(do_something(arg0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
#[inline(always)] fn is_pure(&self) -> bool { true }
|
||||||
|
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||||
}
|
}
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
@ -357,13 +359,14 @@ mod generate_tests {
|
|||||||
}
|
}
|
||||||
impl PluginFunction for Token {
|
impl PluginFunction for Token {
|
||||||
#[inline(always)]
|
#[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 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_method_call(&self) -> bool { false }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
#[inline(always)] fn is_pure(&self) -> bool { true }
|
||||||
|
#[inline(always)] fn has_context(&self) -> bool { true }
|
||||||
}
|
}
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
@ -399,12 +402,13 @@ mod generate_tests {
|
|||||||
}
|
}
|
||||||
impl PluginFunction for Token {
|
impl PluginFunction for Token {
|
||||||
#[inline(always)]
|
#[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()))
|
Ok(Dynamic::from(return_dynamic()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
#[inline(always)] fn is_pure(&self) -> bool { true }
|
||||||
|
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||||
}
|
}
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
@ -432,13 +436,14 @@ mod generate_tests {
|
|||||||
}
|
}
|
||||||
impl PluginFunction for TestStruct {
|
impl PluginFunction for TestStruct {
|
||||||
#[inline(always)]
|
#[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 arg0 = mem::take(args[0usize]).cast::<usize>();
|
||||||
Ok(Dynamic::from(do_something(arg0)))
|
Ok(Dynamic::from(do_something(arg0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
#[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 {
|
impl PluginFunction for Token {
|
||||||
#[inline(always)]
|
#[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 arg0 = mem::take(args[0usize]).cast::<usize>();
|
||||||
let arg1 = mem::take(args[1usize]).cast::<usize>();
|
let arg1 = mem::take(args[1usize]).cast::<usize>();
|
||||||
Ok(Dynamic::from(add_together(arg0, arg1)))
|
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_method_call(&self) -> bool { false }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
#[inline(always)] fn is_pure(&self) -> bool { true }
|
||||||
|
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||||
}
|
}
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
@ -505,7 +511,7 @@ mod generate_tests {
|
|||||||
}
|
}
|
||||||
impl PluginFunction for Token {
|
impl PluginFunction for Token {
|
||||||
#[inline(always)]
|
#[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 arg1 = mem::take(args[1usize]).cast::<usize>();
|
||||||
let arg0 = &mut args[0usize].write_lock::<usize>().unwrap();
|
let arg0 = &mut args[0usize].write_lock::<usize>().unwrap();
|
||||||
Ok(Dynamic::from(increment(arg0, arg1)))
|
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_method_call(&self) -> bool { true }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { false }
|
#[inline(always)] fn is_pure(&self) -> bool { false }
|
||||||
|
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||||
}
|
}
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
@ -546,13 +553,14 @@ mod generate_tests {
|
|||||||
}
|
}
|
||||||
impl PluginFunction for Token {
|
impl PluginFunction for Token {
|
||||||
#[inline(always)]
|
#[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();
|
let arg0 = mem::take(args[0usize]).into_immutable_string().unwrap();
|
||||||
Ok(Dynamic::from(special_print(&arg0)))
|
Ok(Dynamic::from(special_print(&arg0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
#[inline(always)] fn is_pure(&self) -> bool { true }
|
||||||
|
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||||
}
|
}
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
@ -92,10 +92,11 @@ mod module_tests {
|
|||||||
.cloned()
|
.cloned()
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
vec![
|
vec![
|
||||||
"/// This is a doc-comment.",
|
"\
|
||||||
"/// Another line.",
|
/// This is a doc-comment.\n\
|
||||||
"/// block doc-comment ",
|
/// Another line.\n\
|
||||||
"/// Final line.",
|
/// block doc-comment \n\
|
||||||
|
/// Final line.",
|
||||||
"/** doc-comment\n in multiple lines\n */"
|
"/** doc-comment\n in multiple lines\n */"
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
@ -385,12 +386,13 @@ mod generate_tests {
|
|||||||
}
|
}
|
||||||
impl PluginFunction for get_mystic_number_token {
|
impl PluginFunction for get_mystic_number_token {
|
||||||
#[inline(always)]
|
#[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()))
|
Ok(Dynamic::from(get_mystic_number()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
#[inline(always)] fn is_pure(&self) -> bool { true }
|
||||||
|
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -402,6 +404,12 @@ mod generate_tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn one_factory_fn_with_comments_module() {
|
fn one_factory_fn_with_comments_module() {
|
||||||
let input_tokens: TokenStream = quote! {
|
let input_tokens: TokenStream = quote! {
|
||||||
|
/// This is the one_fn module!
|
||||||
|
/** block doc-comment
|
||||||
|
* multi-line
|
||||||
|
*/
|
||||||
|
/// Another line!
|
||||||
|
/// Final line!
|
||||||
pub mod one_fn {
|
pub mod one_fn {
|
||||||
/// This is a doc-comment.
|
/// This is a doc-comment.
|
||||||
/// Another line.
|
/// Another line.
|
||||||
@ -418,6 +426,12 @@ mod generate_tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let expected_tokens = quote! {
|
let expected_tokens = quote! {
|
||||||
|
/// This is the one_fn module!
|
||||||
|
/** block doc-comment
|
||||||
|
* multi-line
|
||||||
|
*/
|
||||||
|
/// Another line!
|
||||||
|
/// Final line!
|
||||||
pub mod one_fn {
|
pub mod one_fn {
|
||||||
/// This is a doc-comment.
|
/// This is a doc-comment.
|
||||||
/// Another line.
|
/// Another line.
|
||||||
@ -436,6 +450,7 @@ mod generate_tests {
|
|||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn rhai_module_generate() -> Module {
|
pub fn rhai_module_generate() -> Module {
|
||||||
let mut m = Module::new();
|
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);
|
rhai_generate_into_module(&mut m, false);
|
||||||
m.build_index();
|
m.build_index();
|
||||||
m
|
m
|
||||||
@ -444,11 +459,8 @@ mod generate_tests {
|
|||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
|
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
|
||||||
m.set_fn_with_comments("get_mystic_number", FnNamespace::Internal, FnAccess::Public,
|
m.set_fn_with_comments("get_mystic_number", FnNamespace::Internal, FnAccess::Public,
|
||||||
Some(get_mystic_number_token::PARAM_NAMES), &[], &[
|
Some(get_mystic_number_token::PARAM_NAMES), [], [
|
||||||
"/// This is a doc-comment.",
|
"/// This is a doc-comment.\n/// Another line.\n/// block doc-comment \n/// Final line.",
|
||||||
"/// Another line.",
|
|
||||||
"/// block doc-comment ",
|
|
||||||
"/// Final line.",
|
|
||||||
"/** doc-comment\n in multiple lines\n */"
|
"/** doc-comment\n in multiple lines\n */"
|
||||||
], get_mystic_number_token().into());
|
], get_mystic_number_token().into());
|
||||||
if flatten {} else {}
|
if flatten {} else {}
|
||||||
@ -463,12 +475,13 @@ mod generate_tests {
|
|||||||
}
|
}
|
||||||
impl PluginFunction for get_mystic_number_token {
|
impl PluginFunction for get_mystic_number_token {
|
||||||
#[inline(always)]
|
#[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()))
|
Ok(Dynamic::from(get_mystic_number()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
#[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 {
|
impl PluginFunction for add_one_to_token {
|
||||||
#[inline(always)]
|
#[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 arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||||
Ok(Dynamic::from(add_one_to(arg0)))
|
Ok(Dynamic::from(add_one_to(arg0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
#[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 {
|
impl PluginFunction for add_one_to_token {
|
||||||
#[inline(always)]
|
#[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 arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||||
Ok(Dynamic::from(add_one_to(arg0)))
|
Ok(Dynamic::from(add_one_to(arg0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
#[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 {
|
impl PluginFunction for add_one_to_token {
|
||||||
#[inline(always)]
|
#[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 arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||||
Ok(Dynamic::from(add_one_to(arg0)))
|
Ok(Dynamic::from(add_one_to(arg0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
#[inline(always)] fn is_pure(&self) -> bool { true }
|
||||||
|
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
@ -670,7 +686,7 @@ mod generate_tests {
|
|||||||
}
|
}
|
||||||
impl PluginFunction for add_n_to_token {
|
impl PluginFunction for add_n_to_token {
|
||||||
#[inline(always)]
|
#[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 arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||||
let arg1 = mem::take(args[1usize]).cast::<INT>();
|
let arg1 = mem::take(args[1usize]).cast::<INT>();
|
||||||
Ok(Dynamic::from(add_n_to(arg0, arg1)))
|
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_method_call(&self) -> bool { false }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
#[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 {
|
impl PluginFunction for add_together_token {
|
||||||
#[inline(always)]
|
#[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 arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||||
let arg1 = mem::take(args[1usize]).cast::<INT>();
|
let arg1 = mem::take(args[1usize]).cast::<INT>();
|
||||||
Ok(Dynamic::from(add_together(arg0, arg1)))
|
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_method_call(&self) -> bool { false }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
#[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 {
|
impl PluginFunction for add_together_token {
|
||||||
#[inline(always)]
|
#[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 arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||||
let arg1 = mem::take(args[1usize]).cast::<INT>();
|
let arg1 = mem::take(args[1usize]).cast::<INT>();
|
||||||
Ok(Dynamic::from(add_together(arg0, arg1)))
|
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_method_call(&self) -> bool { false }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
#[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 {
|
impl PluginFunction for get_mystic_number_token {
|
||||||
#[inline(always)]
|
#[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();
|
let arg0 = &mut args[0usize].write_lock::<Hello>().unwrap();
|
||||||
Ok(Dynamic::from(get_mystic_number(arg0)))
|
Ok(Dynamic::from(get_mystic_number(arg0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { false }
|
#[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 {
|
impl PluginFunction for get_mystic_number_token {
|
||||||
#[inline(always)]
|
#[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()))
|
Ok(Dynamic::from(get_mystic_number()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
#[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 {
|
impl PluginFunction for print_out_to_token {
|
||||||
#[inline(always)]
|
#[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();
|
let arg0 = mem::take(args[0usize]).into_immutable_string().unwrap();
|
||||||
Ok(Dynamic::from(print_out_to(&arg0)))
|
Ok(Dynamic::from(print_out_to(&arg0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
#[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 {
|
impl PluginFunction for print_out_to_token {
|
||||||
#[inline(always)]
|
#[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();
|
let arg0 = mem::take(args[0usize]).into_string().unwrap();
|
||||||
Ok(Dynamic::from(print_out_to(arg0)))
|
Ok(Dynamic::from(print_out_to(arg0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
#[inline(always)] fn is_method_call(&self) -> bool { false }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { true }
|
#[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 {
|
impl PluginFunction for foo_token {
|
||||||
#[inline(always)]
|
#[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 arg1 = mem::take(args[1usize]).cast::<INT>();
|
||||||
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
|
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
|
||||||
Ok(Dynamic::from(foo(arg0, arg1)))
|
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_method_call(&self) -> bool { true }
|
||||||
#[inline(always)] fn is_pure(&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 {
|
impl PluginFunction for increment_token {
|
||||||
#[inline(always)]
|
#[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();
|
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
|
||||||
Ok(Dynamic::from(increment(arg0)))
|
Ok(Dynamic::from(increment(arg0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { false }
|
#[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 {
|
impl PluginFunction for increment_token {
|
||||||
#[inline(always)]
|
#[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();
|
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
|
||||||
Ok(Dynamic::from(increment(arg0)))
|
Ok(Dynamic::from(increment(arg0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { false }
|
#[inline(always)] fn is_pure(&self) -> bool { false }
|
||||||
|
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
@ -1492,13 +1518,14 @@ mod generate_tests {
|
|||||||
}
|
}
|
||||||
impl PluginFunction for increment_token {
|
impl PluginFunction for increment_token {
|
||||||
#[inline(always)]
|
#[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();
|
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
|
||||||
Ok(Dynamic::from(increment(arg0)))
|
Ok(Dynamic::from(increment(arg0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { false }
|
#[inline(always)] fn is_pure(&self) -> bool { false }
|
||||||
|
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
@ -1577,13 +1604,14 @@ mod generate_tests {
|
|||||||
}
|
}
|
||||||
impl PluginFunction for int_foo_token {
|
impl PluginFunction for int_foo_token {
|
||||||
#[inline(always)]
|
#[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();
|
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
|
||||||
Ok(Dynamic::from(int_foo(arg0)))
|
Ok(Dynamic::from(int_foo(arg0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { false }
|
#[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 {
|
impl PluginFunction for int_foo_token {
|
||||||
#[inline(always)]
|
#[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();
|
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
|
||||||
Ok(Dynamic::from(int_foo(arg0)))
|
Ok(Dynamic::from(int_foo(arg0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { false }
|
#[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 {
|
impl PluginFunction for int_foo_token {
|
||||||
#[inline(always)]
|
#[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 arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
|
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
|
||||||
Ok(Dynamic::from(int_foo(arg0, arg1)))
|
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_method_call(&self) -> bool { true }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { false }
|
#[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 {
|
impl PluginFunction for int_foo_token {
|
||||||
#[inline(always)]
|
#[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 arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
|
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
|
||||||
Ok(Dynamic::from(int_foo(arg0, arg1)))
|
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_method_call(&self) -> bool { true }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { false }
|
#[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 {
|
impl PluginFunction for get_by_index_token {
|
||||||
#[inline(always)]
|
#[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 arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
let arg0 = &mut args[0usize].write_lock::<MyCollection>().unwrap();
|
let arg0 = &mut args[0usize].write_lock::<MyCollection>().unwrap();
|
||||||
Ok(Dynamic::from(get_by_index(arg0, arg1)))
|
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_method_call(&self) -> bool { true }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { false }
|
#[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)]
|
#[cfg(hello)]
|
||||||
impl PluginFunction for get_by_index_token {
|
impl PluginFunction for get_by_index_token {
|
||||||
#[inline(always)]
|
#[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 arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
let arg0 = &mut args[0usize].write_lock::<MyCollection>().unwrap();
|
let arg0 = &mut args[0usize].write_lock::<MyCollection>().unwrap();
|
||||||
Ok(Dynamic::from(get_by_index(arg0, arg1)))
|
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_method_call(&self) -> bool { true }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { false }
|
#[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 {
|
impl PluginFunction for get_by_index_token {
|
||||||
#[inline(always)]
|
#[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 arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
let arg0 = &mut args[0usize].write_lock::<MyCollection>().unwrap();
|
let arg0 = &mut args[0usize].write_lock::<MyCollection>().unwrap();
|
||||||
Ok(Dynamic::from(get_by_index(arg0, arg1)))
|
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_method_call(&self) -> bool { true }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { false }
|
#[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 {
|
impl PluginFunction for set_by_index_token {
|
||||||
#[inline(always)]
|
#[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 arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
let arg2 = mem::take(args[2usize]).cast::<FLOAT>();
|
let arg2 = mem::take(args[2usize]).cast::<FLOAT>();
|
||||||
let arg0 = &mut args[0usize].write_lock::<MyCollection>().unwrap();
|
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_method_call(&self) -> bool { true }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { false }
|
#[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 {
|
impl PluginFunction for set_by_index_token {
|
||||||
#[inline(always)]
|
#[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 arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
let arg2 = mem::take(args[2usize]).cast::<FLOAT>();
|
let arg2 = mem::take(args[2usize]).cast::<FLOAT>();
|
||||||
let arg0 = &mut args[0usize].write_lock::<MyCollection>().unwrap();
|
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_method_call(&self) -> bool { true }
|
||||||
#[inline(always)] fn is_pure(&self) -> bool { false }
|
#[inline(always)] fn is_pure(&self) -> bool { false }
|
||||||
|
#[inline(always)] fn has_context(&self) -> bool { false }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
fn init() {
|
fn init() {
|
||||||
// Can detect system-provided default states!
|
// Can detect system-provided default states!
|
||||||
// Add 'bool_state' as new state variable if one does not exist
|
// 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;
|
this.bool_state = false;
|
||||||
}
|
}
|
||||||
// Add 'value' as new state variable (overwrites any existing)
|
// Add 'value' as new state variable (overwrites any existing)
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
/// State is stored inside an object map bound to 'state'.
|
/// State is stored inside an object map bound to 'state'.
|
||||||
fn init() {
|
fn init() {
|
||||||
// Add 'bool_state' as new state variable if one does not exist
|
// 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;
|
state.bool_state = false;
|
||||||
}
|
}
|
||||||
// Add 'obj_state' as new state variable (overwrites any existing)
|
// Add 'obj_state' as new state variable (overwrites any existing)
|
||||||
@ -37,7 +37,7 @@ fn start(data) {
|
|||||||
|
|
||||||
/// 'end' event handler
|
/// 'end' event handler
|
||||||
fn end(data) {
|
fn end(data) {
|
||||||
if !state.bool_state || !("start_mode" in state) {
|
if !state.bool_state || "start_mode" !in state {
|
||||||
throw "Not yet started!";
|
throw "Not yet started!";
|
||||||
}
|
}
|
||||||
if state.value > 0 {
|
if state.value > 0 {
|
||||||
|
@ -88,6 +88,7 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// To define a pretty-print name, call [`with_name`][`TypeBuilder::with_name`],
|
/// To define a pretty-print name, call [`with_name`][`TypeBuilder::with_name`],
|
||||||
/// to use [`Engine::register_type_with_name`] instead.
|
/// to use [`Engine::register_type_with_name`] instead.
|
||||||
|
#[must_use]
|
||||||
pub struct TypeBuilder<'a, T: Variant + Clone> {
|
pub struct TypeBuilder<'a, T: Variant + Clone> {
|
||||||
engine: &'a mut Engine,
|
engine: &'a mut Engine,
|
||||||
name: Option<&'static str>,
|
name: Option<&'static str>,
|
||||||
@ -101,7 +102,7 @@ impl<'a, T: Variant + Clone> TypeBuilder<'a, T> {
|
|||||||
Self {
|
Self {
|
||||||
engine,
|
engine,
|
||||||
name: None,
|
name: None,
|
||||||
_marker: PhantomData::default(),
|
_marker: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -116,10 +117,10 @@ impl<'a, T: Variant + Clone> TypeBuilder<'a, T> {
|
|||||||
|
|
||||||
/// Register a custom function.
|
/// Register a custom function.
|
||||||
#[inline(always)]
|
#[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,
|
&mut self,
|
||||||
name: impl AsRef<str> + Into<Identifier>,
|
name: impl AsRef<str> + Into<Identifier>,
|
||||||
method: impl RegisterNativeFunction<A, R, S>,
|
method: impl RegisterNativeFunction<A, N, C, R, L>,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.engine.register_fn(name, method);
|
self.engine.register_fn(name, method);
|
||||||
self
|
self
|
||||||
@ -148,10 +149,10 @@ impl<'a, T: Variant + Clone> TypeBuilder<'a, T> {
|
|||||||
///
|
///
|
||||||
/// Not available under `no_object`.
|
/// Not available under `no_object`.
|
||||||
#[inline(always)]
|
#[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,
|
&mut self,
|
||||||
name: impl AsRef<str>,
|
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 {
|
) -> &mut Self {
|
||||||
self.engine.register_get(name, get_fn);
|
self.engine.register_get(name, get_fn);
|
||||||
self
|
self
|
||||||
@ -161,10 +162,10 @@ impl<'a, T: Variant + Clone> TypeBuilder<'a, T> {
|
|||||||
///
|
///
|
||||||
/// Not available under `no_object`.
|
/// Not available under `no_object`.
|
||||||
#[inline(always)]
|
#[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,
|
&mut self,
|
||||||
name: impl AsRef<str>,
|
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 {
|
) -> &mut Self {
|
||||||
self.engine.register_set(name, set_fn);
|
self.engine.register_set(name, set_fn);
|
||||||
self
|
self
|
||||||
@ -176,11 +177,19 @@ impl<'a, T: Variant + Clone> TypeBuilder<'a, T> {
|
|||||||
///
|
///
|
||||||
/// Not available under `no_object`.
|
/// Not available under `no_object`.
|
||||||
#[inline(always)]
|
#[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,
|
&mut self,
|
||||||
name: impl AsRef<str>,
|
name: impl AsRef<str>,
|
||||||
get_fn: impl RegisterNativeFunction<(Mut<T>,), V, S1> + 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), (), S2> + crate::func::SendSync + 'static,
|
set_fn: impl RegisterNativeFunction<(Mut<T>, V), 2, C2, (), L2>
|
||||||
|
+ crate::func::SendSync
|
||||||
|
+ 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.engine.register_get_set(name, get_fn, set_fn);
|
self.engine.register_get_set(name, get_fn, set_fn);
|
||||||
self
|
self
|
||||||
@ -195,9 +204,14 @@ impl<'a, T: Variant + Clone> TypeBuilder<'a, T> {
|
|||||||
///
|
///
|
||||||
/// Not available under both `no_index` and `no_object`.
|
/// Not available under both `no_index` and `no_object`.
|
||||||
#[inline(always)]
|
#[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,
|
&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 {
|
) -> &mut Self {
|
||||||
self.engine.register_indexer_get(get_fn);
|
self.engine.register_indexer_get(get_fn);
|
||||||
self
|
self
|
||||||
@ -207,9 +221,16 @@ impl<'a, T: Variant + Clone> TypeBuilder<'a, T> {
|
|||||||
///
|
///
|
||||||
/// Not available under both `no_index` and `no_object`.
|
/// Not available under both `no_index` and `no_object`.
|
||||||
#[inline(always)]
|
#[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,
|
&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 {
|
) -> &mut Self {
|
||||||
self.engine.register_indexer_set(set_fn);
|
self.engine.register_indexer_set(set_fn);
|
||||||
self
|
self
|
||||||
@ -219,10 +240,19 @@ impl<'a, T: Variant + Clone> TypeBuilder<'a, T> {
|
|||||||
///
|
///
|
||||||
/// Not available under both `no_index` and `no_object`.
|
/// Not available under both `no_index` and `no_object`.
|
||||||
#[inline(always)]
|
#[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,
|
&mut self,
|
||||||
get_fn: impl RegisterNativeFunction<(Mut<T>, X), V, S1> + 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), (), S2> + crate::func::SendSync + 'static,
|
set_fn: impl RegisterNativeFunction<(Mut<T>, X, V), 3, C2, (), L2>
|
||||||
|
+ crate::func::SendSync
|
||||||
|
+ 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.engine.register_indexer_get_set(get_fn, set_fn);
|
self.engine.register_indexer_get_set(get_fn, set_fn);
|
||||||
self
|
self
|
||||||
|
@ -3,10 +3,8 @@
|
|||||||
|
|
||||||
use crate::eval::{Caches, GlobalRuntimeState};
|
use crate::eval::{Caches, GlobalRuntimeState};
|
||||||
use crate::types::dynamic::Variant;
|
use crate::types::dynamic::Variant;
|
||||||
use crate::types::RestoreOnDrop;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
reify, Dynamic, Engine, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, AST,
|
Dynamic, Engine, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, AST, ERR,
|
||||||
ERR,
|
|
||||||
};
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -81,7 +79,7 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// The [`AST`] is evaluated before calling the function.
|
/// The [`AST`] is evaluated before calling the function.
|
||||||
/// This allows a script to load the necessary modules.
|
/// 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
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -240,10 +238,10 @@ impl Engine {
|
|||||||
let rewind_scope = options.rewind_scope;
|
let rewind_scope = options.rewind_scope;
|
||||||
|
|
||||||
let result = if options.eval_ast && !statements.is_empty() {
|
let result = if options.eval_ast && !statements.is_empty() {
|
||||||
let orig_scope_len = scope.len();
|
auto_restore! {
|
||||||
let scope = &mut *RestoreOnDrop::lock_if(rewind_scope, scope, move |s| {
|
scope if rewind_scope => rewind;
|
||||||
s.rewind(orig_scope_len);
|
let orig_scope_len = scope.len();
|
||||||
});
|
}
|
||||||
|
|
||||||
self.eval_global_statements(global, caches, scope, statements)
|
self.eval_global_statements(global, caches, scope, statements)
|
||||||
} else {
|
} else {
|
||||||
@ -254,7 +252,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Check for data race.
|
// Check for data race.
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[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()
|
ast.shared_lib()
|
||||||
.get_script_fn(name, args.len())
|
.get_script_fn(name, args.len())
|
||||||
@ -276,7 +274,7 @@ impl Engine {
|
|||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
if self.debugger.is_some() {
|
if self.is_debugger_registered() {
|
||||||
global.debugger_mut().status = crate::eval::DebuggerStatus::Terminate;
|
global.debugger_mut().status = crate::eval::DebuggerStatus::Terminate;
|
||||||
let node = &crate::ast::Stmt::Noop(Position::NONE);
|
let node = &crate::ast::Stmt::Noop(Position::NONE);
|
||||||
self.run_debugger(global, caches, scope, this_ptr, node)?;
|
self.run_debugger(global, caches, scope, this_ptr, node)?;
|
||||||
|
@ -218,15 +218,12 @@ impl Engine {
|
|||||||
scripts: impl AsRef<[S]>,
|
scripts: impl AsRef<[S]>,
|
||||||
optimization_level: OptimizationLevel,
|
optimization_level: OptimizationLevel,
|
||||||
) -> ParseResult<AST> {
|
) -> ParseResult<AST> {
|
||||||
let (stream, tokenizer_control) = self.lex_raw(
|
let (stream, tc) = self.lex_raw(scripts.as_ref(), self.token_mapper.as_deref());
|
||||||
scripts.as_ref(),
|
|
||||||
self.token_mapper.as_ref().map(<_>::as_ref),
|
|
||||||
);
|
|
||||||
let interned_strings = &mut *locked_write(&self.interned_strings);
|
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, tc);
|
||||||
let mut _ast = self.parse(&mut stream.peekable(), &mut state, optimization_level)?;
|
let mut _ast = self.parse(stream.peekable(), state, optimization_level)?;
|
||||||
#[cfg(feature = "metadata")]
|
#[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)
|
Ok(_ast)
|
||||||
}
|
}
|
||||||
/// Compile a string containing an expression into an [`AST`],
|
/// Compile a string containing an expression into an [`AST`],
|
||||||
@ -292,12 +289,9 @@ impl Engine {
|
|||||||
script: impl AsRef<str>,
|
script: impl AsRef<str>,
|
||||||
) -> ParseResult<AST> {
|
) -> ParseResult<AST> {
|
||||||
let scripts = [script];
|
let scripts = [script];
|
||||||
let (stream, tokenizer_control) =
|
let (stream, t) = self.lex_raw(&scripts, self.token_mapper.as_deref());
|
||||||
self.lex_raw(&scripts, self.token_mapper.as_ref().map(<_>::as_ref));
|
|
||||||
|
|
||||||
let mut peekable = stream.peekable();
|
|
||||||
let interned_strings = &mut *locked_write(&self.interned_strings);
|
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, t);
|
||||||
self.parse_global_expr(&mut peekable, &mut state, |_| {}, self.optimization_level)
|
self.parse_global_expr(stream.peekable(), state, |_| {}, self.optimization_level)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,11 @@
|
|||||||
use crate::ast::Expr;
|
use crate::ast::Expr;
|
||||||
use crate::func::SendSync;
|
use crate::func::SendSync;
|
||||||
use crate::parser::ParseResult;
|
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::types::dynamic::Variant;
|
||||||
use crate::{
|
use crate::{
|
||||||
reify, Dynamic, Engine, EvalContext, Identifier, ImmutableString, LexError, Position,
|
Dynamic, Engine, EvalContext, Identifier, ImmutableString, LexError, Position, RhaiResult,
|
||||||
RhaiResult, StaticVec,
|
StaticVec,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -166,6 +166,7 @@ impl Deref for Expression<'_> {
|
|||||||
type Target = Expr;
|
type Target = Expr;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
@ -230,11 +231,11 @@ impl Engine {
|
|||||||
continue;
|
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) {
|
if Token::is_reserved_keyword(s) {
|
||||||
Some(Token::Reserved(Box::new(s.into())))
|
Token::Reserved(Box::new(s.into()))
|
||||||
} else {
|
} else {
|
||||||
None
|
NO_TOKEN
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -255,16 +256,16 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
CUSTOM_SYNTAX_MARKER_FLOAT if !segments.is_empty() => s.into(),
|
CUSTOM_SYNTAX_MARKER_FLOAT if !segments.is_empty() => s.into(),
|
||||||
// Standard or reserved keyword/symbol not in first position
|
// 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
|
// Make it a custom keyword/symbol if it is disabled or reserved
|
||||||
if (self
|
if (self
|
||||||
.disabled_symbols
|
.disabled_symbols
|
||||||
.as_ref()
|
.as_deref()
|
||||||
.map_or(false, |m| m.contains(s))
|
.map_or(false, |m| m.contains(s))
|
||||||
|| token.map_or(false, |v| v.is_reserved()))
|
|| token.is_reserved())
|
||||||
&& !self
|
&& !self
|
||||||
.custom_keywords
|
.custom_keywords
|
||||||
.as_ref()
|
.as_deref()
|
||||||
.map_or(false, |m| m.contains_key(s))
|
.map_or(false, |m| m.contains_key(s))
|
||||||
{
|
{
|
||||||
self.custom_keywords
|
self.custom_keywords
|
||||||
@ -275,10 +276,10 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// Standard keyword in first position but not disabled
|
// Standard keyword in first position but not disabled
|
||||||
_ if segments.is_empty()
|
_ if segments.is_empty()
|
||||||
&& token.as_ref().map_or(false, Token::is_standard_keyword)
|
&& token.is_standard_keyword()
|
||||||
&& !self
|
&& !self
|
||||||
.disabled_symbols
|
.disabled_symbols
|
||||||
.as_ref()
|
.as_deref()
|
||||||
.map_or(false, |m| m.contains(s)) =>
|
.map_or(false, |m| m.contains(s)) =>
|
||||||
{
|
{
|
||||||
return Err(LexError::ImproperSymbol(
|
return Err(LexError::ImproperSymbol(
|
||||||
@ -295,12 +296,12 @@ impl Engine {
|
|||||||
// Make it a custom keyword/symbol if it is disabled or reserved
|
// Make it a custom keyword/symbol if it is disabled or reserved
|
||||||
if self
|
if self
|
||||||
.disabled_symbols
|
.disabled_symbols
|
||||||
.as_ref()
|
.as_deref()
|
||||||
.map_or(false, |m| m.contains(s))
|
.map_or(false, |m| m.contains(s))
|
||||||
|| (token.map_or(false, |v| v.is_reserved())
|
|| (token.is_reserved()
|
||||||
&& !self
|
&& !self
|
||||||
.custom_keywords
|
.custom_keywords
|
||||||
.as_ref()
|
.as_deref()
|
||||||
.map_or(false, |m| m.contains_key(s)))
|
.map_or(false, |m| m.contains_key(s)))
|
||||||
{
|
{
|
||||||
self.custom_keywords
|
self.custom_keywords
|
||||||
|
@ -29,6 +29,7 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
pub fn definitions(&self) -> Definitions {
|
pub fn definitions(&self) -> Definitions {
|
||||||
Definitions {
|
Definitions {
|
||||||
engine: self,
|
engine: self,
|
||||||
@ -55,6 +56,7 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
pub fn definitions_with_scope<'e>(&'e self, scope: &'e Scope<'e>) -> Definitions<'e> {
|
pub fn definitions_with_scope<'e>(&'e self, scope: &'e Scope<'e>) -> Definitions<'e> {
|
||||||
Definitions {
|
Definitions {
|
||||||
engine: self,
|
engine: self,
|
||||||
@ -67,7 +69,6 @@ impl Engine {
|
|||||||
/// Internal configuration for module generation.
|
/// Internal configuration for module generation.
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[must_use]
|
|
||||||
pub struct DefinitionsConfig {
|
pub struct DefinitionsConfig {
|
||||||
/// Write `module ...` headers in definition files (default `false`).
|
/// Write `module ...` headers in definition files (default `false`).
|
||||||
pub write_headers: bool,
|
pub write_headers: bool,
|
||||||
@ -77,6 +78,7 @@ pub struct DefinitionsConfig {
|
|||||||
|
|
||||||
impl Default for DefinitionsConfig {
|
impl Default for DefinitionsConfig {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
write_headers: false,
|
write_headers: false,
|
||||||
@ -89,7 +91,6 @@ impl Default for DefinitionsConfig {
|
|||||||
/// contents of an [`Engine`].
|
/// contents of an [`Engine`].
|
||||||
/// Exported under the `internals` and `metadata` feature only.
|
/// Exported under the `internals` and `metadata` feature only.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[must_use]
|
|
||||||
pub struct Definitions<'e> {
|
pub struct Definitions<'e> {
|
||||||
/// The [`Engine`].
|
/// The [`Engine`].
|
||||||
engine: &'e 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
|
/// Headers are always present in content that is expected to be written to a file
|
||||||
/// (i.e. `write_to*` and `*_file` methods).
|
/// (i.e. `write_to*` and `*_file` methods).
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
pub const fn with_headers(mut self, headers: bool) -> Self {
|
pub const fn with_headers(mut self, headers: bool) -> Self {
|
||||||
self.config.write_headers = headers;
|
self.config.write_headers = headers;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Include standard packages when writing definition files.
|
/// Include standard packages when writing definition files.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
pub const fn include_standard_packages(mut self, include_standard_packages: bool) -> Self {
|
pub const fn include_standard_packages(mut self, include_standard_packages: bool) -> Self {
|
||||||
self.config.include_standard_packages = include_standard_packages;
|
self.config.include_standard_packages = include_standard_packages;
|
||||||
self
|
self
|
||||||
@ -128,6 +131,7 @@ impl Definitions<'_> {
|
|||||||
}
|
}
|
||||||
/// Get the configuration.
|
/// Get the configuration.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
pub(crate) const fn config(&self) -> &DefinitionsConfig {
|
pub(crate) const fn config(&self) -> &DefinitionsConfig {
|
||||||
&self.config
|
&self.config
|
||||||
}
|
}
|
||||||
@ -368,8 +372,9 @@ impl Definitions<'_> {
|
|||||||
let mut m = self
|
let mut m = self
|
||||||
.engine
|
.engine
|
||||||
.global_sub_modules
|
.global_sub_modules
|
||||||
.iter()
|
.as_deref()
|
||||||
.flat_map(|m| m.iter())
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
.map(move |(name, module)| {
|
.map(move |(name, module)| {
|
||||||
(
|
(
|
||||||
name.to_string(),
|
name.to_string(),
|
||||||
@ -391,6 +396,7 @@ impl Definitions<'_> {
|
|||||||
impl Module {
|
impl Module {
|
||||||
/// Return definitions for all items inside the [`Module`].
|
/// Return definitions for all items inside the [`Module`].
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
#[must_use]
|
||||||
fn definition(&self, def: &Definitions) -> String {
|
fn definition(&self, def: &Definitions) -> String {
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
self.write_definition(&mut s, def).unwrap();
|
self.write_definition(&mut s, def).unwrap();
|
||||||
@ -455,7 +461,7 @@ impl Module {
|
|||||||
|| def
|
|| def
|
||||||
.engine
|
.engine
|
||||||
.custom_keywords
|
.custom_keywords
|
||||||
.as_ref()
|
.as_deref()
|
||||||
.map_or(false, |m| m.contains_key(f.metadata.name.as_str()));
|
.map_or(false, |m| m.contains_key(f.metadata.name.as_str()));
|
||||||
|
|
||||||
f.write_definition(writer, def, operator)?;
|
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.
|
/// It tries to flatten types, removing `&` and `&mut`, and paths, while keeping generics.
|
||||||
///
|
///
|
||||||
/// Associated generic types are also rewritten into regular generic type parameters.
|
/// 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> {
|
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 = engine.format_type_name(ty).replace("crate::", "");
|
||||||
let ty = ty.strip_prefix("&mut").unwrap_or(&*ty).trim();
|
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 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")]
|
#[deprecated(since = "1.1.0", note = "use `run_file` instead")]
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
#[cfg(not(target_family = "wasm"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
@ -137,12 +137,6 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
/// 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`.
|
/// Not available under `no_function`.
|
||||||
///
|
///
|
||||||
/// # Deprecated
|
/// # Deprecated
|
||||||
@ -191,10 +185,10 @@ impl Engine {
|
|||||||
/// This method will be removed in the next major version.
|
/// This method will be removed in the next major version.
|
||||||
#[deprecated(since = "1.9.1", note = "use `register_fn` instead")]
|
#[deprecated(since = "1.9.1", note = "use `register_fn` instead")]
|
||||||
#[inline(always)]
|
#[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,
|
&mut self,
|
||||||
name: impl AsRef<str> + Into<Identifier>,
|
name: impl AsRef<str> + Into<Identifier>,
|
||||||
func: impl RegisterNativeFunction<A, R, RhaiResultOf<S>>,
|
func: impl RegisterNativeFunction<A, N, C, R, true>,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.register_fn(name, func)
|
self.register_fn(name, func)
|
||||||
}
|
}
|
||||||
@ -212,12 +206,10 @@ impl Engine {
|
|||||||
#[deprecated(since = "1.9.1", note = "use `register_get` instead")]
|
#[deprecated(since = "1.9.1", note = "use `register_get` instead")]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[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,
|
&mut self,
|
||||||
name: impl AsRef<str>,
|
name: impl AsRef<str>,
|
||||||
get_fn: impl RegisterNativeFunction<(Mut<T>,), V, RhaiResultOf<S>>
|
get_fn: impl RegisterNativeFunction<(Mut<T>,), 1, C, V, true> + crate::func::SendSync + 'static,
|
||||||
+ crate::func::SendSync
|
|
||||||
+ 'static,
|
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.register_get(name, get_fn)
|
self.register_get(name, get_fn)
|
||||||
}
|
}
|
||||||
@ -233,10 +225,10 @@ impl Engine {
|
|||||||
#[deprecated(since = "1.9.1", note = "use `register_set` instead")]
|
#[deprecated(since = "1.9.1", note = "use `register_set` instead")]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[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,
|
&mut self,
|
||||||
name: impl AsRef<str>,
|
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
|
+ crate::func::SendSync
|
||||||
+ 'static,
|
+ 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
@ -253,12 +245,6 @@ impl Engine {
|
|||||||
/// This method is deprecated. Use [`register_indexer_get`][Engine::register_indexer_get] instead.
|
/// This method is deprecated. Use [`register_indexer_get`][Engine::register_indexer_get] instead.
|
||||||
///
|
///
|
||||||
/// This method will be removed in the next major version.
|
/// 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")]
|
#[deprecated(since = "1.9.1", note = "use `register_indexer_get` instead")]
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -266,10 +252,10 @@ impl Engine {
|
|||||||
T: Variant + Clone,
|
T: Variant + Clone,
|
||||||
X: Variant + Clone,
|
X: Variant + Clone,
|
||||||
V: Variant + Clone,
|
V: Variant + Clone,
|
||||||
S,
|
const C: bool,
|
||||||
>(
|
>(
|
||||||
&mut self,
|
&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
|
+ crate::func::SendSync
|
||||||
+ 'static,
|
+ 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
@ -284,12 +270,6 @@ impl Engine {
|
|||||||
/// This method is deprecated. Use [`register_indexer_set`][Engine::register_indexer_set] instead.
|
/// This method is deprecated. Use [`register_indexer_set`][Engine::register_indexer_set] instead.
|
||||||
///
|
///
|
||||||
/// This method will be removed in the next major version.
|
/// 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")]
|
#[deprecated(since = "1.9.1", note = "use `register_indexer_set` instead")]
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -297,10 +277,10 @@ impl Engine {
|
|||||||
T: Variant + Clone,
|
T: Variant + Clone,
|
||||||
X: Variant + Clone,
|
X: Variant + Clone,
|
||||||
V: Variant + Clone,
|
V: Variant + Clone,
|
||||||
S,
|
const C: bool,
|
||||||
>(
|
>(
|
||||||
&mut self,
|
&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
|
+ crate::func::SendSync
|
||||||
+ 'static,
|
+ 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
@ -316,34 +296,6 @@ impl Engine {
|
|||||||
/// Use [`register_custom_syntax_with_state_raw`][Engine::register_custom_syntax_with_state_raw] instead.
|
/// 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.
|
/// 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(
|
#[deprecated(
|
||||||
since = "1.11.0",
|
since = "1.11.0",
|
||||||
note = "use `register_custom_syntax_with_state_raw` instead"
|
note = "use `register_custom_syntax_with_state_raw` instead"
|
||||||
@ -368,6 +320,24 @@ impl Engine {
|
|||||||
move |context, expressions, _| func(context, expressions),
|
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 {
|
impl Dynamic {
|
||||||
@ -544,10 +514,15 @@ impl<'a, T: Variant + Clone> TypeBuilder<'a, T> {
|
|||||||
/// This method will be removed in the next major version.
|
/// This method will be removed in the next major version.
|
||||||
#[deprecated(since = "1.9.1", note = "use `with_fn` instead")]
|
#[deprecated(since = "1.9.1", note = "use `with_fn` instead")]
|
||||||
#[inline(always)]
|
#[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
|
where
|
||||||
N: AsRef<str> + Into<Identifier>,
|
S: AsRef<str> + Into<Identifier>,
|
||||||
F: RegisterNativeFunction<A, R, RhaiResultOf<S>>,
|
R: Variant + Clone,
|
||||||
|
F: RegisterNativeFunction<A, N, C, R, true>,
|
||||||
{
|
{
|
||||||
self.with_fn(name, method)
|
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")]
|
#[deprecated(since = "1.9.1", note = "use `with_get` instead")]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn with_get_result<V: Variant + Clone, S>(
|
pub fn with_get_result<V: Variant + Clone, const C: bool>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl AsRef<str>,
|
name: impl AsRef<str>,
|
||||||
get_fn: impl RegisterNativeFunction<(Mut<T>,), V, RhaiResultOf<S>>
|
get_fn: impl RegisterNativeFunction<(Mut<T>,), 1, C, V, true> + crate::func::SendSync + 'static,
|
||||||
+ crate::func::SendSync
|
|
||||||
+ 'static,
|
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.with_get(name, get_fn)
|
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")]
|
#[deprecated(since = "1.9.1", note = "use `with_set` instead")]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn with_set_result<V: Variant + Clone, S>(
|
pub fn with_set_result<V: Variant + Clone, const C: bool>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl AsRef<str>,
|
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
|
+ crate::func::SendSync
|
||||||
+ 'static,
|
+ 'static,
|
||||||
) -> &mut Self {
|
) -> &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")]
|
#[deprecated(since = "1.9.1", note = "use `with_indexer_get` instead")]
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[inline(always)]
|
#[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,
|
&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
|
+ crate::func::SendSync
|
||||||
+ 'static,
|
+ 'static,
|
||||||
) -> &mut Self {
|
) -> &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")]
|
#[deprecated(since = "1.9.1", note = "use `with_indexer_set` instead")]
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[inline(always)]
|
#[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,
|
&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
|
+ crate::func::SendSync
|
||||||
+ 'static,
|
+ 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
|
@ -5,7 +5,7 @@ use crate::func::native::locked_write;
|
|||||||
use crate::parser::ParseState;
|
use crate::parser::ParseState;
|
||||||
use crate::types::dynamic::Variant;
|
use crate::types::dynamic::Variant;
|
||||||
use crate::{
|
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")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -121,15 +121,14 @@ impl Engine {
|
|||||||
let ast = {
|
let ast = {
|
||||||
let interned_strings = &mut *locked_write(&self.interned_strings);
|
let interned_strings = &mut *locked_write(&self.interned_strings);
|
||||||
|
|
||||||
let (stream, tokenizer_control) =
|
let (stream, tc) = self.lex_raw(&scripts, self.token_mapper.as_deref());
|
||||||
self.lex_raw(&scripts, self.token_mapper.as_ref().map(<_>::as_ref));
|
|
||||||
|
|
||||||
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
|
// No need to optimize a lone expression
|
||||||
self.parse_global_expr(
|
self.parse_global_expr(
|
||||||
&mut stream.peekable(),
|
stream.peekable(),
|
||||||
&mut state,
|
state,
|
||||||
|_| {},
|
|_| {},
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
OptimizationLevel::None,
|
OptimizationLevel::None,
|
||||||
@ -243,7 +242,7 @@ impl Engine {
|
|||||||
let result = self.eval_global_statements(global, caches, scope, statements);
|
let result = self.eval_global_statements(global, caches, scope, statements);
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
if self.debugger.is_some() {
|
if self.is_debugger_registered() {
|
||||||
global.debugger_mut().status = crate::eval::DebuggerStatus::Terminate;
|
global.debugger_mut().status = crate::eval::DebuggerStatus::Terminate;
|
||||||
let mut this = Dynamic::NULL;
|
let mut this = Dynamic::NULL;
|
||||||
let node = &crate::ast::Stmt::Noop(Position::NONE);
|
let node = &crate::ast::Stmt::Noop(Position::NONE);
|
||||||
@ -263,25 +262,6 @@ impl Engine {
|
|||||||
|
|
||||||
result
|
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.
|
/// Evaluate a string as a script, returning the result value or an error.
|
||||||
|
@ -349,7 +349,9 @@ impl Engine {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn register_debugger(
|
pub fn register_debugger(
|
||||||
&mut self,
|
&mut self,
|
||||||
init: impl Fn(&Self) -> Dynamic + SendSync + 'static,
|
init: impl Fn(&Self, crate::debugger::Debugger) -> crate::debugger::Debugger
|
||||||
|
+ SendSync
|
||||||
|
+ 'static,
|
||||||
callback: impl Fn(
|
callback: impl Fn(
|
||||||
EvalContext,
|
EvalContext,
|
||||||
crate::eval::DebuggerEvent,
|
crate::eval::DebuggerEvent,
|
||||||
@ -360,7 +362,7 @@ impl Engine {
|
|||||||
+ SendSync
|
+ SendSync
|
||||||
+ 'static,
|
+ 'static,
|
||||||
) -> &mut Self {
|
) -> &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
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,8 +64,8 @@ impl Engine {
|
|||||||
|
|
||||||
let (stream, tokenizer_control) = self.lex_raw(
|
let (stream, tokenizer_control) = self.lex_raw(
|
||||||
&scripts,
|
&scripts,
|
||||||
if has_null {
|
Some(if has_null {
|
||||||
Some(&|token, _, _| {
|
&|token, _, _| {
|
||||||
match token {
|
match token {
|
||||||
// `null` => `()`
|
// `null` => `()`
|
||||||
Token::Reserved(s) if &*s == "null" => Token::Unit,
|
Token::Reserved(s) if &*s == "null" => Token::Unit,
|
||||||
@ -86,9 +86,9 @@ impl Engine {
|
|||||||
// All others
|
// All others
|
||||||
_ => token,
|
_ => token,
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
} else {
|
} else {
|
||||||
Some(&|token, _, _| {
|
&|token, _, _| {
|
||||||
match token {
|
match token {
|
||||||
Token::Reserved(s) if &*s == "null" => Token::LexError(
|
Token::Reserved(s) if &*s == "null" => Token::LexError(
|
||||||
LexError::ImproperSymbol("null".to_string(), String::new()).into(),
|
LexError::ImproperSymbol("null".to_string(), String::new()).into(),
|
||||||
@ -97,34 +97,31 @@ impl Engine {
|
|||||||
Token::LeftBrace => Token::MapStart,
|
Token::LeftBrace => Token::MapStart,
|
||||||
// Disallowed syntax
|
// Disallowed syntax
|
||||||
t @ (Token::Unit | Token::MapStart) => Token::LexError(
|
t @ (Token::Unit | Token::MapStart) => Token::LexError(
|
||||||
LexError::ImproperSymbol(
|
LexError::ImproperSymbol(t.literal_syntax().to_string(), String::new())
|
||||||
t.literal_syntax().to_string(),
|
.into(),
|
||||||
"Invalid JSON syntax".to_string(),
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
),
|
),
|
||||||
Token::InterpolatedString(..) => Token::LexError(
|
Token::InterpolatedString(..) => Token::LexError(
|
||||||
LexError::ImproperSymbol(
|
LexError::ImproperSymbol(
|
||||||
"interpolated string".to_string(),
|
"interpolated string".to_string(),
|
||||||
"Invalid JSON syntax".to_string(),
|
String::new(),
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
),
|
),
|
||||||
// All others
|
// All others
|
||||||
_ => token,
|
_ => token,
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
},
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
let ast = {
|
let ast = {
|
||||||
let scope = Scope::new();
|
let scope = Scope::new();
|
||||||
let interned_strings = &mut *locked_write(&self.interned_strings);
|
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(
|
self.parse_global_expr(
|
||||||
&mut stream.peekable(),
|
stream.peekable(),
|
||||||
&mut state,
|
state,
|
||||||
|s| s.flags |= ParseSettingFlags::DISALLOW_UNQUOTED_MAP_PROPERTIES,
|
|s| s.flags |= ParseSettingFlags::DISALLOW_UNQUOTED_MAP_PROPERTIES,
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
OptimizationLevel::None,
|
OptimizationLevel::None,
|
||||||
|
@ -6,23 +6,20 @@ use std::num::{NonZeroU64, NonZeroUsize};
|
|||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
pub mod default_limits {
|
pub mod default_limits {
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub const MAX_CALL_STACK_DEPTH: usize = 8;
|
pub const MAX_CALL_STACK_DEPTH: usize = 8;
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
pub const MAX_EXPR_DEPTH: usize = 32;
|
pub const MAX_EXPR_DEPTH: usize = 32;
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
pub const MAX_FUNCTION_EXPR_DEPTH: usize = 16;
|
pub const MAX_FUNCTION_EXPR_DEPTH: usize = 16;
|
||||||
|
}
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
|
pub mod default_limits {
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub const MAX_CALL_STACK_DEPTH: usize = 64;
|
pub const MAX_CALL_STACK_DEPTH: usize = 64;
|
||||||
#[cfg(not(debug_assertions))]
|
|
||||||
pub const MAX_EXPR_DEPTH: usize = 64;
|
pub const MAX_EXPR_DEPTH: usize = 64;
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(not(debug_assertions))]
|
|
||||||
pub const MAX_FUNCTION_EXPR_DEPTH: usize = 32;
|
pub const MAX_FUNCTION_EXPR_DEPTH: usize = 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +52,7 @@ pub struct Limits {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
pub max_modules: usize,
|
pub max_modules: usize,
|
||||||
/// Maximum length of a [string][crate::ImmutableString].
|
/// 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].
|
/// Maximum length of an [array][crate::Array].
|
||||||
///
|
///
|
||||||
/// Not available under `no_index`.
|
/// Not available under `no_index`.
|
||||||
@ -83,7 +80,7 @@ impl Limits {
|
|||||||
max_operations: None,
|
max_operations: None,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
max_modules: usize::MAX,
|
max_modules: usize::MAX,
|
||||||
max_string_size: None,
|
max_string_len: None,
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
max_array_size: None,
|
max_array_size: None,
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
@ -104,7 +101,7 @@ impl Engine {
|
|||||||
/// Is there a data size limit set?
|
/// Is there a data size limit set?
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) const fn has_data_size_limit(&self) -> bool {
|
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"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
{
|
{
|
||||||
@ -222,19 +219,19 @@ impl Engine {
|
|||||||
#[cfg(feature = "no_function")]
|
#[cfg(feature = "no_function")]
|
||||||
return 0;
|
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`.
|
/// Not available under `unchecked`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_max_string_size(&mut self, max_size: usize) -> &mut Self {
|
pub fn set_max_string_size(&mut self, max_len: usize) -> &mut Self {
|
||||||
self.limits.max_string_size = NonZeroUsize::new(max_size);
|
self.limits.max_string_len = NonZeroUsize::new(max_len);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// The maximum length of [strings][crate::ImmutableString] (0 for unlimited).
|
/// The maximum length, in bytes, of [strings][crate::ImmutableString] (0 for unlimited).
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn max_string_size(&self) -> usize {
|
pub const fn max_string_size(&self) -> usize {
|
||||||
match self.limits.max_string_size {
|
match self.limits.max_string_len {
|
||||||
Some(n) => n.get(),
|
Some(n) => n.get(),
|
||||||
None => 0,
|
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.
|
//! Module defining the public API of the Rhai engine.
|
||||||
|
|
||||||
pub mod type_names;
|
|
||||||
|
|
||||||
pub mod eval;
|
pub mod eval;
|
||||||
|
|
||||||
pub mod run;
|
pub mod run;
|
||||||
@ -21,18 +19,21 @@ pub mod options;
|
|||||||
pub mod optimize;
|
pub mod optimize;
|
||||||
|
|
||||||
pub mod limits;
|
pub mod limits;
|
||||||
|
pub mod limits_unchecked;
|
||||||
|
|
||||||
pub mod events;
|
pub mod events;
|
||||||
|
|
||||||
pub mod custom_syntax;
|
pub mod type_names;
|
||||||
|
|
||||||
pub mod deprecated;
|
pub mod custom_syntax;
|
||||||
|
|
||||||
pub mod build_type;
|
pub mod build_type;
|
||||||
|
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
pub mod definitions;
|
pub mod definitions;
|
||||||
|
|
||||||
|
pub mod deprecated;
|
||||||
|
|
||||||
use crate::{Dynamic, Engine, Identifier};
|
use crate::{Dynamic, Engine, Identifier};
|
||||||
|
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
@ -167,7 +168,7 @@ impl Engine {
|
|||||||
Some(token) if token.is_standard_keyword() => {
|
Some(token) if token.is_standard_keyword() => {
|
||||||
if !self
|
if !self
|
||||||
.disabled_symbols
|
.disabled_symbols
|
||||||
.as_ref()
|
.as_deref()
|
||||||
.map_or(false, |m| m.contains(token.literal_syntax()))
|
.map_or(false, |m| m.contains(token.literal_syntax()))
|
||||||
{
|
{
|
||||||
return Err(format!("'{keyword}' is a reserved keyword"));
|
return Err(format!("'{keyword}' is a reserved keyword"));
|
||||||
@ -177,7 +178,7 @@ impl Engine {
|
|||||||
Some(token) if token.is_standard_symbol() => {
|
Some(token) if token.is_standard_symbol() => {
|
||||||
if !self
|
if !self
|
||||||
.disabled_symbols
|
.disabled_symbols
|
||||||
.as_ref()
|
.as_deref()
|
||||||
.map_or(false, |m| m.contains(token.literal_syntax()))
|
.map_or(false, |m| m.contains(token.literal_syntax()))
|
||||||
{
|
{
|
||||||
return Err(format!("'{keyword}' is a reserved operator"));
|
return Err(format!("'{keyword}' is a reserved operator"));
|
||||||
@ -187,7 +188,7 @@ impl Engine {
|
|||||||
Some(token)
|
Some(token)
|
||||||
if !self
|
if !self
|
||||||
.disabled_symbols
|
.disabled_symbols
|
||||||
.as_ref()
|
.as_deref()
|
||||||
.map_or(false, |m| m.contains(token.literal_syntax())) =>
|
.map_or(false, |m| m.contains(token.literal_syntax())) =>
|
||||||
{
|
{
|
||||||
return Err(format!("'{keyword}' is a reserved symbol"))
|
return Err(format!("'{keyword}' is a reserved symbol"))
|
||||||
@ -223,71 +224,3 @@ impl Engine {
|
|||||||
self
|
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 {
|
) -> AST {
|
||||||
let mut ast = ast;
|
let mut ast = ast;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
let mut _new_ast = self.optimize_into_ast(
|
||||||
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,
|
|
||||||
scope,
|
scope,
|
||||||
ast.take_statements(),
|
ast.take_statements(),
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[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,
|
optimization_level,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -56,7 +56,14 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[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,
|
&mut self,
|
||||||
name: impl AsRef<str> + Into<Identifier>,
|
name: impl AsRef<str> + Into<Identifier>,
|
||||||
func: F,
|
func: F,
|
||||||
@ -83,13 +90,24 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "metadata"))]
|
#[cfg(not(feature = "metadata"))]
|
||||||
let param_type_names: Option<&[&str]> = None;
|
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(
|
self.global_namespace_mut().set_fn(
|
||||||
name,
|
name,
|
||||||
FnNamespace::Global,
|
FnNamespace::Global,
|
||||||
FnAccess::Public,
|
FnAccess::Public,
|
||||||
param_type_names,
|
param_type_names,
|
||||||
param_types,
|
param_types,
|
||||||
func.into_callable_function(),
|
func,
|
||||||
);
|
);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -299,10 +317,10 @@ impl Engine {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[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,
|
&mut self,
|
||||||
name: impl AsRef<str>,
|
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 {
|
) -> &mut Self {
|
||||||
self.register_fn(crate::engine::make_getter(name.as_ref()).as_str(), get_fn)
|
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"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[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,
|
&mut self,
|
||||||
name: impl AsRef<str>,
|
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 {
|
) -> &mut Self {
|
||||||
self.register_fn(crate::engine::make_setter(name.as_ref()).as_str(), set_fn)
|
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"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[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,
|
&mut self,
|
||||||
name: impl AsRef<str>,
|
name: impl AsRef<str>,
|
||||||
get_fn: impl RegisterNativeFunction<(Mut<T>,), V, S1> + SendSync + 'static,
|
get_fn: impl RegisterNativeFunction<(Mut<T>,), 1, C1, V, L1> + SendSync + 'static,
|
||||||
set_fn: impl RegisterNativeFunction<(Mut<T>, V), (), S2> + SendSync + 'static,
|
set_fn: impl RegisterNativeFunction<(Mut<T>, V), 2, C2, (), L2> + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.register_get(&name, get_fn).register_set(&name, set_fn)
|
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")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[inline]
|
#[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,
|
&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 {
|
) -> &mut Self {
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<crate::Array>() {
|
if TypeId::of::<T>() == TypeId::of::<crate::Array>() {
|
||||||
@ -537,9 +568,15 @@ impl Engine {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[inline]
|
#[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,
|
&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 {
|
) -> &mut Self {
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<crate::Array>() {
|
if TypeId::of::<T>() == TypeId::of::<crate::Array>() {
|
||||||
@ -616,13 +653,15 @@ impl Engine {
|
|||||||
pub fn register_indexer_get_set<
|
pub fn register_indexer_get_set<
|
||||||
T: Variant + Clone,
|
T: Variant + Clone,
|
||||||
X: Variant + Clone,
|
X: Variant + Clone,
|
||||||
|
const C1: bool,
|
||||||
|
const C2: bool,
|
||||||
V: Variant + Clone,
|
V: Variant + Clone,
|
||||||
S1,
|
const L1: bool,
|
||||||
S2,
|
const L2: bool,
|
||||||
>(
|
>(
|
||||||
&mut self,
|
&mut self,
|
||||||
get_fn: impl RegisterNativeFunction<(Mut<T>, X), V, S1> + SendSync + 'static,
|
get_fn: impl RegisterNativeFunction<(Mut<T>, X), 2, C1, V, L1> + SendSync + 'static,
|
||||||
set_fn: impl RegisterNativeFunction<(Mut<T>, X, V), (), S2> + SendSync + 'static,
|
set_fn: impl RegisterNativeFunction<(Mut<T>, X, V), 3, C2, (), L2> + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.register_indexer_get(get_fn)
|
self.register_indexer_get(get_fn)
|
||||||
.register_indexer_set(set_fn)
|
.register_indexer_set(set_fn)
|
||||||
@ -743,7 +782,7 @@ impl Engine {
|
|||||||
signatures.extend(self.global_namespace().gen_fn_signatures());
|
signatures.extend(self.global_namespace().gen_fn_signatures());
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[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}")));
|
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<()> {
|
pub fn run_with_scope(&self, scope: &mut Scope, script: &str) -> RhaiResultOf<()> {
|
||||||
let scripts = [script];
|
let scripts = [script];
|
||||||
let ast = {
|
let ast = {
|
||||||
|
let (stream, tc) = self.lex_raw(&scripts, self.token_mapper.as_deref());
|
||||||
let interned_strings = &mut *locked_write(&self.interned_strings);
|
let interned_strings = &mut *locked_write(&self.interned_strings);
|
||||||
|
let state = &mut ParseState::new(scope, interned_strings, tc);
|
||||||
let (stream, tokenizer_control) =
|
self.parse(stream.peekable(), state, self.optimization_level)?
|
||||||
self.lex_raw(&scripts, self.token_mapper.as_ref().map(<_>::as_ref));
|
|
||||||
|
|
||||||
let mut state = ParseState::new(scope, interned_strings, tokenizer_control);
|
|
||||||
|
|
||||||
self.parse(&mut stream.peekable(), &mut state, self.optimization_level)?
|
|
||||||
};
|
};
|
||||||
self.run_ast_with_scope(scope, &ast)
|
self.run_ast_with_scope(scope, &ast)
|
||||||
}
|
}
|
||||||
@ -131,19 +127,23 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let statements = ast.statements();
|
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")]
|
#[cfg(feature = "debugging")]
|
||||||
if self.debugger.is_some() {
|
if self.is_debugger_registered() {
|
||||||
global.debugger_mut().status = crate::eval::DebuggerStatus::Terminate;
|
global.debugger_mut().status = crate::eval::DebuggerStatus::Terminate;
|
||||||
let mut this = crate::Dynamic::NULL;
|
let mut this = crate::Dynamic::NULL;
|
||||||
let node = &crate::ast::Stmt::Noop(crate::Position::NONE);
|
let node = &crate::ast::Stmt::Noop(crate::Position::NONE);
|
||||||
self.run_debugger(global, caches, scope, &mut this, node)?;
|
self.run_debugger(global, caches, scope, &mut this, node)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,8 +204,9 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
return self
|
return self
|
||||||
.global_sub_modules
|
.global_sub_modules
|
||||||
.iter()
|
.as_deref()
|
||||||
.flat_map(|m| m.iter())
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
.find_map(|(_, m)| m.get_custom_type(name));
|
.find_map(|(_, m)| m.get_custom_type(name));
|
||||||
#[cfg(feature = "no_module")]
|
#[cfg(feature = "no_module")]
|
||||||
return None;
|
return None;
|
||||||
@ -238,8 +239,9 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
return self
|
return self
|
||||||
.global_sub_modules
|
.global_sub_modules
|
||||||
.iter()
|
.as_deref()
|
||||||
.flat_map(|m| m.iter())
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
.find_map(|(_, m)| m.get_custom_type(name));
|
.find_map(|(_, m)| m.get_custom_type(name));
|
||||||
#[cfg(feature = "no_module")]
|
#[cfg(feature = "no_module")]
|
||||||
return None;
|
return None;
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
//! Module defining script expressions.
|
//! 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::engine::{KEYWORD_FN_PTR, OP_EXCLUSIVE_RANGE, OP_INCLUSIVE_RANGE};
|
||||||
use crate::func::hashing::ALT_ZERO_HASH;
|
use crate::func::hashing::ALT_ZERO_HASH;
|
||||||
use crate::tokenizer::Token;
|
use crate::tokenizer::{Token, NO_TOKEN};
|
||||||
use crate::types::dynamic::Union;
|
use crate::types::dynamic::Union;
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_fn_hash, Dynamic, FnPtr, Identifier, ImmutableString, Position, SmartString, StaticVec,
|
calc_fn_hash, Dynamic, FnPtr, Identifier, ImmutableString, Position, SmartString, StaticVec,
|
||||||
@ -197,8 +197,7 @@ impl FnCallHashes {
|
|||||||
#[derive(Clone, Hash)]
|
#[derive(Clone, Hash)]
|
||||||
pub struct FnCallExpr {
|
pub struct FnCallExpr {
|
||||||
/// Namespace of the function, if any.
|
/// Namespace of the function, if any.
|
||||||
#[cfg(not(feature = "no_module"))]
|
pub namespace: Namespace,
|
||||||
pub namespace: super::Namespace,
|
|
||||||
/// Function name.
|
/// Function name.
|
||||||
pub name: ImmutableString,
|
pub name: ImmutableString,
|
||||||
/// Pre-calculated hashes.
|
/// Pre-calculated hashes.
|
||||||
@ -208,7 +207,8 @@ pub struct FnCallExpr {
|
|||||||
/// Does this function call capture the parent scope?
|
/// Does this function call capture the parent scope?
|
||||||
pub capture_parent_scope: bool,
|
pub capture_parent_scope: bool,
|
||||||
/// Is this function call a native operator?
|
/// 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 {
|
impl fmt::Debug for FnCallExpr {
|
||||||
@ -216,15 +216,14 @@ impl fmt::Debug for FnCallExpr {
|
|||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let mut ff = f.debug_struct("FnCallExpr");
|
let mut ff = f.debug_struct("FnCallExpr");
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
if !self.namespace.is_empty() {
|
if !self.namespace.is_empty() {
|
||||||
ff.field("namespace", &self.namespace);
|
ff.field("namespace", &self.namespace);
|
||||||
}
|
}
|
||||||
ff.field("hash", &self.hashes)
|
ff.field("hash", &self.hashes)
|
||||||
.field("name", &self.name)
|
.field("name", &self.name)
|
||||||
.field("args", &self.args);
|
.field("args", &self.args);
|
||||||
if let Some(ref token) = self.op_token {
|
if self.op_token != NO_TOKEN {
|
||||||
ff.field("op_token", token);
|
ff.field("op_token", &self.op_token);
|
||||||
}
|
}
|
||||||
if self.capture_parent_scope {
|
if self.capture_parent_scope {
|
||||||
ff.field("capture_parent_scope", &self.capture_parent_scope);
|
ff.field("capture_parent_scope", &self.capture_parent_scope);
|
||||||
@ -240,10 +239,7 @@ impl FnCallExpr {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_qualified(&self) -> bool {
|
pub fn is_qualified(&self) -> bool {
|
||||||
#[cfg(not(feature = "no_module"))]
|
!self.namespace.is_empty()
|
||||||
return !self.namespace.is_empty();
|
|
||||||
#[cfg(feature = "no_module")]
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
/// Convert this into an [`Expr::FnCall`].
|
/// Convert this into an [`Expr::FnCall`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -303,9 +299,7 @@ pub enum Expr {
|
|||||||
/// majority of cases (unless there are more than 255 variables defined!).
|
/// majority of cases (unless there are more than 255 variables defined!).
|
||||||
/// This is to avoid reading a pointer redirection during each variable access.
|
/// This is to avoid reading a pointer redirection during each variable access.
|
||||||
Variable(
|
Variable(
|
||||||
#[cfg(not(feature = "no_module"))]
|
Box<(Option<NonZeroUsize>, Namespace, u64, ImmutableString)>,
|
||||||
Box<(Option<NonZeroUsize>, super::Namespace, u64, ImmutableString)>,
|
|
||||||
#[cfg(feature = "no_module")] Box<(Option<NonZeroUsize>, (), u64, ImmutableString)>,
|
|
||||||
Option<NonZeroU8>,
|
Option<NonZeroU8>,
|
||||||
Position,
|
Position,
|
||||||
),
|
),
|
||||||
@ -583,13 +577,12 @@ impl Expr {
|
|||||||
|
|
||||||
Union::FnPtr(f, ..) if !f.is_curried() => Self::FnCall(
|
Union::FnPtr(f, ..) if !f.is_curried() => Self::FnCall(
|
||||||
FnCallExpr {
|
FnCallExpr {
|
||||||
#[cfg(not(feature = "no_module"))]
|
namespace: Namespace::NONE,
|
||||||
namespace: super::Namespace::NONE,
|
|
||||||
name: KEYWORD_FN_PTR.into(),
|
name: KEYWORD_FN_PTR.into(),
|
||||||
hashes: calc_fn_hash(None, f.fn_name(), 1).into(),
|
hashes: calc_fn_hash(None, f.fn_name(), 1).into(),
|
||||||
args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(),
|
args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(),
|
||||||
capture_parent_scope: false,
|
capture_parent_scope: false,
|
||||||
op_token: None,
|
op_token: NO_TOKEN,
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
pos,
|
pos,
|
||||||
|
@ -5,6 +5,7 @@ pub mod expr;
|
|||||||
pub mod flags;
|
pub mod flags;
|
||||||
pub mod ident;
|
pub mod ident;
|
||||||
pub mod namespace;
|
pub mod namespace;
|
||||||
|
pub mod namespace_none;
|
||||||
pub mod script_fn;
|
pub mod script_fn;
|
||||||
pub mod stmt;
|
pub mod stmt;
|
||||||
|
|
||||||
@ -16,6 +17,8 @@ pub use flags::{ASTFlags, FnAccess};
|
|||||||
pub use ident::Ident;
|
pub use ident::Ident;
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
pub use namespace::Namespace;
|
pub use namespace::Namespace;
|
||||||
|
#[cfg(feature = "no_module")]
|
||||||
|
pub use namespace_none::Namespace;
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub use script_fn::EncapsulatedEnviron;
|
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`.
|
/// Not available under `no_module` or `no_function`.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct EncapsulatedEnviron {
|
pub struct EncapsulatedEnviron {
|
||||||
/// Functions defined within the same [`AST`][crate::AST].
|
/// Functions defined within the same [`AST`][crate::AST].
|
||||||
@ -24,7 +23,7 @@ pub struct EncapsulatedEnviron {
|
|||||||
/// Imported [modules][crate::Module].
|
/// Imported [modules][crate::Module].
|
||||||
pub imports: Box<[(ImmutableString, crate::SharedModule)]>,
|
pub imports: Box<[(ImmutableString, crate::SharedModule)]>,
|
||||||
/// Globally-defined constants.
|
/// 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.
|
/// _(internals)_ A type containing information on a script-defined function.
|
||||||
@ -35,7 +34,6 @@ pub struct ScriptFnDef {
|
|||||||
pub body: StmtBlock,
|
pub body: StmtBlock,
|
||||||
/// Encapsulated AST environment, if any.
|
/// Encapsulated AST environment, if any.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
pub environ: Option<crate::Shared<EncapsulatedEnviron>>,
|
pub environ: Option<crate::Shared<EncapsulatedEnviron>>,
|
||||||
/// Function name.
|
/// Function name.
|
||||||
pub name: ImmutableString,
|
pub name: ImmutableString,
|
||||||
@ -51,12 +49,14 @@ pub struct ScriptFnDef {
|
|||||||
///
|
///
|
||||||
/// Block doc-comments are kept in a single string slice with line-breaks within.
|
/// 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
|
/// Leading white-spaces are stripped, and each string slice always starts with the
|
||||||
/// corresponding doc-comment leader: `///` or `/**`.
|
/// corresponding doc-comment leader: `///` or `/**`.
|
||||||
|
///
|
||||||
|
/// Each line in non-block doc-comments starts with `///`.
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
pub comments: Box<[Box<str>]>,
|
pub comments: Box<[crate::Identifier]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ScriptFnDef {
|
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.
|
/// 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
|
/// Leading white-spaces are stripped, and each string slice always starts with the
|
||||||
/// corresponding doc-comment leader: `///` or `/**`.
|
/// corresponding doc-comment leader: `///` or `/**`.
|
||||||
|
///
|
||||||
|
/// Each line in non-block doc-comments starts with `///`.
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
pub comments: Vec<&'a str>,
|
pub comments: Vec<&'a str>,
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
use super::{ASTFlags, ASTNode, BinaryExpr, Expr, FnCallExpr, Ident};
|
use super::{ASTFlags, ASTNode, BinaryExpr, Expr, FnCallExpr, Ident};
|
||||||
use crate::engine::KEYWORD_EVAL;
|
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};
|
use crate::{calc_fn_hash, Position, StaticVec, INT};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -61,10 +62,8 @@ impl OpAssignment {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new_op_assignment(name: &str, pos: Position) -> Self {
|
pub fn new_op_assignment(name: &str, pos: Position) -> Self {
|
||||||
Self::new_op_assignment_from_token(
|
let op = Token::lookup_symbol_from_syntax(name).expect("operator");
|
||||||
&Token::lookup_symbol_from_syntax(name).expect("operator"),
|
Self::new_op_assignment_from_token(op, pos)
|
||||||
pos,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
/// Create a new [`OpAssignment`] from a [`Token`].
|
/// Create a new [`OpAssignment`] from a [`Token`].
|
||||||
///
|
///
|
||||||
@ -72,10 +71,11 @@ impl OpAssignment {
|
|||||||
///
|
///
|
||||||
/// Panics if the token is not an op-assignment operator.
|
/// Panics if the token is not an op-assignment operator.
|
||||||
#[must_use]
|
#[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
|
let op_raw = op
|
||||||
.get_base_op_from_assignment()
|
.get_base_op_from_assignment()
|
||||||
.expect("op-assignment operator");
|
.expect("op-assignment operator");
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
hash_op_assign: calc_fn_hash(None, op.literal_syntax(), 2),
|
hash_op_assign: calc_fn_hash(None, op.literal_syntax(), 2),
|
||||||
hash_op: calc_fn_hash(None, op_raw.literal_syntax(), 2),
|
hash_op: calc_fn_hash(None, op_raw.literal_syntax(), 2),
|
||||||
@ -92,10 +92,8 @@ impl OpAssignment {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new_op_assignment_from_base(name: &str, pos: Position) -> Self {
|
pub fn new_op_assignment_from_base(name: &str, pos: Position) -> Self {
|
||||||
Self::new_op_assignment_from_base_token(
|
let op = Token::lookup_symbol_from_syntax(name).expect("operator");
|
||||||
&Token::lookup_symbol_from_syntax(name).expect("operator"),
|
Self::new_op_assignment_from_base_token(op, pos)
|
||||||
pos,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
/// Convert a [`Token`] into a new [`OpAssignment`].
|
/// 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.
|
/// Panics if the token is cannot be converted into an op-assignment operator.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new_op_assignment_from_base_token(op: &Token, pos: Position) -> Self {
|
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)
|
Self::new_op_assignment_from_token(op.convert_to_op_assignment().expect("operator"), pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,17 +100,19 @@ fn print_error(input: &str, mut err: EvalAltResult) {
|
|||||||
// Print error position
|
// Print error position
|
||||||
if pos.is_none() {
|
if pos.is_none() {
|
||||||
// No position
|
// No position
|
||||||
println!("{err}");
|
println!("\x1b[31m{err}\x1b[39m");
|
||||||
} else {
|
} else {
|
||||||
// Specific position - print line text
|
// Specific position - print line text
|
||||||
println!("{line_no}{}", lines[pos.line().unwrap() - 1]);
|
println!("{line_no}{}", lines[pos.line().unwrap() - 1]);
|
||||||
|
|
||||||
// Display position marker
|
for (i, err_line) in err.to_string().split('\n').enumerate() {
|
||||||
println!(
|
// Display position marker
|
||||||
"{0:>1$} {err}",
|
println!(
|
||||||
"^",
|
"\x1b[31m{0:>1$}{err_line}\x1b[39m",
|
||||||
line_no.len() + pos.position().unwrap(),
|
if i > 0 { "| " } else { "^ " },
|
||||||
);
|
line_no.len() + pos.position().unwrap() + 1,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,7 +239,13 @@ fn debug_callback(
|
|||||||
) -> Result<DebuggerCommand, Box<EvalAltResult>> {
|
) -> Result<DebuggerCommand, Box<EvalAltResult>> {
|
||||||
// Check event
|
// Check event
|
||||||
match 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::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::End => println!("\x1b[31m! Script end\x1b[39m"),
|
||||||
DebuggerEvent::Step => (),
|
DebuggerEvent::Step => (),
|
||||||
DebuggerEvent::BreakPoint(n) => {
|
DebuggerEvent::BreakPoint(n) => {
|
||||||
@ -572,7 +580,7 @@ fn debug_callback(
|
|||||||
break Err(EvalAltResult::ErrorRuntime(msg.trim().into(), pos).into());
|
break Err(EvalAltResult::ErrorRuntime(msg.trim().into(), pos).into());
|
||||||
}
|
}
|
||||||
["run" | "r"] => {
|
["run" | "r"] => {
|
||||||
println!("Restarting script...");
|
println!("Terminating current run...");
|
||||||
break Err(EvalAltResult::ErrorTerminated(Dynamic::UNIT, pos).into());
|
break Err(EvalAltResult::ErrorTerminated(Dynamic::UNIT, pos).into());
|
||||||
}
|
}
|
||||||
_ => eprintln!(
|
_ => eprintln!(
|
||||||
@ -604,7 +612,10 @@ fn main() {
|
|||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
engine.register_debugger(
|
engine.register_debugger(
|
||||||
// Store the current source in the debugger state
|
// Store the current source in the debugger state
|
||||||
|_| "".into(),
|
|engine, mut debugger| {
|
||||||
|
debugger.set_state(engine.const_empty_string());
|
||||||
|
debugger
|
||||||
|
},
|
||||||
// Main debugging interface
|
// Main debugging interface
|
||||||
move |context, event, node, source, pos| {
|
move |context, event, node, source, pos| {
|
||||||
debug_callback(context, event, node, source, pos, &lines)
|
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) {
|
while let Err(err) = engine.run_ast_with_scope(&mut Scope::new(), &ast) {
|
||||||
match *err {
|
match *err {
|
||||||
// Loop back to restart
|
// Loop back to restart
|
||||||
EvalAltResult::ErrorTerminated(..) => (),
|
EvalAltResult::ErrorTerminated(..) => {
|
||||||
|
println!("Restarting script...");
|
||||||
|
}
|
||||||
// Break evaluation
|
// Break evaluation
|
||||||
_ => {
|
_ => {
|
||||||
print_error(&script, *err);
|
print_error(&script, *err);
|
||||||
|
println!();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,12 +31,14 @@ fn print_error(input: &str, mut err: EvalAltResult) {
|
|||||||
// Specific position - print line text
|
// Specific position - print line text
|
||||||
println!("{line_no}{}", lines[pos.line().unwrap() - 1]);
|
println!("{line_no}{}", lines[pos.line().unwrap() - 1]);
|
||||||
|
|
||||||
// Display position marker
|
for (i, err_line) in err.to_string().split('\n').enumerate() {
|
||||||
println!(
|
// Display position marker
|
||||||
"{0:>1$} {err}",
|
println!(
|
||||||
"^",
|
"{0:>1$}{err_line}",
|
||||||
line_no.len() + pos.position().unwrap(),
|
if i > 0 { "| " } else { "^ " },
|
||||||
);
|
line_no.len() + pos.position().unwrap() + 1,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,6 +247,7 @@ fn setup_editor() -> Editor<()> {
|
|||||||
rl
|
rl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Module containing sample functions.
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod sample_functions {
|
mod sample_functions {
|
||||||
/// This is a sample function.
|
/// This is a sample function.
|
||||||
|
@ -8,11 +8,15 @@ fn eprint_error(input: &str, mut err: EvalAltResult) {
|
|||||||
let line_no = format!("{line}: ");
|
let line_no = format!("{line}: ");
|
||||||
|
|
||||||
eprintln!("{line_no}{}", lines[line - 1]);
|
eprintln!("{line_no}{}", lines[line - 1]);
|
||||||
eprintln!(
|
|
||||||
"{:>1$} {err_msg}",
|
for (i, err_line) in err_msg.to_string().split('\n').enumerate() {
|
||||||
"^",
|
// Display position marker
|
||||||
line_no.len() + pos.position().unwrap(),
|
println!(
|
||||||
);
|
"{0:>1$}{err_line}",
|
||||||
|
if i > 0 { "| " } else { "^ " },
|
||||||
|
line_no.len() + pos.position().unwrap() + 1,
|
||||||
|
);
|
||||||
|
}
|
||||||
eprintln!();
|
eprintln!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@ pub struct Engine {
|
|||||||
|
|
||||||
/// Callback closure for debugging.
|
/// Callback closure for debugging.
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
pub(crate) debugger: Option<
|
pub(crate) debugger_interface: Option<
|
||||||
Box<(
|
Box<(
|
||||||
Box<crate::eval::OnDebuggingInit>,
|
Box<crate::eval::OnDebuggingInit>,
|
||||||
Box<crate::eval::OnDebuggerCallback>,
|
Box<crate::eval::OnDebuggerCallback>,
|
||||||
@ -191,6 +191,9 @@ impl fmt::Debug for Engine {
|
|||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
f.field("limits", &self.limits);
|
f.field("limits", &self.limits);
|
||||||
|
|
||||||
|
#[cfg(feature = "debugging")]
|
||||||
|
f.field("debugger_interface", &self.debugger_interface.is_some());
|
||||||
|
|
||||||
f.finish()
|
f.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -305,7 +308,7 @@ impl Engine {
|
|||||||
limits: crate::api::limits::Limits::new(),
|
limits: crate::api::limits::Limits::new(),
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
debugger: None,
|
debugger_interface: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add the global namespace module
|
// Add the global namespace module
|
||||||
@ -348,4 +351,12 @@ impl Engine {
|
|||||||
pub fn const_empty_string(&self) -> ImmutableString {
|
pub fn const_empty_string(&self) -> ImmutableString {
|
||||||
self.get_interned_string("")
|
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::ast::{ASTFlags, Expr, OpAssignment};
|
||||||
use crate::config::hashing::SusLock;
|
use crate::config::hashing::SusLock;
|
||||||
use crate::engine::{FN_IDX_GET, FN_IDX_SET};
|
use crate::engine::{FN_IDX_GET, FN_IDX_SET};
|
||||||
|
use crate::tokenizer::NO_TOKEN;
|
||||||
use crate::types::dynamic::Union;
|
use crate::types::dynamic::Union;
|
||||||
use crate::types::RestoreOnDrop;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_fn_hash, Dynamic, Engine, FnArgsVec, Position, RhaiResult, RhaiResultOf, Scope, ERR,
|
calc_fn_hash, Dynamic, Engine, FnArgsVec, Position, RhaiResult, RhaiResultOf, Scope, ERR,
|
||||||
};
|
};
|
||||||
@ -68,21 +68,13 @@ impl Engine {
|
|||||||
idx: &mut Dynamic,
|
idx: &mut Dynamic,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> RhaiResultOf<Dynamic> {
|
) -> RhaiResultOf<Dynamic> {
|
||||||
let orig_level = global.level;
|
auto_restore! { let orig_level = global.level; global.level += 1 }
|
||||||
global.level += 1;
|
|
||||||
let global = &mut *RestoreOnDrop::lock(global, move |g| g.level = orig_level);
|
|
||||||
|
|
||||||
self.exec_native_fn_call(
|
let hash = hash_idx().0;
|
||||||
global,
|
let args = &mut [target, idx];
|
||||||
caches,
|
|
||||||
FN_IDX_GET,
|
self.exec_native_fn_call(global, caches, FN_IDX_GET, NO_TOKEN, hash, args, true, pos)
|
||||||
None,
|
.map(|(r, ..)| r)
|
||||||
hash_idx().0,
|
|
||||||
&mut [target, idx],
|
|
||||||
true,
|
|
||||||
pos,
|
|
||||||
)
|
|
||||||
.map(|(r, ..)| r)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call a set indexer.
|
/// Call a set indexer.
|
||||||
@ -97,19 +89,13 @@ impl Engine {
|
|||||||
is_ref_mut: bool,
|
is_ref_mut: bool,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> RhaiResultOf<(Dynamic, bool)> {
|
) -> RhaiResultOf<(Dynamic, bool)> {
|
||||||
let orig_level = global.level;
|
auto_restore! { let orig_level = global.level; global.level += 1 }
|
||||||
global.level += 1;
|
|
||||||
let global = &mut *RestoreOnDrop::lock(global, move |g| g.level = orig_level);
|
let hash = hash_idx().1;
|
||||||
|
let args = &mut [target, idx, new_val];
|
||||||
|
|
||||||
self.exec_native_fn_call(
|
self.exec_native_fn_call(
|
||||||
global,
|
global, caches, FN_IDX_SET, NO_TOKEN, hash, args, is_ref_mut, pos,
|
||||||
caches,
|
|
||||||
FN_IDX_SET,
|
|
||||||
None,
|
|
||||||
hash_idx().1,
|
|
||||||
&mut [target, idx, new_val],
|
|
||||||
is_ref_mut,
|
|
||||||
pos,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -599,13 +585,15 @@ impl Engine {
|
|||||||
// Try to call index setter if value is changed
|
// Try to call index setter if value is changed
|
||||||
let idx = &mut idx_val_for_setter;
|
let idx = &mut idx_val_for_setter;
|
||||||
let new_val = &mut new_val;
|
let new_val = &mut new_val;
|
||||||
self.call_indexer_set(
|
// The return value of a indexer setter (usually `()`) is thrown away and not used.
|
||||||
global, caches, target, idx, new_val, is_ref_mut, op_pos,
|
let _ = self
|
||||||
)
|
.call_indexer_set(
|
||||||
.or_else(|e| match *e {
|
global, caches, target, idx, new_val, is_ref_mut, op_pos,
|
||||||
ERR::ErrorIndexingType(..) => Ok((Dynamic::UNIT, false)),
|
)
|
||||||
_ => Err(e),
|
.or_else(|e| match *e {
|
||||||
})?;
|
ERR::ErrorIndexingType(..) => Ok((Dynamic::UNIT, false)),
|
||||||
|
_ => Err(e),
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
@ -659,8 +647,8 @@ impl Engine {
|
|||||||
|
|
||||||
// Try to call index setter
|
// Try to call index setter
|
||||||
let new_val = &mut new_val;
|
let new_val = &mut new_val;
|
||||||
|
// The return value of a indexer setter (usually `()`) is thrown away and not used.
|
||||||
self.call_indexer_set(
|
let _ = self.call_indexer_set(
|
||||||
global, caches, target, idx_val, new_val, is_ref_mut, op_pos,
|
global, caches, target, idx_val, new_val, is_ref_mut, op_pos,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
@ -696,19 +684,17 @@ impl Engine {
|
|||||||
let reset =
|
let reset =
|
||||||
self.run_debugger_with_reset(global, caches, scope, this_ptr, rhs)?;
|
self.run_debugger_with_reset(global, caches, scope, this_ptr, rhs)?;
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
let global =
|
auto_restore!(global if reset.is_some() => move |g| g.debugger_mut().reset_status(reset));
|
||||||
&mut *RestoreOnDrop::lock_if(reset.is_some(), global, move |g| {
|
|
||||||
g.debugger_mut().reset_status(reset)
|
|
||||||
});
|
|
||||||
|
|
||||||
let crate::ast::FnCallExpr {
|
let crate::ast::FnCallExpr {
|
||||||
name, hashes, args, ..
|
name, hashes, args, ..
|
||||||
} = &**x;
|
} = &**x;
|
||||||
|
|
||||||
// Truncate the index values upon exit
|
// Truncate the index values upon exit
|
||||||
let offset = idx_values.len() - args.len();
|
auto_restore! {
|
||||||
let idx_values =
|
idx_values => truncate;
|
||||||
&mut *RestoreOnDrop::lock(idx_values, move |v| v.truncate(offset));
|
let offset = idx_values.len() - args.len();
|
||||||
|
}
|
||||||
|
|
||||||
let call_args = &mut idx_values[offset..];
|
let call_args = &mut idx_values[offset..];
|
||||||
let arg1_pos = args.get(0).map_or(Position::NONE, Expr::position);
|
let arg1_pos = args.get(0).map_or(Position::NONE, Expr::position);
|
||||||
@ -764,9 +750,11 @@ impl Engine {
|
|||||||
|
|
||||||
if op_info.is_op_assignment() {
|
if op_info.is_op_assignment() {
|
||||||
let args = &mut [target.as_mut()];
|
let args = &mut [target.as_mut()];
|
||||||
|
|
||||||
let (mut orig_val, ..) = self
|
let (mut orig_val, ..) = self
|
||||||
.exec_native_fn_call(
|
.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 {
|
.or_else(|err| match *err {
|
||||||
// Try an indexer if property does not exist
|
// Try an indexer if property does not exist
|
||||||
@ -798,8 +786,9 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let args = &mut [target.as_mut(), &mut new_val];
|
let args = &mut [target.as_mut(), &mut new_val];
|
||||||
|
|
||||||
self.exec_native_fn_call(
|
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 {
|
.or_else(|err| match *err {
|
||||||
// Try an indexer if property does not exist
|
// Try an indexer if property does not exist
|
||||||
@ -824,8 +813,9 @@ impl Engine {
|
|||||||
|
|
||||||
let ((getter, hash_get), _, name) = &**x;
|
let ((getter, hash_get), _, name) = &**x;
|
||||||
let args = &mut [target.as_mut()];
|
let args = &mut [target.as_mut()];
|
||||||
|
|
||||||
self.exec_native_fn_call(
|
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(
|
.map_or_else(
|
||||||
|err| match *err {
|
|err| match *err {
|
||||||
@ -866,21 +856,17 @@ impl Engine {
|
|||||||
global, caches, scope, this_ptr, _node,
|
global, caches, scope, this_ptr, _node,
|
||||||
)?;
|
)?;
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
let global = &mut *RestoreOnDrop::lock_if(
|
auto_restore!(global if reset.is_some() => move |g| g.debugger_mut().reset_status(reset));
|
||||||
reset.is_some(),
|
|
||||||
global,
|
|
||||||
move |g| g.debugger_mut().reset_status(reset),
|
|
||||||
);
|
|
||||||
|
|
||||||
let crate::ast::FnCallExpr {
|
let crate::ast::FnCallExpr {
|
||||||
name, hashes, args, ..
|
name, hashes, args, ..
|
||||||
} = &**x;
|
} = &**x;
|
||||||
|
|
||||||
// Truncate the index values upon exit
|
// Truncate the index values upon exit
|
||||||
let offset = idx_values.len() - args.len();
|
auto_restore! {
|
||||||
let idx_values = &mut *RestoreOnDrop::lock(idx_values, move |v| {
|
idx_values => truncate;
|
||||||
v.truncate(offset)
|
let offset = idx_values.len() - args.len();
|
||||||
});
|
}
|
||||||
|
|
||||||
let call_args = &mut idx_values[offset..];
|
let call_args = &mut idx_values[offset..];
|
||||||
let arg1_pos = args.get(0).map_or(Position::NONE, Expr::position);
|
let arg1_pos = args.get(0).map_or(Position::NONE, Expr::position);
|
||||||
@ -915,14 +901,13 @@ impl Engine {
|
|||||||
self.run_debugger(global, caches, scope, this_ptr, _node)?;
|
self.run_debugger(global, caches, scope, this_ptr, _node)?;
|
||||||
|
|
||||||
let ((getter, hash_get), (setter, hash_set), name) = &**p;
|
let ((getter, hash_get), (setter, hash_set), name) = &**p;
|
||||||
let mut arg_values = [target.as_mut(), &mut Dynamic::UNIT.clone()];
|
let args = &mut [target.as_mut()];
|
||||||
let args = &mut arg_values[..1];
|
|
||||||
|
|
||||||
// Assume getters are always pure
|
// Assume getters are always pure
|
||||||
let (mut val, ..) = self
|
let (mut val, ..) = self
|
||||||
.exec_native_fn_call(
|
.exec_native_fn_call(
|
||||||
global, caches, getter, None, *hash_get, args, is_ref_mut,
|
global, caches, getter, NO_TOKEN, *hash_get, args,
|
||||||
pos,
|
is_ref_mut, pos,
|
||||||
)
|
)
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
// Try an indexer if property does not exist
|
// 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
|
// Feed the value back via a setter just in case it has been updated
|
||||||
if may_be_changed {
|
if may_be_changed {
|
||||||
// Re-use args because the first &mut parameter will not be consumed
|
// 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 [target.as_mut(), val.as_mut()];
|
||||||
let args = &mut arg_values;
|
|
||||||
self.exec_native_fn_call(
|
// The return value is thrown away and not used.
|
||||||
global, caches, setter, None, *hash_set, args, is_ref_mut,
|
let _ = self
|
||||||
pos,
|
.exec_native_fn_call(
|
||||||
)
|
global, caches, setter, NO_TOKEN, *hash_set, args,
|
||||||
.or_else(
|
is_ref_mut, pos,
|
||||||
|err| match *err {
|
)
|
||||||
|
.or_else(|err| match *err {
|
||||||
// Try an indexer if property does not exist
|
// Try an indexer if property does not exist
|
||||||
ERR::ErrorDotExpr(..) => {
|
ERR::ErrorDotExpr(..) => {
|
||||||
let idx = &mut name.into();
|
let idx = &mut name.into();
|
||||||
@ -978,8 +964,7 @@ impl Engine {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
},
|
})?;
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((result, may_be_changed))
|
Ok((result, may_be_changed))
|
||||||
@ -992,22 +977,17 @@ impl Engine {
|
|||||||
global, caches, scope, this_ptr, _node,
|
global, caches, scope, this_ptr, _node,
|
||||||
)?;
|
)?;
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
let global = &mut *RestoreOnDrop::lock_if(
|
auto_restore!(global if reset.is_some() => move |g| g.debugger_mut().reset_status(reset));
|
||||||
reset.is_some(),
|
|
||||||
global,
|
|
||||||
move |g| g.debugger_mut().reset_status(reset),
|
|
||||||
);
|
|
||||||
|
|
||||||
let crate::ast::FnCallExpr {
|
let crate::ast::FnCallExpr {
|
||||||
name, hashes, args, ..
|
name, hashes, args, ..
|
||||||
} = &**f;
|
} = &**f;
|
||||||
|
|
||||||
// Truncate the index values upon exit
|
// Truncate the index values upon exit
|
||||||
let offset = idx_values.len() - args.len();
|
auto_restore! {
|
||||||
let idx_values =
|
idx_values => truncate;
|
||||||
&mut *RestoreOnDrop::lock(idx_values, move |v| {
|
let offset = idx_values.len() - args.len();
|
||||||
v.truncate(offset)
|
}
|
||||||
});
|
|
||||||
|
|
||||||
let call_args = &mut idx_values[offset..];
|
let call_args = &mut idx_values[offset..];
|
||||||
let pos1 = args.get(0).map_or(Position::NONE, Expr::position);
|
let pos1 = args.get(0).map_or(Position::NONE, Expr::position);
|
||||||
|
@ -74,13 +74,10 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// [`Position`] in [`EvalAltResult`][crate::EvalAltResult] is always [`NONE`][Position::NONE]
|
/// [`Position`] in [`EvalAltResult`][crate::EvalAltResult] is always [`NONE`][Position::NONE]
|
||||||
/// and should be set afterwards.
|
/// and should be set afterwards.
|
||||||
pub(crate) fn raise_err_if_over_data_size_limit(
|
pub(crate) fn throw_on_size(&self, (_arr, _map, s): (usize, usize, usize)) -> RhaiResultOf<()> {
|
||||||
&self,
|
|
||||||
(_arr, _map, s): (usize, usize, usize),
|
|
||||||
) -> RhaiResultOf<()> {
|
|
||||||
if self
|
if self
|
||||||
.limits
|
.limits
|
||||||
.max_string_size
|
.max_string_len
|
||||||
.map_or(false, |max| s > max.get())
|
.map_or(false, |max| s > max.get())
|
||||||
{
|
{
|
||||||
return Err(
|
return Err(
|
||||||
@ -127,9 +124,10 @@ impl Engine {
|
|||||||
|
|
||||||
let sizes = value.borrow().calc_data_sizes(true);
|
let sizes = value.borrow().calc_data_sizes(true);
|
||||||
|
|
||||||
self.raise_err_if_over_data_size_limit(sizes)
|
self.throw_on_size(sizes)
|
||||||
.map(|_| value)
|
.map_err(|err| err.fill_position(pos))?;
|
||||||
.map_err(|err| err.fill_position(pos))
|
|
||||||
|
Ok(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Raise an error if the size of a [`Dynamic`] is out of limits (if any).
|
/// 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.
|
/// Callback function to initialize the debugger.
|
||||||
#[cfg(not(feature = "sync"))]
|
#[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.
|
/// Callback function to initialize the debugger.
|
||||||
#[cfg(feature = "sync")]
|
#[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.
|
/// Callback function for debugging.
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
@ -268,12 +268,12 @@ impl Debugger {
|
|||||||
/// Create a new [`Debugger`].
|
/// Create a new [`Debugger`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn new(status: DebuggerStatus, state: Dynamic) -> Self {
|
pub const fn new(status: DebuggerStatus) -> Self {
|
||||||
Self {
|
Self {
|
||||||
status,
|
status,
|
||||||
break_points: Vec::new(),
|
break_points: Vec::new(),
|
||||||
call_stack: Vec::new(),
|
call_stack: Vec::new(),
|
||||||
state,
|
state: Dynamic::UNIT,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get the current call stack.
|
/// Get the current call stack.
|
||||||
@ -415,7 +415,7 @@ impl Engine {
|
|||||||
this_ptr: &mut Dynamic,
|
this_ptr: &mut Dynamic,
|
||||||
node: impl Into<ASTNode<'a>>,
|
node: impl Into<ASTNode<'a>>,
|
||||||
) -> RhaiResultOf<()> {
|
) -> RhaiResultOf<()> {
|
||||||
if self.debugger.is_some() {
|
if self.is_debugger_registered() {
|
||||||
if let Some(cmd) =
|
if let Some(cmd) =
|
||||||
self.run_debugger_with_reset_raw(global, caches, scope, this_ptr, node)?
|
self.run_debugger_with_reset_raw(global, caches, scope, this_ptr, node)?
|
||||||
{
|
{
|
||||||
@ -440,7 +440,7 @@ impl Engine {
|
|||||||
this_ptr: &mut Dynamic,
|
this_ptr: &mut Dynamic,
|
||||||
node: impl Into<ASTNode<'a>>,
|
node: impl Into<ASTNode<'a>>,
|
||||||
) -> RhaiResultOf<Option<DebuggerStatus>> {
|
) -> 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)
|
self.run_debugger_with_reset_raw(global, caches, scope, this_ptr, node)
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
@ -508,11 +508,10 @@ impl Engine {
|
|||||||
node: ASTNode<'a>,
|
node: ASTNode<'a>,
|
||||||
event: DebuggerEvent,
|
event: DebuggerEvent,
|
||||||
) -> Result<Option<DebuggerStatus>, Box<crate::EvalAltResult>> {
|
) -> Result<Option<DebuggerStatus>, Box<crate::EvalAltResult>> {
|
||||||
let src = global.source_raw().cloned();
|
if let Some(ref x) = self.debugger_interface {
|
||||||
let src = src.as_ref().map(|s| s.as_str());
|
let src = global.source_raw().cloned();
|
||||||
let context = crate::EvalContext::new(self, global, caches, scope, this_ptr);
|
let src = src.as_ref().map(|s| s.as_str());
|
||||||
|
let context = EvalContext::new(self, global, caches, scope, this_ptr);
|
||||||
if let Some(ref x) = self.debugger {
|
|
||||||
let (.., ref on_debugger) = **x;
|
let (.., ref on_debugger) = **x;
|
||||||
|
|
||||||
let command = on_debugger(context, event, node, src, node.position())?;
|
let command = on_debugger(context, event, node, src, node.position())?;
|
||||||
|
@ -6,7 +6,6 @@ use crate::{Dynamic, Engine, Scope};
|
|||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
/// Context of a script evaluation process.
|
/// Context of a script evaluation process.
|
||||||
#[derive(Debug)]
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub struct EvalContext<'a, 's, 'ps, 'g, 'c, 't> {
|
pub struct EvalContext<'a, 's, 'ps, 'g, 'c, 't> {
|
||||||
/// The current [`Engine`].
|
/// The current [`Engine`].
|
||||||
|
@ -62,25 +62,22 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
Expr::Variable(v, None, ..) => match &**v {
|
Expr::Variable(v, None, ..) => match &**v {
|
||||||
// Normal variable access
|
// Normal variable access
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
(_, ns, ..) if ns.is_empty() => {
|
(_, ns, ..) if ns.is_empty() => {
|
||||||
self.search_scope_only(global, caches, scope, this_ptr, expr)
|
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
|
// Qualified variable access
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
(_, namespace, hash_var, var_name) => {
|
(_, ns, hash_var, var_name) => {
|
||||||
// foo:bar::baz::VARIABLE
|
// 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(
|
return module.get_qualified_var(*hash_var).map_or_else(
|
||||||
|| {
|
|| {
|
||||||
let sep = crate::tokenizer::Token::DoubleColon.literal_syntax();
|
let sep = crate::tokenizer::Token::DoubleColon.literal_syntax();
|
||||||
|
|
||||||
Err(ERR::ErrorVariableNotFound(
|
Err(ERR::ErrorVariableNotFound(
|
||||||
format!("{namespace}{sep}{var_name}"),
|
format!("{ns}{sep}{var_name}"),
|
||||||
namespace.position(),
|
ns.position(),
|
||||||
)
|
)
|
||||||
.into())
|
.into())
|
||||||
},
|
},
|
||||||
@ -94,7 +91,7 @@ impl Engine {
|
|||||||
|
|
||||||
// global::VARIABLE
|
// global::VARIABLE
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[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(ref constants) = global.constants {
|
||||||
if let Some(value) =
|
if let Some(value) =
|
||||||
crate::func::locked_write(constants).get_mut(var_name.as_str())
|
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();
|
let sep = crate::tokenizer::Token::DoubleColon.literal_syntax();
|
||||||
|
|
||||||
return Err(ERR::ErrorVariableNotFound(
|
return Err(ERR::ErrorVariableNotFound(
|
||||||
format!("{namespace}{sep}{var_name}"),
|
format!("{ns}{sep}{var_name}"),
|
||||||
namespace.position(),
|
ns.position(),
|
||||||
)
|
)
|
||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(
|
Err(ERR::ErrorModuleNotFound(ns.to_string(), ns.position()).into())
|
||||||
ERR::ErrorModuleNotFound(namespace.to_string(), namespace.position())
|
|
||||||
.into(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "no_module")]
|
||||||
|
_ => unreachable!("Invalid expression {:?}", expr),
|
||||||
},
|
},
|
||||||
_ => unreachable!("Expr::Variable expected but gets {:?}", expr),
|
_ => unreachable!("Expr::Variable expected but gets {:?}", expr),
|
||||||
}
|
}
|
||||||
@ -142,14 +139,18 @@ impl Engine {
|
|||||||
|
|
||||||
let index = match expr {
|
let index = match expr {
|
||||||
// Check if the variable is `this`
|
// 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() {
|
return if this_ptr.is_null() {
|
||||||
Err(ERR::ErrorUnboundThis(expr.position()).into())
|
Err(ERR::ErrorUnboundThis(expr.position()).into())
|
||||||
} else {
|
} else {
|
||||||
Ok(this_ptr.into())
|
Ok(this_ptr.into())
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_ if global.always_search_scope => 0,
|
_ if global.always_search_scope => 0,
|
||||||
|
|
||||||
Expr::Variable(_, Some(i), ..) => i.get() as usize,
|
Expr::Variable(_, Some(i), ..) => i.get() as usize,
|
||||||
// Scripted function with the same name
|
// Scripted function with the same name
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
@ -165,6 +166,7 @@ impl Engine {
|
|||||||
return Ok(val.into());
|
return Ok(val.into());
|
||||||
}
|
}
|
||||||
Expr::Variable(v, None, ..) => v.0.map_or(0, NonZeroUsize::get),
|
Expr::Variable(v, None, ..) => v.0.map_or(0, NonZeroUsize::get),
|
||||||
|
|
||||||
_ => unreachable!("Expr::Variable expected but gets {:?}", expr),
|
_ => unreachable!("Expr::Variable expected but gets {:?}", expr),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -232,10 +234,7 @@ impl Engine {
|
|||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
let reset = self.run_debugger_with_reset(global, caches, scope, this_ptr, expr)?;
|
let reset = self.run_debugger_with_reset(global, caches, scope, this_ptr, expr)?;
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
let global =
|
auto_restore!(global if reset.is_some() => move |g| g.debugger_mut().reset_status(reset));
|
||||||
&mut *crate::types::RestoreOnDrop::lock_if(reset.is_some(), global, move |g| {
|
|
||||||
g.debugger_mut().reset_status(reset)
|
|
||||||
});
|
|
||||||
|
|
||||||
self.track_operation(global, expr.position())?;
|
self.track_operation(global, expr.position())?;
|
||||||
|
|
||||||
@ -266,10 +265,7 @@ impl Engine {
|
|||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
let reset = self.run_debugger_with_reset(global, caches, scope, this_ptr, expr)?;
|
let reset = self.run_debugger_with_reset(global, caches, scope, this_ptr, expr)?;
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
let global =
|
auto_restore!(global if reset.is_some() => move |g| g.debugger_mut().reset_status(reset));
|
||||||
&mut *crate::types::RestoreOnDrop::lock_if(reset.is_some(), global, move |g| {
|
|
||||||
g.debugger_mut().reset_status(reset)
|
|
||||||
});
|
|
||||||
|
|
||||||
self.track_operation(global, expr.position())?;
|
self.track_operation(global, expr.position())?;
|
||||||
|
|
||||||
@ -327,7 +323,7 @@ impl Engine {
|
|||||||
total_data_sizes.1 + val_sizes.1,
|
total_data_sizes.1 + val_sizes.1,
|
||||||
total_data_sizes.2 + val_sizes.2,
|
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()))?;
|
.map_err(|err| err.fill_position(item_expr.position()))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -358,7 +354,7 @@ impl Engine {
|
|||||||
total_data_sizes.1 + delta.1,
|
total_data_sizes.1 + delta.1,
|
||||||
total_data_sizes.2 + delta.2,
|
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()))?;
|
.map_err(|err| err.fill_position(value_expr.position()))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ use std::prelude::v1::*;
|
|||||||
/// Collection of globally-defined constants.
|
/// Collection of globally-defined constants.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub type GlobalConstants =
|
pub type SharedGlobalConstants =
|
||||||
crate::Shared<crate::Locked<std::collections::BTreeMap<ImmutableString, Dynamic>>>;
|
crate::Shared<crate::Locked<std::collections::BTreeMap<ImmutableString, Dynamic>>>;
|
||||||
|
|
||||||
/// _(internals)_ Global runtime states.
|
/// _(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.
|
/// Interior mutability is needed because it is shared in order to aid in cloning.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub constants: Option<GlobalConstants>,
|
pub constants: Option<SharedGlobalConstants>,
|
||||||
/// Custom state that can be used by the external host.
|
/// Custom state that can be used by the external host.
|
||||||
pub tag: Dynamic,
|
pub tag: Dynamic,
|
||||||
/// Debugging interface.
|
/// Debugging interface.
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
pub(crate) debugger: Option<super::Debugger>,
|
pub(crate) debugger: Option<Box<super::Debugger>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GlobalRuntimeState {
|
impl GlobalRuntimeState {
|
||||||
@ -103,8 +103,9 @@ impl GlobalRuntimeState {
|
|||||||
tag: engine.default_tag().clone(),
|
tag: engine.default_tag().clone(),
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
debugger: engine.debugger.as_ref().map(|x| {
|
debugger: engine.debugger_interface.as_ref().map(|x| {
|
||||||
crate::eval::Debugger::new(crate::eval::DebuggerStatus::Init, (x.0)(engine))
|
let dbg = crate::eval::Debugger::new(crate::eval::DebuggerStatus::Init);
|
||||||
|
(x.0)(engine, dbg).into()
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,7 +116,7 @@ impl GlobalRuntimeState {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn num_imports(&self) -> usize {
|
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.
|
/// Get the globally-imported [module][crate::Module] at a particular index.
|
||||||
///
|
///
|
||||||
@ -138,7 +139,7 @@ impl GlobalRuntimeState {
|
|||||||
&mut self,
|
&mut self,
|
||||||
index: usize,
|
index: usize,
|
||||||
) -> Option<&mut crate::SharedModule> {
|
) -> 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.
|
/// Get the index of a globally-imported [module][crate::Module] by name.
|
||||||
///
|
///
|
||||||
@ -183,8 +184,8 @@ impl GlobalRuntimeState {
|
|||||||
self.imports = None;
|
self.imports = None;
|
||||||
self.modules = None;
|
self.modules = None;
|
||||||
} else if self.imports.is_some() {
|
} else if self.imports.is_some() {
|
||||||
self.imports.as_mut().unwrap().truncate(size);
|
self.imports.as_deref_mut().unwrap().truncate(size);
|
||||||
self.modules.as_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.
|
/// Get an iterator to the stack of globally-imported [modules][crate::Module] in reverse order.
|
||||||
@ -194,10 +195,11 @@ impl GlobalRuntimeState {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &crate::Module)> {
|
pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &crate::Module)> {
|
||||||
self.imports
|
self.imports
|
||||||
.iter()
|
.as_deref()
|
||||||
.flat_map(|x| x.iter())
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
.rev()
|
.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))
|
.map(|(name, module)| (name.as_str(), &**module))
|
||||||
}
|
}
|
||||||
/// Get an iterator to the stack of globally-imported [modules][crate::Module] in reverse order.
|
/// Get an iterator to the stack of globally-imported [modules][crate::Module] in reverse order.
|
||||||
@ -209,10 +211,11 @@ impl GlobalRuntimeState {
|
|||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<Item = (&ImmutableString, &crate::SharedModule)> {
|
) -> impl Iterator<Item = (&ImmutableString, &crate::SharedModule)> {
|
||||||
self.imports
|
self.imports
|
||||||
.iter()
|
.as_deref()
|
||||||
.flat_map(|x| x.iter())
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
.rev()
|
.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.
|
/// Get an iterator to the stack of globally-imported [modules][crate::Module] in forward order.
|
||||||
///
|
///
|
||||||
@ -223,9 +226,10 @@ impl GlobalRuntimeState {
|
|||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<Item = (&ImmutableString, &crate::SharedModule)> {
|
) -> impl Iterator<Item = (&ImmutableString, &crate::SharedModule)> {
|
||||||
self.imports
|
self.imports
|
||||||
.iter()
|
.as_deref()
|
||||||
.flat_map(|x| x.iter())
|
.into_iter()
|
||||||
.zip(self.modules.iter().flat_map(|x| x.iter()))
|
.flatten()
|
||||||
|
.zip(self.modules.as_deref().into_iter().flatten())
|
||||||
}
|
}
|
||||||
/// Can the particular function with [`Dynamic`] parameter(s) exist in the stack of
|
/// Can the particular function with [`Dynamic`] parameter(s) exist in the stack of
|
||||||
/// globally-imported [modules][crate::Module]?
|
/// globally-imported [modules][crate::Module]?
|
||||||
@ -234,7 +238,7 @@ impl GlobalRuntimeState {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn may_contain_dynamic_fn(&self, hash_script: u64) -> bool {
|
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))
|
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.
|
/// Panics if the debugging interface is not set.
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
pub fn debugger_mut(&mut self) -> &mut super::Debugger {
|
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,
|
OnDebuggerCallback, OnDebuggingInit,
|
||||||
};
|
};
|
||||||
pub use eval_context::EvalContext;
|
pub use eval_context::EvalContext;
|
||||||
|
pub use global_state::GlobalRuntimeState;
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub use global_state::GlobalConstants;
|
pub use global_state::SharedGlobalConstants;
|
||||||
pub use global_state::GlobalRuntimeState;
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
pub use target::calc_offset_len;
|
pub use target::calc_offset_len;
|
||||||
pub use target::{calc_index, Target};
|
pub use target::{calc_index, Target};
|
||||||
|
176
src/eval/stmt.rs
176
src/eval/stmt.rs
@ -3,16 +3,28 @@
|
|||||||
use super::{Caches, EvalContext, GlobalRuntimeState, Target};
|
use super::{Caches, EvalContext, GlobalRuntimeState, Target};
|
||||||
use crate::api::events::VarDefInfo;
|
use crate::api::events::VarDefInfo;
|
||||||
use crate::ast::{
|
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::func::{get_builtin_op_assignment_fn, get_hasher};
|
||||||
use crate::types::dynamic::AccessMode;
|
use crate::types::dynamic::{AccessMode, Union};
|
||||||
use crate::types::RestoreOnDrop;
|
|
||||||
use crate::{Dynamic, Engine, RhaiResult, RhaiResultOf, Scope, ERR, INT};
|
use crate::{Dynamic, Engine, RhaiResult, RhaiResultOf, Scope, ERR, INT};
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
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 {
|
impl Engine {
|
||||||
/// Evaluate a statements block.
|
/// Evaluate a statements block.
|
||||||
pub(crate) fn eval_stmt_block(
|
pub(crate) fn eval_stmt_block(
|
||||||
@ -29,10 +41,10 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Restore scope at end of block if necessary
|
// Restore scope at end of block if necessary
|
||||||
let orig_scope_len = scope.len();
|
auto_restore! {
|
||||||
let scope = &mut *RestoreOnDrop::lock_if(restore_orig_state, scope, move |s| {
|
scope if restore_orig_state => rewind;
|
||||||
s.rewind(orig_scope_len);
|
let orig_scope_len = scope.len();
|
||||||
});
|
}
|
||||||
|
|
||||||
// Restore global state at end of block if necessary
|
// Restore global state at end of block if necessary
|
||||||
let orig_always_search_scope = global.always_search_scope;
|
let orig_always_search_scope = global.always_search_scope;
|
||||||
@ -43,7 +55,7 @@ impl Engine {
|
|||||||
global.scope_level += 1;
|
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;
|
g.scope_level -= 1;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -55,10 +67,10 @@ impl Engine {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Pop new function resolution caches at end of block
|
// Pop new function resolution caches at end of block
|
||||||
let orig_fn_resolution_caches_len = caches.fn_resolution_caches_len();
|
auto_restore! {
|
||||||
let caches = &mut *RestoreOnDrop::lock(caches, move |c| {
|
caches => rewind_fn_resolution_caches;
|
||||||
c.rewind_fn_resolution_caches(orig_fn_resolution_caches_len)
|
let orig_fn_resolution_caches_len = caches.fn_resolution_caches_len();
|
||||||
});
|
}
|
||||||
|
|
||||||
// Run the statements
|
// Run the statements
|
||||||
statements.iter().try_fold(Dynamic::UNIT, |_, stmt| {
|
statements.iter().try_fold(Dynamic::UNIT, |_, stmt| {
|
||||||
@ -129,23 +141,25 @@ impl Engine {
|
|||||||
let args = &mut [&mut *lock_guard, &mut new_val];
|
let args = &mut [&mut *lock_guard, &mut new_val];
|
||||||
|
|
||||||
if self.fast_operators() {
|
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
|
// Built-in found
|
||||||
let op = op_assign_token.literal_syntax();
|
let op = op_assign_token.literal_syntax();
|
||||||
|
auto_restore! { let orig_level = global.level; global.level += 1 }
|
||||||
|
|
||||||
let orig_level = global.level;
|
let context = if ctx {
|
||||||
global.level += 1;
|
Some((self, op, None, &*global, *op_pos).into())
|
||||||
let global = &*RestoreOnDrop::lock(global, move |g| g.level = orig_level);
|
} else {
|
||||||
|
None
|
||||||
let context = (self, op, None, global, *op_pos).into();
|
};
|
||||||
return func(context, args).map(|_| ());
|
return func(context, args).map(|_| ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let op_assign = op_assign_token.literal_syntax();
|
let op_assign = op_assign_token.literal_syntax();
|
||||||
let op = op_token.literal_syntax();
|
let op = op_token.literal_syntax();
|
||||||
let token = Some(op_assign_token);
|
let token = op_assign_token.clone();
|
||||||
|
|
||||||
match self
|
match self
|
||||||
.exec_native_fn_call(global, caches, op_assign, token, hash, args, true, *op_pos)
|
.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)) =>
|
Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_assign)) =>
|
||||||
{
|
{
|
||||||
// Expand to `var = var op rhs`
|
// Expand to `var = var op rhs`
|
||||||
let token = Some(op_token);
|
let token = op_token.clone();
|
||||||
|
|
||||||
*args[0] = self
|
*args[0] = self
|
||||||
.exec_native_fn_call(
|
.exec_native_fn_call(
|
||||||
@ -168,14 +182,14 @@ impl Engine {
|
|||||||
self.check_data_size(&*args[0], root.position())?;
|
self.check_data_size(&*args[0], root.position())?;
|
||||||
} else {
|
} else {
|
||||||
// Normal assignment
|
// Normal assignment
|
||||||
|
match target {
|
||||||
// If value is a string, intern it
|
// Lock it again just in case it is shared
|
||||||
if new_val.is_string() {
|
Target::RefMut(_) | Target::TempValue(_) => {
|
||||||
let value = new_val.into_immutable_string().expect("`ImmutableString`");
|
*target.write_lock::<Dynamic>().unwrap() = new_val
|
||||||
new_val = self.get_interned_string(value).into();
|
}
|
||||||
|
#[allow(unreachable_patterns)]
|
||||||
|
_ => **target = new_val,
|
||||||
}
|
}
|
||||||
|
|
||||||
*target.write_lock::<Dynamic>().unwrap() = new_val;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
target.propagate_changed_value(op_info.pos)
|
target.propagate_changed_value(op_info.pos)
|
||||||
@ -194,9 +208,7 @@ impl Engine {
|
|||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
let reset = self.run_debugger_with_reset(global, caches, scope, this_ptr, stmt)?;
|
let reset = self.run_debugger_with_reset(global, caches, scope, this_ptr, stmt)?;
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
let global = &mut *RestoreOnDrop::lock_if(reset.is_some(), global, move |g| {
|
auto_restore!(global if reset.is_some() => move |g| g.debugger_mut().reset_status(reset));
|
||||||
g.debugger_mut().reset_status(reset)
|
|
||||||
});
|
|
||||||
|
|
||||||
// Coded this way for better branch prediction.
|
// Coded this way for better branch prediction.
|
||||||
// Popular branches are lifted out of the `match` statement into their own branches.
|
// Popular branches are lifted out of the `match` statement into their own branches.
|
||||||
@ -223,14 +235,13 @@ impl Engine {
|
|||||||
|
|
||||||
let mut target = self.search_namespace(global, caches, scope, this_ptr, lhs)?;
|
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();
|
let var_name = x.3.as_str();
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
// Also handle case where target is a `Dynamic` shared value
|
// Also handle case where target is a `Dynamic` shared value
|
||||||
// (returned by a variable resolver, for example)
|
// (returned by a variable resolver, for example)
|
||||||
let is_temp_result = !target.is_ref() && !target.is_shared();
|
let is_temp_result = is_temp_result && !target.is_shared();
|
||||||
#[cfg(feature = "no_closure")]
|
|
||||||
let is_temp_result = !target.is_ref();
|
|
||||||
|
|
||||||
// Cannot assign to temp result from expression
|
// Cannot assign to temp result from expression
|
||||||
if is_temp_result {
|
if is_temp_result {
|
||||||
@ -250,36 +261,31 @@ impl Engine {
|
|||||||
|
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[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)?
|
.eval_expr(global, caches, scope, this_ptr, rhs)?
|
||||||
.flatten();
|
.flatten()
|
||||||
|
.intern_string(self);
|
||||||
// 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
let _new_val = Some((rhs_val, op_info));
|
let _new_val = Some((rhs_val, op_info));
|
||||||
|
|
||||||
// Must be either `var[index] op= val` or `var.prop op= val`
|
// Must be either `var[index] op= val` or `var.prop op= val`.
|
||||||
match lhs {
|
// The return value of any op-assignment (should be `()`) is thrown away and not used.
|
||||||
// name op= rhs (handled above)
|
let _ =
|
||||||
Expr::Variable(..) => {
|
match lhs {
|
||||||
unreachable!("Expr::Variable case is already handled")
|
// name op= rhs (handled above)
|
||||||
}
|
Expr::Variable(..) => {
|
||||||
// idx_lhs[idx_expr] op= rhs
|
unreachable!("Expr::Variable case is already handled")
|
||||||
#[cfg(not(feature = "no_index"))]
|
}
|
||||||
Expr::Index(..) => {
|
// idx_lhs[idx_expr] op= rhs
|
||||||
self.eval_dot_index_chain(global, caches, scope, this_ptr, lhs, _new_val)
|
#[cfg(not(feature = "no_index"))]
|
||||||
}
|
Expr::Index(..) => self
|
||||||
// dot_lhs.dot_rhs op= rhs
|
.eval_dot_index_chain(global, caches, scope, this_ptr, lhs, _new_val),
|
||||||
#[cfg(not(feature = "no_object"))]
|
// dot_lhs.dot_rhs op= rhs
|
||||||
Expr::Dot(..) => {
|
#[cfg(not(feature = "no_object"))]
|
||||||
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),
|
_ => unreachable!("cannot assign to expression: {:?}", lhs),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
return Ok(Dynamic::UNIT);
|
return Ok(Dynamic::UNIT);
|
||||||
}
|
}
|
||||||
@ -494,27 +500,29 @@ impl Engine {
|
|||||||
// 2) Global modules - packages
|
// 2) Global modules - packages
|
||||||
// 3) Imported modules - functions marked with global namespace
|
// 3) Imported modules - functions marked with global namespace
|
||||||
// 4) Global sub-modules - functions marked with global namespace
|
// 4) Global sub-modules - functions marked with global namespace
|
||||||
let func = self
|
let iter_func = self
|
||||||
.global_modules
|
.global_modules
|
||||||
.iter()
|
.iter()
|
||||||
.find_map(|m| m.get_iter(iter_type));
|
.find_map(|m| m.get_iter(iter_type));
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
let func = func.or_else(|| global.get_iter(iter_type)).or_else(|| {
|
let iter_func = iter_func
|
||||||
self.global_sub_modules
|
.or_else(|| global.get_iter(iter_type))
|
||||||
.iter()
|
.or_else(|| {
|
||||||
.flat_map(|m| m.values())
|
self.global_sub_modules
|
||||||
.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
|
// Restore scope at end of statement
|
||||||
let orig_scope_len = scope.len();
|
auto_restore! {
|
||||||
let scope = &mut *RestoreOnDrop::lock(scope, move |s| {
|
scope => rewind;
|
||||||
s.rewind(orig_scope_len);
|
let orig_scope_len = scope.len();
|
||||||
});
|
}
|
||||||
|
|
||||||
// Add the loop variables
|
// Add the loop variables
|
||||||
let counter_index = if counter.is_empty() {
|
let counter_index = if counter.is_empty() {
|
||||||
usize::MAX
|
usize::MAX
|
||||||
@ -528,7 +536,7 @@ impl Engine {
|
|||||||
|
|
||||||
let mut result = Dynamic::UNIT;
|
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
|
// Increment counter
|
||||||
if counter_index < usize::MAX {
|
if counter_index < usize::MAX {
|
||||||
// As the variable increments from 0, this should always work
|
// As the variable increments from 0, this should always work
|
||||||
@ -596,10 +604,7 @@ impl Engine {
|
|||||||
Stmt::TryCatch(x, ..) => {
|
Stmt::TryCatch(x, ..) => {
|
||||||
let TryCatchBlock {
|
let TryCatchBlock {
|
||||||
try_block,
|
try_block,
|
||||||
catch_var:
|
catch_var,
|
||||||
Ident {
|
|
||||||
name: catch_var, ..
|
|
||||||
},
|
|
||||||
catch_block,
|
catch_block,
|
||||||
} = &**x;
|
} = &**x;
|
||||||
|
|
||||||
@ -644,14 +649,13 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Restore scope at end of block
|
// Restore scope at end of block
|
||||||
let orig_scope_len = scope.len();
|
auto_restore! {
|
||||||
let scope =
|
scope if !catch_var.is_empty() => rewind;
|
||||||
&mut *RestoreOnDrop::lock_if(!catch_var.is_empty(), scope, move |s| {
|
let orig_scope_len = scope.len();
|
||||||
s.rewind(orig_scope_len);
|
}
|
||||||
});
|
|
||||||
|
|
||||||
if !catch_var.is_empty() {
|
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)
|
self.eval_stmt_block(global, caches, scope, this_ptr, catch_block, true)
|
||||||
@ -721,7 +725,8 @@ impl Engine {
|
|||||||
// Evaluate initial value
|
// Evaluate initial value
|
||||||
let mut value = self
|
let mut value = self
|
||||||
.eval_expr(global, caches, scope, this_ptr, expr)?
|
.eval_expr(global, caches, scope, this_ptr, expr)?
|
||||||
.flatten();
|
.flatten()
|
||||||
|
.intern_string(self);
|
||||||
|
|
||||||
let _alias = if !rewind_scope {
|
let _alias = if !rewind_scope {
|
||||||
// Put global constants into global module
|
// Put global constants into global module
|
||||||
@ -759,7 +764,7 @@ impl Engine {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
if let Some(alias) = _alias {
|
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)
|
Ok(Dynamic::UNIT)
|
||||||
@ -826,6 +831,7 @@ impl Engine {
|
|||||||
// Export statement
|
// Export statement
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Stmt::Export(x, ..) => {
|
Stmt::Export(x, ..) => {
|
||||||
|
use crate::ast::Ident;
|
||||||
let (Ident { name, pos, .. }, Ident { name: alias, .. }) = &**x;
|
let (Ident { name, pos, .. }, Ident { name: alias, .. }) = &**x;
|
||||||
// Mark scope variables as public
|
// Mark scope variables as public
|
||||||
scope.search(name).map_or_else(
|
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.
|
/// A type that encapsulates a mutation target for an expression with side effects.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
#[must_use]
|
||||||
pub enum Target<'a> {
|
pub enum Target<'a> {
|
||||||
/// The target is a mutable reference to a [`Dynamic`].
|
/// The target is a mutable reference to a [`Dynamic`].
|
||||||
RefMut(&'a mut Dynamic),
|
RefMut(&'a mut Dynamic),
|
||||||
@ -88,9 +89,9 @@ pub enum Target<'a> {
|
|||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
SharedValue {
|
SharedValue {
|
||||||
/// Lock guard to the shared [`Dynamic`].
|
/// Lock guard to the shared [`Dynamic`].
|
||||||
source: crate::types::dynamic::DynamicWriteLock<'a, Dynamic>,
|
guard: crate::types::dynamic::DynamicWriteLock<'a, Dynamic>,
|
||||||
/// Copy of the value.
|
/// Copy of the shared value.
|
||||||
value: Dynamic,
|
shared_value: Dynamic,
|
||||||
},
|
},
|
||||||
/// The target is a temporary [`Dynamic`] value (i.e. its mutation can cause no side effects).
|
/// The target is a temporary [`Dynamic`] value (i.e. its mutation can cause no side effects).
|
||||||
TempValue(Dynamic),
|
TempValue(Dynamic),
|
||||||
@ -177,13 +178,12 @@ impl<'a> Target<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Is the [`Target`] a shared value?
|
/// Is the [`Target`] a shared value?
|
||||||
#[cfg(not(feature = "no_closure"))]
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_shared(&self) -> bool {
|
pub fn is_shared(&self) -> bool {
|
||||||
match self {
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
return match self {
|
||||||
Self::RefMut(r) => r.is_shared(),
|
Self::RefMut(r) => r.is_shared(),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
|
||||||
Self::SharedValue { .. } => true,
|
Self::SharedValue { .. } => true,
|
||||||
Self::TempValue(value) => value.is_shared(),
|
Self::TempValue(value) => value.is_shared(),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -191,16 +191,17 @@ impl<'a> Target<'a> {
|
|||||||
| Self::BitField { .. }
|
| Self::BitField { .. }
|
||||||
| Self::BlobByte { .. }
|
| Self::BlobByte { .. }
|
||||||
| Self::StringChar { .. } => false,
|
| Self::StringChar { .. } => false,
|
||||||
}
|
};
|
||||||
|
#[cfg(feature = "no_closure")]
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
/// Get the value of the [`Target`] as a [`Dynamic`], cloning a referenced value if necessary.
|
/// Get the value of the [`Target`] as a [`Dynamic`], cloning a referenced value if necessary.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
pub fn take_or_clone(self) -> Dynamic {
|
pub fn take_or_clone(self) -> Dynamic {
|
||||||
match self {
|
match self {
|
||||||
Self::RefMut(r) => r.clone(), // Referenced value is cloned
|
Self::RefMut(r) => r.clone(), // Referenced value is cloned
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[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
|
Self::TempValue(value) => value, // Owned value is simply taken
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::Bit { value, .. } => value, // boolean is taken
|
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.
|
/// Convert a shared or reference [`Target`] into a target with an owned value.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn into_owned(self) -> Self {
|
pub fn into_owned(self) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Self::RefMut(r) => Self::TempValue(r.clone()),
|
Self::RefMut(r) => Self::TempValue(r.clone()),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Self::SharedValue { value, .. } => Self::TempValue(value),
|
Self::SharedValue { shared_value, .. } => Self::TempValue(shared_value),
|
||||||
_ => self,
|
_ => self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -240,7 +240,7 @@ impl<'a> Target<'a> {
|
|||||||
match self {
|
match self {
|
||||||
Self::RefMut(r) => r,
|
Self::RefMut(r) => r,
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Self::SharedValue { source, .. } => source,
|
Self::SharedValue { guard, .. } => guard,
|
||||||
Self::TempValue(value) => value,
|
Self::TempValue(value) => value,
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::Bit { source, .. } => source,
|
Self::Bit { source, .. } => source,
|
||||||
@ -366,9 +366,12 @@ impl<'a> From<&'a mut Dynamic> for Target<'a> {
|
|||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
if value.is_shared() {
|
if value.is_shared() {
|
||||||
// Cloning is cheap for a shared value
|
// Cloning is cheap for a shared value
|
||||||
let val = value.clone();
|
let shared_value = value.clone();
|
||||||
let source = value.write_lock::<Dynamic>().expect("`Dynamic`");
|
let guard = value.write_lock::<Dynamic>().expect("`Dynamic`");
|
||||||
return Self::SharedValue { source, value: val };
|
return Self::SharedValue {
|
||||||
|
guard,
|
||||||
|
shared_value,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::RefMut(value)
|
Self::RefMut(value)
|
||||||
@ -383,7 +386,7 @@ impl Deref for Target<'_> {
|
|||||||
match self {
|
match self {
|
||||||
Self::RefMut(r) => r,
|
Self::RefMut(r) => r,
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Self::SharedValue { source, .. } => source,
|
Self::SharedValue { guard, .. } => guard,
|
||||||
Self::TempValue(ref value) => value,
|
Self::TempValue(ref value) => value,
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::Bit { ref value, .. }
|
Self::Bit { ref value, .. }
|
||||||
@ -416,7 +419,7 @@ impl DerefMut for Target<'_> {
|
|||||||
match self {
|
match self {
|
||||||
Self::RefMut(r) => r,
|
Self::RefMut(r) => r,
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Self::SharedValue { source, .. } => &mut *source,
|
Self::SharedValue { guard, .. } => &mut *guard,
|
||||||
Self::TempValue(ref mut value) => value,
|
Self::TempValue(ref mut value) => value,
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::Bit { ref mut value, .. }
|
Self::Bit { ref mut value, .. }
|
||||||
@ -437,7 +440,6 @@ impl AsMut<Dynamic> for Target<'_> {
|
|||||||
|
|
||||||
impl<T: Into<Dynamic>> From<T> for Target<'_> {
|
impl<T: Into<Dynamic>> From<T> for Target<'_> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
fn from(value: T) -> Self {
|
fn from(value: T) -> Self {
|
||||||
Self::TempValue(value.into())
|
Self::TempValue(value.into())
|
||||||
}
|
}
|
||||||
|
@ -24,8 +24,8 @@ use num_traits::Float;
|
|||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
use rust_decimal::Decimal;
|
use rust_decimal::Decimal;
|
||||||
|
|
||||||
/// The message: data type was checked
|
/// The `unchecked` feature is not active.
|
||||||
const BUILTIN: &str = "data type was checked";
|
const CHECKED_BUILD: bool = cfg!(not(feature = "unchecked"));
|
||||||
|
|
||||||
/// Is the type a numeric type?
|
/// Is the type a numeric type?
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -71,12 +71,12 @@ fn is_numeric(type_id: TypeId) -> bool {
|
|||||||
|
|
||||||
/// A function that returns `true`.
|
/// A function that returns `true`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn const_true_fn(_: NativeCallContext, _: &mut [&mut Dynamic]) -> RhaiResult {
|
fn const_true_fn(_: Option<NativeCallContext>, _: &mut [&mut Dynamic]) -> RhaiResult {
|
||||||
Ok(Dynamic::TRUE)
|
Ok(Dynamic::TRUE)
|
||||||
}
|
}
|
||||||
/// A function that returns `false`.
|
/// A function that returns `false`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn const_false_fn(_: NativeCallContext, _: &mut [&mut Dynamic]) -> RhaiResult {
|
fn const_false_fn(_: Option<NativeCallContext>, _: &mut [&mut Dynamic]) -> RhaiResult {
|
||||||
Ok(Dynamic::FALSE)
|
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.
|
/// The return function will be registered as a _method_, so the first parameter cannot be consumed.
|
||||||
#[must_use]
|
#[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 type1 = x.type_id();
|
||||||
let type2 = y.type_id();
|
let type2 = y.type_id();
|
||||||
|
|
||||||
macro_rules! impl_op {
|
macro_rules! impl_op {
|
||||||
($xx:ident $op:tt $yy:ident) => { |_, args| {
|
($xx:ident $op:tt $yy:ident) => { (|_, args| {
|
||||||
let x = &*args[0].read_lock::<$xx>().expect(BUILTIN);
|
let x = &*args[0].read_lock::<$xx>().unwrap();
|
||||||
let y = &*args[1].read_lock::<$yy>().expect(BUILTIN);
|
let y = &*args[1].read_lock::<$yy>().unwrap();
|
||||||
Ok((x $op y).into())
|
Ok((x $op y).into())
|
||||||
} };
|
}, false) };
|
||||||
($xx:ident . $func:ident ( $yy:ty )) => { |_, args| {
|
($xx:ident . $func:ident ( $yy:ty )) => { (|_, args| {
|
||||||
let x = &*args[0].read_lock::<$xx>().expect(BUILTIN);
|
let x = &*args[0].read_lock::<$xx>().unwrap();
|
||||||
let y = &*args[1].read_lock::<$yy>().expect(BUILTIN);
|
let y = &*args[1].read_lock::<$yy>().unwrap();
|
||||||
Ok(x.$func(y).into())
|
Ok(x.$func(y).into())
|
||||||
} };
|
}, false) };
|
||||||
($xx:ident . $func:ident ( $yy:ident . $yyy:ident () )) => { |_, args| {
|
($xx:ident . $func:ident ( $yy:ident . $yyy:ident () )) => { (|_, args| {
|
||||||
let x = &*args[0].read_lock::<$xx>().expect(BUILTIN);
|
let x = &*args[0].read_lock::<$xx>().unwrap();
|
||||||
let y = &*args[1].read_lock::<$yy>().expect(BUILTIN);
|
let y = &*args[1].read_lock::<$yy>().unwrap();
|
||||||
Ok(x.$func(y.$yyy()).into())
|
Ok(x.$func(y.$yyy()).into())
|
||||||
} };
|
}, false) };
|
||||||
($func:ident ( $op:tt )) => { |_, args| {
|
($func:ident ( $op:tt )) => { (|_, args| {
|
||||||
let (x, y) = $func(args);
|
let (x, y) = $func(args);
|
||||||
Ok((x $op y).into())
|
Ok((x $op y).into())
|
||||||
} };
|
}, false) };
|
||||||
($base:ty => $xx:ident $op:tt $yy:ident) => { |_, args| {
|
($base:ty => $xx:ident $op:tt $yy:ident) => { (|_, args| {
|
||||||
let x = args[0].$xx().expect(BUILTIN) as $base;
|
let x = args[0].$xx().unwrap() as $base;
|
||||||
let y = args[1].$yy().expect(BUILTIN) as $base;
|
let y = args[1].$yy().unwrap() as $base;
|
||||||
Ok((x $op y).into())
|
Ok((x $op y).into())
|
||||||
} };
|
}, false) };
|
||||||
($base:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty)) => { |_, args| {
|
($base:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty)) => { (|_, args| {
|
||||||
let x = args[0].$xx().expect(BUILTIN) as $base;
|
let x = args[0].$xx().unwrap() as $base;
|
||||||
let y = args[1].$yy().expect(BUILTIN) as $base;
|
let y = args[1].$yy().unwrap() as $base;
|
||||||
Ok(x.$func(y as $yyy).into())
|
Ok(x.$func(y as $yyy).into())
|
||||||
} };
|
}, false) };
|
||||||
($base:ty => $func:ident ( $xx:ident, $yy:ident )) => { |_, args| {
|
($base:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| {
|
||||||
let x = args[0].$xx().expect(BUILTIN) as $base;
|
let x = args[0].$xx().unwrap() as $base;
|
||||||
let y = args[1].$yy().expect(BUILTIN) as $base;
|
let y = args[1].$yy().unwrap() as $base;
|
||||||
$func(x, y).map(Into::into)
|
$func(x, y).map(Into::into)
|
||||||
} };
|
}, false) };
|
||||||
(from $base:ty => $xx:ident $op:tt $yy:ident) => { |_, args| {
|
(from $base:ty => $xx:ident $op:tt $yy:ident) => { (|_, args| {
|
||||||
let x = <$base>::from(args[0].$xx().expect(BUILTIN));
|
let x = <$base>::from(args[0].$xx().unwrap());
|
||||||
let y = <$base>::from(args[1].$yy().expect(BUILTIN));
|
let y = <$base>::from(args[1].$yy().unwrap());
|
||||||
Ok((x $op y).into())
|
Ok((x $op y).into())
|
||||||
} };
|
}, false) };
|
||||||
(from $base:ty => $xx:ident . $func:ident ( $yy:ident )) => { |_, args| {
|
(from $base:ty => $xx:ident . $func:ident ( $yy:ident )) => { (|_, args| {
|
||||||
let x = <$base>::from(args[0].$xx().expect(BUILTIN));
|
let x = <$base>::from(args[0].$xx().unwrap());
|
||||||
let y = <$base>::from(args[1].$yy().expect(BUILTIN));
|
let y = <$base>::from(args[1].$yy().unwrap());
|
||||||
Ok(x.$func(y).into())
|
Ok(x.$func(y).into())
|
||||||
} };
|
}, false) };
|
||||||
(from $base:ty => $func:ident ( $xx:ident, $yy:ident )) => { |_, args| {
|
(from $base:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| {
|
||||||
let x = <$base>::from(args[0].$xx().expect(BUILTIN));
|
let x = <$base>::from(args[0].$xx().unwrap());
|
||||||
let y = <$base>::from(args[1].$yy().expect(BUILTIN));
|
let y = <$base>::from(args[1].$yy().unwrap());
|
||||||
$func(x, y).map(Into::into)
|
$func(x, y).map(Into::into)
|
||||||
} };
|
}, false) };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for common patterns
|
// 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)),
|
Ampersand => Some(impl_op!(INT => as_int & as_int)),
|
||||||
Pipe => 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)),
|
XOr => Some(impl_op!(INT => as_int ^ as_int)),
|
||||||
ExclusiveRange => Some(|_, args| {
|
ExclusiveRange => Some(impl_op!(INT => as_int .. as_int)),
|
||||||
let x = args[0].as_int().expect(BUILTIN);
|
InclusiveRange => Some(impl_op!(INT => as_int ..= as_int)),
|
||||||
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())
|
|
||||||
}),
|
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -214,19 +206,20 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
|
|||||||
|
|
||||||
if type1 == TypeId::of::<ImmutableString>() {
|
if type1 == TypeId::of::<ImmutableString>() {
|
||||||
return match op {
|
return match op {
|
||||||
Plus => Some(|_ctx, args| {
|
Plus => Some((
|
||||||
let s1 = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
|_ctx, args| {
|
||||||
let s2 = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN);
|
let s1 = &*args[0].read_lock::<ImmutableString>().unwrap();
|
||||||
|
let s2 = &*args[1].read_lock::<ImmutableString>().unwrap();
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
if !s1.is_empty() && !s2.is_empty() {
|
_ctx.unwrap()
|
||||||
let total_len = s1.len() + s2.len();
|
.engine()
|
||||||
_ctx.engine()
|
.throw_on_size((0, 0, s1.len() + s2.len()))?;
|
||||||
.raise_err_if_over_data_size_limit((0, 0, total_len))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((s1 + s2).into())
|
Ok((s1 + s2).into())
|
||||||
}),
|
},
|
||||||
|
CHECKED_BUILD,
|
||||||
|
)),
|
||||||
Minus => Some(impl_op!(ImmutableString - ImmutableString)),
|
Minus => Some(impl_op!(ImmutableString - ImmutableString)),
|
||||||
EqualsTo => Some(impl_op!(ImmutableString == ImmutableString)),
|
EqualsTo => Some(impl_op!(ImmutableString == ImmutableString)),
|
||||||
NotEqualsTo => 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>() {
|
if type1 == TypeId::of::<char>() {
|
||||||
return match op {
|
return match op {
|
||||||
Plus => Some(|_ctx, args| {
|
Plus => Some((
|
||||||
let x = args[0].as_char().expect(BUILTIN);
|
|_ctx, args| {
|
||||||
let y = args[1].as_char().expect(BUILTIN);
|
let x = args[0].as_char().unwrap();
|
||||||
|
let y = args[1].as_char().unwrap();
|
||||||
|
|
||||||
let mut result = SmartString::new_const();
|
let mut result = SmartString::new_const();
|
||||||
result.push(x);
|
result.push(x);
|
||||||
result.push(y);
|
result.push(y);
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
_ctx.engine()
|
_ctx.unwrap().engine().throw_on_size((0, 0, result.len()))?;
|
||||||
.raise_err_if_over_data_size_limit((0, 0, result.len()))?;
|
|
||||||
|
|
||||||
Ok(result.into())
|
Ok(result.into())
|
||||||
}),
|
},
|
||||||
|
CHECKED_BUILD,
|
||||||
|
)),
|
||||||
EqualsTo => Some(impl_op!(char => as_char == as_char)),
|
EqualsTo => Some(impl_op!(char => as_char == as_char)),
|
||||||
NotEqualsTo => 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)),
|
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;
|
use crate::Blob;
|
||||||
|
|
||||||
return match op {
|
return match op {
|
||||||
Plus => Some(|_ctx, args| {
|
Plus => Some((
|
||||||
let blob1 = &*args[0].read_lock::<Blob>().expect(BUILTIN);
|
|_ctx, args| {
|
||||||
let blob2 = &*args[1].read_lock::<Blob>().expect(BUILTIN);
|
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"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
_ctx.engine().raise_err_if_over_data_size_limit((
|
_ctx.unwrap()
|
||||||
blob1.len() + blob2.len(),
|
.engine()
|
||||||
0,
|
.throw_on_size((b1.len() + b2.len(), 0, 0))?;
|
||||||
0,
|
|
||||||
))?;
|
|
||||||
|
|
||||||
let mut blob = blob1.clone();
|
let mut blob = b1.clone();
|
||||||
blob.extend(blob2);
|
blob.extend(b2);
|
||||||
blob
|
Ok(Dynamic::from_blob(blob))
|
||||||
}))
|
},
|
||||||
}),
|
CHECKED_BUILD,
|
||||||
|
)),
|
||||||
EqualsTo => Some(impl_op!(Blob == Blob)),
|
EqualsTo => Some(impl_op!(Blob == Blob)),
|
||||||
NotEqualsTo => Some(impl_op!(Blob != Blob)),
|
NotEqualsTo => Some(impl_op!(Blob != Blob)),
|
||||||
_ => None,
|
_ => None,
|
||||||
@ -298,9 +294,9 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
|
|||||||
|
|
||||||
if type1 == TypeId::of::<()>() {
|
if type1 == TypeId::of::<()>() {
|
||||||
return match op {
|
return match op {
|
||||||
EqualsTo => Some(const_true_fn),
|
EqualsTo => Some((const_true_fn, false)),
|
||||||
NotEqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => {
|
NotEqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => {
|
||||||
Some(const_false_fn)
|
Some((const_false_fn, false))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
@ -393,8 +389,8 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
|
|||||||
// char op string
|
// char op string
|
||||||
if (type1, type2) == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
|
if (type1, type2) == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
|
||||||
fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) {
|
fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) {
|
||||||
let x = args[0].as_char().expect(BUILTIN);
|
let x = args[0].as_char().unwrap();
|
||||||
let y = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN);
|
let y = &*args[1].read_lock::<ImmutableString>().unwrap();
|
||||||
let s1 = [x, '\0'];
|
let s1 = [x, '\0'];
|
||||||
let mut y = y.chars();
|
let mut y = y.chars();
|
||||||
let s2 = [y.next().unwrap_or('\0'), y.next().unwrap_or('\0')];
|
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 {
|
return match op {
|
||||||
Plus => Some(|_ctx, args| {
|
Plus => Some((
|
||||||
let x = args[0].as_char().expect(BUILTIN);
|
|_ctx, args| {
|
||||||
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 mut result = SmartString::new_const();
|
let mut result = SmartString::new_const();
|
||||||
result.push(x);
|
result.push(x);
|
||||||
result.push_str(y);
|
result.push_str(y);
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
_ctx.engine()
|
_ctx.unwrap().engine().throw_on_size((0, 0, result.len()))?;
|
||||||
.raise_err_if_over_data_size_limit((0, 0, result.len()))?;
|
|
||||||
|
|
||||||
Ok(result.into())
|
Ok(result.into())
|
||||||
}),
|
},
|
||||||
|
CHECKED_BUILD,
|
||||||
|
)),
|
||||||
EqualsTo => Some(impl_op!(get_s1s2(==))),
|
EqualsTo => Some(impl_op!(get_s1s2(==))),
|
||||||
NotEqualsTo => Some(impl_op!(get_s1s2(!=))),
|
NotEqualsTo => Some(impl_op!(get_s1s2(!=))),
|
||||||
GreaterThan => 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
|
// string op char
|
||||||
if (type1, type2) == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
|
if (type1, type2) == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
|
||||||
fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) {
|
fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) {
|
||||||
let x = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
let x = &*args[0].read_lock::<ImmutableString>().unwrap();
|
||||||
let y = args[1].as_char().expect(BUILTIN);
|
let y = args[1].as_char().unwrap();
|
||||||
let mut x = x.chars();
|
let mut x = x.chars();
|
||||||
let s1 = [x.next().unwrap_or('\0'), x.next().unwrap_or('\0')];
|
let s1 = [x.next().unwrap_or('\0'), x.next().unwrap_or('\0')];
|
||||||
let s2 = [y, '\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 {
|
return match op {
|
||||||
Plus => Some(|_ctx, args| {
|
Plus => Some((
|
||||||
let x = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
|_ctx, args| {
|
||||||
let y = args[1].as_char().expect(BUILTIN);
|
let x = &*args[0].read_lock::<ImmutableString>().unwrap();
|
||||||
let result = x + y;
|
let y = args[1].as_char().unwrap();
|
||||||
|
let result = x + y;
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
_ctx.engine()
|
_ctx.unwrap().engine().throw_on_size((0, 0, result.len()))?;
|
||||||
.raise_err_if_over_data_size_limit((0, 0, result.len()))?;
|
|
||||||
|
|
||||||
Ok(result.into())
|
Ok(result.into())
|
||||||
}),
|
},
|
||||||
Minus => Some(|_, args| {
|
CHECKED_BUILD,
|
||||||
let x = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
)),
|
||||||
let y = args[1].as_char().expect(BUILTIN);
|
Minus => Some((
|
||||||
Ok((x - y).into())
|
|_, 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(==))),
|
EqualsTo => Some(impl_op!(get_s1s2(==))),
|
||||||
NotEqualsTo => Some(impl_op!(get_s1s2(!=))),
|
NotEqualsTo => Some(impl_op!(get_s1s2(!=))),
|
||||||
GreaterThan => 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
|
// () op string
|
||||||
if (type1, type2) == (TypeId::of::<()>(), TypeId::of::<ImmutableString>()) {
|
if (type1, type2) == (TypeId::of::<()>(), TypeId::of::<ImmutableString>()) {
|
||||||
return match op {
|
return match op {
|
||||||
Plus => Some(|_, args| Ok(args[1].clone())),
|
Plus => Some((|_, args| Ok(args[1].clone()), false)),
|
||||||
EqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => {
|
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,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// string op ()
|
// string op ()
|
||||||
if (type1, type2) == (TypeId::of::<ImmutableString>(), TypeId::of::<()>()) {
|
if (type1, type2) == (TypeId::of::<ImmutableString>(), TypeId::of::<()>()) {
|
||||||
return match op {
|
return match op {
|
||||||
Plus => Some(|_, args| Ok(args[0].clone())),
|
Plus => Some((|_, args| Ok(args[0].clone()), false)),
|
||||||
EqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => {
|
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,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -492,21 +495,22 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
|
|||||||
|
|
||||||
if type2 == TypeId::of::<char>() {
|
if type2 == TypeId::of::<char>() {
|
||||||
return match op {
|
return match op {
|
||||||
Plus => Some(|_ctx, args| {
|
Plus => Some((
|
||||||
let mut blob = args[0].read_lock::<Blob>().expect(BUILTIN).clone();
|
|_ctx, args| {
|
||||||
let mut buf = [0_u8; 4];
|
let mut blob = args[0].read_lock::<Blob>().unwrap().clone();
|
||||||
let x = args[1].as_char().expect(BUILTIN).encode_utf8(&mut buf);
|
let mut buf = [0_u8; 4];
|
||||||
|
let x = args[1].as_char().unwrap().encode_utf8(&mut buf);
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
_ctx.engine().raise_err_if_over_data_size_limit((
|
_ctx.unwrap()
|
||||||
blob.len() + x.len(),
|
.engine()
|
||||||
0,
|
.throw_on_size((blob.len() + x.len(), 0, 0))?;
|
||||||
0,
|
|
||||||
))?;
|
|
||||||
|
|
||||||
blob.extend(x.as_bytes());
|
blob.extend(x.as_bytes());
|
||||||
Ok(Dynamic::from_blob(blob))
|
Ok(Dynamic::from_blob(blob))
|
||||||
}),
|
},
|
||||||
|
CHECKED_BUILD,
|
||||||
|
)),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -525,8 +529,8 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
return match op {
|
return match op {
|
||||||
NotEqualsTo => Some(const_true_fn),
|
NotEqualsTo => Some((const_true_fn, false)),
|
||||||
Equals => Some(const_false_fn),
|
Equals => Some((const_false_fn, false)),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -556,9 +560,9 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
|
|||||||
} else if type1 != type2 {
|
} else if type1 != type2 {
|
||||||
// If the types are not the same, default to not compare
|
// If the types are not the same, default to not compare
|
||||||
match op {
|
match op {
|
||||||
NotEqualsTo => Some(const_true_fn),
|
NotEqualsTo => Some((const_true_fn, false)),
|
||||||
EqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => {
|
EqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => {
|
||||||
Some(const_false_fn)
|
Some((const_false_fn, false))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => 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
|
// Default comparison operators for different types
|
||||||
if type2 != type1 {
|
if type2 != type1 {
|
||||||
return match op {
|
return match op {
|
||||||
NotEqualsTo => Some(const_true_fn),
|
NotEqualsTo => Some((const_true_fn, false)),
|
||||||
EqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => {
|
EqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => {
|
||||||
Some(const_false_fn)
|
Some((const_false_fn, false))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => 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.
|
/// The return function is registered as a _method_, so the first parameter cannot be consumed.
|
||||||
#[must_use]
|
#[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 type1 = x.type_id();
|
||||||
let type2 = y.type_id();
|
let type2 = y.type_id();
|
||||||
|
|
||||||
macro_rules! impl_op {
|
macro_rules! impl_op {
|
||||||
($x:ty = x $op:tt $yy:ident) => { |_, args| {
|
($x:ty = x $op:tt $yy:ident) => { (|_, args| {
|
||||||
let x = args[0].$yy().expect(BUILTIN);
|
let x = args[0].$yy().unwrap();
|
||||||
let y = args[1].$yy().expect(BUILTIN) as $x;
|
let y = args[1].$yy().unwrap() as $x;
|
||||||
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) = x $op y).into())
|
Ok((*args[0].write_lock::<$x>().unwrap() = x $op y).into())
|
||||||
} };
|
}, false) };
|
||||||
($x:ident $op:tt $yy:ident) => { |_, args| {
|
($x:ident $op:tt $yy:ident) => { (|_, args| {
|
||||||
let y = args[1].$yy().expect(BUILTIN) as $x;
|
let y = args[1].$yy().unwrap() as $x;
|
||||||
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) $op y).into())
|
Ok((*args[0].write_lock::<$x>().unwrap() $op y).into())
|
||||||
} };
|
}, false) };
|
||||||
($x:ident $op:tt $yy:ident as $yyy:ty) => { |_, args| {
|
($x:ident $op:tt $yy:ident as $yyy:ty) => { (|_, args| {
|
||||||
let y = args[1].$yy().expect(BUILTIN) as $yyy;
|
let y = args[1].$yy().unwrap() as $yyy;
|
||||||
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) $op y).into())
|
Ok((*args[0].write_lock::<$x>().unwrap() $op y).into())
|
||||||
} };
|
}, false) };
|
||||||
($x:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty )) => { |_, args| {
|
($x:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty )) => { (|_, args| {
|
||||||
let x = args[0].$xx().expect(BUILTIN);
|
let x = args[0].$xx().unwrap();
|
||||||
let y = args[1].$yy().expect(BUILTIN) as $x;
|
let y = args[1].$yy().unwrap() as $x;
|
||||||
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) = x.$func(y as $yyy)).into())
|
Ok((*args[0].write_lock::<$x>().unwrap() = x.$func(y as $yyy)).into())
|
||||||
} };
|
}, false) };
|
||||||
($x:ty => $func:ident ( $xx:ident, $yy:ident )) => { |_, args| {
|
($x:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| {
|
||||||
let x = args[0].$xx().expect(BUILTIN);
|
let x = args[0].$xx().unwrap();
|
||||||
let y = args[1].$yy().expect(BUILTIN) as $x;
|
let y = args[1].$yy().unwrap() as $x;
|
||||||
Ok((*args[0].write_lock().expect(BUILTIN) = $func(x, y)?).into())
|
Ok((*args[0].write_lock().unwrap() = $func(x, y)?).into())
|
||||||
} };
|
}, false) };
|
||||||
(from $x:ident $op:tt $yy:ident) => { |_, args| {
|
(from $x:ident $op:tt $yy:ident) => { (|_, args| {
|
||||||
let y = <$x>::from(args[1].$yy().expect(BUILTIN));
|
let y = <$x>::from(args[1].$yy().unwrap());
|
||||||
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) $op y).into())
|
Ok((*args[0].write_lock::<$x>().unwrap() $op y).into())
|
||||||
} };
|
}, false) };
|
||||||
(from $x:ty => $xx:ident . $func:ident ( $yy:ident )) => { |_, args| {
|
(from $x:ty => $xx:ident . $func:ident ( $yy:ident )) => { (|_, args| {
|
||||||
let x = args[0].$xx().expect(BUILTIN);
|
let x = args[0].$xx().unwrap();
|
||||||
let y = <$x>::from(args[1].$yy().expect(BUILTIN));
|
let y = <$x>::from(args[1].$yy().unwrap());
|
||||||
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) = x.$func(y)).into())
|
Ok((*args[0].write_lock::<$x>().unwrap() = x.$func(y)).into())
|
||||||
} };
|
}, false) };
|
||||||
(from $x:ty => $func:ident ( $xx:ident, $yy:ident )) => { |_, args| {
|
(from $x:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| {
|
||||||
let x = args[0].$xx().expect(BUILTIN);
|
let x = args[0].$xx().unwrap();
|
||||||
let y = <$x>::from(args[1].$yy().expect(BUILTIN));
|
let y = <$x>::from(args[1].$yy().unwrap());
|
||||||
Ok((*args[0].write_lock().expect(BUILTIN) = $func(x, y)?).into())
|
Ok((*args[0].write_lock().unwrap() = $func(x, y)?).into())
|
||||||
} };
|
}, false) };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for common patterns
|
// 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>() {
|
if type1 == TypeId::of::<char>() {
|
||||||
return match op {
|
return match op {
|
||||||
PlusAssign => Some(|_, args| {
|
PlusAssign => Some((
|
||||||
let y = args[1].as_char().expect(BUILTIN);
|
|_, args| {
|
||||||
let x = &mut *args[0].write_lock::<Dynamic>().expect(BUILTIN);
|
let y = args[1].as_char().unwrap();
|
||||||
|
let x = &mut *args[0].write_lock::<Dynamic>().unwrap();
|
||||||
|
|
||||||
let mut buf = SmartString::new_const();
|
let mut buf = SmartString::new_const();
|
||||||
write!(&mut buf, "{y}").unwrap();
|
write!(&mut buf, "{y}").unwrap();
|
||||||
buf.push(y);
|
buf.push(y);
|
||||||
|
|
||||||
Ok((*x = buf.into()).into())
|
Ok((*x = buf.into()).into())
|
||||||
}),
|
},
|
||||||
|
false,
|
||||||
|
)),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if type1 == TypeId::of::<ImmutableString>() {
|
if type1 == TypeId::of::<ImmutableString>() {
|
||||||
return match op {
|
return match op {
|
||||||
PlusAssign => Some(|_ctx, args| {
|
PlusAssign => Some((
|
||||||
let (first, second) = args.split_first_mut().expect(BUILTIN);
|
|_ctx, args| {
|
||||||
let x = &mut *first.write_lock::<ImmutableString>().expect(BUILTIN);
|
let (first, second) = args.split_first_mut().unwrap();
|
||||||
let y = &*second[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
let x = &mut *first.write_lock::<ImmutableString>().unwrap();
|
||||||
|
let y = &*second[0].read_lock::<ImmutableString>().unwrap();
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
if !x.is_empty() && !y.is_empty() {
|
if !x.is_empty() && !y.is_empty() {
|
||||||
let total_len = x.len() + y.len();
|
let total_len = x.len() + y.len();
|
||||||
_ctx.engine()
|
_ctx.unwrap().engine().throw_on_size((0, 0, total_len))?;
|
||||||
.raise_err_if_over_data_size_limit((0, 0, total_len))?;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Ok((*x += y).into())
|
Ok((*x += y).into())
|
||||||
}),
|
},
|
||||||
MinusAssign => Some(|_, args| {
|
CHECKED_BUILD,
|
||||||
let (first, second) = args.split_first_mut().expect(BUILTIN);
|
)),
|
||||||
let x = &mut *first.write_lock::<ImmutableString>().expect(BUILTIN);
|
MinusAssign => Some((
|
||||||
let y = &*second[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
|_, args| {
|
||||||
Ok((*x -= y).into())
|
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,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -729,27 +741,30 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
|
|||||||
use crate::Array;
|
use crate::Array;
|
||||||
|
|
||||||
return match op {
|
return match op {
|
||||||
PlusAssign => Some(|_ctx, args| {
|
PlusAssign => Some((
|
||||||
let x = std::mem::take(args[1]).into_array().expect(BUILTIN);
|
|_ctx, args| {
|
||||||
|
let x = std::mem::take(args[1]).into_array().unwrap();
|
||||||
|
|
||||||
if x.is_empty() {
|
if x.is_empty() {
|
||||||
return Ok(Dynamic::UNIT);
|
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"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
if !_array_is_empty {
|
if !_array_is_empty {
|
||||||
_ctx.engine().check_data_size(
|
_ctx.unwrap().engine().check_data_size(
|
||||||
&*args[0].read_lock().expect(BUILTIN),
|
&*args[0].read_lock().unwrap(),
|
||||||
crate::Position::NONE,
|
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())
|
Ok(append(array, x).into())
|
||||||
}),
|
},
|
||||||
|
CHECKED_BUILD,
|
||||||
|
)),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -761,19 +776,20 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
|
|||||||
use crate::Blob;
|
use crate::Blob;
|
||||||
|
|
||||||
return match op {
|
return match op {
|
||||||
PlusAssign => Some(|_ctx, args| {
|
PlusAssign => Some((
|
||||||
let blob2 = std::mem::take(args[1]).into_blob().expect(BUILTIN);
|
|_ctx, args| {
|
||||||
let blob1 = &mut *args[0].write_lock::<Blob>().expect(BUILTIN);
|
let blob2 = std::mem::take(args[1]).into_blob().unwrap();
|
||||||
|
let blob1 = &mut *args[0].write_lock::<Blob>().unwrap();
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
_ctx.engine().raise_err_if_over_data_size_limit((
|
_ctx.unwrap()
|
||||||
blob1.len() + blob2.len(),
|
.engine()
|
||||||
0,
|
.throw_on_size((blob1.len() + blob2.len(), 0, 0))?;
|
||||||
0,
|
|
||||||
))?;
|
|
||||||
|
|
||||||
Ok(append(blob1, blob2).into())
|
Ok(append(blob1, blob2).into())
|
||||||
}),
|
},
|
||||||
|
CHECKED_BUILD,
|
||||||
|
)),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -847,17 +863,21 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
|
|||||||
// string op= char
|
// string op= char
|
||||||
if (type1, type2) == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
|
if (type1, type2) == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
|
||||||
return match op {
|
return match op {
|
||||||
PlusAssign => Some(|_ctx, args| {
|
PlusAssign => Some((
|
||||||
let mut buf = [0_u8; 4];
|
|_ctx, args| {
|
||||||
let ch = &*args[1].as_char().expect(BUILTIN).encode_utf8(&mut buf);
|
let mut buf = [0_u8; 4];
|
||||||
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"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
_ctx.engine()
|
_ctx.unwrap()
|
||||||
.raise_err_if_over_data_size_limit((0, 0, x.len() + ch.len()))?;
|
.engine()
|
||||||
|
.throw_on_size((0, 0, x.len() + ch.len()))?;
|
||||||
|
|
||||||
Ok((*x += ch).into())
|
Ok((*x += ch).into())
|
||||||
}),
|
},
|
||||||
|
CHECKED_BUILD,
|
||||||
|
)),
|
||||||
MinusAssign => Some(impl_op!(ImmutableString -= as_char as char)),
|
MinusAssign => Some(impl_op!(ImmutableString -= as_char as char)),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
@ -865,28 +885,32 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
|
|||||||
// char op= string
|
// char op= string
|
||||||
if (type1, type2) == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
|
if (type1, type2) == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
|
||||||
return match op {
|
return match op {
|
||||||
PlusAssign => Some(|_ctx, args| {
|
PlusAssign => Some((
|
||||||
let ch = {
|
|_ctx, args| {
|
||||||
let s = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN);
|
let ch = {
|
||||||
|
let s = &*args[1].read_lock::<ImmutableString>().unwrap();
|
||||||
|
|
||||||
if s.is_empty() {
|
if s.is_empty() {
|
||||||
return Ok(Dynamic::UNIT);
|
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"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
_ctx.engine()
|
_ctx.unwrap()
|
||||||
.raise_err_if_over_data_size_limit((0, 0, ch.len() + s.len()))?;
|
.engine()
|
||||||
|
.throw_on_size((0, 0, ch.len() + s.len()))?;
|
||||||
|
|
||||||
ch.push_str(s);
|
ch.push_str(s);
|
||||||
ch
|
ch
|
||||||
};
|
};
|
||||||
|
|
||||||
*args[0].write_lock::<Dynamic>().expect(BUILTIN) = ch.into();
|
*args[0].write_lock::<Dynamic>().unwrap() = ch.into();
|
||||||
|
|
||||||
Ok(Dynamic::UNIT)
|
Ok(Dynamic::UNIT)
|
||||||
}),
|
},
|
||||||
|
CHECKED_BUILD,
|
||||||
|
)),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -899,21 +923,23 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
|
|||||||
use crate::Array;
|
use crate::Array;
|
||||||
|
|
||||||
return match op {
|
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 x = std::mem::take(args[1]);
|
||||||
push(array, x);
|
let array = &mut *args[0].write_lock::<Array>().unwrap();
|
||||||
}
|
push(array, x);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
_ctx.engine().check_data_size(
|
_ctx.unwrap()
|
||||||
&*args[0].read_lock().expect(BUILTIN),
|
.engine()
|
||||||
crate::Position::NONE,
|
.check_data_size(&*args[0].read_lock().unwrap(), crate::Position::NONE)?;
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(Dynamic::UNIT)
|
Ok(Dynamic::UNIT)
|
||||||
}),
|
},
|
||||||
|
CHECKED_BUILD,
|
||||||
|
)),
|
||||||
_ => None,
|
_ => 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::*;
|
use crate::packages::blob_basic::blob_functions::*;
|
||||||
|
|
||||||
return match op {
|
return match op {
|
||||||
PlusAssign => Some(|_ctx, args| {
|
PlusAssign => Some((
|
||||||
let x = args[1].as_int().expect(BUILTIN);
|
|_ctx, args| {
|
||||||
let blob = &mut *args[0].write_lock::<Blob>().expect(BUILTIN);
|
let x = args[1].as_int().unwrap();
|
||||||
|
let blob = &mut *args[0].write_lock::<Blob>().unwrap();
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
_ctx.engine()
|
_ctx.unwrap()
|
||||||
.raise_err_if_over_data_size_limit((blob.len() + 1, 0, 0))?;
|
.engine()
|
||||||
|
.throw_on_size((blob.len() + 1, 0, 0))?;
|
||||||
|
|
||||||
Ok(push(blob, x).into())
|
Ok(push(blob, x).into())
|
||||||
}),
|
},
|
||||||
|
CHECKED_BUILD,
|
||||||
|
)),
|
||||||
_ => None,
|
_ => 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::*;
|
use crate::packages::blob_basic::blob_functions::*;
|
||||||
|
|
||||||
return match op {
|
return match op {
|
||||||
PlusAssign => Some(|_ctx, args| {
|
PlusAssign => Some((
|
||||||
let x = args[1].as_char().expect(BUILTIN);
|
|_ctx, args| {
|
||||||
let blob = &mut *args[0].write_lock::<Blob>().expect(BUILTIN);
|
let x = args[1].as_char().unwrap();
|
||||||
|
let blob = &mut *args[0].write_lock::<Blob>().unwrap();
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
_ctx.engine()
|
_ctx.unwrap()
|
||||||
.raise_err_if_over_data_size_limit((blob.len() + 1, 0, 0))?;
|
.engine()
|
||||||
|
.throw_on_size((blob.len() + 1, 0, 0))?;
|
||||||
|
|
||||||
Ok(append_char(blob, x).into())
|
Ok(append_char(blob, x).into())
|
||||||
}),
|
},
|
||||||
|
CHECKED_BUILD,
|
||||||
|
)),
|
||||||
_ => None,
|
_ => 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::*;
|
use crate::packages::blob_basic::blob_functions::*;
|
||||||
|
|
||||||
return match op {
|
return match op {
|
||||||
PlusAssign => Some(|_ctx, args| {
|
PlusAssign => Some((
|
||||||
let (first, second) = args.split_first_mut().expect(BUILTIN);
|
|_ctx, args| {
|
||||||
let blob = &mut *first.write_lock::<Blob>().expect(BUILTIN);
|
let (first, second) = args.split_first_mut().unwrap();
|
||||||
let s = &*second[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
let blob = &mut *first.write_lock::<Blob>().unwrap();
|
||||||
|
let s = &*second[0].read_lock::<ImmutableString>().unwrap();
|
||||||
|
|
||||||
if s.is_empty() {
|
if s.is_empty() {
|
||||||
return Ok(Dynamic::UNIT);
|
return Ok(Dynamic::UNIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
_ctx.engine().raise_err_if_over_data_size_limit((
|
_ctx.unwrap()
|
||||||
blob.len() + s.len(),
|
.engine()
|
||||||
0,
|
.throw_on_size((blob.len() + s.len(), 0, 0))?;
|
||||||
0,
|
|
||||||
))?;
|
|
||||||
|
|
||||||
Ok(append_str(blob, s).into())
|
Ok(append_str(blob, s).into())
|
||||||
}),
|
},
|
||||||
|
CHECKED_BUILD,
|
||||||
|
)),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
175
src/func/call.rs
175
src/func/call.rs
@ -8,8 +8,7 @@ use crate::engine::{
|
|||||||
KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF,
|
KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF,
|
||||||
};
|
};
|
||||||
use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState};
|
use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState};
|
||||||
use crate::tokenizer::{is_valid_function_name, Token};
|
use crate::tokenizer::{is_valid_function_name, Token, NO_TOKEN};
|
||||||
use crate::types::RestoreOnDrop;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString,
|
calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString,
|
||||||
OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, Shared, ERR,
|
OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, Shared, ERR,
|
||||||
@ -165,7 +164,7 @@ impl Engine {
|
|||||||
_global: &GlobalRuntimeState,
|
_global: &GlobalRuntimeState,
|
||||||
caches: &'s mut Caches,
|
caches: &'s mut Caches,
|
||||||
local_entry: &'s mut Option<FnResolutionCacheEntry>,
|
local_entry: &'s mut Option<FnResolutionCacheEntry>,
|
||||||
op_token: Option<&Token>,
|
op_token: Token,
|
||||||
hash_base: u64,
|
hash_base: u64,
|
||||||
args: Option<&mut FnCallArgs>,
|
args: Option<&mut FnCallArgs>,
|
||||||
allow_dynamic: bool,
|
allow_dynamic: bool,
|
||||||
@ -174,7 +173,7 @@ impl Engine {
|
|||||||
return None;
|
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()))
|
calc_fn_hash_full(hash_base, args.iter().map(|a| a.type_id()))
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -183,7 +182,7 @@ impl Engine {
|
|||||||
match cache.map.entry(hash) {
|
match cache.map.entry(hash) {
|
||||||
Entry::Occupied(entry) => entry.into_mut().as_ref(),
|
Entry::Occupied(entry) => entry.into_mut().as_ref(),
|
||||||
Entry::Vacant(entry) => {
|
Entry::Vacant(entry) => {
|
||||||
let num_args = args.as_ref().map_or(0, |a| a.len());
|
let num_args = args.as_deref().map_or(0, |a| a.len());
|
||||||
let mut max_bitmask = 0; // One above maximum bitmask based on number of parameters.
|
let mut max_bitmask = 0; // One above maximum bitmask based on number of parameters.
|
||||||
// Set later when a specific matching function is not found.
|
// Set later when a specific matching function is not found.
|
||||||
let mut bitmask = 1usize; // Bitmask of which parameter to replace with `Dynamic`
|
let mut bitmask = 1usize; // Bitmask of which parameter to replace with `Dynamic`
|
||||||
@ -212,9 +211,12 @@ impl Engine {
|
|||||||
} else {
|
} else {
|
||||||
func.or_else(|| _global.get_qualified_fn(hash)).or_else(|| {
|
func.or_else(|| _global.get_qualified_fn(hash)).or_else(|| {
|
||||||
self.global_sub_modules
|
self.global_sub_modules
|
||||||
.iter()
|
.as_deref()
|
||||||
.flat_map(|m| m.values())
|
.into_iter()
|
||||||
.find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw())))
|
.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"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
let is_dynamic = is_dynamic
|
let is_dynamic = is_dynamic
|
||||||
|| _global.may_contain_dynamic_fn(hash_base)
|
|| _global.may_contain_dynamic_fn(hash_base)
|
||||||
|| self
|
|| self.global_sub_modules.as_deref().map_or(false, |m| {
|
||||||
.global_sub_modules
|
m.values().any(|m| m.may_contain_dynamic_fn(hash_base))
|
||||||
.iter()
|
});
|
||||||
.flat_map(|m| m.values())
|
|
||||||
.any(|m| m.may_contain_dynamic_fn(hash_base));
|
|
||||||
|
|
||||||
// Set maximum bitmask when there are dynamic versions of the function
|
// Set maximum bitmask when there are dynamic versions of the function
|
||||||
if is_dynamic {
|
if is_dynamic {
|
||||||
@ -272,22 +272,22 @@ impl Engine {
|
|||||||
// Try to find a built-in version
|
// Try to find a built-in version
|
||||||
let builtin =
|
let builtin =
|
||||||
args.and_then(|args| match op_token {
|
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();
|
let (first_arg, rest_args) = args.split_first().unwrap();
|
||||||
|
|
||||||
get_builtin_op_assignment_fn(token, first_arg, rest_args[0])
|
get_builtin_op_assignment_fn(token, first_arg, rest_args[0])
|
||||||
.map(|f| FnResolutionCacheEntry {
|
.map(|(f, ctx)| FnResolutionCacheEntry {
|
||||||
func: CallableFunction::Method(Shared::new(f)),
|
func: CallableFunction::Method(Shared::new(f), ctx),
|
||||||
source: None,
|
source: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Some(token) => get_builtin_binary_op_fn(token, args[0], args[1])
|
token => get_builtin_binary_op_fn(token, args[0], args[1]).map(
|
||||||
.map(|f| FnResolutionCacheEntry {
|
|(f, ctx)| FnResolutionCacheEntry {
|
||||||
func: CallableFunction::Method(Shared::new(f)),
|
func: CallableFunction::Method(Shared::new(f), ctx),
|
||||||
source: None,
|
source: None,
|
||||||
}),
|
},
|
||||||
|
),
|
||||||
None => None,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return if cache.filter.is_absent_and_set(hash) {
|
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
|
/// # WARNING
|
||||||
///
|
///
|
||||||
@ -340,7 +340,7 @@ impl Engine {
|
|||||||
global: &mut GlobalRuntimeState,
|
global: &mut GlobalRuntimeState,
|
||||||
caches: &mut Caches,
|
caches: &mut Caches,
|
||||||
name: &str,
|
name: &str,
|
||||||
op_token: Option<&Token>,
|
op_token: Token,
|
||||||
hash: u64,
|
hash: u64,
|
||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
is_ref_mut: bool,
|
is_ref_mut: bool,
|
||||||
@ -380,12 +380,10 @@ impl Engine {
|
|||||||
// Clone the first argument
|
// Clone the first argument
|
||||||
backup.change_first_arg_to_copy(args);
|
backup.change_first_arg_to_copy(args);
|
||||||
}
|
}
|
||||||
|
auto_restore!(args if swap => move |a| backup.restore_first_arg(a));
|
||||||
let args =
|
|
||||||
&mut *RestoreOnDrop::lock_if(swap, args, move |a| backup.restore_first_arg(a));
|
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
if self.debugger.is_some() {
|
if self.is_debugger_registered() {
|
||||||
let source = source.clone().or_else(|| global.source.clone());
|
let source = source.clone().or_else(|| global.source.clone());
|
||||||
|
|
||||||
global.debugger_mut().push_call_stack_frame(
|
global.debugger_mut().push_call_stack_frame(
|
||||||
@ -399,25 +397,29 @@ impl Engine {
|
|||||||
// Run external function
|
// Run external function
|
||||||
let is_method = func.is_method();
|
let is_method = func.is_method();
|
||||||
let src = source.as_ref().map(|s| s.as_str());
|
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 context = if func.has_context() {
|
||||||
let f = func.get_plugin_fn().unwrap();
|
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() {
|
if !f.is_pure() && !args.is_empty() && args[0].is_read_only() {
|
||||||
Err(ERR::ErrorNonPureMethodCallOnConstant(name.to_string(), pos).into())
|
Err(ERR::ErrorNonPureMethodCallOnConstant(name.to_string(), pos).into())
|
||||||
} else {
|
} else {
|
||||||
f.call(context, args)
|
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 {
|
} 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))
|
.and_then(|r| self.check_data_size(r, pos))
|
||||||
};
|
.map_err(|err| err.fill_position(pos));
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
if self.debugger.is_some() {
|
if self.is_debugger_registered() {
|
||||||
use crate::eval::{DebuggerEvent, DebuggerStatus};
|
use crate::eval::{DebuggerEvent, DebuggerStatus};
|
||||||
|
|
||||||
let trigger = match global.debugger().status {
|
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
|
/// # WARNING
|
||||||
///
|
///
|
||||||
@ -559,7 +561,7 @@ impl Engine {
|
|||||||
caches: &mut Caches,
|
caches: &mut Caches,
|
||||||
_scope: Option<&mut Scope>,
|
_scope: Option<&mut Scope>,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
op_token: Option<&Token>,
|
op_token: Token,
|
||||||
hashes: FnCallHashes,
|
hashes: FnCallHashes,
|
||||||
mut _args: &mut FnCallArgs,
|
mut _args: &mut FnCallArgs,
|
||||||
is_ref_mut: bool,
|
is_ref_mut: bool,
|
||||||
@ -578,9 +580,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
ensure_no_data_race(fn_name, _args, is_ref_mut)?;
|
ensure_no_data_race(fn_name, _args, is_ref_mut)?;
|
||||||
|
|
||||||
let orig_level = global.level;
|
auto_restore! { let orig_level = global.level; global.level += 1 }
|
||||||
global.level += 1;
|
|
||||||
let global = &mut *RestoreOnDrop::lock(global, move |g| g.level = orig_level);
|
|
||||||
|
|
||||||
// These may be redirected from method style calls.
|
// These may be redirected from method style calls.
|
||||||
if hashes.is_native_only() {
|
if hashes.is_native_only() {
|
||||||
@ -638,7 +638,7 @@ impl Engine {
|
|||||||
let local_entry = &mut None;
|
let local_entry = &mut None;
|
||||||
|
|
||||||
if let Some(FnResolutionCacheEntry { func, ref source }) = self
|
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()
|
.cloned()
|
||||||
{
|
{
|
||||||
// Script function call
|
// Script function call
|
||||||
@ -659,7 +659,7 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let orig_source = mem::replace(&mut global.source, source.clone());
|
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 {
|
return if _is_method_call {
|
||||||
// Method call of script function - map first argument to `this`
|
// Method call of script function - map first argument to `this`
|
||||||
@ -679,9 +679,7 @@ impl Engine {
|
|||||||
backup.change_first_arg_to_copy(_args);
|
backup.change_first_arg_to_copy(_args);
|
||||||
}
|
}
|
||||||
|
|
||||||
let args = &mut *RestoreOnDrop::lock_if(swap, _args, move |a| {
|
auto_restore!(args = (_args) if swap => move |a| backup.restore_first_arg(a));
|
||||||
backup.restore_first_arg(a)
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut this = Dynamic::NULL;
|
let mut this = Dynamic::NULL;
|
||||||
|
|
||||||
@ -721,15 +719,13 @@ impl Engine {
|
|||||||
|
|
||||||
// Do not match function exit for arguments
|
// Do not match function exit for arguments
|
||||||
#[cfg(feature = "debugging")]
|
#[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| {
|
dbg.clear_status_if(|status| {
|
||||||
matches!(status, crate::eval::DebuggerStatus::FunctionExit(..))
|
matches!(status, crate::eval::DebuggerStatus::FunctionExit(..))
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
let global = &mut *RestoreOnDrop::lock_if(reset.is_some(), global, move |g| {
|
auto_restore!(global if reset.is_some() => move |g| g.debugger_mut().reset_status(reset));
|
||||||
g.debugger_mut().reset_status(reset)
|
|
||||||
});
|
|
||||||
|
|
||||||
self.eval_expr(global, caches, scope, this_ptr, arg_expr)
|
self.eval_expr(global, caches, scope, this_ptr, arg_expr)
|
||||||
.map(|r| (r, arg_expr.start_position()))
|
.map(|r| (r, arg_expr.start_position()))
|
||||||
@ -782,7 +778,7 @@ impl Engine {
|
|||||||
caches,
|
caches,
|
||||||
None,
|
None,
|
||||||
fn_name,
|
fn_name,
|
||||||
None,
|
NO_TOKEN,
|
||||||
new_hash,
|
new_hash,
|
||||||
&mut args,
|
&mut args,
|
||||||
false,
|
false,
|
||||||
@ -836,7 +832,7 @@ impl Engine {
|
|||||||
caches,
|
caches,
|
||||||
None,
|
None,
|
||||||
&fn_name,
|
&fn_name,
|
||||||
None,
|
NO_TOKEN,
|
||||||
new_hash,
|
new_hash,
|
||||||
&mut args,
|
&mut args,
|
||||||
is_ref_mut,
|
is_ref_mut,
|
||||||
@ -935,7 +931,7 @@ impl Engine {
|
|||||||
caches,
|
caches,
|
||||||
None,
|
None,
|
||||||
fn_name,
|
fn_name,
|
||||||
None,
|
NO_TOKEN,
|
||||||
hash,
|
hash,
|
||||||
&mut args,
|
&mut args,
|
||||||
is_ref_mut,
|
is_ref_mut,
|
||||||
@ -961,7 +957,7 @@ impl Engine {
|
|||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
this_ptr: &mut Dynamic,
|
this_ptr: &mut Dynamic,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
op_token: Option<&Token>,
|
op_token: Token,
|
||||||
first_arg: Option<&Expr>,
|
first_arg: Option<&Expr>,
|
||||||
args_expr: &[Expr],
|
args_expr: &[Expr],
|
||||||
hashes: FnCallHashes,
|
hashes: FnCallHashes,
|
||||||
@ -977,7 +973,7 @@ impl Engine {
|
|||||||
let redirected; // Handle call() - Redirect function call
|
let redirected; // Handle call() - Redirect function call
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
_ if op_token.is_some() => (),
|
_ if op_token != NO_TOKEN => (),
|
||||||
|
|
||||||
// Handle call()
|
// Handle call()
|
||||||
KEYWORD_FN_PTR_CALL if total_args >= 1 => {
|
KEYWORD_FN_PTR_CALL if total_args >= 1 => {
|
||||||
@ -1208,12 +1204,7 @@ impl Engine {
|
|||||||
|
|
||||||
self.track_operation(global, first_expr.position())?;
|
self.track_operation(global, first_expr.position())?;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
if target.is_shared() || target.is_temp_value() {
|
||||||
let target_is_shared = target.is_shared();
|
|
||||||
#[cfg(feature = "no_closure")]
|
|
||||||
let target_is_shared = false;
|
|
||||||
|
|
||||||
if target_is_shared || target.is_temp_value() {
|
|
||||||
arg_values.insert(0, target.take_or_clone().flatten());
|
arg_values.insert(0, target.take_or_clone().flatten());
|
||||||
} else {
|
} else {
|
||||||
// Turn it into a method call only if the object is not shared and not a simple value
|
// 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;
|
auto_restore! { let orig_level = global.level; global.level += 1 }
|
||||||
global.level += 1;
|
|
||||||
let global = &mut *RestoreOnDrop::lock(global, move |g| g.level = orig_level);
|
|
||||||
|
|
||||||
match func {
|
match func {
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
@ -1382,7 +1371,7 @@ impl Engine {
|
|||||||
let mut this = Dynamic::NULL;
|
let mut this = Dynamic::NULL;
|
||||||
|
|
||||||
let orig_source = mem::replace(&mut global.source, module.id_raw().cloned());
|
let orig_source = mem::replace(&mut global.source, module.id_raw().cloned());
|
||||||
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(
|
self.call_script_fn(
|
||||||
global, caches, new_scope, &mut this, fn_def, &mut args, true, pos,
|
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() => {
|
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 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() {
|
if !f.is_pure() && !args.is_empty() && args[0].is_read_only() {
|
||||||
Err(ERR::ErrorNonPureMethodCallOnConstant(fn_name.to_string(), pos).into())
|
Err(ERR::ErrorNonPureMethodCallOnConstant(fn_name.to_string(), pos).into())
|
||||||
} else {
|
} else {
|
||||||
@ -1402,7 +1395,11 @@ impl Engine {
|
|||||||
|
|
||||||
Some(f) if f.is_native() => {
|
Some(f) if f.is_native() => {
|
||||||
let func = f.get_native_fn().expect("native function");
|
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))
|
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)
|
self.eval_global_statements(global, caches, scope, statements)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// # Main Entry-Point (`FnCallExpr`)
|
||||||
|
///
|
||||||
/// Evaluate a function call expression.
|
/// Evaluate a function call expression.
|
||||||
pub(crate) fn eval_fn_call_expr(
|
pub(crate) fn eval_fn_call_expr(
|
||||||
&self,
|
&self,
|
||||||
@ -1488,10 +1487,26 @@ impl Engine {
|
|||||||
..
|
..
|
||||||
} = expr;
|
} = 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
|
// 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
|
let mut lhs = self
|
||||||
.get_arg_value(global, caches, scope, this_ptr, &args[0])?
|
.get_arg_value(global, caches, scope, this_ptr, &args[0])?
|
||||||
.0
|
.0
|
||||||
@ -1504,15 +1519,17 @@ impl Engine {
|
|||||||
|
|
||||||
let operands = &mut [&mut lhs, &mut rhs];
|
let operands = &mut [&mut lhs, &mut rhs];
|
||||||
|
|
||||||
if let Some(func) =
|
if let Some((func, ctx)) =
|
||||||
get_builtin_binary_op_fn(op_token.as_ref().unwrap(), operands[0], operands[1])
|
get_builtin_binary_op_fn(op_token.clone(), operands[0], operands[1])
|
||||||
{
|
{
|
||||||
// Built-in found
|
// Built-in found
|
||||||
let orig_level = global.level;
|
auto_restore! { let orig_level = global.level; global.level += 1 }
|
||||||
global.level += 1;
|
|
||||||
let global = &*RestoreOnDrop::lock(global, move |g| g.level = orig_level);
|
|
||||||
|
|
||||||
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);
|
return func(context, operands);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,10 +14,10 @@ use std::prelude::v1::*;
|
|||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum CallableFunction {
|
pub enum CallableFunction {
|
||||||
/// A pure native Rust function with all arguments passed by value.
|
/// 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,
|
/// A native Rust object method with the first argument passed by reference,
|
||||||
/// and the rest passed by value.
|
/// and the rest passed by value.
|
||||||
Method(Shared<FnAny>),
|
Method(Shared<FnAny>, bool),
|
||||||
/// An iterator function.
|
/// An iterator function.
|
||||||
Iterator(Shared<IteratorFn>),
|
Iterator(Shared<IteratorFn>),
|
||||||
/// A plugin function,
|
/// A plugin function,
|
||||||
@ -136,6 +136,18 @@ impl CallableFunction {
|
|||||||
Self::Script(..) => false,
|
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.
|
/// Get the access mode.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -156,7 +168,7 @@ impl CallableFunction {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_native_fn(&self) -> Option<&Shared<FnAny>> {
|
pub fn get_native_fn(&self) -> Option<&Shared<FnAny>> {
|
||||||
match self {
|
match self {
|
||||||
Self::Pure(f) | Self::Method(f) => Some(f),
|
Self::Pure(f, ..) | Self::Method(f, ..) => Some(f),
|
||||||
Self::Iterator(..) | Self::Plugin(..) => None,
|
Self::Iterator(..) | Self::Plugin(..) => None,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
@ -204,16 +216,16 @@ impl CallableFunction {
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
impl From<crate::ast::ScriptFnDef> for CallableFunction {
|
impl From<crate::ast::ScriptFnDef> for CallableFunction {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(_func: crate::ast::ScriptFnDef) -> Self {
|
fn from(func: crate::ast::ScriptFnDef) -> Self {
|
||||||
Self::Script(_func.into())
|
Self::Script(func.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
impl From<Shared<crate::ast::ScriptFnDef>> for CallableFunction {
|
impl From<Shared<crate::ast::ScriptFnDef>> for CallableFunction {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(_func: Shared<crate::ast::ScriptFnDef>) -> Self {
|
fn from(func: Shared<crate::ast::ScriptFnDef>) -> Self {
|
||||||
Self::Script(_func)
|
Self::Script(func)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ use std::prelude::v1::*;
|
|||||||
/// Trait to create a Rust closure from a script.
|
/// Trait to create a Rust closure from a script.
|
||||||
///
|
///
|
||||||
/// Not available under `no_function`.
|
/// Not available under `no_function`.
|
||||||
pub trait Func<ARGS, RET> {
|
pub trait Func<A, R> {
|
||||||
/// The closure's output type.
|
/// The closure's output type.
|
||||||
type Output;
|
type Output;
|
||||||
|
|
||||||
@ -91,14 +91,14 @@ macro_rules! def_anonymous_fn {
|
|||||||
impl<$($par: Variant + Clone,)* RET: Variant + Clone> Func<($($par,)*), RET> for Engine
|
impl<$($par: Variant + Clone,)* RET: Variant + Clone> Func<($($par,)*), RET> for Engine
|
||||||
{
|
{
|
||||||
#[cfg(feature = "sync")]
|
#[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"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
type Output = Box<dyn Fn($($par),*) -> RhaiResultOf<RET>>;
|
type Output = Box<dyn Fn($($par,)*) -> RhaiResultOf<RET>>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn create_from_ast(self, ast: AST, entry_point: &str) -> Self::Output {
|
fn create_from_ast(self, ast: AST, entry_point: &str) -> Self::Output {
|
||||||
let fn_name: SmartString = entry_point.into();
|
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]
|
#[inline]
|
||||||
|
@ -4,11 +4,11 @@ use super::call::FnCallArgs;
|
|||||||
use crate::ast::FnCallHashes;
|
use crate::ast::FnCallHashes;
|
||||||
use crate::eval::{Caches, GlobalRuntimeState};
|
use crate::eval::{Caches, GlobalRuntimeState};
|
||||||
use crate::plugin::PluginFunction;
|
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::types::dynamic::Variant;
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_fn_hash, reify, Dynamic, Engine, EvalContext, FuncArgs, Position, RhaiResult,
|
calc_fn_hash, Dynamic, Engine, EvalContext, FuncArgs, Position, RhaiResult, RhaiResultOf,
|
||||||
RhaiResultOf, StaticVec, VarDefInfo, ERR,
|
StaticVec, VarDefInfo, ERR,
|
||||||
};
|
};
|
||||||
use std::any::{type_name, TypeId};
|
use std::any::{type_name, TypeId};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
@ -424,8 +424,7 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
let caches = &mut Caches::new();
|
let caches = &mut Caches::new();
|
||||||
|
|
||||||
let fn_name = fn_name.as_ref();
|
let fn_name = fn_name.as_ref();
|
||||||
let op_token = Token::lookup_symbol_from_syntax(fn_name);
|
let op_token = Token::lookup_symbol_from_syntax(fn_name).unwrap_or(NO_TOKEN);
|
||||||
let op_token = op_token.as_ref();
|
|
||||||
let args_len = args.len();
|
let args_len = args.len();
|
||||||
|
|
||||||
global.level += 1;
|
global.level += 1;
|
||||||
@ -547,13 +546,16 @@ pub fn locked_write<T>(value: &Locked<T>) -> LockGuardMut<T> {
|
|||||||
|
|
||||||
/// General Rust function trail object.
|
/// General Rust function trail object.
|
||||||
#[cfg(not(feature = "sync"))]
|
#[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.
|
/// General Rust function trail object.
|
||||||
#[cfg(feature = "sync")]
|
#[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.
|
/// 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.
|
/// Function that gets an iterator from a type.
|
||||||
#[cfg(not(feature = "sync"))]
|
#[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.
|
/// Use the `#[export_module]` and `#[export_fn]` procedural attributes instead.
|
||||||
pub trait PluginFunction {
|
pub trait PluginFunction {
|
||||||
/// Call the plugin function with the arguments provided.
|
/// 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?
|
/// Is this plugin function a method?
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn is_method_call(&self) -> bool;
|
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?
|
/// Is this plugin function pure?
|
||||||
///
|
///
|
||||||
/// This defaults to `true` such that any old implementation that has constant-checking code
|
/// 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.
|
//! Module which defines the function registration mechanism.
|
||||||
|
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
#![allow(unused_imports)]
|
||||||
|
#![allow(unused_mut)]
|
||||||
|
#![allow(unused_variables)]
|
||||||
|
|
||||||
use super::call::FnCallArgs;
|
use super::call::FnCallArgs;
|
||||||
use super::callable_function::CallableFunction;
|
use super::callable_function::CallableFunction;
|
||||||
use super::native::{SendSync, Shared};
|
use super::native::{SendSync, Shared};
|
||||||
use crate::types::dynamic::{DynamicWriteLock, Variant};
|
use crate::types::dynamic::{DynamicWriteLock, Variant};
|
||||||
use crate::{reify, Dynamic, NativeCallContext, RhaiResultOf};
|
use crate::{Dynamic, Identifier, NativeCallContext, RhaiResultOf};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
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
|
/// 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.
|
/// of function parameter types in order to make each trait implementation unique.
|
||||||
@ -19,13 +25,13 @@ use std::{any::TypeId, mem};
|
|||||||
///
|
///
|
||||||
/// # Examples
|
/// # 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.
|
/// These types are not actually used anywhere.
|
||||||
pub struct Mut<T>(T);
|
pub struct Mut<T>(T);
|
||||||
@ -64,123 +70,137 @@ pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T {
|
|||||||
///
|
///
|
||||||
/// # Type Parameters
|
/// # Type Parameters
|
||||||
///
|
///
|
||||||
/// * `ARGS` - a tuple containing parameter types, with `&mut T` represented by `Mut<T>`.
|
/// * `A` - 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.
|
/// * `N` - a constant generic containing the number of parameters, must be consistent with `ARGS`.
|
||||||
pub trait RegisterNativeFunction<ARGS, RET, RESULT> {
|
/// * `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`].
|
/// Convert this function into a [`CallableFunction`].
|
||||||
#[must_use]
|
#[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.
|
/// Get the type ID's of this function's parameters.
|
||||||
#[must_use]
|
#[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.
|
/// _(metadata)_ Get the type names of this function's parameters.
|
||||||
/// Exported under the `metadata` feature only.
|
/// Exported under the `metadata` feature only.
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
#[must_use]
|
#[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.
|
/// _(metadata)_ Get the type ID of this function's return value.
|
||||||
/// Exported under the `metadata` feature only.
|
/// Exported under the `metadata` feature only.
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
|
#[inline(always)]
|
||||||
#[must_use]
|
#[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.
|
/// _(metadata)_ Get the type name of this function's return value.
|
||||||
/// Exported under the `metadata` feature only.
|
/// Exported under the `metadata` feature only.
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn return_type_name() -> &'static str {
|
fn return_type_name() -> &'static str {
|
||||||
std::any::type_name::<RET>()
|
type_name::<R>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const EXPECT_ARGS: &str = "arguments";
|
|
||||||
|
|
||||||
macro_rules! check_constant {
|
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")))]
|
#[cfg(any(not(feature = "no_object"), not(feature = "no_index")))]
|
||||||
if stringify!($abi) == "Method" && !$args.is_empty() {
|
if stringify!($abi) == "Method" && $no_const && $args[0].is_read_only() {
|
||||||
let deny = match $args.len() {
|
return Err(crate::ERR::ErrorNonPureMethodCallOnConstant(
|
||||||
#[cfg(not(feature = "no_index"))]
|
$fn_name.to_string(),
|
||||||
3 if $ctx.fn_name() == crate::engine::FN_IDX_SET && $args[0].is_read_only() => true,
|
crate::Position::NONE,
|
||||||
#[cfg(not(feature = "no_object"))]
|
)
|
||||||
2 if $ctx.fn_name().starts_with(crate::engine::FN_SET)
|
.into());
|
||||||
&& $args[0].is_read_only() =>
|
|
||||||
{
|
|
||||||
true
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if deny {
|
|
||||||
return Err(crate::ERR::ErrorNonPureMethodCallOnConstant(
|
|
||||||
$ctx.fn_name().to_string(),
|
|
||||||
crate::Position::NONE,
|
|
||||||
)
|
|
||||||
.into());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! def_register {
|
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
|
// ^ function ABI type
|
||||||
// ^ function parameter generic type name (A, B, C etc.)
|
// ^ number of parameters
|
||||||
// ^ call argument(like A, *B, &mut C etc)
|
// ^ function parameter generic type name (A, B, C etc.)
|
||||||
// ^ function parameter marker type (A, Ref<B> or Mut<C>)
|
// ^ call argument(like A, *B, &mut C etc)
|
||||||
// ^ function parameter actual type (A, &B or &mut C)
|
// ^ function parameter marker type (A, Ref<B> or Mut<C>)
|
||||||
// ^ argument let statement
|
// ^ function parameter actual type (A, &B or &mut C)
|
||||||
|
// ^ parameter access function (by_value or by_ref)
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
FN: Fn($($param),*) -> RET + SendSync + 'static,
|
FN: Fn($($param),*) -> RET + SendSync + 'static,
|
||||||
$($par: Variant + Clone,)*
|
$($par: Variant + Clone,)*
|
||||||
RET: Variant + Clone
|
RET: Variant + Clone,
|
||||||
> RegisterNativeFunction<($($mark,)*), RET, ()> for FN {
|
> RegisterNativeFunction<($($mark,)*), $n, false, RET, false> for FN {
|
||||||
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
|
#[inline(always)] fn param_types() -> [TypeId;$n] { [$(TypeId::of::<$par>()),*] }
|
||||||
#[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 param_names() -> [&'static str;$n] { [$(type_name::<$param>()),*] }
|
||||||
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<RET>() }
|
#[inline(always)] fn into_callable_function(self, fn_name: Identifier, no_const: bool) -> CallableFunction {
|
||||||
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
CallableFunction::$abi(Shared::new(move |_, args: &mut FnCallArgs| {
|
||||||
CallableFunction::$abi(Shared::new(move |_ctx: NativeCallContext, args: &mut FnCallArgs| {
|
|
||||||
// The arguments are assumed to be of the correct number and types!
|
// 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 mut drain = args.iter_mut();
|
||||||
$($let $par = ($clone)(_drain.next().expect(EXPECT_ARGS)); )*
|
$(let mut $par = $clone(drain.next().unwrap()); )*
|
||||||
|
|
||||||
// Call the function with each argument value
|
// Call the function with each argument value
|
||||||
let r = self($($arg),*);
|
let r = self($($arg),*);
|
||||||
|
|
||||||
// Map the result
|
// Map the result
|
||||||
Ok(Dynamic::from(r))
|
Ok(Dynamic::from(r))
|
||||||
}))
|
}), false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
FN: for<'a> Fn(NativeCallContext<'a>, $($param),*) -> RET + SendSync + 'static,
|
FN: for<'a> Fn(NativeCallContext<'a>, $($param),*) -> RET + SendSync + 'static,
|
||||||
$($par: Variant + Clone,)*
|
$($par: Variant + Clone,)*
|
||||||
RET: Variant + Clone
|
RET: Variant + Clone,
|
||||||
> RegisterNativeFunction<($($mark,)*), RET, NativeCallContext<'static>> for FN {
|
> RegisterNativeFunction<($($mark,)*), $n, true, RET, false> for FN {
|
||||||
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
|
#[inline(always)] fn param_types() -> [TypeId;$n] { [$(TypeId::of::<$par>()),*] }
|
||||||
#[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 param_names() -> [&'static str;$n] { [$(type_name::<$param>()),*] }
|
||||||
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<RET>() }
|
#[inline(always)] fn into_callable_function(self, fn_name: Identifier, no_const: bool) -> CallableFunction {
|
||||||
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
CallableFunction::$abi(Shared::new(move |ctx: Option<NativeCallContext>, args: &mut FnCallArgs| {
|
||||||
CallableFunction::$abi(Shared::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
|
let ctx = ctx.unwrap();
|
||||||
// The arguments are assumed to be of the correct number and types!
|
|
||||||
check_constant!($abi, ctx, args);
|
|
||||||
|
|
||||||
let mut _drain = args.iter_mut();
|
// The arguments are assumed to be of the correct number and types!
|
||||||
$($let $par = ($clone)(_drain.next().expect(EXPECT_ARGS)); )*
|
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
|
// Call the function with each argument value
|
||||||
let r = self(ctx, $($arg),*);
|
let r = self(ctx, $($arg),*);
|
||||||
|
|
||||||
// Map the result
|
// Map the result
|
||||||
Ok(Dynamic::from(r))
|
Ok(Dynamic::from(r))
|
||||||
}))
|
}), true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,22 +208,21 @@ macro_rules! def_register {
|
|||||||
FN: Fn($($param),*) -> RhaiResultOf<RET> + SendSync + 'static,
|
FN: Fn($($param),*) -> RhaiResultOf<RET> + SendSync + 'static,
|
||||||
$($par: Variant + Clone,)*
|
$($par: Variant + Clone,)*
|
||||||
RET: Variant + Clone
|
RET: Variant + Clone
|
||||||
> RegisterNativeFunction<($($mark,)*), RET, RhaiResultOf<()>> for FN {
|
> RegisterNativeFunction<($($mark,)*), $n, false, RET, true> for FN {
|
||||||
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
|
#[inline(always)] fn param_types() -> [TypeId;$n] { [$(TypeId::of::<$par>()),*] }
|
||||||
#[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 param_names() -> [&'static str;$n] { [$(type_name::<$param>()),*] }
|
||||||
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<RhaiResultOf<RET>>() }
|
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { type_name::<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, fn_name: Identifier, no_const: bool) -> CallableFunction {
|
||||||
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
CallableFunction::$abi(Shared::new(move |_, args: &mut FnCallArgs| {
|
||||||
CallableFunction::$abi(Shared::new(move |_ctx: NativeCallContext, args: &mut FnCallArgs| {
|
|
||||||
// The arguments are assumed to be of the correct number and types!
|
// 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 mut drain = args.iter_mut();
|
||||||
$($let $par = ($clone)(_drain.next().expect(EXPECT_ARGS)); )*
|
$(let mut $par = $clone(drain.next().unwrap()); )*
|
||||||
|
|
||||||
// Call the function with each argument value
|
// Call the function with each argument value
|
||||||
self($($arg),*).map(Dynamic::from)
|
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,
|
FN: for<'a> Fn(NativeCallContext<'a>, $($param),*) -> RhaiResultOf<RET> + SendSync + 'static,
|
||||||
$($par: Variant + Clone,)*
|
$($par: Variant + Clone,)*
|
||||||
RET: Variant + Clone
|
RET: Variant + Clone
|
||||||
> RegisterNativeFunction<($($mark,)*), RET, RhaiResultOf<NativeCallContext<'static>>> for FN {
|
> RegisterNativeFunction<($($mark,)*), $n, true, RET, true> for FN {
|
||||||
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
|
#[inline(always)] fn param_types() -> [TypeId;$n] { [$(TypeId::of::<$par>()),*] }
|
||||||
#[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 param_names() -> [&'static str;$n] { [$(type_name::<$param>()),*] }
|
||||||
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<RhaiResultOf<RET>>() }
|
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { type_name::<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, fn_name: Identifier, no_const: bool) -> CallableFunction {
|
||||||
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
CallableFunction::$abi(Shared::new(move |ctx: Option<NativeCallContext>, args: &mut FnCallArgs| {
|
||||||
CallableFunction::$abi(Shared::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
|
let ctx = ctx.unwrap();
|
||||||
// The arguments are assumed to be of the correct number and types!
|
|
||||||
check_constant!($abi, ctx, args);
|
|
||||||
|
|
||||||
let mut _drain = args.iter_mut();
|
// The arguments are assumed to be of the correct number and types!
|
||||||
$($let $par = ($clone)(_drain.next().expect(EXPECT_ARGS)); )*
|
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
|
// Call the function with each argument value
|
||||||
self(ctx, $($arg),*).map(Dynamic::from)
|
self(ctx, $($arg),*).map(Dynamic::from)
|
||||||
}))
|
}), true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//def_register!(imp_pop $($par => $mark => $param),*);
|
//def_register!(imp_pop $($par => $mark => $param),*);
|
||||||
};
|
};
|
||||||
($p0:ident $(, $p:ident)*) => {
|
($p0:ident:$n0:expr $(, $p:ident: $n:expr)*) => {
|
||||||
def_register!(imp Pure : $p0 => $p0 => $p0 => $p0 => let $p0 => by_value $(, $p => $p => $p => $p => let $p => by_value)*);
|
def_register!(imp Pure : $n0 ; $p0 => $p0 => $p0 => $p0 => by_value $(, $p => $p => $p => $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)*);
|
def_register!(imp Method : $n0 ; $p0 => &mut $p0 => Mut<$p0> => &mut $p0 => by_ref $(, $p => $p => $p => $p => by_value)*);
|
||||||
// ^ CallableFunction constructor
|
// ^ CallableFunction constructor
|
||||||
// ^ first parameter passed through
|
// ^ number of arguments ^ first parameter passed through
|
||||||
// ^ others passed by value (by_value)
|
// ^ others passed by value (by_value)
|
||||||
|
|
||||||
// Currently does not support first argument which is a reference, as there will be
|
// Currently does not support first argument which is a reference, as there will be
|
||||||
// conflicting implementations since &T: Any and T: Any cannot be distinguished
|
// 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!(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 super::call::FnCallArgs;
|
||||||
use crate::ast::ScriptFnDef;
|
use crate::ast::ScriptFnDef;
|
||||||
use crate::eval::{Caches, GlobalRuntimeState};
|
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;
|
use std::mem;
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -33,32 +33,6 @@ impl Engine {
|
|||||||
rewind_scope: bool,
|
rewind_scope: bool,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> RhaiResult {
|
) -> 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());
|
assert!(fn_def.params.len() == args.len());
|
||||||
|
|
||||||
self.track_operation(global, pos)?;
|
self.track_operation(global, pos)?;
|
||||||
@ -69,7 +43,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[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);
|
return Ok(Dynamic::UNIT);
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "debugging"))]
|
#[cfg(not(feature = "debugging"))]
|
||||||
@ -96,15 +70,14 @@ impl Engine {
|
|||||||
|
|
||||||
// Push a new call stack frame
|
// Push a new call stack frame
|
||||||
#[cfg(feature = "debugging")]
|
#[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();
|
let source = global.source.clone();
|
||||||
|
|
||||||
global.debugger_mut().push_call_stack_frame(
|
global
|
||||||
fn_def.name.clone(),
|
.debugger_mut()
|
||||||
scope.iter().skip(orig_scope_len).map(|(.., v)| v).collect(),
|
.push_call_stack_frame(fn_name, args, source, pos);
|
||||||
source,
|
|
||||||
pos,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge in encapsulated environment, if any
|
// Merge in encapsulated environment, if any
|
||||||
@ -131,37 +104,42 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
if self.debugger.is_some() {
|
if self.is_debugger_registered() {
|
||||||
let node = crate::ast::Stmt::Noop(fn_def.body.position());
|
let node = crate::ast::Stmt::Noop(fn_def.body.position());
|
||||||
self.run_debugger(global, caches, scope, this_ptr, &node)?;
|
self.run_debugger(global, caches, scope, this_ptr, &node)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate the function
|
// 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)
|
.eval_stmt_block(global, caches, scope, this_ptr, &fn_def.body, rewind_scope)
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
// Convert return statement to return value
|
// Convert return statement to return value
|
||||||
ERR::Return(x, ..) => Ok(x),
|
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
|
// System errors are passed straight-through
|
||||||
mut err if err.is_system_exception() => {
|
mut err if err.is_system_exception() => {
|
||||||
err.set_position(pos);
|
err.set_position(pos);
|
||||||
Err(err.into())
|
Err(err.into())
|
||||||
}
|
}
|
||||||
// Other errors are wrapped in `ErrorInFunctionCall`
|
// 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")]
|
#[cfg(feature = "debugging")]
|
||||||
if self.debugger.is_some() {
|
if self.is_debugger_registered() {
|
||||||
let trigger = match global.debugger_mut().status {
|
let trigger = match global.debugger_mut().status {
|
||||||
crate::eval::DebuggerStatus::FunctionExit(n) => n >= global.level,
|
crate::eval::DebuggerStatus::FunctionExit(n) => n >= global.level,
|
||||||
crate::eval::DebuggerStatus::Next(.., true) => true,
|
crate::eval::DebuggerStatus::Next(.., true) => true,
|
||||||
@ -236,7 +214,9 @@ impl Engine {
|
|||||||
// Then check imported modules
|
// Then check imported modules
|
||||||
global.contains_qualified_fn(hash_script)
|
global.contains_qualified_fn(hash_script)
|
||||||
// Then check sub-modules
|
// Then check sub-modules
|
||||||
|| 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) {
|
if !result && !cache.filter.is_absent_and_set(hash_script) {
|
||||||
// Do not cache "one-hit wonders"
|
// 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::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
// Internal modules
|
// Internal modules
|
||||||
|
#[macro_use]
|
||||||
|
mod reify;
|
||||||
|
#[macro_use]
|
||||||
|
mod types;
|
||||||
|
|
||||||
mod api;
|
mod api;
|
||||||
mod ast;
|
mod ast;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
@ -95,10 +100,8 @@ mod module;
|
|||||||
mod optimizer;
|
mod optimizer;
|
||||||
pub mod packages;
|
pub mod packages;
|
||||||
mod parser;
|
mod parser;
|
||||||
mod reify;
|
|
||||||
mod tests;
|
mod tests;
|
||||||
mod tokenizer;
|
mod tokenizer;
|
||||||
mod types;
|
|
||||||
|
|
||||||
/// Error encountered when parsing a script.
|
/// Error encountered when parsing a script.
|
||||||
type PERR = ParseErrorType;
|
type PERR = ParseErrorType;
|
||||||
@ -207,9 +210,9 @@ pub use engine::{Engine, OP_CONTAINS, OP_EQUALS};
|
|||||||
pub use eval::EvalContext;
|
pub use eval::EvalContext;
|
||||||
pub use func::{NativeCallContext, RegisterNativeFunction};
|
pub use func::{NativeCallContext, RegisterNativeFunction};
|
||||||
pub use module::{FnNamespace, Module};
|
pub use module::{FnNamespace, Module};
|
||||||
pub use tokenizer::Position;
|
|
||||||
#[cfg(not(feature = "no_time"))]
|
#[cfg(not(feature = "no_time"))]
|
||||||
pub use types::Instant;
|
pub use types::Instant;
|
||||||
|
pub use types::Position;
|
||||||
pub use types::{
|
pub use types::{
|
||||||
Dynamic, EvalAltResult, FnPtr, ImmutableString, LexError, ParseError, ParseErrorType, Scope,
|
Dynamic, EvalAltResult, FnPtr, ImmutableString, LexError, ParseError, ParseErrorType, Scope,
|
||||||
};
|
};
|
||||||
@ -311,12 +314,12 @@ pub use types::dynamic::{AccessMode, DynamicReadLock, DynamicWriteLock, Variant}
|
|||||||
pub use types::FloatWrapper;
|
pub use types::FloatWrapper;
|
||||||
|
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
pub use types::StringsInterner;
|
pub use types::{Span, StringsInterner};
|
||||||
|
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
pub use tokenizer::{
|
pub use tokenizer::{
|
||||||
get_next_token, is_valid_function_name, is_valid_identifier, parse_string_literal, InputStream,
|
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,
|
TokenizerControlBlock,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -9,8 +9,8 @@ use crate::func::{
|
|||||||
};
|
};
|
||||||
use crate::types::{dynamic::Variant, BloomFilterU64, CustomTypesCollection};
|
use crate::types::{dynamic::Variant, BloomFilterU64, CustomTypesCollection};
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_fn_hash, calc_fn_hash_full, Dynamic, Identifier, ImmutableString, NativeCallContext,
|
calc_fn_hash, calc_fn_hash_full, Dynamic, FnArgsVec, Identifier, ImmutableString,
|
||||||
RhaiResultOf, Shared, SharedModule, SmartString, StaticVec,
|
NativeCallContext, RhaiResultOf, Shared, SharedModule, SmartString,
|
||||||
};
|
};
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
@ -72,20 +72,20 @@ pub struct FuncInfoMetadata {
|
|||||||
/// Function access mode.
|
/// Function access mode.
|
||||||
pub access: FnAccess,
|
pub access: FnAccess,
|
||||||
/// Function name.
|
/// Function name.
|
||||||
pub name: ImmutableString,
|
pub name: Identifier,
|
||||||
/// Number of parameters.
|
/// Number of parameters.
|
||||||
pub num_params: usize,
|
pub num_params: usize,
|
||||||
/// Parameter types (if applicable).
|
/// Parameter types (if applicable).
|
||||||
pub param_types: StaticVec<TypeId>,
|
pub param_types: FnArgsVec<TypeId>,
|
||||||
/// Parameter names and types (if available).
|
/// Parameter names and types (if available).
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
pub params_info: StaticVec<Identifier>,
|
pub params_info: FnArgsVec<Identifier>,
|
||||||
/// Return type name.
|
/// Return type name.
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
pub return_type: Identifier,
|
pub return_type: Identifier,
|
||||||
/// Comments.
|
/// Comments.
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
pub comments: Box<[Box<str>]>,
|
pub comments: Box<[Identifier]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type containing a single registered function.
|
/// A type containing a single registered function.
|
||||||
@ -115,7 +115,7 @@ impl FuncInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let params: StaticVec<_> = self
|
let params: FnArgsVec<_> = self
|
||||||
.metadata
|
.metadata
|
||||||
.params_info
|
.params_info
|
||||||
.iter()
|
.iter()
|
||||||
@ -231,7 +231,8 @@ impl fmt::Debug for Module {
|
|||||||
"modules",
|
"modules",
|
||||||
&self
|
&self
|
||||||
.modules
|
.modules
|
||||||
.iter()
|
.as_deref()
|
||||||
|
.into_iter()
|
||||||
.flat_map(|m| m.keys())
|
.flat_map(|m| m.keys())
|
||||||
.map(SmartString::as_str)
|
.map(SmartString::as_str)
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
@ -392,7 +393,7 @@ impl Module {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn doc(&self) -> &str {
|
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`].
|
/// Set the documentation of the [`Module`].
|
||||||
@ -542,16 +543,28 @@ impl Module {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
!self.flags.contains(ModuleFlags::INDEXED_GLOBAL_FUNCTIONS)
|
!self.flags.contains(ModuleFlags::INDEXED_GLOBAL_FUNCTIONS)
|
||||||
&& self.functions.as_ref().map_or(true, |m| m.is_empty())
|
&& self
|
||||||
&& self.variables.as_ref().map_or(true, |m| m.is_empty())
|
.functions
|
||||||
&& self.modules.as_ref().map_or(true, |m| m.is_empty())
|
.as_ref()
|
||||||
&& self.type_iterators.as_ref().map_or(true, |t| t.is_empty())
|
.map_or(true, StraightHashMap::is_empty)
|
||||||
&& self.all_functions.as_ref().map_or(true, |m| m.is_empty())
|
&& self.variables.as_deref().map_or(true, BTreeMap::is_empty)
|
||||||
&& self.all_variables.as_ref().map_or(true, |m| m.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
|
&& self
|
||||||
.all_type_iterators
|
.all_type_iterators
|
||||||
.as_ref()
|
.as_deref()
|
||||||
.map_or(true, |m| m.is_empty())
|
.map_or(true, BTreeMap::is_empty)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the [`Module`] indexed?
|
/// Is the [`Module`] indexed?
|
||||||
@ -710,7 +723,7 @@ impl Module {
|
|||||||
namespace: FnNamespace::Internal,
|
namespace: FnNamespace::Internal,
|
||||||
access: fn_def.access,
|
access: fn_def.access,
|
||||||
num_params,
|
num_params,
|
||||||
param_types: StaticVec::new_const(),
|
param_types: FnArgsVec::new_const(),
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
params_info,
|
params_info,
|
||||||
#[cfg(feature = "metadata")]
|
#[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.
|
/// In other words, the number of entries should be one larger than the number of parameters.
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn update_fn_metadata<S: AsRef<str>>(
|
pub fn update_fn_metadata<S: Into<Identifier>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
hash_fn: u64,
|
hash_fn: u64,
|
||||||
arg_names: impl AsRef<[S]>,
|
arg_names: impl IntoIterator<Item = S>,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
let mut param_names: StaticVec<_> = arg_names
|
let mut param_names: FnArgsVec<_> = arg_names.into_iter().map(Into::into).collect();
|
||||||
.as_ref()
|
|
||||||
.iter()
|
|
||||||
.map(|s| s.as_ref().into())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if let Some(f) = self.functions.as_mut().and_then(|m| m.get_mut(&hash_fn)) {
|
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 {
|
let (param_names, return_type_name) = if param_names.len() > f.metadata.num_params {
|
||||||
@ -907,32 +916,30 @@ impl Module {
|
|||||||
///
|
///
|
||||||
/// ## Comments
|
/// ## 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
|
/// Leading white-spaces should be stripped, and each string slice always starts with the corresponding
|
||||||
/// doc-comment leader: `///` or `/**`.
|
/// doc-comment leader: `///` or `/**`.
|
||||||
|
///
|
||||||
|
/// Each line in non-block doc-comments should start with `///`.
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
#[inline]
|
#[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,
|
&mut self,
|
||||||
hash_fn: u64,
|
hash_fn: u64,
|
||||||
arg_names: impl AsRef<[A]>,
|
arg_names: impl IntoIterator<Item = A>,
|
||||||
comments: impl AsRef<[C]>,
|
comments: impl IntoIterator<Item = C>,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.update_fn_metadata(hash_fn, arg_names);
|
self.update_fn_metadata(hash_fn, arg_names);
|
||||||
|
|
||||||
let comments = comments.as_ref();
|
self.functions
|
||||||
|
.as_mut()
|
||||||
if !comments.is_empty() {
|
.and_then(|m| m.get_mut(&hash_fn))
|
||||||
let f = self
|
.unwrap()
|
||||||
.functions
|
.metadata
|
||||||
.as_mut()
|
.comments = comments.into_iter().map(Into::into).collect();
|
||||||
.and_then(|m| m.get_mut(&hash_fn))
|
|
||||||
.unwrap();
|
|
||||||
f.metadata.comments = comments.iter().map(|s| s.as_ref().into()).collect();
|
|
||||||
}
|
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -997,12 +1004,11 @@ impl Module {
|
|||||||
let _arg_names = arg_names;
|
let _arg_names = arg_names;
|
||||||
let is_method = func.is_method();
|
let is_method = func.is_method();
|
||||||
|
|
||||||
let mut param_types: StaticVec<_> = arg_types
|
let mut param_types: FnArgsVec<_> = arg_types
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.iter()
|
.iter()
|
||||||
.copied()
|
|
||||||
.enumerate()
|
.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();
|
.collect();
|
||||||
param_types.shrink_to_fit();
|
param_types.shrink_to_fit();
|
||||||
|
|
||||||
@ -1013,11 +1019,11 @@ impl Module {
|
|||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
let (param_names, return_type_name) = {
|
let (param_names, return_type_name) = {
|
||||||
let mut names = _arg_names
|
let mut names = _arg_names
|
||||||
.iter()
|
.into_iter()
|
||||||
.flat_map(|&p| p.iter())
|
.flatten()
|
||||||
.map(|&s| s.into())
|
.map(|&s| s.into())
|
||||||
.collect::<StaticVec<_>>();
|
.collect::<FnArgsVec<_>>();
|
||||||
let return_type = if names.len() > arg_types.as_ref().len() {
|
let return_type = if names.len() > param_types.len() {
|
||||||
names.pop().unwrap()
|
names.pop().unwrap()
|
||||||
} else {
|
} else {
|
||||||
crate::SmartString::new_const()
|
crate::SmartString::new_const()
|
||||||
@ -1086,12 +1092,14 @@ impl Module {
|
|||||||
///
|
///
|
||||||
/// ## Comments
|
/// ## 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
|
/// Leading white-spaces should be stripped, and each string slice always starts with the corresponding
|
||||||
/// doc-comment leader: `///` or `/**`.
|
/// doc-comment leader: `///` or `/**`.
|
||||||
|
///
|
||||||
|
/// Each line in non-block doc-comments should start with `///`.
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_fn_with_comments<S: AsRef<str>>(
|
pub fn set_fn_with_comments<S: AsRef<str>>(
|
||||||
@ -1101,17 +1109,18 @@ impl Module {
|
|||||||
access: FnAccess,
|
access: FnAccess,
|
||||||
arg_names: Option<&[&str]>,
|
arg_names: Option<&[&str]>,
|
||||||
arg_types: impl AsRef<[TypeId]>,
|
arg_types: impl AsRef<[TypeId]>,
|
||||||
comments: impl AsRef<[S]>,
|
comments: impl IntoIterator<Item = S>,
|
||||||
func: CallableFunction,
|
func: CallableFunction,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let hash = self.set_fn(name, namespace, access, arg_names, arg_types, func);
|
let hash = self.set_fn(name, namespace, access, arg_names, arg_types, func);
|
||||||
|
|
||||||
let comments = comments.as_ref();
|
self.functions
|
||||||
|
.as_mut()
|
||||||
if !comments.is_empty() {
|
.unwrap()
|
||||||
let f = self.functions.as_mut().unwrap().get_mut(&hash).unwrap();
|
.get_mut(&hash)
|
||||||
f.metadata.comments = comments.iter().map(|s| s.as_ref().into()).collect();
|
.unwrap()
|
||||||
}
|
.metadata
|
||||||
|
.comments = comments.into_iter().map(|s| s.as_ref().into()).collect();
|
||||||
|
|
||||||
hash
|
hash
|
||||||
}
|
}
|
||||||
@ -1191,8 +1200,9 @@ impl Module {
|
|||||||
arg_types: impl AsRef<[TypeId]>,
|
arg_types: impl AsRef<[TypeId]>,
|
||||||
func: impl Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResultOf<T> + SendSync + 'static,
|
func: impl Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResultOf<T> + SendSync + 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f =
|
let f = move |ctx: Option<NativeCallContext>, args: &mut FnCallArgs| {
|
||||||
move |ctx: NativeCallContext, args: &mut FnCallArgs| func(ctx, args).map(Dynamic::from);
|
func(ctx.unwrap(), args).map(Dynamic::from)
|
||||||
|
};
|
||||||
|
|
||||||
self.set_fn(
|
self.set_fn(
|
||||||
name,
|
name,
|
||||||
@ -1200,7 +1210,7 @@ impl Module {
|
|||||||
access,
|
access,
|
||||||
None,
|
None,
|
||||||
arg_types,
|
arg_types,
|
||||||
CallableFunction::Method(Shared::new(f)),
|
CallableFunction::Method(Shared::new(f), true),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1227,22 +1237,33 @@ impl Module {
|
|||||||
/// assert!(module.contains_fn(hash));
|
/// assert!(module.contains_fn(hash));
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[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,
|
&mut self,
|
||||||
name: impl AsRef<str> + Into<Identifier>,
|
name: impl AsRef<str> + Into<Identifier>,
|
||||||
func: F,
|
func: F,
|
||||||
) -> u64
|
) -> u64
|
||||||
where
|
where
|
||||||
T: Variant + Clone,
|
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(
|
self.set_fn(
|
||||||
name,
|
fn_name,
|
||||||
FnNamespace::Internal,
|
FnNamespace::Internal,
|
||||||
FnAccess::Public,
|
FnAccess::Public,
|
||||||
None,
|
None,
|
||||||
&F::param_types(),
|
F::param_types(),
|
||||||
func.into_callable_function(),
|
func,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1266,19 +1287,22 @@ impl Module {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[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
|
where
|
||||||
A: Variant + Clone,
|
A: Variant + Clone,
|
||||||
T: 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(
|
self.set_fn(
|
||||||
crate::engine::make_getter(name.as_ref()).as_str(),
|
fn_name,
|
||||||
FnNamespace::Global,
|
FnNamespace::Global,
|
||||||
FnAccess::Public,
|
FnAccess::Public,
|
||||||
None,
|
None,
|
||||||
&F::param_types(),
|
F::param_types(),
|
||||||
func.into_callable_function(),
|
func,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1307,19 +1331,22 @@ impl Module {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[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
|
where
|
||||||
A: Variant + Clone,
|
A: Variant + Clone,
|
||||||
T: 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(
|
self.set_fn(
|
||||||
crate::engine::make_setter(name.as_ref()).as_str(),
|
fn_name,
|
||||||
FnNamespace::Global,
|
FnNamespace::Global,
|
||||||
FnAccess::Public,
|
FnAccess::Public,
|
||||||
None,
|
None,
|
||||||
&F::param_types(),
|
F::param_types(),
|
||||||
func.into_callable_function(),
|
func,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1353,11 +1380,16 @@ impl Module {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[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,
|
&mut self,
|
||||||
name: impl AsRef<str>,
|
name: impl AsRef<str>,
|
||||||
getter: impl RegisterNativeFunction<(Mut<A>,), T, RhaiResultOf<S1>> + SendSync + 'static,
|
getter: impl RegisterNativeFunction<(Mut<A>,), 1, C1, T, true> + SendSync + 'static,
|
||||||
setter: impl RegisterNativeFunction<(Mut<A>, T), (), RhaiResultOf<S2>> + SendSync + 'static,
|
setter: impl RegisterNativeFunction<(Mut<A>, T), 2, C2, (), true> + SendSync + 'static,
|
||||||
) -> (u64, u64) {
|
) -> (u64, u64) {
|
||||||
(
|
(
|
||||||
self.set_getter_fn(name.as_ref(), getter),
|
self.set_getter_fn(name.as_ref(), getter),
|
||||||
@ -1394,12 +1426,12 @@ impl Module {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[inline]
|
#[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
|
where
|
||||||
A: Variant + Clone,
|
A: Variant + Clone,
|
||||||
B: Variant + Clone,
|
B: Variant + Clone,
|
||||||
T: 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"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
if TypeId::of::<A>() == TypeId::of::<crate::Array>() {
|
if TypeId::of::<A>() == TypeId::of::<crate::Array>() {
|
||||||
@ -1421,8 +1453,8 @@ impl Module {
|
|||||||
FnNamespace::Global,
|
FnNamespace::Global,
|
||||||
FnAccess::Public,
|
FnAccess::Public,
|
||||||
None,
|
None,
|
||||||
&F::param_types(),
|
F::param_types(),
|
||||||
func.into_callable_function(),
|
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")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[inline]
|
#[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
|
where
|
||||||
A: Variant + Clone,
|
A: Variant + Clone,
|
||||||
B: Variant + Clone,
|
B: Variant + Clone,
|
||||||
T: 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"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
if TypeId::of::<A>() == TypeId::of::<crate::Array>() {
|
if TypeId::of::<A>() == TypeId::of::<crate::Array>() {
|
||||||
@ -1482,8 +1514,8 @@ impl Module {
|
|||||||
FnNamespace::Global,
|
FnNamespace::Global,
|
||||||
FnAccess::Public,
|
FnAccess::Public,
|
||||||
None,
|
None,
|
||||||
&F::param_types(),
|
F::param_types(),
|
||||||
func.into_callable_function(),
|
func.into_callable_function(crate::engine::FN_IDX_SET.into(), true),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1527,13 +1559,13 @@ impl Module {
|
|||||||
pub fn set_indexer_get_set_fn<
|
pub fn set_indexer_get_set_fn<
|
||||||
A: Variant + Clone,
|
A: Variant + Clone,
|
||||||
B: Variant + Clone,
|
B: Variant + Clone,
|
||||||
|
const C1: bool,
|
||||||
|
const C2: bool,
|
||||||
T: Variant + Clone,
|
T: Variant + Clone,
|
||||||
S1,
|
|
||||||
S2,
|
|
||||||
>(
|
>(
|
||||||
&mut self,
|
&mut self,
|
||||||
get_fn: impl RegisterNativeFunction<(Mut<A>, B), T, RhaiResultOf<S1>> + SendSync + 'static,
|
get_fn: impl RegisterNativeFunction<(Mut<A>, B), 2, C1, T, true> + SendSync + 'static,
|
||||||
set_fn: impl RegisterNativeFunction<(Mut<A>, B, T), (), RhaiResultOf<S2>> + SendSync + 'static,
|
set_fn: impl RegisterNativeFunction<(Mut<A>, B, T), 3, C2, (), true> + SendSync + 'static,
|
||||||
) -> (u64, u64) {
|
) -> (u64, u64) {
|
||||||
(
|
(
|
||||||
self.set_indexer_get_fn(get_fn),
|
self.set_indexer_get_fn(get_fn),
|
||||||
@ -1633,8 +1665,8 @@ impl Module {
|
|||||||
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
||||||
|
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
if !other.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_ref().map_or(true, |s| s.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.get_or_insert_with(Default::default).push('\n');
|
||||||
}
|
}
|
||||||
self.doc
|
self.doc
|
||||||
@ -1689,8 +1721,8 @@ impl Module {
|
|||||||
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
||||||
|
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
if !other.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_ref().map_or(true, |s| s.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.get_or_insert_with(Default::default).push('\n');
|
||||||
}
|
}
|
||||||
self.doc
|
self.doc
|
||||||
@ -1754,8 +1786,8 @@ impl Module {
|
|||||||
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
||||||
|
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
if !other.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_ref().map_or(true, |s| s.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.get_or_insert_with(Default::default).push('\n');
|
||||||
}
|
}
|
||||||
self.doc
|
self.doc
|
||||||
@ -1837,8 +1869,8 @@ impl Module {
|
|||||||
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
||||||
|
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
if !other.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_ref().map_or(true, |s| s.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.get_or_insert_with(Default::default).push('\n');
|
||||||
}
|
}
|
||||||
self.doc
|
self.doc
|
||||||
@ -1886,9 +1918,9 @@ impl Module {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn count(&self) -> (usize, usize, usize) {
|
pub fn count(&self) -> (usize, usize, usize) {
|
||||||
(
|
(
|
||||||
self.variables.as_ref().map_or(0, |m| m.len()),
|
self.variables.as_deref().map_or(0, BTreeMap::len),
|
||||||
self.functions.as_ref().map_or(0, |m| m.len()),
|
self.functions.as_ref().map_or(0, StraightHashMap::len),
|
||||||
self.type_iterators.as_ref().map_or(0, |t| t.len()),
|
self.type_iterators.as_deref().map_or(0, BTreeMap::len),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1896,16 +1928,20 @@ impl Module {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn iter_sub_modules(&self) -> impl Iterator<Item = (&str, &SharedModule)> {
|
pub fn iter_sub_modules(&self) -> impl Iterator<Item = (&str, &SharedModule)> {
|
||||||
self.modules
|
self.modules
|
||||||
.iter()
|
.as_deref()
|
||||||
.flat_map(|m| m.iter().map(|(k, m)| (k.as_str(), m)))
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.map(|(k, m)| (k.as_str(), m))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an iterator to the variables in the [`Module`].
|
/// Get an iterator to the variables in the [`Module`].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn iter_var(&self) -> impl Iterator<Item = (&str, &Dynamic)> {
|
pub fn iter_var(&self) -> impl Iterator<Item = (&str, &Dynamic)> {
|
||||||
self.variables
|
self.variables
|
||||||
.iter()
|
.as_deref()
|
||||||
.flat_map(|m| m.iter().map(|(k, v)| (k.as_str(), v)))
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.map(|(k, v)| (k.as_str(), v))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an iterator to the functions in the [`Module`].
|
/// Get an iterator to the functions in the [`Module`].
|
||||||
@ -2065,7 +2101,7 @@ impl Module {
|
|||||||
let mut module = Module::new();
|
let mut module = Module::new();
|
||||||
|
|
||||||
// Extra modules left become sub-modules
|
// Extra modules left become sub-modules
|
||||||
let mut imports = StaticVec::new_const();
|
let mut imports = crate::StaticVec::new_const();
|
||||||
|
|
||||||
if result.is_ok() {
|
if result.is_ok() {
|
||||||
global
|
global
|
||||||
@ -2088,7 +2124,8 @@ impl Module {
|
|||||||
|
|
||||||
global.source = orig_source;
|
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
|
// Variables with an alias left in the scope become module variables
|
||||||
for (_name, value, mut aliases) in scope {
|
for (_name, value, mut aliases) in scope {
|
||||||
@ -2223,7 +2260,7 @@ impl Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Index all Rust functions
|
// 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 {
|
match f.metadata.namespace {
|
||||||
FnNamespace::Global => {
|
FnNamespace::Global => {
|
||||||
// Flatten all functions with global namespace
|
// Flatten all functions with global namespace
|
||||||
@ -2260,11 +2297,11 @@ impl Module {
|
|||||||
if !self.is_indexed() {
|
if !self.is_indexed() {
|
||||||
let mut path = Vec::with_capacity(4);
|
let mut path = Vec::with_capacity(4);
|
||||||
let mut variables = StraightHashMap::with_capacity_and_hasher(
|
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(),
|
Default::default(),
|
||||||
);
|
);
|
||||||
let mut functions = StraightHashMap::with_capacity_and_hasher(
|
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(),
|
Default::default(),
|
||||||
);
|
);
|
||||||
let mut type_iterators = BTreeMap::new();
|
let mut type_iterators = BTreeMap::new();
|
||||||
|
@ -170,7 +170,7 @@ impl FileModuleResolver {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn base_path(&self) -> Option<&Path> {
|
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.
|
/// Set the base path for script files.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
328
src/optimizer.rs
328
src/optimizer.rs
@ -24,6 +24,9 @@ use std::{
|
|||||||
mem,
|
mem,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Standard not operator.
|
||||||
|
const OP_NOT: &str = Token::Bang.literal_syntax();
|
||||||
|
|
||||||
/// Level of optimization performed.
|
/// Level of optimization performed.
|
||||||
#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)]
|
#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
@ -65,18 +68,19 @@ struct OptimizerState<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> OptimizerState<'a> {
|
impl<'a> OptimizerState<'a> {
|
||||||
/// Create a new State.
|
/// Create a new [`OptimizerState`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
engine: &'a Engine,
|
engine: &'a Engine,
|
||||||
#[cfg(not(feature = "no_function"))] lib: &'a [crate::SharedModule],
|
lib: &'a [crate::SharedModule],
|
||||||
optimization_level: OptimizationLevel,
|
optimization_level: OptimizationLevel,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut _global = GlobalRuntimeState::new(engine);
|
let mut _global = GlobalRuntimeState::new(engine);
|
||||||
|
let _lib = lib;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
{
|
{
|
||||||
_global.lib = lib.iter().cloned().collect();
|
_global.lib = _lib.iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
@ -138,7 +142,7 @@ impl<'a> OptimizerState<'a> {
|
|||||||
pub fn call_fn_with_constant_arguments(
|
pub fn call_fn_with_constant_arguments(
|
||||||
&mut self,
|
&mut self,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
op_token: Option<&Token>,
|
op_token: Token,
|
||||||
arg_values: &mut [Dynamic],
|
arg_values: &mut [Dynamic],
|
||||||
) -> Dynamic {
|
) -> Dynamic {
|
||||||
self.engine
|
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].
|
/// Optimize a block of [statements][Stmt].
|
||||||
fn optimize_stmt_block(
|
fn optimize_stmt_block(
|
||||||
mut statements: StmtBlockContainer,
|
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() => {
|
Expr::InterpolatedString(x, pos) if x.is_empty() => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*expr = Expr::StringConstant(state.engine.get_interned_string(""), *pos);
|
*expr = Expr::StringConstant(state.engine.const_empty_string(), *pos);
|
||||||
}
|
}
|
||||||
// `... ${const} ...`
|
// `... ${const} ...`
|
||||||
Expr::InterpolatedString(..) if expr.is_constant() => {
|
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);
|
*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!
|
// eval!
|
||||||
Expr::FnCall(x, ..) if x.name == KEYWORD_EVAL => {
|
Expr::FnCall(x, ..) if x.name == KEYWORD_EVAL => {
|
||||||
state.propagate_constants = false;
|
state.propagate_constants = false;
|
||||||
@ -1150,10 +1135,14 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Overloaded operators can override built-in.
|
// 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 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.as_ref().unwrap(), &arg_values[0], &arg_values[1])
|
if let Some(result) = get_builtin_binary_op_fn(x.op_token.clone(), &arg_values[0], &arg_values[1])
|
||||||
.and_then(|f| {
|
.and_then(|(f, ctx)| {
|
||||||
let context = (state.engine, x.name.as_str(),None, &state.global, *pos).into();
|
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();
|
let (first, second) = arg_values.split_first_mut().unwrap();
|
||||||
(f)(context, &mut [ first, &mut second[0] ]).ok()
|
(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(),
|
KEYWORD_TYPE_OF if arg_values.len() == 1 => state.engine.map_type_name(arg_values[0].type_name()).into(),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
crate::engine::KEYWORD_IS_SHARED if arg_values.len() == 1 => Dynamic::FALSE,
|
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() {
|
if !result.is_null() {
|
||||||
@ -1260,124 +1249,143 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Optimize a block of [statements][Stmt] at top level.
|
impl Engine {
|
||||||
///
|
/// Has a system function a Rust-native override?
|
||||||
/// Constants and variables from the scope are added.
|
fn has_native_fn_override(&self, hash_script: u64, arg_types: impl AsRef<[TypeId]>) -> bool {
|
||||||
fn optimize_top_level(
|
let hash = calc_fn_hash_full(hash_script, arg_types.as_ref().iter().copied());
|
||||||
statements: StmtBlockContainer,
|
|
||||||
engine: &Engine,
|
// First check the global namespace and packages, but skip modules that are standard because
|
||||||
scope: &Scope,
|
// they should never conflict with system functions.
|
||||||
#[cfg(not(feature = "no_function"))] lib: &[crate::SharedModule],
|
if self
|
||||||
optimization_level: OptimizationLevel,
|
.global_modules
|
||||||
) -> StmtBlockContainer {
|
.iter()
|
||||||
let mut statements = statements;
|
.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,
|
||||||
|
scope: &Scope,
|
||||||
|
lib: &[crate::SharedModule],
|
||||||
|
optimization_level: OptimizationLevel,
|
||||||
|
) -> StmtBlockContainer {
|
||||||
|
let mut statements = statements;
|
||||||
|
|
||||||
|
// If optimization level is None then skip optimizing
|
||||||
|
if optimization_level == OptimizationLevel::None {
|
||||||
|
statements.shrink_to_fit();
|
||||||
|
return statements;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up the state
|
||||||
|
let mut state = OptimizerState::new(self, lib, optimization_level);
|
||||||
|
|
||||||
|
// Add constants from global modules
|
||||||
|
for (name, value) in self.global_modules.iter().rev().flat_map(|m| m.iter_var()) {
|
||||||
|
state.push_var(name, AccessMode::ReadOnly, value.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add constants and variables from the scope
|
||||||
|
for (name, constant, value) in scope.iter() {
|
||||||
|
if constant {
|
||||||
|
state.push_var(name, AccessMode::ReadOnly, value);
|
||||||
|
} else {
|
||||||
|
state.push_var(name, AccessMode::ReadWrite, Dynamic::NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
optimize_stmt_block(statements, &mut state, true, false, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<
|
||||||
|
crate::Shared<crate::ast::ScriptFnDef>,
|
||||||
|
>,
|
||||||
|
optimization_level: OptimizationLevel,
|
||||||
|
) -> AST {
|
||||||
|
let mut statements = statements;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
let lib: crate::Shared<_> = {
|
||||||
|
let mut module = crate::Module::new();
|
||||||
|
|
||||||
|
if optimization_level != OptimizationLevel::None {
|
||||||
|
// We only need the script library's signatures for optimization purposes
|
||||||
|
let mut lib2 = crate::Module::new();
|
||||||
|
|
||||||
|
for fn_def in &functions {
|
||||||
|
lib2.set_script_fn(crate::ast::ScriptFnDef {
|
||||||
|
name: fn_def.name.clone(),
|
||||||
|
access: fn_def.access,
|
||||||
|
body: crate::ast::StmtBlock::NONE,
|
||||||
|
params: fn_def.params.clone(),
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
environ: None,
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
|
comments: Box::default(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let lib2 = &[lib2.into()];
|
||||||
|
|
||||||
|
for fn_def in functions {
|
||||||
|
let mut fn_def = crate::func::shared_take_or_clone(fn_def);
|
||||||
|
|
||||||
|
// Optimize the function body
|
||||||
|
let body = mem::take(&mut *fn_def.body);
|
||||||
|
|
||||||
|
*fn_def.body = self.optimize_top_level(body, scope, lib2, optimization_level);
|
||||||
|
|
||||||
|
module.set_script_fn(fn_def);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for fn_def in functions {
|
||||||
|
module.set_script_fn(fn_def);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.into()
|
||||||
|
};
|
||||||
|
#[cfg(feature = "no_function")]
|
||||||
|
let lib: crate::Shared<_> = crate::Module::new().into();
|
||||||
|
|
||||||
// If optimization level is None then skip optimizing
|
|
||||||
if optimization_level == OptimizationLevel::None {
|
|
||||||
statements.shrink_to_fit();
|
statements.shrink_to_fit();
|
||||||
return statements;
|
|
||||||
|
AST::new(
|
||||||
|
match optimization_level {
|
||||||
|
OptimizationLevel::None => statements,
|
||||||
|
OptimizationLevel::Simple | OptimizationLevel::Full => {
|
||||||
|
self.optimize_top_level(statements, scope, &[lib.clone()], optimization_level)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
lib,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up the state
|
|
||||||
let mut state = OptimizerState::new(
|
|
||||||
engine,
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
lib,
|
|
||||||
optimization_level,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add constants from global modules
|
|
||||||
for (name, value) in engine
|
|
||||||
.global_modules
|
|
||||||
.iter()
|
|
||||||
.rev()
|
|
||||||
.flat_map(|m| m.iter_var())
|
|
||||||
{
|
|
||||||
state.push_var(name, AccessMode::ReadOnly, value.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add constants and variables from the scope
|
|
||||||
for (name, constant, value) in scope.iter() {
|
|
||||||
if constant {
|
|
||||||
state.push_var(name, AccessMode::ReadOnly, value);
|
|
||||||
} else {
|
|
||||||
state.push_var(name, AccessMode::ReadWrite, Dynamic::NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
optimize_stmt_block(statements, &mut state, true, false, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Optimize an [`AST`].
|
|
||||||
pub fn optimize_into_ast(
|
|
||||||
engine: &Engine,
|
|
||||||
scope: &Scope,
|
|
||||||
statements: StmtBlockContainer,
|
|
||||||
#[cfg(not(feature = "no_function"))] functions: StaticVec<
|
|
||||||
crate::Shared<crate::ast::ScriptFnDef>,
|
|
||||||
>,
|
|
||||||
optimization_level: OptimizationLevel,
|
|
||||||
) -> AST {
|
|
||||||
let mut statements = statements;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
let lib: crate::Shared<_> = {
|
|
||||||
let mut module = crate::Module::new();
|
|
||||||
|
|
||||||
if optimization_level != OptimizationLevel::None {
|
|
||||||
// We only need the script library's signatures for optimization purposes
|
|
||||||
let mut lib2 = crate::Module::new();
|
|
||||||
|
|
||||||
for fn_def in &functions {
|
|
||||||
lib2.set_script_fn(crate::ast::ScriptFnDef {
|
|
||||||
name: fn_def.name.clone(),
|
|
||||||
access: fn_def.access,
|
|
||||||
body: crate::ast::StmtBlock::NONE,
|
|
||||||
params: fn_def.params.clone(),
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
environ: None,
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
#[cfg(feature = "metadata")]
|
|
||||||
comments: Box::default(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let lib2 = &[lib2.into()];
|
|
||||||
|
|
||||||
for fn_def in functions {
|
|
||||||
let mut fn_def = crate::func::shared_take_or_clone(fn_def);
|
|
||||||
|
|
||||||
// Optimize the function body
|
|
||||||
let body = mem::take(&mut *fn_def.body);
|
|
||||||
|
|
||||||
*fn_def.body = optimize_top_level(body, engine, scope, lib2, optimization_level);
|
|
||||||
|
|
||||||
module.set_script_fn(fn_def);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for fn_def in functions {
|
|
||||||
module.set_script_fn(fn_def);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.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,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
lib,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
@ -261,8 +261,7 @@ pub mod array_functions {
|
|||||||
m1 += m2;
|
m1 += m2;
|
||||||
s1 += s2;
|
s1 += s2;
|
||||||
|
|
||||||
_ctx.engine()
|
_ctx.engine().throw_on_size((a1, m1, s1))?;
|
||||||
.raise_err_if_over_data_size_limit((a1, m1, s1))?;
|
|
||||||
|
|
||||||
guard.push(item.clone());
|
guard.push(item.clone());
|
||||||
arr_len += 1;
|
arr_len += 1;
|
||||||
|
@ -81,8 +81,7 @@ pub mod blob_functions {
|
|||||||
|
|
||||||
// Check if blob will be over max size limit
|
// Check if blob will be over max size limit
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
_ctx.engine()
|
_ctx.engine().throw_on_size((len, 0, 0))?;
|
||||||
.raise_err_if_over_data_size_limit((len, 0, 0))?;
|
|
||||||
|
|
||||||
let mut blob = Blob::new();
|
let mut blob = Blob::new();
|
||||||
blob.resize(len, (value & 0x0000_00ff) as u8);
|
blob.resize(len, (value & 0x0000_00ff) as u8);
|
||||||
|
@ -235,18 +235,18 @@ macro_rules! reg_range {
|
|||||||
concat!("from: ", stringify!($y)),
|
concat!("from: ", stringify!($y)),
|
||||||
concat!("to: ", stringify!($y)),
|
concat!("to: ", stringify!($y)),
|
||||||
concat!("Iterator<", stringify!($y), ">"),
|
concat!("Iterator<", stringify!($y), ">"),
|
||||||
], [
|
], ["\
|
||||||
"/// Return an iterator over the exclusive range of `from..to`.",
|
/// Return an iterator over the exclusive range of `from..to`.\n\
|
||||||
"/// The value `to` is never included.",
|
/// The value `to` is never included.\n\
|
||||||
"///",
|
///\n\
|
||||||
"/// # Example",
|
/// # Example\n\
|
||||||
"///",
|
///\n\
|
||||||
"/// ```rhai",
|
/// ```rhai\n\
|
||||||
"/// // prints all values from 8 to 17",
|
/// // prints all values from 8 to 17\n\
|
||||||
"/// for n in range(8, 18) {",
|
/// for n in range(8, 18) {\n\
|
||||||
"/// print(n);",
|
/// print(n);\n\
|
||||||
"/// }",
|
/// }\n\
|
||||||
"/// ```"
|
/// ```"
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$lib.set_iterator::<RangeInclusive<$y>>();
|
$lib.set_iterator::<RangeInclusive<$y>>();
|
||||||
@ -269,27 +269,27 @@ macro_rules! reg_range {
|
|||||||
concat!("to: ", stringify!($y)),
|
concat!("to: ", stringify!($y)),
|
||||||
concat!("step: ", stringify!($y)),
|
concat!("step: ", stringify!($y)),
|
||||||
concat!("Iterator<", stringify!($y), ">")
|
concat!("Iterator<", stringify!($y), ">")
|
||||||
], [
|
], ["\
|
||||||
"/// Return an iterator over the exclusive range of `from..to`, each iteration increasing by `step`.",
|
/// Return an iterator over the exclusive range of `from..to`, each iteration increasing by `step`.\n\
|
||||||
"/// The value `to` is never included.",
|
/// The value `to` is never included.\n\
|
||||||
"///",
|
///\n\
|
||||||
"/// If `from` > `to` and `step` < 0, iteration goes backwards.",
|
/// 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.",
|
/// If `from` > `to` and `step` > 0 or `from` < `to` and `step` < 0, an empty iterator is returned.\n\
|
||||||
"///",
|
///\n\
|
||||||
"/// # Example",
|
/// # Example\n\
|
||||||
"///",
|
///\n\
|
||||||
"/// ```rhai",
|
/// ```rhai\n\
|
||||||
"/// // prints all values from 8 to 17 in steps of 3",
|
/// // prints all values from 8 to 17 in steps of 3\n\
|
||||||
"/// for n in range(8, 18, 3) {",
|
/// for n in range(8, 18, 3) {\n\
|
||||||
"/// print(n);",
|
/// print(n);\n\
|
||||||
"/// }",
|
/// }\n\
|
||||||
"///",
|
///\n\
|
||||||
"/// // prints all values down from 18 to 9 in steps of -3",
|
/// // prints all values down from 18 to 9 in steps of -3\n\
|
||||||
"/// for n in range(18, 8, -3) {",
|
/// for n in range(18, 8, -3) {\n\
|
||||||
"/// print(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));
|
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!("range: Range<", stringify!($y), ">"),
|
||||||
concat!("step: ", stringify!($y)),
|
concat!("step: ", stringify!($y)),
|
||||||
concat!("Iterator<", stringify!($y), ">")
|
concat!("Iterator<", stringify!($y), ">")
|
||||||
], [
|
], ["\
|
||||||
"/// Return an iterator over an exclusive range, each iteration increasing by `step`.",
|
/// Return an iterator over an exclusive range, each iteration increasing by `step`.\n\
|
||||||
"///",
|
///\n\
|
||||||
"/// If `range` is reversed and `step` < 0, iteration goes backwards.",
|
/// If `range` is reversed and `step` < 0, iteration goes backwards.\n\
|
||||||
"///",
|
///\n\
|
||||||
"/// Otherwise, if `range` is empty, an empty iterator is returned.",
|
/// Otherwise, if `range` is empty, an empty iterator is returned.\n\
|
||||||
"///",
|
///\n\
|
||||||
"/// # Example",
|
/// # Example\n\
|
||||||
"///",
|
///\n\
|
||||||
"/// ```rhai",
|
/// ```rhai\n\
|
||||||
"/// // prints all values from 8 to 17 in steps of 3",
|
/// // prints all values from 8 to 17 in steps of 3\n\
|
||||||
"/// for n in range(8..18, 3) {",
|
/// for n in range(8..18, 3) {\n\
|
||||||
"/// print(n);",
|
/// print(n);\n\
|
||||||
"/// }",
|
/// }\n\
|
||||||
"///",
|
///\n\
|
||||||
"/// // prints all values down from 18 to 9 in steps of -3",
|
/// // prints all values down from 18 to 9 in steps of -3\n\
|
||||||
"/// for n in range(18..8, -3) {",
|
/// for n in range(18..8, -3) {\n\
|
||||||
"/// print(n);",
|
/// print(n);\n\
|
||||||
"/// }",
|
/// }\n\
|
||||||
"/// ```"
|
/// ```"
|
||||||
]);
|
]);
|
||||||
)*
|
)*
|
||||||
};
|
};
|
||||||
@ -665,6 +665,7 @@ mod range_functions {
|
|||||||
}
|
}
|
||||||
/// Return true if the range contains no items.
|
/// Return true if the range contains no items.
|
||||||
#[rhai_fn(get = "is_empty", name = "is_empty", pure)]
|
#[rhai_fn(get = "is_empty", name = "is_empty", pure)]
|
||||||
|
#[allow(unstable_name_collisions)]
|
||||||
pub fn is_empty_exclusive(range: &mut ExclusiveRange) -> bool {
|
pub fn is_empty_exclusive(range: &mut ExclusiveRange) -> bool {
|
||||||
range.is_empty()
|
range.is_empty()
|
||||||
}
|
}
|
||||||
|
@ -220,7 +220,7 @@ fn collect_fn_metadata(
|
|||||||
"comments".into(),
|
"comments".into(),
|
||||||
func.comments
|
func.comments
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| engine.get_interned_string(s.as_ref()).into())
|
.map(|s| engine.get_interned_string(s.as_str()).into())
|
||||||
.collect::<Array>()
|
.collect::<Array>()
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
@ -267,9 +267,10 @@ fn collect_fn_metadata(
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
ctx.engine()
|
ctx.engine()
|
||||||
.global_sub_modules
|
.global_sub_modules
|
||||||
.iter()
|
.as_deref()
|
||||||
.flat_map(|m| m.values())
|
.into_iter()
|
||||||
.flat_map(|m| m.iter_script_fn())
|
.flatten()
|
||||||
|
.flat_map(|(_, m)| m.iter_script_fn())
|
||||||
.filter(|(ns, a, n, p, f)| filter(*ns, *a, n, *p, f))
|
.filter(|(ns, a, n, p, f)| filter(*ns, *a, n, *p, f))
|
||||||
.for_each(|(.., f)| {
|
.for_each(|(.., f)| {
|
||||||
list.push(
|
list.push(
|
||||||
|
537
src/parser.rs
537
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_`,|`_temp-variable_`: `_type_`|` _code_ `)`
|
||||||
/// * `reify!(`_variable_ or _expression_ `=>` `Option<`_type_`>` `)`
|
/// * `reify!(`_variable_ or _expression_ `=>` `Option<`_type_`>` `)`
|
||||||
/// * `reify!(`_variable_ or _expression_ `=>` _type_ `)`
|
/// * `reify!(`_variable_ or _expression_ `=>` _type_ `)`
|
||||||
#[macro_export]
|
|
||||||
macro_rules! reify {
|
macro_rules! reify {
|
||||||
($old:ident, |$new:ident : $t:ty| $code:expr, || $fallback:expr) => {{
|
($old:ident, |$new:ident : $t:ty| $code:expr, || $fallback:expr) => {{
|
||||||
#[allow(clippy::redundant_else)]
|
#[allow(clippy::redundant_else)]
|
||||||
|
@ -168,10 +168,12 @@ pub fn gen_metadata_to_json(
|
|||||||
include_standard_packages: bool,
|
include_standard_packages: bool,
|
||||||
) -> serde_json::Result<String> {
|
) -> serde_json::Result<String> {
|
||||||
let _ast = ast;
|
let _ast = ast;
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
|
let mut global_doc = String::new();
|
||||||
let mut global = ModuleMetadata::new();
|
let mut global = ModuleMetadata::new();
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[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());
|
global.modules.insert(name, m.as_ref().into());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,7 +187,16 @@ pub fn gen_metadata_to_json(
|
|||||||
.global_modules
|
.global_modules
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|m| !m.flags.contains(exclude_flags))
|
.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| {
|
.for_each(|f| {
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
let mut meta: FnMetadata = f.into();
|
let mut meta: FnMetadata = f.into();
|
||||||
@ -213,7 +224,17 @@ pub fn gen_metadata_to_json(
|
|||||||
|
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
if let Some(ast) = _ast {
|
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)
|
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,
|
KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_THIS, KEYWORD_TYPE_OF,
|
||||||
};
|
};
|
||||||
use crate::func::native::OnParseTokenCallback;
|
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")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{
|
use std::{
|
||||||
@ -13,7 +13,6 @@ use std::{
|
|||||||
char, fmt,
|
char, fmt,
|
||||||
iter::{FusedIterator, Peekable},
|
iter::{FusedIterator, Peekable},
|
||||||
num::NonZeroUsize,
|
num::NonZeroUsize,
|
||||||
ops::{Add, AddAssign},
|
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
str::{Chars, FromStr},
|
str::{Chars, FromStr},
|
||||||
};
|
};
|
||||||
@ -24,9 +23,9 @@ pub struct TokenizerControlBlock {
|
|||||||
/// Is the current tokenizer position within an interpolated text string?
|
/// Is the current tokenizer position within an interpolated text string?
|
||||||
/// This flag allows switching the tokenizer back to _text_ parsing after an interpolation stream.
|
/// This flag allows switching the tokenizer back to _text_ parsing after an interpolation stream.
|
||||||
pub is_within_text: bool,
|
pub is_within_text: bool,
|
||||||
/// Collection of global comments.
|
/// Global comments.
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
pub global_comments: Vec<SmartString>,
|
pub global_comments: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TokenizerControlBlock {
|
impl TokenizerControlBlock {
|
||||||
@ -37,7 +36,7 @@ impl TokenizerControlBlock {
|
|||||||
Self {
|
Self {
|
||||||
is_within_text: false,
|
is_within_text: false,
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
global_comments: Vec::new(),
|
global_comments: String::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -50,326 +49,12 @@ type LERR = LexError;
|
|||||||
/// Separator character for numbers.
|
/// Separator character for numbers.
|
||||||
const NUMBER_SEPARATOR: char = '_';
|
const NUMBER_SEPARATOR: char = '_';
|
||||||
|
|
||||||
|
/// No token.
|
||||||
|
pub const NO_TOKEN: Token = Token::NONE;
|
||||||
|
|
||||||
/// A stream of tokens.
|
/// A stream of tokens.
|
||||||
pub type TokenStream<'a> = Peekable<TokenIterator<'a>>;
|
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.
|
/// _(internals)_ A Rhai language token.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
#[derive(Debug, PartialEq, Clone, Hash)]
|
#[derive(Debug, PartialEq, Clone, Hash)]
|
||||||
@ -489,6 +174,8 @@ pub enum Token {
|
|||||||
For,
|
For,
|
||||||
/// `in`
|
/// `in`
|
||||||
In,
|
In,
|
||||||
|
/// `!in`
|
||||||
|
NotIn,
|
||||||
/// `<`
|
/// `<`
|
||||||
LessThan,
|
LessThan,
|
||||||
/// `>`
|
/// `>`
|
||||||
@ -575,7 +262,7 @@ pub enum Token {
|
|||||||
/// A lexer error.
|
/// A lexer error.
|
||||||
LexError(Box<LexError>),
|
LexError(Box<LexError>),
|
||||||
/// A comment block.
|
/// A comment block.
|
||||||
Comment(Box<SmartString>),
|
Comment(Box<String>),
|
||||||
/// A reserved symbol.
|
/// A reserved symbol.
|
||||||
Reserved(Box<SmartString>),
|
Reserved(Box<SmartString>),
|
||||||
/// A custom keyword.
|
/// A custom keyword.
|
||||||
@ -584,7 +271,10 @@ pub enum Token {
|
|||||||
#[cfg(not(feature = "no_custom_syntax"))]
|
#[cfg(not(feature = "no_custom_syntax"))]
|
||||||
Custom(Box<SmartString>),
|
Custom(Box<SmartString>),
|
||||||
/// End of the input stream.
|
/// End of the input stream.
|
||||||
|
/// Used as a placeholder for the end of input.
|
||||||
EOF,
|
EOF,
|
||||||
|
/// Placeholder to indicate the lack of a token.
|
||||||
|
NONE,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Token {
|
impl fmt::Display for Token {
|
||||||
@ -610,6 +300,7 @@ impl fmt::Display for Token {
|
|||||||
Comment(s) => f.write_str(s),
|
Comment(s) => f.write_str(s),
|
||||||
|
|
||||||
EOF => f.write_str("{EOF}"),
|
EOF => f.write_str("{EOF}"),
|
||||||
|
NONE => f.write_str("{NONE}"),
|
||||||
|
|
||||||
token => f.write_str(token.literal_syntax()),
|
token => f.write_str(token.literal_syntax()),
|
||||||
}
|
}
|
||||||
@ -638,7 +329,7 @@ impl Token {
|
|||||||
Custom(..) => false,
|
Custom(..) => false,
|
||||||
LexError(..) | Comment(..) => false,
|
LexError(..) | Comment(..) => false,
|
||||||
|
|
||||||
EOF => false,
|
EOF | NONE => false,
|
||||||
|
|
||||||
_ => true,
|
_ => true,
|
||||||
}
|
}
|
||||||
@ -696,6 +387,7 @@ impl Token {
|
|||||||
Loop => "loop",
|
Loop => "loop",
|
||||||
For => "for",
|
For => "for",
|
||||||
In => "in",
|
In => "in",
|
||||||
|
NotIn => "!in",
|
||||||
LessThan => "<",
|
LessThan => "<",
|
||||||
GreaterThan => ">",
|
GreaterThan => ">",
|
||||||
Bang => "!",
|
Bang => "!",
|
||||||
@ -750,37 +442,43 @@ impl Token {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn is_op_assignment(&self) -> bool {
|
pub const fn is_op_assignment(&self) -> bool {
|
||||||
|
#[allow(clippy::enum_glob_use)]
|
||||||
|
use Token::*;
|
||||||
|
|
||||||
matches!(
|
matches!(
|
||||||
self,
|
self,
|
||||||
Self::PlusAssign
|
PlusAssign
|
||||||
| Self::MinusAssign
|
| MinusAssign
|
||||||
| Self::MultiplyAssign
|
| MultiplyAssign
|
||||||
| Self::DivideAssign
|
| DivideAssign
|
||||||
| Self::LeftShiftAssign
|
| LeftShiftAssign
|
||||||
| Self::RightShiftAssign
|
| RightShiftAssign
|
||||||
| Self::ModuloAssign
|
| ModuloAssign
|
||||||
| Self::PowerOfAssign
|
| PowerOfAssign
|
||||||
| Self::AndAssign
|
| AndAssign
|
||||||
| Self::OrAssign
|
| OrAssign
|
||||||
| Self::XOrAssign
|
| XOrAssign
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the corresponding operator of the token if it is an op-assignment operator.
|
/// Get the corresponding operator of the token if it is an op-assignment operator.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn get_base_op_from_assignment(&self) -> Option<Self> {
|
pub const fn get_base_op_from_assignment(&self) -> Option<Self> {
|
||||||
|
#[allow(clippy::enum_glob_use)]
|
||||||
|
use Token::*;
|
||||||
|
|
||||||
Some(match self {
|
Some(match self {
|
||||||
Self::PlusAssign => Self::Plus,
|
PlusAssign => Plus,
|
||||||
Self::MinusAssign => Self::Minus,
|
MinusAssign => Minus,
|
||||||
Self::MultiplyAssign => Self::Multiply,
|
MultiplyAssign => Multiply,
|
||||||
Self::DivideAssign => Self::Divide,
|
DivideAssign => Divide,
|
||||||
Self::LeftShiftAssign => Self::LeftShift,
|
LeftShiftAssign => LeftShift,
|
||||||
Self::RightShiftAssign => Self::RightShift,
|
RightShiftAssign => RightShift,
|
||||||
Self::ModuloAssign => Self::Modulo,
|
ModuloAssign => Modulo,
|
||||||
Self::PowerOfAssign => Self::PowerOf,
|
PowerOfAssign => PowerOf,
|
||||||
Self::AndAssign => Self::Ampersand,
|
AndAssign => Ampersand,
|
||||||
Self::OrAssign => Self::Pipe,
|
OrAssign => Pipe,
|
||||||
Self::XOrAssign => Self::XOr,
|
XOrAssign => XOr,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -789,37 +487,42 @@ impl Token {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn has_op_assignment(&self) -> bool {
|
pub const fn has_op_assignment(&self) -> bool {
|
||||||
|
#[allow(clippy::enum_glob_use)]
|
||||||
|
use Token::*;
|
||||||
|
|
||||||
matches!(
|
matches!(
|
||||||
self,
|
self,
|
||||||
Self::Plus
|
Plus | Minus
|
||||||
| Self::Minus
|
| Multiply
|
||||||
| Self::Multiply
|
| Divide
|
||||||
| Self::Divide
|
| LeftShift
|
||||||
| Self::LeftShift
|
| RightShift
|
||||||
| Self::RightShift
|
| Modulo
|
||||||
| Self::Modulo
|
| PowerOf
|
||||||
| Self::PowerOf
|
| Ampersand
|
||||||
| Self::Ampersand
|
| Pipe
|
||||||
| Self::Pipe
|
| XOr
|
||||||
| Self::XOr
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the corresponding op-assignment operator of the token.
|
/// Get the corresponding op-assignment operator of the token.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn convert_to_op_assignment(&self) -> Option<Self> {
|
pub const fn convert_to_op_assignment(&self) -> Option<Self> {
|
||||||
|
#[allow(clippy::enum_glob_use)]
|
||||||
|
use Token::*;
|
||||||
|
|
||||||
Some(match self {
|
Some(match self {
|
||||||
Self::Plus => Self::PlusAssign,
|
Plus => PlusAssign,
|
||||||
Self::Minus => Self::MinusAssign,
|
Minus => MinusAssign,
|
||||||
Self::Multiply => Self::MultiplyAssign,
|
Multiply => MultiplyAssign,
|
||||||
Self::Divide => Self::DivideAssign,
|
Divide => DivideAssign,
|
||||||
Self::LeftShift => Self::LeftShiftAssign,
|
LeftShift => LeftShiftAssign,
|
||||||
Self::RightShift => Self::RightShiftAssign,
|
RightShift => RightShiftAssign,
|
||||||
Self::Modulo => Self::ModuloAssign,
|
Modulo => ModuloAssign,
|
||||||
Self::PowerOf => Self::PowerOfAssign,
|
PowerOf => PowerOfAssign,
|
||||||
Self::Ampersand => Self::AndAssign,
|
Ampersand => AndAssign,
|
||||||
Self::Pipe => Self::OrAssign,
|
Pipe => OrAssign,
|
||||||
Self::XOr => Self::XOrAssign,
|
XOr => XOrAssign,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -871,6 +574,7 @@ impl Token {
|
|||||||
"loop" => Loop,
|
"loop" => Loop,
|
||||||
"for" => For,
|
"for" => For,
|
||||||
"in" => In,
|
"in" => In,
|
||||||
|
"!in" => NotIn,
|
||||||
"<" => LessThan,
|
"<" => LessThan,
|
||||||
">" => GreaterThan,
|
">" => GreaterThan,
|
||||||
"!" => Bang,
|
"!" => 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
|
/// If another operator is after these, it's probably a unary operator
|
||||||
/// (not sure about `fn` name).
|
/// (not sure about `fn` name).
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -1018,6 +715,7 @@ impl Token {
|
|||||||
While |
|
While |
|
||||||
Until |
|
Until |
|
||||||
In |
|
In |
|
||||||
|
NotIn |
|
||||||
And |
|
And |
|
||||||
AndAssign |
|
AndAssign |
|
||||||
Or |
|
Or |
|
||||||
@ -1049,7 +747,7 @@ impl Token {
|
|||||||
|
|
||||||
EqualsTo | NotEqualsTo => 90,
|
EqualsTo | NotEqualsTo => 90,
|
||||||
|
|
||||||
In => 110,
|
In | NotIn => 110,
|
||||||
|
|
||||||
LessThan | LessThanEqualsTo | GreaterThan | GreaterThanEqualsTo => 130,
|
LessThan | LessThanEqualsTo | GreaterThan | GreaterThanEqualsTo => 130,
|
||||||
|
|
||||||
@ -1170,7 +868,7 @@ impl From<Token> for String {
|
|||||||
#[derive(Debug, Clone, Eq, PartialEq, Default)]
|
#[derive(Debug, Clone, Eq, PartialEq, Default)]
|
||||||
pub struct TokenizeState {
|
pub struct TokenizeState {
|
||||||
/// Maximum length of a string.
|
/// 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?
|
/// Can the next token be a unary operator?
|
||||||
pub next_token_cannot_be_unary: bool,
|
pub next_token_cannot_be_unary: bool,
|
||||||
/// Shared object to allow controlling the tokenizer externally.
|
/// Shared object to allow controlling the tokenizer externally.
|
||||||
@ -1197,6 +895,18 @@ pub trait InputStream {
|
|||||||
fn peek_next(&mut self) -> Option<char>;
|
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.
|
/// _(internals)_ Parse a string literal ended by a specified termination character.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
@ -1286,11 +996,8 @@ pub fn parse_string_literal(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(max) = state.max_string_size {
|
ensure_string_len_within_limit(state.max_string_len, &result)
|
||||||
if result.len() > max.get() {
|
.map_err(|err| (err, start))?;
|
||||||
return Err((LexError::StringTooLong(max.get()), *pos));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close wrapper
|
// Close wrapper
|
||||||
if termination_char == next_char && escape.is_empty() {
|
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 {
|
ensure_string_len_within_limit(state.max_string_len, &result).map_err(|err| (err, start))?;
|
||||||
if result.len() > max.get() {
|
|
||||||
return Err((LexError::StringTooLong(max.get()), *pos));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((result, interpolated, first_char))
|
Ok((result, interpolated, first_char))
|
||||||
}
|
}
|
||||||
@ -1446,7 +1149,7 @@ fn scan_block_comment(
|
|||||||
stream: &mut impl InputStream,
|
stream: &mut impl InputStream,
|
||||||
level: usize,
|
level: usize,
|
||||||
pos: &mut Position,
|
pos: &mut Position,
|
||||||
comment: Option<&mut SmartString>,
|
comment: Option<&mut String>,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
let mut level = level;
|
let mut level = level;
|
||||||
let mut comment = comment;
|
let mut comment = comment;
|
||||||
@ -1541,7 +1244,7 @@ fn get_next_token_inner(
|
|||||||
if state.comment_level > 0 {
|
if state.comment_level > 0 {
|
||||||
let start_pos = *pos;
|
let start_pos = *pos;
|
||||||
let mut comment = if state.include_comments {
|
let mut comment = if state.include_comments {
|
||||||
Some(SmartString::new_const())
|
Some(String::new())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
@ -1748,11 +1451,17 @@ fn get_next_token_inner(
|
|||||||
// letter or underscore ...
|
// letter or underscore ...
|
||||||
#[cfg(not(feature = "unicode-xid-ident"))]
|
#[cfg(not(feature = "unicode-xid-ident"))]
|
||||||
('a'..='z' | '_' | 'A'..='Z', ..) => {
|
('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")]
|
#[cfg(feature = "unicode-xid-ident")]
|
||||||
(ch, ..) if unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_' => {
|
(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
|
// " - string literal
|
||||||
@ -1928,7 +1637,7 @@ fn get_next_token_inner(
|
|||||||
('/', '/') => {
|
('/', '/') => {
|
||||||
eat_next(stream, pos);
|
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(not(feature = "no_function"))]
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
Some('/') => {
|
Some('/') => {
|
||||||
@ -1971,11 +1680,13 @@ fn get_next_token_inner(
|
|||||||
if let Some(comment) = comment {
|
if let Some(comment) = comment {
|
||||||
match comment {
|
match comment {
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
_ if comment.starts_with("//!") => state
|
_ if comment.starts_with("//!") => {
|
||||||
.tokenizer_control
|
let g = &mut state.tokenizer_control.borrow_mut().global_comments;
|
||||||
.borrow_mut()
|
if !g.is_empty() {
|
||||||
.global_comments
|
g.push('\n');
|
||||||
.push(comment),
|
}
|
||||||
|
g.push_str(&comment);
|
||||||
|
}
|
||||||
_ => return Some((Token::Comment(comment.into()), start_pos)),
|
_ => return Some((Token::Comment(comment.into()), start_pos)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1984,7 +1695,7 @@ fn get_next_token_inner(
|
|||||||
state.comment_level = 1;
|
state.comment_level = 1;
|
||||||
eat_next(stream, pos);
|
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(not(feature = "no_function"))]
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
Some('*') => {
|
Some('*') => {
|
||||||
@ -2119,6 +1830,15 @@ fn get_next_token_inner(
|
|||||||
}
|
}
|
||||||
('>', ..) => return Some((Token::GreaterThan, start_pos)),
|
('>', ..) => 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);
|
eat_next(stream, pos);
|
||||||
|
|
||||||
@ -2220,12 +1940,12 @@ fn get_next_token_inner(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the next token, parsing it as an identifier.
|
/// Get the next token, parsing it as an identifier.
|
||||||
fn get_token_as_identifier(
|
fn parse_identifier_token(
|
||||||
stream: &mut impl InputStream,
|
stream: &mut impl InputStream,
|
||||||
pos: &mut Position,
|
pos: &mut Position,
|
||||||
start_pos: Position,
|
start_pos: Position,
|
||||||
first_char: char,
|
first_char: char,
|
||||||
) -> (Token, Position) {
|
) -> Result<(Token, Position), LexError> {
|
||||||
let mut identifier = SmartString::new_const();
|
let mut identifier = SmartString::new_const();
|
||||||
identifier.push(first_char);
|
identifier.push(first_char);
|
||||||
|
|
||||||
@ -2240,19 +1960,20 @@ fn get_token_as_identifier(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(token) = Token::lookup_symbol_from_syntax(&identifier) {
|
if let Some(token) = Token::lookup_symbol_from_syntax(&identifier) {
|
||||||
return (token, start_pos);
|
return Ok((token, start_pos));
|
||||||
} else if Token::is_reserved_keyword(&identifier) {
|
}
|
||||||
return (Token::Reserved(Box::new(identifier)), start_pos);
|
if Token::is_reserved_keyword(&identifier) {
|
||||||
|
return Ok((Token::Reserved(Box::new(identifier)), start_pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !is_valid_identifier(&identifier) {
|
if !is_valid_identifier(&identifier) {
|
||||||
return (
|
return Ok((
|
||||||
Token::LexError(LERR::MalformedIdentifier(identifier.to_string()).into()),
|
Token::LexError(LERR::MalformedIdentifier(identifier.to_string()).into()),
|
||||||
start_pos,
|
start_pos,
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
(Token::Identifier(identifier.into()), start_pos)
|
Ok((Token::Identifier(identifier.into()), start_pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is a keyword allowed as a function?
|
/// Is a keyword allowed as a function?
|
||||||
@ -2435,7 +2156,7 @@ impl<'a> Iterator for TokenIterator<'a> {
|
|||||||
Some((Token::Reserved(s), pos)) => (match
|
Some((Token::Reserved(s), pos)) => (match
|
||||||
(s.as_str(),
|
(s.as_str(),
|
||||||
#[cfg(not(feature = "no_custom_syntax"))]
|
#[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")]
|
#[cfg(feature = "no_custom_syntax")]
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
@ -2472,7 +2193,7 @@ impl<'a> Iterator for TokenIterator<'a> {
|
|||||||
#[cfg(feature = "no_custom_syntax")]
|
#[cfg(feature = "no_custom_syntax")]
|
||||||
(.., true) => unreachable!("no custom operators"),
|
(.., true) => unreachable!("no custom operators"),
|
||||||
// Reserved keyword that is not custom and disabled.
|
// 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"});
|
let msg = format!("reserved {} '{token}' is disabled", if is_valid_identifier(token) { "keyword"} else {"symbol"});
|
||||||
Token::LexError(LERR::ImproperSymbol(s.to_string(), msg).into())
|
Token::LexError(LERR::ImproperSymbol(s.to_string(), msg).into())
|
||||||
},
|
},
|
||||||
@ -2481,13 +2202,13 @@ impl<'a> Iterator for TokenIterator<'a> {
|
|||||||
}, pos),
|
}, pos),
|
||||||
// Custom keyword
|
// Custom keyword
|
||||||
#[cfg(not(feature = "no_custom_syntax"))]
|
#[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)
|
(Token::Custom(s), pos)
|
||||||
}
|
}
|
||||||
// Custom keyword/symbol - must be disabled
|
// Custom keyword/symbol - must be disabled
|
||||||
#[cfg(not(feature = "no_custom_syntax"))]
|
#[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())) => {
|
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_ref().map_or(false,|m| m.contains(token.literal_syntax())) {
|
if self.engine.disabled_symbols.as_deref().map_or(false,|m| m.contains(token.literal_syntax())) {
|
||||||
// Disabled standard keyword/symbol
|
// Disabled standard keyword/symbol
|
||||||
(Token::Custom(Box::new(token.literal_syntax().into())), pos)
|
(Token::Custom(Box::new(token.literal_syntax().into())), pos)
|
||||||
} else {
|
} else {
|
||||||
@ -2496,7 +2217,7 @@ impl<'a> Iterator for TokenIterator<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Disabled symbol
|
// 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)
|
(Token::Reserved(Box::new(token.literal_syntax().into())), pos)
|
||||||
}
|
}
|
||||||
// Normal symbol
|
// Normal symbol
|
||||||
@ -2554,10 +2275,7 @@ impl Engine {
|
|||||||
TokenIterator {
|
TokenIterator {
|
||||||
engine: self,
|
engine: self,
|
||||||
state: TokenizeState {
|
state: TokenizeState {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
max_string_len: NonZeroUsize::new(self.max_string_size()),
|
||||||
max_string_size: self.limits.max_string_size,
|
|
||||||
#[cfg(feature = "unchecked")]
|
|
||||||
max_string_size: None,
|
|
||||||
next_token_cannot_be_unary: false,
|
next_token_cannot_be_unary: false,
|
||||||
tokenizer_control: buffer,
|
tokenizer_control: buffer,
|
||||||
comment_level: 0,
|
comment_level: 0,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! Collection of custom types.
|
//! Collection of custom types.
|
||||||
|
|
||||||
use crate::Identifier;
|
use crate::Identifier;
|
||||||
use std::{any::type_name, collections::BTreeMap, fmt};
|
use std::{any::type_name, collections::BTreeMap};
|
||||||
|
|
||||||
/// _(internals)_ Information for a custom type.
|
/// _(internals)_ Information for a custom type.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
@ -13,18 +13,9 @@ pub struct CustomTypeInfo {
|
|||||||
|
|
||||||
/// _(internals)_ A collection of custom types.
|
/// _(internals)_ A collection of custom types.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
#[derive(Clone, Hash)]
|
#[derive(Debug, Clone, Hash)]
|
||||||
pub struct CustomTypesCollection(BTreeMap<Identifier, CustomTypeInfo>);
|
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 {
|
impl Default for CustomTypesCollection {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
@ -65,6 +56,7 @@ impl CustomTypesCollection {
|
|||||||
}
|
}
|
||||||
/// Find a custom type.
|
/// Find a custom type.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
pub fn get(&self, key: &str) -> Option<&CustomTypeInfo> {
|
pub fn get(&self, key: &str) -> Option<&CustomTypeInfo> {
|
||||||
self.0.get(key)
|
self.0.get(key)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Helper module which defines the [`Dynamic`] data type.
|
//! 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")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{
|
use std::{
|
||||||
@ -49,11 +49,13 @@ pub type Tag = i16;
|
|||||||
const DEFAULT_TAG_VALUE: Tag = 0;
|
const DEFAULT_TAG_VALUE: Tag = 0;
|
||||||
|
|
||||||
/// Dynamic type containing any value.
|
/// Dynamic type containing any value.
|
||||||
|
#[must_use]
|
||||||
pub struct Dynamic(pub(crate) Union);
|
pub struct Dynamic(pub(crate) Union);
|
||||||
|
|
||||||
/// Internal [`Dynamic`] representation.
|
/// Internal [`Dynamic`] representation.
|
||||||
///
|
///
|
||||||
/// Most variants are boxed to reduce the size.
|
/// Most variants are boxed to reduce the size.
|
||||||
|
#[must_use]
|
||||||
pub enum Union {
|
pub enum Union {
|
||||||
/// An error value which should not exist.
|
/// An error value which should not exist.
|
||||||
Null,
|
Null,
|
||||||
@ -107,10 +109,12 @@ pub enum Union {
|
|||||||
/// This type provides transparent interoperability between normal [`Dynamic`] and shared
|
/// This type provides transparent interoperability between normal [`Dynamic`] and shared
|
||||||
/// [`Dynamic`] values.
|
/// [`Dynamic`] values.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
#[must_use]
|
||||||
pub struct DynamicReadLock<'d, T: Clone>(DynamicReadLockInner<'d, T>);
|
pub struct DynamicReadLock<'d, T: Clone>(DynamicReadLockInner<'d, T>);
|
||||||
|
|
||||||
/// Different types of read guards for [`DynamicReadLock`].
|
/// Different types of read guards for [`DynamicReadLock`].
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
#[must_use]
|
||||||
enum DynamicReadLockInner<'d, T: Clone> {
|
enum DynamicReadLockInner<'d, T: Clone> {
|
||||||
/// A simple reference to a non-shared value.
|
/// A simple reference to a non-shared value.
|
||||||
Reference(&'d T),
|
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
|
/// This type provides transparent interoperability between normal [`Dynamic`] and shared
|
||||||
/// [`Dynamic`] values.
|
/// [`Dynamic`] values.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
#[must_use]
|
||||||
pub struct DynamicWriteLock<'d, T: Clone>(DynamicWriteLockInner<'d, T>);
|
pub struct DynamicWriteLock<'d, T: Clone>(DynamicWriteLockInner<'d, T>);
|
||||||
|
|
||||||
/// Different types of write guards for [`DynamicReadLock`].
|
/// Different types of write guards for [`DynamicReadLock`].
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
#[must_use]
|
||||||
enum DynamicWriteLockInner<'d, T: Clone> {
|
enum DynamicWriteLockInner<'d, T: Clone> {
|
||||||
/// A simple mutable reference to a non-shared value.
|
/// A simple mutable reference to a non-shared value.
|
||||||
Reference(&'d mut T),
|
Reference(&'d mut T),
|
||||||
@ -686,7 +692,6 @@ impl Clone for Dynamic {
|
|||||||
|
|
||||||
impl Default for Dynamic {
|
impl Default for Dynamic {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::UNIT
|
Self::UNIT
|
||||||
}
|
}
|
||||||
@ -852,19 +857,16 @@ impl Dynamic {
|
|||||||
|
|
||||||
/// Create a new [`Dynamic`] from a [`bool`].
|
/// Create a new [`Dynamic`] from a [`bool`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub const fn from_bool(value: bool) -> Self {
|
pub const fn from_bool(value: bool) -> Self {
|
||||||
Self(Union::Bool(value, DEFAULT_TAG_VALUE, ReadWrite))
|
Self(Union::Bool(value, DEFAULT_TAG_VALUE, ReadWrite))
|
||||||
}
|
}
|
||||||
/// Create a new [`Dynamic`] from an [`INT`].
|
/// Create a new [`Dynamic`] from an [`INT`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub const fn from_int(value: INT) -> Self {
|
pub const fn from_int(value: INT) -> Self {
|
||||||
Self(Union::Int(value, DEFAULT_TAG_VALUE, ReadWrite))
|
Self(Union::Int(value, DEFAULT_TAG_VALUE, ReadWrite))
|
||||||
}
|
}
|
||||||
/// Create a new [`Dynamic`] from a [`char`].
|
/// Create a new [`Dynamic`] from a [`char`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub const fn from_char(value: char) -> Self {
|
pub const fn from_char(value: char) -> Self {
|
||||||
Self(Union::Char(value, DEFAULT_TAG_VALUE, ReadWrite))
|
Self(Union::Char(value, DEFAULT_TAG_VALUE, ReadWrite))
|
||||||
}
|
}
|
||||||
@ -873,7 +875,6 @@ impl Dynamic {
|
|||||||
/// Not available under `no_float`.
|
/// Not available under `no_float`.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub const fn from_float(value: crate::FLOAT) -> Self {
|
pub const fn from_float(value: crate::FLOAT) -> Self {
|
||||||
Self(Union::Float(
|
Self(Union::Float(
|
||||||
super::FloatWrapper::new(value),
|
super::FloatWrapper::new(value),
|
||||||
@ -886,28 +887,24 @@ impl Dynamic {
|
|||||||
/// Exported under the `decimal` feature only.
|
/// Exported under the `decimal` feature only.
|
||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn from_decimal(value: rust_decimal::Decimal) -> Self {
|
pub fn from_decimal(value: rust_decimal::Decimal) -> Self {
|
||||||
Self(Union::Decimal(value.into(), DEFAULT_TAG_VALUE, ReadWrite))
|
Self(Union::Decimal(value.into(), DEFAULT_TAG_VALUE, ReadWrite))
|
||||||
}
|
}
|
||||||
/// Create a [`Dynamic`] from an [`Array`][crate::Array].
|
/// Create a [`Dynamic`] from an [`Array`][crate::Array].
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn from_array(array: crate::Array) -> Self {
|
pub fn from_array(array: crate::Array) -> Self {
|
||||||
Self(Union::Array(array.into(), DEFAULT_TAG_VALUE, ReadWrite))
|
Self(Union::Array(array.into(), DEFAULT_TAG_VALUE, ReadWrite))
|
||||||
}
|
}
|
||||||
/// Create a [`Dynamic`] from a [`Blob`][crate::Blob].
|
/// Create a [`Dynamic`] from a [`Blob`][crate::Blob].
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn from_blob(blob: crate::Blob) -> Self {
|
pub fn from_blob(blob: crate::Blob) -> Self {
|
||||||
Self(Union::Blob(blob.into(), DEFAULT_TAG_VALUE, ReadWrite))
|
Self(Union::Blob(blob.into(), DEFAULT_TAG_VALUE, ReadWrite))
|
||||||
}
|
}
|
||||||
/// Create a [`Dynamic`] from a [`Map`][crate::Map].
|
/// Create a [`Dynamic`] from a [`Map`][crate::Map].
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn from_map(map: crate::Map) -> Self {
|
pub fn from_map(map: crate::Map) -> Self {
|
||||||
Self(Union::Map(map.into(), DEFAULT_TAG_VALUE, ReadWrite))
|
Self(Union::Map(map.into(), DEFAULT_TAG_VALUE, ReadWrite))
|
||||||
}
|
}
|
||||||
@ -916,7 +913,6 @@ impl Dynamic {
|
|||||||
/// Not available under `no-std` or `no_time`.
|
/// Not available under `no-std` or `no_time`.
|
||||||
#[cfg(not(feature = "no_time"))]
|
#[cfg(not(feature = "no_time"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn from_timestamp(value: Instant) -> Self {
|
pub fn from_timestamp(value: Instant) -> Self {
|
||||||
Self(Union::TimeStamp(value.into(), DEFAULT_TAG_VALUE, ReadWrite))
|
Self(Union::TimeStamp(value.into(), DEFAULT_TAG_VALUE, ReadWrite))
|
||||||
}
|
}
|
||||||
@ -991,7 +987,6 @@ impl Dynamic {
|
|||||||
}
|
}
|
||||||
/// Make this [`Dynamic`] read-only (i.e. a constant).
|
/// Make this [`Dynamic`] read-only (i.e. a constant).
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn into_read_only(self) -> Self {
|
pub fn into_read_only(self) -> Self {
|
||||||
let mut value = self;
|
let mut value = self;
|
||||||
value.set_access_mode(AccessMode::ReadOnly);
|
value.set_access_mode(AccessMode::ReadOnly);
|
||||||
@ -1085,7 +1080,6 @@ impl Dynamic {
|
|||||||
/// assert_eq!(new_result.to_string(), "hello");
|
/// assert_eq!(new_result.to_string(), "hello");
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
pub fn from<T: Variant + Clone>(value: T) -> Self {
|
pub fn from<T: Variant + Clone>(value: T) -> Self {
|
||||||
// Coded this way in order to maximally leverage potentials for dead-code removal.
|
// Coded this way in order to maximally leverage potentials for dead-code removal.
|
||||||
|
|
||||||
@ -1143,7 +1137,6 @@ impl Dynamic {
|
|||||||
/// If the [`Dynamic`] value is already shared, this method returns itself.
|
/// If the [`Dynamic`] value is already shared, this method returns itself.
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
pub fn into_shared(self) -> Self {
|
pub fn into_shared(self) -> Self {
|
||||||
let _access = self.access_mode();
|
let _access = self.access_mode();
|
||||||
|
|
||||||
@ -1182,6 +1175,7 @@ impl Dynamic {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
#[allow(unused_mut)]
|
||||||
pub fn try_cast<T: Any>(mut self) -> Option<T> {
|
pub fn try_cast<T: Any>(mut self) -> Option<T> {
|
||||||
// Coded this way in order to maximally leverage potentials for dead-code removal.
|
// Coded this way in order to maximally leverage potentials for dead-code removal.
|
||||||
|
|
||||||
@ -1365,7 +1359,6 @@ impl Dynamic {
|
|||||||
///
|
///
|
||||||
/// If the [`Dynamic`] is a shared value, it returns a cloned copy of the shared value.
|
/// If the [`Dynamic`] is a shared value, it returns a cloned copy of the shared value.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
pub fn flatten_clone(&self) -> Self {
|
pub fn flatten_clone(&self) -> Self {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[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
|
/// If the [`Dynamic`] is a shared value, it returns the shared value if there are no
|
||||||
/// outstanding references, or a cloned copy.
|
/// outstanding references, or a cloned copy.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
pub fn flatten(self) -> Self {
|
pub fn flatten(self) -> Self {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[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).
|
/// 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.
|
/// Otherwise, this call panics if the data is currently borrowed for write.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
pub fn read_lock<T: Any + Clone>(&self) -> Option<DynamicReadLock<T>> {
|
pub fn read_lock<T: Any + Clone>(&self) -> Option<DynamicReadLock<T>> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[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).
|
/// 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.
|
/// Otherwise, this call panics if the data is currently borrowed for write.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
pub fn write_lock<T: Any + Clone>(&mut self) -> Option<DynamicWriteLock<T>> {
|
pub fn write_lock<T: Any + Clone>(&mut self) -> Option<DynamicWriteLock<T>> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
@ -135,22 +135,20 @@ impl fmt::Display for EvalAltResult {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
Self::ErrorInFunctionCall(s, src, err, ..) if crate::parser::is_anonymous_fn(s) => {
|
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() {
|
if !src.is_empty() {
|
||||||
write!(f, " @ '{src}'")?;
|
write!(f, " @ '{src}'")?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::ErrorInFunctionCall(s, src, err, ..) => {
|
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() {
|
if !src.is_empty() {
|
||||||
write!(f, " @ '{src}'")?;
|
write!(f, " @ '{src}'")?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::ErrorInModule(s, err, ..) if s.is_empty() => {
|
Self::ErrorInModule(s, err, ..) if s.is_empty() => write!(f, "{err}\nin module")?,
|
||||||
write!(f, "Error in module > {err}")?
|
Self::ErrorInModule(s, err, ..) => write!(f, "{err}\nin module '{s}'")?,
|
||||||
}
|
|
||||||
Self::ErrorInModule(s, err, ..) => write!(f, "Error in module '{s}' > {err}")?,
|
|
||||||
|
|
||||||
Self::ErrorVariableExists(s, ..) => write!(f, "Variable already defined: {s}")?,
|
Self::ErrorVariableExists(s, ..) => write!(f, "Variable already defined: {s}")?,
|
||||||
Self::ErrorForbiddenVariable(s, ..) => write!(f, "Forbidden variable name: {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::ErrorIndexNotFound(s, ..) => write!(f, "Invalid index: {s}")?,
|
||||||
Self::ErrorFunctionNotFound(s, ..) => write!(f, "Function not found: {s}")?,
|
Self::ErrorFunctionNotFound(s, ..) => write!(f, "Function not found: {s}")?,
|
||||||
Self::ErrorModuleNotFound(s, ..) => write!(f, "Module not found: {s}")?,
|
Self::ErrorModuleNotFound(s, ..) => write!(f, "Module not found: {s}")?,
|
||||||
Self::ErrorDataRace(s, ..) => {
|
Self::ErrorDataRace(s, ..) => write!(f, "Data race detected on variable '{s}'")?,
|
||||||
write!(f, "Data race detected when accessing variable: {s}")?
|
|
||||||
}
|
|
||||||
|
|
||||||
Self::ErrorDotExpr(s, ..) if s.is_empty() => f.write_str("Malformed dot expression")?,
|
Self::ErrorDotExpr(s, ..) if s.is_empty() => f.write_str("Malformed dot expression")?,
|
||||||
Self::ErrorDotExpr(s, ..) => f.write_str(s)?,
|
Self::ErrorDotExpr(s, ..) => f.write_str(s)?,
|
||||||
|
|
||||||
Self::ErrorIndexingType(s, ..) => write!(f, "Indexer unavailable: {s}")?,
|
Self::ErrorIndexingType(s, ..) => write!(f, "Indexer unavailable: {s}")?,
|
||||||
Self::ErrorUnboundThis(..) => f.write_str("'this' not bound")?,
|
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::ErrorTooManyOperations(..) => f.write_str("Too many operations")?,
|
||||||
Self::ErrorTooManyModules(..) => f.write_str("Too many modules imported")?,
|
Self::ErrorTooManyModules(..) => f.write_str("Too many modules imported")?,
|
||||||
Self::ErrorStackOverflow(..) => f.write_str("Stack overflow")?,
|
Self::ErrorStackOverflow(..) => f.write_str("Stack overflow")?,
|
||||||
@ -188,31 +184,25 @@ impl fmt::Display for EvalAltResult {
|
|||||||
if s.starts_with(crate::engine::FN_GET) =>
|
if s.starts_with(crate::engine::FN_GET) =>
|
||||||
{
|
{
|
||||||
let prop = &s[crate::engine::FN_GET.len()..];
|
let prop = &s[crate::engine::FN_GET.len()..];
|
||||||
write!(
|
write!(f, "Non-pure property {prop} cannot be accessed on constant")?
|
||||||
f,
|
|
||||||
"Property {prop} is not pure and cannot be accessed on a constant"
|
|
||||||
)?
|
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Self::ErrorNonPureMethodCallOnConstant(s, ..)
|
Self::ErrorNonPureMethodCallOnConstant(s, ..)
|
||||||
if s.starts_with(crate::engine::FN_SET) =>
|
if s.starts_with(crate::engine::FN_SET) =>
|
||||||
{
|
{
|
||||||
let prop = &s[crate::engine::FN_SET.len()..];
|
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"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::ErrorNonPureMethodCallOnConstant(s, ..) if s == crate::engine::FN_IDX_GET => {
|
Self::ErrorNonPureMethodCallOnConstant(s, ..) if s == crate::engine::FN_IDX_GET => {
|
||||||
write!(
|
write!(f, "Non-pure indexer cannot be accessed on constant")?
|
||||||
f,
|
|
||||||
"Indexer is not pure and cannot be accessed on a constant"
|
|
||||||
)?
|
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::ErrorNonPureMethodCallOnConstant(s, ..) if s == crate::engine::FN_IDX_SET => {
|
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, ..) => {
|
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}")?,
|
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, ..) if s.is_empty() => f.write_str("Arithmetic error")?,
|
||||||
Self::ErrorArithmetic(s, ..) => f.write_str(s)?,
|
Self::ErrorArithmetic(s, ..) => f.write_str(s)?,
|
||||||
|
|
||||||
Self::LoopBreak(true, ..) => f.write_str("'break' 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 inside 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")?,
|
Self::Return(..) => f.write_str("NOT AN ERROR - function returns value")?,
|
||||||
|
|
||||||
@ -261,7 +251,7 @@ impl fmt::Display for EvalAltResult {
|
|||||||
f,
|
f,
|
||||||
"Bit-field index {index} out of bounds: only {max} bits in bit-field",
|
"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(" "))?,
|
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`.
|
/// Not available under `no_float`.
|
||||||
#[derive(Clone, Copy, Eq, PartialEq, PartialOrd)]
|
#[derive(Clone, Copy, Eq, PartialEq, PartialOrd)]
|
||||||
|
#[must_use]
|
||||||
pub struct FloatWrapper<F>(F);
|
pub struct FloatWrapper<F>(F);
|
||||||
|
|
||||||
impl Hash for FloatWrapper<crate::FLOAT> {
|
impl Hash for FloatWrapper<crate::FLOAT> {
|
||||||
@ -108,7 +109,6 @@ impl<F: Float> FloatWrapper<F> {
|
|||||||
|
|
||||||
/// Create a new [`FloatWrapper`].
|
/// Create a new [`FloatWrapper`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub const fn new(value: F) -> Self {
|
pub const fn new(value: F) -> Self {
|
||||||
Self(value)
|
Self(value)
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,8 @@ use crate::eval::GlobalRuntimeState;
|
|||||||
use crate::tokenizer::is_valid_function_name;
|
use crate::tokenizer::is_valid_function_name;
|
||||||
use crate::types::dynamic::Variant;
|
use crate::types::dynamic::Variant;
|
||||||
use crate::{
|
use crate::{
|
||||||
reify, Dynamic, Engine, FuncArgs, ImmutableString, NativeCallContext, Position, RhaiError,
|
Dynamic, Engine, FuncArgs, ImmutableString, NativeCallContext, Position, RhaiError, RhaiResult,
|
||||||
RhaiResult, RhaiResultOf, StaticVec, AST, ERR,
|
RhaiResultOf, StaticVec, AST, ERR,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
@ -24,7 +24,6 @@ pub const MAX_STRING_LEN: usize = 24;
|
|||||||
/// _(internals)_ A cache for interned strings.
|
/// _(internals)_ A cache for interned strings.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
#[must_use]
|
|
||||||
pub struct StringsInterner {
|
pub struct StringsInterner {
|
||||||
/// Cached strings.
|
/// Cached strings.
|
||||||
cache: StraightHashMap<ImmutableString>,
|
cache: StraightHashMap<ImmutableString>,
|
||||||
@ -34,6 +33,7 @@ pub struct StringsInterner {
|
|||||||
|
|
||||||
impl Default for StringsInterner {
|
impl Default for StringsInterner {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
@ -50,6 +50,7 @@ impl fmt::Debug for StringsInterner {
|
|||||||
impl StringsInterner {
|
impl StringsInterner {
|
||||||
/// Create a new [`StringsInterner`].
|
/// Create a new [`StringsInterner`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
cache: StraightHashMap::default(),
|
cache: StraightHashMap::default(),
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
//! Module defining Rhai data types.
|
//! Module defining Rhai data types.
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
pub mod restore;
|
||||||
|
|
||||||
pub mod bloom_filter;
|
pub mod bloom_filter;
|
||||||
pub mod custom_types;
|
pub mod custom_types;
|
||||||
pub mod dynamic;
|
pub mod dynamic;
|
||||||
@ -9,7 +12,8 @@ pub mod fn_ptr;
|
|||||||
pub mod immutable_string;
|
pub mod immutable_string;
|
||||||
pub mod interner;
|
pub mod interner;
|
||||||
pub mod parse_error;
|
pub mod parse_error;
|
||||||
pub mod restore;
|
pub mod position;
|
||||||
|
pub mod position_none;
|
||||||
pub mod scope;
|
pub mod scope;
|
||||||
pub mod variant;
|
pub mod variant;
|
||||||
|
|
||||||
@ -25,6 +29,12 @@ pub use fn_ptr::FnPtr;
|
|||||||
pub use immutable_string::ImmutableString;
|
pub use immutable_string::ImmutableString;
|
||||||
pub use interner::StringsInterner;
|
pub use interner::StringsInterner;
|
||||||
pub use parse_error::{LexError, ParseError, ParseErrorType};
|
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 restore::RestoreOnDrop;
|
||||||
pub use scope::Scope;
|
pub use scope::Scope;
|
||||||
pub use variant::Variant;
|
pub use variant::Variant;
|
||||||
|
@ -19,7 +19,7 @@ pub enum LexError {
|
|||||||
UnexpectedInput(String),
|
UnexpectedInput(String),
|
||||||
/// A string literal is not terminated before a new-line or EOF.
|
/// A string literal is not terminated before a new-line or EOF.
|
||||||
UnterminatedString,
|
UnterminatedString,
|
||||||
/// An identifier is in an invalid format.
|
/// An identifier or string literal is longer than the maximum allowed length.
|
||||||
StringTooLong(usize),
|
StringTooLong(usize),
|
||||||
/// An string/character/numeric escape sequence is in an invalid format.
|
/// An string/character/numeric escape sequence is in an invalid format.
|
||||||
MalformedEscapeSequence(String),
|
MalformedEscapeSequence(String),
|
||||||
@ -44,11 +44,7 @@ impl fmt::Display for LexError {
|
|||||||
Self::MalformedChar(s) => write!(f, "Invalid character: '{s}'"),
|
Self::MalformedChar(s) => write!(f, "Invalid character: '{s}'"),
|
||||||
Self::MalformedIdentifier(s) => write!(f, "Variable name is not proper: '{s}'"),
|
Self::MalformedIdentifier(s) => write!(f, "Variable name is not proper: '{s}'"),
|
||||||
Self::UnterminatedString => f.write_str("Open string is not terminated"),
|
Self::UnterminatedString => f.write_str("Open string is not terminated"),
|
||||||
Self::StringTooLong(max) => write!(
|
Self::StringTooLong(max) => write!(f, "String is too long (max {max})"),
|
||||||
f,
|
|
||||||
"Length of string literal exceeds the maximum limit ({})",
|
|
||||||
max
|
|
||||||
),
|
|
||||||
Self::ImproperSymbol(s, d) if d.is_empty() => {
|
Self::ImproperSymbol(s, d) if d.is_empty() => {
|
||||||
write!(f, "Invalid symbol encountered: '{s}'")
|
write!(f, "Invalid symbol encountered: '{s}'")
|
||||||
}
|
}
|
||||||
@ -262,7 +258,7 @@ impl From<LexError> for ParseErrorType {
|
|||||||
fn from(err: LexError) -> Self {
|
fn from(err: LexError) -> Self {
|
||||||
match err {
|
match err {
|
||||||
LexError::StringTooLong(max) => {
|
LexError::StringTooLong(max) => {
|
||||||
Self::LiteralTooLarge("Length of string literal".to_string(), max)
|
Self::LiteralTooLarge("Length of string".to_string(), max)
|
||||||
}
|
}
|
||||||
_ => Self::BadInput(err),
|
_ => 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")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
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.
|
/// Run custom restoration logic upon the end of scope.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub struct RestoreOnDrop<'a, T: ?Sized, R: FnOnce(&mut T)> {
|
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> {
|
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
|
/// Create a new [`RestoreOnDrop`] that locks a mutable reference and runs restoration logic at
|
||||||
/// the end of scope.
|
/// 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> {
|
impl<'a, T: ?Sized, R: FnOnce(&mut T)> Drop for RestoreOnDrop<'a, T, R> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if let Some(restore) = self.restore.take() {
|
self.restore.take().unwrap()(self.value);
|
||||||
restore(self.value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -386,7 +386,7 @@ impl Scope<'_> {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn pop(&mut self) -> &mut Self {
|
pub fn pop(&mut self) -> &mut Self {
|
||||||
self.names.pop().expect("`Scope` must not be empty");
|
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.aliases.pop().expect("`Scope` must not be empty");
|
||||||
self
|
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[-1]")?, 3);
|
||||||
assert_eq!(engine.eval::<INT>("let y = [1, 2, 3]; y[-3]")?, 1);
|
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]; 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]; y += 4; y[3]")?, 4);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>("let y = [1, 2, 3]; pad(y, 5, 42); len(y)")?,
|
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();
|
let mut handler = Handler::new();
|
||||||
assert!(!handler.scope.get_value::<bool>("state").unwrap());
|
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!(handler.scope.get_value::<bool>("state").unwrap());
|
||||||
assert_eq!(handler.on_event("start", 999).as_int().unwrap(), 1041);
|
assert_eq!(handler.on_event("start", 999).as_int().unwrap(), 1041);
|
||||||
|
|
||||||
|
@ -75,9 +75,15 @@ fn test_comments_doc() -> Result<(), Box<EvalAltResult>> {
|
|||||||
",
|
",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_position"))]
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ast.iter_functions().next().unwrap().comments[0],
|
ast.iter_functions().next().unwrap().comments[0],
|
||||||
"/** Hello world\n ** how are you?\n **/"
|
"/** 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
|
assert!(engine
|
||||||
|
@ -17,7 +17,7 @@ fn test_max_string_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
.compile(r#"let x = "hello, world!";"#)
|
.compile(r#"let x = "hello, world!";"#)
|
||||||
.expect_err("should error")
|
.expect_err("should error")
|
||||||
.err_type(),
|
.err_type(),
|
||||||
ParseErrorType::LiteralTooLarge("Length of string literal".to_string(), 10)
|
ParseErrorType::LiteralTooLarge("Length of string".to_string(), 10)
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -25,7 +25,7 @@ fn test_max_string_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
.compile(r#"let x = "朝に紅顔、暮に白骨";"#)
|
.compile(r#"let x = "朝に紅顔、暮に白骨";"#)
|
||||||
.expect_err("should error")
|
.expect_err("should error")
|
||||||
.err_type(),
|
.err_type(),
|
||||||
ParseErrorType::LiteralTooLarge("Length of string literal".to_string(), 10)
|
ParseErrorType::LiteralTooLarge("Length of string".to_string(), 10)
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
|
@ -12,7 +12,7 @@ fn test_debugging() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
engine.register_debugger(
|
engine.register_debugger(
|
||||||
|_| Dynamic::UNIT,
|
|_, dbg| dbg,
|
||||||
|_, _, _, _, _| Ok(rhai::debugger::DebuggerCommand::Continue),
|
|_, _, _, _, _| Ok(rhai::debugger::DebuggerCommand::Continue),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -47,19 +47,20 @@ fn test_debugger_state() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
engine.register_debugger(
|
engine.register_debugger(
|
||||||
|_| {
|
|_, mut debugger| {
|
||||||
// Say, use an object map for the debugger state
|
// Say, use an object map for the debugger state
|
||||||
let mut state = Map::new();
|
let mut state = Map::new();
|
||||||
// Initialize properties
|
// Initialize properties
|
||||||
state.insert("hello".into(), (42 as INT).into());
|
state.insert("hello".into(), (42 as INT).into());
|
||||||
state.insert("foo".into(), false.into());
|
state.insert("foo".into(), false.into());
|
||||||
Dynamic::from_map(state)
|
debugger.set_state(state);
|
||||||
|
debugger
|
||||||
},
|
},
|
||||||
|mut context, _, _, _, _| {
|
|mut context, _, _, _, _| {
|
||||||
// Print debugger state - which is an object map
|
// Print debugger state - which is an object map
|
||||||
println!(
|
println!(
|
||||||
"Current state = {}",
|
"Current state = {}",
|
||||||
context.global_runtime_state_mut().debugger().state()
|
context.global_runtime_state().debugger().state()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Modify state
|
// Modify state
|
||||||
|
@ -34,6 +34,15 @@ fn test_loop() -> Result<(), Box<EvalAltResult>> {
|
|||||||
ParseErrorType::LoopBreak
|
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!(
|
assert_eq!(
|
||||||
*engine
|
*engine
|
||||||
.compile("let x = 0; if x > 0 { continue; }")
|
.compile("let x = 0; if x > 0 { continue; }")
|
||||||
|
Loading…
Reference in New Issue
Block a user