Merge pull request #503 from schungx/master

Efficiency refactor.
This commit is contained in:
Stephen Chung 2022-01-07 10:50:24 +08:00 committed by GitHub
commit 595636add4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
106 changed files with 8477 additions and 7145 deletions

View File

@ -5,12 +5,59 @@ Version 1.4.0
============= =============
This version adds support for integer _ranges_ via the `..` and `..=` operators. This version adds support for integer _ranges_ via the `..` and `..=` operators.
Many standard API's are extended with range parameters where appropriate.
Script-breaking changes
-----------------------
* `is` is (pun intended) now a reserved keyword to prepare for possible future type checking expressions (e.g. `x is "string"`).
Breaking changes
----------------
* `LogicPackage` is removed from `CorePackage`.
* Bit-field functions are moved into a new `BitFieldPackage` (used to be in `LogicPackage`) which makes more sense.
Bug fixes
---------
* Constructing a literal array or object map now checks for size limits for each item instead of at the very end when it is already too late.
* Non-`INT` integer types are now treated exactly as custom types under `only_i64` and `only_i32`.
* Calling `pad` on an array now checks for total size over limit after each item added.
New features New features
------------ ------------
* Added support for integer _ranges_ via the `..` and `..=` operators. * Added support for integer _ranges_ via the `..` and `..=` operators.
Enhancements
------------
* A new syntax is introduced for `def_package!` that will replace the old syntax in future versions.
* Added `NativeCallContext::call_fn` to easily call a function.
* Doc-comments on plugin module functions are extracted into the functions' metadata.
Deprecated API's
----------------
* `Expression::get_variable_name` is deprecated in favor of the new `Expression::get_string_value`.
* The old syntax of `def_package!` is deprecated in favor of the new syntax.
Version 1.3.1
=============
Bug fixes
---------
* Custom syntax now works properly inside binary expressions and with method calls.
* Hex numbers with the high-bit set now parse correctly into negative integer numbers.
Enhancements
------------
* `BLOB`'s are refined to display in a more compact hex format.
Version 1.3.0 Version 1.3.0
============= =============
@ -71,8 +118,8 @@ Bug fixes
Version 1.2.0 Version 1.2.0
============= =============
Bug fixes with breaking script changes Bug fixes (potentially script-breaking)
------------------------------------- --------------------------------------
* As originally intended, function calls with a bang (`!`) now operates directly on the caller's scope, allowing variables inside the scope to be mutated. * As originally intended, function calls with a bang (`!`) now operates directly on the caller's scope, allowing variables inside the scope to be mutated.
* As originally intended, `Engine::XXX_with_scope` API's now properly propagate constants within the provided scope also to _functions_ in the script. * As originally intended, `Engine::XXX_with_scope` API's now properly propagate constants within the provided scope also to _functions_ in the script.
@ -104,7 +151,7 @@ Deprecated API's
---------------- ----------------
* `NativeCallContext::call_fn_dynamic_raw` is deprecated and `NativeCallContext::call_fn_raw` is added. * `NativeCallContext::call_fn_dynamic_raw` is deprecated and `NativeCallContext::call_fn_raw` is added.
* `From<EvalAltResult>` for `Result<T, Box<EvalAltResult>>` is deprecated so it will no longer be possible to do `EvalAltResult::ErrorXXXXX.into()` to convert to a `Result`; instead, `Err(EvalAltResult:ErrorXXXXX.into())` must be used. Code is clearer if errors are explicitly wrapped in `Err`. * `From<EvalAltResult>` for `Result<T, Box<EvalAltResult> >` is deprecated so it will no longer be possible to do `EvalAltResult::ErrorXXXXX.into()` to convert to a `Result`; instead, `Err(EvalAltResult:ErrorXXXXX.into())` must be used. Code is clearer if errors are explicitly wrapped in `Err`.
Version 1.1.2 Version 1.1.2
@ -177,7 +224,7 @@ Enhancements
### `Scope` API ### `Scope` API
* `Scope::set_value` now takes anything that implements `Into<Cow<str>>`. * `Scope::set_value` now takes anything that implements `Into<Cow<str> >`.
* Added `Scope::is_constant` to check if a variable is constant. * Added `Scope::is_constant` to check if a variable is constant.
* Added `Scope::set_or_push` to add a new variable only if one doesn't already exist. * Added `Scope::set_or_push` to add a new variable only if one doesn't already exist.
@ -462,7 +509,8 @@ Enhancements
* Replaced all `HashMap` usage with `BTreeMap` for better performance because collections in Rhai are tiny. * Replaced all `HashMap` usage with `BTreeMap` for better performance because collections in Rhai are tiny.
* `Engine::register_result_fn` no longer requires the successful return type to be `Dynamic`. It can now be any clonable type. * `Engine::register_result_fn` no longer requires the successful return type to be `Dynamic`. It can now be any clonable type.
* `#[rhai_fn(return_raw)]` can now return `Result<T, Box<EvalAltResult>>` where `T` is any clonable type instead of `Result<Dynamic, Box<EvalAltResult>>`. * `#[rhai_fn(return_raw)]` can now return `Result<T, Box<EvalAltResult> >` where `T` is any clonable
type instead of `Result<Dynamic, Box<EvalAltResult> >`.
* `Dynamic::clone_cast` is added to simplify casting from a `&Dynamic`. * `Dynamic::clone_cast` is added to simplify casting from a `&Dynamic`.
@ -932,7 +980,7 @@ Breaking changes
---------------- ----------------
* `AST::iter_functions` now returns an iterator instead of taking a closure. * `AST::iter_functions` now returns an iterator instead of taking a closure.
* `Module::get_script_function_by_signature` renamed to `Module::get_script_fn` and returns `&<Shared<ScriptFnDef>>`. * `Module::get_script_function_by_signature` renamed to `Module::get_script_fn` and returns `&<Shared<ScriptFnDef> >`.
* `Module::num_fn`, `Module::num_var` and `Module::num_iter` are removed and merged into `Module::count`. * `Module::num_fn`, `Module::num_var` and `Module::num_iter` are removed and merged into `Module::count`.
* The `merge_namespaces` parameter to `Module::eval_ast_as_new` is removed and now defaults to `true`. * The `merge_namespaces` parameter to `Module::eval_ast_as_new` is removed and now defaults to `true`.
* `GlobalFileModuleResolver` is removed because its performance gain over the `FileModuleResolver` is no longer very significant. * `GlobalFileModuleResolver` is removed because its performance gain over the `FileModuleResolver` is no longer very significant.
@ -980,7 +1028,7 @@ Bug fixes
Breaking changes Breaking changes
---------------- ----------------
* `Engine::register_set_result` and `Engine::register_indexer_set_result` now take a function that returns `Result<(), Box<EvalAltResult>>`. * `Engine::register_set_result` and `Engine::register_indexer_set_result` now take a function that returns `Result<(), Box<EvalAltResult> >`.
* `Engine::register_indexer_XXX` and `Module::set_indexer_XXX` panic when the type is `Array`, `Map` or `String`. * `Engine::register_indexer_XXX` and `Module::set_indexer_XXX` panic when the type is `Array`, `Map` or `String`.
* `EvalAltResult` has a new variant `ErrorInModule` which holds errors when loading an external module. * `EvalAltResult` has a new variant `ErrorInModule` which holds errors when loading an external module.
* `Module::eval_ast_as_new` now takes an extra boolean parameter, indicating whether to encapsulate the entire module into a separate namespace. * `Module::eval_ast_as_new` now takes an extra boolean parameter, indicating whether to encapsulate the entire module into a separate namespace.
@ -1101,7 +1149,7 @@ Breaking changes
* `EvalAltResult::ErrorMismatchOutputType` has an extra argument containing the name of the requested type. * `EvalAltResult::ErrorMismatchOutputType` has an extra argument containing the name of the requested type.
* `Engine::call_fn_dynamic` take an extra argument, allowing a `Dynamic` value to be bound to the `this` pointer. * `Engine::call_fn_dynamic` take an extra argument, allowing a `Dynamic` value to be bound to the `this` pointer.
* Precedence of the `%` (modulo) operator is lowered to below `<<` ad `>>`. This is to handle the case of `x << 3 % 10`. * Precedence of the `%` (modulo) operator is lowered to below bit shifts. This is to handle the case of `x < < 3 % 10`.
New features New features
------------ ------------

View File

@ -79,7 +79,7 @@ The [`scripts`](https://github.com/rhaiscript/rhai/tree/master/scripts) subdirec
Below is the standard _Fibonacci_ example for scripting languages: Below is the standard _Fibonacci_ example for scripting languages:
```js ```ts
// This Rhai script calculates the n-th Fibonacci number using a // This Rhai script calculates the n-th Fibonacci number using a
// really dumb algorithm to test the speed of the scripting engine. // really dumb algorithm to test the speed of the scripting engine.

View File

@ -64,7 +64,7 @@ fn bench_eval_array_large_set(bench: &mut Bencher) {
#[bench] #[bench]
fn bench_eval_array_loop(bench: &mut Bencher) { fn bench_eval_array_loop(bench: &mut Bencher) {
let script = r#" let script = "
let list = []; let list = [];
for i in 0..10_000 { for i in 0..10_000 {
@ -76,7 +76,7 @@ fn bench_eval_array_loop(bench: &mut Bencher) {
for i in list { for i in list {
sum += i; sum += i;
} }
"#; ";
let mut engine = Engine::new(); let mut engine = Engine::new();
engine.set_optimization_level(OptimizationLevel::None); engine.set_optimization_level(OptimizationLevel::None);

View File

@ -108,12 +108,12 @@ fn bench_eval_call(bench: &mut Bencher) {
#[bench] #[bench]
fn bench_eval_loop_number(bench: &mut Bencher) { fn bench_eval_loop_number(bench: &mut Bencher) {
let script = r#" let script = "
let s = 0; let s = 0;
for x in 0..10000 { for x in 0..10000 {
s += 1; s += 1;
} }
"#; ";
let mut engine = Engine::new(); let mut engine = Engine::new();
engine.set_optimization_level(OptimizationLevel::None); engine.set_optimization_level(OptimizationLevel::None);
@ -159,7 +159,7 @@ fn bench_eval_loop_strings_no_build(bench: &mut Bencher) {
#[bench] #[bench]
fn bench_eval_switch(bench: &mut Bencher) { fn bench_eval_switch(bench: &mut Bencher) {
let script = r#" let script = "
let sum = 0; let sum = 0;
let rem = 0; let rem = 0;
@ -179,7 +179,7 @@ fn bench_eval_switch(bench: &mut Bencher) {
9 => 1, 9 => 1,
} }
} }
"#; ";
let mut engine = Engine::new(); let mut engine = Engine::new();
engine.set_optimization_level(OptimizationLevel::None); engine.set_optimization_level(OptimizationLevel::None);
@ -191,7 +191,7 @@ fn bench_eval_switch(bench: &mut Bencher) {
#[bench] #[bench]
fn bench_eval_nested_if(bench: &mut Bencher) { fn bench_eval_nested_if(bench: &mut Bencher) {
let script = r#" let script = "
let sum = 0; let sum = 0;
let rem = 0; let rem = 0;
@ -209,7 +209,7 @@ fn bench_eval_nested_if(bench: &mut Bencher) {
else if rem == 8 { 42 } else if rem == 8 { 42 }
else if rem == 9 { 1 }; else if rem == 9 { 1 };
} }
"#; ";
let mut engine = Engine::new(); let mut engine = Engine::new();
engine.set_optimization_level(OptimizationLevel::None); engine.set_optimization_level(OptimizationLevel::None);

View File

@ -8,10 +8,10 @@ use test::Bencher;
#[bench] #[bench]
fn bench_eval_module(bench: &mut Bencher) { fn bench_eval_module(bench: &mut Bencher) {
let script = r#" let script = "
fn foo(x) { x + 1 } fn foo(x) { x + 1 }
fn bar(x) { foo(x) } fn bar(x) { foo(x) }
"#; ";
let mut engine = Engine::new(); let mut engine = Engine::new();
engine.set_optimization_level(OptimizationLevel::None); engine.set_optimization_level(OptimizationLevel::None);
@ -24,10 +24,10 @@ fn bench_eval_module(bench: &mut Bencher) {
let ast = engine let ast = engine
.compile( .compile(
r#" "
fn foo(x) { x - 1 } fn foo(x) { x - 1 }
testing::bar(41) testing::bar(41)
"#, ",
) )
.unwrap(); .unwrap();
@ -41,11 +41,11 @@ fn bench_eval_function_call(bench: &mut Bencher) {
let ast = engine let ast = engine
.compile( .compile(
r#" "
fn foo(x) { x - 1 } fn foo(x) { x - 1 }
fn bar(x) { foo(x) } fn bar(x) { foo(x) }
bar(41) bar(41)
"#, ",
) )
.unwrap(); .unwrap();

View File

@ -8,13 +8,13 @@ use test::Bencher;
#[bench] #[bench]
fn bench_iterations_1000(bench: &mut Bencher) { fn bench_iterations_1000(bench: &mut Bencher) {
let script = r#" let script = "
let x = 1_000; let x = 1_000;
while x > 0 { while x > 0 {
x -= 1; x -= 1;
} }
"#; ";
let mut engine = Engine::new(); let mut engine = Engine::new();
engine.set_optimization_level(OptimizationLevel::None); engine.set_optimization_level(OptimizationLevel::None);
@ -26,7 +26,7 @@ fn bench_iterations_1000(bench: &mut Bencher) {
#[bench] #[bench]
fn bench_iterations_fibonacci(bench: &mut Bencher) { fn bench_iterations_fibonacci(bench: &mut Bencher) {
let script = r#" let script = "
fn fibonacci(n) { fn fibonacci(n) {
if n < 2 { if n < 2 {
n n
@ -36,7 +36,7 @@ fn bench_iterations_fibonacci(bench: &mut Bencher) {
} }
fibonacci(20) fibonacci(20)
"#; ";
let mut engine = Engine::new(); let mut engine = Engine::new();
engine.set_optimization_level(OptimizationLevel::None); engine.set_optimization_level(OptimizationLevel::None);
@ -48,11 +48,11 @@ fn bench_iterations_fibonacci(bench: &mut Bencher) {
#[bench] #[bench]
fn bench_iterations_array(bench: &mut Bencher) { fn bench_iterations_array(bench: &mut Bencher) {
let script = r#" let script = "
let x = []; let x = [];
x.pad(1000, 0); x.pad(1000, 0);
for i in 0..1000 { x[i] = i % 256; } for i in 0..1000 { x[i] = i % 256; }
"#; ";
let mut engine = Engine::new(); let mut engine = Engine::new();
engine.set_optimization_level(OptimizationLevel::None); engine.set_optimization_level(OptimizationLevel::None);
@ -64,10 +64,10 @@ fn bench_iterations_array(bench: &mut Bencher) {
#[bench] #[bench]
fn bench_iterations_blob(bench: &mut Bencher) { fn bench_iterations_blob(bench: &mut Bencher) {
let script = r#" let script = "
let x = blob(1000, 0); let x = blob(1000, 0);
for i in 0..1000 { x[i] = i % 256; } for i in 0..1000 { x[i] = i % 256; }
"#; ";
let mut engine = Engine::new(); let mut engine = Engine::new();
engine.set_optimization_level(OptimizationLevel::None); engine.set_optimization_level(OptimizationLevel::None);

View File

@ -64,7 +64,7 @@ fn bench_parse_map(bench: &mut Bencher) {
#[bench] #[bench]
fn bench_parse_primes(bench: &mut Bencher) { fn bench_parse_primes(bench: &mut Bencher) {
let script = r#" let script = "
// This script uses the Sieve of Eratosthenes to calculate prime numbers. // This script uses the Sieve of Eratosthenes to calculate prime numbers.
let now = timestamp(); let now = timestamp();
@ -79,7 +79,7 @@ fn bench_parse_primes(bench: &mut Bencher) {
let total_primes_found = 0; let total_primes_found = 0;
for p in 2..=MAX_NUMBER_TO_CHECK { for p in 2..MAX_NUMBER_TO_CHECK {
if prime_mask[p] { if prime_mask[p] {
print(p); print(p);
@ -95,7 +95,7 @@ fn bench_parse_primes(bench: &mut Bencher) {
print(`Total ${total_primes_found} primes.`); print(`Total ${total_primes_found} primes.`);
print(`Run time = ${now.elapsed} seconds.`); print(`Run time = ${now.elapsed} seconds.`);
"#; ";
let mut engine = Engine::new(); let mut engine = Engine::new();
engine.set_optimization_level(OptimizationLevel::None); engine.set_optimization_level(OptimizationLevel::None);

View File

@ -8,29 +8,29 @@ use test::Bencher;
// This script uses the Sieve of Eratosthenes to calculate prime numbers. // This script uses the Sieve of Eratosthenes to calculate prime numbers.
const SCRIPT: &str = r#" const SCRIPT: &str = "
const MAX_NUMBER_TO_CHECK = 1_000; // 168 primes <= 1000 const MAX_NUMBER_TO_CHECK = 1_000; // 168 primes <= 1000
let prime_mask = []; let prime_mask = [];
prime_mask.pad(MAX_NUMBER_TO_CHECK, true); prime_mask.pad(MAX_NUMBER_TO_CHECK, true);
prime_mask[0] = false; prime_mask[0] = false;
prime_mask[1] = false; prime_mask[1] = false;
let total_primes_found = 0; let total_primes_found = 0;
for p in 2..=MAX_NUMBER_TO_CHECK { for p in 2..MAX_NUMBER_TO_CHECK {
if prime_mask[p] { if prime_mask[p] {
total_primes_found += 1; total_primes_found += 1;
let i = 2 * p; let i = 2 * p;
while i < MAX_NUMBER_TO_CHECK { while i < MAX_NUMBER_TO_CHECK {
prime_mask[i] = false; prime_mask[i] = false;
i += p; i += p;
}
} }
} }
} ";
"#;
#[bench] #[bench]
fn bench_eval_primes(bench: &mut Bencher) { fn bench_eval_primes(bench: &mut Bencher) {

View File

@ -1,6 +1,6 @@
[package] [package]
name = "rhai_codegen" name = "rhai_codegen"
version = "1.2.0" version = "1.3.0"
edition = "2018" edition = "2018"
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"
@ -16,7 +16,7 @@ default = []
metadata = [] metadata = []
[dev-dependencies] [dev-dependencies]
rhai = { path = "..", version = "1.1" } rhai = { path = "..", version = "1.4" }
trybuild = "1" trybuild = "1"
[dependencies] [dependencies]

View File

@ -117,18 +117,64 @@ pub fn inner_item_attributes<T: ExportedParams>(
attrs: &mut Vec<syn::Attribute>, attrs: &mut Vec<syn::Attribute>,
attr_name: &str, attr_name: &str,
) -> syn::Result<T> { ) -> syn::Result<T> {
// Find the #[rhai_fn] attribute which will turn be read for the function parameters. // Find the #[rhai_fn] attribute which will turn be read for function parameters.
if let Some(rhai_fn_idx) = attrs if let Some(index) = attrs
.iter() .iter()
.position(|a| a.path.get_ident().map(|i| *i == attr_name).unwrap_or(false)) .position(|a| a.path.get_ident().map(|i| *i == attr_name).unwrap_or(false))
{ {
let rhai_fn_attr = attrs.remove(rhai_fn_idx); let rhai_fn_attr = attrs.remove(index);
// Cannot have more than one #[rhai_fn]
if let Some(duplicate) = attrs
.iter()
.find(|a| a.path.get_ident().map(|i| *i == attr_name).unwrap_or(false))
{
return Err(syn::Error::new(
duplicate.span(),
format!("duplicated attribute '{}'", attr_name),
));
}
rhai_fn_attr.parse_args_with(T::parse_stream) rhai_fn_attr.parse_args_with(T::parse_stream)
} else { } else {
Ok(T::no_attrs()) Ok(T::no_attrs())
} }
} }
#[cfg(feature = "metadata")]
pub fn doc_attributes(attrs: &mut Vec<syn::Attribute>) -> syn::Result<Vec<String>> {
// Find the #[doc] attribute which will turn be read for function documentation.
let mut comments = Vec::new();
while let Some(index) = attrs
.iter()
.position(|attr| attr.path.get_ident().map(|i| *i == "doc").unwrap_or(false))
{
match attrs.remove(index).parse_meta()? {
syn::Meta::NameValue(syn::MetaNameValue {
lit: syn::Lit::Str(s),
..
}) => {
let mut line = s.value();
if line.contains('\n') {
// Must be a block comment `/** ... */`
line.insert_str(0, "/**");
line.push_str("*/");
} else {
// Single line - assume it is `///`
line.insert_str(0, "///");
}
comments.push(line);
}
_ => continue,
}
}
Ok(comments)
}
pub fn collect_cfg_attr(attrs: &[syn::Attribute]) -> Vec<syn::Attribute> { pub fn collect_cfg_attr(attrs: &[syn::Attribute]) -> Vec<syn::Attribute> {
attrs attrs
.iter() .iter()

View File

@ -282,6 +282,8 @@ pub struct ExportedFn {
mut_receiver: bool, mut_receiver: bool,
params: ExportedFnParams, params: ExportedFnParams,
cfg_attrs: Vec<syn::Attribute>, cfg_attrs: Vec<syn::Attribute>,
#[cfg(feature = "metadata")]
comments: Vec<String>,
} }
impl Parse for ExportedFn { impl Parse for ExportedFn {
@ -404,6 +406,8 @@ impl Parse for ExportedFn {
mut_receiver, mut_receiver,
params: Default::default(), params: Default::default(),
cfg_attrs, cfg_attrs,
#[cfg(feature = "metadata")]
comments: Default::default(),
}) })
} }
} }
@ -503,6 +507,16 @@ impl ExportedFn {
} }
} }
#[cfg(feature = "metadata")]
pub fn comments(&self) -> &[String] {
&self.comments
}
#[cfg(feature = "metadata")]
pub fn set_comments(&mut self, comments: Vec<String>) {
self.comments = comments
}
pub fn set_cfg_attrs(&mut self, cfg_attrs: Vec<syn::Attribute>) { pub fn set_cfg_attrs(&mut self, cfg_attrs: Vec<syn::Attribute>) {
self.cfg_attrs = cfg_attrs self.cfg_attrs = cfg_attrs
} }

View File

@ -117,18 +117,22 @@ impl Parse for Module {
syn::Item::Fn(f) => Some(f), syn::Item::Fn(f) => Some(f),
_ => None, _ => None,
}) })
.try_fold(Vec::new(), |mut vec, item_fn| { .try_fold(Vec::new(), |mut vec, item_fn| -> syn::Result<_> {
let params = let params =
crate::attrs::inner_item_attributes(&mut item_fn.attrs, "rhai_fn")?; crate::attrs::inner_item_attributes(&mut item_fn.attrs, "rhai_fn")?;
syn::parse2::<ExportedFn>(item_fn.to_token_stream()) let f =
.and_then(|mut f| { syn::parse2(item_fn.to_token_stream()).and_then(|mut f: ExportedFn| {
f.set_params(params)?; f.set_params(params)?;
f.set_cfg_attrs(crate::attrs::collect_cfg_attr(&item_fn.attrs)); f.set_cfg_attrs(crate::attrs::collect_cfg_attr(&item_fn.attrs));
#[cfg(feature = "metadata")]
f.set_comments(crate::attrs::doc_attributes(&mut item_fn.attrs)?);
Ok(f) Ok(f)
}) })?;
.map(|f| vec.push(f))
.map(|_| vec) vec.push(f);
Ok(vec)
})?; })?;
// Gather and parse constants definitions. // Gather and parse constants definitions.
for item in content.iter() { for item in content.iter() {

View File

@ -166,20 +166,32 @@ pub fn generate_body(
); );
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
let param_names = quote! { let (param_names, comments) = (
Some(#fn_token_name::PARAM_NAMES) quote! { Some(#fn_token_name::PARAM_NAMES) },
}; function
.comments()
.iter()
.map(|s| syn::LitStr::new(s, Span::call_site()))
.collect::<Vec<_>>(),
);
#[cfg(not(feature = "metadata"))] #[cfg(not(feature = "metadata"))]
let param_names = quote! { None }; let (param_names, comments) = (quote! { None }, Vec::<syn::LitStr>::new());
set_fn_statements.push( set_fn_statements.push(if comments.is_empty() {
syn::parse2::<syn::Stmt>(quote! { syn::parse2::<syn::Stmt>(quote! {
#(#cfg_attrs)* #(#cfg_attrs)*
m.set_fn(#fn_literal, FnNamespace::#ns_str, FnAccess::Public, m.set_fn(#fn_literal, FnNamespace::#ns_str, FnAccess::Public,
#param_names, &[#(#fn_input_types),*], #fn_token_name().into()); #param_names, &[#(#fn_input_types),*], #fn_token_name().into());
}) })
.unwrap(), .unwrap()
); } else {
syn::parse2::<syn::Stmt>(quote! {
#(#cfg_attrs)*
m.set_fn_with_comments(#fn_literal, FnNamespace::#ns_str, FnAccess::Public,
#param_names, &[#(#fn_input_types),*], &[#(#comments),*], #fn_token_name().into());
})
.unwrap()
});
} }
gen_fn_tokens.push(quote! { gen_fn_tokens.push(quote! {

View File

@ -37,6 +37,49 @@ mod module_tests {
); );
} }
#[test]
fn one_factory_fn_with_comments_module() {
let input_tokens: TokenStream = quote! {
pub mod one_fn {
/// This is a doc-comment.
/// Another line.
/** block doc-comment */
// Regular comment
/// Final line.
/** doc-comment
in multiple lines
*/
pub fn get_mystic_number() -> INT {
42
}
}
};
let item_mod = syn::parse2::<Module>(input_tokens).unwrap();
assert!(item_mod.consts().is_empty());
assert_eq!(item_mod.fns().len(), 1);
assert_eq!(item_mod.fns()[0].name().to_string(), "get_mystic_number");
assert_eq!(
item_mod.fns()[0]
.comments()
.iter()
.cloned()
.collect::<Vec<_>>(),
vec![
"/// This is a doc-comment.",
"/// Another line.",
"/// block doc-comment ",
"/// Final line.",
"/** doc-comment\n in multiple lines\n */"
]
);
assert_eq!(item_mod.fns()[0].arg_count(), 0);
assert_eq!(
item_mod.fns()[0].return_type().unwrap(),
&syn::parse2::<syn::Type>(quote! { INT }).unwrap()
);
}
#[test] #[test]
fn one_single_arg_fn_module() { fn one_single_arg_fn_module() {
let input_tokens: TokenStream = quote! { let input_tokens: TokenStream = quote! {
@ -323,6 +366,66 @@ mod generate_tests {
assert_streams_eq(item_mod.generate(), expected_tokens); assert_streams_eq(item_mod.generate(), expected_tokens);
} }
#[test]
fn one_factory_fn_with_comments_module() {
let input_tokens: TokenStream = quote! {
pub mod one_fn {
/// This is a doc-comment.
/// Another line.
/** block doc-comment */
// Regular comment
/// Final line.
/** doc-comment
in multiple lines
*/
pub fn get_mystic_number() -> INT {
42
}
}
};
let expected_tokens = quote! {
pub mod one_fn {
pub fn get_mystic_number() -> INT {
42
}
#[allow(unused_imports)]
use super::*;
pub fn rhai_module_generate() -> Module {
let mut m = Module::new();
rhai_generate_into_module(&mut m, false);
m.build_index();
m
}
#[allow(unused_mut)]
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
m.set_fn_with_comments("get_mystic_number", FnNamespace::Internal, FnAccess::Public,
Some(get_mystic_number_token::PARAM_NAMES), &[], &["/// This is a doc-comment.","/// Another line.","/// block doc-comment ","/// Final line.","/** doc-comment\n in multiple lines\n */"],
get_mystic_number_token().into());
if flatten {} else {}
}
#[allow(non_camel_case_types)]
pub struct get_mystic_number_token();
impl get_mystic_number_token {
pub const PARAM_NAMES: &'static [&'static str] = &["INT"];
#[inline(always)] pub fn param_types() -> [TypeId; 0usize] { [] }
}
impl PluginFunction for get_mystic_number_token {
#[inline(always)]
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
Ok(Dynamic::from(get_mystic_number()))
}
#[inline(always)] fn is_method_call(&self) -> bool { false }
}
}
};
let item_mod = syn::parse2::<Module>(input_tokens).unwrap();
assert_streams_eq(item_mod.generate(), expected_tokens);
}
#[test] #[test]
fn one_single_arg_global_fn_module() { fn one_single_arg_global_fn_module() {
let input_tokens: TokenStream = quote! { let input_tokens: TokenStream = quote! {

View File

@ -0,0 +1,18 @@
use rhai::plugin::*;
#[export_module]
pub mod test_module {
#[rhai_fn(name = "test")]
#[rhai_fn(pure)]
pub fn test_fn(input: Point) -> bool {
input.x > input.y
}
}
fn main() {
if test_module::test_fn(n) {
println!("yes");
} else {
println!("no");
}
}

View File

@ -0,0 +1,17 @@
error: duplicated attribute 'rhai_fn'
--> ui_tests/rhai_fn_duplicate_attr.rs:6:5
|
6 | #[rhai_fn(pure)]
| ^^^^^^^^^^^^^^^^
error[E0433]: failed to resolve: use of undeclared crate or module `test_module`
--> ui_tests/rhai_fn_duplicate_attr.rs:13:8
|
13 | if test_module::test_fn(n) {
| ^^^^^^^^^^^ use of undeclared crate or module `test_module`
error[E0425]: cannot find value `n` in this scope
--> ui_tests/rhai_fn_duplicate_attr.rs:13:29
|
13 | if test_module::test_fn(n) {
| ^ not found in this scope

View File

@ -10,5 +10,5 @@ error[E0277]: the trait bound `NonClonable: Clone` is not satisfied
note: required by a bound in `rhai::Dynamic::from` note: required by a bound in `rhai::Dynamic::from`
--> $WORKSPACE/src/types/dynamic.rs --> $WORKSPACE/src/types/dynamic.rs
| |
| pub fn from<T: Variant + Clone>(mut value: T) -> Self { | pub fn from<T: Variant + Clone>(value: T) -> Self {
| ^^^^^ required by this bound in `rhai::Dynamic::from` | ^^^^^ required by this bound in `rhai::Dynamic::from`

View File

@ -10,5 +10,5 @@ error[E0277]: the trait bound `NonClonable: Clone` is not satisfied
note: required by a bound in `rhai::Dynamic::from` note: required by a bound in `rhai::Dynamic::from`
--> $WORKSPACE/src/types/dynamic.rs --> $WORKSPACE/src/types/dynamic.rs
| |
| pub fn from<T: Variant + Clone>(mut value: T) -> Self { | pub fn from<T: Variant + Clone>(value: T) -> Self {
| ^^^^^ required by this bound in `rhai::Dynamic::from` | ^^^^^ required by this bound in `rhai::Dynamic::from`

20
scripts/doc-comments.rhai Normal file
View File

@ -0,0 +1,20 @@
/// The function `foo`, which prints `hello, world!` and a magic number,
/// accepts three parameters.
///
/// # Parameters
///
/// `x` - `i64`
/// `y` - `string`
/// `z` - `bool`
///
/// # Notes
///
/// This is a doc-comment. It can be obtained with the `metadata` feature.
///
/// An example is the `rhai-doc` app.
///
fn foo(x, y, z) {
print(`hello, world! ${if z { x + y.len() } else { x } }`);
}
foo(39, "bar", true);

View File

@ -11,7 +11,7 @@ for (a, i) in arr {
if a == 3 { break; } if a == 3 { break; }
} }
//print(a); // <- if you uncomment this line, the script will fail to run //print(a); // <- if you uncomment this line, the script will fail to compile
// because 'a' is not defined here // because 'a' is not defined here
for i in range(5, 0, -1) { // runs from 5 down to 1 for i in range(5, 0, -1) { // runs from 5 down to 1

26
scripts/for3.rhai Normal file
View File

@ -0,0 +1,26 @@
const MAX = 100;
const CHECK = ((MAX - 1) ** 2) * MAX;
print("Ready... Go!");
let now = timestamp();
print(`Creating ${MAX} closures...`);
let list = [];
for i in 0..MAX {
list.push(|| i ** 2);
}
print(`Time = ${now.elapsed} seconds...`);
print(`Summing ${MAX} closures...`);
let sum = 0;
for f in list {
sum += f.call();
}
print(`Sum = ${sum} (should be ${CHECK})`);
print(`Finished. Total run time = ${now.elapsed} seconds.`);

View File

@ -1,11 +1,13 @@
let arr = [42, 123.456, "hello", true, 'x', 999, 1]; let arr = [42, 123.456, "hello", true, "hey", 'x', 999, 1, 2, 3, 4];
for item in arr { for item in arr {
switch item { switch item {
42 => print("The Answer!"), 42 => print("The Answer!"),
123.456 => print(`Floating point... ${item}`), 123.456 => print(`Floating point... ${item}`),
"hello" => print(`${item} world!`), "hello" => print(`${item} world!`),
999 => print(`A number: ${item}`), 999 => print(`Got 999: ${item}`),
_ => print(`Something else: <${item}>`) 0..100 if item % 2 == 0 => print(`A small even number: ${item}`),
0..100 => print(`A small odd number: ${item}`),
_ => print(`Something else: <${item}> is ${type_of(item)}`)
} }
} }

View File

@ -1,10 +1,10 @@
//! Module that defines the `call_fn` API of [`Engine`]. //! Module that defines the `call_fn` API of [`Engine`].
#![cfg(not(feature = "no_function"))] #![cfg(not(feature = "no_function"))]
use crate::engine::{EvalState, Imports}; use crate::engine::{EvalState, GlobalRuntimeState};
use crate::types::dynamic::Variant; use crate::types::dynamic::Variant;
use crate::{ use crate::{
Dynamic, Engine, EvalAltResult, FuncArgs, Position, RhaiResult, Scope, StaticVec, AST, Dynamic, Engine, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, AST, ERR,
}; };
use std::any::type_name; use std::any::type_name;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
@ -60,7 +60,7 @@ impl Engine {
ast: &AST, ast: &AST,
name: impl AsRef<str>, name: impl AsRef<str>,
args: impl FuncArgs, args: impl FuncArgs,
) -> Result<T, Box<EvalAltResult>> { ) -> RhaiResultOf<T> {
let mut arg_values = StaticVec::new_const(); let mut arg_values = StaticVec::new_const();
args.parse(&mut arg_values); args.parse(&mut arg_values);
@ -69,7 +69,7 @@ impl Engine {
let typ = self.map_type_name(result.type_name()); let typ = self.map_type_name(result.type_name());
result.try_cast().ok_or_else(|| { result.try_cast().ok_or_else(|| {
EvalAltResult::ErrorMismatchOutputType( ERR::ErrorMismatchOutputType(
self.map_type_name(type_name::<T>()).into(), self.map_type_name(type_name::<T>()).into(),
typ.into(), typ.into(),
Position::NONE, Position::NONE,
@ -90,10 +90,11 @@ impl Engine {
/// ///
/// This function is very low level. /// This function is very low level.
/// ///
/// ## Arguments /// # Arguments
/// ///
/// All the arguments are _consumed_, meaning that they're replaced by `()`. /// All the arguments are _consumed_, meaning that they're replaced by `()`.
/// This is to avoid unnecessarily cloning the arguments. /// This is to avoid unnecessarily cloning the arguments.
///
/// Do not use the arguments after this call. If they are needed afterwards, /// Do not use the arguments after this call. If they are needed afterwards,
/// clone them _before_ calling this function. /// clone them _before_ calling this function.
/// ///
@ -129,10 +130,10 @@ impl Engine {
/// let result = engine.call_fn_raw(&mut scope, &ast, true, true, "bar", None, [])?; /// let result = engine.call_fn_raw(&mut scope, &ast, true, true, "bar", None, [])?;
/// assert_eq!(result.cast::<i64>(), 21); /// assert_eq!(result.cast::<i64>(), 21);
/// ///
/// let mut value: Dynamic = 1_i64.into(); /// let mut value = 1_i64.into();
/// let result = engine.call_fn_raw(&mut scope, &ast, true, true, "action", Some(&mut value), [ 41_i64.into() ])?; /// let result = engine.call_fn_raw(&mut scope, &ast, true, true, "action", Some(&mut value), [ 41_i64.into() ])?;
/// // ^^^^^^^^^^^^^^^^ binding the 'this' pointer /// // ^^^^^^^^^^^^^^^^ binding the 'this' pointer
/// assert_eq!(value.as_int().expect("value should be INT"), 42); /// assert_eq!(value.as_int().unwrap(), 42);
/// ///
/// engine.call_fn_raw(&mut scope, &ast, true, false, "decl", None, [ 42_i64.into() ])?; /// engine.call_fn_raw(&mut scope, &ast, true, false, "decl", None, [ 42_i64.into() ])?;
/// // ^^^^^ do not rewind scope /// // ^^^^^ do not rewind scope
@ -153,14 +154,13 @@ impl Engine {
arg_values: impl AsMut<[Dynamic]>, arg_values: impl AsMut<[Dynamic]>,
) -> RhaiResult { ) -> RhaiResult {
let state = &mut EvalState::new(); let state = &mut EvalState::new();
let mods = &mut Imports::new(); let global = &mut GlobalRuntimeState::new();
let statements = ast.statements(); let statements = ast.statements();
let orig_scope_len = scope.len(); let orig_scope_len = scope.len();
if eval_ast && !statements.is_empty() { if eval_ast && !statements.is_empty() {
// Make sure new variables introduced at global level do not _spill_ into the function call self.eval_global_statements(scope, global, state, statements, &[ast.as_ref()], 0)?;
self.eval_global_statements(scope, mods, state, statements, &[ast.as_ref()], 0)?;
if rewind_scope { if rewind_scope {
scope.rewind(orig_scope_len); scope.rewind(orig_scope_len);
@ -175,7 +175,7 @@ impl Engine {
let fn_def = ast let fn_def = ast
.shared_lib() .shared_lib()
.get_script_fn(name, args.len()) .get_script_fn(name, args.len())
.ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::NONE))?; .ok_or_else(|| ERR::ErrorFunctionNotFound(name.into(), Position::NONE))?;
// Check for data race. // Check for data race.
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
@ -183,7 +183,7 @@ impl Engine {
let result = self.call_script_fn( let result = self.call_script_fn(
scope, scope,
mods, global,
state, state,
&[ast.as_ref()], &[ast.as_ref()],
&mut this_ptr, &mut this_ptr,

View File

@ -1,7 +1,7 @@
//! Module that defines the public compilation API of [`Engine`]. //! Module that defines the public compilation API of [`Engine`].
use crate::parser::ParseState; use crate::parser::{ParseResult, ParseState};
use crate::{Engine, ParseError, Scope, AST}; use crate::{Engine, Scope, AST};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -26,7 +26,7 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn compile(&self, script: impl AsRef<str>) -> Result<AST, ParseError> { pub fn compile(&self, script: impl AsRef<str>) -> ParseResult<AST> {
self.compile_with_scope(&Scope::new(), script) self.compile_with_scope(&Scope::new(), script)
} }
/// Compile a string into an [`AST`] using own scope, which can be used later for evaluation. /// Compile a string into an [`AST`] using own scope, which can be used later for evaluation.
@ -67,11 +67,7 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn compile_with_scope( pub fn compile_with_scope(&self, scope: &Scope, script: impl AsRef<str>) -> ParseResult<AST> {
&self,
scope: &Scope,
script: impl AsRef<str>,
) -> Result<AST, ParseError> {
self.compile_scripts_with_scope(scope, &[script]) self.compile_scripts_with_scope(scope, &[script])
} }
/// Compile a string into an [`AST`] using own scope, which can be used later for evaluation, /// Compile a string into an [`AST`] using own scope, which can be used later for evaluation,
@ -88,7 +84,7 @@ impl Engine {
&self, &self,
scope: &Scope, scope: &Scope,
script: impl AsRef<str>, script: impl AsRef<str>,
) -> Result<AST, Box<crate::EvalAltResult>> { ) -> crate::RhaiResultOf<AST> {
use crate::{ use crate::{
ast::{ASTNode, Expr, Stmt}, ast::{ASTNode, Expr, Stmt},
func::native::shared_take_or_clone, func::native::shared_take_or_clone,
@ -101,18 +97,16 @@ impl Engine {
resolver: &StaticModuleResolver, resolver: &StaticModuleResolver,
imports: &mut BTreeSet<crate::Identifier>, imports: &mut BTreeSet<crate::Identifier>,
) { ) {
ast.walk( ast.walk(&mut |path| match path.last().unwrap() {
&mut |path| match path.last().expect("contains current node") { // Collect all `import` statements with a string constant path
// Collect all `import` statements with a string constant path ASTNode::Stmt(Stmt::Import(Expr::StringConstant(s, _), _, _))
ASTNode::Stmt(Stmt::Import(Expr::StringConstant(s, _), _, _)) if !resolver.contains_path(s) && !imports.contains(s.as_str()) =>
if !resolver.contains_path(s) && !imports.contains(s.as_str()) => {
{ imports.insert(s.clone().into());
imports.insert(s.clone().into()); true
true }
} _ => true,
_ => true, });
},
);
} }
let mut ast = self.compile_scripts_with_scope(scope, &[script])?; let mut ast = self.compile_scripts_with_scope(scope, &[script])?;
@ -197,11 +191,11 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn compile_scripts_with_scope( pub fn compile_scripts_with_scope<S: AsRef<str>>(
&self, &self,
scope: &Scope, scope: &Scope,
scripts: &[impl AsRef<str>], scripts: impl AsRef<[S]>,
) -> Result<AST, ParseError> { ) -> ParseResult<AST> {
self.compile_with_scope_and_optimization_level( self.compile_with_scope_and_optimization_level(
scope, scope,
scripts, scripts,
@ -217,14 +211,16 @@ impl Engine {
/// throughout the script _including_ functions. This allows functions to be optimized based on /// throughout the script _including_ functions. This allows functions to be optimized based on
/// dynamic global constants. /// dynamic global constants.
#[inline] #[inline]
pub(crate) fn compile_with_scope_and_optimization_level( pub(crate) fn compile_with_scope_and_optimization_level<S: AsRef<str>>(
&self, &self,
scope: &Scope, scope: &Scope,
scripts: &[impl AsRef<str>], scripts: impl AsRef<[S]>,
#[cfg(not(feature = "no_optimize"))] optimization_level: crate::OptimizationLevel, #[cfg(not(feature = "no_optimize"))] optimization_level: crate::OptimizationLevel,
) -> Result<AST, ParseError> { ) -> ParseResult<AST> {
let (stream, tokenizer_control) = let (stream, tokenizer_control) = self.lex_raw(
self.lex_raw(scripts, self.token_mapper.as_ref().map(Box::as_ref)); scripts.as_ref(),
self.token_mapper.as_ref().map(Box::as_ref),
);
let mut state = ParseState::new(self, tokenizer_control); let mut state = ParseState::new(self, tokenizer_control);
self.parse( self.parse(
&mut stream.peekable(), &mut stream.peekable(),
@ -255,7 +251,7 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn compile_expression(&self, script: impl AsRef<str>) -> Result<AST, ParseError> { pub fn compile_expression(&self, script: impl AsRef<str>) -> ParseResult<AST> {
self.compile_expression_with_scope(&Scope::new(), script) self.compile_expression_with_scope(&Scope::new(), script)
} }
/// Compile a string containing an expression into an [`AST`] using own scope, /// Compile a string containing an expression into an [`AST`] using own scope,
@ -295,7 +291,7 @@ impl Engine {
&self, &self,
scope: &Scope, scope: &Scope,
script: impl AsRef<str>, script: impl AsRef<str>,
) -> Result<AST, ParseError> { ) -> ParseResult<AST> {
let scripts = [script]; let scripts = [script];
let (stream, tokenizer_control) = let (stream, tokenizer_control) =
self.lex_raw(&scripts, self.token_mapper.as_ref().map(Box::as_ref)); self.lex_raw(&scripts, self.token_mapper.as_ref().map(Box::as_ref));
@ -359,14 +355,14 @@ impl Engine {
&self, &self,
json: impl AsRef<str>, json: impl AsRef<str>,
has_null: bool, has_null: bool,
) -> Result<crate::Map, Box<crate::EvalAltResult>> { ) -> crate::RhaiResultOf<crate::Map> {
use crate::tokenizer::Token; use crate::tokenizer::Token;
fn parse_json_inner( fn parse_json_inner(
engine: &Engine, engine: &Engine,
json: &str, json: &str,
has_null: bool, has_null: bool,
) -> Result<crate::Map, Box<crate::EvalAltResult>> { ) -> crate::RhaiResultOf<crate::Map> {
let mut scope = Scope::new(); let mut scope = Scope::new();
let json_text = json.trim_start(); let json_text = json.trim_start();
let scripts = if json_text.starts_with(Token::MapStart.literal_syntax()) { let scripts = if json_text.starts_with(Token::MapStart.literal_syntax()) {
@ -374,7 +370,7 @@ impl Engine {
} else if json_text.starts_with(Token::LeftBrace.literal_syntax()) { } else if json_text.starts_with(Token::LeftBrace.literal_syntax()) {
["#", json_text] ["#", json_text]
} else { } else {
return Err(crate::ParseErrorType::MissingToken( return Err(crate::PERR::MissingToken(
Token::LeftBrace.syntax().into(), Token::LeftBrace.syntax().into(),
"to start a JSON object hash".into(), "to start a JSON object hash".into(),
) )

View File

@ -3,12 +3,12 @@
use crate::ast::Expr; use crate::ast::Expr;
use crate::engine::EvalContext; use crate::engine::EvalContext;
use crate::func::native::SendSync; use crate::func::native::SendSync;
use crate::parser::ParseResult;
use crate::r#unsafe::unsafe_try_cast; use crate::r#unsafe::unsafe_try_cast;
use crate::tokenizer::{is_valid_identifier, Token}; use crate::tokenizer::{is_valid_identifier, Token};
use crate::types::dynamic::Variant; use crate::types::dynamic::Variant;
use crate::{ use crate::{
Engine, Identifier, ImmutableString, LexError, ParseError, Position, RhaiResult, Shared, Engine, Identifier, ImmutableString, LexError, Position, RhaiResult, Shared, StaticVec, INT,
StaticVec, INT,
}; };
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -47,11 +47,11 @@ pub type FnCustomSyntaxEval = dyn Fn(&mut EvalContext, &[Expression]) -> RhaiRes
/// A general expression parsing trait object. /// A general expression parsing trait object.
#[cfg(not(feature = "sync"))] #[cfg(not(feature = "sync"))]
pub type FnCustomSyntaxParse = pub type FnCustomSyntaxParse =
dyn Fn(&[ImmutableString], &str) -> Result<Option<ImmutableString>, ParseError>; dyn Fn(&[ImmutableString], &str) -> ParseResult<Option<ImmutableString>>;
/// A general expression parsing trait object. /// A general expression parsing trait object.
#[cfg(feature = "sync")] #[cfg(feature = "sync")]
pub type FnCustomSyntaxParse = pub type FnCustomSyntaxParse =
dyn Fn(&[ImmutableString], &str) -> Result<Option<ImmutableString>, ParseError> + Send + Sync; dyn Fn(&[ImmutableString], &str) -> ParseResult<Option<ImmutableString>> + Send + Sync;
/// An expression sub-tree in an [`AST`][crate::AST]. /// An expression sub-tree in an [`AST`][crate::AST].
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -65,11 +65,17 @@ impl<'a> From<&'a Expr> for Expression<'a> {
} }
impl Expression<'_> { impl Expression<'_> {
/// If this expression is a variable name, return it. Otherwise [`None`]. /// Get the value of this expression if it is a variable name or a string constant.
///
/// Returns [`None`] also if the constant is not of the specified type.
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn get_variable_name(&self) -> Option<&str> { pub fn get_string_value(&self) -> Option<&str> {
self.0.get_variable_name(true) match self.0 {
Expr::Variable(_, _, x) if x.1.is_none() => Some(x.2.as_str()),
Expr::StringConstant(x, _) => Some(x.as_str()),
_ => None,
}
} }
/// Get the position of this expression. /// Get the position of this expression.
#[inline(always)] #[inline(always)]
@ -89,38 +95,39 @@ impl Expression<'_> {
if TypeId::of::<T>() == TypeId::of::<INT>() { if TypeId::of::<T>() == TypeId::of::<INT>() {
return match self.0 { return match self.0 {
Expr::IntegerConstant(x, _) => unsafe_try_cast(*x).ok(), Expr::IntegerConstant(x, _) => unsafe_try_cast(*x),
_ => None, _ => None,
}; };
} }
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
if TypeId::of::<T>() == TypeId::of::<crate::FLOAT>() { if TypeId::of::<T>() == TypeId::of::<crate::FLOAT>() {
return match self.0 { return match self.0 {
Expr::FloatConstant(x, _) => unsafe_try_cast(*x).ok(), Expr::FloatConstant(x, _) => unsafe_try_cast(*x),
_ => None, _ => None,
}; };
} }
if TypeId::of::<T>() == TypeId::of::<char>() { if TypeId::of::<T>() == TypeId::of::<char>() {
return match self.0 { return match self.0 {
Expr::CharConstant(x, _) => unsafe_try_cast(*x).ok(), Expr::CharConstant(x, _) => unsafe_try_cast(*x),
_ => None, _ => None,
}; };
} }
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() { if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
return match self.0 { return match self.0 {
Expr::StringConstant(x, _) => unsafe_try_cast(x.clone()).ok(), Expr::StringConstant(x, _) => unsafe_try_cast(x.clone()),
Expr::Variable(_, _, x) => unsafe_try_cast(Into::<ImmutableString>::into(&x.2)),
_ => None, _ => None,
}; };
} }
if TypeId::of::<T>() == TypeId::of::<bool>() { if TypeId::of::<T>() == TypeId::of::<bool>() {
return match self.0 { return match self.0 {
Expr::BoolConstant(x, _) => unsafe_try_cast(*x).ok(), Expr::BoolConstant(x, _) => unsafe_try_cast(*x),
_ => None, _ => None,
}; };
} }
if TypeId::of::<T>() == TypeId::of::<()>() { if TypeId::of::<T>() == TypeId::of::<()>() {
return match self.0 { return match self.0 {
Expr::Unit(_) => unsafe_try_cast(()).ok(), Expr::Unit(_) => unsafe_try_cast(()),
_ => None, _ => None,
}; };
} }
@ -154,7 +161,7 @@ impl EvalContext<'_, '_, '_, '_, '_, '_, '_, '_> {
pub fn eval_expression_tree(&mut self, expr: &Expression) -> RhaiResult { pub fn eval_expression_tree(&mut self, expr: &Expression) -> RhaiResult {
self.engine.eval_expr( self.engine.eval_expr(
self.scope, self.scope,
self.mods, self.global,
self.state, self.state,
self.lib, self.lib,
self.this_ptr, self.this_ptr,
@ -204,15 +211,15 @@ impl Engine {
/// does NOT count, so `false` should be passed. /// does NOT count, so `false` should be passed.
pub fn register_custom_syntax<S: AsRef<str> + Into<Identifier>>( pub fn register_custom_syntax<S: AsRef<str> + Into<Identifier>>(
&mut self, &mut self,
symbols: &[S], symbols: impl AsRef<[S]>,
scope_may_be_changed: bool, scope_may_be_changed: bool,
func: impl Fn(&mut EvalContext, &[Expression]) -> RhaiResult + SendSync + 'static, func: impl Fn(&mut EvalContext, &[Expression]) -> RhaiResult + SendSync + 'static,
) -> Result<&mut Self, ParseError> { ) -> ParseResult<&mut Self> {
use markers::*; use markers::*;
let mut segments = StaticVec::<ImmutableString>::new(); let mut segments = StaticVec::<ImmutableString>::new();
for s in symbols { for s in symbols.as_ref() {
let s = s.as_ref().trim(); let s = s.as_ref().trim();
// Skip empty symbols // Skip empty symbols
@ -344,11 +351,11 @@ impl Engine {
/// ///
/// * `Ok(None)`: parsing complete and there are no more symbols to match. /// * `Ok(None)`: parsing complete and there are no more symbols to match.
/// * `Ok(Some(symbol))`: the next symbol to match, which can also be `$expr$`, `$ident$` or `$block$`. /// * `Ok(Some(symbol))`: the next symbol to match, which can also be `$expr$`, `$ident$` or `$block$`.
/// * `Err(ParseError)`: error that is reflected back to the [`Engine`], normally `ParseError(ParseErrorType::BadInput(LexError::ImproperSymbol(message)), Position::NONE)` to indicate a syntax error, but it can be any [`ParseError`]. /// * `Err(ParseError)`: error that is reflected back to the [`Engine`], normally `ParseError(ParseErrorType::BadInput(LexError::ImproperSymbol(message)), Position::NONE)` to indicate a syntax error, but it can be any [`ParseError`][crate::ParseError].
pub fn register_custom_syntax_raw( pub fn register_custom_syntax_raw(
&mut self, &mut self,
key: impl Into<Identifier>, key: impl Into<Identifier>,
parse: impl Fn(&[ImmutableString], &str) -> Result<Option<ImmutableString>, ParseError> parse: impl Fn(&[ImmutableString], &str) -> ParseResult<Option<ImmutableString>>
+ SendSync + SendSync
+ 'static, + 'static,
scope_may_be_changed: bool, scope_may_be_changed: bool,

View File

@ -1,8 +1,8 @@
//! Module containing all deprecated API that will be removed in the next major version. //! Module containing all deprecated API that will be removed in the next major version.
use crate::{ use crate::{
Dynamic, Engine, EvalAltResult, FnPtr, ImmutableString, NativeCallContext, RhaiResult, Scope, Dynamic, Engine, EvalAltResult, Expression, FnPtr, ImmutableString, NativeCallContext,
AST, RhaiResult, RhaiResultOf, Scope, AST,
}; };
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -20,9 +20,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.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(any(target_arch = "wasm32", target_arch = "wasm64")))] #[cfg(not(target_arch = "wasm32"))]
#[cfg(not(target_arch = "wasm64"))]
#[inline(always)] #[inline(always)]
pub fn consume_file(&self, path: std::path::PathBuf) -> Result<(), Box<EvalAltResult>> { pub fn consume_file(&self, path: std::path::PathBuf) -> RhaiResultOf<()> {
self.run_file(path) self.run_file(path)
} }
@ -38,13 +39,14 @@ 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.1.0", note = "use `run_file_with_scope` instead")] #[deprecated(since = "1.1.0", note = "use `run_file_with_scope` instead")]
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #[cfg(not(target_arch = "wasm32"))]
#[cfg(not(target_arch = "wasm64"))]
#[inline(always)] #[inline(always)]
pub fn consume_file_with_scope( pub fn consume_file_with_scope(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
path: std::path::PathBuf, path: std::path::PathBuf,
) -> Result<(), Box<EvalAltResult>> { ) -> RhaiResultOf<()> {
self.run_file_with_scope(scope, path) self.run_file_with_scope(scope, path)
} }
@ -58,7 +60,7 @@ 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.1.0", note = "use `run` instead")] #[deprecated(since = "1.1.0", note = "use `run` instead")]
#[inline(always)] #[inline(always)]
pub fn consume(&self, script: &str) -> Result<(), Box<EvalAltResult>> { pub fn consume(&self, script: &str) -> RhaiResultOf<()> {
self.run(script) self.run(script)
} }
@ -72,11 +74,7 @@ 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.1.0", note = "use `run_with_scope` instead")] #[deprecated(since = "1.1.0", note = "use `run_with_scope` instead")]
#[inline(always)] #[inline(always)]
pub fn consume_with_scope( pub fn consume_with_scope(&self, scope: &mut Scope, script: &str) -> RhaiResultOf<()> {
&self,
scope: &mut Scope,
script: &str,
) -> Result<(), Box<EvalAltResult>> {
self.run_with_scope(scope, script) self.run_with_scope(scope, script)
} }
@ -90,7 +88,7 @@ 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.1.0", note = "use `run_ast` instead")] #[deprecated(since = "1.1.0", note = "use `run_ast` instead")]
#[inline(always)] #[inline(always)]
pub fn consume_ast(&self, ast: &AST) -> Result<(), Box<EvalAltResult>> { pub fn consume_ast(&self, ast: &AST) -> RhaiResultOf<()> {
self.run_ast(ast) self.run_ast(ast)
} }
@ -104,11 +102,7 @@ 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.1.0", note = "use `run_ast_with_scope` instead")] #[deprecated(since = "1.1.0", note = "use `run_ast_with_scope` instead")]
#[inline(always)] #[inline(always)]
pub fn consume_ast_with_scope( pub fn consume_ast_with_scope(&self, scope: &mut Scope, ast: &AST) -> RhaiResultOf<()> {
&self,
scope: &mut Scope,
ast: &AST,
) -> Result<(), Box<EvalAltResult>> {
self.run_ast_with_scope(scope, ast) self.run_ast_with_scope(scope, ast)
} }
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments /// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments
@ -128,10 +122,11 @@ impl Engine {
/// ///
/// This function is very low level. /// This function is very low level.
/// ///
/// ## Arguments /// # Arguments
/// ///
/// All the arguments are _consumed_, meaning that they're replaced by `()`. /// All the arguments are _consumed_, meaning that they're replaced by `()`.
/// This is to avoid unnecessarily cloning the arguments. /// This is to avoid unnecessarily cloning the arguments.
///
/// Do not use the arguments after this call. If they are needed afterwards, /// Do not use the arguments after this call. If they are needed afterwards,
/// clone them _before_ calling this function. /// clone them _before_ calling this function.
/// ///
@ -228,7 +223,7 @@ impl NativeCallContext<'_> {
/// ///
/// This function is very low level. /// This function is very low level.
/// ///
/// ## Arguments /// # Arguments
/// ///
/// All arguments may be _consumed_, meaning that they may be replaced by `()`. This is to avoid /// All arguments may be _consumed_, meaning that they may be replaced by `()`. This is to avoid
/// unnecessarily cloning the arguments. /// unnecessarily cloning the arguments.
@ -258,7 +253,7 @@ impl NativeCallContext<'_> {
#[allow(useless_deprecated)] #[allow(useless_deprecated)]
#[deprecated(since = "1.2.0", note = "explicitly wrap `EvalAltResult` in `Err`")] #[deprecated(since = "1.2.0", note = "explicitly wrap `EvalAltResult` in `Err`")]
impl<T> From<EvalAltResult> for Result<T, Box<EvalAltResult>> { impl<T> From<EvalAltResult> for RhaiResultOf<T> {
#[inline(always)] #[inline(always)]
fn from(err: EvalAltResult) -> Self { fn from(err: EvalAltResult) -> Self {
Err(err.into()) Err(err.into())
@ -284,10 +279,11 @@ impl FnPtr {
/// ///
/// This function is very low level. /// This function is very low level.
/// ///
/// ## Arguments /// # Arguments
/// ///
/// All the arguments are _consumed_, meaning that they're replaced by `()`. /// All the arguments are _consumed_, meaning that they're replaced by `()`.
/// This is to avoid unnecessarily cloning the arguments. /// This is to avoid unnecessarily cloning the arguments.
///
/// Do not use the arguments after this call. If they are needed afterwards, /// Do not use the arguments after this call. If they are needed afterwards,
/// clone them _before_ calling this function. /// clone them _before_ calling this function.
#[deprecated( #[deprecated(
@ -304,3 +300,19 @@ impl FnPtr {
self.call_raw(context, this_ptr, arg_values) self.call_raw(context, this_ptr, arg_values)
} }
} }
impl Expression<'_> {
/// If this expression is a variable name, return it. Otherwise [`None`].
///
/// # Deprecated
///
/// This method is deprecated. Use [`get_string_value`][Expression::get_string_value] instead.
///
/// This method will be removed in the next major version.
#[deprecated(since = "1.4.0", note = "use `get_string_value` instead")]
#[inline(always)]
#[must_use]
pub fn get_variable_name(&self) -> Option<&str> {
self.get_string_value()
}
}

View File

@ -1,9 +1,9 @@
//! Module that defines the public evaluation API of [`Engine`]. //! Module that defines the public evaluation API of [`Engine`].
use crate::engine::{EvalState, Imports}; use crate::engine::{EvalState, GlobalRuntimeState};
use crate::parser::ParseState; use crate::parser::ParseState;
use crate::types::dynamic::Variant; use crate::types::dynamic::Variant;
use crate::{Dynamic, Engine, EvalAltResult, Module, Position, RhaiResult, Scope, AST}; use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, AST, ERR};
use std::any::type_name; use std::any::type_name;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -24,7 +24,7 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn eval<T: Variant + Clone>(&self, script: &str) -> Result<T, Box<EvalAltResult>> { pub fn eval<T: Variant + Clone>(&self, script: &str) -> RhaiResultOf<T> {
self.eval_with_scope(&mut Scope::new(), script) self.eval_with_scope(&mut Scope::new(), script)
} }
/// Evaluate a string with own scope. /// Evaluate a string with own scope.
@ -60,7 +60,7 @@ impl Engine {
&self, &self,
scope: &mut Scope, scope: &mut Scope,
script: &str, script: &str,
) -> Result<T, Box<EvalAltResult>> { ) -> RhaiResultOf<T> {
let ast = self.compile_with_scope_and_optimization_level( let ast = self.compile_with_scope_and_optimization_level(
scope, scope,
&[script], &[script],
@ -84,10 +84,7 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn eval_expression<T: Variant + Clone>( pub fn eval_expression<T: Variant + Clone>(&self, script: &str) -> RhaiResultOf<T> {
&self,
script: &str,
) -> Result<T, Box<EvalAltResult>> {
self.eval_expression_with_scope(&mut Scope::new(), script) self.eval_expression_with_scope(&mut Scope::new(), script)
} }
/// Evaluate a string containing an expression with own scope. /// Evaluate a string containing an expression with own scope.
@ -113,7 +110,7 @@ impl Engine {
&self, &self,
scope: &mut Scope, scope: &mut Scope,
script: &str, script: &str,
) -> Result<T, Box<EvalAltResult>> { ) -> RhaiResultOf<T> {
let scripts = [script]; let scripts = [script];
let (stream, tokenizer_control) = let (stream, tokenizer_control) =
self.lex_raw(&scripts, self.token_mapper.as_ref().map(Box::as_ref)); self.lex_raw(&scripts, self.token_mapper.as_ref().map(Box::as_ref));
@ -149,7 +146,7 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn eval_ast<T: Variant + Clone>(&self, ast: &AST) -> Result<T, Box<EvalAltResult>> { pub fn eval_ast<T: Variant + Clone>(&self, ast: &AST) -> RhaiResultOf<T> {
self.eval_ast_with_scope(&mut Scope::new(), ast) self.eval_ast_with_scope(&mut Scope::new(), ast)
} }
/// Evaluate an [`AST`] with own scope. /// Evaluate an [`AST`] with own scope.
@ -186,15 +183,15 @@ impl Engine {
&self, &self,
scope: &mut Scope, scope: &mut Scope,
ast: &AST, ast: &AST,
) -> Result<T, Box<EvalAltResult>> { ) -> RhaiResultOf<T> {
let mods = &mut Imports::new(); let global = &mut GlobalRuntimeState::new();
let result = self.eval_ast_with_scope_raw(scope, mods, ast, 0)?; let result = self.eval_ast_with_scope_raw(scope, global, ast, 0)?;
let typ = self.map_type_name(result.type_name()); let typ = self.map_type_name(result.type_name());
result.try_cast::<T>().ok_or_else(|| { result.try_cast::<T>().ok_or_else(|| {
EvalAltResult::ErrorMismatchOutputType( ERR::ErrorMismatchOutputType(
self.map_type_name(type_name::<T>()).into(), self.map_type_name(type_name::<T>()).into(),
typ.into(), typ.into(),
Position::NONE, Position::NONE,
@ -207,17 +204,16 @@ impl Engine {
pub(crate) fn eval_ast_with_scope_raw<'a>( pub(crate) fn eval_ast_with_scope_raw<'a>(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
mods: &mut Imports, global: &mut GlobalRuntimeState,
ast: &'a AST, ast: &'a AST,
level: usize, level: usize,
) -> RhaiResult { ) -> RhaiResult {
let mut state = EvalState::new(); let mut state = EvalState::new();
if ast.source_raw().is_some() { global.source = ast.source_raw().clone();
mods.source = ast.source_raw().cloned();
}
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
{ {
mods.embedded_module_resolver = ast.resolver().cloned(); global.embedded_module_resolver = ast.resolver().cloned();
} }
let statements = ast.statements(); let statements = ast.statements();
@ -235,6 +231,6 @@ impl Engine {
} else { } else {
&lib &lib
}; };
self.eval_global_statements(scope, mods, &mut state, statements, lib, level) self.eval_global_statements(scope, global, &mut state, statements, lib, level)
} }
} }

View File

@ -2,7 +2,7 @@
use crate::engine::EvalContext; use crate::engine::EvalContext;
use crate::func::SendSync; use crate::func::SendSync;
use crate::{Dynamic, Engine, EvalAltResult, Position}; use crate::{Dynamic, Engine, Position, RhaiResultOf};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -58,7 +58,7 @@ impl Engine {
#[inline(always)] #[inline(always)]
pub fn on_var( pub fn on_var(
&mut self, &mut self,
callback: impl Fn(&str, usize, &EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>> callback: impl Fn(&str, usize, &EvalContext) -> RhaiResultOf<Option<Dynamic>>
+ SendSync + SendSync
+ 'static, + 'static,
) -> &mut Self { ) -> &mut Self {

View File

@ -1,19 +1,20 @@
//! Module that defines the public file-based API of [`Engine`]. //! Module that defines the public file-based API of [`Engine`].
#![cfg(not(feature = "no_std"))] #![cfg(not(feature = "no_std"))]
#![cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #![cfg(not(target_arch = "wasm32"))]
#![cfg(not(target_arch = "wasm64"))]
use crate::types::dynamic::Variant; use crate::types::dynamic::Variant;
use crate::{Engine, EvalAltResult, Scope, AST}; use crate::{Engine, RhaiResultOf, Scope, AST, ERR};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
impl Engine { impl Engine {
/// Read the contents of a file into a string. /// Read the contents of a file into a string.
fn read_file(path: std::path::PathBuf) -> Result<String, Box<EvalAltResult>> { fn read_file(path: std::path::PathBuf) -> RhaiResultOf<String> {
use std::io::Read; use std::io::Read;
let mut f = std::fs::File::open(path.clone()).map_err(|err| { let mut f = std::fs::File::open(path.clone()).map_err(|err| {
EvalAltResult::ErrorSystem( ERR::ErrorSystem(
format!("Cannot open script file '{}'", path.to_string_lossy()), format!("Cannot open script file '{}'", path.to_string_lossy()),
err.into(), err.into(),
) )
@ -22,7 +23,7 @@ impl Engine {
let mut contents = String::new(); let mut contents = String::new();
f.read_to_string(&mut contents).map_err(|err| { f.read_to_string(&mut contents).map_err(|err| {
EvalAltResult::ErrorSystem( ERR::ErrorSystem(
format!("Cannot read script file '{}'", path.to_string_lossy()), format!("Cannot read script file '{}'", path.to_string_lossy()),
err.into(), err.into(),
) )
@ -62,7 +63,7 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn compile_file(&self, path: std::path::PathBuf) -> Result<AST, Box<EvalAltResult>> { pub fn compile_file(&self, path: std::path::PathBuf) -> RhaiResultOf<AST> {
self.compile_file_with_scope(&Scope::new(), path) self.compile_file_with_scope(&Scope::new(), path)
} }
/// Compile a script file into an [`AST`] using own scope, which can be used later for evaluation. /// Compile a script file into an [`AST`] using own scope, which can be used later for evaluation.
@ -103,7 +104,7 @@ impl Engine {
&self, &self,
scope: &Scope, scope: &Scope,
path: std::path::PathBuf, path: std::path::PathBuf,
) -> Result<AST, Box<EvalAltResult>> { ) -> RhaiResultOf<AST> {
Self::read_file(path).and_then(|contents| Ok(self.compile_with_scope(scope, &contents)?)) Self::read_file(path).and_then(|contents| Ok(self.compile_with_scope(scope, &contents)?))
} }
/// Evaluate a script file. /// Evaluate a script file.
@ -124,10 +125,7 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[inline] #[inline]
pub fn eval_file<T: Variant + Clone>( pub fn eval_file<T: Variant + Clone>(&self, path: std::path::PathBuf) -> RhaiResultOf<T> {
&self,
path: std::path::PathBuf,
) -> Result<T, Box<EvalAltResult>> {
Self::read_file(path).and_then(|contents| self.eval::<T>(&contents)) Self::read_file(path).and_then(|contents| self.eval::<T>(&contents))
} }
/// Evaluate a script file with own scope. /// Evaluate a script file with own scope.
@ -162,16 +160,14 @@ impl Engine {
&self, &self,
scope: &mut Scope, scope: &mut Scope,
path: std::path::PathBuf, path: std::path::PathBuf,
) -> Result<T, Box<EvalAltResult>> { ) -> RhaiResultOf<T> {
Self::read_file(path).and_then(|contents| self.eval_with_scope(scope, &contents)) Self::read_file(path).and_then(|contents| self.eval_with_scope(scope, &contents))
} }
/// Evaluate a file, returning any error (if any). /// Evaluate a file, returning any error (if any).
/// ///
/// Not available under `no_std` or `WASM`. /// Not available under `no_std` or `WASM`.
#[cfg(not(feature = "no_std"))]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[inline] #[inline]
pub fn run_file(&self, path: std::path::PathBuf) -> Result<(), Box<EvalAltResult>> { pub fn run_file(&self, path: std::path::PathBuf) -> RhaiResultOf<()> {
Self::read_file(path).and_then(|contents| self.run(&contents)) Self::read_file(path).and_then(|contents| self.run(&contents))
} }
/// Evaluate a file with own scope, returning any error (if any). /// Evaluate a file with own scope, returning any error (if any).
@ -183,14 +179,12 @@ impl Engine {
/// If not [`OptimizationLevel::None`][crate::OptimizationLevel::None], constants defined within /// If not [`OptimizationLevel::None`][crate::OptimizationLevel::None], constants defined within
/// the scope are propagated throughout the script _including_ functions. This allows functions /// the scope are propagated throughout the script _including_ functions. This allows functions
/// to be optimized based on dynamic global constants. /// to be optimized based on dynamic global constants.
#[cfg(not(feature = "no_std"))]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[inline] #[inline]
pub fn run_file_with_scope( pub fn run_file_with_scope(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
path: std::path::PathBuf, path: std::path::PathBuf,
) -> Result<(), Box<EvalAltResult>> { ) -> RhaiResultOf<()> {
Self::read_file(path).and_then(|contents| self.run_with_scope(scope, &contents)) Self::read_file(path).and_then(|contents| self.run_with_scope(scope, &contents))
} }
} }

View File

@ -1,6 +1,7 @@
//! Settings for [`Engine`]'s limitations. //! Settings for [`Engine`]'s limitations.
#![cfg(not(feature = "unchecked"))] #![cfg(not(feature = "unchecked"))]
use super::default_limits;
use crate::Engine; use crate::Engine;
use std::num::{NonZeroU64, NonZeroUsize}; use std::num::{NonZeroU64, NonZeroUsize};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
@ -56,10 +57,10 @@ impl Limits {
pub const fn new() -> Self { pub const fn new() -> Self {
Self { Self {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
max_call_stack_depth: crate::engine::MAX_CALL_STACK_DEPTH, max_call_stack_depth: default_limits::MAX_CALL_STACK_DEPTH,
max_expr_depth: NonZeroUsize::new(crate::engine::MAX_EXPR_DEPTH), max_expr_depth: NonZeroUsize::new(default_limits::MAX_EXPR_DEPTH),
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
max_function_expr_depth: NonZeroUsize::new(crate::engine::MAX_FUNCTION_EXPR_DEPTH), max_function_expr_depth: NonZeroUsize::new(default_limits::MAX_FUNCTION_EXPR_DEPTH),
max_operations: None, max_operations: None,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
max_modules: usize::MAX, max_modules: usize::MAX,

View File

@ -18,6 +18,8 @@ pub mod limits;
pub mod events; pub mod events;
pub mod custom_syntax;
pub mod deprecated; pub mod deprecated;
use crate::engine::Precedence; use crate::engine::Precedence;
@ -27,6 +29,34 @@ use crate::{Engine, Identifier};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
pub mod default_limits {
#[cfg(not(feature = "unchecked"))]
#[cfg(debug_assertions)]
#[cfg(not(feature = "no_function"))]
pub const MAX_CALL_STACK_DEPTH: usize = 8;
#[cfg(not(feature = "unchecked"))]
#[cfg(debug_assertions)]
pub const MAX_EXPR_DEPTH: usize = 32;
#[cfg(not(feature = "unchecked"))]
#[cfg(not(feature = "no_function"))]
#[cfg(debug_assertions)]
pub const MAX_FUNCTION_EXPR_DEPTH: usize = 16;
#[cfg(not(feature = "unchecked"))]
#[cfg(not(debug_assertions))]
#[cfg(not(feature = "no_function"))]
pub const MAX_CALL_STACK_DEPTH: usize = 64;
#[cfg(not(feature = "unchecked"))]
#[cfg(not(debug_assertions))]
pub const MAX_EXPR_DEPTH: usize = 64;
#[cfg(not(feature = "unchecked"))]
#[cfg(not(feature = "no_function"))]
#[cfg(not(debug_assertions))]
pub const MAX_FUNCTION_EXPR_DEPTH: usize = 32;
pub const MAX_DYNAMIC_PARAMETERS: usize = 16;
}
/// Script optimization API. /// Script optimization API.
#[cfg(not(feature = "no_optimize"))] #[cfg(not(feature = "no_optimize"))]
impl Engine { impl Engine {
@ -83,17 +113,15 @@ impl Engine {
.map(|f| { .map(|f| {
f.func f.func
.get_script_fn_def() .get_script_fn_def()
.expect("scripted function") .expect("script-defined function")
.clone() .clone()
}) })
.collect(); .collect();
let statements = std::mem::take(ast.statements_mut());
crate::optimizer::optimize_into_ast( crate::optimizer::optimize_into_ast(
self, self,
scope, scope,
statements, ast.take_statements(),
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
lib, lib,
optimization_level, optimization_level,
@ -185,7 +213,7 @@ impl Engine {
/// ``` /// ```
pub fn register_custom_operator( pub fn register_custom_operator(
&mut self, &mut self,
keyword: impl AsRef<str> + Into<Identifier>, keyword: impl AsRef<str>,
precedence: u8, precedence: u8,
) -> Result<&mut Self, String> { ) -> Result<&mut Self, String> {
let precedence = Precedence::new(precedence); let precedence = Precedence::new(precedence);
@ -219,7 +247,8 @@ impl Engine {
} }
// Add to custom keywords // Add to custom keywords
self.custom_keywords.insert(keyword.into(), precedence); self.custom_keywords
.insert(keyword.as_ref().into(), precedence);
Ok(self) Ok(self)
} }

View File

@ -3,8 +3,7 @@
use crate::func::{FnCallArgs, RegisterNativeFunction, SendSync}; use crate::func::{FnCallArgs, RegisterNativeFunction, SendSync};
use crate::types::dynamic::Variant; use crate::types::dynamic::Variant;
use crate::{ use crate::{
Engine, EvalAltResult, FnAccess, FnNamespace, Identifier, Module, NativeCallContext, Shared, Engine, FnAccess, FnNamespace, Identifier, Module, NativeCallContext, RhaiResultOf, Shared,
SmartString,
}; };
use std::any::{type_name, TypeId}; use std::any::{type_name, TypeId};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
@ -15,13 +14,14 @@ impl Engine {
#[inline(always)] #[inline(always)]
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) fn global_namespace(&self) -> &Module { pub(crate) fn global_namespace(&self) -> &Module {
self.global_modules.first().expect("not empty") self.global_modules.first().unwrap()
} }
/// Get a mutable reference to the global namespace module /// Get a mutable reference to the global namespace module
/// (which is the first module in `global_modules`). /// (which is the first module in `global_modules`).
#[inline(always)] #[inline(always)]
pub(crate) fn global_namespace_mut(&mut self) -> &mut Module { pub(crate) fn global_namespace_mut(&mut self) -> &mut Module {
Shared::get_mut(self.global_modules.first_mut().expect("not empty")).expect("not shared") let module = self.global_modules.first_mut().unwrap();
Shared::get_mut(module).expect("not shared")
} }
/// Register a custom function with the [`Engine`]. /// Register a custom function with the [`Engine`].
/// ///
@ -69,18 +69,20 @@ impl Engine {
} }
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
let param_type_names: Option<crate::StaticVec<_>> = let param_type_names: crate::StaticVec<_> =
Some(param_type_names.iter().map(|ty| ty.as_str()).collect()); param_type_names.iter().map(|ty| ty.as_str()).collect();
#[cfg(feature = "metadata")]
let param_type_names = Some(param_type_names.as_ref());
#[cfg(not(feature = "metadata"))] #[cfg(not(feature = "metadata"))]
let param_type_names: Option<[&str; 0]> = None; let param_type_names: Option<&[&str]> = None;
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.as_ref().map(|v| v.as_ref()), param_type_names,
&param_types, param_types,
func.into_callable_function(), func.into_callable_function(),
); );
self self
@ -113,7 +115,7 @@ impl Engine {
pub fn register_result_fn<N, A, F, R>(&mut self, name: N, func: F) -> &mut Self pub fn register_result_fn<N, A, F, R>(&mut self, name: N, func: F) -> &mut Self
where where
N: AsRef<str> + Into<Identifier>, N: AsRef<str> + Into<Identifier>,
F: RegisterNativeFunction<A, Result<R, Box<EvalAltResult>>>, F: RegisterNativeFunction<A, RhaiResultOf<R>>,
{ {
let param_types = F::param_types(); let param_types = F::param_types();
@ -127,18 +129,20 @@ impl Engine {
.collect(); .collect();
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
let param_type_names: Option<crate::StaticVec<_>> = let param_type_names: crate::StaticVec<_> =
Some(param_type_names.iter().map(|ty| ty.as_str()).collect()); param_type_names.iter().map(|ty| ty.as_str()).collect();
#[cfg(feature = "metadata")]
let param_type_names = Some(param_type_names.as_ref());
#[cfg(not(feature = "metadata"))] #[cfg(not(feature = "metadata"))]
let param_type_names: Option<[&str; 0]> = None; let param_type_names: Option<&[&str]> = None;
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.as_ref().map(|v| v.as_ref()), param_type_names,
&param_types, param_types,
func.into_callable_function(), func.into_callable_function(),
); );
self self
@ -150,7 +154,7 @@ impl Engine {
/// This function is very low level. It takes a list of [`TypeId`][std::any::TypeId]'s /// This function is very low level. It takes a list of [`TypeId`][std::any::TypeId]'s
/// indicating the actual types of the parameters. /// indicating the actual types of the parameters.
/// ///
/// ## Arguments /// # Arguments
/// ///
/// Arguments are simply passed in as a mutable array of [`&mut Dynamic`][crate::Dynamic]. /// Arguments are simply passed in as a mutable array of [`&mut Dynamic`][crate::Dynamic].
/// The arguments are guaranteed to be of the correct types matching the [`TypeId`][std::any::TypeId]'s. /// The arguments are guaranteed to be of the correct types matching the [`TypeId`][std::any::TypeId]'s.
@ -165,10 +169,8 @@ impl Engine {
pub fn register_raw_fn<N, T>( pub fn register_raw_fn<N, T>(
&mut self, &mut self,
name: N, name: N,
arg_types: &[TypeId], arg_types: impl AsRef<[TypeId]>,
func: impl Fn(NativeCallContext, &mut FnCallArgs) -> Result<T, Box<EvalAltResult>> func: impl Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResultOf<T> + SendSync + 'static,
+ SendSync
+ 'static,
) -> &mut Self ) -> &mut Self
where where
N: AsRef<str> + Into<Identifier>, N: AsRef<str> + Into<Identifier>,
@ -195,8 +197,12 @@ impl Engine {
/// } /// }
/// ///
/// impl TestStruct { /// impl TestStruct {
/// fn new() -> Self { Self { field: 1 } } /// fn new() -> Self {
/// fn update(&mut self, offset: i64) { self.field += offset; } /// Self { field: 1 }
/// }
/// fn update(&mut self, offset: i64) {
/// self.field += offset;
/// }
/// } /// }
/// ///
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> { /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
@ -235,7 +241,9 @@ impl Engine {
/// } /// }
/// ///
/// impl TestStruct { /// impl TestStruct {
/// fn new() -> Self { Self { field: 1 } } /// fn new() -> Self {
/// Self { field: 1 }
/// }
/// } /// }
/// ///
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> { /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
@ -276,8 +284,8 @@ impl Engine {
#[inline(always)] #[inline(always)]
pub fn register_type_with_name_raw( pub fn register_type_with_name_raw(
&mut self, &mut self,
fully_qualified_type_path: impl Into<SmartString>, fully_qualified_type_path: impl Into<Identifier>,
name: impl Into<SmartString>, name: impl Into<Identifier>,
) -> &mut Self { ) -> &mut Self {
// Add the pretty-print type name into the map // Add the pretty-print type name into the map
self.type_names self.type_names
@ -310,9 +318,13 @@ impl Engine {
/// } /// }
/// ///
/// impl TestStruct { /// impl TestStruct {
/// fn new() -> Self { Self { field: 1 } } /// fn new() -> Self {
/// Self { field: 1 }
/// }
/// // Even a getter must start with `&mut self` and not `&self`. /// // Even a getter must start with `&mut self` and not `&self`.
/// fn get_field(&mut self) -> i64 { self.field } /// fn get_field(&mut self) -> i64 {
/// self.field
/// }
/// } /// }
/// ///
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> { /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
@ -338,7 +350,7 @@ impl Engine {
name: impl AsRef<str>, name: impl AsRef<str>,
get_fn: impl Fn(&mut T) -> V + SendSync + 'static, get_fn: impl Fn(&mut T) -> V + SendSync + 'static,
) -> &mut Self { ) -> &mut Self {
self.register_fn(&crate::engine::make_getter(name), get_fn) self.register_fn(crate::engine::make_getter(name.as_ref()).as_str(), get_fn)
} }
/// Register a getter function for a member of a registered type with the [`Engine`]. /// Register a getter function for a member of a registered type with the [`Engine`].
/// ///
@ -357,7 +369,9 @@ impl Engine {
/// } /// }
/// ///
/// impl TestStruct { /// impl TestStruct {
/// fn new() -> Self { Self { field: 1 } } /// fn new() -> Self {
/// Self { field: 1 }
/// }
/// // Even a getter must start with `&mut self` and not `&self`. /// // Even a getter must start with `&mut self` and not `&self`.
/// fn get_field(&mut self) -> Result<i64, Box<EvalAltResult>> { /// fn get_field(&mut self) -> Result<i64, Box<EvalAltResult>> {
/// Ok(self.field) /// Ok(self.field)
@ -383,9 +397,9 @@ impl Engine {
pub fn register_get_result<T: Variant + Clone, V: Variant + Clone>( pub fn register_get_result<T: Variant + Clone, V: Variant + Clone>(
&mut self, &mut self,
name: impl AsRef<str>, name: impl AsRef<str>,
get_fn: impl Fn(&mut T) -> Result<V, Box<EvalAltResult>> + SendSync + 'static, get_fn: impl Fn(&mut T) -> RhaiResultOf<V> + SendSync + 'static,
) -> &mut Self { ) -> &mut Self {
self.register_result_fn(&crate::engine::make_getter(name), get_fn) self.register_result_fn(crate::engine::make_getter(name.as_ref()).as_str(), get_fn)
} }
/// Register a setter function for a member of a registered type with the [`Engine`]. /// Register a setter function for a member of a registered type with the [`Engine`].
/// ///
@ -400,8 +414,12 @@ impl Engine {
/// } /// }
/// ///
/// impl TestStruct { /// impl TestStruct {
/// fn new() -> Self { Self { field: 1 } } /// fn new() -> Self {
/// fn set_field(&mut self, new_val: i64) { self.field = new_val; } /// Self { field: 1 }
/// }
/// fn set_field(&mut self, new_val: i64) {
/// self.field = new_val;
/// }
/// } /// }
/// ///
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> { /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
@ -431,7 +449,7 @@ impl Engine {
name: impl AsRef<str>, name: impl AsRef<str>,
set_fn: impl Fn(&mut T, V) + SendSync + 'static, set_fn: impl Fn(&mut T, V) + SendSync + 'static,
) -> &mut Self { ) -> &mut Self {
self.register_fn(&crate::engine::make_setter(name), set_fn) self.register_fn(crate::engine::make_setter(name.as_ref()).as_str(), set_fn)
} }
/// Register a setter function for a member of a registered type with the [`Engine`]. /// Register a setter function for a member of a registered type with the [`Engine`].
/// ///
@ -448,8 +466,10 @@ impl Engine {
/// } /// }
/// ///
/// impl TestStruct { /// impl TestStruct {
/// fn new() -> Self { Self { field: 1 } } /// fn new() -> Self {
/// fn set_field(&mut self, new_val: i64) -> Result<(), Box<EvalAltResult>> { /// Self { field: 1 }
/// }
/// fn set_field(&mut self, new_val: i64) -> Result<(), Box<rhai::EvalAltResult>> {
/// self.field = new_val; /// self.field = new_val;
/// Ok(()) /// Ok(())
/// } /// }
@ -478,9 +498,9 @@ impl Engine {
pub fn register_set_result<T: Variant + Clone, V: Variant + Clone>( pub fn register_set_result<T: Variant + Clone, V: Variant + Clone>(
&mut self, &mut self,
name: impl AsRef<str>, name: impl AsRef<str>,
set_fn: impl Fn(&mut T, V) -> Result<(), Box<EvalAltResult>> + SendSync + 'static, set_fn: impl Fn(&mut T, V) -> RhaiResultOf<()> + SendSync + 'static,
) -> &mut Self { ) -> &mut Self {
self.register_result_fn(&crate::engine::make_setter(name), set_fn) self.register_result_fn(crate::engine::make_setter(name.as_ref()).as_str(), set_fn)
} }
/// Short-hand for registering both getter and setter functions /// Short-hand for registering both getter and setter functions
/// of a registered type with the [`Engine`]. /// of a registered type with the [`Engine`].
@ -498,10 +518,16 @@ impl Engine {
/// } /// }
/// ///
/// impl TestStruct { /// impl TestStruct {
/// fn new() -> Self { Self { field: 1 } } /// fn new() -> Self {
/// Self { field: 1 }
/// }
/// // Even a getter must start with `&mut self` and not `&self`. /// // Even a getter must start with `&mut self` and not `&self`.
/// fn get_field(&mut self) -> i64 { self.field } /// fn get_field(&mut self) -> i64 {
/// fn set_field(&mut self, new_val: i64) { self.field = new_val; } /// self.field
/// }
/// fn set_field(&mut self, new_val: i64) {
/// self.field = new_val;
/// }
/// } /// }
/// ///
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> { /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
@ -552,9 +578,13 @@ impl Engine {
/// } /// }
/// ///
/// impl TestStruct { /// impl TestStruct {
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } } /// fn new() -> Self {
/// Self { fields: vec![1, 2, 3, 4, 5] }
/// }
/// // Even a getter must start with `&mut self` and not `&self`. /// // Even a getter must start with `&mut self` and not `&self`.
/// fn get_field(&mut self, index: i64) -> i64 { self.fields[index as usize] } /// fn get_field(&mut self, index: i64) -> i64 {
/// self.fields[index as usize]
/// }
/// } /// }
/// ///
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> { /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
@ -625,7 +655,9 @@ impl Engine {
/// } /// }
/// ///
/// impl TestStruct { /// impl TestStruct {
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } } /// fn new() -> Self {
/// Self { fields: vec![1, 2, 3, 4, 5] }
/// }
/// // Even a getter must start with `&mut self` and not `&self`. /// // Even a getter must start with `&mut self` and not `&self`.
/// fn get_field(&mut self, index: i64) -> Result<i64, Box<EvalAltResult>> { /// fn get_field(&mut self, index: i64) -> Result<i64, Box<EvalAltResult>> {
/// Ok(self.fields[index as usize]) /// Ok(self.fields[index as usize])
@ -657,7 +689,7 @@ impl Engine {
V: Variant + Clone, V: Variant + Clone,
>( >(
&mut self, &mut self,
get_fn: impl Fn(&mut T, X) -> Result<V, Box<EvalAltResult>> + SendSync + 'static, get_fn: impl Fn(&mut T, X) -> RhaiResultOf<V> + 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>() {
@ -698,8 +730,12 @@ impl Engine {
/// } /// }
/// ///
/// impl TestStruct { /// impl TestStruct {
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } } /// fn new() -> Self {
/// fn set_field(&mut self, index: i64, value: i64) { self.fields[index as usize] = value; } /// Self { fields: vec![1, 2, 3, 4, 5] }
/// }
/// fn set_field(&mut self, index: i64, value: i64) {
/// self.fields[index as usize] = value;
/// }
/// } /// }
/// ///
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> { /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
@ -717,10 +753,10 @@ impl Engine {
/// .register_indexer_set(TestStruct::set_field); /// .register_indexer_set(TestStruct::set_field);
/// ///
/// # #[cfg(not(feature = "no_index"))] /// # #[cfg(not(feature = "no_index"))]
/// assert_eq!( /// let result = engine.eval::<TestStruct>("let a = new_ts(); a[2] = 42; a")?;
/// engine.eval::<TestStruct>("let a = new_ts(); a[2] = 42; a")?.fields[2], ///
/// 42 /// # #[cfg(not(feature = "no_index"))]
/// ); /// assert_eq!(result.fields[2], 42);
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
@ -771,8 +807,10 @@ impl Engine {
/// } /// }
/// ///
/// impl TestStruct { /// impl TestStruct {
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } } /// fn new() -> Self {
/// fn set_field(&mut self, index: i64, value: i64) -> Result<(), Box<EvalAltResult>> { /// Self { fields: vec![1, 2, 3, 4, 5] }
/// }
/// fn set_field(&mut self, index: i64, value: i64) -> Result<(), Box<rhai::EvalAltResult>> {
/// self.fields[index as usize] = value; /// self.fields[index as usize] = value;
/// Ok(()) /// Ok(())
/// } /// }
@ -791,10 +829,10 @@ impl Engine {
/// .register_indexer_set_result(TestStruct::set_field); /// .register_indexer_set_result(TestStruct::set_field);
/// ///
/// # #[cfg(not(feature = "no_index"))] /// # #[cfg(not(feature = "no_index"))]
/// assert_eq!( /// let result = engine.eval::<TestStruct>("let a = new_ts(); a[2] = 42; a")?;
/// engine.eval::<TestStruct>("let a = new_ts(); a[2] = 42; a")?.fields[2], ///
/// 42 /// # #[cfg(not(feature = "no_index"))]
/// ); /// assert_eq!(result.fields[2], 42);
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
@ -806,7 +844,7 @@ impl Engine {
V: Variant + Clone, V: Variant + Clone,
>( >(
&mut self, &mut self,
set_fn: impl Fn(&mut T, X, V) -> Result<(), Box<EvalAltResult>> + SendSync + 'static, set_fn: impl Fn(&mut T, X, V) -> RhaiResultOf<()> + 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>() {
@ -847,10 +885,16 @@ impl Engine {
/// } /// }
/// ///
/// impl TestStruct { /// impl TestStruct {
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } } /// fn new() -> Self {
/// Self { fields: vec![1, 2, 3, 4, 5] }
/// }
/// // Even a getter must start with `&mut self` and not `&self`. /// // Even a getter must start with `&mut self` and not `&self`.
/// fn get_field(&mut self, index: i64) -> i64 { self.fields[index as usize] } /// fn get_field(&mut self, index: i64) -> i64 {
/// fn set_field(&mut self, index: i64, value: i64) { self.fields[index as usize] = value; } /// self.fields[index as usize]
/// }
/// fn set_field(&mut self, index: i64, value: i64) {
/// self.fields[index as usize] = value;
/// }
/// } /// }
/// ///
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> { /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
@ -935,17 +979,17 @@ impl Engine {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
pub fn register_static_module( pub fn register_static_module(
&mut self, &mut self,
name: impl AsRef<str> + Into<Identifier>, name: impl AsRef<str>,
module: Shared<Module>, module: Shared<Module>,
) -> &mut Self { ) -> &mut Self {
fn register_static_module_raw( fn register_static_module_raw(
root: &mut std::collections::BTreeMap<Identifier, Shared<Module>>, root: &mut std::collections::BTreeMap<Identifier, Shared<Module>>,
name: impl AsRef<str> + Into<Identifier>, name: &str,
module: Shared<Module>, module: Shared<Module>,
) { ) {
let separator = crate::tokenizer::Token::DoubleColon.syntax(); let separator = crate::tokenizer::Token::DoubleColon.syntax();
if !name.as_ref().contains(separator.as_ref()) { if !name.contains(separator.as_ref()) {
if !module.is_indexed() { if !module.is_indexed() {
// Index the module (making a clone copy if necessary) if it is not indexed // Index the module (making a clone copy if necessary) if it is not indexed
let mut module = crate::func::native::shared_take_or_clone(module); let mut module = crate::func::native::shared_take_or_clone(module);
@ -955,7 +999,7 @@ impl Engine {
root.insert(name.into(), module); root.insert(name.into(), module);
} }
} else { } else {
let mut iter = name.as_ref().splitn(2, separator.as_ref()); let mut iter = name.splitn(2, separator.as_ref());
let sub_module = iter.next().expect("contains separator").trim(); let sub_module = iter.next().expect("contains separator").trim();
let remainder = iter.next().expect("contains separator").trim(); let remainder = iter.next().expect("contains separator").trim();
@ -974,7 +1018,7 @@ impl Engine {
} }
} }
register_static_module_raw(&mut self.global_sub_modules, name, module); register_static_module_raw(&mut self.global_sub_modules, name.as_ref(), module);
self self
} }
/// _(metadata)_ Generate a list of all registered functions. /// _(metadata)_ Generate a list of all registered functions.

View File

@ -1,15 +1,15 @@
//! Module that defines the public evaluation API of [`Engine`]. //! Module that defines the public evaluation API of [`Engine`].
use crate::engine::{EvalState, Imports}; use crate::engine::{EvalState, GlobalRuntimeState};
use crate::parser::ParseState; use crate::parser::ParseState;
use crate::{Engine, EvalAltResult, Module, Scope, AST}; use crate::{Engine, Module, RhaiResultOf, Scope, AST};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
impl Engine { impl Engine {
/// Evaluate a script, returning any error (if any). /// Evaluate a script, returning any error (if any).
#[inline(always)] #[inline(always)]
pub fn run(&self, script: &str) -> Result<(), Box<EvalAltResult>> { pub fn run(&self, script: &str) -> RhaiResultOf<()> {
self.run_with_scope(&mut Scope::new(), script) self.run_with_scope(&mut Scope::new(), script)
} }
/// Evaluate a script with own scope, returning any error (if any). /// Evaluate a script with own scope, returning any error (if any).
@ -20,11 +20,7 @@ impl Engine {
/// the scope are propagated throughout the script _including_ functions. This allows functions /// the scope are propagated throughout the script _including_ functions. This allows functions
/// to be optimized based on dynamic global constants. /// to be optimized based on dynamic global constants.
#[inline] #[inline]
pub fn run_with_scope( pub fn run_with_scope(&self, scope: &mut Scope, script: &str) -> RhaiResultOf<()> {
&self,
scope: &mut Scope,
script: &str,
) -> Result<(), Box<EvalAltResult>> {
let scripts = [script]; let scripts = [script];
let (stream, tokenizer_control) = let (stream, tokenizer_control) =
self.lex_raw(&scripts, self.token_mapper.as_ref().map(Box::as_ref)); self.lex_raw(&scripts, self.token_mapper.as_ref().map(Box::as_ref));
@ -42,24 +38,19 @@ impl Engine {
} }
/// Evaluate an [`AST`], returning any error (if any). /// Evaluate an [`AST`], returning any error (if any).
#[inline(always)] #[inline(always)]
pub fn run_ast(&self, ast: &AST) -> Result<(), Box<EvalAltResult>> { pub fn run_ast(&self, ast: &AST) -> RhaiResultOf<()> {
self.run_ast_with_scope(&mut Scope::new(), ast) self.run_ast_with_scope(&mut Scope::new(), ast)
} }
/// Evaluate an [`AST`] with own scope, returning any error (if any). /// Evaluate an [`AST`] with own scope, returning any error (if any).
#[inline] #[inline]
pub fn run_ast_with_scope( pub fn run_ast_with_scope(&self, scope: &mut Scope, ast: &AST) -> RhaiResultOf<()> {
&self, let global = &mut GlobalRuntimeState::new();
scope: &mut Scope,
ast: &AST,
) -> Result<(), Box<EvalAltResult>> {
let mods = &mut Imports::new();
let mut state = EvalState::new(); let mut state = EvalState::new();
if ast.source_raw().is_some() { global.source = ast.source_raw().clone();
mods.source = ast.source_raw().cloned();
}
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
{ {
mods.embedded_module_resolver = ast.resolver().cloned(); global.embedded_module_resolver = ast.resolver().cloned();
} }
let statements = ast.statements(); let statements = ast.statements();
@ -73,7 +64,7 @@ impl Engine {
} else { } else {
&lib &lib
}; };
self.eval_global_statements(scope, mods, &mut state, statements, lib, 0)?; self.eval_global_statements(scope, global, &mut state, statements, lib, 0)?;
} }
Ok(()) Ok(())
} }

2585
src/ast.rs

File diff suppressed because it is too large Load Diff

863
src/ast/ast.rs Normal file
View File

@ -0,0 +1,863 @@
//! Module defining the AST (abstract syntax tree).
use super::{Expr, FnAccess, Stmt, StmtBlock, AST_OPTION_FLAGS};
use crate::{Dynamic, FnNamespace, Identifier, Position, StaticVec};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{
hash::Hash,
ops::{Add, AddAssign},
};
/// Compiled AST (abstract syntax tree) of a Rhai script.
///
/// # Thread Safety
///
/// Currently, [`AST`] is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`.
#[derive(Debug, Clone)]
pub struct AST {
/// Source of the [`AST`].
/// No source if string is empty.
source: Identifier,
/// Global statements.
body: StmtBlock,
/// Script-defined functions.
#[cfg(not(feature = "no_function"))]
functions: crate::Shared<crate::Module>,
/// Embedded module resolver, if any.
#[cfg(not(feature = "no_module"))]
resolver: Option<crate::Shared<crate::module::resolvers::StaticModuleResolver>>,
}
impl Default for AST {
#[inline(always)]
fn default() -> Self {
Self::empty()
}
}
impl AST {
/// Create a new [`AST`].
#[cfg(not(feature = "internals"))]
#[inline(always)]
#[must_use]
pub(crate) fn new(
statements: impl IntoIterator<Item = Stmt>,
#[cfg(not(feature = "no_function"))] functions: impl Into<crate::Shared<crate::Module>>,
) -> Self {
Self {
source: Identifier::new_const(),
body: StmtBlock::new(statements, Position::NONE),
#[cfg(not(feature = "no_function"))]
functions: functions.into(),
#[cfg(not(feature = "no_module"))]
resolver: None,
}
}
/// _(internals)_ Create a new [`AST`].
/// Exported under the `internals` feature only.
#[cfg(feature = "internals")]
#[inline(always)]
#[must_use]
pub fn new(
statements: impl IntoIterator<Item = Stmt>,
#[cfg(not(feature = "no_function"))] functions: impl Into<crate::Shared<crate::Module>>,
) -> Self {
Self {
source: Identifier::new_const(),
body: StmtBlock::new(statements, Position::NONE),
#[cfg(not(feature = "no_function"))]
functions: functions.into(),
#[cfg(not(feature = "no_module"))]
resolver: None,
}
}
/// Create a new [`AST`] with a source name.
#[cfg(not(feature = "internals"))]
#[inline(always)]
#[must_use]
pub(crate) fn new_with_source(
statements: impl IntoIterator<Item = Stmt>,
#[cfg(not(feature = "no_function"))] functions: impl Into<crate::Shared<crate::Module>>,
source: impl Into<Identifier>,
) -> Self {
let mut ast = Self::new(
statements,
#[cfg(not(feature = "no_function"))]
functions,
);
ast.set_source(source);
ast
}
/// _(internals)_ Create a new [`AST`] with a source name.
/// Exported under the `internals` feature only.
#[cfg(feature = "internals")]
#[inline(always)]
#[must_use]
pub fn new_with_source(
statements: impl IntoIterator<Item = Stmt>,
#[cfg(not(feature = "no_function"))] functions: impl Into<crate::Shared<crate::Module>>,
source: impl Into<Identifier>,
) -> Self {
let mut ast = Self::new(
statements,
#[cfg(not(feature = "no_function"))]
functions,
);
ast.set_source(source);
ast
}
/// Create an empty [`AST`].
#[inline]
#[must_use]
pub fn empty() -> Self {
Self {
source: Identifier::new_const(),
body: StmtBlock::NONE,
#[cfg(not(feature = "no_function"))]
functions: crate::Module::new().into(),
#[cfg(not(feature = "no_module"))]
resolver: None,
}
}
/// Get the source, if any.
#[inline(always)]
#[must_use]
pub fn source(&self) -> Option<&str> {
match self.source.as_str() {
"" => None,
s => Some(s),
}
}
/// Get a reference to the source.
#[inline(always)]
#[must_use]
pub(crate) fn source_raw(&self) -> &Identifier {
&self.source
}
/// Set the source.
#[inline]
pub fn set_source(&mut self, source: impl Into<Identifier>) -> &mut Self {
let source = source.into();
#[cfg(not(feature = "no_function"))]
crate::Shared::get_mut(&mut self.functions)
.as_mut()
.map(|m| m.set_id(source.clone()));
self.source = source;
self
}
/// Clear the source.
#[inline(always)]
pub fn clear_source(&mut self) -> &mut Self {
self.source.clear();
self
}
/// Get the statements.
#[cfg(not(feature = "internals"))]
#[inline(always)]
#[must_use]
pub(crate) fn statements(&self) -> &[Stmt] {
self.body.statements()
}
/// _(internals)_ Get the statements.
/// Exported under the `internals` feature only.
#[cfg(feature = "internals")]
#[inline(always)]
#[must_use]
pub fn statements(&self) -> &[Stmt] {
self.body.statements()
}
/// Extract the statements.
#[allow(dead_code)]
#[inline(always)]
#[must_use]
pub(crate) fn take_statements(&mut self) -> StaticVec<Stmt> {
self.body.take_statements()
}
/// Does this [`AST`] contain script-defined functions?
///
/// Not available under `no_function`.
#[cfg(not(feature = "no_function"))]
#[inline(always)]
#[must_use]
pub fn has_functions(&self) -> bool {
!self.functions.is_empty()
}
/// Get the internal shared [`Module`][crate::Module] containing all script-defined functions.
#[cfg(not(feature = "internals"))]
#[cfg(not(feature = "no_function"))]
#[inline(always)]
#[must_use]
pub(crate) fn shared_lib(&self) -> &crate::Shared<crate::Module> {
&self.functions
}
/// _(internals)_ Get the internal shared [`Module`][crate::Module] containing all script-defined functions.
/// Exported under the `internals` feature only.
///
/// Not available under `no_function`.
#[cfg(feature = "internals")]
#[cfg(not(feature = "no_function"))]
#[inline(always)]
#[must_use]
pub fn shared_lib(&self) -> &crate::Shared<crate::Module> {
&self.functions
}
/// Get the embedded [module resolver][`ModuleResolver`].
#[cfg(not(feature = "internals"))]
#[cfg(not(feature = "no_module"))]
#[inline(always)]
#[must_use]
pub(crate) fn resolver(
&self,
) -> Option<&crate::Shared<crate::module::resolvers::StaticModuleResolver>> {
self.resolver.as_ref()
}
/// _(internals)_ Get the embedded [module resolver][crate::ModuleResolver].
/// Exported under the `internals` feature only.
///
/// Not available under `no_module`.
#[cfg(feature = "internals")]
#[cfg(not(feature = "no_module"))]
#[inline(always)]
#[must_use]
pub fn resolver(
&self,
) -> Option<&crate::Shared<crate::module::resolvers::StaticModuleResolver>> {
self.resolver.as_ref()
}
/// Set the embedded [module resolver][`ModuleResolver`].
#[cfg(not(feature = "no_module"))]
#[inline(always)]
pub(crate) fn set_resolver(
&mut self,
resolver: impl Into<crate::Shared<crate::module::resolvers::StaticModuleResolver>>,
) -> &mut Self {
self.resolver = Some(resolver.into());
self
}
/// Clone the [`AST`]'s functions into a new [`AST`].
/// No statements are cloned.
///
/// Not available under `no_function`.
///
/// This operation is cheap because functions are shared.
#[cfg(not(feature = "no_function"))]
#[inline(always)]
#[must_use]
pub fn clone_functions_only(&self) -> Self {
self.clone_functions_only_filtered(|_, _, _, _, _| true)
}
/// Clone the [`AST`]'s functions into a new [`AST`] based on a filter predicate.
/// No statements are cloned.
///
/// Not available under `no_function`.
///
/// This operation is cheap because functions are shared.
#[cfg(not(feature = "no_function"))]
#[inline]
#[must_use]
pub fn clone_functions_only_filtered(
&self,
filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
) -> Self {
let mut functions = crate::Module::new();
functions.merge_filtered(&self.functions, &filter);
Self {
source: self.source.clone(),
body: StmtBlock::NONE,
functions: functions.into(),
#[cfg(not(feature = "no_module"))]
resolver: self.resolver.clone(),
}
}
/// Clone the [`AST`]'s script statements into a new [`AST`].
/// No functions are cloned.
#[inline(always)]
#[must_use]
pub fn clone_statements_only(&self) -> Self {
Self {
source: self.source.clone(),
body: self.body.clone(),
#[cfg(not(feature = "no_function"))]
functions: crate::Module::new().into(),
#[cfg(not(feature = "no_module"))]
resolver: self.resolver.clone(),
}
}
/// Merge two [`AST`] into one. Both [`AST`]'s are untouched and a new, merged,
/// version is returned.
///
/// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_.
/// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried.
/// Of course, if the first [`AST`] uses a `return` statement at the end, then
/// the second [`AST`] will essentially be dead code.
///
/// All script-defined functions in the second [`AST`] overwrite similarly-named functions
/// in the first [`AST`] with the same number of parameters.
///
/// # Example
///
/// ```
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
/// # #[cfg(not(feature = "no_function"))]
/// # {
/// use rhai::Engine;
///
/// let engine = Engine::new();
///
/// let ast1 = engine.compile("
/// fn foo(x) { 42 + x }
/// foo(1)
/// ")?;
///
/// let ast2 = engine.compile(r#"
/// fn foo(n) { `hello${n}` }
/// foo("!")
/// "#)?;
///
/// let ast = ast1.merge(&ast2); // Merge 'ast2' into 'ast1'
///
/// // Notice that using the '+' operator also works:
/// // let ast = &ast1 + &ast2;
///
/// // 'ast' is essentially:
/// //
/// // fn foo(n) { `hello${n}` } // <- definition of first 'foo' is overwritten
/// // foo(1) // <- notice this will be "hello1" instead of 43,
/// // // but it is no longer the return value
/// // foo("!") // returns "hello!"
///
/// // Evaluate it
/// assert_eq!(engine.eval_ast::<String>(&ast)?, "hello!");
/// # }
/// # Ok(())
/// # }
/// ```
#[inline(always)]
#[must_use]
pub fn merge(&self, other: &Self) -> Self {
self.merge_filtered_impl(other, |_, _, _, _, _| true)
}
/// Combine one [`AST`] with another. The second [`AST`] is consumed.
///
/// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_.
/// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried.
/// Of course, if the first [`AST`] uses a `return` statement at the end, then
/// the second [`AST`] will essentially be dead code.
///
/// All script-defined functions in the second [`AST`] overwrite similarly-named functions
/// in the first [`AST`] with the same number of parameters.
///
/// # Example
///
/// ```
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
/// # #[cfg(not(feature = "no_function"))]
/// # {
/// use rhai::Engine;
///
/// let engine = Engine::new();
///
/// let mut ast1 = engine.compile("
/// fn foo(x) { 42 + x }
/// foo(1)
/// ")?;
///
/// let ast2 = engine.compile(r#"
/// fn foo(n) { `hello${n}` }
/// foo("!")
/// "#)?;
///
/// ast1.combine(ast2); // Combine 'ast2' into 'ast1'
///
/// // Notice that using the '+=' operator also works:
/// // ast1 += ast2;
///
/// // 'ast1' is essentially:
/// //
/// // fn foo(n) { `hello${n}` } // <- definition of first 'foo' is overwritten
/// // foo(1) // <- notice this will be "hello1" instead of 43,
/// // // but it is no longer the return value
/// // foo("!") // returns "hello!"
///
/// // Evaluate it
/// assert_eq!(engine.eval_ast::<String>(&ast1)?, "hello!");
/// # }
/// # Ok(())
/// # }
/// ```
#[inline(always)]
pub fn combine(&mut self, other: Self) -> &mut Self {
self.combine_filtered_impl(other, |_, _, _, _, _| true)
}
/// Merge two [`AST`] into one. Both [`AST`]'s are untouched and a new, merged, version
/// is returned.
///
/// Not available under `no_function`.
///
/// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_.
/// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried.
/// Of course, if the first [`AST`] uses a `return` statement at the end, then
/// the second [`AST`] will essentially be dead code.
///
/// All script-defined functions in the second [`AST`] are first selected based on a filter
/// predicate, then overwrite similarly-named functions in the first [`AST`] with the
/// same number of parameters.
///
/// # Example
///
/// ```
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
/// use rhai::Engine;
///
/// let engine = Engine::new();
///
/// let ast1 = engine.compile("
/// fn foo(x) { 42 + x }
/// foo(1)
/// ")?;
///
/// let ast2 = engine.compile(r#"
/// fn foo(n) { `hello${n}` }
/// fn error() { 0 }
/// foo("!")
/// "#)?;
///
/// // Merge 'ast2', picking only 'error()' but not 'foo(_)', into 'ast1'
/// let ast = ast1.merge_filtered(&ast2, |_, _, script, name, params|
/// script && name == "error" && params == 0);
///
/// // 'ast' is essentially:
/// //
/// // fn foo(n) { 42 + n } // <- definition of 'ast1::foo' is not overwritten
/// // // because 'ast2::foo' is filtered away
/// // foo(1) // <- notice this will be 43 instead of "hello1",
/// // // but it is no longer the return value
/// // fn error() { 0 } // <- this function passes the filter and is merged
/// // foo("!") // <- returns "42!"
///
/// // Evaluate it
/// assert_eq!(engine.eval_ast::<String>(&ast)?, "42!");
/// # Ok(())
/// # }
/// ```
#[cfg(not(feature = "no_function"))]
#[inline(always)]
#[must_use]
pub fn merge_filtered(
&self,
other: &Self,
filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
) -> Self {
self.merge_filtered_impl(other, filter)
}
/// Merge two [`AST`] into one. Both [`AST`]'s are untouched and a new, merged, version
/// is returned.
#[inline]
#[must_use]
fn merge_filtered_impl(
&self,
other: &Self,
_filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
) -> Self {
let merged = match (self.body.is_empty(), other.body.is_empty()) {
(false, false) => {
let mut body = self.body.clone();
body.extend(other.body.iter().cloned());
body
}
(false, true) => self.body.clone(),
(true, false) => other.body.clone(),
(true, true) => StmtBlock::NONE,
};
#[cfg(not(feature = "no_function"))]
let functions = {
let mut functions = self.functions.as_ref().clone();
functions.merge_filtered(&other.functions, &_filter);
functions
};
if !other.source.is_empty() {
Self::new_with_source(
merged,
#[cfg(not(feature = "no_function"))]
functions,
other.source.clone(),
)
} else {
Self::new(
merged,
#[cfg(not(feature = "no_function"))]
functions,
)
}
}
/// Combine one [`AST`] with another. The second [`AST`] is consumed.
///
/// Not available under `no_function`.
///
/// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_.
/// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried.
/// Of course, if the first [`AST`] uses a `return` statement at the end, then
/// the second [`AST`] will essentially be dead code.
///
/// All script-defined functions in the second [`AST`] are first selected based on a filter
/// predicate, then overwrite similarly-named functions in the first [`AST`] with the
/// same number of parameters.
///
/// # Example
///
/// ```
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
/// use rhai::Engine;
///
/// let engine = Engine::new();
///
/// let mut ast1 = engine.compile("
/// fn foo(x) { 42 + x }
/// foo(1)
/// ")?;
///
/// let ast2 = engine.compile(r#"
/// fn foo(n) { `hello${n}` }
/// fn error() { 0 }
/// foo("!")
/// "#)?;
///
/// // Combine 'ast2', picking only 'error()' but not 'foo(_)', into 'ast1'
/// ast1.combine_filtered(ast2, |_, _, script, name, params|
/// script && name == "error" && params == 0);
///
/// // 'ast1' is essentially:
/// //
/// // fn foo(n) { 42 + n } // <- definition of 'ast1::foo' is not overwritten
/// // // because 'ast2::foo' is filtered away
/// // foo(1) // <- notice this will be 43 instead of "hello1",
/// // // but it is no longer the return value
/// // fn error() { 0 } // <- this function passes the filter and is merged
/// // foo("!") // <- returns "42!"
///
/// // Evaluate it
/// assert_eq!(engine.eval_ast::<String>(&ast1)?, "42!");
/// # Ok(())
/// # }
/// ```
#[cfg(not(feature = "no_function"))]
#[inline(always)]
pub fn combine_filtered(
&mut self,
other: Self,
filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
) -> &mut Self {
self.combine_filtered_impl(other, filter)
}
/// Combine one [`AST`] with another. The second [`AST`] is consumed.
#[inline]
fn combine_filtered_impl(
&mut self,
other: Self,
_filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
) -> &mut Self {
self.body.extend(other.body.into_iter());
#[cfg(not(feature = "no_function"))]
if !other.functions.is_empty() {
crate::func::native::shared_make_mut(&mut self.functions)
.merge_filtered(&other.functions, &_filter);
}
self
}
/// Filter out the functions, retaining only some based on a filter predicate.
///
/// Not available under `no_function`.
///
/// # Example
///
/// ```
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
/// # #[cfg(not(feature = "no_function"))]
/// # {
/// use rhai::Engine;
///
/// let engine = Engine::new();
///
/// let mut ast = engine.compile(r#"
/// fn foo(n) { n + 1 }
/// fn bar() { print("hello"); }
/// "#)?;
///
/// // Remove all functions except 'foo(_)'
/// ast.retain_functions(|_, _, name, params| name == "foo" && params == 1);
/// # }
/// # Ok(())
/// # }
/// ```
#[cfg(not(feature = "no_function"))]
#[inline]
pub fn retain_functions(
&mut self,
filter: impl Fn(FnNamespace, FnAccess, &str, usize) -> bool,
) -> &mut Self {
if !self.functions.is_empty() {
crate::func::native::shared_make_mut(&mut self.functions)
.retain_script_functions(filter);
}
self
}
/// Iterate through all function definitions.
///
/// Not available under `no_function`.
#[cfg(not(feature = "no_function"))]
#[allow(dead_code)]
#[inline]
pub(crate) fn iter_fn_def(&self) -> impl Iterator<Item = &super::ScriptFnDef> {
self.functions
.iter_script_fn()
.map(|(_, _, _, _, fn_def)| fn_def.as_ref())
}
/// Iterate through all function definitions.
///
/// Not available under `no_function`.
#[cfg(not(feature = "no_function"))]
#[inline]
pub fn iter_functions<'a>(&'a self) -> impl Iterator<Item = super::ScriptFnMetadata> + 'a {
self.functions
.iter_script_fn()
.map(|(_, _, _, _, fn_def)| fn_def.as_ref().into())
}
/// Clear all function definitions in the [`AST`].
///
/// Not available under `no_function`.
#[cfg(not(feature = "no_function"))]
#[inline(always)]
pub fn clear_functions(&mut self) -> &mut Self {
self.functions = crate::Module::new().into();
self
}
/// Clear all statements in the [`AST`], leaving only function definitions.
#[inline(always)]
pub fn clear_statements(&mut self) -> &mut Self {
self.body = StmtBlock::NONE;
self
}
/// Extract all top-level literal constant and/or variable definitions.
/// This is useful for extracting all global constants from a script without actually running it.
///
/// A literal constant/variable definition takes the form of:
/// `const VAR = `_value_`;` and `let VAR = `_value_`;`
/// where _value_ is a literal expression or will be optimized into a literal.
///
/// # Example
///
/// ```
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
/// use rhai::{Engine, Scope};
///
/// let engine = Engine::new();
///
/// let ast = engine.compile(
/// "
/// const A = 40 + 2; // constant that optimizes into a literal
/// let b = 123; // literal variable
/// const B = b * A; // non-literal constant
/// const C = 999; // literal constant
/// b = A + C; // expression
///
/// { // <- new block scope
/// const Z = 0; // <- literal constant not at top-level
/// }
/// ")?;
///
/// let mut iter = ast.iter_literal_variables(true, false)
/// .map(|(name, is_const, value)| (name, is_const, value.as_int().unwrap()));
///
/// # #[cfg(not(feature = "no_optimize"))]
/// assert_eq!(iter.next(), Some(("A", true, 42)));
/// assert_eq!(iter.next(), Some(("C", true, 999)));
/// assert_eq!(iter.next(), None);
///
/// let mut iter = ast.iter_literal_variables(false, true)
/// .map(|(name, is_const, value)| (name, is_const, value.as_int().unwrap()));
///
/// assert_eq!(iter.next(), Some(("b", false, 123)));
/// assert_eq!(iter.next(), None);
///
/// let mut iter = ast.iter_literal_variables(true, true)
/// .map(|(name, is_const, value)| (name, is_const, value.as_int().unwrap()));
///
/// # #[cfg(not(feature = "no_optimize"))]
/// assert_eq!(iter.next(), Some(("A", true, 42)));
/// assert_eq!(iter.next(), Some(("b", false, 123)));
/// assert_eq!(iter.next(), Some(("C", true, 999)));
/// assert_eq!(iter.next(), None);
///
/// let scope: Scope = ast.iter_literal_variables(true, false).collect();
///
/// # #[cfg(not(feature = "no_optimize"))]
/// assert_eq!(scope.len(), 2);
///
/// Ok(())
/// # }
/// ```
pub fn iter_literal_variables(
&self,
include_constants: bool,
include_variables: bool,
) -> impl Iterator<Item = (&str, bool, Dynamic)> {
self.statements().iter().filter_map(move |stmt| match stmt {
Stmt::Var(expr, name, options, _)
if options.contains(AST_OPTION_FLAGS::AST_OPTION_CONSTANT) && include_constants
|| !options.contains(AST_OPTION_FLAGS::AST_OPTION_CONSTANT)
&& include_variables =>
{
if let Some(value) = expr.get_literal_value() {
Some((
name.as_str(),
options.contains(AST_OPTION_FLAGS::AST_OPTION_CONSTANT),
value,
))
} else {
None
}
}
_ => None,
})
}
/// Recursively walk the [`AST`], including function bodies (if any).
/// Return `false` from the callback to terminate the walk.
#[cfg(not(feature = "internals"))]
#[cfg(not(feature = "no_module"))]
#[inline]
pub(crate) fn walk(&self, on_node: &mut impl FnMut(&[ASTNode]) -> bool) -> bool {
let path = &mut Vec::new();
for stmt in self.statements() {
if !stmt.walk(path, on_node) {
return false;
}
}
#[cfg(not(feature = "no_function"))]
for stmt in self.iter_fn_def().flat_map(|f| f.body.iter()) {
if !stmt.walk(path, on_node) {
return false;
}
}
true
}
/// _(internals)_ Recursively walk the [`AST`], including function bodies (if any).
/// Return `false` from the callback to terminate the walk.
/// Exported under the `internals` feature only.
#[cfg(feature = "internals")]
#[inline]
pub fn walk(&self, on_node: &mut impl FnMut(&[ASTNode]) -> bool) -> bool {
let path = &mut Vec::new();
for stmt in self.statements() {
if !stmt.walk(path, on_node) {
return false;
}
}
#[cfg(not(feature = "no_function"))]
for stmt in self.iter_fn_def().flat_map(|f| f.body.iter()) {
if !stmt.walk(path, on_node) {
return false;
}
}
true
}
}
impl<A: AsRef<AST>> Add<A> for &AST {
type Output = AST;
#[inline(always)]
fn add(self, rhs: A) -> Self::Output {
self.merge(rhs.as_ref())
}
}
impl<A: Into<AST>> AddAssign<A> for AST {
#[inline(always)]
fn add_assign(&mut self, rhs: A) {
self.combine(rhs.into());
}
}
impl AsRef<[Stmt]> for AST {
#[inline(always)]
fn as_ref(&self) -> &[Stmt] {
self.statements()
}
}
#[cfg(not(feature = "no_function"))]
impl AsRef<crate::Module> for AST {
#[inline(always)]
fn as_ref(&self) -> &crate::Module {
self.shared_lib().as_ref()
}
}
#[cfg(not(feature = "no_function"))]
impl AsRef<crate::Shared<crate::Module>> for AST {
#[inline(always)]
fn as_ref(&self) -> &crate::Shared<crate::Module> {
self.shared_lib()
}
}
/// _(internals)_ An [`AST`] node, consisting of either an [`Expr`] or a [`Stmt`].
/// Exported under the `internals` feature only.
#[derive(Debug, Clone, Hash)]
pub enum ASTNode<'a> {
/// A statement ([`Stmt`]).
Stmt(&'a Stmt),
/// An expression ([`Expr`]).
Expr(&'a Expr),
}
impl<'a> From<&'a Stmt> for ASTNode<'a> {
fn from(stmt: &'a Stmt) -> Self {
Self::Stmt(stmt)
}
}
impl<'a> From<&'a Expr> for ASTNode<'a> {
fn from(expr: &'a Expr) -> Self {
Self::Expr(expr)
}
}
impl ASTNode<'_> {
/// Get the [`Position`] of this [`ASTNode`].
pub const fn position(&self) -> Position {
match self {
ASTNode::Stmt(stmt) => stmt.position(),
ASTNode::Expr(expr) => expr.position(),
}
}
}
impl AST {
/// _(internals)_ Get the internal [`Module`][crate::Module] containing all script-defined functions.
/// Exported under the `internals` feature only.
///
/// Not available under `no_function`.
///
/// # Deprecated
///
/// This method is deprecated. Use [`shared_lib`][AST::shared_lib] instead.
///
/// This method will be removed in the next major version.
#[deprecated(since = "1.3.0", note = "use `shared_lib` instead")]
#[cfg(feature = "internals")]
#[cfg(not(feature = "no_function"))]
#[inline(always)]
#[must_use]
pub fn lib(&self) -> &crate::Module {
&self.functions
}
}

831
src/ast/expr.rs Normal file
View File

@ -0,0 +1,831 @@
//! Module defining script expressions.
use super::{ASTNode, Ident, Stmt, StmtBlock};
use crate::engine::{OP_EXCLUSIVE_RANGE, OP_INCLUSIVE_RANGE};
use crate::func::hashing::ALT_ZERO_HASH;
use crate::module::Namespace;
use crate::tokenizer::Token;
use crate::types::dynamic::Union;
use crate::{Dynamic, Identifier, ImmutableString, Position, StaticVec, INT};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{
collections::BTreeMap,
fmt,
hash::Hash,
num::{NonZeroU8, NonZeroUsize},
};
#[cfg(not(feature = "no_float"))]
use std::str::FromStr;
#[cfg(not(feature = "no_float"))]
use num_traits::Float;
/// _(internals)_ A binary expression.
/// Exported under the `internals` feature only.
#[derive(Debug, Clone, Hash)]
pub struct BinaryExpr {
/// LHS expression.
pub lhs: Expr,
/// RHS expression.
pub rhs: Expr,
}
/// _(internals)_ A custom syntax expression.
/// Exported under the `internals` feature only.
#[derive(Debug, Clone, Hash)]
pub struct CustomExpr {
/// List of keywords.
pub inputs: StaticVec<Expr>,
/// List of tokens actually parsed.
pub tokens: StaticVec<Identifier>,
/// Is the current [`Scope`][crate::Scope] possibly modified by this custom statement
/// (e.g. introducing a new variable)?
pub scope_may_be_changed: bool,
/// Is this custom syntax self-terminated?
pub self_terminated: bool,
}
impl CustomExpr {
/// Is this custom syntax self-terminated (i.e. no need for a semicolon terminator)?
///
/// A self-terminated custom syntax always ends in `$block$`, `}` or `;`
#[must_use]
#[inline(always)]
pub const fn is_self_terminated(&self) -> bool {
self.self_terminated
}
}
/// _(internals)_ A set of function call hashes. Exported under the `internals` feature only.
///
/// Two separate hashes are pre-calculated because of the following patterns:
///
/// ```js
/// func(a, b, c); // Native: func(a, b, c) - 3 parameters
/// // Script: func(a, b, c) - 3 parameters
///
/// a.func(b, c); // Native: func(&mut a, b, c) - 3 parameters
/// // Script: func(b, c) - 2 parameters
/// ```
///
/// For normal function calls, the native hash equals the script hash.
///
/// For method-style calls, the script hash contains one fewer parameter.
///
/// Function call hashes are used in the following manner:
///
/// * First, the script hash is tried, which contains only the called function's name plus the
/// number of parameters.
///
/// * Next, the actual types of arguments are hashed and _combined_ with the native hash, which is
/// then used to search for a native function. In other words, a complete native function call
/// hash always contains the called function's name plus the types of the arguments. This is due
/// to possible function overloading for different parameter types.
#[derive(Clone, Copy, Eq, PartialEq, Hash, Default)]
pub struct FnCallHashes {
/// Pre-calculated hash for a script-defined function (zero if native functions only).
#[cfg(not(feature = "no_function"))]
pub script: u64,
/// Pre-calculated hash for a native Rust function with no parameter types.
pub native: u64,
}
impl fmt::Debug for FnCallHashes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(not(feature = "no_function"))]
if self.script != 0 {
return if self.script == self.native {
fmt::Debug::fmt(&self.native, f)
} else {
write!(f, "({}, {})", self.script, self.native)
};
}
write!(f, "{} (native only)", self.native)
}
}
impl From<u64> for FnCallHashes {
#[inline(always)]
fn from(hash: u64) -> Self {
let hash = if hash == 0 { ALT_ZERO_HASH } else { hash };
Self {
#[cfg(not(feature = "no_function"))]
script: hash,
native: hash,
}
}
}
impl FnCallHashes {
/// Create a [`FnCallHashes`] with only the native Rust hash.
#[inline(always)]
#[must_use]
pub const fn from_native(hash: u64) -> Self {
Self {
#[cfg(not(feature = "no_function"))]
script: 0,
native: if hash == 0 { ALT_ZERO_HASH } else { hash },
}
}
/// Create a [`FnCallHashes`] with both native Rust and script function hashes.
#[inline(always)]
#[must_use]
pub const fn from_all(#[cfg(not(feature = "no_function"))] script: u64, native: u64) -> Self {
Self {
#[cfg(not(feature = "no_function"))]
script: if script == 0 { ALT_ZERO_HASH } else { script },
native: if native == 0 { ALT_ZERO_HASH } else { native },
}
}
/// Is this [`FnCallHashes`] native Rust only?
#[inline(always)]
#[must_use]
pub const fn is_native_only(&self) -> bool {
#[cfg(not(feature = "no_function"))]
return self.script == 0;
#[cfg(feature = "no_function")]
return true;
}
}
/// _(internals)_ A function call.
/// Exported under the `internals` feature only.
#[derive(Debug, Clone, Default, Hash)]
pub struct FnCallExpr {
/// Namespace of the function, if any.
pub namespace: Option<Namespace>,
/// Function name.
pub name: Identifier,
/// Pre-calculated hashes.
pub hashes: FnCallHashes,
/// List of function call argument expressions.
pub args: StaticVec<Expr>,
/// List of function call arguments that are constants.
///
/// Any arguments in `args` that is [`Expr::Stack`] indexes into this
/// array to find the constant for use as its argument value.
///
/// # Notes
///
/// Constant arguments are very common in function calls, and keeping each constant in
/// an [`Expr::DynamicConstant`] involves an additional allocation. Keeping the constant
/// values in an inlined array avoids these extra allocations.
pub constants: StaticVec<Dynamic>,
/// Does this function call capture the parent scope?
pub capture_parent_scope: bool,
}
impl FnCallExpr {
/// Does this function call contain a qualified namespace?
#[inline(always)]
#[must_use]
pub const fn is_qualified(&self) -> bool {
self.namespace.is_some()
}
/// Convert this into an [`Expr::FnCall`].
#[inline(always)]
#[must_use]
pub fn into_fn_call_expr(self, pos: Position) -> Expr {
Expr::FnCall(self.into(), pos)
}
}
/// A type that wraps a floating-point number and implements [`Hash`].
///
/// Not available under `no_float`.
#[cfg(not(feature = "no_float"))]
#[derive(Clone, Copy, PartialEq, PartialOrd)]
pub struct FloatWrapper<F>(F);
#[cfg(not(feature = "no_float"))]
impl Hash for FloatWrapper<crate::FLOAT> {
#[inline(always)]
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.to_ne_bytes().hash(state);
}
}
#[cfg(not(feature = "no_float"))]
impl<F: Float> AsRef<F> for FloatWrapper<F> {
#[inline(always)]
fn as_ref(&self) -> &F {
&self.0
}
}
#[cfg(not(feature = "no_float"))]
impl<F: Float> AsMut<F> for FloatWrapper<F> {
#[inline(always)]
fn as_mut(&mut self) -> &mut F {
&mut self.0
}
}
#[cfg(not(feature = "no_float"))]
impl<F: Float> std::ops::Deref for FloatWrapper<F> {
type Target = F;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(not(feature = "no_float"))]
impl<F: Float> std::ops::DerefMut for FloatWrapper<F> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[cfg(not(feature = "no_float"))]
impl<F: Float + fmt::Debug> fmt::Debug for FloatWrapper<F> {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
#[cfg(not(feature = "no_float"))]
impl<F: Float + fmt::Display + fmt::LowerExp + From<f32>> fmt::Display for FloatWrapper<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let abs = self.0.abs();
if abs.is_zero() {
f.write_str("0.0")
} else if abs > Self::MAX_NATURAL_FLOAT_FOR_DISPLAY.into()
|| abs < Self::MIN_NATURAL_FLOAT_FOR_DISPLAY.into()
{
write!(f, "{:e}", self.0)
} else {
fmt::Display::fmt(&self.0, f)?;
if abs.fract().is_zero() {
f.write_str(".0")?;
}
Ok(())
}
}
}
#[cfg(not(feature = "no_float"))]
impl<F: Float> From<F> for FloatWrapper<F> {
#[inline(always)]
fn from(value: F) -> Self {
Self::new(value)
}
}
#[cfg(not(feature = "no_float"))]
impl<F: Float + FromStr> FromStr for FloatWrapper<F> {
type Err = <F as FromStr>::Err;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
F::from_str(s).map(Into::into)
}
}
#[cfg(not(feature = "no_float"))]
impl<F: Float> FloatWrapper<F> {
/// Maximum floating-point number for natural display before switching to scientific notation.
pub const MAX_NATURAL_FLOAT_FOR_DISPLAY: f32 = 10000000000000.0;
/// Minimum floating-point number for natural display before switching to scientific notation.
pub const MIN_NATURAL_FLOAT_FOR_DISPLAY: f32 = 0.0000000000001;
/// Create a new [`FloatWrapper`].
#[inline(always)]
#[must_use]
pub fn new(value: F) -> Self {
Self(value)
}
}
#[cfg(not(feature = "no_float"))]
impl FloatWrapper<crate::FLOAT> {
/// Create a new [`FloatWrapper`].
#[inline(always)]
#[must_use]
pub const fn new_const(value: crate::FLOAT) -> Self {
Self(value)
}
}
/// _(internals)_ An expression sub-tree.
/// Exported under the `internals` feature only.
#[derive(Clone, Hash)]
pub enum Expr {
/// Dynamic constant.
///
/// Used to hold complex constants such as [`Array`][crate::Array] or [`Map`][crate::Map] for quick cloning.
/// Primitive data types should use the appropriate variants to avoid an allocation.
DynamicConstant(Box<Dynamic>, Position),
/// Boolean constant.
BoolConstant(bool, Position),
/// Integer constant.
IntegerConstant(INT, Position),
/// Floating-point constant.
///
/// Not available under `no_float`.
#[cfg(not(feature = "no_float"))]
FloatConstant(FloatWrapper<crate::FLOAT>, Position),
/// Character constant.
CharConstant(char, Position),
/// [String][ImmutableString] constant.
StringConstant(ImmutableString, Position),
/// An interpolated [string][ImmutableString].
InterpolatedString(Box<StaticVec<Expr>>, Position),
/// [ expr, ... ]
Array(Box<StaticVec<Expr>>, Position),
/// #{ name:expr, ... }
Map(
Box<(StaticVec<(Ident, Expr)>, BTreeMap<Identifier, Dynamic>)>,
Position,
),
/// ()
Unit(Position),
/// Variable access - optional short index, position, (optional index, optional (hash, modules), variable name)
///
/// The short index is [`u8`] which is used when the index is <= 255, which should be the vast
/// majority of cases (unless there are more than 255 variables defined!).
/// This is to avoid reading a pointer redirection during each variable access.
Variable(
Option<NonZeroU8>,
Position,
Box<(Option<NonZeroUsize>, Option<(Namespace, u64)>, Identifier)>,
),
/// Property access - ((getter, hash), (setter, hash), prop)
Property(
Box<(
(Identifier, u64),
(Identifier, u64),
(ImmutableString, Position),
)>,
),
/// Stack slot for function calls. See [`FnCallExpr`] for more details.
///
/// This variant does not map to any language structure. It is used in function calls with
/// constant arguments where the `usize` number indexes into an array containing a list of
/// constant arguments for the function call.
Stack(usize, Position),
/// { [statement][Stmt] ... }
Stmt(Box<StmtBlock>),
/// func `(` expr `,` ... `)`
FnCall(Box<FnCallExpr>, Position),
/// lhs `.` rhs - bool variable is a dummy
Dot(Box<BinaryExpr>, bool, Position),
/// expr `[` expr `]` - boolean indicates whether the dotting/indexing chain stops
Index(Box<BinaryExpr>, bool, Position),
/// lhs `&&` rhs
And(Box<BinaryExpr>, Position),
/// lhs `||` rhs
Or(Box<BinaryExpr>, Position),
/// Custom syntax
Custom(Box<CustomExpr>, Position),
}
impl Default for Expr {
#[inline(always)]
fn default() -> Self {
Self::Unit(Position::NONE)
}
}
impl fmt::Debug for Expr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut display_pos = self.position();
match self {
Self::DynamicConstant(value, _) => write!(f, "{:?}", value),
Self::BoolConstant(value, _) => write!(f, "{:?}", value),
Self::IntegerConstant(value, _) => write!(f, "{:?}", value),
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(value, _) => write!(f, "{:?}", value),
Self::CharConstant(value, _) => write!(f, "{:?}", value),
Self::StringConstant(value, _) => write!(f, "{:?}", value),
Self::Unit(_) => f.write_str("()"),
Self::InterpolatedString(x, _) => {
f.write_str("InterpolatedString")?;
return f.debug_list().entries(x.iter()).finish();
}
Self::Array(x, _) => {
f.write_str("Array")?;
f.debug_list().entries(x.iter()).finish()
}
Self::Map(x, _) => {
f.write_str("Map")?;
f.debug_map()
.entries(x.0.iter().map(|(k, v)| (k, v)))
.finish()
}
Self::Variable(i, _, x) => {
f.write_str("Variable(")?;
if let Some((_, ref namespace)) = x.1 {
write!(f, "{}{}", namespace, Token::DoubleColon.literal_syntax())?
}
f.write_str(&x.2)?;
if let Some(n) = i.map_or_else(|| x.0, |n| NonZeroUsize::new(n.get() as usize)) {
write!(f, " #{}", n)?
}
f.write_str(")")
}
Self::Property(x) => write!(f, "Property({})", (x.2).0),
Self::Stack(x, _) => write!(f, "StackSlot({})", x),
Self::Stmt(x) => {
f.write_str("ExprStmtBlock")?;
f.debug_list().entries(x.iter()).finish()
}
Self::FnCall(x, _) => {
let mut ff = f.debug_struct("FnCall");
x.namespace.as_ref().map(|ns| ff.field("namespace", ns));
ff.field("name", &x.name)
.field("hash", &x.hashes)
.field("args", &x.args);
if !x.constants.is_empty() {
ff.field("constants", &x.constants);
}
if x.capture_parent_scope {
ff.field("capture_parent_scope", &x.capture_parent_scope);
}
ff.finish()
}
Self::Index(x, term, pos) => {
display_pos = *pos;
f.debug_struct("Index")
.field("lhs", &x.lhs)
.field("rhs", &x.rhs)
.field("terminate", term)
.finish()
}
Self::Dot(x, _, pos) | Self::And(x, pos) | Self::Or(x, pos) => {
let op_name = match self {
Self::Dot(_, _, _) => "Dot",
Self::And(_, _) => "And",
Self::Or(_, _) => "Or",
expr => unreachable!(
"Self::Dot or Self::And or Self::Or expected but gets {:?}",
expr
),
};
display_pos = *pos;
f.debug_struct(op_name)
.field("lhs", &x.lhs)
.field("rhs", &x.rhs)
.finish()
}
Self::Custom(x, _) => f.debug_tuple("Custom").field(x).finish(),
}?;
display_pos.debug_print(f)
}
}
impl Expr {
/// Get the [`Dynamic`] value of a literal constant expression.
///
/// Returns [`None`] if the expression is not a literal constant.
#[inline]
#[must_use]
pub fn get_literal_value(&self) -> Option<Dynamic> {
Some(match self {
Self::DynamicConstant(x, _) => x.as_ref().clone(),
Self::IntegerConstant(x, _) => (*x).into(),
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(x, _) => (*x).into(),
Self::CharConstant(x, _) => (*x).into(),
Self::StringConstant(x, _) => x.clone().into(),
Self::BoolConstant(x, _) => (*x).into(),
Self::Unit(_) => Dynamic::UNIT,
#[cfg(not(feature = "no_index"))]
Self::Array(x, _) if self.is_constant() => {
let mut arr = crate::Array::with_capacity(x.len());
arr.extend(x.iter().map(|v| v.get_literal_value().unwrap()));
Dynamic::from_array(arr)
}
#[cfg(not(feature = "no_object"))]
Self::Map(x, _) if self.is_constant() => {
Dynamic::from_map(x.0.iter().fold(x.1.clone(), |mut map, (k, v)| {
let value_ref = map.get_mut(k.name.as_str()).unwrap();
*value_ref = v.get_literal_value().unwrap();
map
}))
}
// Binary operators
Self::FnCall(x, _) if x.args.len() == 2 => match x.name.as_str() {
// x..y
OP_EXCLUSIVE_RANGE => {
if let Expr::IntegerConstant(ref start, _) = x.args[0] {
if let Expr::IntegerConstant(ref end, _) = x.args[1] {
(*start..*end).into()
} else {
return None;
}
} else {
return None;
}
}
// x..=y
OP_INCLUSIVE_RANGE => {
if let Expr::IntegerConstant(ref start, _) = x.args[0] {
if let Expr::IntegerConstant(ref end, _) = x.args[1] {
(*start..=*end).into()
} else {
return None;
}
} else {
return None;
}
}
_ => return None,
},
_ => return None,
})
}
/// Create an [`Expr`] from a [`Dynamic`] value.
#[inline]
#[must_use]
pub fn from_dynamic(value: Dynamic, pos: Position) -> Self {
match value.0 {
Union::Unit(_, _, _) => Self::Unit(pos),
Union::Bool(b, _, _) => Self::BoolConstant(b, pos),
Union::Str(s, _, _) => Self::StringConstant(s, pos),
Union::Char(c, _, _) => Self::CharConstant(c, pos),
Union::Int(i, _, _) => Self::IntegerConstant(i, pos),
#[cfg(feature = "decimal")]
Union::Decimal(value, _, _) => Self::DynamicConstant(Box::new((*value).into()), pos),
#[cfg(not(feature = "no_float"))]
Union::Float(f, _, _) => Self::FloatConstant(f, pos),
#[cfg(not(feature = "no_index"))]
Union::Array(a, _, _) => Self::DynamicConstant(Box::new((*a).into()), pos),
#[cfg(not(feature = "no_object"))]
Union::Map(m, _, _) => Self::DynamicConstant(Box::new((*m).into()), pos),
_ => Self::DynamicConstant(value.into(), pos),
}
}
/// Is the expression a simple variable access?
#[inline]
#[must_use]
pub(crate) const fn is_variable_access(&self, non_qualified: bool) -> bool {
match self {
Self::Variable(_, _, x) => !non_qualified || x.1.is_none(),
_ => false,
}
}
/// Return the variable name if the expression a simple variable access.
#[inline]
#[must_use]
pub(crate) fn get_variable_name(&self, non_qualified: bool) -> Option<&str> {
match self {
Self::Variable(_, _, x) if !non_qualified || x.1.is_none() => Some(x.2.as_str()),
_ => None,
}
}
/// Get the [position][Position] of the expression.
#[inline]
#[must_use]
pub const fn position(&self) -> Position {
match self {
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(_, pos) => *pos,
Self::DynamicConstant(_, pos)
| Self::BoolConstant(_, pos)
| Self::IntegerConstant(_, pos)
| Self::CharConstant(_, pos)
| Self::Unit(pos)
| Self::StringConstant(_, pos)
| Self::Array(_, pos)
| Self::Map(_, pos)
| Self::Variable(_, pos, _)
| Self::Stack(_, pos)
| Self::FnCall(_, pos)
| Self::Custom(_, pos)
| Self::InterpolatedString(_, pos) => *pos,
Self::Property(x) => (x.2).1,
Self::Stmt(x) => x.position(),
Self::And(x, _) | Self::Or(x, _) | Self::Dot(x, _, _) | Self::Index(x, _, _) => {
x.lhs.position()
}
}
}
/// Override the [position][Position] of the expression.
#[inline]
pub fn set_position(&mut self, new_pos: Position) -> &mut Self {
match self {
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(_, pos) => *pos = new_pos,
Self::DynamicConstant(_, pos)
| Self::BoolConstant(_, pos)
| Self::IntegerConstant(_, pos)
| Self::CharConstant(_, pos)
| Self::Unit(pos)
| Self::StringConstant(_, pos)
| Self::Array(_, pos)
| Self::Map(_, pos)
| Self::And(_, pos)
| Self::Or(_, pos)
| Self::Dot(_, _, pos)
| Self::Index(_, _, pos)
| Self::Variable(_, pos, _)
| Self::Stack(_, pos)
| Self::FnCall(_, pos)
| Self::Custom(_, pos)
| Self::InterpolatedString(_, pos) => *pos = new_pos,
Self::Property(x) => (x.2).1 = new_pos,
Self::Stmt(x) => x.set_position(new_pos),
}
self
}
/// Is the expression pure?
///
/// A pure expression has no side effects.
#[inline]
#[must_use]
pub fn is_pure(&self) -> bool {
match self {
Self::InterpolatedString(x, _) | Self::Array(x, _) => x.iter().all(Self::is_pure),
Self::Map(x, _) => x.0.iter().map(|(_, v)| v).all(Self::is_pure),
Self::And(x, _) | Self::Or(x, _) => x.lhs.is_pure() && x.rhs.is_pure(),
Self::Stmt(x) => x.iter().all(Stmt::is_pure),
Self::Variable(_, _, _) | Self::Stack(_, _) => true,
_ => self.is_constant(),
}
}
/// Is the expression the unit `()` literal?
#[inline(always)]
#[must_use]
pub const fn is_unit(&self) -> bool {
matches!(self, Self::Unit(_))
}
/// Is the expression a constant?
#[inline]
#[must_use]
pub fn is_constant(&self) -> bool {
match self {
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(_, _) => true,
Self::DynamicConstant(_, _)
| Self::BoolConstant(_, _)
| Self::IntegerConstant(_, _)
| Self::CharConstant(_, _)
| Self::StringConstant(_, _)
| Self::Unit(_)
| Self::Stack(_, _) => true,
Self::InterpolatedString(x, _) | Self::Array(x, _) => x.iter().all(Self::is_constant),
Self::Map(x, _) => x.0.iter().map(|(_, expr)| expr).all(Self::is_constant),
_ => false,
}
}
/// Is a particular [token][Token] allowed as a postfix operator to this expression?
#[inline]
#[must_use]
pub const fn is_valid_postfix(&self, token: &Token) -> bool {
match token {
#[cfg(not(feature = "no_object"))]
Token::Period => return true,
_ => (),
}
match self {
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(_, _) => false,
Self::DynamicConstant(_, _)
| Self::BoolConstant(_, _)
| Self::CharConstant(_, _)
| Self::And(_, _)
| Self::Or(_, _)
| Self::Unit(_) => false,
Self::IntegerConstant(_, _)
| Self::StringConstant(_, _)
| Self::InterpolatedString(_, _)
| Self::FnCall(_, _)
| Self::Stmt(_)
| Self::Dot(_, _, _)
| Self::Index(_, _, _)
| Self::Array(_, _)
| Self::Map(_, _)
| Self::Custom(_, _) => match token {
#[cfg(not(feature = "no_index"))]
Token::LeftBracket => true,
_ => false,
},
Self::Variable(_, _, _) => match token {
#[cfg(not(feature = "no_index"))]
Token::LeftBracket => true,
Token::LeftParen => true,
Token::Bang => true,
Token::DoubleColon => true,
_ => false,
},
Self::Property(_) => match token {
#[cfg(not(feature = "no_index"))]
Token::LeftBracket => true,
Token::LeftParen => true,
_ => false,
},
Self::Stack(_, _) => false,
}
}
/// Recursively walk this expression.
/// Return `false` from the callback to terminate the walk.
pub fn walk<'a>(
&'a self,
path: &mut Vec<ASTNode<'a>>,
on_node: &mut impl FnMut(&[ASTNode]) -> bool,
) -> bool {
// Push the current node onto the path
path.push(self.into());
if !on_node(path) {
return false;
}
match self {
Self::Stmt(x) => {
for s in x.iter() {
if !s.walk(path, on_node) {
return false;
}
}
}
Self::InterpolatedString(x, _) | Self::Array(x, _) => {
for e in x.as_ref() {
if !e.walk(path, on_node) {
return false;
}
}
}
Self::Map(x, _) => {
for (_, e) in &x.0 {
if !e.walk(path, on_node) {
return false;
}
}
}
Self::Index(x, _, _) | Self::Dot(x, _, _) | Expr::And(x, _) | Expr::Or(x, _) => {
if !x.lhs.walk(path, on_node) {
return false;
}
if !x.rhs.walk(path, on_node) {
return false;
}
}
Self::FnCall(x, _) => {
for e in &x.args {
if !e.walk(path, on_node) {
return false;
}
}
}
Self::Custom(x, _) => {
for e in &x.inputs {
if !e.walk(path, on_node) {
return false;
}
}
}
_ => (),
}
path.pop().unwrap();
true
}
}

169
src/ast/flags.rs Normal file
View File

@ -0,0 +1,169 @@
//! Module defining script options.
use std::ops::{Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, Not, Sub, SubAssign};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
/// A type representing the access mode of a function.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum FnAccess {
/// Public function.
Public,
/// Private function.
Private,
}
/// A type that holds a configuration option with bit-flags.
/// Exported under the `internals` feature only.
#[derive(PartialEq, Eq, Copy, Clone, Hash, Default)]
pub struct OptionFlags(u8);
impl OptionFlags {
/// Does this [`OptionFlags`] contain a particular option flag?
#[inline(always)]
#[must_use]
pub const fn contains(self, flag: Self) -> bool {
self.0 & flag.0 != 0
}
}
impl Not for OptionFlags {
type Output = Self;
/// Return the negation of the [`OptionFlags`].
#[inline(always)]
fn not(self) -> Self::Output {
Self(!self.0) & AST_OPTION_FLAGS::AST_OPTION_ALL
}
}
impl Add for OptionFlags {
type Output = Self;
/// Return the union of two [`OptionFlags`].
#[inline(always)]
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 | rhs.0)
}
}
impl AddAssign for OptionFlags {
/// Add the option flags in one [`OptionFlags`] to another.
#[inline(always)]
fn add_assign(&mut self, rhs: Self) {
self.0 |= rhs.0
}
}
impl BitOr for OptionFlags {
type Output = Self;
/// Return the union of two [`OptionFlags`].
#[inline(always)]
fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0 | rhs.0)
}
}
impl BitOrAssign for OptionFlags {
/// Add the option flags in one [`OptionFlags`] to another.
#[inline(always)]
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0
}
}
impl Sub for OptionFlags {
type Output = Self;
/// Return the difference of two [`OptionFlags`].
#[inline(always)]
fn sub(self, rhs: Self) -> Self::Output {
Self(self.0 & !rhs.0)
}
}
impl SubAssign for OptionFlags {
/// Remove the option flags in one [`OptionFlags`] from another.
#[inline(always)]
fn sub_assign(&mut self, rhs: Self) {
self.0 &= !rhs.0
}
}
impl BitAnd for OptionFlags {
type Output = Self;
/// Return the intersection of two [`OptionFlags`].
#[inline(always)]
fn bitand(self, rhs: Self) -> Self::Output {
Self(self.0 & !rhs.0)
}
}
impl BitAndAssign for OptionFlags {
/// Keep only the intersection of one [`OptionFlags`] with another.
#[inline(always)]
fn bitand_assign(&mut self, rhs: Self) {
self.0 &= !rhs.0
}
}
/// Option bit-flags for [`AST`][super::AST] nodes.
#[allow(non_snake_case)]
pub mod AST_OPTION_FLAGS {
use super::OptionFlags;
/// _(internals)_ No options for the [`AST`][crate::AST] node.
/// Exported under the `internals` feature only.
pub const AST_OPTION_NONE: OptionFlags = OptionFlags(0b0000_0000);
/// _(internals)_ The [`AST`][crate::AST] node is constant.
/// Exported under the `internals` feature only.
pub const AST_OPTION_CONSTANT: OptionFlags = OptionFlags(0b0000_0001);
/// _(internals)_ The [`AST`][crate::AST] node is public.
/// Exported under the `internals` feature only.
pub const AST_OPTION_PUBLIC: OptionFlags = OptionFlags(0b0000_0010);
/// _(internals)_ The [`AST`][crate::AST] node is in negated mode.
/// Exported under the `internals` feature only.
pub const AST_OPTION_NEGATED: OptionFlags = OptionFlags(0b0000_0100);
/// _(internals)_ The [`AST`][crate::AST] node breaks out of normal control flow.
/// Exported under the `internals` feature only.
pub const AST_OPTION_BREAK_OUT: OptionFlags = OptionFlags(0b0000_1000);
/// _(internals)_ Mask of all options.
/// Exported under the `internals` feature only.
pub(crate) const AST_OPTION_ALL: OptionFlags = OptionFlags(
AST_OPTION_CONSTANT.0 | AST_OPTION_PUBLIC.0 | AST_OPTION_NEGATED.0 | AST_OPTION_BREAK_OUT.0,
);
impl std::fmt::Debug for OptionFlags {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn write_option(
options: &OptionFlags,
f: &mut std::fmt::Formatter<'_>,
num_flags: &mut usize,
flag: OptionFlags,
name: &str,
) -> std::fmt::Result {
if options.contains(flag) {
if *num_flags > 0 {
f.write_str("+")?;
}
f.write_str(name)?;
*num_flags += 1;
}
Ok(())
}
let num_flags = &mut 0;
f.write_str("(")?;
write_option(self, f, num_flags, AST_OPTION_CONSTANT, "Constant")?;
write_option(self, f, num_flags, AST_OPTION_PUBLIC, "Public")?;
write_option(self, f, num_flags, AST_OPTION_NEGATED, "Negated")?;
write_option(self, f, num_flags, AST_OPTION_BREAK_OUT, "Break")?;
f.write_str(")")?;
Ok(())
}
}
}

37
src/ast/ident.rs Normal file
View File

@ -0,0 +1,37 @@
//! Module defining script identifiers.
use crate::{Identifier, Position};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{fmt, hash::Hash};
/// _(internals)_ An identifier containing a name and a [position][Position].
/// Exported under the `internals` feature only.
#[derive(Clone, Eq, PartialEq, Hash)]
pub struct Ident {
/// Identifier name.
pub name: Identifier,
/// Position.
pub pos: Position,
}
impl fmt::Debug for Ident {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.name)?;
self.pos.debug_print(f)
}
}
impl AsRef<str> for Ident {
#[inline(always)]
fn as_ref(&self) -> &str {
self.name.as_ref()
}
}
impl Ident {
#[inline(always)]
pub fn as_str(&self) -> &str {
self.name.as_str()
}
}

22
src/ast/mod.rs Normal file
View File

@ -0,0 +1,22 @@
//! Module defining the AST (abstract syntax tree).
pub mod ast;
pub mod expr;
pub mod flags;
pub mod ident;
pub mod script_fn;
pub mod stmt;
pub use ast::{ASTNode, AST};
pub use expr::{BinaryExpr, CustomExpr, Expr, FnCallExpr, FnCallHashes};
pub use flags::{FnAccess, OptionFlags, AST_OPTION_FLAGS};
pub use ident::Ident;
#[cfg(not(feature = "no_function"))]
pub use script_fn::{ScriptFnDef, ScriptFnMetadata};
pub use stmt::{OpAssignment, Stmt, StmtBlock};
#[cfg(not(feature = "no_float"))]
pub use expr::FloatWrapper;
#[cfg(feature = "no_function")]
pub struct ScriptFnDef;

128
src/ast/script_fn.rs Normal file
View File

@ -0,0 +1,128 @@
//! Module defining script-defined functions.
#![cfg(not(feature = "no_function"))]
use super::{FnAccess, StmtBlock};
use crate::{Identifier, Module, Shared, StaticVec};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{fmt, hash::Hash};
/// _(internals)_ A type containing information on a script-defined function.
/// Exported under the `internals` feature only.
#[derive(Debug, Clone)]
pub struct ScriptFnDef {
/// Function body.
pub body: StmtBlock,
/// Encapsulated running environment, if any.
pub lib: Option<Shared<Module>>,
/// Encapsulated stack of imported modules, if any.
///
/// Not available under `no_module`.
#[cfg(not(feature = "no_module"))]
pub global: Option<Box<[(Identifier, Shared<Module>)]>>,
/// Function name.
pub name: Identifier,
/// Function access mode.
pub access: FnAccess,
/// Names of function parameters.
pub params: StaticVec<Identifier>,
/// _(metadata)_ Function doc-comments (if any).
/// Exported under the `metadata` feature only.
#[cfg(feature = "metadata")]
pub comments: Option<Box<[Box<str>]>>,
}
impl fmt::Display for ScriptFnDef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}{}({})",
match self.access {
FnAccess::Public => "",
FnAccess::Private => "private ",
},
self.name,
self.params
.iter()
.map(|s| s.as_str())
.collect::<StaticVec<_>>()
.join(", ")
)
}
}
/// A type containing the metadata of a script-defined function.
///
/// Not available under `no_function`.
///
/// Created by [`AST::iter_functions`][super::AST::iter_functions].
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
pub struct ScriptFnMetadata<'a> {
/// _(metadata)_ Function doc-comments (if any).
/// Exported under the `metadata` feature only.
///
/// 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.
///
/// Leading white-spaces are stripped, and each string slice always starts with the corresponding
/// doc-comment leader: `///` or `/**`.
#[cfg(feature = "metadata")]
pub comments: Vec<&'a str>,
/// Function access mode.
pub access: FnAccess,
/// Function name.
pub name: &'a str,
/// Function parameters (if any).
pub params: Vec<&'a str>,
}
impl fmt::Display for ScriptFnMetadata<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}{}({})",
match self.access {
FnAccess::Public => "",
FnAccess::Private => "private ",
},
self.name,
self.params
.iter()
.cloned()
.collect::<StaticVec<_>>()
.join(", ")
)
}
}
impl<'a> From<&'a ScriptFnDef> for ScriptFnMetadata<'a> {
#[inline]
fn from(value: &'a ScriptFnDef) -> Self {
Self {
#[cfg(feature = "metadata")]
comments: value
.comments
.as_ref()
.map_or_else(|| Vec::new(), |v| v.iter().map(Box::as_ref).collect()),
access: value.access,
name: &value.name,
params: value.params.iter().map(|s| s.as_str()).collect(),
}
}
}
impl std::cmp::PartialOrd for ScriptFnMetadata<'_> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl std::cmp::Ord for ScriptFnMetadata<'_> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
match self.name.cmp(other.name) {
std::cmp::Ordering::Equal => self.params.len().cmp(&other.params.len()),
cmp => cmp,
}
}
}

667
src/ast/stmt.rs Normal file
View File

@ -0,0 +1,667 @@
//! Module defining script statements.
use super::{ASTNode, Expr, FnCallExpr, Ident, OptionFlags, AST_OPTION_FLAGS};
use crate::engine::KEYWORD_EVAL;
use crate::tokenizer::Token;
use crate::{calc_fn_hash, Position, StaticVec, INT};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{
collections::BTreeMap,
fmt,
hash::Hash,
mem,
ops::{Deref, DerefMut},
};
/// _(internals)_ An op-assignment operator.
/// Exported under the `internals` feature only.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct OpAssignment<'a> {
/// Hash of the op-assignment call.
pub hash_op_assign: u64,
/// Hash of the underlying operator call (for fallback).
pub hash_op: u64,
/// Op-assignment operator.
pub op: &'a str,
}
impl OpAssignment<'_> {
/// Create a new [`OpAssignment`].
///
/// # Panics
///
/// Panics if the name is not an op-assignment operator.
#[must_use]
#[inline(always)]
pub fn new(name: &str) -> Self {
Self::new_from_token(Token::lookup_from_syntax(name).expect("operator"))
}
/// Create a new [`OpAssignment`] from a [`Token`].
///
/// # Panics
///
/// Panics if the token is not an op-assignment operator.
#[must_use]
pub fn new_from_token(op: Token) -> Self {
let op_raw = op
.map_op_assignment()
.expect("op-assignment operator")
.literal_syntax();
Self {
hash_op_assign: calc_fn_hash(op.literal_syntax(), 2),
hash_op: calc_fn_hash(op_raw, 2),
op: op.literal_syntax(),
}
}
}
/// _(internals)_ A scoped block of statements.
/// Exported under the `internals` feature only.
#[derive(Clone, Hash, Default)]
pub struct StmtBlock(StaticVec<Stmt>, Position);
impl StmtBlock {
/// A [`StmtBlock`] that does not exist.
pub const NONE: Self = Self::empty(Position::NONE);
/// Create a new [`StmtBlock`].
#[must_use]
pub fn new(statements: impl IntoIterator<Item = Stmt>, pos: Position) -> Self {
let mut statements: StaticVec<_> = statements.into_iter().collect();
statements.shrink_to_fit();
Self(statements, pos)
}
/// Create an empty [`StmtBlock`].
#[inline(always)]
#[must_use]
pub const fn empty(pos: Position) -> Self {
Self(StaticVec::new_const(), pos)
}
/// Is this statements block empty?
#[inline(always)]
#[must_use]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
/// Number of statements in this statements block.
#[inline(always)]
#[must_use]
pub fn len(&self) -> usize {
self.0.len()
}
/// Get the statements of this statements block.
#[inline(always)]
#[must_use]
pub fn statements(&self) -> &[Stmt] {
&self.0
}
/// Extract the statements.
#[inline(always)]
#[must_use]
pub(crate) fn take_statements(&mut self) -> StaticVec<Stmt> {
mem::take(&mut self.0)
}
/// Get an iterator over the statements of this statements block.
#[inline(always)]
#[must_use]
pub fn iter(&self) -> impl Iterator<Item = &Stmt> {
self.0.iter()
}
/// Get the position (location of the beginning `{`) of this statements block.
#[inline(always)]
#[must_use]
pub const fn position(&self) -> Position {
self.1
}
/// Set the position (location of the beginning `{`) of this statements block.
#[inline(always)]
pub fn set_position(&mut self, pos: Position) {
self.1 = pos;
}
}
impl Deref for StmtBlock {
type Target = StaticVec<Stmt>;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for StmtBlock {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl fmt::Debug for StmtBlock {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Block")?;
fmt::Debug::fmt(&self.0, f)?;
self.1.debug_print(f)
}
}
impl From<Stmt> for StmtBlock {
#[inline]
fn from(stmt: Stmt) -> Self {
match stmt {
Stmt::Block(mut block, pos) => Self(block.iter_mut().map(mem::take).collect(), pos),
Stmt::Noop(pos) => Self(StaticVec::new_const(), pos),
_ => {
let pos = stmt.position();
Self(vec![stmt].into(), pos)
}
}
}
}
impl IntoIterator for StmtBlock {
type Item = Stmt;
type IntoIter = smallvec::IntoIter<[Stmt; 3]>;
#[inline(always)]
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl Extend<Stmt> for StmtBlock {
#[inline(always)]
fn extend<T: IntoIterator<Item = Stmt>>(&mut self, iter: T) {
self.0.extend(iter)
}
}
/// _(internals)_ A statement.
/// Exported under the `internals` feature only.
#[derive(Debug, Clone, Hash)]
pub enum Stmt {
/// No-op.
Noop(Position),
/// `if` expr `{` stmt `}` `else` `{` stmt `}`
If(Expr, Box<(StmtBlock, StmtBlock)>, Position),
/// `switch` expr `{` literal or range or _ `if` condition `=>` stmt `,` ... `}`
///
/// ### Data Structure
///
/// 0) Hash table for (condition, block)
/// 1) Default block
/// 2) List of ranges: (start, end, inclusive, condition, statement)
Switch(
Expr,
Box<(
BTreeMap<u64, Box<(Option<Expr>, StmtBlock)>>,
StmtBlock,
StaticVec<(INT, INT, bool, Option<Expr>, StmtBlock)>,
)>,
Position,
),
/// `while` expr `{` stmt `}` | `loop` `{` stmt `}`
///
/// If the guard expression is [`UNIT`][Expr::Unit], then it is a `loop` statement.
While(Expr, Box<StmtBlock>, Position),
/// `do` `{` stmt `}` `while`|`until` expr
///
/// ### Option Flags
///
/// * [`AST_OPTION_NONE`][AST_OPTION_FLAGS::AST_OPTION_NONE] = `while`
/// * [`AST_OPTION_NEGATED`][AST_OPTION_FLAGS::AST_OPTION_NEGATED] = `until`
Do(Box<StmtBlock>, Expr, OptionFlags, Position),
/// `for` `(` id `,` counter `)` `in` expr `{` stmt `}`
For(Expr, Box<(Ident, Option<Ident>, StmtBlock)>, Position),
/// \[`export`\] `let`|`const` id `=` expr
///
/// ### Option Flags
///
/// * [`AST_OPTION_PUBLIC`][AST_OPTION_FLAGS::AST_OPTION_PUBLIC] = `export`
/// * [`AST_OPTION_CONSTANT`][AST_OPTION_FLAGS::AST_OPTION_CONSTANT] = `const`
Var(Expr, Box<Ident>, OptionFlags, Position),
/// expr op`=` expr
Assignment(Box<(Expr, Option<OpAssignment<'static>>, Expr)>, Position),
/// func `(` expr `,` ... `)`
///
/// Note - this is a duplicate of [`Expr::FnCall`] to cover the very common pattern of a single
/// function call forming one statement.
FnCall(Box<FnCallExpr>, Position),
/// `{` stmt`;` ... `}`
Block(Box<[Stmt]>, Position),
/// `try` `{` stmt; ... `}` `catch` `(` var `)` `{` stmt; ... `}`
TryCatch(Box<(StmtBlock, Option<Ident>, StmtBlock)>, Position),
/// [expression][Expr]
Expr(Expr),
/// `continue`/`break`
///
/// ### Option Flags
///
/// * [`AST_OPTION_NONE`][AST_OPTION_FLAGS::AST_OPTION_NONE] = `continue`
/// * [`AST_OPTION_BREAK_OUT`][AST_OPTION_FLAGS::AST_OPTION_BREAK_OUT] = `break`
BreakLoop(OptionFlags, Position),
/// `return`/`throw`
///
/// ### Option Flags
///
/// * [`AST_OPTION_NONE`][AST_OPTION_FLAGS::AST_OPTION_NONE] = `return`
/// * [`AST_OPTION_BREAK_OUT`][AST_OPTION_FLAGS::AST_OPTION_BREAK_OUT] = `throw`
Return(OptionFlags, Option<Expr>, Position),
/// `import` expr `as` var
///
/// Not available under `no_module`.
#[cfg(not(feature = "no_module"))]
Import(Expr, Option<Box<Ident>>, Position),
/// `export` var `as` var `,` ...
///
/// Not available under `no_module`.
#[cfg(not(feature = "no_module"))]
Export(Box<[(Ident, Ident)]>, Position),
/// Convert a variable to shared.
///
/// Not available under `no_closure`.
///
/// # Notes
///
/// This variant does not map to any language structure. It is currently only used only to
/// convert a normal variable into a shared variable when the variable is _captured_ by a closure.
#[cfg(not(feature = "no_closure"))]
Share(crate::Identifier),
}
impl Default for Stmt {
#[inline(always)]
fn default() -> Self {
Self::Noop(Position::NONE)
}
}
impl From<StmtBlock> for Stmt {
#[inline(always)]
fn from(block: StmtBlock) -> Self {
Self::Block(block.0.into_boxed_slice(), block.1)
}
}
impl Stmt {
/// Is this statement [`Noop`][Stmt::Noop]?
#[inline(always)]
#[must_use]
pub const fn is_noop(&self) -> bool {
matches!(self, Self::Noop(_))
}
/// Get the [position][Position] of this statement.
#[must_use]
pub const fn position(&self) -> Position {
match self {
Self::Noop(pos)
| Self::BreakLoop(_, pos)
| Self::Block(_, pos)
| Self::Assignment(_, pos)
| Self::FnCall(_, pos)
| Self::If(_, _, pos)
| Self::Switch(_, _, pos)
| Self::While(_, _, pos)
| Self::Do(_, _, _, pos)
| Self::For(_, _, pos)
| Self::Return(_, _, pos)
| Self::Var(_, _, _, pos)
| Self::TryCatch(_, pos) => *pos,
Self::Expr(x) => x.position(),
#[cfg(not(feature = "no_module"))]
Self::Import(_, _, pos) => *pos,
#[cfg(not(feature = "no_module"))]
Self::Export(_, pos) => *pos,
#[cfg(not(feature = "no_closure"))]
Self::Share(_) => Position::NONE,
}
}
/// Override the [position][Position] of this statement.
pub fn set_position(&mut self, new_pos: Position) -> &mut Self {
match self {
Self::Noop(pos)
| Self::BreakLoop(_, pos)
| Self::Block(_, pos)
| Self::Assignment(_, pos)
| Self::FnCall(_, pos)
| Self::If(_, _, pos)
| Self::Switch(_, _, pos)
| Self::While(_, _, pos)
| Self::Do(_, _, _, pos)
| Self::For(_, _, pos)
| Self::Return(_, _, pos)
| Self::Var(_, _, _, pos)
| Self::TryCatch(_, pos) => *pos = new_pos,
Self::Expr(x) => {
x.set_position(new_pos);
}
#[cfg(not(feature = "no_module"))]
Self::Import(_, _, pos) => *pos = new_pos,
#[cfg(not(feature = "no_module"))]
Self::Export(_, pos) => *pos = new_pos,
#[cfg(not(feature = "no_closure"))]
Self::Share(_) => (),
}
self
}
/// Does this statement return a value?
#[must_use]
pub const fn returns_value(&self) -> bool {
match self {
Self::If(_, _, _)
| Self::Switch(_, _, _)
| Self::Block(_, _)
| Self::Expr(_)
| Self::FnCall(_, _) => true,
Self::Noop(_)
| Self::While(_, _, _)
| Self::Do(_, _, _, _)
| Self::For(_, _, _)
| Self::TryCatch(_, _) => false,
Self::Var(_, _, _, _)
| Self::Assignment(_, _)
| Self::BreakLoop(_, _)
| Self::Return(_, _, _) => false,
#[cfg(not(feature = "no_module"))]
Self::Import(_, _, _) | Self::Export(_, _) => false,
#[cfg(not(feature = "no_closure"))]
Self::Share(_) => false,
}
}
/// Is this statement self-terminated (i.e. no need for a semicolon terminator)?
#[must_use]
pub const fn is_self_terminated(&self) -> bool {
match self {
Self::If(_, _, _)
| Self::Switch(_, _, _)
| Self::While(_, _, _)
| Self::For(_, _, _)
| Self::Block(_, _)
| Self::TryCatch(_, _) => true,
// A No-op requires a semicolon in order to know it is an empty statement!
Self::Noop(_) => false,
Self::Expr(Expr::Custom(x, _)) if x.is_self_terminated() => true,
Self::Var(_, _, _, _)
| Self::Assignment(_, _)
| Self::Expr(_)
| Self::FnCall(_, _)
| Self::Do(_, _, _, _)
| Self::BreakLoop(_, _)
| Self::Return(_, _, _) => false,
#[cfg(not(feature = "no_module"))]
Self::Import(_, _, _) | Self::Export(_, _) => false,
#[cfg(not(feature = "no_closure"))]
Self::Share(_) => false,
}
}
/// Is this statement _pure_?
///
/// A pure statement has no side effects.
#[must_use]
pub fn is_pure(&self) -> bool {
match self {
Self::Noop(_) => true,
Self::Expr(expr) => expr.is_pure(),
Self::If(condition, x, _) => {
condition.is_pure()
&& (x.0).0.iter().all(Stmt::is_pure)
&& (x.1).0.iter().all(Stmt::is_pure)
}
Self::Switch(expr, x, _) => {
expr.is_pure()
&& x.0.values().all(|block| {
block.0.as_ref().map(Expr::is_pure).unwrap_or(true)
&& (block.1).0.iter().all(Stmt::is_pure)
})
&& (x.2).iter().all(|(_, _, _, condition, stmt)| {
condition.as_ref().map(Expr::is_pure).unwrap_or(true)
&& stmt.0.iter().all(Stmt::is_pure)
})
&& (x.1).0.iter().all(Stmt::is_pure)
}
// Loops that exit can be pure because it can never be infinite.
Self::While(Expr::BoolConstant(false, _), _, _) => true,
Self::Do(body, Expr::BoolConstant(x, _), options, _)
if *x == options.contains(AST_OPTION_FLAGS::AST_OPTION_NEGATED) =>
{
body.iter().all(Stmt::is_pure)
}
// Loops are never pure since they can be infinite - and that's a side effect.
Self::While(_, _, _) | Self::Do(_, _, _, _) => false,
// For loops can be pure because if the iterable is pure, it is finite,
// so infinite loops can never occur.
Self::For(iterable, x, _) => iterable.is_pure() && (x.2).0.iter().all(Stmt::is_pure),
Self::Var(_, _, _, _) | Self::Assignment(_, _) | Self::FnCall(_, _) => false,
Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()),
Self::BreakLoop(_, _) | Self::Return(_, _, _) => false,
Self::TryCatch(x, _) => {
(x.0).0.iter().all(Stmt::is_pure) && (x.2).0.iter().all(Stmt::is_pure)
}
#[cfg(not(feature = "no_module"))]
Self::Import(_, _, _) => false,
#[cfg(not(feature = "no_module"))]
Self::Export(_, _) => false,
#[cfg(not(feature = "no_closure"))]
Self::Share(_) => false,
}
}
/// Does this statement's behavior depend on its containing block?
///
/// A statement that depends on its containing block behaves differently when promoted
/// to an upper block.
///
/// Currently only variable definitions (i.e. `let` and `const`), `import`/`export` statements,
/// and `eval` calls (which may in turn call define variables) fall under this category.
#[inline]
#[must_use]
pub fn is_block_dependent(&self) -> bool {
match self {
Self::Var(_, _, _, _) => true,
Self::Expr(Expr::Stmt(s)) => s.iter().all(Stmt::is_block_dependent),
Self::FnCall(x, _) | Self::Expr(Expr::FnCall(x, _)) => {
x.namespace.is_none() && x.name == KEYWORD_EVAL
}
#[cfg(not(feature = "no_module"))]
Self::Import(_, _, _) | Self::Export(_, _) => true,
_ => false,
}
}
/// Is this statement _pure_ within the containing block?
///
/// An internally pure statement only has side effects that disappear outside the block.
///
/// Currently only variable definitions (i.e. `let` and `const`) and `import`/`export`
/// statements are internally pure, other than pure expressions.
#[inline]
#[must_use]
pub fn is_internally_pure(&self) -> bool {
match self {
Self::Var(expr, _, _, _) => expr.is_pure(),
Self::Expr(Expr::Stmt(s)) => s.iter().all(Stmt::is_internally_pure),
#[cfg(not(feature = "no_module"))]
Self::Import(expr, _, _) => expr.is_pure(),
#[cfg(not(feature = "no_module"))]
Self::Export(_, _) => true,
_ => self.is_pure(),
}
}
/// Does this statement break the current control flow through the containing block?
///
/// Currently this is only true for `return`, `throw`, `break` and `continue`.
///
/// All statements following this statement will essentially be dead code.
#[inline]
#[must_use]
pub const fn is_control_flow_break(&self) -> bool {
match self {
Self::Return(_, _, _) | Self::BreakLoop(_, _) => true,
_ => false,
}
}
/// Recursively walk this statement.
/// Return `false` from the callback to terminate the walk.
pub fn walk<'a>(
&'a self,
path: &mut Vec<ASTNode<'a>>,
on_node: &mut impl FnMut(&[ASTNode]) -> bool,
) -> bool {
// Push the current node onto the path
path.push(self.into());
if !on_node(path) {
return false;
}
match self {
Self::Var(e, _, _, _) => {
if !e.walk(path, on_node) {
return false;
}
}
Self::If(e, x, _) => {
if !e.walk(path, on_node) {
return false;
}
for s in &(x.0).0 {
if !s.walk(path, on_node) {
return false;
}
}
for s in &(x.1).0 {
if !s.walk(path, on_node) {
return false;
}
}
}
Self::Switch(e, x, _) => {
if !e.walk(path, on_node) {
return false;
}
for b in x.0.values() {
if !b.0.as_ref().map(|e| e.walk(path, on_node)).unwrap_or(true) {
return false;
}
for s in &(b.1).0 {
if !s.walk(path, on_node) {
return false;
}
}
}
for (_, _, _, c, stmt) in &x.2 {
if !c.as_ref().map(|e| e.walk(path, on_node)).unwrap_or(true) {
return false;
}
for s in &stmt.0 {
if !s.walk(path, on_node) {
return false;
}
}
}
for s in &(x.1).0 {
if !s.walk(path, on_node) {
return false;
}
}
}
Self::While(e, s, _) | Self::Do(s, e, _, _) => {
if !e.walk(path, on_node) {
return false;
}
for s in &s.0 {
if !s.walk(path, on_node) {
return false;
}
}
}
Self::For(e, x, _) => {
if !e.walk(path, on_node) {
return false;
}
for s in &(x.2).0 {
if !s.walk(path, on_node) {
return false;
}
}
}
Self::Assignment(x, _) => {
if !x.0.walk(path, on_node) {
return false;
}
if !x.2.walk(path, on_node) {
return false;
}
}
Self::FnCall(x, _) => {
for s in &x.args {
if !s.walk(path, on_node) {
return false;
}
}
}
Self::Block(x, _) => {
for s in x.iter() {
if !s.walk(path, on_node) {
return false;
}
}
}
Self::TryCatch(x, _) => {
for s in &(x.0).0 {
if !s.walk(path, on_node) {
return false;
}
}
for s in &(x.2).0 {
if !s.walk(path, on_node) {
return false;
}
}
}
Self::Expr(e) | Self::Return(_, Some(e), _) => {
if !e.walk(path, on_node) {
return false;
}
}
#[cfg(not(feature = "no_module"))]
Self::Import(e, _, _) => {
if !e.walk(path, on_node) {
return false;
}
}
_ => (),
}
path.pop().unwrap();
true
}
}

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,7 @@ use std::prelude::v1::*;
/// Trait that parses arguments to a function call. /// Trait that parses arguments to a function call.
/// ///
/// Any data type can implement this trait in order to pass arguments to a function call. /// Any data type can implement this trait in order to pass arguments to [`Engine::call_fn`][crate::Engine::call_fn].
pub trait FuncArgs { pub trait FuncArgs {
/// Parse function call arguments into a container. /// Parse function call arguments into a container.
/// ///
@ -26,10 +26,10 @@ pub trait FuncArgs {
/// } /// }
/// ///
/// impl FuncArgs for Options { /// impl FuncArgs for Options {
/// fn parse<CONTAINER: Extend<Dynamic>>(self, container: &mut CONTAINER) { /// fn parse<ARGS: Extend<Dynamic>>(self, args: &mut ARGS) {
/// container.extend(std::iter::once(self.foo.into())); /// args.extend(std::iter::once(self.foo.into()));
/// container.extend(std::iter::once(self.bar.into())); /// args.extend(std::iter::once(self.bar.into()));
/// container.extend(std::iter::once(self.baz.into())); /// args.extend(std::iter::once(self.baz.into()));
/// } /// }
/// } /// }
/// ///
@ -55,13 +55,13 @@ pub trait FuncArgs {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
fn parse<CONTAINER: Extend<Dynamic>>(self, container: &mut CONTAINER); fn parse<ARGS: Extend<Dynamic>>(self, args: &mut ARGS);
} }
impl<T: Variant + Clone> FuncArgs for Vec<T> { impl<T: Variant + Clone> FuncArgs for Vec<T> {
#[inline] #[inline]
fn parse<CONTAINER: Extend<Dynamic>>(self, container: &mut CONTAINER) { fn parse<ARGS: Extend<Dynamic>>(self, args: &mut ARGS) {
container.extend(self.into_iter().map(Variant::into_dynamic)); args.extend(self.into_iter().map(Dynamic::from));
} }
} }
@ -73,9 +73,9 @@ macro_rules! impl_args {
{ {
#[inline] #[inline]
#[allow(unused_variables)] #[allow(unused_variables)]
fn parse<CONTAINER: Extend<Dynamic>>(self, container: &mut CONTAINER) { fn parse<ARGS: Extend<Dynamic>>(self, args: &mut ARGS) {
let ($($p,)*) = self; let ($($p,)*) = self;
$(container.extend(Some($p.into_dynamic()));)* $(args.extend(Some(Dynamic::from($p)));)*
} }
} }

View File

@ -1,8 +1,9 @@
//! Built-in implementations for common operators. //! Built-in implementations for common operators.
use super::call::FnCallArgs; use super::call::FnCallArgs;
use super::native::FnBuiltin;
use crate::engine::OP_CONTAINS; use crate::engine::OP_CONTAINS;
use crate::{Dynamic, ImmutableString, NativeCallContext, RhaiResult, INT}; use crate::{Dynamic, ExclusiveRange, ImmutableString, InclusiveRange, INT};
use std::any::TypeId; use std::any::TypeId;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -24,7 +25,12 @@ const BUILTIN: &str = "data type was checked";
#[inline] #[inline]
#[must_use] #[must_use]
fn is_numeric(type_id: TypeId) -> bool { fn is_numeric(type_id: TypeId) -> bool {
let result = type_id == TypeId::of::<u8>() let result = false;
#[cfg(not(feature = "only_i64"))]
#[cfg(not(feature = "only_i32"))]
let result = result
|| type_id == TypeId::of::<u8>()
|| type_id == TypeId::of::<u16>() || type_id == TypeId::of::<u16>()
|| type_id == TypeId::of::<u32>() || type_id == TypeId::of::<u32>()
|| type_id == TypeId::of::<u64>() || type_id == TypeId::of::<u64>()
@ -33,7 +39,10 @@ fn is_numeric(type_id: TypeId) -> bool {
|| type_id == TypeId::of::<i32>() || type_id == TypeId::of::<i32>()
|| type_id == TypeId::of::<i64>(); || type_id == TypeId::of::<i64>();
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #[cfg(not(feature = "only_i64"))]
#[cfg(not(feature = "only_i32"))]
#[cfg(not(target_arch = "wasm32"))]
#[cfg(not(target_arch = "wasm64"))]
let result = result || type_id == TypeId::of::<u128>() || type_id == TypeId::of::<i128>(); let result = result || type_id == TypeId::of::<u128>() || type_id == TypeId::of::<i128>();
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
@ -46,33 +55,13 @@ fn is_numeric(type_id: TypeId) -> bool {
} }
/// Build in common binary operator implementations to avoid the cost of calling a registered function. /// Build in common binary operator implementations to avoid the cost of calling a registered function.
///
/// 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( pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<FnBuiltin> {
op: &str,
x: &Dynamic,
y: &Dynamic,
) -> Option<fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult> {
let type1 = x.type_id(); let type1 = x.type_id();
let type2 = y.type_id(); let type2 = y.type_id();
// One of the operands is a custom type, so it is never built-in
if x.is_variant() || y.is_variant() {
return if is_numeric(type1) && is_numeric(type2) {
// Disallow comparisons between different numeric types
None
} else if type1 != type2 {
// If the types are not the same, default to not compare
match op {
"!=" => Some(|_, _| Ok(Dynamic::TRUE)),
"==" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)),
_ => None,
}
} else {
// Disallow comparisons between the same type
None
};
}
let types_pair = (type1, type2); let types_pair = (type1, type2);
macro_rules! impl_op { macro_rules! impl_op {
@ -108,7 +97,7 @@ pub fn get_builtin_binary_op_fn(
($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().expect(BUILTIN) as $base;
let y = args[1].$yy().expect(BUILTIN) as $base; let y = args[1].$yy().expect(BUILTIN) as $base;
$func(x, y).map(Into::<Dynamic>::into) $func(x, y).map(Into::into)
} }; } };
(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().expect(BUILTIN));
@ -123,7 +112,7 @@ pub fn get_builtin_binary_op_fn(
(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().expect(BUILTIN));
let y = <$base>::from(args[1].$yy().expect(BUILTIN)); let y = <$base>::from(args[1].$yy().expect(BUILTIN));
$func(x, y).map(Into::<Dynamic>::into) $func(x, y).map(Into::into)
} }; } };
} }
@ -266,7 +255,7 @@ pub fn get_builtin_binary_op_fn(
OP_CONTAINS => Some(|_, args| { OP_CONTAINS => Some(|_, args| {
let s = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN); let s = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
let c = args[1].as_char().expect(BUILTIN); let c = args[1].as_char().expect(BUILTIN);
Ok((s.contains(c)).into()) Ok(s.contains(c).into())
}), }),
_ => None, _ => None,
}; };
@ -290,6 +279,30 @@ pub fn get_builtin_binary_op_fn(
}; };
} }
// blob
#[cfg(not(feature = "no_index"))]
if type1 == TypeId::of::<crate::Blob>() {
use crate::Blob;
if type2 == TypeId::of::<INT>() {
return match op {
OP_CONTAINS => Some(|_, args| {
let blob = &*args[0].read_lock::<Blob>().expect(BUILTIN);
let x = (args[1].as_int().expect("`INT`") & 0x000000ff) as u8;
Ok(blob.contains(&x).into())
}),
_ => None,
};
}
if type1 == type2 {
return match op {
"==" => Some(impl_op!(Blob == Blob)),
"!=" => Some(impl_op!(Blob != Blob)),
_ => None,
};
}
}
// map op string // map op string
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
if types_pair == (TypeId::of::<crate::Map>(), TypeId::of::<ImmutableString>()) { if types_pair == (TypeId::of::<crate::Map>(), TypeId::of::<ImmutableString>()) {
@ -301,6 +314,84 @@ pub fn get_builtin_binary_op_fn(
}; };
} }
// Non-compatible ranges
if types_pair
== (
TypeId::of::<ExclusiveRange>(),
TypeId::of::<InclusiveRange>(),
)
|| types_pair
== (
TypeId::of::<InclusiveRange>(),
TypeId::of::<ExclusiveRange>(),
)
{
return match op {
"!=" => Some(|_, _| Ok(Dynamic::TRUE)),
"==" => Some(|_, _| Ok(Dynamic::FALSE)),
_ => None,
};
}
// Handle ranges here because ranges are implemented as custom type
if type1 == TypeId::of::<ExclusiveRange>() {
if type2 == TypeId::of::<INT>() {
return match op {
OP_CONTAINS => Some(|_, args| {
let range = &*args[0].read_lock::<ExclusiveRange>().expect(BUILTIN);
let x = args[1].as_int().expect("`INT`");
Ok(range.contains(&x).into())
}),
_ => None,
};
}
if type1 == type2 {
return match op {
"==" => Some(impl_op!(ExclusiveRange == ExclusiveRange)),
"!=" => Some(impl_op!(ExclusiveRange != ExclusiveRange)),
_ => None,
};
}
}
if type1 == TypeId::of::<InclusiveRange>() {
if type2 == TypeId::of::<INT>() {
return match op {
OP_CONTAINS => Some(|_, args| {
let range = &*args[0].read_lock::<InclusiveRange>().expect(BUILTIN);
let x = args[1].as_int().expect("`INT`");
Ok(range.contains(&x).into())
}),
_ => None,
};
}
if type1 == type2 {
return match op {
"==" => Some(impl_op!(InclusiveRange == InclusiveRange)),
"!=" => Some(impl_op!(InclusiveRange != InclusiveRange)),
_ => None,
};
}
}
// One of the operands is a custom type, so it is never built-in
if x.is_variant() || y.is_variant() {
return if is_numeric(type1) && is_numeric(type2) {
// Disallow comparisons between different numeric types
None
} else if type1 != type2 {
// If the types are not the same, default to not compare
match op {
"!=" => Some(|_, _| Ok(Dynamic::TRUE)),
"==" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)),
_ => None,
}
} else {
// Disallow comparisons between the same type
None
};
}
// Default comparison operators for different types // Default comparison operators for different types
if type2 != type1 { if type2 != type1 {
return match op { return match op {
@ -391,11 +482,7 @@ pub fn get_builtin_binary_op_fn(
">=" => Some(impl_op!(ImmutableString >= ImmutableString)), ">=" => Some(impl_op!(ImmutableString >= ImmutableString)),
"<" => Some(impl_op!(ImmutableString < ImmutableString)), "<" => Some(impl_op!(ImmutableString < ImmutableString)),
"<=" => Some(impl_op!(ImmutableString <= ImmutableString)), "<=" => Some(impl_op!(ImmutableString <= ImmutableString)),
OP_CONTAINS => Some(|_, args| { OP_CONTAINS => Some(impl_op!(ImmutableString.contains(ImmutableString.as_str()))),
let s1 = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
let s2 = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN);
Ok((s1.contains(s2.as_str())).into())
}),
_ => None, _ => None,
}; };
} }
@ -429,12 +516,10 @@ pub fn get_builtin_binary_op_fn(
} }
/// Build in common operator assignment implementations to avoid the cost of calling a registered function. /// Build in common operator assignment implementations to avoid the cost of calling a registered function.
///
/// 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( pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<FnBuiltin> {
op: &str,
x: &Dynamic,
y: &Dynamic,
) -> Option<fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult> {
let type1 = x.type_id(); let type1 = x.type_id();
let type2 = y.type_id(); let type2 = y.type_id();
@ -571,6 +656,48 @@ pub fn get_builtin_op_assignment_fn(
}; };
} }
// array op= any
#[cfg(not(feature = "no_index"))]
if type1 == TypeId::of::<crate::Array>() {
use crate::packages::array_basic::array_functions::*;
use crate::Array;
if type2 == TypeId::of::<crate::Array>() {
return match op {
"+=" => Some(|_, args| {
let array2 = std::mem::take(args[1]).cast::<Array>();
let array1 = &mut *args[0].write_lock::<Array>().expect(BUILTIN);
Ok(append(array1, array2).into())
}),
_ => None,
};
} else {
return match op {
"+=" => Some(|_, args| {
let x = std::mem::take(args[1]);
let array = &mut *args[0].write_lock::<Array>().expect(BUILTIN);
Ok(push(array, x).into())
}),
_ => None,
};
}
}
// blob op= int
#[cfg(not(feature = "no_index"))]
if types_pair == (TypeId::of::<crate::Blob>(), TypeId::of::<INT>()) {
use crate::Blob;
return match op {
"+=" => Some(|_, args| {
let x = (args[1].as_int().expect("`INT`") & 0x000000ff) as u8;
let mut blob = args[0].write_lock::<Blob>().expect(BUILTIN);
Ok(blob.push(x).into())
}),
_ => None,
};
}
// No built-in op-assignments for different types. // No built-in op-assignments for different types.
if type2 != type1 { if type2 != type1 {
return None; return None;
@ -652,5 +779,20 @@ pub fn get_builtin_op_assignment_fn(
}; };
} }
#[cfg(not(feature = "no_index"))]
if type1 == TypeId::of::<crate::Blob>() {
use crate::packages::blob_basic::blob_functions::*;
use crate::Blob;
return match op {
"+=" => Some(|_, args| {
let blob2 = std::mem::take(args[1]).cast::<Blob>();
let blob1 = &mut *args[0].write_lock::<Blob>().expect(BUILTIN);
Ok(append(blob1, blob2).into())
}),
_ => None,
};
}
None None
} }

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,256 @@
//! Module defining the standard Rhai function type.
use super::native::{FnAny, FnPlugin, IteratorFn, SendSync};
use crate::ast::FnAccess;
use crate::plugin::PluginFunction;
use crate::Shared;
use std::fmt;
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
/// A type encapsulating a function callable by Rhai.
#[derive(Clone)]
pub enum CallableFunction {
/// A pure native Rust function with all arguments passed by value.
Pure(Shared<FnAny>),
/// A native Rust object method with the first argument passed by reference,
/// and the rest passed by value.
Method(Shared<FnAny>),
/// An iterator function.
Iterator(IteratorFn),
/// A plugin function,
Plugin(Shared<FnPlugin>),
/// A script-defined function.
///
/// Not available under `no_function`.
#[cfg(not(feature = "no_function"))]
Script(Shared<crate::ast::ScriptFnDef>),
}
impl fmt::Debug for CallableFunction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Pure(_) => write!(f, "NativePureFunction"),
Self::Method(_) => write!(f, "NativeMethod"),
Self::Iterator(_) => write!(f, "NativeIterator"),
Self::Plugin(_) => write!(f, "PluginFunction"),
#[cfg(not(feature = "no_function"))]
Self::Script(fn_def) => fmt::Debug::fmt(fn_def, f),
}
}
}
impl fmt::Display for CallableFunction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Pure(_) => write!(f, "NativePureFunction"),
Self::Method(_) => write!(f, "NativeMethod"),
Self::Iterator(_) => write!(f, "NativeIterator"),
Self::Plugin(_) => write!(f, "PluginFunction"),
#[cfg(not(feature = "no_function"))]
Self::Script(s) => fmt::Display::fmt(s, f),
}
}
}
impl CallableFunction {
/// Is this a pure native Rust function?
#[inline]
#[must_use]
pub fn is_pure(&self) -> bool {
match self {
Self::Pure(_) => true,
Self::Method(_) | Self::Iterator(_) => false,
Self::Plugin(p) => !p.is_method_call(),
#[cfg(not(feature = "no_function"))]
Self::Script(_) => false,
}
}
/// Is this a native Rust method function?
#[inline]
#[must_use]
pub fn is_method(&self) -> bool {
match self {
Self::Method(_) => true,
Self::Pure(_) | Self::Iterator(_) => false,
Self::Plugin(p) => p.is_method_call(),
#[cfg(not(feature = "no_function"))]
Self::Script(_) => false,
}
}
/// Is this an iterator function?
#[inline]
#[must_use]
pub const fn is_iter(&self) -> bool {
match self {
Self::Iterator(_) => true,
Self::Pure(_) | Self::Method(_) | Self::Plugin(_) => false,
#[cfg(not(feature = "no_function"))]
Self::Script(_) => false,
}
}
/// Is this a script-defined function?
#[inline]
#[must_use]
pub const fn is_script(&self) -> bool {
#[cfg(feature = "no_function")]
return false;
#[cfg(not(feature = "no_function"))]
match self {
Self::Script(_) => true,
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Plugin(_) => false,
}
}
/// Is this a plugin function?
#[inline]
#[must_use]
pub const fn is_plugin_fn(&self) -> bool {
match self {
Self::Plugin(_) => true,
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => false,
#[cfg(not(feature = "no_function"))]
Self::Script(_) => false,
}
}
/// Is this a native Rust function?
#[inline]
#[must_use]
pub const fn is_native(&self) -> bool {
#[cfg(feature = "no_function")]
return true;
#[cfg(not(feature = "no_function"))]
match self {
Self::Pure(_) | Self::Method(_) => true,
Self::Plugin(_) => true,
Self::Iterator(_) => true,
Self::Script(_) => false,
}
}
/// Get the access mode.
#[inline]
#[must_use]
pub fn access(&self) -> FnAccess {
#[cfg(feature = "no_function")]
return FnAccess::Public;
#[cfg(not(feature = "no_function"))]
match self {
Self::Plugin(_) => FnAccess::Public,
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => FnAccess::Public,
Self::Script(f) => f.access,
}
}
/// Get a shared reference to a native Rust function.
#[inline]
#[must_use]
pub fn get_native_fn(&self) -> Option<&Shared<FnAny>> {
match self {
Self::Pure(f) | Self::Method(f) => Some(f),
Self::Iterator(_) | Self::Plugin(_) => None,
#[cfg(not(feature = "no_function"))]
Self::Script(_) => None,
}
}
/// Get a shared reference to a script-defined function definition.
///
/// Not available under `no_function`.
#[cfg(not(feature = "no_function"))]
#[inline]
#[must_use]
pub const fn get_script_fn_def(&self) -> Option<&Shared<crate::ast::ScriptFnDef>> {
match self {
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Plugin(_) => None,
Self::Script(f) => Some(f),
}
}
/// Get a reference to an iterator function.
#[inline]
#[must_use]
pub fn get_iter_fn(&self) -> Option<IteratorFn> {
match self {
Self::Iterator(f) => Some(*f),
Self::Pure(_) | Self::Method(_) | Self::Plugin(_) => None,
#[cfg(not(feature = "no_function"))]
Self::Script(_) => None,
}
}
/// Get a shared reference to a plugin function.
#[inline]
#[must_use]
pub fn get_plugin_fn(&self) -> Option<&Shared<FnPlugin>> {
match self {
Self::Plugin(f) => Some(f),
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => None,
#[cfg(not(feature = "no_function"))]
Self::Script(_) => None,
}
}
/// Create a new [`CallableFunction::Pure`].
#[inline(always)]
#[must_use]
pub fn from_pure(func: Box<FnAny>) -> Self {
Self::Pure(func.into())
}
/// Create a new [`CallableFunction::Method`].
#[inline(always)]
#[must_use]
pub fn from_method(func: Box<FnAny>) -> Self {
Self::Method(func.into())
}
/// Create a new [`CallableFunction::Plugin`].
#[inline(always)]
#[must_use]
pub fn from_plugin(func: impl PluginFunction + 'static + SendSync) -> Self {
Self::Plugin((Box::new(func) as Box<FnPlugin>).into())
}
}
impl From<IteratorFn> for CallableFunction {
#[inline(always)]
fn from(func: IteratorFn) -> Self {
Self::Iterator(func)
}
}
#[cfg(not(feature = "no_function"))]
impl From<crate::ast::ScriptFnDef> for CallableFunction {
#[inline(always)]
fn from(_func: crate::ast::ScriptFnDef) -> Self {
Self::Script(_func.into())
}
}
#[cfg(not(feature = "no_function"))]
impl From<Shared<crate::ast::ScriptFnDef>> for CallableFunction {
#[inline(always)]
fn from(_func: Shared<crate::ast::ScriptFnDef>) -> Self {
Self::Script(_func)
}
}
impl<T: PluginFunction + 'static + SendSync> From<T> for CallableFunction {
#[inline(always)]
fn from(func: T) -> Self {
Self::from_plugin(func)
}
}
impl From<Shared<FnPlugin>> for CallableFunction {
#[inline(always)]
fn from(func: Shared<FnPlugin>) -> Self {
Self::Plugin(func)
}
}

View File

@ -3,8 +3,9 @@
#![cfg(not(feature = "no_function"))] #![cfg(not(feature = "no_function"))]
#![allow(non_snake_case)] #![allow(non_snake_case)]
use crate::parser::ParseResult;
use crate::types::dynamic::Variant; use crate::types::dynamic::Variant;
use crate::{Engine, EvalAltResult, ParseError, Scope, SmartString, AST}; use crate::{Engine, RhaiResultOf, Scope, SmartString, AST};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -77,11 +78,7 @@ pub trait Func<ARGS, RET> {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
fn create_from_script( fn create_from_script(self, script: &str, entry_point: &str) -> ParseResult<Self::Output>;
self,
script: &str,
entry_point: &str,
) -> Result<Self::Output, ParseError>;
} }
macro_rules! def_anonymous_fn { macro_rules! def_anonymous_fn {
@ -92,9 +89,9 @@ 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),*) -> Result<RET, Box<EvalAltResult>> + 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),*) -> Result<RET, Box<EvalAltResult>>>; 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 {
@ -103,7 +100,7 @@ macro_rules! def_anonymous_fn {
} }
#[inline] #[inline]
fn create_from_script(self, script: &str, entry_point: &str) -> Result<Self::Output, ParseError> { fn create_from_script(self, script: &str, entry_point: &str) -> ParseResult<Self::Output> {
let ast = self.compile(script)?; let ast = self.compile(script)?;
Ok(Func::<($($par,)*), RET>::create_from_ast(self, ast, entry_point)) Ok(Func::<($($par,)*), RET>::create_from_ast(self, ast, entry_point))
} }

View File

@ -87,10 +87,7 @@ pub fn get_hasher() -> ahash::AHasher {
/// The first module name is skipped. Hashing starts from the _second_ module in the chain. /// The first module name is skipped. Hashing starts from the _second_ module in the chain.
#[inline] #[inline]
#[must_use] #[must_use]
pub fn calc_qualified_var_hash<'a>( pub fn calc_qualified_var_hash<'a>(modules: impl Iterator<Item = &'a str>, var_name: &str) -> u64 {
modules: impl Iterator<Item = impl AsRef<str> + 'a>,
var_name: impl AsRef<str>,
) -> u64 {
let s = &mut get_hasher(); let s = &mut get_hasher();
// We always skip the first module // We always skip the first module
@ -98,9 +95,9 @@ pub fn calc_qualified_var_hash<'a>(
modules modules
.inspect(|_| len += 1) .inspect(|_| len += 1)
.skip(1) .skip(1)
.for_each(|m| m.as_ref().hash(s)); .for_each(|m| m.hash(s));
len.hash(s); len.hash(s);
var_name.as_ref().hash(s); var_name.hash(s);
match s.finish() { match s.finish() {
0 => ALT_ZERO_HASH, 0 => ALT_ZERO_HASH,
@ -123,9 +120,9 @@ pub fn calc_qualified_var_hash<'a>(
/// The first module name is skipped. Hashing starts from the _second_ module in the chain. /// The first module name is skipped. Hashing starts from the _second_ module in the chain.
#[inline] #[inline]
#[must_use] #[must_use]
pub fn calc_qualified_fn_hash( pub fn calc_qualified_fn_hash<'a>(
modules: impl Iterator<Item = impl AsRef<str>>, modules: impl Iterator<Item = &'a str>,
fn_name: impl AsRef<str>, fn_name: &str,
num: usize, num: usize,
) -> u64 { ) -> u64 {
let s = &mut get_hasher(); let s = &mut get_hasher();
@ -135,9 +132,9 @@ pub fn calc_qualified_fn_hash(
modules modules
.inspect(|_| len += 1) .inspect(|_| len += 1)
.skip(1) .skip(1)
.for_each(|m| m.as_ref().hash(s)); .for_each(|m| m.hash(s));
len.hash(s); len.hash(s);
fn_name.as_ref().hash(s); fn_name.hash(s);
num.hash(s); num.hash(s);
match s.finish() { match s.finish() {
@ -156,8 +153,8 @@ pub fn calc_qualified_fn_hash(
/// If the hash happens to be zero, it is mapped to `DEFAULT_HASH`. /// If the hash happens to be zero, it is mapped to `DEFAULT_HASH`.
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn calc_fn_hash(fn_name: impl AsRef<str>, num: usize) -> u64 { pub fn calc_fn_hash(fn_name: &str, num: usize) -> u64 {
calc_qualified_fn_hash(empty::<&str>(), fn_name, num) calc_qualified_fn_hash(empty(), fn_name, num)
} }
/// Calculate a non-zero [`u64`] hash key from a list of parameter types. /// Calculate a non-zero [`u64`] hash key from a list of parameter types.

View File

@ -3,6 +3,7 @@
pub mod args; pub mod args;
pub mod builtin; pub mod builtin;
pub mod call; pub mod call;
pub mod callable_function;
pub mod func; pub mod func;
pub mod hashing; pub mod hashing;
pub mod native; pub mod native;
@ -13,6 +14,7 @@ pub mod script;
pub use args::FuncArgs; pub use args::FuncArgs;
pub use builtin::{get_builtin_binary_op_fn, get_builtin_op_assignment_fn}; pub use builtin::{get_builtin_binary_op_fn, get_builtin_op_assignment_fn};
pub use call::FnCallArgs; pub use call::FnCallArgs;
pub use callable_function::CallableFunction;
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
pub use func::Func; pub use func::Func;
pub use hashing::{ pub use hashing::{
@ -20,8 +22,8 @@ pub use hashing::{
combine_hashes, get_hasher, combine_hashes, get_hasher,
}; };
pub use native::{ pub use native::{
shared_make_mut, shared_take, shared_take_or_clone, shared_try_take, shared_write_lock, shared_make_mut, shared_take, shared_take_or_clone, shared_try_take, shared_write_lock, FnAny,
CallableFunction, FnAny, FnPlugin, IteratorFn, Locked, NativeCallContext, SendSync, Shared, FnPlugin, IteratorFn, Locked, NativeCallContext, SendSync, Shared,
}; };
pub use plugin::PluginFunction; pub use plugin::PluginFunction;
pub use register::RegisterNativeFunction; pub use register::RegisterNativeFunction;

View File

@ -1,14 +1,16 @@
//! Module defining interfaces to native-Rust functions. //! Module defining interfaces to native-Rust functions.
use super::call::FnCallArgs; use super::call::FnCallArgs;
use crate::ast::{FnAccess, FnCallHashes}; use crate::ast::FnCallHashes;
use crate::engine::{EvalState, Imports}; use crate::engine::{EvalState, GlobalRuntimeState};
use crate::plugin::PluginFunction; use crate::plugin::PluginFunction;
use crate::tokenizer::{Token, TokenizeState}; use crate::tokenizer::{Token, TokenizeState};
use crate::types::dynamic::Variant;
use crate::{ use crate::{
calc_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, Module, Position, RhaiResult, calc_fn_hash, Dynamic, Engine, EvalContext, FuncArgs, Module, Position, RhaiResult,
RhaiResultOf, StaticVec, ERR,
}; };
use std::fmt; use std::any::type_name;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -56,11 +58,17 @@ pub type LockGuard<'a, T> = std::sync::RwLockWriteGuard<'a, T>;
/// Context of a native Rust function call. /// Context of a native Rust function call.
#[derive(Debug)] #[derive(Debug)]
pub struct NativeCallContext<'a> { pub struct NativeCallContext<'a> {
/// The current [`Engine`].
engine: &'a Engine, engine: &'a Engine,
/// Name of function called.
fn_name: &'a str, fn_name: &'a str,
/// Function source, if any.
source: Option<&'a str>, source: Option<&'a str>,
mods: Option<&'a Imports>, /// The current [`GlobalRuntimeState`], if any.
global: Option<&'a GlobalRuntimeState>,
/// The current stack of loaded [modules][Module].
lib: &'a [&'a Module], lib: &'a [&'a Module],
/// [Position] of the function call.
pos: Position, pos: Position,
} }
@ -69,7 +77,7 @@ impl<'a, M: AsRef<[&'a Module]> + ?Sized, S: AsRef<str> + 'a + ?Sized>
&'a Engine, &'a Engine,
&'a S, &'a S,
Option<&'a S>, Option<&'a S>,
&'a Imports, &'a GlobalRuntimeState,
&'a M, &'a M,
Position, Position,
)> for NativeCallContext<'a> )> for NativeCallContext<'a>
@ -80,7 +88,7 @@ impl<'a, M: AsRef<[&'a Module]> + ?Sized, S: AsRef<str> + 'a + ?Sized>
&'a Engine, &'a Engine,
&'a S, &'a S,
Option<&'a S>, Option<&'a S>,
&'a Imports, &'a GlobalRuntimeState,
&'a M, &'a M,
Position, Position,
), ),
@ -88,8 +96,8 @@ impl<'a, M: AsRef<[&'a Module]> + ?Sized, S: AsRef<str> + 'a + ?Sized>
Self { Self {
engine: value.0, engine: value.0,
fn_name: value.1.as_ref(), fn_name: value.1.as_ref(),
source: value.2.map(|v| v.as_ref()), source: value.2.map(S::as_ref),
mods: Some(value.3), global: Some(value.3),
lib: value.4.as_ref(), lib: value.4.as_ref(),
pos: value.5, pos: value.5,
} }
@ -105,7 +113,7 @@ impl<'a, M: AsRef<[&'a Module]> + ?Sized, S: AsRef<str> + 'a + ?Sized>
engine: value.0, engine: value.0,
fn_name: value.1.as_ref(), fn_name: value.1.as_ref(),
source: None, source: None,
mods: None, global: None,
lib: value.2.as_ref(), lib: value.2.as_ref(),
pos: Position::NONE, pos: Position::NONE,
} }
@ -130,7 +138,7 @@ impl<'a> NativeCallContext<'a> {
engine, engine,
fn_name: fn_name.as_ref(), fn_name: fn_name.as_ref(),
source: None, source: None,
mods: None, global: None,
lib, lib,
pos: Position::NONE, pos: Position::NONE,
} }
@ -147,15 +155,15 @@ impl<'a> NativeCallContext<'a> {
engine: &'a Engine, engine: &'a Engine,
fn_name: &'a (impl AsRef<str> + 'a + ?Sized), fn_name: &'a (impl AsRef<str> + 'a + ?Sized),
source: Option<&'a (impl AsRef<str> + 'a + ?Sized)>, source: Option<&'a (impl AsRef<str> + 'a + ?Sized)>,
imports: &'a Imports, global: &'a GlobalRuntimeState,
lib: &'a [&Module], lib: &'a [&Module],
pos: Position, pos: Position,
) -> Self { ) -> Self {
Self { Self {
engine, engine,
fn_name: fn_name.as_ref(), fn_name: fn_name.as_ref(),
source: source.map(|v| v.as_ref()), source: source.map(<_>::as_ref),
mods: Some(imports), global: Some(global),
lib, lib,
pos, pos,
} }
@ -172,7 +180,7 @@ impl<'a> NativeCallContext<'a> {
pub const fn fn_name(&self) -> &str { pub const fn fn_name(&self) -> &str {
self.fn_name self.fn_name
} }
/// [Position][`Position`] of the function call. /// [Position] of the function call.
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub const fn position(&self) -> Position { pub const fn position(&self) -> Position {
@ -184,24 +192,25 @@ impl<'a> NativeCallContext<'a> {
pub const fn source(&self) -> Option<&str> { pub const fn source(&self) -> Option<&str> {
self.source self.source
} }
/// Get an iterator over the current set of modules imported via `import` statements. /// Get an iterator over the current set of modules imported via `import` statements
/// in reverse order.
/// ///
/// Not available under `no_module`. /// Not available under `no_module`.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[inline] #[inline]
pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &Module)> { pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &Module)> {
self.mods.iter().flat_map(|&m| m.iter()) self.global.iter().flat_map(|&m| m.iter_modules())
} }
/// Get an iterator over the current set of modules imported via `import` statements. /// Get an iterator over the current set of modules imported via `import` statements in reverse order.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[allow(dead_code)] #[allow(dead_code)]
#[inline] #[inline]
pub(crate) fn iter_imports_raw( pub(crate) fn iter_imports_raw(
&self, &self,
) -> impl Iterator<Item = (&crate::Identifier, &Shared<Module>)> { ) -> impl Iterator<Item = (&crate::Identifier, &Shared<Module>)> {
self.mods.iter().flat_map(|&m| m.iter_raw()) self.global.iter().flat_map(|&m| m.iter_modules_raw())
} }
/// _(internals)_ The current set of modules imported via `import` statements. /// _(internals)_ The current [`GlobalRuntimeState`], if any.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
/// ///
/// Not available under `no_module`. /// Not available under `no_module`.
@ -209,13 +218,14 @@ impl<'a> NativeCallContext<'a> {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub const fn imports(&self) -> Option<&Imports> { pub const fn global_runtime_state(&self) -> Option<&GlobalRuntimeState> {
self.mods self.global
} }
/// Get an iterator over the namespaces containing definitions of all script-defined functions. /// Get an iterator over the namespaces containing definitions of all script-defined functions
/// in reverse order.
#[inline] #[inline]
pub fn iter_namespaces(&self) -> impl Iterator<Item = &Module> { pub fn iter_namespaces(&self) -> impl Iterator<Item = &Module> {
self.lib.iter().cloned() self.lib.iter().rev().cloned()
} }
/// _(internals)_ The current set of namespaces containing definitions of all script-defined functions. /// _(internals)_ The current set of namespaces containing definitions of all script-defined functions.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
@ -225,6 +235,31 @@ impl<'a> NativeCallContext<'a> {
pub const fn namespaces(&self) -> &[&Module] { pub const fn namespaces(&self) -> &[&Module] {
self.lib self.lib
} }
/// Call a function inside the call context with the provided arguments.
#[inline]
pub fn call_fn<T: Variant + Clone>(
&self,
fn_name: impl AsRef<str>,
args: impl FuncArgs,
) -> RhaiResultOf<T> {
let mut arg_values = StaticVec::new_const();
args.parse(&mut arg_values);
let mut args: StaticVec<_> = arg_values.iter_mut().collect();
let result = self.call_fn_raw(fn_name, false, false, &mut args)?;
let typ = self.engine().map_type_name(result.type_name());
result.try_cast().ok_or_else(|| {
ERR::ErrorMismatchOutputType(
self.engine().map_type_name(type_name::<T>()).into(),
typ.into(),
Position::NONE,
)
.into()
})
}
/// Call a function inside the call context. /// Call a function inside the call context.
/// ///
/// If `is_method_call` is [`true`], the first argument is assumed to be the `this` pointer for /// If `is_method_call` is [`true`], the first argument is assumed to be the `this` pointer for
@ -250,22 +285,26 @@ impl<'a> NativeCallContext<'a> {
is_ref_mut: bool, is_ref_mut: bool,
is_method_call: bool, is_method_call: bool,
args: &mut [&mut Dynamic], args: &mut [&mut Dynamic],
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> RhaiResult {
let fn_name = fn_name.as_ref(); let fn_name = fn_name.as_ref();
let len = args.len();
let hash = if is_method_call { let hash = if is_method_call {
FnCallHashes::from_all( FnCallHashes::from_all(
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
calc_fn_hash(fn_name, args.len() - 1), calc_fn_hash(fn_name, len - 1),
calc_fn_hash(fn_name, args.len()), calc_fn_hash(fn_name, len),
) )
} else { } else {
calc_fn_hash(fn_name, args.len()).into() calc_fn_hash(fn_name, len).into()
}; };
self.engine() self.engine()
.exec_fn_call( .exec_fn_call(
&mut self.mods.cloned().unwrap_or_else(|| Imports::new()), &mut self
.global
.cloned()
.unwrap_or_else(|| GlobalRuntimeState::new()),
&mut EvalState::new(), &mut EvalState::new(),
self.lib, self.lib,
fn_name, fn_name,
@ -336,6 +375,9 @@ pub type FnAny = dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult;
#[cfg(feature = "sync")] #[cfg(feature = "sync")]
pub type FnAny = dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult + Send + Sync; pub type FnAny = dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult + Send + Sync;
/// A trail object for built-in functions.
pub type FnBuiltin = fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult;
/// A standard function that gets an iterator from a type. /// A standard function that gets an iterator from a type.
pub type IteratorFn = fn(Dynamic) -> Box<dyn Iterator<Item = Dynamic>>; pub type IteratorFn = fn(Dynamic) -> Box<dyn Iterator<Item = Dynamic>>;
@ -378,253 +420,8 @@ pub type OnParseTokenCallback =
/// A standard callback function for variable access. /// A standard callback function for variable access.
#[cfg(not(feature = "sync"))] #[cfg(not(feature = "sync"))]
pub type OnVarCallback = pub type OnVarCallback =
Box<dyn Fn(&str, usize, &EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>> + 'static>; Box<dyn Fn(&str, usize, &EvalContext) -> RhaiResultOf<Option<Dynamic>> + 'static>;
/// A standard callback function for variable access. /// A standard callback function for variable access.
#[cfg(feature = "sync")] #[cfg(feature = "sync")]
pub type OnVarCallback = Box< pub type OnVarCallback =
dyn Fn(&str, usize, &EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>> Box<dyn Fn(&str, usize, &EvalContext) -> RhaiResultOf<Option<Dynamic>> + Send + Sync + 'static>;
+ Send
+ Sync
+ 'static,
>;
/// A type encapsulating a function callable by Rhai.
#[derive(Clone)]
pub enum CallableFunction {
/// A pure native Rust function with all arguments passed by value.
Pure(Shared<FnAny>),
/// A native Rust object method with the first argument passed by reference,
/// and the rest passed by value.
Method(Shared<FnAny>),
/// An iterator function.
Iterator(IteratorFn),
/// A plugin function,
Plugin(Shared<FnPlugin>),
/// A script-defined function.
///
/// Not available under `no_function`.
#[cfg(not(feature = "no_function"))]
Script(Shared<crate::ast::ScriptFnDef>),
}
impl fmt::Debug for CallableFunction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Pure(_) => write!(f, "NativePureFunction"),
Self::Method(_) => write!(f, "NativeMethod"),
Self::Iterator(_) => write!(f, "NativeIterator"),
Self::Plugin(_) => write!(f, "PluginFunction"),
#[cfg(not(feature = "no_function"))]
Self::Script(fn_def) => fmt::Debug::fmt(fn_def, f),
}
}
}
impl fmt::Display for CallableFunction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Pure(_) => write!(f, "NativePureFunction"),
Self::Method(_) => write!(f, "NativeMethod"),
Self::Iterator(_) => write!(f, "NativeIterator"),
Self::Plugin(_) => write!(f, "PluginFunction"),
#[cfg(not(feature = "no_function"))]
CallableFunction::Script(s) => fmt::Display::fmt(s, f),
}
}
}
impl CallableFunction {
/// Is this a pure native Rust function?
#[inline]
#[must_use]
pub fn is_pure(&self) -> bool {
match self {
Self::Pure(_) => true,
Self::Method(_) | Self::Iterator(_) => false,
Self::Plugin(p) => !p.is_method_call(),
#[cfg(not(feature = "no_function"))]
Self::Script(_) => false,
}
}
/// Is this a native Rust method function?
#[inline]
#[must_use]
pub fn is_method(&self) -> bool {
match self {
Self::Method(_) => true,
Self::Pure(_) | Self::Iterator(_) => false,
Self::Plugin(p) => p.is_method_call(),
#[cfg(not(feature = "no_function"))]
Self::Script(_) => false,
}
}
/// Is this an iterator function?
#[inline]
#[must_use]
pub const fn is_iter(&self) -> bool {
match self {
Self::Iterator(_) => true,
Self::Pure(_) | Self::Method(_) | Self::Plugin(_) => false,
#[cfg(not(feature = "no_function"))]
Self::Script(_) => false,
}
}
/// Is this a Rhai-scripted function?
#[inline]
#[must_use]
pub const fn is_script(&self) -> bool {
match self {
#[cfg(not(feature = "no_function"))]
Self::Script(_) => true,
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Plugin(_) => false,
}
}
/// Is this a plugin function?
#[inline]
#[must_use]
pub const fn is_plugin_fn(&self) -> bool {
match self {
Self::Plugin(_) => true,
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => false,
#[cfg(not(feature = "no_function"))]
Self::Script(_) => false,
}
}
/// Is this a native Rust function?
#[inline]
#[must_use]
pub const fn is_native(&self) -> bool {
match self {
Self::Pure(_) | Self::Method(_) => true,
Self::Plugin(_) => true,
Self::Iterator(_) => true,
#[cfg(not(feature = "no_function"))]
Self::Script(_) => false,
}
}
/// Get the access mode.
#[inline]
#[must_use]
pub fn access(&self) -> FnAccess {
match self {
Self::Plugin(_) => FnAccess::Public,
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => FnAccess::Public,
#[cfg(not(feature = "no_function"))]
Self::Script(f) => f.access,
}
}
/// Get a shared reference to a native Rust function.
#[inline]
#[must_use]
pub fn get_native_fn(&self) -> Option<&Shared<FnAny>> {
match self {
Self::Pure(f) | Self::Method(f) => Some(f),
Self::Iterator(_) | Self::Plugin(_) => None,
#[cfg(not(feature = "no_function"))]
Self::Script(_) => None,
}
}
/// Get a shared reference to a script-defined function definition.
///
/// Not available under `no_function`.
#[cfg(not(feature = "no_function"))]
#[inline]
#[must_use]
pub const fn get_script_fn_def(&self) -> Option<&Shared<crate::ast::ScriptFnDef>> {
match self {
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Plugin(_) => None,
Self::Script(f) => Some(f),
}
}
/// Get a reference to an iterator function.
#[inline]
#[must_use]
pub fn get_iter_fn(&self) -> Option<IteratorFn> {
match self {
Self::Iterator(f) => Some(*f),
Self::Pure(_) | Self::Method(_) | Self::Plugin(_) => None,
#[cfg(not(feature = "no_function"))]
Self::Script(_) => None,
}
}
/// Get a shared reference to a plugin function.
#[inline]
#[must_use]
pub fn get_plugin_fn(&self) -> Option<&Shared<FnPlugin>> {
match self {
Self::Plugin(f) => Some(f),
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => None,
#[cfg(not(feature = "no_function"))]
Self::Script(_) => None,
}
}
/// Create a new [`CallableFunction::Pure`].
#[inline(always)]
#[must_use]
pub fn from_pure(func: Box<FnAny>) -> Self {
Self::Pure(func.into())
}
/// Create a new [`CallableFunction::Method`].
#[inline(always)]
#[must_use]
pub fn from_method(func: Box<FnAny>) -> Self {
Self::Method(func.into())
}
/// Create a new [`CallableFunction::Plugin`].
#[inline(always)]
#[must_use]
pub fn from_plugin(func: impl PluginFunction + 'static + SendSync) -> Self {
Self::Plugin((Box::new(func) as Box<FnPlugin>).into())
}
}
impl From<IteratorFn> for CallableFunction {
#[inline(always)]
fn from(func: IteratorFn) -> Self {
Self::Iterator(func)
}
}
#[cfg(not(feature = "no_function"))]
impl From<crate::ast::ScriptFnDef> for CallableFunction {
#[inline(always)]
fn from(_func: crate::ast::ScriptFnDef) -> Self {
Self::Script(_func.into())
}
}
#[cfg(not(feature = "no_function"))]
impl From<Shared<crate::ast::ScriptFnDef>> for CallableFunction {
#[inline(always)]
fn from(_func: Shared<crate::ast::ScriptFnDef>) -> Self {
Self::Script(_func)
}
}
impl<T: PluginFunction + 'static + SendSync> From<T> for CallableFunction {
#[inline(always)]
fn from(func: T) -> Self {
Self::from_plugin(func)
}
}
impl From<Shared<FnPlugin>> for CallableFunction {
#[inline(always)]
fn from(func: Shared<FnPlugin>) -> Self {
Self::Plugin(func)
}
}

View File

@ -10,7 +10,7 @@ pub use crate::{
use std::prelude::v1::*; use std::prelude::v1::*;
pub use std::{any::TypeId, mem}; pub use std::{any::TypeId, mem};
pub type RhaiResult = Result<Dynamic, Box<EvalAltResult>>; pub type RhaiResult = crate::RhaiResult;
#[cfg(not(features = "no_module"))] #[cfg(not(features = "no_module"))]
pub use rhai_codegen::*; pub use rhai_codegen::*;

View File

@ -2,12 +2,13 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
use crate::func::call::FnCallArgs; use super::call::FnCallArgs;
use crate::func::native::{CallableFunction, FnAny, SendSync}; use super::callable_function::CallableFunction;
use crate::r#unsafe::unsafe_try_cast; use super::native::{FnAny, SendSync};
use crate::r#unsafe::unsafe_cast;
use crate::tokenizer::Position; use crate::tokenizer::Position;
use crate::types::dynamic::{DynamicWriteLock, Variant}; use crate::types::dynamic::{DynamicWriteLock, Variant};
use crate::{Dynamic, EvalAltResult, NativeCallContext}; use crate::{Dynamic, NativeCallContext, RhaiResultOf, ERR};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
use std::{any::TypeId, mem}; use std::{any::TypeId, mem};
@ -49,8 +50,7 @@ pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T {
ref_t.clone() ref_t.clone()
} else if TypeId::of::<T>() == TypeId::of::<String>() { } else if TypeId::of::<T>() == TypeId::of::<String>() {
// If T is `String`, data must be `ImmutableString`, so map directly to it // If T is `String`, data must be `ImmutableString`, so map directly to it
let value = mem::take(data).into_string().expect("`ImmutableString`"); unsafe_cast(mem::take(data).into_string().expect("`ImmutableString`"))
unsafe_try_cast(value).expect("checked")
} else { } else {
// We consume the argument and then replace it with () - the argument is not supposed to be used again. // We consume the argument and then replace it with () - the argument is not supposed to be used again.
// This way, we avoid having to clone the argument again, because it is already a clone when passed here. // This way, we avoid having to clone the argument again, because it is already a clone when passed here.
@ -123,7 +123,7 @@ macro_rules! def_register {
#[inline(always)] fn into_callable_function(self) -> CallableFunction { #[inline(always)] fn into_callable_function(self) -> CallableFunction {
CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| { CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
if args.len() == 2 && args[0].is_read_only() && is_setter(ctx.fn_name()) { if args.len() == 2 && args[0].is_read_only() && is_setter(ctx.fn_name()) {
return Err(EvalAltResult::ErrorAssignmentToConstant(String::new(), Position::NONE).into()); return Err(ERR::ErrorAssignmentToConstant(String::new(), Position::NONE).into());
} }
// The arguments are assumed to be of the correct number and types! // The arguments are assumed to be of the correct number and types!
@ -134,7 +134,7 @@ macro_rules! def_register {
let r = self($($arg),*); let r = self($($arg),*);
// Map the result // Map the result
Ok(r.into_dynamic()) Ok(Dynamic::from(r))
}) as Box<FnAny>) }) as Box<FnAny>)
} }
} }
@ -151,7 +151,7 @@ macro_rules! def_register {
#[inline(always)] fn into_callable_function(self) -> CallableFunction { #[inline(always)] fn into_callable_function(self) -> CallableFunction {
CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| { CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
if args.len() == 2 && args[0].is_read_only() && is_setter(ctx.fn_name()) { if args.len() == 2 && args[0].is_read_only() && is_setter(ctx.fn_name()) {
return Err(EvalAltResult::ErrorAssignmentToConstant(String::new(), Position::NONE).into()); return Err(ERR::ErrorAssignmentToConstant(String::new(), Position::NONE).into());
} }
// The arguments are assumed to be of the correct number and types! // The arguments are assumed to be of the correct number and types!
@ -162,24 +162,24 @@ macro_rules! def_register {
let r = self(ctx, $($arg),*); let r = self(ctx, $($arg),*);
// Map the result // Map the result
Ok(r.into_dynamic()) Ok(Dynamic::from(r))
}) as Box<FnAny>) }) as Box<FnAny>)
} }
} }
impl< impl<
FN: Fn($($param),*) -> Result<RET, Box<EvalAltResult>> + SendSync + 'static, FN: Fn($($param),*) -> RhaiResultOf<RET> + SendSync + 'static,
$($par: Variant + Clone,)* $($par: Variant + Clone,)*
RET: Variant + Clone RET: Variant + Clone
> RegisterNativeFunction<($($mark,)*), Result<RET, Box<EvalAltResult>>> for FN { > RegisterNativeFunction<($($mark,)*), RhaiResultOf<RET>> for FN {
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() } #[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(std::any::type_name::<$par>()),*].into_boxed_slice() } #[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(std::any::type_name::<$par>()),*].into_boxed_slice() }
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<Result<RET, Box<EvalAltResult>>>() } #[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<RhaiResultOf<RET>>() }
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { std::any::type_name::<Result<RET, Box<EvalAltResult>>>() } #[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { std::any::type_name::<RhaiResultOf<RET>>() }
#[inline(always)] fn into_callable_function(self) -> CallableFunction { #[inline(always)] fn into_callable_function(self) -> CallableFunction {
CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| { CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
if args.len() == 2 && args[0].is_read_only() && is_setter(ctx.fn_name()) { if args.len() == 2 && args[0].is_read_only() && is_setter(ctx.fn_name()) {
return Err(EvalAltResult::ErrorAssignmentToConstant(String::new(), Position::NONE).into()); return Err(ERR::ErrorAssignmentToConstant(String::new(), Position::NONE).into());
} }
// The arguments are assumed to be of the correct number and types! // The arguments are assumed to be of the correct number and types!
@ -193,18 +193,18 @@ macro_rules! def_register {
} }
impl< impl<
FN: for<'a> Fn(NativeCallContext<'a>, $($param),*) -> Result<RET, Box<EvalAltResult>> + 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<(NativeCallContext<'static>, $($mark,)*), Result<RET, Box<EvalAltResult>>> for FN { > RegisterNativeFunction<(NativeCallContext<'static>, $($mark,)*), RhaiResultOf<RET>> for FN {
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() } #[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(std::any::type_name::<$par>()),*].into_boxed_slice() } #[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(std::any::type_name::<$par>()),*].into_boxed_slice() }
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<Result<RET, Box<EvalAltResult>>>() } #[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<RhaiResultOf<RET>>() }
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { std::any::type_name::<Result<RET, Box<EvalAltResult>>>() } #[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { std::any::type_name::<RhaiResultOf<RET>>() }
#[inline(always)] fn into_callable_function(self) -> CallableFunction { #[inline(always)] fn into_callable_function(self) -> CallableFunction {
CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| { CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
if args.len() == 2 && args[0].is_read_only() && is_setter(ctx.fn_name()) { if args.len() == 2 && args[0].is_read_only() && is_setter(ctx.fn_name()) {
return Err(EvalAltResult::ErrorAssignmentToConstant(String::new(), Position::NONE).into()); return Err(ERR::ErrorAssignmentToConstant(String::new(), Position::NONE).into());
} }
// The arguments are assumed to be of the correct number and types! // The arguments are assumed to be of the correct number and types!

View File

@ -1,11 +1,11 @@
//! Implement script function-calling mechanism for [`Engine`]. //! Implement script function-calling mechanism for [`Engine`].
#![cfg(not(feature = "no_function"))] #![cfg(not(feature = "no_function"))]
use super::call::FnCallArgs;
use crate::ast::ScriptFnDef; use crate::ast::ScriptFnDef;
use crate::engine::{EvalState, Imports}; use crate::engine::{EvalState, GlobalRuntimeState};
use crate::func::call::FnCallArgs;
use crate::r#unsafe::unsafe_cast_var_name_to_lifetime; use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
use crate::{Dynamic, Engine, EvalAltResult, Module, Position, RhaiResult, Scope, StaticVec}; use crate::{Dynamic, Engine, Module, Position, RhaiError, RhaiResult, Scope, StaticVec, ERR};
use std::mem; use std::mem;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -24,7 +24,7 @@ impl Engine {
pub(crate) fn call_script_fn( pub(crate) fn call_script_fn(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
mods: &mut Imports, global: &mut GlobalRuntimeState,
state: &mut EvalState, state: &mut EvalState,
lib: &[&Module], lib: &[&Module],
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Option<&mut Dynamic>,
@ -38,18 +38,17 @@ impl Engine {
fn make_error( fn make_error(
name: String, name: String,
fn_def: &ScriptFnDef, fn_def: &ScriptFnDef,
mods: &Imports, global: &GlobalRuntimeState,
err: Box<EvalAltResult>, err: RhaiError,
pos: Position, pos: Position,
) -> RhaiResult { ) -> RhaiResult {
Err(EvalAltResult::ErrorInFunctionCall( Err(ERR::ErrorInFunctionCall(
name, name,
fn_def fn_def
.lib .lib
.as_ref() .as_ref()
.and_then(|m| m.id().map(|id| id.to_string())) .and_then(|m| m.id().map(str::to_string))
.or_else(|| mods.source.as_ref().map(|s| s.to_string())) .unwrap_or_else(|| global.source.to_string()),
.unwrap_or_default(),
err, err,
pos, pos,
) )
@ -59,7 +58,7 @@ impl Engine {
assert!(fn_def.params.len() == args.len()); assert!(fn_def.params.len() == args.len());
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut mods.num_operations, pos)?; self.inc_operations(&mut global.num_operations, pos)?;
if fn_def.body.is_empty() { if fn_def.body.is_empty() {
return Ok(Dynamic::UNIT); return Ok(Dynamic::UNIT);
@ -68,11 +67,11 @@ impl Engine {
// Check for stack overflow // Check for stack overflow
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if level > self.max_call_levels() { if level > self.max_call_levels() {
return Err(EvalAltResult::ErrorStackOverflow(pos).into()); return Err(ERR::ErrorStackOverflow(pos).into());
} }
let orig_scope_len = scope.len(); let orig_scope_len = scope.len();
let orig_mods_len = mods.len(); let orig_mods_len = global.num_imported_modules();
// Put arguments into scope as variables // Put arguments into scope as variables
// Actually consume the arguments instead of cloning them // Actually consume the arguments instead of cloning them
@ -106,39 +105,37 @@ impl Engine {
}; };
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
if !fn_def.mods.is_empty() { if let Some(ref modules) = fn_def.global {
fn_def modules
.mods .iter()
.iter_raw() .cloned()
.for_each(|(n, m)| mods.push(n.clone(), m.clone())); .for_each(|(n, m)| global.push_module(n, m));
} }
// Evaluate the function // Evaluate the function
let body = &fn_def.body;
let result = self let result = self
.eval_stmt_block( .eval_stmt_block(
scope, scope,
mods, global,
state, state,
lib, lib,
this_ptr, this_ptr,
body, &fn_def.body,
true,
rewind_scope, rewind_scope,
level, level,
) )
.or_else(|err| match *err { .or_else(|err| match *err {
// Convert return statement to return value // Convert return statement to return value
EvalAltResult::Return(x, _) => Ok(x), ERR::Return(x, _) => Ok(x),
// Error in sub function call // Error in sub function call
EvalAltResult::ErrorInFunctionCall(name, src, err, _) => { ERR::ErrorInFunctionCall(name, src, err, _) => {
let fn_name = if src.is_empty() { let fn_name = if src.is_empty() {
format!("{} < {}", name, fn_def.name) format!("{} < {}", name, fn_def.name)
} else { } else {
format!("{} @ '{}' < {}", name, src, fn_def.name) format!("{} @ '{}' < {}", name, src, fn_def.name)
}; };
make_error(fn_name, fn_def, mods, err, pos) 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() => {
@ -146,28 +143,29 @@ impl Engine {
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, mods, err, pos), _ => make_error(fn_def.name.to_string(), fn_def, global, err, pos),
}); });
// Remove all local variables // Remove all local variables and imported modules
if rewind_scope { if rewind_scope {
scope.rewind(orig_scope_len); scope.rewind(orig_scope_len);
} else if !args.is_empty() { } else if !args.is_empty() {
// Remove arguments only, leaving new variables in the scope // Remove arguments only, leaving new variables in the scope
scope.remove_range(orig_scope_len, args.len()) scope.remove_range(orig_scope_len, args.len())
} }
global.truncate_modules(orig_mods_len);
mods.truncate(orig_mods_len); // Restore state
state.rewind_fn_resolution_caches(orig_fn_resolution_caches_len); state.rewind_fn_resolution_caches(orig_fn_resolution_caches_len);
result result
} }
// Does a scripted function exist? // Does a script-defined function exist?
#[must_use] #[must_use]
pub(crate) fn has_script_fn( pub(crate) fn has_script_fn(
&self, &self,
mods: Option<&Imports>, global: Option<&GlobalRuntimeState>,
state: &mut EvalState, state: &mut EvalState,
lib: &[&Module], lib: &[&Module],
hash_script: u64, hash_script: u64,
@ -183,7 +181,7 @@ impl Engine {
// Then check the global namespace and packages // Then check the global namespace and packages
|| self.global_modules.iter().any(|m| m.contains_fn(hash_script)) || self.global_modules.iter().any(|m| m.contains_fn(hash_script))
// Then check imported modules // Then check imported modules
|| mods.map_or(false, |m| m.contains_fn(hash_script)) || global.map_or(false, |m| m.contains_fn(hash_script))
// Then check sub-modules // Then check sub-modules
|| self.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash_script)); || self.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash_script));

View File

@ -41,12 +41,12 @@
//! engine.register_fn("compute", compute_something); //! engine.register_fn("compute", compute_something);
//! //!
//! # #[cfg(not(feature = "no_std"))] //! # #[cfg(not(feature = "no_std"))]
//! # #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] //! # #[cfg(not(target_arch = "wasm32"))]
//! assert_eq!( //! # #[cfg(not(target_arch = "wasm64"))]
//! // Evaluate the script, expects a 'bool' return //! // Evaluate the script, expecting a 'bool' result
//! engine.eval_file::<bool>("my_script.rhai".into())?, //! let result = engine.eval_file::<bool>("my_script.rhai".into())?;
//! true //!
//! ); //! assert_eq!(result, true);
//! //!
//! Ok(()) //! Ok(())
//! } //! }
@ -71,7 +71,6 @@ use std::prelude::v1::*;
mod api; mod api;
mod ast; mod ast;
mod custom_syntax;
mod engine; mod engine;
mod func; mod func;
mod module; mod module;
@ -83,7 +82,16 @@ mod tokenizer;
mod types; mod types;
mod r#unsafe; mod r#unsafe;
type RhaiResult = Result<Dynamic, Box<EvalAltResult>>; /// Error encountered when parsing a script.
type PERR = ParseErrorType;
/// Evaluation result.
type ERR = EvalAltResult;
/// General evaluation error for Rhai scripts.
type RhaiError = Box<ERR>;
/// Generic [`Result`] type for Rhai functions.
type RhaiResultOf<T> = Result<T, RhaiError>;
/// General [`Result`] type for Rhai functions returning [`Dynamic`] values.
type RhaiResult = RhaiResultOf<Dynamic>;
/// The system integer type. It is defined as [`i64`]. /// The system integer type. It is defined as [`i64`].
/// ///
@ -98,6 +106,20 @@ pub type INT = i64;
#[cfg(feature = "only_i32")] #[cfg(feature = "only_i32")]
pub type INT = i32; pub type INT = i32;
/// The unsigned system base integer type. It is defined as [`u64`].
///
/// If the `only_i32` feature is enabled, this will be [`u32`] instead.
#[cfg(not(feature = "only_i32"))]
#[allow(non_camel_case_types)]
type UNSIGNED_INT = u64;
/// The unsigned system integer base type.
/// It is defined as [`u32`] since the `only_i32` feature is used.
///
/// If the `only_i32` feature is not used, this will be `u64` instead.
#[cfg(feature = "only_i32")]
#[allow(non_camel_case_types)]
type UNSIGNED_INT = u32;
/// The system floating-point type. It is defined as [`f64`]. /// The system floating-point type. It is defined as [`f64`].
/// Not available under `no_float`. /// Not available under `no_float`.
/// ///
@ -115,14 +137,15 @@ pub type FLOAT = f64;
#[cfg(feature = "f32_float")] #[cfg(feature = "f32_float")]
pub type FLOAT = f32; pub type FLOAT = f32;
pub type ExclusiveRange = std::ops::Range<INT>; /// An exclusive integer range.
pub type InclusiveRange = std::ops::RangeInclusive<INT>; type ExclusiveRange = std::ops::Range<INT>;
/// An inclusive integer range.
type InclusiveRange = std::ops::RangeInclusive<INT>;
pub use api::custom_syntax::Expression;
pub use ast::{FnAccess, AST}; pub use ast::{FnAccess, AST};
pub use custom_syntax::Expression; pub use engine::{Engine, EvalContext, OP_CONTAINS, OP_EQUALS};
pub use engine::{
Engine, EvalContext, OP_CONTAINS, OP_EQUALS, OP_EXCLUSIVE_RANGE, OP_INCLUSIVE_RANGE,
};
pub use func::{NativeCallContext, RegisterNativeFunction}; pub use func::{NativeCallContext, RegisterNativeFunction};
pub use module::{FnNamespace, Module}; pub use module::{FnNamespace, Module};
pub use tokenizer::Position; pub use tokenizer::Position;
@ -171,8 +194,11 @@ pub type Array = Vec<Dynamic>;
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
pub type Blob = Vec<u8>; pub type Blob = Vec<u8>;
/// Hash map of [`Dynamic`] values with [`SmartString`](https://crates.io/crates/smartstring) keys. /// A dictionary of [`Dynamic`] values with string keys.
/// Not available under `no_object`. /// Not available under `no_object`.
///
/// [`SmartString`](https://crates.io/crates/smartstring) is used as the key type because most
/// property names are ASCII and short, fewer than 23 characters, so they can be stored inline.
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
pub type Map = std::collections::BTreeMap<Identifier, Dynamic>; pub type Map = std::collections::BTreeMap<Identifier, Dynamic>;
@ -204,7 +230,10 @@ pub use tokenizer::{
}; };
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
pub use parser::{IdentifierBuilder, ParseState}; pub use types::StringsInterner;
#[cfg(feature = "internals")]
pub use parser::ParseState;
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
pub use ast::{ pub use ast::{
@ -217,10 +246,13 @@ pub use ast::{
pub use ast::FloatWrapper; pub use ast::FloatWrapper;
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
pub use engine::{EvalState, FnResolutionCache, FnResolutionCacheEntry, Imports}; pub use engine::{EvalState, GlobalRuntimeState};
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
pub use module::NamespaceRef; pub use func::call::{FnResolutionCache, FnResolutionCacheEntry};
#[cfg(feature = "internals")]
pub use module::Namespace;
/// Alias to [`smallvec::SmallVec<[T; 3]>`](https://crates.io/crates/smallvec), which is a /// Alias to [`smallvec::SmallVec<[T; 3]>`](https://crates.io/crates/smallvec), which is a
/// specialized [`Vec`] backed by a small, inline, fixed-size array when there are ≤ 3 items stored. /// specialized [`Vec`] backed by a small, inline, fixed-size array when there are ≤ 3 items stored.
@ -291,6 +323,26 @@ type StaticVec<T> = smallvec::SmallVec<[T; 3]>;
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
pub type StaticVec<T> = smallvec::SmallVec<[T; 3]>; pub type StaticVec<T> = smallvec::SmallVec<[T; 3]>;
/// Inline arguments storage for function calls.
///
/// # Notes
///
/// Since most usage of this is during a function call to gather up arguments, this is mostly
/// allocated on the stack, so we can tolerate a larger number of values stored inline.
///
/// Most functions have few parameters, but closures with a lot of captured variables can
/// potentially have many. Having a larger inline storage for arguments reduces allocations in
/// scripts with heavy closure usage.
///
/// Under `no_closure`, this type aliases to [`StaticVec`][crate::StaticVec] instead.
#[cfg(not(feature = "no_closure"))]
type FnArgsVec<T> = smallvec::SmallVec<[T; 8]>;
/// Inline arguments storage for function calls.
/// This type aliases to [`StaticVec`][crate::StaticVec].
#[cfg(feature = "no_closure")]
type FnArgsVec<T> = crate::StaticVec<T>;
pub(crate) type SmartString = smartstring::SmartString<smartstring::LazyCompact>; pub(crate) type SmartString = smartstring::SmartString<smartstring::LazyCompact>;
// Compiler guards against mutually-exclusive feature flags // Compiler guards against mutually-exclusive feature flags
@ -315,11 +367,13 @@ compile_error!("`stdweb` cannot be used with `no-std`");
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
compile_error!("`no_std` cannot be used for WASM target"); compile_error!("`no_std` cannot be used for WASM target");
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #[cfg(not(target_arch = "wasm32"))]
#[cfg(not(target_arch = "wasm64"))]
#[cfg(feature = "wasm-bindgen")] #[cfg(feature = "wasm-bindgen")]
compile_error!("`wasm-bindgen` cannot be used for non-WASM target"); compile_error!("`wasm-bindgen` cannot be used for non-WASM target");
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #[cfg(not(target_arch = "wasm32"))]
#[cfg(not(target_arch = "wasm64"))]
#[cfg(feature = "stdweb")] #[cfg(feature = "stdweb")]
compile_error!("`stdweb` cannot be used non-WASM target"); compile_error!("`stdweb` cannot be used non-WASM target");

View File

@ -5,12 +5,11 @@ use crate::func::{
shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, RegisterNativeFunction, shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, RegisterNativeFunction,
SendSync, SendSync,
}; };
use crate::parser::IdentifierBuilder;
use crate::tokenizer::Token; use crate::tokenizer::Token;
use crate::types::dynamic::Variant; use crate::types::dynamic::Variant;
use crate::{ use crate::{
calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes, Dynamic, EvalAltResult, calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes, Dynamic, Identifier,
Identifier, ImmutableString, NativeCallContext, Shared, StaticVec, ImmutableString, NativeCallContext, RhaiResultOf, Shared, StaticVec,
}; };
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -47,9 +46,15 @@ pub struct FuncInfo {
pub params: usize, pub params: usize,
/// Parameter types (if applicable). /// Parameter types (if applicable).
pub param_types: StaticVec<TypeId>, pub param_types: StaticVec<TypeId>,
/// Parameter names (if available). /// Parameter names and types (if available).
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
pub param_names: StaticVec<Identifier>, pub param_names_and_types: StaticVec<Identifier>,
/// Return type name.
#[cfg(feature = "metadata")]
pub return_type_name: Identifier,
/// Comments.
#[cfg(feature = "metadata")]
pub comments: Option<Box<[Box<str>]>>,
} }
impl FuncInfo { impl FuncInfo {
@ -60,16 +65,21 @@ impl FuncInfo {
pub fn gen_signature(&self) -> String { pub fn gen_signature(&self) -> String {
let mut sig = format!("{}(", self.name); let mut sig = format!("{}(", self.name);
if !self.param_names.is_empty() { if !self.param_names_and_types.is_empty() {
let mut params: StaticVec<Box<str>> = let params: StaticVec<_> = self
self.param_names.iter().map(|s| s.as_str().into()).collect(); .param_names_and_types
let return_type = params.pop().unwrap_or_else(|| "()".into()); .iter()
.map(|s| s.as_str())
.collect();
sig.push_str(&params.join(", ")); sig.push_str(&params.join(", "));
if &*return_type != "()" { sig.push_str(")");
sig.push_str(") -> ");
sig.push_str(&return_type); match self.return_type_name.as_str() {
} else { "" | "()" => (),
sig.push(')'); ty => {
sig.push_str(" -> ");
sig.push_str(ty);
}
} }
} else { } else {
for x in 0..self.params { for x in 0..self.params {
@ -82,7 +92,12 @@ impl FuncInfo {
if self.func.is_script() { if self.func.is_script() {
sig.push(')'); sig.push(')');
} else { } else {
sig.push_str(") -> ?"); sig.push_str(")");
match self.return_type_name.as_str() {
"()" => (),
_ => sig.push_str(" -> ?"),
}
} }
} }
@ -100,12 +115,12 @@ impl FuncInfo {
/// ///
/// The first module name is skipped. Hashing starts from the _second_ module in the chain. /// The first module name is skipped. Hashing starts from the _second_ module in the chain.
#[inline] #[inline]
pub fn calc_native_fn_hash( pub fn calc_native_fn_hash<'a>(
modules: impl Iterator<Item = impl AsRef<str>>, modules: impl Iterator<Item = &'a str>,
fn_name: impl AsRef<str>, fn_name: &str,
params: &[TypeId], params: &[TypeId],
) -> u64 { ) -> u64 {
let hash_script = calc_qualified_fn_hash(modules, fn_name.as_ref(), params.len()); let hash_script = calc_qualified_fn_hash(modules, fn_name, params.len());
let hash_params = calc_fn_params_hash(params.iter().cloned()); let hash_params = calc_fn_params_hash(params.iter().cloned());
combine_hashes(hash_script, hash_params) combine_hashes(hash_script, hash_params)
} }
@ -115,7 +130,8 @@ pub fn calc_native_fn_hash(
#[derive(Clone)] #[derive(Clone)]
pub struct Module { pub struct Module {
/// ID identifying the module. /// ID identifying the module.
id: Option<Identifier>, /// No ID if string is empty.
id: Identifier,
/// Is this module internal? /// Is this module internal?
pub(crate) internal: bool, pub(crate) internal: bool,
/// Is this module part of a standard library? /// Is this module part of a standard library?
@ -139,8 +155,6 @@ pub struct Module {
indexed: bool, indexed: bool,
/// Does the [`Module`] contain indexed functions that have been exposed to the global namespace? /// Does the [`Module`] contain indexed functions that have been exposed to the global namespace?
contains_indexed_global_functions: bool, contains_indexed_global_functions: bool,
/// Interned strings
identifiers: IdentifierBuilder,
} }
impl Default for Module { impl Default for Module {
@ -154,8 +168,9 @@ impl fmt::Debug for Module {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut d = f.debug_struct("Module"); let mut d = f.debug_struct("Module");
self.id.as_ref().map(|id| d.field("id", id)); if !self.id.is_empty() {
d.field("id", &self.id);
}
if !self.modules.is_empty() { if !self.modules.is_empty() {
d.field( d.field(
"modules", "modules",
@ -225,7 +240,7 @@ impl Module {
#[must_use] #[must_use]
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
id: None, id: Identifier::new_const(),
internal: false, internal: false,
standard: false, standard: false,
modules: BTreeMap::new(), modules: BTreeMap::new(),
@ -237,7 +252,6 @@ impl Module {
all_type_iterators: BTreeMap::new(), all_type_iterators: BTreeMap::new(),
indexed: true, indexed: true,
contains_indexed_global_functions: false, contains_indexed_global_functions: false,
identifiers: IdentifierBuilder::new(),
} }
} }
@ -254,18 +268,24 @@ impl Module {
#[inline] #[inline]
#[must_use] #[must_use]
pub fn id(&self) -> Option<&str> { pub fn id(&self) -> Option<&str> {
self.id_raw().map(|s| s.as_str()) if self.id_raw().is_empty() {
None
} else {
Some(self.id_raw())
}
} }
/// Get the ID of the [`Module`] as an [`Identifier`], if any. /// Get the ID of the [`Module`] as an [`Identifier`], if any.
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub(crate) const fn id_raw(&self) -> Option<&Identifier> { pub(crate) const fn id_raw(&self) -> &Identifier {
self.id.as_ref() &self.id
} }
/// Set the ID of the [`Module`]. /// Set the ID of the [`Module`].
/// ///
/// If the string is empty, it is equivalent to clearing the ID.
///
/// # Example /// # Example
/// ///
/// ``` /// ```
@ -276,7 +296,7 @@ impl Module {
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn set_id(&mut self, id: impl Into<Identifier>) -> &mut Self { pub fn set_id(&mut self, id: impl Into<Identifier>) -> &mut Self {
self.id = Some(id.into()); self.id = id.into();
self self
} }
/// Clear the ID of the [`Module`]. /// Clear the ID of the [`Module`].
@ -293,7 +313,7 @@ impl Module {
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn clear_id(&mut self) -> &mut Self { pub fn clear_id(&mut self) -> &mut Self {
self.id = None; self.id.clear();
self self
} }
@ -440,9 +460,9 @@ impl Module {
/// Name and Position in [`EvalAltResult`] are [`None`] and [`NONE`][Position::NONE] and must be set afterwards. /// Name and Position in [`EvalAltResult`] are [`None`] and [`NONE`][Position::NONE] and must be set afterwards.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[inline] #[inline]
pub(crate) fn get_qualified_var(&self, hash_var: u64) -> Result<&Dynamic, Box<EvalAltResult>> { pub(crate) fn get_qualified_var(&self, hash_var: u64) -> RhaiResultOf<&Dynamic> {
self.all_variables.get(&hash_var).ok_or_else(|| { self.all_variables.get(&hash_var).ok_or_else(|| {
EvalAltResult::ErrorVariableNotFound(String::new(), crate::Position::NONE).into() crate::ERR::ErrorVariableNotFound(String::new(), crate::Position::NONE).into()
}) })
} }
@ -457,8 +477,8 @@ impl Module {
// None + function name + number of arguments. // None + function name + number of arguments.
let num_params = fn_def.params.len(); let num_params = fn_def.params.len();
let hash_script = crate::calc_fn_hash(&fn_def.name, num_params); let hash_script = crate::calc_fn_hash(&fn_def.name, num_params);
let mut param_names = fn_def.params.clone(); #[cfg(feature = "metadata")]
param_names.push("Dynamic".into()); let param_names_and_types = fn_def.params.iter().cloned().collect();
self.functions.insert( self.functions.insert(
hash_script, hash_script,
FuncInfo { FuncInfo {
@ -468,7 +488,11 @@ impl Module {
params: num_params, params: num_params,
param_types: StaticVec::new_const(), param_types: StaticVec::new_const(),
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
param_names, param_names_and_types,
#[cfg(feature = "metadata")]
return_type_name: "Dynamic".into(),
#[cfg(feature = "metadata")]
comments: None,
func: Into::<CallableFunction>::into(fn_def).into(), func: Into::<CallableFunction>::into(fn_def).into(),
} }
.into(), .into(),
@ -597,7 +621,7 @@ impl Module {
self.functions.contains_key(&hash_fn) self.functions.contains_key(&hash_fn)
} }
/// Update the metadata (parameter names/types and return type) of a registered function. /// _(metadata)_ Update the metadata (parameter names/types and return type) of a registered function.
/// Exported under the `metadata` feature only. /// Exported under the `metadata` feature only.
/// ///
/// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call. /// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call.
@ -612,14 +636,69 @@ 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(&mut self, hash_fn: u64, arg_names: &[impl AsRef<str>]) -> &mut Self { pub fn update_fn_metadata<S: AsRef<str>>(
let param_names = arg_names &mut self,
hash_fn: u64,
arg_names: impl AsRef<[S]>,
) -> &mut Self {
let mut param_names: StaticVec<_> = arg_names
.as_ref()
.iter() .iter()
.map(|name| self.identifiers.get(name.as_ref())) .map(|s| s.as_ref().into())
.collect(); .collect();
if let Some(f) = self.functions.get_mut(&hash_fn) { if let Some(f) = self.functions.get_mut(&hash_fn) {
f.param_names = param_names; let (param_names, return_type_name) = if param_names.len() > f.params {
let return_type = param_names.pop().unwrap();
(param_names, return_type)
} else {
(param_names, Default::default())
};
f.param_names_and_types = param_names;
f.return_type_name = return_type_name;
}
self
}
/// _(metadata)_ Update the metadata (parameter names/types, return type and doc-comments) of a
/// registered function.
/// Exported under the `metadata` feature only.
///
/// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call.
///
/// ## Parameter Names and Types
///
/// Each parameter name/type pair should be a single string of the format: `var_name: type`.
///
/// ## Return Type
///
/// The _last entry_ in the list should be the _return type_ of the function. In other words,
/// the number of entries should be one larger than the number of parameters.
///
/// ## Comments
///
/// Block doc-comments should be kept in a single line.
///
/// Line doc-comments should be kept in one string slice per line without the termination line-break.
///
/// Leading white-spaces should be stripped, and each string slice always starts with the corresponding
/// doc-comment leader: `///` or `/**`.
#[cfg(feature = "metadata")]
#[inline]
pub fn update_fn_metadata_with_comments<A: AsRef<str>, C: AsRef<str>>(
&mut self,
hash_fn: u64,
arg_names: impl AsRef<[A]>,
comments: impl AsRef<[C]>,
) -> &mut Self {
self.update_fn_metadata(hash_fn, arg_names);
let comments = comments.as_ref();
if !comments.is_empty() {
let f = self.functions.get_mut(&hash_fn).unwrap();
f.comments = Some(comments.iter().map(|s| s.as_ref().into()).collect());
} }
self self
@ -664,20 +743,30 @@ impl Module {
/// # WARNING - Low Level API /// # WARNING - Low Level API
/// ///
/// This function is very low level. /// This function is very low level.
///
/// ## Parameter Names and Types
///
/// Each parameter name/type pair should be a single string of the format: `var_name: type`.
///
/// ## Return Type
///
/// The _last entry_ in the list should be the _return type_ of the function.
/// In other words, the number of entries should be one larger than the number of parameters.
#[inline] #[inline]
pub fn set_fn( pub fn set_fn(
&mut self, &mut self,
name: impl AsRef<str> + Into<Identifier>, name: impl AsRef<str>,
namespace: FnNamespace, namespace: FnNamespace,
access: FnAccess, access: FnAccess,
arg_names: Option<&[&str]>, arg_names: Option<&[&str]>,
arg_types: &[TypeId], arg_types: impl AsRef<[TypeId]>,
func: CallableFunction, func: CallableFunction,
) -> u64 { ) -> u64 {
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: StaticVec<_> = arg_types
.as_ref()
.iter() .iter()
.cloned() .cloned()
.enumerate() .enumerate()
@ -686,26 +775,37 @@ impl Module {
param_types.shrink_to_fit(); param_types.shrink_to_fit();
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
let mut param_names: StaticVec<_> = _arg_names let (param_names, return_type_name) = {
.iter() let mut names = _arg_names
.flat_map(|&p| p.iter()) .iter()
.map(|&arg| self.identifiers.get(arg)) .flat_map(|&p| p.iter())
.collect(); .map(|&s| s.into())
#[cfg(feature = "metadata")] .collect::<StaticVec<_>>();
param_names.shrink_to_fit(); let return_type = if names.len() > arg_types.as_ref().len() {
names.pop().unwrap()
} else {
Default::default()
};
names.shrink_to_fit();
(names, return_type)
};
let hash_fn = calc_native_fn_hash(empty::<&str>(), name.as_ref(), &param_types); let hash_fn = calc_native_fn_hash(empty::<&str>(), name.as_ref(), &param_types);
self.functions.insert( self.functions.insert(
hash_fn, hash_fn,
FuncInfo { FuncInfo {
name: self.identifiers.get(name), name: name.as_ref().into(),
namespace, namespace,
access, access,
params: param_types.len(), params: param_types.len(),
param_types, param_types,
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
param_names, param_names_and_types: param_names,
#[cfg(feature = "metadata")]
return_type_name,
#[cfg(feature = "metadata")]
comments: None,
func: func.into(), func: func.into(),
} }
.into(), .into(),
@ -717,6 +817,56 @@ impl Module {
hash_fn hash_fn
} }
/// _(metadata)_ Set a Rust function into the [`Module`], returning a non-zero hash key.
/// Exported under the `metadata` feature only.
///
/// If there is an existing Rust function of the same hash, it is replaced.
///
/// # WARNING - Low Level API
///
/// This function is very low level.
///
/// ## Parameter Names and Types
///
/// Each parameter name/type pair should be a single string of the format: `var_name: type`.
///
/// ## Return Type
///
/// The _last entry_ in the list should be the _return type_ of the function.
/// In other words, the number of entries should be one larger than the number of parameters.
///
/// ## Comments
///
/// Block doc-comments should be kept in a single line.
///
/// Line doc-comments should be kept in one string slice per line without the termination line-break.
///
/// Leading white-spaces should be stripped, and each string slice always starts with the corresponding
/// doc-comment leader: `///` or `/**`.
#[cfg(feature = "metadata")]
#[inline]
pub fn set_fn_with_comments<S: AsRef<str>>(
&mut self,
name: impl AsRef<str>,
namespace: FnNamespace,
access: FnAccess,
arg_names: Option<&[&str]>,
arg_types: impl AsRef<[TypeId]>,
comments: impl AsRef<[S]>,
func: CallableFunction,
) -> u64 {
let hash = self.set_fn(name, namespace, access, arg_names, arg_types, func);
let comments = comments.as_ref();
if !comments.is_empty() {
let f = self.functions.get_mut(&hash).unwrap();
f.comments = Some(comments.iter().map(|s| s.as_ref().into()).collect());
}
hash
}
/// Set a Rust function taking a reference to the scripting [`Engine`][crate::Engine], /// Set a Rust function taking a reference to the scripting [`Engine`][crate::Engine],
/// the current set of functions, plus a list of mutable [`Dynamic`] references /// the current set of functions, plus a list of mutable [`Dynamic`] references
/// into the [`Module`], returning a non-zero hash key. /// into the [`Module`], returning a non-zero hash key.
@ -731,7 +881,7 @@ impl Module {
/// ///
/// This function is very low level. /// This function is very low level.
/// ///
/// ## Arguments /// # Arguments
/// ///
/// A list of [`TypeId`]'s is taken as the argument types. /// A list of [`TypeId`]'s is taken as the argument types.
/// ///
@ -750,7 +900,7 @@ impl Module {
/// ///
/// # Function Metadata /// # Function Metadata
/// ///
/// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. /// No metadata for the function is registered. Use [`update_fn_metadata`][Module::update_fn_metadata] to add metadata.
/// ///
/// # Example /// # Example
/// ///
@ -778,26 +928,23 @@ impl Module {
/// *x *= 2; // the first argument can be mutated /// *x *= 2; // the first argument can be mutated
/// } /// }
/// ///
/// Ok(orig) // return Result<T, Box<EvalAltResult>> /// Ok(orig) // return RhaiResult<T>
/// }); /// });
/// ///
/// assert!(module.contains_fn(hash)); /// assert!(module.contains_fn(hash));
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn set_raw_fn<N, T, F>( pub fn set_raw_fn<T, F>(
&mut self, &mut self,
name: N, name: impl AsRef<str>,
namespace: FnNamespace, namespace: FnNamespace,
access: FnAccess, access: FnAccess,
arg_types: &[TypeId], arg_types: impl AsRef<[TypeId]>,
func: F, func: F,
) -> u64 ) -> u64
where where
N: AsRef<str> + Into<Identifier>,
T: Variant + Clone, T: Variant + Clone,
F: Fn(NativeCallContext, &mut FnCallArgs) -> Result<T, Box<EvalAltResult>> F: Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResultOf<T> + SendSync + 'static,
+ SendSync
+ 'static,
{ {
let f = let f =
move |ctx: NativeCallContext, args: &mut FnCallArgs| func(ctx, args).map(Dynamic::from); move |ctx: NativeCallContext, args: &mut FnCallArgs| func(ctx, args).map(Dynamic::from);
@ -839,7 +986,7 @@ impl Module {
where where
N: AsRef<str> + Into<Identifier>, N: AsRef<str> + Into<Identifier>,
T: Variant + Clone, T: Variant + Clone,
F: RegisterNativeFunction<ARGS, Result<T, Box<EvalAltResult>>>, F: RegisterNativeFunction<ARGS, RhaiResultOf<T>>,
{ {
self.set_fn( self.set_fn(
name, name,
@ -858,7 +1005,8 @@ impl Module {
/// ///
/// # Function Metadata /// # Function Metadata
/// ///
/// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. /// No metadata for the function is registered.
/// Use [`update_fn_metadata`][Module::update_fn_metadata] to add metadata.
/// ///
/// # Example /// # Example
/// ///
@ -874,11 +1022,11 @@ impl Module {
where where
A: Variant + Clone, A: Variant + Clone,
T: Variant + Clone, T: Variant + Clone,
F: RegisterNativeFunction<ARGS, Result<T, Box<EvalAltResult>>>, F: RegisterNativeFunction<ARGS, RhaiResultOf<T>>,
F: Fn(&mut A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static, F: Fn(&mut A) -> RhaiResultOf<T> + SendSync + 'static,
{ {
self.set_fn( self.set_fn(
&crate::engine::make_getter(name), crate::engine::make_getter(name.as_ref()).as_str(),
FnNamespace::Global, FnNamespace::Global,
FnAccess::Public, FnAccess::Public,
None, None,
@ -895,7 +1043,8 @@ impl Module {
/// ///
/// # Function Metadata /// # Function Metadata
/// ///
/// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. /// No metadata for the function is registered.
/// Use [`update_fn_metadata`][Module::update_fn_metadata] to add metadata.
/// ///
/// # Example /// # Example
/// ///
@ -915,11 +1064,11 @@ impl Module {
where where
A: Variant + Clone, A: Variant + Clone,
B: Variant + Clone, B: Variant + Clone,
F: RegisterNativeFunction<ARGS, Result<(), Box<EvalAltResult>>>, F: RegisterNativeFunction<ARGS, RhaiResultOf<()>>,
F: Fn(&mut A, B) -> Result<(), Box<EvalAltResult>> + SendSync + 'static, F: Fn(&mut A, B) -> RhaiResultOf<()> + SendSync + 'static,
{ {
self.set_fn( self.set_fn(
&crate::engine::make_setter(name), crate::engine::make_setter(name.as_ref()).as_str(),
FnNamespace::Global, FnNamespace::Global,
FnAccess::Public, FnAccess::Public,
None, None,
@ -941,7 +1090,8 @@ impl Module {
/// ///
/// # Function Metadata /// # Function Metadata
/// ///
/// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. /// No metadata for the function is registered.
/// Use [`update_fn_metadata`][Module::update_fn_metadata] to add metadata.
/// ///
/// # Example /// # Example
/// ///
@ -961,8 +1111,8 @@ impl Module {
A: Variant + Clone, A: Variant + Clone,
B: Variant + Clone, B: Variant + Clone,
T: Variant + Clone, T: Variant + Clone,
F: RegisterNativeFunction<ARGS, Result<T, Box<EvalAltResult>>>, F: RegisterNativeFunction<ARGS, RhaiResultOf<T>>,
F: Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static, F: Fn(&mut A, B) -> RhaiResultOf<T> + 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>() {
@ -1002,7 +1152,8 @@ impl Module {
/// ///
/// # Function Metadata /// # Function Metadata
/// ///
/// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. /// No metadata for the function is registered.
/// Use [`update_fn_metadata`][Module::update_fn_metadata] to add metadata.
/// ///
/// # Example /// # Example
/// ///
@ -1022,8 +1173,8 @@ impl Module {
A: Variant + Clone, A: Variant + Clone,
B: Variant + Clone, B: Variant + Clone,
C: Variant + Clone, C: Variant + Clone,
F: RegisterNativeFunction<ARGS, Result<(), Box<EvalAltResult>>>, F: RegisterNativeFunction<ARGS, RhaiResultOf<()>>,
F: Fn(&mut A, B, C) -> Result<(), Box<EvalAltResult>> + SendSync + 'static, F: Fn(&mut A, B, C) -> RhaiResultOf<()> + 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>() {
@ -1063,7 +1214,8 @@ impl Module {
/// ///
/// # Function Metadata /// # Function Metadata
/// ///
/// No metadata for the function is registered. Use `update_fn_metadata` to add metadata. /// No metadata for the function is registered.
/// Use [`update_fn_metadata`][Module::update_fn_metadata] to add metadata.
/// ///
/// # Example /// # Example
/// ///
@ -1086,8 +1238,8 @@ impl Module {
#[inline(always)] #[inline(always)]
pub fn set_indexer_get_set_fn<A, B, T>( pub fn set_indexer_get_set_fn<A, B, T>(
&mut self, &mut self,
get_fn: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static, get_fn: impl Fn(&mut A, B) -> RhaiResultOf<T> + SendSync + 'static,
set_fn: impl Fn(&mut A, B, T) -> Result<(), Box<EvalAltResult>> + SendSync + 'static, set_fn: impl Fn(&mut A, B, T) -> RhaiResultOf<()> + SendSync + 'static,
) -> (u64, u64) ) -> (u64, u64)
where where
A: Variant + Clone, A: Variant + Clone,
@ -1142,7 +1294,6 @@ impl Module {
self.all_type_iterators.clear(); self.all_type_iterators.clear();
self.indexed = false; self.indexed = false;
self.contains_indexed_global_functions = false; self.contains_indexed_global_functions = false;
self.identifiers += other.identifiers;
self self
} }
@ -1162,7 +1313,6 @@ impl Module {
self.all_type_iterators.clear(); self.all_type_iterators.clear();
self.indexed = false; self.indexed = false;
self.contains_indexed_global_functions = false; self.contains_indexed_global_functions = false;
self.identifiers += other.identifiers;
self self
} }
@ -1191,7 +1341,6 @@ impl Module {
self.all_type_iterators.clear(); self.all_type_iterators.clear();
self.indexed = false; self.indexed = false;
self.contains_indexed_global_functions = false; self.contains_indexed_global_functions = false;
self.identifiers.merge(&other.identifiers);
self self
} }
@ -1241,7 +1390,6 @@ impl Module {
self.all_type_iterators.clear(); self.all_type_iterators.clear();
self.indexed = false; self.indexed = false;
self.contains_indexed_global_functions = false; self.contains_indexed_global_functions = false;
self.identifiers.merge(&other.identifiers);
self self
} }
@ -1284,8 +1432,8 @@ impl Module {
/// Get an iterator to the sub-modules in the [`Module`]. /// Get an iterator to the sub-modules in the [`Module`].
#[inline] #[inline]
pub fn iter_sub_modules(&self) -> impl Iterator<Item = (&str, Shared<Module>)> { pub fn iter_sub_modules(&self) -> impl Iterator<Item = (&str, &Shared<Module>)> {
self.modules.iter().map(|(k, m)| (k.as_str(), m.clone())) self.modules.iter().map(|(k, m)| (k.as_str(), m))
} }
/// Get an iterator to the variables in the [`Module`]. /// Get an iterator to the variables in the [`Module`].
@ -1328,7 +1476,7 @@ impl Module {
f.access, f.access,
f.name.as_str(), f.name.as_str(),
f.params, f.params,
f.func.get_script_fn_def().expect("scripted function"), f.func.get_script_fn_def().expect("script-defined function"),
) )
}) })
} }
@ -1404,13 +1552,13 @@ impl Module {
scope: crate::Scope, scope: crate::Scope,
ast: &crate::AST, ast: &crate::AST,
engine: &crate::Engine, engine: &crate::Engine,
) -> Result<Self, Box<EvalAltResult>> { ) -> RhaiResultOf<Self> {
let mut scope = scope; let mut scope = scope;
let mut mods = crate::engine::Imports::new(); let mut global = crate::engine::GlobalRuntimeState::new();
let orig_mods_len = mods.len(); let orig_mods_len = global.num_imported_modules();
// Run the script // Run the script
engine.eval_ast_with_scope_raw(&mut scope, &mut mods, &ast, 0)?; engine.eval_ast_with_scope_raw(&mut scope, &mut global, &ast, 0)?;
// Create new module // Create new module
let mut module = let mut module =
@ -1421,11 +1569,11 @@ impl Module {
match aliases.len() { match aliases.len() {
0 => (), 0 => (),
1 => { 1 => {
let alias = aliases.pop().expect("not empty"); let alias = aliases.pop().unwrap();
module.set_var(alias, value); module.set_var(alias, value);
} }
_ => { _ => {
let last_alias = aliases.pop().expect("not empty"); let last_alias = aliases.pop().unwrap();
aliases.into_iter().for_each(|alias| { aliases.into_iter().for_each(|alias| {
module.set_var(alias, value.clone()); module.set_var(alias, value.clone());
}); });
@ -1437,13 +1585,18 @@ impl Module {
}); });
// Extra modules left in the scope become sub-modules // Extra modules left in the scope become sub-modules
let mut func_mods = crate::engine::Imports::new(); let mut func_global = None;
mods.into_iter().skip(orig_mods_len).for_each(|(alias, m)| { global.into_iter().skip(orig_mods_len).for_each(|kv| {
func_mods.push(alias.clone(), m.clone()); if func_global.is_none() {
module.set_sub_module(alias, m); func_global = Some(StaticVec::new());
}
func_global.as_mut().expect("`Some`").push(kv.clone());
module.set_sub_module(kv.0, kv.1);
}); });
let func_global = func_global.map(|v| v.into_boxed_slice());
// Non-private functions defined become module functions // Non-private functions defined become module functions
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
if ast.has_functions() { if ast.has_functions() {
@ -1459,20 +1612,16 @@ impl Module {
let mut func = f let mut func = f
.func .func
.get_script_fn_def() .get_script_fn_def()
.expect("scripted function") .expect("script-defined function")
.as_ref() .as_ref()
.clone(); .clone();
func.lib = Some(ast.shared_lib().clone()); func.lib = Some(ast.shared_lib().clone());
func.mods = func_mods.clone(); func.global = func_global.clone();
module.set_script_fn(func); module.set_script_fn(func);
}); });
} }
if let Some(s) = ast.source_raw() { module.set_id(ast.source_raw().clone());
module.set_id(s.clone());
} else {
module.clear_id();
}
module.build_index(); module.build_index();
@ -1647,20 +1796,21 @@ impl Module {
} }
} }
/// _(internals)_ A chain of [module][Module] names to namespace-qualify a variable or function call. /// _(internals)_ A chain of [module][Module] names to namespace-qualify a variable or function
/// Exported under the `internals` feature only. /// call. Exported under the `internals` feature only.
/// ///
/// A [`u64`] offset to the current [`Scope`][crate::Scope] is cached for quick search purposes. /// A [`u64`] offset to the current [stack of imported modules][crate::GlobalRuntimeState] is
/// cached for quick search purposes.
/// ///
/// A [`StaticVec`] is used because most namespace-qualified access contains only one level, /// A [`StaticVec`] is used because the vast majority of namespace-qualified access contains only
/// and it is wasteful to always allocate a [`Vec`] with one element. /// one level, and it is wasteful to always allocate a [`Vec`] with one element.
#[derive(Clone, Eq, PartialEq, Default, Hash)] #[derive(Clone, Eq, PartialEq, Default, Hash)]
pub struct NamespaceRef { pub struct Namespace {
index: Option<NonZeroUsize>, index: Option<NonZeroUsize>,
path: StaticVec<Ident>, path: StaticVec<Ident>,
} }
impl fmt::Debug for NamespaceRef { impl fmt::Debug for Namespace {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(index) = self.index { if let Some(index) = self.index {
write!(f, "{} -> ", index)?; write!(f, "{} -> ", index)?;
@ -1677,7 +1827,7 @@ impl fmt::Debug for NamespaceRef {
} }
} }
impl fmt::Display for NamespaceRef { impl fmt::Display for Namespace {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str( f.write_str(
&self &self
@ -1690,7 +1840,7 @@ impl fmt::Display for NamespaceRef {
} }
} }
impl Deref for NamespaceRef { impl Deref for Namespace {
type Target = StaticVec<Ident>; type Target = StaticVec<Ident>;
#[inline(always)] #[inline(always)]
@ -1699,14 +1849,14 @@ impl Deref for NamespaceRef {
} }
} }
impl DerefMut for NamespaceRef { impl DerefMut for Namespace {
#[inline(always)] #[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.path &mut self.path
} }
} }
impl From<Vec<Ident>> for NamespaceRef { impl From<Vec<Ident>> for Namespace {
#[inline(always)] #[inline(always)]
fn from(mut path: Vec<Ident>) -> Self { fn from(mut path: Vec<Ident>) -> Self {
path.shrink_to_fit(); path.shrink_to_fit();
@ -1717,7 +1867,7 @@ impl From<Vec<Ident>> for NamespaceRef {
} }
} }
impl From<StaticVec<Ident>> for NamespaceRef { impl From<StaticVec<Ident>> for Namespace {
#[inline(always)] #[inline(always)]
fn from(mut path: StaticVec<Ident>) -> Self { fn from(mut path: StaticVec<Ident>) -> Self {
path.shrink_to_fit(); path.shrink_to_fit();
@ -1725,8 +1875,8 @@ impl From<StaticVec<Ident>> for NamespaceRef {
} }
} }
impl NamespaceRef { impl Namespace {
/// Create a new [`NamespaceRef`]. /// Create a new [`Namespace`].
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub const fn new() -> Self { pub const fn new() -> Self {

View File

@ -1,4 +1,4 @@
use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, Shared}; use crate::{Engine, Module, ModuleResolver, Position, RhaiResultOf, Shared, ERR};
use std::ops::AddAssign; use std::ops::AddAssign;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -77,7 +77,7 @@ impl ModuleResolversCollection {
/// Get an iterator of all the [module resolvers][ModuleResolver]. /// Get an iterator of all the [module resolvers][ModuleResolver].
#[inline] #[inline]
pub fn iter(&self) -> impl Iterator<Item = &dyn ModuleResolver> { pub fn iter(&self) -> impl Iterator<Item = &dyn ModuleResolver> {
self.0.iter().map(|v| v.as_ref()) self.0.iter().map(<_>::as_ref)
} }
/// Remove all [module resolvers][ModuleResolver]. /// Remove all [module resolvers][ModuleResolver].
#[inline(always)] #[inline(always)]
@ -123,19 +123,19 @@ impl ModuleResolver for ModuleResolversCollection {
source_path: Option<&str>, source_path: Option<&str>,
path: &str, path: &str,
pos: Position, pos: Position,
) -> Result<Shared<Module>, Box<EvalAltResult>> { ) -> RhaiResultOf<Shared<Module>> {
for resolver in self.0.iter() { for resolver in self.0.iter() {
match resolver.resolve(engine, source_path, path, pos) { match resolver.resolve(engine, source_path, path, pos) {
Ok(module) => return Ok(module), Ok(module) => return Ok(module),
Err(err) => match *err { Err(err) => match *err {
EvalAltResult::ErrorModuleNotFound(_, _) => continue, ERR::ErrorModuleNotFound(_, _) => continue,
EvalAltResult::ErrorInModule(_, err, _) => return Err(err), ERR::ErrorInModule(_, err, _) => return Err(err),
_ => panic!("ModuleResolver::resolve returns error that is not ErrorModuleNotFound or ErrorInModule"), _ => panic!("ModuleResolver::resolve returns error that is not ErrorModuleNotFound or ErrorInModule"),
}, },
} }
} }
Err(EvalAltResult::ErrorModuleNotFound(path.into(), pos).into()) Err(ERR::ErrorModuleNotFound(path.into(), pos).into())
} }
} }

View File

@ -1,4 +1,4 @@
use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, Shared}; use crate::{Engine, Module, ModuleResolver, Position, RhaiResultOf, Shared, ERR};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -44,7 +44,7 @@ impl ModuleResolver for DummyModuleResolver {
_: Option<&str>, _: Option<&str>,
path: &str, path: &str,
pos: Position, pos: Position,
) -> Result<Shared<Module>, Box<EvalAltResult>> { ) -> RhaiResultOf<Shared<Module>> {
Err(EvalAltResult::ErrorModuleNotFound(path.into(), pos).into()) Err(ERR::ErrorModuleNotFound(path.into(), pos).into())
} }
} }

View File

@ -1,8 +1,11 @@
#![cfg(not(feature = "no_std"))] #![cfg(not(feature = "no_std"))]
#![cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #![cfg(not(target_arch = "wasm32"))]
#![cfg(not(target_arch = "wasm64"))]
use crate::func::native::shared_write_lock; use crate::func::native::shared_write_lock;
use crate::{Engine, EvalAltResult, Identifier, Module, ModuleResolver, Position, Scope, Shared}; use crate::{
Engine, Identifier, Module, ModuleResolver, Position, RhaiResultOf, Scope, Shared, ERR,
};
use std::{ use std::{
collections::BTreeMap, collections::BTreeMap,
@ -223,7 +226,7 @@ impl FileModuleResolver {
path: impl AsRef<str>, path: impl AsRef<str>,
source_path: Option<impl AsRef<str>>, source_path: Option<impl AsRef<str>>,
) -> Option<Shared<Module>> { ) -> Option<Shared<Module>> {
let file_path = self.get_file_path(path.as_ref(), source_path.as_ref().map(|v| v.as_ref())); let file_path = self.get_file_path(path.as_ref(), source_path.as_ref().map(<_>::as_ref));
shared_write_lock(&self.cache) shared_write_lock(&self.cache)
.remove_entry(&file_path) .remove_entry(&file_path)
@ -259,7 +262,7 @@ impl ModuleResolver for FileModuleResolver {
source_path: Option<&str>, source_path: Option<&str>,
path: &str, path: &str,
pos: Position, pos: Position,
) -> Result<Shared<Module>, Box<EvalAltResult>> { ) -> RhaiResultOf<Shared<Module>> {
// Load relative paths from source if there is no base path specified // Load relative paths from source if there is no base path specified
let source_path = let source_path =
source_path.and_then(|p| Path::new(p).parent().map(|p| p.to_string_lossy())); source_path.and_then(|p| Path::new(p).parent().map(|p| p.to_string_lossy()));
@ -285,17 +288,17 @@ impl ModuleResolver for FileModuleResolver {
let mut ast = engine let mut ast = engine
.compile_file(file_path.clone()) .compile_file(file_path.clone())
.map_err(|err| match *err { .map_err(|err| match *err {
EvalAltResult::ErrorSystem(_, err) if err.is::<IoError>() => { ERR::ErrorSystem(_, err) if err.is::<IoError>() => {
Box::new(EvalAltResult::ErrorModuleNotFound(path.to_string(), pos)) Box::new(ERR::ErrorModuleNotFound(path.to_string(), pos))
} }
_ => Box::new(EvalAltResult::ErrorInModule(path.to_string(), err, pos)), _ => Box::new(ERR::ErrorInModule(path.to_string(), err, pos)),
})?; })?;
ast.set_source(path); ast.set_source(path);
// Make a module from the AST // Make a module from the AST
let m: Shared<Module> = Module::eval_ast_as_new(scope, &ast, engine) let m: Shared<Module> = Module::eval_ast_as_new(scope, &ast, engine)
.map_err(|err| Box::new(EvalAltResult::ErrorInModule(path.to_string(), err, pos)))? .map_err(|err| Box::new(ERR::ErrorInModule(path.to_string(), err, pos)))?
.into(); .into();
// Put it into the cache // Put it into the cache
@ -315,7 +318,7 @@ impl ModuleResolver for FileModuleResolver {
source_path: Option<&str>, source_path: Option<&str>,
path: &str, path: &str,
pos: Position, pos: Position,
) -> Option<Result<crate::AST, Box<EvalAltResult>>> { ) -> Option<RhaiResultOf<crate::AST>> {
// Construct the script file path // Construct the script file path
let file_path = self.get_file_path(path, source_path); let file_path = self.get_file_path(path, source_path);
@ -328,10 +331,10 @@ impl ModuleResolver for FileModuleResolver {
ast ast
}) })
.map_err(|err| match *err { .map_err(|err| match *err {
EvalAltResult::ErrorSystem(_, err) if err.is::<IoError>() => { ERR::ErrorSystem(_, err) if err.is::<IoError>() => {
EvalAltResult::ErrorModuleNotFound(path.to_string(), pos).into() ERR::ErrorModuleNotFound(path.to_string(), pos).into()
} }
_ => EvalAltResult::ErrorInModule(path.to_string(), err, pos).into(), _ => ERR::ErrorInModule(path.to_string(), err, pos).into(),
}), }),
) )
} }

View File

@ -1,5 +1,5 @@
use crate::func::native::SendSync; use crate::func::native::SendSync;
use crate::{Engine, EvalAltResult, Module, Position, Shared, AST}; use crate::{Engine, Module, Position, RhaiResultOf, Shared, AST};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -11,7 +11,8 @@ mod stat;
pub use collection::ModuleResolversCollection; pub use collection::ModuleResolversCollection;
pub use dummy::DummyModuleResolver; pub use dummy::DummyModuleResolver;
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #[cfg(not(target_arch = "wasm32"))]
#[cfg(not(target_arch = "wasm64"))]
pub use file::FileModuleResolver; pub use file::FileModuleResolver;
pub use stat::StaticModuleResolver; pub use stat::StaticModuleResolver;
@ -24,7 +25,7 @@ pub trait ModuleResolver: SendSync {
source_path: Option<&str>, source_path: Option<&str>,
path: &str, path: &str,
pos: Position, pos: Position,
) -> Result<Shared<Module>, Box<EvalAltResult>>; ) -> RhaiResultOf<Shared<Module>>;
/// Resolve an `AST` based on a path string. /// Resolve an `AST` based on a path string.
/// ///
@ -43,7 +44,7 @@ pub trait ModuleResolver: SendSync {
source_path: Option<&str>, source_path: Option<&str>,
path: &str, path: &str,
pos: Position, pos: Position,
) -> Option<Result<AST, Box<EvalAltResult>>> { ) -> Option<RhaiResultOf<AST>> {
None None
} }
} }

View File

@ -1,5 +1,5 @@
use crate::{ use crate::{
Engine, EvalAltResult, Identifier, Module, ModuleResolver, Position, Shared, SmartString, Engine, Identifier, Module, ModuleResolver, Position, RhaiResultOf, Shared, SmartString, ERR,
}; };
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -131,11 +131,11 @@ impl ModuleResolver for StaticModuleResolver {
_: Option<&str>, _: Option<&str>,
path: &str, path: &str,
pos: Position, pos: Position,
) -> Result<Shared<Module>, Box<EvalAltResult>> { ) -> RhaiResultOf<Shared<Module>> {
self.0 self.0
.get(path) .get(path)
.cloned() .cloned()
.ok_or_else(|| EvalAltResult::ErrorModuleNotFound(path.into(), pos).into()) .ok_or_else(|| ERR::ErrorModuleNotFound(path.into(), pos).into())
} }
} }

View File

@ -3,20 +3,24 @@
use crate::ast::{Expr, OpAssignment, Stmt, AST_OPTION_FLAGS::*}; use crate::ast::{Expr, OpAssignment, Stmt, AST_OPTION_FLAGS::*};
use crate::engine::{ use crate::engine::{
EvalState, Imports, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF, EvalState, GlobalRuntimeState, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT,
KEYWORD_TYPE_OF,
}; };
use crate::func::builtin::get_builtin_binary_op_fn; use crate::func::builtin::get_builtin_binary_op_fn;
use crate::func::hashing::get_hasher; use crate::func::hashing::get_hasher;
use crate::tokenizer::Token; use crate::tokenizer::Token;
use crate::types::dynamic::AccessMode; use crate::types::dynamic::AccessMode;
use crate::{calc_fn_hash, Dynamic, Engine, FnPtr, Position, Scope, StaticVec, AST, INT}; use crate::{
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, Position, Scope,
StaticVec, AST, INT,
};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
use std::{ use std::{
any::TypeId,
convert::TryFrom, convert::TryFrom,
hash::{Hash, Hasher}, hash::{Hash, Hasher},
mem, mem,
ops::DerefMut,
}; };
/// Level of optimization performed. /// Level of optimization performed.
@ -106,13 +110,11 @@ impl<'a> OptimizerState<'a> {
} }
/// Look up a constant from the list. /// Look up a constant from the list.
#[inline] #[inline]
pub fn find_constant(&self, name: impl AsRef<str>) -> Option<&Dynamic> { pub fn find_constant(&self, name: &str) -> Option<&Dynamic> {
if !self.propagate_constants { if !self.propagate_constants {
return None; return None;
} }
let name = name.as_ref();
for (n, access, value) in self.variables.iter().rev() { for (n, access, value) in self.variables.iter().rev() {
if n == name { if n == name {
return match access { return match access {
@ -128,7 +130,7 @@ impl<'a> OptimizerState<'a> {
#[inline] #[inline]
pub fn call_fn_with_constant_arguments( pub fn call_fn_with_constant_arguments(
&self, &self,
fn_name: impl AsRef<str>, fn_name: &str,
arg_values: &mut [Dynamic], arg_values: &mut [Dynamic],
) -> Option<Dynamic> { ) -> Option<Dynamic> {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
@ -138,10 +140,10 @@ impl<'a> OptimizerState<'a> {
self.engine self.engine
.call_native_fn( .call_native_fn(
&mut Imports::new(), &mut GlobalRuntimeState::new(),
&mut EvalState::new(), &mut EvalState::new(),
lib, lib,
&fn_name, fn_name,
calc_fn_hash(&fn_name, arg_values.len()), calc_fn_hash(&fn_name, arg_values.len()),
&mut arg_values.iter_mut().collect::<StaticVec<_>>(), &mut arg_values.iter_mut().collect::<StaticVec<_>>(),
false, false,
@ -153,6 +155,22 @@ 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_params = calc_fn_params_hash(arg_types.as_ref().iter().cloned());
let hash = combine_hashes(hash_script, hash_params);
// First check the global namespace and packages, but skip modules that are standard because
// they should never conflict with system functions.
engine.global_modules.iter().filter(|m| !m.standard).any(|m| m.contains_fn(hash))
// Then check sub-modules
|| engine.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash))
}
/// Optimize a block of [statements][Stmt]. /// Optimize a block of [statements][Stmt].
fn optimize_stmt_block( fn optimize_stmt_block(
mut statements: StaticVec<Stmt>, mut statements: StaticVec<Stmt>,
@ -173,6 +191,32 @@ fn optimize_stmt_block(
Stmt::is_pure Stmt::is_pure
}; };
// Flatten blocks
loop {
if let Some(n) = statements.iter().enumerate().find_map(|(i, s)| match s {
Stmt::Block(block, _) if !block.iter().any(Stmt::is_block_dependent) => Some(i),
_ => None,
}) {
let (first, second) = statements.split_at_mut(n);
let stmt = mem::take(&mut second[0]);
let mut stmts = match stmt {
Stmt::Block(block, _) => block,
stmt => unreachable!("Stmt::Block expected but gets {:?}", stmt),
};
statements = first
.iter_mut()
.map(mem::take)
.chain(stmts.iter_mut().map(mem::take))
.chain(second.iter_mut().skip(1).map(mem::take))
.collect();
} else {
break;
}
is_dirty = true;
}
// Optimize
loop { loop {
state.clear_dirty(); state.clear_dirty();
@ -282,14 +326,14 @@ fn optimize_stmt_block(
&& !last_stmt.returns_value() => && !last_stmt.returns_value() =>
{ {
state.set_dirty(); state.set_dirty();
statements.pop().expect(">= 2 elements"); statements.pop().unwrap();
} }
// { ...; return val; } -> { ...; val } // { ...; return val; } -> { ...; val }
[.., Stmt::Return(options, ref mut expr, pos)] [.., Stmt::Return(options, ref mut expr, pos)]
if reduce_return && !options.contains(AST_OPTION_BREAK_OUT) => if reduce_return && !options.contains(AST_OPTION_BREAK_OUT) =>
{ {
state.set_dirty(); state.set_dirty();
*statements.last_mut().expect(">= 2 elements") = expr *statements.last_mut().unwrap() = expr
.as_mut() .as_mut()
.map_or_else(|| Stmt::Noop(pos), |e| Stmt::Expr(mem::take(e))); .map_or_else(|| Stmt::Noop(pos), |e| Stmt::Expr(mem::take(e)));
} }
@ -306,10 +350,9 @@ fn optimize_stmt_block(
{ {
state.set_dirty(); state.set_dirty();
if second_last_stmt.returns_value() { if second_last_stmt.returns_value() {
*statements.last_mut().expect(">= 2 elements") = *statements.last_mut().unwrap() = Stmt::Noop(last_stmt.position());
Stmt::Noop(last_stmt.position());
} else { } else {
statements.pop().expect(">= 2 elements"); statements.pop().unwrap();
} }
} }
_ => break, _ => break,
@ -327,7 +370,7 @@ fn optimize_stmt_block(
if reduce_return && !options.contains(AST_OPTION_BREAK_OUT) => if reduce_return && !options.contains(AST_OPTION_BREAK_OUT) =>
{ {
state.set_dirty(); state.set_dirty();
statements.pop().expect(">= 2 elements"); statements.pop().unwrap();
} }
// { ...; return pure_val; } -> { ... } // { ...; return pure_val; } -> { ... }
[.., Stmt::Return(options, Some(ref expr), _)] [.., Stmt::Return(options, Some(ref expr), _)]
@ -336,11 +379,11 @@ fn optimize_stmt_block(
&& expr.is_pure() => && expr.is_pure() =>
{ {
state.set_dirty(); state.set_dirty();
statements.pop().expect(">= 2 elements"); statements.pop().unwrap();
} }
[.., ref last_stmt] if is_pure(last_stmt) => { [.., ref last_stmt] if is_pure(last_stmt) => {
state.set_dirty(); state.set_dirty();
statements.pop().expect("not empty"); statements.pop().unwrap();
} }
_ => break, _ => break,
} }
@ -382,20 +425,18 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
match x.2 { match x.2 {
Expr::FnCall(ref mut x2, _) => { Expr::FnCall(ref mut x2, _) => {
state.set_dirty(); state.set_dirty();
let op = Token::lookup_from_syntax(&x2.name).expect("operator"); x.1 = Some(OpAssignment::new(&x2.name));
let op_assignment = op.make_op_assignment().expect("operator");
x.1 = Some(OpAssignment::new(op_assignment));
let value = mem::take(&mut x2.args[1]); let value = mem::take(&mut x2.args[1]);
if let Expr::Stack(slot, pos) = value { if let Expr::Stack(slot, pos) = value {
let value = mem::take(x2.constants.get_mut(slot).expect("valid slot")); x.2 =
x.2 = Expr::from_dynamic(value, pos); Expr::from_dynamic(mem::take(x2.constants.get_mut(slot).unwrap()), pos);
} else { } else {
x.2 = value; x.2 = value;
} }
} }
_ => unreachable!(), ref expr => unreachable!("Expr::FnCall expected but gets {:?}", expr),
} }
} }
@ -432,33 +473,33 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
// if false { if_block } else { else_block } -> else_block // if false { if_block } else { else_block } -> else_block
Stmt::If(Expr::BoolConstant(false, _), x, _) => { Stmt::If(Expr::BoolConstant(false, _), x, _) => {
state.set_dirty(); state.set_dirty();
let else_block = mem::take(&mut *x.1); *stmt =
*stmt = match optimize_stmt_block(else_block, state, preserve_result, true, false) { match optimize_stmt_block(mem::take(&mut *x.1), state, preserve_result, true, false)
statements if statements.is_empty() => Stmt::Noop(x.1.position()), {
statements => Stmt::Block(statements.into_boxed_slice(), x.1.position()), statements if statements.is_empty() => Stmt::Noop(x.1.position()),
} statements => Stmt::Block(statements.into_boxed_slice(), x.1.position()),
}
} }
// if true { if_block } else { else_block } -> if_block // if true { if_block } else { else_block } -> if_block
Stmt::If(Expr::BoolConstant(true, _), x, _) => { Stmt::If(Expr::BoolConstant(true, _), x, _) => {
state.set_dirty(); state.set_dirty();
let if_block = mem::take(&mut *x.0); *stmt =
*stmt = match optimize_stmt_block(if_block, state, preserve_result, true, false) { match optimize_stmt_block(mem::take(&mut *x.0), state, preserve_result, true, false)
statements if statements.is_empty() => Stmt::Noop(x.0.position()), {
statements => Stmt::Block(statements.into_boxed_slice(), x.0.position()), statements if statements.is_empty() => Stmt::Noop(x.0.position()),
} statements => Stmt::Block(statements.into_boxed_slice(), x.0.position()),
}
} }
// if expr { if_block } else { else_block } // if expr { if_block } else { else_block }
Stmt::If(condition, x, _) => { Stmt::If(condition, x, _) => {
optimize_expr(condition, state, false); optimize_expr(condition, state, false);
let if_block = mem::take(x.0.deref_mut()); *x.0 = optimize_stmt_block(mem::take(&mut *x.0), state, preserve_result, true, false);
*x.0 = optimize_stmt_block(if_block, state, preserve_result, true, false); *x.1 = optimize_stmt_block(mem::take(&mut *x.1), state, preserve_result, true, false);
let else_block = mem::take(x.1.deref_mut());
*x.1 = optimize_stmt_block(else_block, state, preserve_result, true, false);
} }
// switch const { ... } // switch const { ... }
Stmt::Switch(match_expr, x, pos) if match_expr.is_constant() => { Stmt::Switch(match_expr, x, pos) if match_expr.is_constant() => {
let value = match_expr.get_literal_value().expect("constant"); let value = match_expr.get_literal_value().unwrap();
let hasher = &mut get_hasher(); let hasher = &mut get_hasher();
value.hash(hasher); value.hash(hasher);
let hash = hasher.finish(); let hash = hasher.finish();
@ -471,11 +512,6 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
// switch const { case if condition => stmt, _ => def } => if condition { stmt } else { def } // switch const { case if condition => stmt, _ => def } => if condition { stmt } else { def }
optimize_expr(&mut condition, state, false); optimize_expr(&mut condition, state, false);
let def_pos = if x.1.position().is_none() {
*pos
} else {
x.1.position()
};
let def_stmt = let def_stmt =
optimize_stmt_block(mem::take(&mut *x.1), state, true, true, false); optimize_stmt_block(mem::take(&mut *x.1), state, true, true, false);
@ -483,16 +519,16 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
condition, condition,
Box::new(( Box::new((
mem::take(&mut block.1), mem::take(&mut block.1),
Stmt::Block(def_stmt.into_boxed_slice(), def_pos).into(), Stmt::Block(def_stmt.into_boxed_slice(), x.1.position().or_else(*pos))
.into(),
)), )),
match_expr.position(), match_expr.position(),
); );
} else { } else {
// Promote the matched case // Promote the matched case
let new_pos = block.1.position();
let statements = let statements =
optimize_stmt_block(mem::take(&mut *block.1), state, true, true, false); optimize_stmt_block(mem::take(&mut *block.1), state, true, true, false);
*stmt = Stmt::Block(statements.into_boxed_slice(), new_pos); *stmt = Stmt::Block(statements.into_boxed_slice(), block.1.position());
} }
state.set_dirty(); state.set_dirty();
@ -519,29 +555,27 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
// switch const { range if condition => stmt, _ => def } => if condition { stmt } else { def } // switch const { range if condition => stmt, _ => def } => if condition { stmt } else { def }
optimize_expr(&mut condition, state, false); optimize_expr(&mut condition, state, false);
let def_block = mem::take(&mut *x.1); let def_stmt =
let def_stmt = optimize_stmt_block(def_block, state, true, true, false); optimize_stmt_block(mem::take(&mut *x.1), state, true, true, false);
let def_pos = if x.1.position().is_none() {
*pos
} else {
x.1.position()
};
*stmt = Stmt::If( *stmt = Stmt::If(
condition, condition,
Box::new(( Box::new((
mem::take(stmt_block), mem::take(stmt_block),
Stmt::Block(def_stmt.into_boxed_slice(), def_pos).into(), Stmt::Block(
def_stmt.into_boxed_slice(),
x.1.position().or_else(*pos),
)
.into(),
)), )),
match_expr.position(), match_expr.position(),
); );
} else { } else {
// Promote the matched case // Promote the matched case
let new_pos = stmt_block.position();
let statements = mem::take(&mut **stmt_block); let statements = mem::take(&mut **stmt_block);
let statements = let statements =
optimize_stmt_block(statements, state, true, true, false); optimize_stmt_block(statements, state, true, true, false);
*stmt = Stmt::Block(statements.into_boxed_slice(), new_pos); *stmt =
Stmt::Block(statements.into_boxed_slice(), stmt_block.position());
} }
state.set_dirty(); state.set_dirty();
@ -551,10 +585,9 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
// Multiple ranges - clear the table and just keep the right ranges // Multiple ranges - clear the table and just keep the right ranges
if !table.is_empty() { if !table.is_empty() {
state.set_dirty(); state.set_dirty();
table.clear();
} }
table.clear();
let old_ranges_len = ranges.len(); let old_ranges_len = ranges.len();
ranges.retain(|&mut (start, end, inclusive, _, _)| { ranges.retain(|&mut (start, end, inclusive, _, _)| {
@ -585,19 +618,14 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
// Promote the default case // Promote the default case
state.set_dirty(); state.set_dirty();
let def_pos = if x.1.position().is_none() {
*pos
} else {
x.1.position()
};
let def_stmt = optimize_stmt_block(mem::take(&mut *x.1), state, true, true, false); let def_stmt = optimize_stmt_block(mem::take(&mut *x.1), state, true, true, false);
*stmt = Stmt::Block(def_stmt.into_boxed_slice(), def_pos); *stmt = Stmt::Block(def_stmt.into_boxed_slice(), x.1.position().or_else(*pos));
} }
// switch // switch
Stmt::Switch(match_expr, x, _) => { Stmt::Switch(match_expr, x, _) => {
optimize_expr(match_expr, state, false); optimize_expr(match_expr, state, false);
x.0.values_mut().for_each(|block| { x.0.values_mut().for_each(|block| {
let statements = mem::take(block.1.deref_mut()); let statements = mem::take(&mut *block.1);
*block.1 = optimize_stmt_block(statements, state, preserve_result, true, false); *block.1 = optimize_stmt_block(statements, state, preserve_result, true, false);
if let Some(mut condition) = mem::take(&mut block.0) { if let Some(mut condition) = mem::take(&mut block.0) {
@ -618,8 +646,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
x.0.remove(&key); x.0.remove(&key);
} }
let def_block = mem::take(x.1.deref_mut()); *x.1 = optimize_stmt_block(mem::take(&mut *x.1), state, preserve_result, true, false);
*x.1 = optimize_stmt_block(def_block, state, preserve_result, true, false);
} }
// while false { block } -> Noop // while false { block } -> Noop
@ -633,8 +660,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
if let Expr::BoolConstant(true, pos) = condition { if let Expr::BoolConstant(true, pos) = condition {
*condition = Expr::Unit(*pos); *condition = Expr::Unit(*pos);
} }
let block = mem::take(body.as_mut().deref_mut()); ***body = optimize_stmt_block(mem::take(&mut **body), state, false, true, false);
*body.as_mut().deref_mut() = optimize_stmt_block(block, state, false, true, false);
if body.len() == 1 { if body.len() == 1 {
match body[0] { match body[0] {
@ -661,24 +687,21 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
if *x == options.contains(AST_OPTION_NEGATED) => if *x == options.contains(AST_OPTION_NEGATED) =>
{ {
state.set_dirty(); state.set_dirty();
let block_pos = body.position();
let block = mem::take(body.as_mut().deref_mut());
*stmt = Stmt::Block( *stmt = Stmt::Block(
optimize_stmt_block(block, state, false, true, false).into_boxed_slice(), optimize_stmt_block(mem::take(&mut **body), state, false, true, false)
block_pos, .into_boxed_slice(),
body.position(),
); );
} }
// do { block } while|until expr // do { block } while|until expr
Stmt::Do(body, condition, _, _) => { Stmt::Do(body, condition, _, _) => {
optimize_expr(condition, state, false); optimize_expr(condition, state, false);
let block = mem::take(body.as_mut().deref_mut()); ***body = optimize_stmt_block(mem::take(&mut **body), state, false, true, false);
*body.as_mut().deref_mut() = optimize_stmt_block(block, state, false, true, false);
} }
// for id in expr { block } // for id in expr { block }
Stmt::For(iterable, x, _) => { Stmt::For(iterable, x, _) => {
optimize_expr(iterable, state, false); optimize_expr(iterable, state, false);
let body = mem::take(x.2.deref_mut()); *x.2 = optimize_stmt_block(mem::take(&mut *x.2), state, false, true, false);
*x.2 = optimize_stmt_block(body, state, false, true, false);
} }
// let id = expr; // let id = expr;
Stmt::Var(expr, _, options, _) if !options.contains(AST_OPTION_CONSTANT) => { Stmt::Var(expr, _, options, _) if !options.contains(AST_OPTION_CONSTANT) => {
@ -697,8 +720,8 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
state.set_dirty(); state.set_dirty();
*stmt = Stmt::Noop(*pos); *stmt = Stmt::Noop(*pos);
} }
// Only one statement - promote // Only one statement which is not block-dependent - promote
[s] => { [s] if !s.is_block_dependent() => {
state.set_dirty(); state.set_dirty();
*stmt = mem::take(s); *stmt = mem::take(s);
} }
@ -709,19 +732,16 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
Stmt::TryCatch(x, _) if x.0.iter().all(Stmt::is_pure) => { Stmt::TryCatch(x, _) if x.0.iter().all(Stmt::is_pure) => {
// If try block is pure, there will never be any exceptions // If try block is pure, there will never be any exceptions
state.set_dirty(); state.set_dirty();
let try_pos = x.0.position();
let try_block = mem::take(&mut *x.0);
*stmt = Stmt::Block( *stmt = Stmt::Block(
optimize_stmt_block(try_block, state, false, true, false).into_boxed_slice(), optimize_stmt_block(mem::take(&mut *x.0), state, false, true, false)
try_pos, .into_boxed_slice(),
x.0.position(),
); );
} }
// try { try_block } catch ( var ) { catch_block } // try { try_block } catch ( var ) { catch_block }
Stmt::TryCatch(x, _) => { Stmt::TryCatch(x, _) => {
let try_block = mem::take(x.0.deref_mut()); *x.0 = optimize_stmt_block(mem::take(&mut *x.0), state, false, true, false);
*x.0 = optimize_stmt_block(try_block, state, false, true, false); *x.2 = optimize_stmt_block(mem::take(&mut *x.2), state, false, true, false);
let catch_block = mem::take(x.2.deref_mut());
*x.2 = optimize_stmt_block(catch_block, state, false, true, false);
} }
// func(...) // func(...)
Stmt::Expr(expr @ Expr::FnCall(_, _)) => { Stmt::Expr(expr @ Expr::FnCall(_, _)) => {
@ -742,7 +762,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
// {...}; // {...};
Stmt::Expr(Expr::Stmt(x)) => { Stmt::Expr(Expr::Stmt(x)) => {
state.set_dirty(); state.set_dirty();
*stmt = mem::take(x.as_mut()).into(); *stmt = mem::take(&mut **x).into();
} }
// expr; // expr;
Stmt::Expr(expr) => optimize_expr(expr, state, false), Stmt::Expr(expr) => optimize_expr(expr, state, false),
@ -770,8 +790,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
Expr::Stmt(x) if x.is_empty() => { state.set_dirty(); *expr = Expr::Unit(x.position()) } Expr::Stmt(x) if x.is_empty() => { state.set_dirty(); *expr = Expr::Unit(x.position()) }
// { stmt; ... } - do not count promotion as dirty because it gets turned back into an array // { stmt; ... } - do not count promotion as dirty because it gets turned back into an array
Expr::Stmt(x) => { Expr::Stmt(x) => {
*x.as_mut().deref_mut() = ***x = optimize_stmt_block(mem::take(&mut **x), state, true, true, false);
optimize_stmt_block(mem::take(x.as_mut().deref_mut()), state, true, true, false);
// { Stmt(Expr) } - promote // { Stmt(Expr) } - promote
match x.as_mut().as_mut() { match x.as_mut().as_mut() {
@ -805,9 +824,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Index(x, _, _) if !_chaining => match (&mut x.lhs, &mut x.rhs) { Expr::Index(x, _, _) if !_chaining => match (&mut x.lhs, &mut x.rhs) {
// array[int] // array[int]
(Expr::Array(a, pos), Expr::IntegerConstant(i, _)) (Expr::Array(a, pos), Expr::IntegerConstant(i, _)) if *i >= 0 && (*i as usize) < a.len() && a.iter().all(Expr::is_pure) => {
if *i >= 0 && (*i as usize) < a.len() && a.iter().all(Expr::is_pure) =>
{
// Array literal where everything is pure - promote the indexed item. // Array literal where everything is pure - promote the indexed item.
// All other items can be thrown away. // All other items can be thrown away.
state.set_dirty(); state.set_dirty();
@ -816,9 +833,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
*expr = result; *expr = result;
} }
// array[-int] // array[-int]
(Expr::Array(a, pos), Expr::IntegerConstant(i, _)) (Expr::Array(a, pos), Expr::IntegerConstant(i, _)) if *i < 0 && i.checked_abs().map(|n| n as usize <= a.len()).unwrap_or(false) && a.iter().all(Expr::is_pure) => {
if *i < 0 && i.checked_abs().map(|n| n as usize <= a.len()).unwrap_or(false) && a.iter().all(Expr::is_pure) =>
{
// Array literal where everything is pure - promote the indexed item. // Array literal where everything is pure - promote the indexed item.
// All other items can be thrown away. // All other items can be thrown away.
state.set_dirty(); state.set_dirty();
@ -852,13 +867,13 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
(Expr::StringConstant(s, pos), Expr::IntegerConstant(i, _)) if *i >= 0 && (*i as usize) < s.chars().count() => { (Expr::StringConstant(s, pos), Expr::IntegerConstant(i, _)) if *i >= 0 && (*i as usize) < s.chars().count() => {
// String literal indexing - get the character // String literal indexing - get the character
state.set_dirty(); state.set_dirty();
*expr = Expr::CharConstant(s.chars().nth(*i as usize).expect("valid index"), *pos); *expr = Expr::CharConstant(s.chars().nth(*i as usize).unwrap(), *pos);
} }
// string[-int] // string[-int]
(Expr::StringConstant(s, pos), Expr::IntegerConstant(i, _)) if *i < 0 && i.checked_abs().map(|n| n as usize <= s.chars().count()).unwrap_or(false) => { (Expr::StringConstant(s, pos), Expr::IntegerConstant(i, _)) if *i < 0 && i.checked_abs().map(|n| n as usize <= s.chars().count()).unwrap_or(false) => {
// String literal indexing - get the character // String literal indexing - get the character
state.set_dirty(); state.set_dirty();
*expr = Expr::CharConstant(s.chars().rev().nth(i.abs() as usize - 1).expect("valid index"), *pos); *expr = Expr::CharConstant(s.chars().rev().nth(i.abs() as usize - 1).unwrap(), *pos);
} }
// var[rhs] // var[rhs]
(Expr::Variable(_, _, _), rhs) => optimize_expr(rhs, state, true), (Expr::Variable(_, _, _), rhs) => optimize_expr(rhs, state, true),
@ -885,39 +900,14 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
let mut n = 0; let mut n = 0;
// Merge consecutive strings // Merge consecutive strings
while n < x.len()-1 { while n < x.len() - 1 {
match (mem::take(&mut x[n]), mem::take(&mut x[n+1])) { match (mem::take(&mut x[n]), mem::take(&mut x[n+1])) {
(Expr::StringConstant(mut s1, pos), Expr::StringConstant(s2, _)) => { (Expr::StringConstant(mut s1, pos), Expr::StringConstant(s2, _)) => { s1 += s2; x[n] = Expr::StringConstant(s1, pos); x.remove(n+1); state.set_dirty(); }
s1 += s2; (expr1, Expr::Unit(_)) => { x[n] = expr1; x.remove(n+1); state.set_dirty(); }
x[n] = Expr::StringConstant(s1, pos); (Expr::Unit(_), expr2) => { x[n+1] = expr2; x.remove(n); state.set_dirty(); }
x.remove(n+1); (expr1, Expr::StringConstant(s, _)) if s.is_empty() => { x[n] = expr1; x.remove(n+1); state.set_dirty(); }
state.set_dirty(); (Expr::StringConstant(s, _), expr2) if s.is_empty()=> { x[n+1] = expr2; x.remove(n); state.set_dirty(); }
} (expr1, expr2) => { x[n] = expr1; x[n+1] = expr2; n += 1; }
(expr1, Expr::Unit(_)) => {
x[n] = expr1;
x.remove(n+1);
state.set_dirty();
}
(Expr::Unit(_), expr2) => {
x[n+1] = expr2;
x.remove(n);
state.set_dirty();
}
(expr1, Expr::StringConstant(s, _)) if s.is_empty() => {
x[n] = expr1;
x.remove(n+1);
state.set_dirty();
}
(Expr::StringConstant(s, _), expr2) if s.is_empty()=> {
x[n+1] = expr2;
x.remove(n);
state.set_dirty();
}
(expr1, expr2) => {
x[n] = expr1;
x[n+1] = expr2;
n += 1;
}
} }
} }
@ -927,7 +917,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Array(_, _) if expr.is_constant() => { Expr::Array(_, _) if expr.is_constant() => {
state.set_dirty(); state.set_dirty();
*expr = Expr::DynamicConstant(expr.get_literal_value().expect("constant").into(), expr.position()); *expr = Expr::DynamicConstant(expr.get_literal_value().unwrap().into(), expr.position());
} }
// [ items .. ] // [ items .. ]
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
@ -936,7 +926,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Map(_, _) if expr.is_constant() => { Expr::Map(_, _) if expr.is_constant() => {
state.set_dirty(); state.set_dirty();
*expr = Expr::DynamicConstant(expr.get_literal_value().expect("constant").into(), expr.position()); *expr = Expr::DynamicConstant(expr.get_literal_value().unwrap().into(), expr.position());
} }
// #{ key:value, .. } // #{ key:value, .. }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
@ -944,52 +934,24 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
// lhs && rhs // lhs && rhs
Expr::And(x, _) => match (&mut x.lhs, &mut x.rhs) { Expr::And(x, _) => match (&mut x.lhs, &mut x.rhs) {
// true && rhs -> rhs // true && rhs -> rhs
(Expr::BoolConstant(true, _), rhs) => { (Expr::BoolConstant(true, _), rhs) => { state.set_dirty(); optimize_expr(rhs, state, false); *expr = mem::take(rhs); }
state.set_dirty();
optimize_expr(rhs, state, false);
*expr = mem::take(rhs);
}
// false && rhs -> false // false && rhs -> false
(Expr::BoolConstant(false, pos), _) => { (Expr::BoolConstant(false, pos), _) => { state.set_dirty(); *expr = Expr::BoolConstant(false, *pos); }
state.set_dirty();
*expr = Expr::BoolConstant(false, *pos);
}
// lhs && true -> lhs // lhs && true -> lhs
(lhs, Expr::BoolConstant(true, _)) => { (lhs, Expr::BoolConstant(true, _)) => { state.set_dirty(); optimize_expr(lhs, state, false); *expr = mem::take(lhs); }
state.set_dirty();
optimize_expr(lhs, state, false);
*expr = mem::take(lhs);
}
// lhs && rhs // lhs && rhs
(lhs, rhs) => { (lhs, rhs) => { optimize_expr(lhs, state, false); optimize_expr(rhs, state, false); }
optimize_expr(lhs, state, false);
optimize_expr(rhs, state, false);
}
}, },
// lhs || rhs // lhs || rhs
Expr::Or(ref mut x, _) => match (&mut x.lhs, &mut x.rhs) { Expr::Or(ref mut x, _) => match (&mut x.lhs, &mut x.rhs) {
// false || rhs -> rhs // false || rhs -> rhs
(Expr::BoolConstant(false, _), rhs) => { (Expr::BoolConstant(false, _), rhs) => { state.set_dirty(); optimize_expr(rhs, state, false); *expr = mem::take(rhs); }
state.set_dirty();
optimize_expr(rhs, state, false);
*expr = mem::take(rhs);
}
// true || rhs -> true // true || rhs -> true
(Expr::BoolConstant(true, pos), _) => { (Expr::BoolConstant(true, pos), _) => { state.set_dirty(); *expr = Expr::BoolConstant(true, *pos); }
state.set_dirty();
*expr = Expr::BoolConstant(true, *pos);
}
// lhs || false // lhs || false
(lhs, Expr::BoolConstant(false, _)) => { (lhs, Expr::BoolConstant(false, _)) => { state.set_dirty(); optimize_expr(lhs, state, false); *expr = mem::take(lhs); }
state.set_dirty();
optimize_expr(lhs, state, false);
*expr = mem::take(lhs);
}
// lhs || rhs // lhs || rhs
(lhs, rhs) => { (lhs, rhs) => { optimize_expr(lhs, state, false); optimize_expr(rhs, state, false); }
optimize_expr(lhs, state, false);
optimize_expr(rhs, state, false);
}
}, },
// eval! // eval!
@ -1032,7 +994,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
=> { => {
let arg_values = &mut x.args.iter().map(|e| match e { let arg_values = &mut x.args.iter().map(|e| match e {
Expr::Stack(slot, _) => x.constants[*slot].clone(), Expr::Stack(slot, _) => x.constants[*slot].clone(),
_ => e.get_literal_value().expect("constant") _ => e.get_literal_value().unwrap()
}).collect::<StaticVec<_>>(); }).collect::<StaticVec<_>>();
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect(); let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
@ -1050,7 +1012,7 @@ 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 && !state.engine.has_native_fn_override(x.hashes.native, arg_types.as_ref()) => { _ if x.args.len() == 2 && !has_native_fn_override(state.engine, x.hashes.native, arg_types.as_ref()) => {
if let Some(result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1]) if let Some(result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1])
.and_then(|f| { .and_then(|f| {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
@ -1059,7 +1021,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
let lib = &[]; let lib = &[];
let context = (state.engine, x.name.as_str(), lib).into(); let context = (state.engine, x.name.as_str(), lib).into();
let (first, second) = arg_values.split_first_mut().expect("not empty"); 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()
}) { }) {
state.set_dirty(); state.set_dirty();
@ -1098,7 +1060,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
if !has_script_fn { if !has_script_fn {
let arg_values = &mut x.args.iter().map(|e| match e { let arg_values = &mut x.args.iter().map(|e| match e {
Expr::Stack(slot, _) => x.constants[*slot].clone(), Expr::Stack(slot, _) => x.constants[*slot].clone(),
_ => e.get_literal_value().expect("constant") _ => e.get_literal_value().unwrap()
}).collect::<StaticVec<_>>(); }).collect::<StaticVec<_>>();
let result = match x.name.as_str() { let result = match x.name.as_str() {
@ -1133,7 +1095,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
// constant-name // constant-name
Expr::Variable(_, pos, x) if x.1.is_none() && state.find_constant(&x.2).is_some() => { Expr::Variable(_, pos, x) if x.1.is_none() && state.find_constant(&x.2).is_some() => {
// Replace constant with value // Replace constant with value
*expr = Expr::from_dynamic(state.find_constant(&x.2).expect("exists").clone(), *pos); *expr = Expr::from_dynamic(state.find_constant(&x.2).unwrap().clone(), *pos);
state.set_dirty(); state.set_dirty();
} }
@ -1218,7 +1180,7 @@ pub fn optimize_into_ast(
params: fn_def.params.clone(), params: fn_def.params.clone(),
lib: None, lib: None,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
mods: Imports::new(), global: None,
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
comments: None, comments: None,
@ -1235,7 +1197,7 @@ pub fn optimize_into_ast(
let mut fn_def = crate::func::native::shared_take_or_clone(fn_def); let mut fn_def = crate::func::native::shared_take_or_clone(fn_def);
// Optimize the function body // Optimize the function body
let body = mem::take(fn_def.body.deref_mut()); let body = mem::take(&mut *fn_def.body);
*fn_def.body = *fn_def.body =
optimize_top_level(body, engine, scope, lib2, optimization_level); optimize_top_level(body, engine, scope, lib2, optimization_level);

View File

@ -1,7 +1,7 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
use crate::plugin::*; use crate::plugin::*;
use crate::{def_package, EvalAltResult, Position, INT}; use crate::{def_package, Position, RhaiError, RhaiResultOf, ERR, INT};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -9,9 +9,9 @@ use std::prelude::v1::*;
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
use num_traits::Float; use num_traits::Float;
#[inline(never)] #[inline]
pub fn make_err(msg: impl Into<String>) -> Box<EvalAltResult> { pub fn make_err(msg: impl Into<String>) -> RhaiError {
EvalAltResult::ErrorArithmetic(msg.into(), Position::NONE).into() ERR::ErrorArithmetic(msg.into(), Position::NONE).into()
} }
macro_rules! gen_arithmetic_functions { macro_rules! gen_arithmetic_functions {
@ -22,7 +22,7 @@ macro_rules! gen_arithmetic_functions {
#[export_module] #[export_module]
pub mod functions { pub mod functions {
#[rhai_fn(name = "+", return_raw)] #[rhai_fn(name = "+", return_raw)]
pub fn add(x: $arg_type, y: $arg_type) -> Result<$arg_type, Box<EvalAltResult>> { pub fn add(x: $arg_type, y: $arg_type) -> RhaiResultOf<$arg_type> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
x.checked_add(y).ok_or_else(|| make_err(format!("Addition overflow: {} + {}", x, y))) x.checked_add(y).ok_or_else(|| make_err(format!("Addition overflow: {} + {}", x, y)))
} else { } else {
@ -30,7 +30,7 @@ macro_rules! gen_arithmetic_functions {
} }
} }
#[rhai_fn(name = "-", return_raw)] #[rhai_fn(name = "-", return_raw)]
pub fn subtract(x: $arg_type, y: $arg_type) -> Result<$arg_type, Box<EvalAltResult>> { pub fn subtract(x: $arg_type, y: $arg_type) -> RhaiResultOf<$arg_type> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
x.checked_sub(y).ok_or_else(|| make_err(format!("Subtraction overflow: {} - {}", x, y))) x.checked_sub(y).ok_or_else(|| make_err(format!("Subtraction overflow: {} - {}", x, y)))
} else { } else {
@ -38,7 +38,7 @@ macro_rules! gen_arithmetic_functions {
} }
} }
#[rhai_fn(name = "*", return_raw)] #[rhai_fn(name = "*", return_raw)]
pub fn multiply(x: $arg_type, y: $arg_type) -> Result<$arg_type, Box<EvalAltResult>> { pub fn multiply(x: $arg_type, y: $arg_type) -> RhaiResultOf<$arg_type> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
x.checked_mul(y).ok_or_else(|| make_err(format!("Multiplication overflow: {} * {}", x, y))) x.checked_mul(y).ok_or_else(|| make_err(format!("Multiplication overflow: {} * {}", x, y)))
} else { } else {
@ -46,7 +46,7 @@ macro_rules! gen_arithmetic_functions {
} }
} }
#[rhai_fn(name = "/", return_raw)] #[rhai_fn(name = "/", return_raw)]
pub fn divide(x: $arg_type, y: $arg_type) -> Result<$arg_type, Box<EvalAltResult>> { pub fn divide(x: $arg_type, y: $arg_type) -> RhaiResultOf<$arg_type> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
// Detect division by zero // Detect division by zero
if y == 0 { if y == 0 {
@ -59,7 +59,7 @@ macro_rules! gen_arithmetic_functions {
} }
} }
#[rhai_fn(name = "%", return_raw)] #[rhai_fn(name = "%", return_raw)]
pub fn modulo(x: $arg_type, y: $arg_type) -> Result<$arg_type, Box<EvalAltResult>> { pub fn modulo(x: $arg_type, y: $arg_type) -> RhaiResultOf<$arg_type> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
x.checked_rem(y).ok_or_else(|| make_err(format!("Modulo division by zero or overflow: {} % {}", x, y))) x.checked_rem(y).ok_or_else(|| make_err(format!("Modulo division by zero or overflow: {} % {}", x, y)))
} else { } else {
@ -67,7 +67,7 @@ macro_rules! gen_arithmetic_functions {
} }
} }
#[rhai_fn(name = "**", return_raw)] #[rhai_fn(name = "**", return_raw)]
pub fn power(x: $arg_type, y: INT) -> Result<$arg_type, Box<EvalAltResult>> { pub fn power(x: $arg_type, y: INT) -> RhaiResultOf<$arg_type> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) { if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) {
Err(make_err(format!("Integer raised to too large an index: {} ~ {}", x, y))) Err(make_err(format!("Integer raised to too large an index: {} ~ {}", x, y)))
@ -82,7 +82,7 @@ macro_rules! gen_arithmetic_functions {
} }
#[rhai_fn(name = "<<", return_raw)] #[rhai_fn(name = "<<", return_raw)]
pub fn shift_left(x: $arg_type, y: INT) -> Result<$arg_type, Box<EvalAltResult>> { pub fn shift_left(x: $arg_type, y: INT) -> RhaiResultOf<$arg_type> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) { if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) {
Err(make_err(format!("Left-shift by too many bits: {} << {}", x, y))) Err(make_err(format!("Left-shift by too many bits: {} << {}", x, y)))
@ -96,7 +96,7 @@ macro_rules! gen_arithmetic_functions {
} }
} }
#[rhai_fn(name = ">>", return_raw)] #[rhai_fn(name = ">>", return_raw)]
pub fn shift_right(x: $arg_type, y: INT) -> Result<$arg_type, Box<EvalAltResult>> { pub fn shift_right(x: $arg_type, y: INT) -> RhaiResultOf<$arg_type> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) { if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) {
Err(make_err(format!("Right-shift by too many bits: {} >> {}", x, y))) Err(make_err(format!("Right-shift by too many bits: {} >> {}", x, y)))
@ -146,7 +146,7 @@ macro_rules! gen_signed_functions {
#[export_module] #[export_module]
pub mod functions { pub mod functions {
#[rhai_fn(name = "-", return_raw)] #[rhai_fn(name = "-", return_raw)]
pub fn neg(x: $arg_type) -> Result<$arg_type, Box<EvalAltResult>> { pub fn neg(x: $arg_type) -> RhaiResultOf<$arg_type> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
x.checked_neg().ok_or_else(|| make_err(format!("Negation overflow: -{}", x))) x.checked_neg().ok_or_else(|| make_err(format!("Negation overflow: -{}", x)))
} else { } else {
@ -158,7 +158,7 @@ macro_rules! gen_signed_functions {
x x
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn abs(x: $arg_type) -> Result<$arg_type, Box<EvalAltResult>> { pub fn abs(x: $arg_type) -> RhaiResultOf<$arg_type> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
x.checked_abs().ok_or_else(|| make_err(format!("Negation overflow: -{}", x))) x.checked_abs().ok_or_else(|| make_err(format!("Negation overflow: -{}", x)))
} else { } else {
@ -179,36 +179,40 @@ macro_rules! reg_functions {
)* } )* }
} }
def_package!(crate:ArithmeticPackage:"Basic arithmetic", lib, { def_package! {
lib.standard = true; /// Basic arithmetic package.
crate::ArithmeticPackage => |lib| {
lib.standard = true;
combine_with_exported_module!(lib, "int", int_functions); combine_with_exported_module!(lib, "int", int_functions);
reg_functions!(lib += signed_basic; INT); reg_functions!(lib += signed_basic; INT);
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))] #[cfg(not(feature = "only_i64"))]
{
reg_functions!(lib += arith_numbers; i8, u8, i16, u16, i32, u32, u64);
reg_functions!(lib += signed_numbers; i8, i16, i32);
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
{ {
reg_functions!(lib += arith_num_128; i128, u128); reg_functions!(lib += arith_numbers; i8, u8, i16, u16, i32, u32, u64);
reg_functions!(lib += signed_num_128; i128); reg_functions!(lib += signed_numbers; i8, i16, i32);
#[cfg(not(target_arch = "wasm32"))]
#[cfg(not(target_arch = "wasm64"))]
{
reg_functions!(lib += arith_num_128; i128, u128);
reg_functions!(lib += signed_num_128; i128);
}
} }
}
// Basic arithmetic for floating-point // Basic arithmetic for floating-point
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
{ {
combine_with_exported_module!(lib, "f32", f32_functions); combine_with_exported_module!(lib, "f32", f32_functions);
combine_with_exported_module!(lib, "f64", f64_functions); combine_with_exported_module!(lib, "f64", f64_functions);
} }
// Decimal functions // Decimal functions
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
combine_with_exported_module!(lib, "decimal", decimal_functions); combine_with_exported_module!(lib, "decimal", decimal_functions);
}); }
}
#[export_module] #[export_module]
mod int_functions { mod int_functions {
@ -234,7 +238,8 @@ gen_arithmetic_functions!(arith_numbers => i8, u8, i16, u16, i32, u32, u64);
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))] #[cfg(not(feature = "only_i64"))]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #[cfg(not(target_arch = "wasm32"))]
#[cfg(not(target_arch = "wasm64"))]
gen_arithmetic_functions!(arith_num_128 => i128, u128); gen_arithmetic_functions!(arith_num_128 => i128, u128);
gen_signed_functions!(signed_basic => INT); gen_signed_functions!(signed_basic => INT);
@ -245,14 +250,13 @@ gen_signed_functions!(signed_numbers => i8, i16, i32);
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))] #[cfg(not(feature = "only_i64"))]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #[cfg(not(target_arch = "wasm32"))]
#[cfg(not(target_arch = "wasm64"))]
gen_signed_functions!(signed_num_128 => i128); gen_signed_functions!(signed_num_128 => i128);
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
#[export_module] #[export_module]
mod f32_functions { mod f32_functions {
use crate::EvalAltResult;
#[cfg(not(feature = "f32_float"))] #[cfg(not(feature = "f32_float"))]
pub mod basic_arithmetic { pub mod basic_arithmetic {
#[rhai_fn(name = "+")] #[rhai_fn(name = "+")]
@ -334,7 +338,7 @@ mod f32_functions {
x.abs() x.abs()
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn sign(x: f32) -> Result<INT, Box<EvalAltResult>> { pub fn sign(x: f32) -> RhaiResultOf<INT> {
match x.signum() { match x.signum() {
_ if x == 0.0 => Ok(0), _ if x == 0.0 => Ok(0),
x if x.is_nan() => Err(make_err("Sign of NaN is undefined")), x if x.is_nan() => Err(make_err("Sign of NaN is undefined")),
@ -346,7 +350,7 @@ mod f32_functions {
x == 0.0 x == 0.0
} }
#[rhai_fn(name = "**", return_raw)] #[rhai_fn(name = "**", return_raw)]
pub fn pow_f_i(x: f32, y: INT) -> Result<f32, Box<EvalAltResult>> { pub fn pow_f_i(x: f32, y: INT) -> RhaiResultOf<f32> {
if cfg!(not(feature = "unchecked")) && y > (i32::MAX as INT) { if cfg!(not(feature = "unchecked")) && y > (i32::MAX as INT) {
Err(make_err(format!( Err(make_err(format!(
"Number raised to too large an index: {} ~ {}", "Number raised to too large an index: {} ~ {}",
@ -442,7 +446,7 @@ mod f64_functions {
x.abs() x.abs()
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn sign(x: f64) -> Result<INT, Box<EvalAltResult>> { pub fn sign(x: f64) -> RhaiResultOf<INT> {
match x.signum() { match x.signum() {
_ if x == 0.0 => Ok(0), _ if x == 0.0 => Ok(0),
x if x.is_nan() => Err(make_err("Sign of NaN is undefined")), x if x.is_nan() => Err(make_err("Sign of NaN is undefined")),
@ -462,7 +466,7 @@ pub mod decimal_functions {
use rust_decimal::{prelude::Zero, Decimal, MathematicalOps}; use rust_decimal::{prelude::Zero, Decimal, MathematicalOps};
#[rhai_fn(skip, return_raw)] #[rhai_fn(skip, return_raw)]
pub fn add(x: Decimal, y: Decimal) -> Result<Decimal, Box<EvalAltResult>> { pub fn add(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
x.checked_add(y) x.checked_add(y)
.ok_or_else(|| make_err(format!("Addition overflow: {} + {}", x, y))) .ok_or_else(|| make_err(format!("Addition overflow: {} + {}", x, y)))
@ -471,7 +475,7 @@ pub mod decimal_functions {
} }
} }
#[rhai_fn(skip, return_raw)] #[rhai_fn(skip, return_raw)]
pub fn subtract(x: Decimal, y: Decimal) -> Result<Decimal, Box<EvalAltResult>> { pub fn subtract(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
x.checked_sub(y) x.checked_sub(y)
.ok_or_else(|| make_err(format!("Subtraction overflow: {} - {}", x, y))) .ok_or_else(|| make_err(format!("Subtraction overflow: {} - {}", x, y)))
@ -480,7 +484,7 @@ pub mod decimal_functions {
} }
} }
#[rhai_fn(skip, return_raw)] #[rhai_fn(skip, return_raw)]
pub fn multiply(x: Decimal, y: Decimal) -> Result<Decimal, Box<EvalAltResult>> { pub fn multiply(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
x.checked_mul(y) x.checked_mul(y)
.ok_or_else(|| make_err(format!("Multiplication overflow: {} * {}", x, y))) .ok_or_else(|| make_err(format!("Multiplication overflow: {} * {}", x, y)))
@ -489,7 +493,7 @@ pub mod decimal_functions {
} }
} }
#[rhai_fn(skip, return_raw)] #[rhai_fn(skip, return_raw)]
pub fn divide(x: Decimal, y: Decimal) -> Result<Decimal, Box<EvalAltResult>> { pub fn divide(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
// Detect division by zero // Detect division by zero
if y == Decimal::zero() { if y == Decimal::zero() {
@ -503,7 +507,7 @@ pub mod decimal_functions {
} }
} }
#[rhai_fn(skip, return_raw)] #[rhai_fn(skip, return_raw)]
pub fn modulo(x: Decimal, y: Decimal) -> Result<Decimal, Box<EvalAltResult>> { pub fn modulo(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
x.checked_rem(y).ok_or_else(|| { x.checked_rem(y).ok_or_else(|| {
make_err(format!( make_err(format!(
@ -516,7 +520,7 @@ pub mod decimal_functions {
} }
} }
#[rhai_fn(skip, return_raw)] #[rhai_fn(skip, return_raw)]
pub fn power(x: Decimal, y: Decimal) -> Result<Decimal, Box<EvalAltResult>> { pub fn power(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
x.checked_powd(y) x.checked_powd(y)
.ok_or_else(|| make_err(format!("Exponential overflow: {} + {}", x, y))) .ok_or_else(|| make_err(format!("Exponential overflow: {} + {}", x, y)))

View File

@ -4,52 +4,56 @@
use crate::engine::OP_EQUALS; use crate::engine::OP_EQUALS;
use crate::plugin::*; use crate::plugin::*;
use crate::{ use crate::{
def_package, Array, Dynamic, EvalAltResult, ExclusiveRange, FnPtr, InclusiveRange, def_package, Array, Dynamic, ExclusiveRange, FnPtr, InclusiveRange, NativeCallContext,
NativeCallContext, Position, INT, Position, RhaiResultOf, ERR, INT,
}; };
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
use std::{any::TypeId, cmp::Ordering, mem}; use std::{any::TypeId, cmp::Ordering, mem};
def_package!(crate:BasicArrayPackage:"Basic array utilities.", lib, { def_package! {
lib.standard = true; /// Package of basic array utilities.
crate::BasicArrayPackage => |lib| {
lib.standard = true;
combine_with_exported_module!(lib, "array", array_functions); combine_with_exported_module!(lib, "array", array_functions);
// Register array iterator // Register array iterator
lib.set_iterable::<Array>(); lib.set_iterable::<Array>();
}); }
}
#[export_module] #[export_module]
mod array_functions { pub mod array_functions {
#[rhai_fn(name = "len", get = "len", pure)] #[rhai_fn(name = "len", get = "len", pure)]
pub fn len(array: &mut Array) -> INT { pub fn len(array: &mut Array) -> INT {
array.len() as INT array.len() as INT
} }
#[rhai_fn(name = "push", name = "+=")]
pub fn push(array: &mut Array, item: Dynamic) { pub fn push(array: &mut Array, item: Dynamic) {
array.push(item); array.push(item);
} }
#[rhai_fn(name = "append", name = "+=")] pub fn append(array1: &mut Array, array2: Array) {
pub fn append(array: &mut Array, y: Array) { if !array2.is_empty() {
if !y.is_empty() { if array1.is_empty() {
if array.is_empty() { *array1 = array2;
*array = y;
} else { } else {
array.extend(y); array1.extend(array2);
} }
} }
} }
#[rhai_fn(name = "+")] #[rhai_fn(name = "+")]
pub fn concat(mut array: Array, y: Array) -> Array { pub fn concat(array1: Array, array2: Array) -> Array {
if !y.is_empty() { if !array2.is_empty() {
if array.is_empty() { if array1.is_empty() {
array = y; array2
} else { } else {
array.extend(y); let mut array = array1;
array.extend(array2);
array
} }
} else {
array1
} }
array
} }
pub fn insert(array: &mut Array, position: INT, item: Dynamic) { pub fn insert(array: &mut Array, position: INT, item: Dynamic) {
if array.is_empty() { if array.is_empty() {
@ -76,25 +80,45 @@ mod array_functions {
array: &mut Array, array: &mut Array,
len: INT, len: INT,
item: Dynamic, item: Dynamic,
) -> Result<(), Box<EvalAltResult>> { ) -> RhaiResultOf<()> {
if len <= 0 { if len <= 0 || (len as usize) <= array.len() {
return Ok(()); return Ok(());
} }
let _ctx = ctx; let _ctx = ctx;
let len = len as usize;
// Check if array will be over max size limit // Check if array will be over max size limit
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if _ctx.engine().max_array_size() > 0 && (len as usize) > _ctx.engine().max_array_size() { if _ctx.engine().max_array_size() > 0 && len > _ctx.engine().max_array_size() {
return Err(EvalAltResult::ErrorDataTooLarge( return Err(ERR::ErrorDataTooLarge("Size of array".to_string(), Position::NONE).into());
"Size of array".to_string(),
Position::NONE,
)
.into());
} }
if len as usize > array.len() { #[cfg(not(feature = "unchecked"))]
array.resize(len as usize, item); let check_sizes = match item.0 {
crate::types::dynamic::Union::Array(_, _, _)
| crate::types::dynamic::Union::Str(_, _, _) => true,
#[cfg(not(feature = "no_object"))]
crate::types::dynamic::Union::Map(_, _, _) => true,
_ => false,
};
#[cfg(feature = "unchecked")]
let check_sizes = false;
if check_sizes {
let arr = mem::take(array);
let mut arr = Dynamic::from_array(arr);
while array.len() < len {
arr.write_lock::<Array>().unwrap().push(item.clone());
#[cfg(not(feature = "unchecked"))]
_ctx.engine().ensure_data_size_within_limits(&arr)?;
}
*array = arr.into_array().unwrap();
} else {
array.resize(len, item);
} }
Ok(()) Ok(())
@ -170,9 +194,9 @@ mod array_functions {
let arr_len = array.len(); let arr_len = array.len();
start start
.checked_abs() .checked_abs()
.map_or(0, |n| arr_len - (n as usize).min(arr_len)) .map_or(0, |n| arr_len - usize::min(n as usize, arr_len))
} else if start as usize >= array.len() { } else if start as usize >= array.len() {
array.extend(replace.into_iter()); array.extend(replace);
return; return;
} else { } else {
start as usize start as usize
@ -209,7 +233,7 @@ mod array_functions {
let arr_len = array.len(); let arr_len = array.len();
start start
.checked_abs() .checked_abs()
.map_or(0, |n| arr_len - (n as usize).min(arr_len)) .map_or(0, |n| arr_len - usize::min(n as usize, arr_len))
} else if start as usize >= array.len() { } else if start as usize >= array.len() {
return Array::new(); return Array::new();
} else { } else {
@ -259,11 +283,7 @@ mod array_functions {
} }
} }
#[rhai_fn(return_raw, pure)] #[rhai_fn(return_raw, pure)]
pub fn map( pub fn map(ctx: NativeCallContext, array: &mut Array, mapper: FnPtr) -> RhaiResultOf<Array> {
ctx: NativeCallContext,
array: &mut Array,
mapper: FnPtr,
) -> Result<Array, Box<EvalAltResult>> {
if array.is_empty() { if array.is_empty() {
return Ok(array.clone()); return Ok(array.clone());
} }
@ -275,7 +295,7 @@ mod array_functions {
mapper mapper
.call_raw(&ctx, None, [item.clone()]) .call_raw(&ctx, None, [item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) ERR::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(mapper.fn_name()) => if fn_sig.starts_with(mapper.fn_name()) =>
{ {
mapper.call_raw(&ctx, None, [item.clone(), (i as INT).into()]) mapper.call_raw(&ctx, None, [item.clone(), (i as INT).into()])
@ -283,7 +303,7 @@ mod array_functions {
_ => Err(err), _ => Err(err),
}) })
.map_err(|err| { .map_err(|err| {
Box::new(EvalAltResult::ErrorInFunctionCall( Box::new(ERR::ErrorInFunctionCall(
"map".to_string(), "map".to_string(),
ctx.source().unwrap_or("").to_string(), ctx.source().unwrap_or("").to_string(),
err, err,
@ -300,16 +320,12 @@ mod array_functions {
ctx: NativeCallContext, ctx: NativeCallContext,
array: &mut Array, array: &mut Array,
mapper: &str, mapper: &str,
) -> Result<Array, Box<EvalAltResult>> { ) -> RhaiResultOf<Array> {
map(ctx, array, FnPtr::new(mapper)?) map(ctx, array, FnPtr::new(mapper)?)
} }
#[rhai_fn(return_raw, pure)] #[rhai_fn(return_raw, pure)]
pub fn filter( pub fn filter(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf<Array> {
ctx: NativeCallContext,
array: &mut Array,
filter: FnPtr,
) -> Result<Array, Box<EvalAltResult>> {
if array.is_empty() { if array.is_empty() {
return Ok(array.clone()); return Ok(array.clone());
} }
@ -320,7 +336,7 @@ mod array_functions {
if filter if filter
.call_raw(&ctx, None, [item.clone()]) .call_raw(&ctx, None, [item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) ERR::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(filter.fn_name()) => if fn_sig.starts_with(filter.fn_name()) =>
{ {
filter.call_raw(&ctx, None, [item.clone(), (i as INT).into()]) filter.call_raw(&ctx, None, [item.clone(), (i as INT).into()])
@ -328,7 +344,7 @@ mod array_functions {
_ => Err(err), _ => Err(err),
}) })
.map_err(|err| { .map_err(|err| {
Box::new(EvalAltResult::ErrorInFunctionCall( Box::new(ERR::ErrorInFunctionCall(
"filter".to_string(), "filter".to_string(),
ctx.source().unwrap_or("").to_string(), ctx.source().unwrap_or("").to_string(),
err, err,
@ -349,7 +365,7 @@ mod array_functions {
ctx: NativeCallContext, ctx: NativeCallContext,
array: &mut Array, array: &mut Array,
filter_func: &str, filter_func: &str,
) -> Result<Array, Box<EvalAltResult>> { ) -> RhaiResultOf<Array> {
filter(ctx, array, FnPtr::new(filter_func)?) filter(ctx, array, FnPtr::new(filter_func)?)
} }
#[rhai_fn(return_raw, pure)] #[rhai_fn(return_raw, pure)]
@ -357,7 +373,7 @@ mod array_functions {
ctx: NativeCallContext, ctx: NativeCallContext,
array: &mut Array, array: &mut Array,
value: Dynamic, value: Dynamic,
) -> Result<bool, Box<EvalAltResult>> { ) -> RhaiResultOf<bool> {
if array.is_empty() { if array.is_empty() {
return Ok(false); return Ok(false);
} }
@ -366,9 +382,7 @@ mod array_functions {
if ctx if ctx
.call_fn_raw(OP_EQUALS, true, false, &mut [item, &mut value.clone()]) .call_fn_raw(OP_EQUALS, true, false, &mut [item, &mut value.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(ref fn_sig, _) ERR::ErrorFunctionNotFound(ref fn_sig, _) if fn_sig.starts_with(OP_EQUALS) => {
if fn_sig.starts_with(OP_EQUALS) =>
{
if item.type_id() == value.type_id() { if item.type_id() == value.type_id() {
// No default when comparing same type // No default when comparing same type
Err(err) Err(err)
@ -392,7 +406,7 @@ mod array_functions {
ctx: NativeCallContext, ctx: NativeCallContext,
array: &mut Array, array: &mut Array,
value: Dynamic, value: Dynamic,
) -> Result<INT, Box<EvalAltResult>> { ) -> RhaiResultOf<INT> {
if array.is_empty() { if array.is_empty() {
Ok(-1) Ok(-1)
} else { } else {
@ -405,7 +419,7 @@ mod array_functions {
array: &mut Array, array: &mut Array,
value: Dynamic, value: Dynamic,
start: INT, start: INT,
) -> Result<INT, Box<EvalAltResult>> { ) -> RhaiResultOf<INT> {
if array.is_empty() { if array.is_empty() {
return Ok(-1); return Ok(-1);
} }
@ -414,7 +428,7 @@ mod array_functions {
let arr_len = array.len(); let arr_len = array.len();
start start
.checked_abs() .checked_abs()
.map_or(0, |n| arr_len - (n as usize).min(arr_len)) .map_or(0, |n| arr_len - usize::min(n as usize, arr_len))
} else if start as usize >= array.len() { } else if start as usize >= array.len() {
return Ok(-1); return Ok(-1);
} else { } else {
@ -425,9 +439,7 @@ mod array_functions {
if ctx if ctx
.call_fn_raw(OP_EQUALS, true, false, &mut [item, &mut value.clone()]) .call_fn_raw(OP_EQUALS, true, false, &mut [item, &mut value.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(ref fn_sig, _) ERR::ErrorFunctionNotFound(ref fn_sig, _) if fn_sig.starts_with(OP_EQUALS) => {
if fn_sig.starts_with(OP_EQUALS) =>
{
if item.type_id() == value.type_id() { if item.type_id() == value.type_id() {
// No default when comparing same type // No default when comparing same type
Err(err) Err(err)
@ -451,7 +463,7 @@ mod array_functions {
ctx: NativeCallContext, ctx: NativeCallContext,
array: &mut Array, array: &mut Array,
filter: &str, filter: &str,
) -> Result<INT, Box<EvalAltResult>> { ) -> RhaiResultOf<INT> {
index_of_filter(ctx, array, FnPtr::new(filter)?) index_of_filter(ctx, array, FnPtr::new(filter)?)
} }
#[rhai_fn(name = "index_of", return_raw, pure)] #[rhai_fn(name = "index_of", return_raw, pure)]
@ -459,7 +471,7 @@ mod array_functions {
ctx: NativeCallContext, ctx: NativeCallContext,
array: &mut Array, array: &mut Array,
filter: FnPtr, filter: FnPtr,
) -> Result<INT, Box<EvalAltResult>> { ) -> RhaiResultOf<INT> {
if array.is_empty() { if array.is_empty() {
Ok(-1) Ok(-1)
} else { } else {
@ -472,7 +484,7 @@ mod array_functions {
array: &mut Array, array: &mut Array,
filter: FnPtr, filter: FnPtr,
start: INT, start: INT,
) -> Result<INT, Box<EvalAltResult>> { ) -> RhaiResultOf<INT> {
if array.is_empty() { if array.is_empty() {
return Ok(-1); return Ok(-1);
} }
@ -481,7 +493,7 @@ mod array_functions {
let arr_len = array.len(); let arr_len = array.len();
start start
.checked_abs() .checked_abs()
.map_or(0, |n| arr_len - (n as usize).min(arr_len)) .map_or(0, |n| arr_len - usize::min(n as usize, arr_len))
} else if start as usize >= array.len() { } else if start as usize >= array.len() {
return Ok(-1); return Ok(-1);
} else { } else {
@ -492,7 +504,7 @@ mod array_functions {
if filter if filter
.call_raw(&ctx, None, [item.clone()]) .call_raw(&ctx, None, [item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) ERR::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(filter.fn_name()) => if fn_sig.starts_with(filter.fn_name()) =>
{ {
filter.call_raw(&ctx, None, [item.clone(), (i as INT).into()]) filter.call_raw(&ctx, None, [item.clone(), (i as INT).into()])
@ -500,7 +512,7 @@ mod array_functions {
_ => Err(err), _ => Err(err),
}) })
.map_err(|err| { .map_err(|err| {
Box::new(EvalAltResult::ErrorInFunctionCall( Box::new(ERR::ErrorInFunctionCall(
"index_of".to_string(), "index_of".to_string(),
ctx.source().unwrap_or("").to_string(), ctx.source().unwrap_or("").to_string(),
err, err,
@ -522,15 +534,11 @@ mod array_functions {
array: &mut Array, array: &mut Array,
filter: &str, filter: &str,
start: INT, start: INT,
) -> Result<INT, Box<EvalAltResult>> { ) -> RhaiResultOf<INT> {
index_of_filter_starting_from(ctx, array, FnPtr::new(filter)?, start) index_of_filter_starting_from(ctx, array, FnPtr::new(filter)?, start)
} }
#[rhai_fn(return_raw, pure)] #[rhai_fn(return_raw, pure)]
pub fn some( pub fn some(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf<bool> {
ctx: NativeCallContext,
array: &mut Array,
filter: FnPtr,
) -> Result<bool, Box<EvalAltResult>> {
if array.is_empty() { if array.is_empty() {
return Ok(false); return Ok(false);
} }
@ -539,7 +547,7 @@ mod array_functions {
if filter if filter
.call_raw(&ctx, None, [item.clone()]) .call_raw(&ctx, None, [item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) ERR::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(filter.fn_name()) => if fn_sig.starts_with(filter.fn_name()) =>
{ {
filter.call_raw(&ctx, None, [item.clone(), (i as INT).into()]) filter.call_raw(&ctx, None, [item.clone(), (i as INT).into()])
@ -547,7 +555,7 @@ mod array_functions {
_ => Err(err), _ => Err(err),
}) })
.map_err(|err| { .map_err(|err| {
Box::new(EvalAltResult::ErrorInFunctionCall( Box::new(ERR::ErrorInFunctionCall(
"some".to_string(), "some".to_string(),
ctx.source().unwrap_or("").to_string(), ctx.source().unwrap_or("").to_string(),
err, err,
@ -568,15 +576,11 @@ mod array_functions {
ctx: NativeCallContext, ctx: NativeCallContext,
array: &mut Array, array: &mut Array,
filter: &str, filter: &str,
) -> Result<bool, Box<EvalAltResult>> { ) -> RhaiResultOf<bool> {
some(ctx, array, FnPtr::new(filter)?) some(ctx, array, FnPtr::new(filter)?)
} }
#[rhai_fn(return_raw, pure)] #[rhai_fn(return_raw, pure)]
pub fn all( pub fn all(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf<bool> {
ctx: NativeCallContext,
array: &mut Array,
filter: FnPtr,
) -> Result<bool, Box<EvalAltResult>> {
if array.is_empty() { if array.is_empty() {
return Ok(true); return Ok(true);
} }
@ -585,7 +589,7 @@ mod array_functions {
if !filter if !filter
.call_raw(&ctx, None, [item.clone()]) .call_raw(&ctx, None, [item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) ERR::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(filter.fn_name()) => if fn_sig.starts_with(filter.fn_name()) =>
{ {
filter.call_raw(&ctx, None, [item.clone(), (i as INT).into()]) filter.call_raw(&ctx, None, [item.clone(), (i as INT).into()])
@ -593,7 +597,7 @@ mod array_functions {
_ => Err(err), _ => Err(err),
}) })
.map_err(|err| { .map_err(|err| {
Box::new(EvalAltResult::ErrorInFunctionCall( Box::new(ERR::ErrorInFunctionCall(
"all".to_string(), "all".to_string(),
ctx.source().unwrap_or("").to_string(), ctx.source().unwrap_or("").to_string(),
err, err,
@ -614,11 +618,11 @@ mod array_functions {
ctx: NativeCallContext, ctx: NativeCallContext,
array: &mut Array, array: &mut Array,
filter: &str, filter: &str,
) -> Result<bool, Box<EvalAltResult>> { ) -> RhaiResultOf<bool> {
all(ctx, array, FnPtr::new(filter)?) all(ctx, array, FnPtr::new(filter)?)
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn dedup(ctx: NativeCallContext, array: &mut Array) -> Result<(), Box<EvalAltResult>> { pub fn dedup(ctx: NativeCallContext, array: &mut Array) -> RhaiResultOf<()> {
dedup_with_fn_name(ctx, array, OP_EQUALS) dedup_with_fn_name(ctx, array, OP_EQUALS)
} }
#[rhai_fn(name = "dedup", return_raw)] #[rhai_fn(name = "dedup", return_raw)]
@ -626,7 +630,7 @@ mod array_functions {
ctx: NativeCallContext, ctx: NativeCallContext,
array: &mut Array, array: &mut Array,
comparer: FnPtr, comparer: FnPtr,
) -> Result<(), Box<EvalAltResult>> { ) -> RhaiResultOf<()> {
if array.is_empty() { if array.is_empty() {
return Ok(()); return Ok(());
} }
@ -646,15 +650,11 @@ mod array_functions {
ctx: NativeCallContext, ctx: NativeCallContext,
array: &mut Array, array: &mut Array,
comparer: &str, comparer: &str,
) -> Result<(), Box<EvalAltResult>> { ) -> RhaiResultOf<()> {
dedup_by_comparer(ctx, array, FnPtr::new(comparer)?) dedup_by_comparer(ctx, array, FnPtr::new(comparer)?)
} }
#[rhai_fn(return_raw, pure)] #[rhai_fn(return_raw, pure)]
pub fn reduce( pub fn reduce(ctx: NativeCallContext, array: &mut Array, reducer: FnPtr) -> RhaiResult {
ctx: NativeCallContext,
array: &mut Array,
reducer: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> {
reduce_with_initial(ctx, array, reducer, Dynamic::UNIT) reduce_with_initial(ctx, array, reducer, Dynamic::UNIT)
} }
#[rhai_fn(name = "reduce", return_raw, pure)] #[rhai_fn(name = "reduce", return_raw, pure)]
@ -662,7 +662,7 @@ mod array_functions {
ctx: NativeCallContext, ctx: NativeCallContext,
array: &mut Array, array: &mut Array,
reducer: &str, reducer: &str,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> RhaiResult {
reduce(ctx, array, FnPtr::new(reducer)?) reduce(ctx, array, FnPtr::new(reducer)?)
} }
#[rhai_fn(name = "reduce", return_raw, pure)] #[rhai_fn(name = "reduce", return_raw, pure)]
@ -671,7 +671,7 @@ mod array_functions {
array: &mut Array, array: &mut Array,
reducer: FnPtr, reducer: FnPtr,
initial: Dynamic, initial: Dynamic,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> RhaiResult {
if array.is_empty() { if array.is_empty() {
return Ok(initial); return Ok(initial);
} }
@ -684,7 +684,7 @@ mod array_functions {
result = reducer result = reducer
.call_raw(&ctx, None, [result.clone(), item.clone()]) .call_raw(&ctx, None, [result.clone(), item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) ERR::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(reducer.fn_name()) => if fn_sig.starts_with(reducer.fn_name()) =>
{ {
reducer.call_raw(&ctx, None, [result, item, (i as INT).into()]) reducer.call_raw(&ctx, None, [result, item, (i as INT).into()])
@ -692,7 +692,7 @@ mod array_functions {
_ => Err(err), _ => Err(err),
}) })
.map_err(|err| { .map_err(|err| {
Box::new(EvalAltResult::ErrorInFunctionCall( Box::new(ERR::ErrorInFunctionCall(
"reduce".to_string(), "reduce".to_string(),
ctx.source().unwrap_or("").to_string(), ctx.source().unwrap_or("").to_string(),
err, err,
@ -709,15 +709,11 @@ mod array_functions {
array: &mut Array, array: &mut Array,
reducer: &str, reducer: &str,
initial: Dynamic, initial: Dynamic,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> RhaiResult {
reduce_with_initial(ctx, array, FnPtr::new(reducer)?, initial) reduce_with_initial(ctx, array, FnPtr::new(reducer)?, initial)
} }
#[rhai_fn(return_raw, pure)] #[rhai_fn(return_raw, pure)]
pub fn reduce_rev( pub fn reduce_rev(ctx: NativeCallContext, array: &mut Array, reducer: FnPtr) -> RhaiResult {
ctx: NativeCallContext,
array: &mut Array,
reducer: FnPtr,
) -> Result<Dynamic, Box<EvalAltResult>> {
reduce_rev_with_initial(ctx, array, reducer, Dynamic::UNIT) reduce_rev_with_initial(ctx, array, reducer, Dynamic::UNIT)
} }
#[rhai_fn(name = "reduce_rev", return_raw, pure)] #[rhai_fn(name = "reduce_rev", return_raw, pure)]
@ -725,7 +721,7 @@ mod array_functions {
ctx: NativeCallContext, ctx: NativeCallContext,
array: &mut Array, array: &mut Array,
reducer: &str, reducer: &str,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> RhaiResult {
reduce_rev(ctx, array, FnPtr::new(reducer)?) reduce_rev(ctx, array, FnPtr::new(reducer)?)
} }
#[rhai_fn(name = "reduce_rev", return_raw, pure)] #[rhai_fn(name = "reduce_rev", return_raw, pure)]
@ -734,7 +730,7 @@ mod array_functions {
array: &mut Array, array: &mut Array,
reducer: FnPtr, reducer: FnPtr,
initial: Dynamic, initial: Dynamic,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> RhaiResult {
if array.is_empty() { if array.is_empty() {
return Ok(initial); return Ok(initial);
} }
@ -748,7 +744,7 @@ mod array_functions {
result = reducer result = reducer
.call_raw(&ctx, None, [result.clone(), item.clone()]) .call_raw(&ctx, None, [result.clone(), item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) ERR::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(reducer.fn_name()) => if fn_sig.starts_with(reducer.fn_name()) =>
{ {
reducer.call_raw(&ctx, None, [result, item, ((len - 1 - i) as INT).into()]) reducer.call_raw(&ctx, None, [result, item, ((len - 1 - i) as INT).into()])
@ -756,7 +752,7 @@ mod array_functions {
_ => Err(err), _ => Err(err),
}) })
.map_err(|err| { .map_err(|err| {
Box::new(EvalAltResult::ErrorInFunctionCall( Box::new(ERR::ErrorInFunctionCall(
"reduce_rev".to_string(), "reduce_rev".to_string(),
ctx.source().unwrap_or("").to_string(), ctx.source().unwrap_or("").to_string(),
err, err,
@ -773,7 +769,7 @@ mod array_functions {
array: &mut Array, array: &mut Array,
reducer: &str, reducer: &str,
initial: Dynamic, initial: Dynamic,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> RhaiResult {
reduce_rev_with_initial(ctx, array, FnPtr::new(reducer)?, initial) reduce_rev_with_initial(ctx, array, FnPtr::new(reducer)?, initial)
} }
#[rhai_fn(name = "sort", return_raw)] #[rhai_fn(name = "sort", return_raw)]
@ -781,15 +777,11 @@ mod array_functions {
ctx: NativeCallContext, ctx: NativeCallContext,
array: &mut Array, array: &mut Array,
comparer: &str, comparer: &str,
) -> Result<(), Box<EvalAltResult>> { ) -> RhaiResultOf<()> {
sort(ctx, array, FnPtr::new(comparer)?) sort(ctx, array, FnPtr::new(comparer)?)
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn sort( pub fn sort(ctx: NativeCallContext, array: &mut Array, comparer: FnPtr) -> RhaiResultOf<()> {
ctx: NativeCallContext,
array: &mut Array,
comparer: FnPtr,
) -> Result<(), Box<EvalAltResult>> {
if array.len() <= 1 { if array.len() <= 1 {
return Ok(()); return Ok(());
} }
@ -803,7 +795,7 @@ mod array_functions {
v if v > 0 => Ordering::Greater, v if v > 0 => Ordering::Greater,
v if v < 0 => Ordering::Less, v if v < 0 => Ordering::Less,
0 => Ordering::Equal, 0 => Ordering::Equal,
_ => unreachable!(), _ => unreachable!("v is {}", v),
}) })
.unwrap_or_else(|| x.type_id().cmp(&y.type_id())) .unwrap_or_else(|| x.type_id().cmp(&y.type_id()))
}); });
@ -811,7 +803,7 @@ mod array_functions {
Ok(()) Ok(())
} }
#[rhai_fn(name = "sort", return_raw)] #[rhai_fn(name = "sort", return_raw)]
pub fn sort_with_builtin(array: &mut Array) -> Result<(), Box<EvalAltResult>> { pub fn sort_with_builtin(array: &mut Array) -> RhaiResultOf<()> {
if array.len() <= 1 { if array.len() <= 1 {
return Ok(()); return Ok(());
} }
@ -819,7 +811,7 @@ mod array_functions {
let type_id = array[0].type_id(); let type_id = array[0].type_id();
if array.iter().any(|a| a.type_id() != type_id) { if array.iter().any(|a| a.type_id() != type_id) {
return Err(EvalAltResult::ErrorFunctionNotFound( return Err(ERR::ErrorFunctionNotFound(
"sort() cannot be called with elements of different types".into(), "sort() cannot be called with elements of different types".into(),
Position::NONE, Position::NONE,
) )
@ -883,11 +875,7 @@ mod array_functions {
Ok(()) Ok(())
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn drain( pub fn drain(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf<Array> {
ctx: NativeCallContext,
array: &mut Array,
filter: FnPtr,
) -> Result<Array, Box<EvalAltResult>> {
if array.is_empty() { if array.is_empty() {
return Ok(Array::new()); return Ok(Array::new());
} }
@ -901,7 +889,7 @@ mod array_functions {
if filter if filter
.call_raw(&ctx, None, [array[x].clone()]) .call_raw(&ctx, None, [array[x].clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) ERR::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(filter.fn_name()) => if fn_sig.starts_with(filter.fn_name()) =>
{ {
filter.call_raw(&ctx, None, [array[x].clone(), (i as INT).into()]) filter.call_raw(&ctx, None, [array[x].clone(), (i as INT).into()])
@ -909,7 +897,7 @@ mod array_functions {
_ => Err(err), _ => Err(err),
}) })
.map_err(|err| { .map_err(|err| {
Box::new(EvalAltResult::ErrorInFunctionCall( Box::new(ERR::ErrorInFunctionCall(
"drain".to_string(), "drain".to_string(),
ctx.source().unwrap_or("").to_string(), ctx.source().unwrap_or("").to_string(),
err, err,
@ -934,7 +922,7 @@ mod array_functions {
ctx: NativeCallContext, ctx: NativeCallContext,
array: &mut Array, array: &mut Array,
filter: &str, filter: &str,
) -> Result<Array, Box<EvalAltResult>> { ) -> RhaiResultOf<Array> {
drain(ctx, array, FnPtr::new(filter)?) drain(ctx, array, FnPtr::new(filter)?)
} }
#[rhai_fn(name = "drain")] #[rhai_fn(name = "drain")]
@ -959,7 +947,7 @@ mod array_functions {
let arr_len = array.len(); let arr_len = array.len();
start start
.checked_abs() .checked_abs()
.map_or(0, |n| arr_len - (n as usize).min(arr_len)) .map_or(0, |n| arr_len - usize::min(n as usize, arr_len))
} else if start as usize >= array.len() { } else if start as usize >= array.len() {
return Array::new(); return Array::new();
} else { } else {
@ -977,11 +965,7 @@ mod array_functions {
array.drain(start..start + len).collect() array.drain(start..start + len).collect()
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn retain( pub fn retain(ctx: NativeCallContext, array: &mut Array, filter: FnPtr) -> RhaiResultOf<Array> {
ctx: NativeCallContext,
array: &mut Array,
filter: FnPtr,
) -> Result<Array, Box<EvalAltResult>> {
if array.is_empty() { if array.is_empty() {
return Ok(Array::new()); return Ok(Array::new());
} }
@ -995,7 +979,7 @@ mod array_functions {
if !filter if !filter
.call_raw(&ctx, None, [array[x].clone()]) .call_raw(&ctx, None, [array[x].clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) ERR::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.starts_with(filter.fn_name()) => if fn_sig.starts_with(filter.fn_name()) =>
{ {
filter.call_raw(&ctx, None, [array[x].clone(), (i as INT).into()]) filter.call_raw(&ctx, None, [array[x].clone(), (i as INT).into()])
@ -1003,7 +987,7 @@ mod array_functions {
_ => Err(err), _ => Err(err),
}) })
.map_err(|err| { .map_err(|err| {
Box::new(EvalAltResult::ErrorInFunctionCall( Box::new(ERR::ErrorInFunctionCall(
"retain".to_string(), "retain".to_string(),
ctx.source().unwrap_or("").to_string(), ctx.source().unwrap_or("").to_string(),
err, err,
@ -1028,7 +1012,7 @@ mod array_functions {
ctx: NativeCallContext, ctx: NativeCallContext,
array: &mut Array, array: &mut Array,
filter: &str, filter: &str,
) -> Result<Array, Box<EvalAltResult>> { ) -> RhaiResultOf<Array> {
retain(ctx, array, FnPtr::new(filter)?) retain(ctx, array, FnPtr::new(filter)?)
} }
#[rhai_fn(name = "retain")] #[rhai_fn(name = "retain")]
@ -1053,7 +1037,7 @@ mod array_functions {
let arr_len = array.len(); let arr_len = array.len();
start start
.checked_abs() .checked_abs()
.map_or(0, |n| arr_len - (n as usize).min(arr_len)) .map_or(0, |n| arr_len - usize::min(n as usize, arr_len))
} else if start as usize >= array.len() { } else if start as usize >= array.len() {
return mem::take(array); return mem::take(array);
} else { } else {
@ -1074,11 +1058,7 @@ mod array_functions {
drained drained
} }
#[rhai_fn(name = "==", return_raw, pure)] #[rhai_fn(name = "==", return_raw, pure)]
pub fn equals( pub fn equals(ctx: NativeCallContext, array1: &mut Array, array2: Array) -> RhaiResultOf<bool> {
ctx: NativeCallContext,
array1: &mut Array,
array2: Array,
) -> Result<bool, Box<EvalAltResult>> {
if array1.len() != array2.len() { if array1.len() != array2.len() {
return Ok(false); return Ok(false);
} }
@ -1092,9 +1072,7 @@ mod array_functions {
if !ctx if !ctx
.call_fn_raw(OP_EQUALS, true, false, &mut [a1, a2]) .call_fn_raw(OP_EQUALS, true, false, &mut [a1, a2])
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(ref fn_sig, _) ERR::ErrorFunctionNotFound(ref fn_sig, _) if fn_sig.starts_with(OP_EQUALS) => {
if fn_sig.starts_with(OP_EQUALS) =>
{
if a1.type_id() == a2.type_id() { if a1.type_id() == a2.type_id() {
// No default when comparing same type // No default when comparing same type
Err(err) Err(err)
@ -1118,7 +1096,7 @@ mod array_functions {
ctx: NativeCallContext, ctx: NativeCallContext,
array1: &mut Array, array1: &mut Array,
array2: Array, array2: Array,
) -> Result<bool, Box<EvalAltResult>> { ) -> RhaiResultOf<bool> {
equals(ctx, array1, array2).map(|r| !r) equals(ctx, array1, array2).map(|r| !r)
} }
} }

198
src/packages/bit_field.rs Normal file
View File

@ -0,0 +1,198 @@
#![allow(non_snake_case)]
use crate::plugin::*;
use crate::{def_package, ExclusiveRange, InclusiveRange, Position, RhaiResultOf, ERR, INT};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
def_package! {
/// Package of basic bit-field utilities.
crate::BitFieldPackage => |lib| {
lib.standard = true;
combine_with_exported_module!(lib, "bit_field", bit_field_functions);
}
}
#[export_module]
mod bit_field_functions {
const BITS: usize = std::mem::size_of::<INT>() * 8;
#[rhai_fn(return_raw)]
pub fn get_bit(value: INT, index: INT) -> RhaiResultOf<bool> {
if index >= 0 {
let offset = index as usize;
if offset >= BITS {
Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into())
} else {
Ok((value & (1 << offset)) != 0)
}
} else if let Some(abs_index) = index.checked_abs() {
let offset = abs_index as usize;
// Count from end if negative
if offset > BITS {
Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into())
} else {
Ok((value & (1 << (BITS - offset))) != 0)
}
} else {
Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into())
}
}
#[rhai_fn(return_raw)]
pub fn set_bit(value: &mut INT, index: INT, new_value: bool) -> RhaiResultOf<()> {
if index >= 0 {
let offset = index as usize;
if offset >= BITS {
Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into())
} else {
let mask = 1 << offset;
if new_value {
*value |= mask;
} else {
*value &= !mask;
}
Ok(())
}
} else if let Some(abs_index) = index.checked_abs() {
let offset = abs_index as usize;
// Count from end if negative
if offset > BITS {
Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into())
} else {
let mask = 1 << offset;
if new_value {
*value |= mask;
} else {
*value &= !mask;
}
Ok(())
}
} else {
Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into())
}
}
#[rhai_fn(name = "get_bits", return_raw)]
pub fn get_bits_range(value: INT, range: ExclusiveRange) -> RhaiResultOf<INT> {
let from = INT::max(range.start, 0);
let to = INT::max(range.end, from);
get_bits(value, from, to - from)
}
#[rhai_fn(name = "get_bits", return_raw)]
pub fn get_bits_range_inclusive(value: INT, range: InclusiveRange) -> RhaiResultOf<INT> {
let from = INT::max(*range.start(), 0);
let to = INT::max(*range.end(), from - 1);
get_bits(value, from, to - from + 1)
}
#[rhai_fn(return_raw)]
pub fn get_bits(value: INT, index: INT, bits: INT) -> RhaiResultOf<INT> {
if bits < 1 {
return Ok(0);
}
let offset = if index >= 0 {
let offset = index as usize;
if offset >= BITS {
return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into());
}
offset
} else if let Some(abs_index) = index.checked_abs() {
let offset = abs_index as usize;
// Count from end if negative
if offset > BITS {
return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into());
}
BITS - offset
} else {
return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into());
};
let bits = if offset + bits as usize > BITS {
BITS - offset
} else {
bits as usize
};
let mut base = 1;
let mut mask = 0;
for _ in 0..bits {
mask |= base;
base <<= 1;
}
Ok(((value & (mask << index)) >> index) & mask)
}
#[rhai_fn(name = "set_bits", return_raw)]
pub fn set_bits_range(
value: &mut INT,
range: ExclusiveRange,
new_value: INT,
) -> RhaiResultOf<()> {
let from = INT::max(range.start, 0);
let to = INT::max(range.end, from);
set_bits(value, from, to - from, new_value)
}
#[rhai_fn(name = "set_bits", return_raw)]
pub fn set_bits_range_inclusive(
value: &mut INT,
range: InclusiveRange,
new_value: INT,
) -> RhaiResultOf<()> {
let from = INT::max(*range.start(), 0);
let to = INT::max(*range.end(), from - 1);
set_bits(value, from, to - from + 1, new_value)
}
#[rhai_fn(return_raw)]
pub fn set_bits(value: &mut INT, index: INT, bits: INT, new_value: INT) -> RhaiResultOf<()> {
if bits < 1 {
return Ok(());
}
let offset = if index >= 0 {
let offset = index as usize;
if offset >= BITS {
return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into());
}
offset
} else if let Some(abs_index) = index.checked_abs() {
let offset = abs_index as usize;
// Count from end if negative
if offset > BITS {
return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into());
}
BITS - offset
} else {
return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into());
};
let bits = if offset + bits as usize > BITS {
BITS - offset
} else {
bits as usize
};
let mut base = 1;
let mut mask = 0;
for _ in 0..bits {
mask |= base;
base <<= 1;
}
*value &= !(mask << index);
*value |= (new_value & mask) << index;
Ok(())
}
}

View File

@ -3,8 +3,8 @@
use crate::plugin::*; use crate::plugin::*;
use crate::{ use crate::{
def_package, Blob, Dynamic, EvalAltResult, ExclusiveRange, InclusiveRange, NativeCallContext, def_package, Blob, Dynamic, ExclusiveRange, InclusiveRange, NativeCallContext, Position,
Position, INT, RhaiResultOf, INT,
}; };
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -13,25 +13,25 @@ use std::{any::TypeId, mem};
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
use crate::FLOAT; use crate::FLOAT;
def_package!(crate:BasicBlobPackage:"Basic BLOB utilities.", lib, { def_package! {
lib.standard = true; /// Package of basic BLOB utilities.
crate::BasicBlobPackage => |lib| {
lib.standard = true;
combine_with_exported_module!(lib, "blob", blob_functions); combine_with_exported_module!(lib, "blob", blob_functions);
// Register blob iterator // Register blob iterator
lib.set_iterable::<Blob>(); lib.set_iterable::<Blob>();
}); }
}
#[export_module] #[export_module]
mod blob_functions { pub mod blob_functions {
pub fn blob() -> Blob { pub const fn blob() -> Blob {
Blob::new() Blob::new()
} }
#[rhai_fn(name = "blob", return_raw)] #[rhai_fn(name = "blob", return_raw)]
pub fn blob_with_capacity( pub fn blob_with_capacity(ctx: NativeCallContext, len: INT) -> RhaiResultOf<Blob> {
ctx: NativeCallContext,
len: INT,
) -> Result<Blob, Box<EvalAltResult>> {
blob_with_capacity_and_value(ctx, len, 0) blob_with_capacity_and_value(ctx, len, 0)
} }
#[rhai_fn(name = "blob", return_raw)] #[rhai_fn(name = "blob", return_raw)]
@ -39,34 +39,30 @@ mod blob_functions {
ctx: NativeCallContext, ctx: NativeCallContext,
len: INT, len: INT,
value: INT, value: INT,
) -> Result<Blob, Box<EvalAltResult>> { ) -> RhaiResultOf<Blob> {
let len = if len < 0 { 0 } else { len as usize }; let len = if len < 0 { 0 } else { len as usize };
let _ctx = ctx; let _ctx = ctx;
// 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"))]
if _ctx.engine().max_array_size() > 0 && len > _ctx.engine().max_array_size() { if _ctx.engine().max_array_size() > 0 && len > _ctx.engine().max_array_size() {
return Err(EvalAltResult::ErrorDataTooLarge( return Err(
"Size of BLOB".to_string(), crate::ERR::ErrorDataTooLarge("Size of BLOB".to_string(), Position::NONE).into(),
Position::NONE, );
)
.into());
} }
let mut blob = Blob::new(); let mut blob = Blob::new();
blob.resize(len, (value & 0x00ff) as u8); blob.resize(len, (value & 0x000000ff) as u8);
Ok(blob) Ok(blob)
} }
#[rhai_fn(name = "len", get = "len", pure)] #[rhai_fn(name = "len", get = "len", pure)]
pub fn len(blob: &mut Blob) -> INT { pub fn len(blob: &mut Blob) -> INT {
blob.len() as INT blob.len() as INT
} }
#[rhai_fn(name = "push", name = "+=")]
pub fn push(blob: &mut Blob, item: INT) { pub fn push(blob: &mut Blob, item: INT) {
let item = (item & 0x00ff) as u8; let item = (item & 0x000000ff) as u8;
blob.push(item); blob.push(item);
} }
#[rhai_fn(name = "append", name = "+=")]
pub fn append(blob: &mut Blob, y: Blob) { pub fn append(blob: &mut Blob, y: Blob) {
if !y.is_empty() { if !y.is_empty() {
if blob.is_empty() { if blob.is_empty() {
@ -77,18 +73,21 @@ mod blob_functions {
} }
} }
#[rhai_fn(name = "+")] #[rhai_fn(name = "+")]
pub fn concat(mut blob: Blob, y: Blob) -> Blob { pub fn concat(blob1: Blob, blob2: Blob) -> Blob {
if !y.is_empty() { if !blob2.is_empty() {
if blob.is_empty() { if blob1.is_empty() {
blob = y; blob2
} else { } else {
blob.extend(y); let mut blob = blob1;
blob.extend(blob2);
blob
} }
} else {
blob1
} }
blob
} }
pub fn insert(blob: &mut Blob, position: INT, item: INT) { pub fn insert(blob: &mut Blob, position: INT, item: INT) {
let item = (item & 0x00ff) as u8; let item = (item & 0x000000ff) as u8;
if blob.is_empty() { if blob.is_empty() {
blob.push(item); blob.push(item);
@ -109,27 +108,20 @@ mod blob_functions {
} }
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn pad( pub fn pad(ctx: NativeCallContext, blob: &mut Blob, len: INT, item: INT) -> RhaiResultOf<()> {
ctx: NativeCallContext,
blob: &mut Blob,
len: INT,
item: INT,
) -> Result<(), Box<EvalAltResult>> {
if len <= 0 { if len <= 0 {
return Ok(()); return Ok(());
} }
let item = (item & 0x00ff) as u8; let item = (item & 0x000000ff) as u8;
let _ctx = ctx; let _ctx = ctx;
// 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"))]
if _ctx.engine().max_array_size() > 0 && (len as usize) > _ctx.engine().max_array_size() { if _ctx.engine().max_array_size() > 0 && (len as usize) > _ctx.engine().max_array_size() {
return Err(EvalAltResult::ErrorDataTooLarge( return Err(
"Size of BLOB".to_string(), crate::ERR::ErrorDataTooLarge("Size of BLOB".to_string(), Position::NONE).into(),
Position::NONE, );
)
.into());
} }
if len as usize > blob.len() { if len as usize > blob.len() {
@ -209,9 +201,9 @@ mod blob_functions {
let start = if start < 0 { let start = if start < 0 {
start start
.checked_abs() .checked_abs()
.map_or(0, |n| blob_len - (n as usize).min(blob_len)) .map_or(0, |n| blob_len - usize::min(n as usize, blob_len))
} else if start as usize >= blob_len { } else if start as usize >= blob_len {
blob.extend(replace.into_iter()); blob.extend(replace);
return; return;
} else { } else {
start as usize start as usize
@ -248,7 +240,7 @@ mod blob_functions {
let start = if start < 0 { let start = if start < 0 {
start start
.checked_abs() .checked_abs()
.map_or(0, |n| blob_len - (n as usize).min(blob_len)) .map_or(0, |n| blob_len - usize::min(n as usize, blob_len))
} else if start as usize >= blob_len { } else if start as usize >= blob_len {
return Blob::new(); return Blob::new();
} else { } else {
@ -312,7 +304,7 @@ mod blob_functions {
let start = if start < 0 { let start = if start < 0 {
start start
.checked_abs() .checked_abs()
.map_or(0, |n| blob_len - (n as usize).min(blob_len)) .map_or(0, |n| blob_len - usize::min(n as usize, blob_len))
} else if start as usize >= blob_len { } else if start as usize >= blob_len {
return Blob::new(); return Blob::new();
} else { } else {
@ -348,7 +340,7 @@ mod blob_functions {
let start = if start < 0 { let start = if start < 0 {
start start
.checked_abs() .checked_abs()
.map_or(0, |n| blob_len - (n as usize).min(blob_len)) .map_or(0, |n| blob_len - usize::min(n as usize, blob_len))
} else if start as usize >= blob_len { } else if start as usize >= blob_len {
return mem::take(blob); return mem::take(blob);
} else { } else {
@ -366,26 +358,6 @@ mod blob_functions {
drained drained
} }
pub fn contains(blob: &mut Blob, value: Dynamic) -> bool {
if blob.is_empty() {
return false;
}
let value = match value.as_int() {
Ok(value) => value as u8,
_ => return false,
};
blob.contains(&value)
}
#[rhai_fn(name = "==", pure)]
pub fn equals(blob1: &mut Blob, blob2: Blob) -> bool {
&*blob1 == &blob2
}
#[rhai_fn(name = "!=", pure)]
pub fn not_equals(blob1: &mut Blob, blob2: Blob) -> bool {
&*blob1 != &blob2
}
#[inline] #[inline]
fn parse_int(blob: &mut Blob, start: INT, len: INT, is_le: bool) -> INT { fn parse_int(blob: &mut Blob, start: INT, len: INT, is_le: bool) -> INT {
@ -397,7 +369,7 @@ mod blob_functions {
let start = if start < 0 { let start = if start < 0 {
start start
.checked_abs() .checked_abs()
.map_or(0, |n| blob_len - (n as usize).min(blob_len)) .map_or(0, |n| blob_len - usize::min(n as usize, blob_len))
} else if start as usize >= blob_len { } else if start as usize >= blob_len {
return 0; return 0;
} else { } else {
@ -466,7 +438,7 @@ mod blob_functions {
let start = if start < 0 { let start = if start < 0 {
start start
.checked_abs() .checked_abs()
.map_or(0, |n| blob_len - (n as usize).min(blob_len)) .map_or(0, |n| blob_len - usize::min(n as usize, blob_len))
} else if start as usize >= blob_len { } else if start as usize >= blob_len {
return; return;
} else { } else {
@ -483,13 +455,11 @@ mod blob_functions {
let len = usize::min(len, INT_BYTES); let len = usize::min(len, INT_BYTES);
let mut buf = [0_u8; INT_BYTES]; let buf = if is_le {
buf.copy_from_slice(&if is_le {
value.to_le_bytes() value.to_le_bytes()
} else { } else {
value.to_be_bytes() value.to_be_bytes()
}); };
blob[start..][..len].copy_from_slice(&buf[..len]); blob[start..][..len].copy_from_slice(&buf[..len]);
} }
@ -537,7 +507,7 @@ mod blob_functions {
let start = if start < 0 { let start = if start < 0 {
start start
.checked_abs() .checked_abs()
.map_or(0, |n| blob_len - (n as usize).min(blob_len)) .map_or(0, |n| blob_len - usize::min(n as usize, blob_len))
} else if start as usize >= blob_len { } else if start as usize >= blob_len {
return 0.0; return 0.0;
} else { } else {
@ -613,7 +583,7 @@ mod blob_functions {
let start = if start < 0 { let start = if start < 0 {
start start
.checked_abs() .checked_abs()
.map_or(0, |n| blob_len - (n as usize).min(blob_len)) .map_or(0, |n| blob_len - usize::min(n as usize, blob_len))
} else if start as usize >= blob_len { } else if start as usize >= blob_len {
return; return;
} else { } else {
@ -629,14 +599,11 @@ mod blob_functions {
const FLOAT_BYTES: usize = mem::size_of::<FLOAT>(); const FLOAT_BYTES: usize = mem::size_of::<FLOAT>();
let len = usize::min(len, FLOAT_BYTES); let len = usize::min(len, FLOAT_BYTES);
let buf = if is_le {
let mut buf = [0_u8; FLOAT_BYTES];
buf.copy_from_slice(&if is_le {
value.to_le_bytes() value.to_le_bytes()
} else { } else {
value.to_be_bytes() value.to_be_bytes()
}); };
blob[start..][..len].copy_from_slice(&buf[..len]); blob[start..][..len].copy_from_slice(&buf[..len]);
} }
@ -678,4 +645,77 @@ mod blob_functions {
pub fn write_be_float(blob: &mut Blob, start: INT, len: INT, value: FLOAT) { pub fn write_be_float(blob: &mut Blob, start: INT, len: INT, value: FLOAT) {
write_float(blob, start, len, value, false) write_float(blob, start, len, value, false)
} }
#[inline]
fn write_string(blob: &mut Blob, start: INT, len: INT, string: &str, ascii_only: bool) {
if len <= 0 || blob.is_empty() || string.is_empty() {
return;
}
let blob_len = blob.len();
let start = if start < 0 {
start
.checked_abs()
.map_or(0, |n| blob_len - usize::min(n as usize, blob_len))
} else if start as usize >= blob_len {
return;
} else {
start as usize
};
let len = if len as usize > blob_len - start {
blob_len - start
} else {
len as usize
};
let len = usize::min(len, string.len());
if ascii_only {
string
.chars()
.filter(char::is_ascii)
.take(len)
.map(|ch| ch as u8)
.enumerate()
.for_each(|(i, x)| blob[start + i] = x);
} else {
blob[start..][..len].copy_from_slice(&string.as_bytes()[..len]);
}
}
#[rhai_fn(name = "write_utf8")]
pub fn write_utf8_string(blob: &mut Blob, start: INT, len: INT, string: &str) {
write_string(blob, start, len, string, false)
}
#[rhai_fn(name = "write_utf8")]
pub fn write_utf8_string_range(blob: &mut Blob, range: ExclusiveRange, string: &str) {
let start = INT::max(range.start, 0);
let end = INT::max(range.end, start);
write_string(blob, start, end - start, string, false)
}
#[rhai_fn(name = "write_utf8")]
pub fn write_utf8_string_range_inclusive(blob: &mut Blob, range: InclusiveRange, string: &str) {
let start = INT::max(*range.start(), 0);
let end = INT::max(*range.end(), start);
write_string(blob, start, end - start + 1, string, false)
}
#[rhai_fn(name = "write_ascii")]
pub fn write_ascii_string(blob: &mut Blob, start: INT, len: INT, string: &str) {
write_string(blob, start, len, string, true)
}
#[rhai_fn(name = "write_ascii")]
pub fn write_ascii_string_range(blob: &mut Blob, range: ExclusiveRange, string: &str) {
let start = INT::max(range.start, 0);
let end = INT::max(range.end, start);
write_string(blob, start, end - start, string, true)
}
#[rhai_fn(name = "write_ascii")]
pub fn write_ascii_string_range_inclusive(
blob: &mut Blob,
range: InclusiveRange,
string: &str,
) {
let start = INT::max(*range.start(), 0);
let end = INT::max(*range.end(), start);
write_string(blob, start, end - start + 1, string, true)
}
} }

View File

@ -3,11 +3,14 @@ use crate::{def_package, FnPtr, ImmutableString, NativeCallContext};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
def_package!(crate:BasicFnPackage:"Basic Fn functions.", lib, { def_package! {
lib.standard = true; /// Package of basic function pointer utilities.
crate::BasicFnPackage => |lib| {
lib.standard = true;
combine_with_exported_module!(lib, "FnPtr", fn_ptr_functions); combine_with_exported_module!(lib, "FnPtr", fn_ptr_functions);
}); }
}
#[export_module] #[export_module]
mod fn_ptr_functions { mod fn_ptr_functions {
@ -41,7 +44,7 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array {
fn make_metadata( fn make_metadata(
dict: &BTreeSet<Identifier>, dict: &BTreeSet<Identifier>,
namespace: Option<Identifier>, namespace: Option<Identifier>,
f: &ScriptFnDef, func: &ScriptFnDef,
) -> Map { ) -> Map {
const DICT: &str = "key exists"; const DICT: &str = "key exists";
@ -50,10 +53,13 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array {
if let Some(ns) = namespace { if let Some(ns) = namespace {
map.insert(dict.get("namespace").expect(DICT).clone(), ns.into()); map.insert(dict.get("namespace").expect(DICT).clone(), ns.into());
} }
map.insert(dict.get("name").expect(DICT).clone(), f.name.clone().into()); map.insert(
dict.get("name").expect(DICT).clone(),
func.name.clone().into(),
);
map.insert( map.insert(
dict.get("access").expect(DICT).clone(), dict.get("access").expect(DICT).clone(),
match f.access { match func.access {
FnAccess::Public => dict.get("public").expect(DICT).clone(), FnAccess::Public => dict.get("public").expect(DICT).clone(),
FnAccess::Private => dict.get("private").expect(DICT).clone(), FnAccess::Private => dict.get("private").expect(DICT).clone(),
} }
@ -61,14 +67,14 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array {
); );
map.insert( map.insert(
dict.get("is_anonymous").expect(DICT).clone(), dict.get("is_anonymous").expect(DICT).clone(),
f.name.starts_with(crate::engine::FN_ANONYMOUS).into(), func.name.starts_with(crate::engine::FN_ANONYMOUS).into(),
); );
map.insert( map.insert(
dict.get("params").expect(DICT).clone(), dict.get("params").expect(DICT).clone(),
f.params func.params
.iter() .iter()
.cloned() .cloned()
.map(Into::<Dynamic>::into) .map(Into::into)
.collect::<Array>() .collect::<Array>()
.into(), .into(),
); );

View File

@ -1,10 +1,12 @@
use crate::plugin::*; use crate::plugin::*;
use crate::types::dynamic::Variant; use crate::types::dynamic::Variant;
use crate::{def_package, EvalAltResult, ExclusiveRange, InclusiveRange, INT}; use crate::{def_package, ExclusiveRange, InclusiveRange, RhaiResultOf, INT};
use std::iter::{ExactSizeIterator, FusedIterator};
use std::ops::{Range, RangeInclusive};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
use std::{
iter::{ExactSizeIterator, FusedIterator},
ops::{Range, RangeInclusive},
};
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
use num_traits::{CheckedAdd as Add, CheckedSub as Sub}; use num_traits::{CheckedAdd as Add, CheckedSub as Sub};
@ -22,19 +24,19 @@ impl<T> StepRange<T>
where where
T: Variant + Copy + PartialOrd + Add<Output = T> + Sub<Output = T>, T: Variant + Copy + PartialOrd + Add<Output = T> + Sub<Output = T>,
{ {
pub fn new(from: T, to: T, step: T) -> Result<Self, Box<EvalAltResult>> { pub fn new(from: T, to: T, step: T) -> RhaiResultOf<Self> {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if let Some(r) = from.checked_add(&step) { if let Some(r) = from.checked_add(&step) {
if r == from { if r == from {
return Err(EvalAltResult::ErrorInFunctionCall( return Err(crate::ERR::ErrorInFunctionCall(
"range".to_string(), "range".to_string(),
String::new(), String::new(),
EvalAltResult::ErrorArithmetic( crate::ERR::ErrorArithmetic(
"step value cannot be zero".to_string(), "step value cannot be zero".to_string(),
crate::Position::NONE, Position::NONE,
) )
.into(), .into(),
crate::Position::NONE, Position::NONE,
) )
.into()); .into());
} }
@ -117,33 +119,24 @@ struct BitRange(INT, INT, usize);
const BITS: usize = std::mem::size_of::<INT>() * 8; const BITS: usize = std::mem::size_of::<INT>() * 8;
impl BitRange { impl BitRange {
pub fn new(value: INT, from: INT, len: INT) -> Result<Self, Box<EvalAltResult>> { pub fn new(value: INT, from: INT, len: INT) -> RhaiResultOf<Self> {
let from = if from >= 0 { let from = if from >= 0 {
let offset = from as usize; let offset = from as usize;
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if offset >= BITS { if offset >= BITS {
return Err( return Err(crate::ERR::ErrorBitFieldBounds(BITS, from, Position::NONE).into());
EvalAltResult::ErrorBitFieldBounds(BITS, from, crate::Position::NONE).into(),
);
} }
offset offset
} else { } else {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if let Some(abs_from) = from.checked_abs() { if let Some(abs_from) = from.checked_abs() {
if (abs_from as usize) > BITS { if (abs_from as usize) > BITS {
return Err(EvalAltResult::ErrorBitFieldBounds( return Err(crate::ERR::ErrorBitFieldBounds(BITS, from, Position::NONE).into());
BITS,
from,
crate::Position::NONE,
)
.into());
} }
BITS - (abs_from as usize) BITS - (abs_from as usize)
} else { } else {
return Err( return Err(crate::ERR::ErrorBitFieldBounds(BITS, from, Position::NONE).into());
EvalAltResult::ErrorBitFieldBounds(BITS, from, crate::Position::NONE).into(),
);
} }
#[cfg(feature = "unchecked")] #[cfg(feature = "unchecked")]
@ -168,12 +161,10 @@ impl Iterator for BitRange {
type Item = bool; type Item = bool;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
let Self(value, mask, len) = *self; if self.2 == 0 {
if len == 0 {
None None
} else { } else {
let r = (value & mask) != 0; let r = (self.0 & self.1) != 0;
self.1 <<= 1; self.1 <<= 1;
self.2 -= 1; self.2 -= 1;
Some(r) Some(r)
@ -272,7 +263,6 @@ macro_rules! reg_range {
($lib:ident | $x:expr => $( $y:ty ),*) => { ($lib:ident | $x:expr => $( $y:ty ),*) => {
$( $(
$lib.set_iterator::<Range<$y>>(); $lib.set_iterator::<Range<$y>>();
$lib.set_iterator::<RangeInclusive<$y>>();
let _hash = $lib.set_native_fn($x, |from: $y, to: $y| Ok(from..to)); let _hash = $lib.set_native_fn($x, |from: $y, to: $y| Ok(from..to));
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
@ -281,6 +271,8 @@ macro_rules! reg_range {
concat!("to: ", stringify!($y)), concat!("to: ", stringify!($y)),
concat!("Iterator<Item=", stringify!($y), ">") concat!("Iterator<Item=", stringify!($y), ">")
]); ]);
$lib.set_iterator::<RangeInclusive<$y>>();
)* )*
}; };
($lib:ident | step $x:expr => $( $y:ty ),*) => { ($lib:ident | step $x:expr => $( $y:ty ),*) => {
@ -299,268 +291,275 @@ macro_rules! reg_range {
}; };
} }
def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, { def_package! {
lib.standard = true; /// Package of basic range iterators
crate::BasicIteratorPackage => |lib| {
lib.standard = true;
reg_range!(lib | "range" => INT); reg_range!(lib | "range" => INT);
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))] #[cfg(not(feature = "only_i64"))]
{ {
reg_range!(lib | "range" => i8, u8, i16, u16, i32, u32, i64, u64); reg_range!(lib | "range" => i8, u8, i16, u16, i32, u32, i64, u64);
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
reg_range!(lib | "range" => i128, u128); #[cfg(not(target_arch = "wasm64"))]
} reg_range!(lib | "range" => i128, u128);
reg_range!(lib | step "range" => INT);
#[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))]
{
reg_range!(lib | step "range" => i8, u8, i16, u16, i32, u32, i64, u64);
#[cfg(not(target_arch = "wasm32"))]
reg_range!(lib | step "range" => i128, u128);
}
#[cfg(not(feature = "no_float"))]
{
use crate::FLOAT;
#[derive(Debug, Clone, Copy, PartialEq)]
struct StepFloatRange(FLOAT, FLOAT, FLOAT);
impl StepFloatRange {
pub fn new(from: FLOAT, to: FLOAT, step: FLOAT) -> Result<Self, Box<EvalAltResult>> {
#[cfg(not(feature = "unchecked"))]
if step == 0.0 {
return Err(EvalAltResult::ErrorInFunctionCall("range".to_string(), "".to_string(),
EvalAltResult::ErrorArithmetic("step value cannot be zero".to_string(), crate::Position::NONE).into(),
crate::Position::NONE,
).into());
}
Ok(Self(from, to, step))
}
} }
impl Iterator for StepFloatRange { reg_range!(lib | step "range" => INT);
type Item = FLOAT;
fn next(&mut self) -> Option<FLOAT> { #[cfg(not(feature = "only_i32"))]
if self.0 == self.1 { #[cfg(not(feature = "only_i64"))]
None {
} else if self.0 < self.1 { reg_range!(lib | step "range" => i8, u8, i16, u16, i32, u32, i64, u64);
#[cfg(not(feature = "unchecked"))]
if self.2 < 0.0 {
return None;
}
let v = self.0; #[cfg(not(target_arch = "wasm32"))]
let n = self.0 + self.2; #[cfg(not(target_arch = "wasm64"))]
reg_range!(lib | step "range" => i128, u128);
self.0 = if n >= self.1 { self.1 } else { n };
Some(v)
} else {
#[cfg(not(feature = "unchecked"))]
if self.2 > 0.0 {
return None;
}
let v = self.0;
let n = self.0 + self.2;
self.0 = if n <= self.1 { self.1 } else { n };
Some(v)
}
}
} }
impl FusedIterator for StepFloatRange {} #[cfg(not(feature = "no_float"))]
{
use crate::FLOAT;
lib.set_iterator::<StepFloatRange>(); #[derive(Debug, Clone, Copy, PartialEq)]
struct StepFloatRange(FLOAT, FLOAT, FLOAT);
let _hash = lib.set_native_fn("range", StepFloatRange::new); impl StepFloatRange {
#[cfg(feature = "metadata")] pub fn new(from: FLOAT, to: FLOAT, step: FLOAT) -> RhaiResultOf<Self> {
lib.update_fn_metadata(_hash, &["from: FLOAT", "to: FLOAT", "step: FLOAT", "Iterator<Item=FLOAT>"]);
}
#[cfg(feature = "decimal")]
{
use rust_decimal::Decimal;
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
struct StepDecimalRange(Decimal, Decimal, Decimal);
impl StepDecimalRange {
pub fn new(from: Decimal, to: Decimal, step: Decimal) -> Result<Self, Box<EvalAltResult>> {
#[cfg(not(feature = "unchecked"))]
if step.is_zero() {
return Err(EvalAltResult::ErrorInFunctionCall("range".to_string(), "".to_string(),
EvalAltResult::ErrorArithmetic("step value cannot be zero".to_string(), crate::Position::NONE).into(),
crate::Position::NONE,
).into());
}
Ok(Self(from, to, step))
}
}
impl Iterator for StepDecimalRange {
type Item = Decimal;
fn next(&mut self) -> Option<Decimal> {
if self.0 == self.1 {
None
} else if self.0 < self.1 {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if self.2.is_sign_negative() { if step == 0.0 {
return None; return Err(crate::ERR::ErrorInFunctionCall("range".to_string(), "".to_string(),
crate::ERR::ErrorArithmetic("step value cannot be zero".to_string(), Position::NONE).into(),
Position::NONE,
).into());
} }
let v = self.0; Ok(Self(from, to, step))
let n = self.0 + self.2;
self.0 = if n >= self.1 { self.1 } else { n };
Some(v)
} else {
#[cfg(not(feature = "unchecked"))]
if self.2.is_sign_positive() {
return None;
}
let v = self.0;
let n = self.0 + self.2;
self.0 = if n <= self.1 { self.1 } else { n };
Some(v)
} }
} }
impl Iterator for StepFloatRange {
type Item = FLOAT;
fn next(&mut self) -> Option<FLOAT> {
if self.0 == self.1 {
None
} else if self.0 < self.1 {
#[cfg(not(feature = "unchecked"))]
if self.2 < 0.0 {
return None;
}
let v = self.0;
let n = self.0 + self.2;
self.0 = if n >= self.1 { self.1 } else { n };
Some(v)
} else {
#[cfg(not(feature = "unchecked"))]
if self.2 > 0.0 {
return None;
}
let v = self.0;
let n = self.0 + self.2;
self.0 = if n <= self.1 { self.1 } else { n };
Some(v)
}
}
}
impl FusedIterator for StepFloatRange {}
lib.set_iterator::<StepFloatRange>();
let _hash = lib.set_native_fn("range", StepFloatRange::new);
#[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["from: FLOAT", "to: FLOAT", "step: FLOAT", "Iterator<Item=FLOAT>"]);
} }
impl FusedIterator for StepDecimalRange {} #[cfg(feature = "decimal")]
{
use rust_decimal::Decimal;
lib.set_iterator::<StepDecimalRange>(); #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
struct StepDecimalRange(Decimal, Decimal, Decimal);
let _hash = lib.set_native_fn("range", StepDecimalRange::new); impl StepDecimalRange {
pub fn new(from: Decimal, to: Decimal, step: Decimal) -> RhaiResultOf<Self> {
#[cfg(not(feature = "unchecked"))]
if step.is_zero() {
return Err(crate::ERR::ErrorInFunctionCall("range".to_string(), "".to_string(),
crate::ERR::ErrorArithmetic("step value cannot be zero".to_string(), Position::NONE).into(),
Position::NONE,
).into());
}
Ok(Self(from, to, step))
}
}
impl Iterator for StepDecimalRange {
type Item = Decimal;
fn next(&mut self) -> Option<Decimal> {
if self.0 == self.1 {
None
} else if self.0 < self.1 {
#[cfg(not(feature = "unchecked"))]
if self.2.is_sign_negative() {
return None;
}
let v = self.0;
let n = self.0 + self.2;
self.0 = if n >= self.1 { self.1 } else { n };
Some(v)
} else {
#[cfg(not(feature = "unchecked"))]
if self.2.is_sign_positive() {
return None;
}
let v = self.0;
let n = self.0 + self.2;
self.0 = if n <= self.1 { self.1 } else { n };
Some(v)
}
}
}
impl FusedIterator for StepDecimalRange {}
lib.set_iterator::<StepDecimalRange>();
let _hash = lib.set_native_fn("range", StepDecimalRange::new);
#[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["from: Decimal", "to: Decimal", "step: Decimal", "Iterator<Item=Decimal>"]);
}
// Register string iterator
lib.set_iterator::<CharsStream>();
let _hash = lib.set_native_fn("chars", |string, range: ExclusiveRange| {
let from = INT::max(range.start, 0);
let to = INT::max(range.end, from);
Ok(CharsStream::new(string, from, to - from))
});
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["from: Decimal", "to: Decimal", "step: Decimal", "Iterator<Item=Decimal>"]); lib.update_fn_metadata(_hash, &["string: &str", "range: Range<INT>", "Iterator<Item=char>"]);
}
// Register string iterator let _hash = lib.set_native_fn("chars", |string, range: InclusiveRange| {
lib.set_iterator::<CharsStream>(); let from = INT::max(*range.start(), 0);
let to = INT::max(*range.end(), from - 1);
let _hash = lib.set_native_fn("chars", |string, range: ExclusiveRange| { Ok(CharsStream::new(string, from, to-from + 1))
let from = INT::max(range.start, 0); });
let to = INT::max(range.end, from);
Ok(CharsStream::new(string, from, to - from))
});
#[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["string: &str", "range: Range", "Iterator<Item=char>"]);
let _hash = lib.set_native_fn("chars", |string, range: InclusiveRange| {
let from = INT::max(*range.start(), 0);
let to = INT::max(*range.end(), from - 1);
Ok(CharsStream::new(string, from, to-from + 1))
});
#[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["string: &str", "range: RangeInclusive", "Iterator<Item=char>"]);
let _hash = lib.set_native_fn("chars", |string, from, len| Ok(CharsStream::new(string, from, len)));
#[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["string: &str", "from: INT", "len: INT", "Iterator<Item=char>"]);
let _hash = lib.set_native_fn("chars", |string, from| Ok(CharsStream::new(string, from, INT::MAX)));
#[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["string: &str", "from: INT", "Iterator<Item=char>"]);
let _hash = lib.set_native_fn("chars", |string| Ok(CharsStream::new(string, 0, INT::MAX)));
#[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["string: &str", "Iterator<Item=char>"]);
#[cfg(not(feature = "no_object"))]
{
let _hash = lib.set_getter_fn("chars", |string: &mut ImmutableString| Ok(CharsStream::new(string, 0, INT::MAX)));
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["string: &mut ImmutableString", "Iterator<Item=char>"]); lib.update_fn_metadata(_hash, &["string: &str", "range: RangeInclusive<INT>", "Iterator<Item=char>"]);
}
// Register bit-field iterator let _hash = lib.set_native_fn("chars", |string, from, len| Ok(CharsStream::new(string, from, len)));
lib.set_iterator::<BitRange>();
let _hash = lib.set_native_fn("bits", |value, range: ExclusiveRange| {
let from = INT::max(range.start, 0);
let to = INT::max(range.end, from);
BitRange::new(value, from, to - from)
});
#[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["value: INT", "range: Range", "Iterator<Item=bool>"]);
let _hash = lib.set_native_fn("bits", |value, range: InclusiveRange| {
let from = INT::max(*range.start(), 0);
let to = INT::max(*range.end(), from - 1);
BitRange::new(value, from, to - from + 1)
});
#[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["value: INT", "range: RangeInclusive", "Iterator<Item=bool>"]);
let _hash = lib.set_native_fn("bits", BitRange::new);
#[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["value: INT", "from: INT", "len: INT", "Iterator<Item=bool>"]);
let _hash = lib.set_native_fn("bits", |value, from| BitRange::new(value, from, INT::MAX));
#[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["value: INT", "from: INT", "Iterator<Item=bool>"]);
let _hash = lib.set_native_fn("bits", |value| BitRange::new(value, 0, INT::MAX) );
#[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["value: INT", "Iterator<Item=bool>"]);
#[cfg(not(feature = "no_object"))]
{
let _hash = lib.set_getter_fn("bits", |value: &mut INT| BitRange::new(*value, 0, INT::MAX) );
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["value: &mut INT", "range: Range", "Iterator<Item=bool>"]); lib.update_fn_metadata(_hash, &["string: &str", "from: INT", "len: INT", "Iterator<Item=char>"]);
}
combine_with_exported_module!(lib, "range", range_functions); let _hash = lib.set_native_fn("chars", |string, from| Ok(CharsStream::new(string, from, INT::MAX)));
}); #[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["string: &str", "from: INT", "Iterator<Item=char>"]);
let _hash = lib.set_native_fn("chars", |string| Ok(CharsStream::new(string, 0, INT::MAX)));
#[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["string: &str", "Iterator<Item=char>"]);
#[cfg(not(feature = "no_object"))]
{
let _hash = lib.set_getter_fn("chars", |string: &mut ImmutableString| Ok(CharsStream::new(string, 0, INT::MAX)));
#[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["string: &mut ImmutableString", "Iterator<Item=char>"]);
}
// Register bit-field iterator
lib.set_iterator::<BitRange>();
let _hash = lib.set_native_fn("bits", |value, range: ExclusiveRange| {
let from = INT::max(range.start, 0);
let to = INT::max(range.end, from);
BitRange::new(value, from, to - from)
});
#[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["value: INT", "range: Range<INT>", "Iterator<Item=bool>"]);
let _hash = lib.set_native_fn("bits", |value, range: InclusiveRange| {
let from = INT::max(*range.start(), 0);
let to = INT::max(*range.end(), from - 1);
BitRange::new(value, from, to - from + 1)
});
#[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["value: INT", "range: RangeInclusive<INT>", "Iterator<Item=bool>"]);
let _hash = lib.set_native_fn("bits", BitRange::new);
#[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["value: INT", "from: INT", "len: INT", "Iterator<Item=bool>"]);
let _hash = lib.set_native_fn("bits", |value, from| BitRange::new(value, from, INT::MAX));
#[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["value: INT", "from: INT", "Iterator<Item=bool>"]);
let _hash = lib.set_native_fn("bits", |value| BitRange::new(value, 0, INT::MAX) );
#[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["value: INT", "Iterator<Item=bool>"]);
#[cfg(not(feature = "no_object"))]
{
let _hash = lib.set_getter_fn("bits", |value: &mut INT| BitRange::new(*value, 0, INT::MAX) );
#[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["value: &mut INT", "range: Range<INT>", "Iterator<Item=bool>"]);
}
combine_with_exported_module!(lib, "range", range_functions);
}
}
#[export_module] #[export_module]
mod range_functions { mod range_functions {
#[rhai_fn(get = "start", name = "start", pure)] #[rhai_fn(get = "start", name = "start", pure)]
pub fn range_start(range: &mut ExclusiveRange) -> INT { pub fn start(range: &mut ExclusiveRange) -> INT {
range.start range.start
} }
#[rhai_fn(get = "end", name = "end", pure)] #[rhai_fn(get = "end", name = "end", pure)]
pub fn range_end(range: &mut ExclusiveRange) -> INT { pub fn end(range: &mut ExclusiveRange) -> INT {
range.end range.end
} }
#[rhai_fn(name = "contains", pure)]
pub fn range_contains(range: &mut ExclusiveRange, value: INT) -> bool {
range.contains(&value)
}
#[rhai_fn(get = "is_inclusive", name = "is_inclusive", pure)] #[rhai_fn(get = "is_inclusive", name = "is_inclusive", pure)]
pub fn range_is_inclusive(range: &mut ExclusiveRange) -> bool { pub fn is_inclusive(range: &mut ExclusiveRange) -> bool {
let _range = range; let _range = range;
false false
} }
#[rhai_fn(get = "start", name = "start", pure)] #[rhai_fn(get = "is_exclusive", name = "is_exclusive", pure)]
pub fn range_inclusive_start(range: &mut InclusiveRange) -> INT { pub fn is_exclusive(range: &mut ExclusiveRange) -> bool {
*range.start()
}
#[rhai_fn(get = "end", name = "end", pure)]
pub fn range_inclusive_end(range: &mut InclusiveRange) -> INT {
*range.end()
}
#[rhai_fn(name = "contains", pure)]
pub fn range_inclusive_contains(range: &mut InclusiveRange, value: INT) -> bool {
range.contains(&value)
}
#[rhai_fn(get = "is_inclusive", name = "is_inclusive", pure)]
pub fn range_inclusive_is_inclusive(range: &mut InclusiveRange) -> bool {
let _range = range; let _range = range;
true true
} }
#[rhai_fn(get = "start", name = "start", pure)]
pub fn start_inclusive(range: &mut InclusiveRange) -> INT {
*range.start()
}
#[rhai_fn(get = "end", name = "end", pure)]
pub fn end_inclusive(range: &mut InclusiveRange) -> INT {
*range.end()
}
#[rhai_fn(get = "is_inclusive", name = "is_inclusive", pure)]
pub fn is_inclusive_inclusive(range: &mut InclusiveRange) -> bool {
let _range = range;
true
}
#[rhai_fn(get = "is_exclusive", name = "is_exclusive", pure)]
pub fn is_exclusive_inclusive(range: &mut InclusiveRange) -> bool {
let _range = range;
false
}
} }

View File

@ -1,20 +1,33 @@
use crate::def_package; use crate::def_package;
use crate::plugin::*; use crate::plugin::*;
use crate::types::dynamic::Tag; use crate::types::dynamic::Tag;
use crate::{Dynamic, EvalAltResult, INT}; use crate::{Dynamic, RhaiResultOf, ERR, INT};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
def_package! {
/// Package of core language features.
crate::LanguageCorePackage => |lib| {
lib.standard = true;
combine_with_exported_module!(lib, "language_core", core_functions);
}
}
#[export_module] #[export_module]
mod core_functions { mod core_functions {
#[rhai_fn(name = "!")]
pub fn not(x: bool) -> bool {
!x
}
#[rhai_fn(name = "tag", get = "tag", pure)] #[rhai_fn(name = "tag", get = "tag", pure)]
pub fn get_tag(value: &mut Dynamic) -> INT { pub fn get_tag(value: &mut Dynamic) -> INT {
value.tag() as INT value.tag() as INT
} }
#[rhai_fn(name = "set_tag", set = "tag", return_raw)] #[rhai_fn(name = "set_tag", set = "tag", return_raw)]
pub fn set_tag(value: &mut Dynamic, tag: INT) -> Result<(), Box<EvalAltResult>> { pub fn set_tag(value: &mut Dynamic, tag: INT) -> RhaiResultOf<()> {
if tag < Tag::MIN as INT { if tag < Tag::MIN as INT {
Err(EvalAltResult::ErrorArithmetic( Err(ERR::ErrorArithmetic(
format!( format!(
"{} is too small to fit into a tag (must be between {} and {})", "{} is too small to fit into a tag (must be between {} and {})",
tag, tag,
@ -25,7 +38,7 @@ mod core_functions {
) )
.into()) .into())
} else if tag > Tag::MAX as INT { } else if tag > Tag::MAX as INT {
Err(EvalAltResult::ErrorArithmetic( Err(ERR::ErrorArithmetic(
format!( format!(
"{} is too large to fit into a tag (must be between {} and {})", "{} is too large to fit into a tag (must be between {} and {})",
tag, tag,
@ -41,9 +54,3 @@ mod core_functions {
} }
} }
} }
def_package!(crate:LanguageCorePackage:"Language core functions.", lib, {
lib.standard = true;
combine_with_exported_module!(lib, "language_core", core_functions);
});

View File

@ -1,7 +1,7 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
use crate::plugin::*; use crate::plugin::*;
use crate::{def_package, EvalAltResult, ExclusiveRange, InclusiveRange, INT}; use crate::{def_package, INT};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -37,38 +37,32 @@ macro_rules! reg_functions {
)* } )* }
} }
def_package!(crate:LogicPackage:"Logical operators.", lib, { def_package! {
lib.standard = true; /// Package of basic logic operators.
crate::LogicPackage => |lib| {
lib.standard = true;
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))] #[cfg(not(feature = "only_i64"))]
{ {
reg_functions!(lib += numbers; i8, u8, i16, u16, i32, u32, u64); reg_functions!(lib += numbers; i8, u8, i16, u16, i32, u32, u64);
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #[cfg(not(target_arch = "wasm32"))]
reg_functions!(lib += num_128; i128, u128); #[cfg(not(target_arch = "wasm64"))]
reg_functions!(lib += num_128; i128, u128);
}
#[cfg(not(feature = "no_float"))]
{
#[cfg(not(feature = "f32_float"))]
reg_functions!(lib += float; f32);
combine_with_exported_module!(lib, "f32", f32_functions);
#[cfg(feature = "f32_float")]
reg_functions!(lib += float; f64);
combine_with_exported_module!(lib, "f64", f64_functions);
}
} }
#[cfg(not(feature = "no_float"))]
{
#[cfg(not(feature = "f32_float"))]
reg_functions!(lib += float; f32);
combine_with_exported_module!(lib, "f32", f32_functions);
#[cfg(feature = "f32_float")]
reg_functions!(lib += float; f64);
combine_with_exported_module!(lib, "f64", f64_functions);
}
set_exported_fn!(lib, "!", not);
combine_with_exported_module!(lib, "bit_field", bit_field_functions);
});
// Logic operators
#[export_fn]
fn not(x: bool) -> bool {
!x
} }
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
@ -77,7 +71,8 @@ gen_cmp_functions!(numbers => i8, u8, i16, u16, i32, u32, u64);
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))] #[cfg(not(feature = "only_i64"))]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #[cfg(not(target_arch = "wasm32"))]
#[cfg(not(target_arch = "wasm64"))]
gen_cmp_functions!(num_128 => i128, u128); gen_cmp_functions!(num_128 => i128, u128);
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
@ -193,194 +188,3 @@ mod f64_functions {
(x as f64) <= (y as f64) (x as f64) <= (y as f64)
} }
} }
#[export_module]
mod bit_field_functions {
const BITS: usize = std::mem::size_of::<INT>() * 8;
#[rhai_fn(return_raw)]
pub fn get_bit(value: INT, index: INT) -> Result<bool, Box<EvalAltResult>> {
if index >= 0 {
let offset = index as usize;
if offset >= BITS {
Err(EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into())
} else {
Ok((value & (1 << offset)) != 0)
}
} else if let Some(abs_index) = index.checked_abs() {
let offset = abs_index as usize;
// Count from end if negative
if offset > BITS {
Err(EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into())
} else {
Ok((value & (1 << (BITS - offset))) != 0)
}
} else {
Err(EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into())
}
}
#[rhai_fn(return_raw)]
pub fn set_bit(value: &mut INT, index: INT, new_value: bool) -> Result<(), Box<EvalAltResult>> {
if index >= 0 {
let offset = index as usize;
if offset >= BITS {
Err(EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into())
} else {
let mask = 1 << offset;
if new_value {
*value |= mask;
} else {
*value &= !mask;
}
Ok(())
}
} else if let Some(abs_index) = index.checked_abs() {
let offset = abs_index as usize;
// Count from end if negative
if offset > BITS {
Err(EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into())
} else {
let mask = 1 << offset;
if new_value {
*value |= mask;
} else {
*value &= !mask;
}
Ok(())
}
} else {
Err(EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into())
}
}
#[rhai_fn(name = "get_bits", return_raw)]
pub fn get_bits_range(value: INT, range: ExclusiveRange) -> Result<INT, Box<EvalAltResult>> {
let from = INT::max(range.start, 0);
let to = INT::max(range.end, from);
get_bits(value, from, to - from)
}
#[rhai_fn(name = "get_bits", return_raw)]
pub fn get_bits_range_inclusive(
value: INT,
range: InclusiveRange,
) -> Result<INT, Box<EvalAltResult>> {
let from = INT::max(*range.start(), 0);
let to = INT::max(*range.end(), from - 1);
get_bits(value, from, to - from + 1)
}
#[rhai_fn(return_raw)]
pub fn get_bits(value: INT, index: INT, bits: INT) -> Result<INT, Box<EvalAltResult>> {
if bits < 1 {
return Ok(0);
}
let offset = if index >= 0 {
let offset = index as usize;
if offset >= BITS {
return Err(EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into());
}
offset
} else if let Some(abs_index) = index.checked_abs() {
let offset = abs_index as usize;
// Count from end if negative
if offset > BITS {
return Err(EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into());
}
BITS - offset
} else {
return Err(EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into());
};
let bits = if offset + bits as usize > BITS {
BITS - offset
} else {
bits as usize
};
let mut base = 1;
let mut mask = 0;
for _ in 0..bits {
mask |= base;
base <<= 1;
}
Ok(((value & (mask << index)) >> index) & mask)
}
#[rhai_fn(name = "set_bits", return_raw)]
pub fn set_bits_range(
value: &mut INT,
range: ExclusiveRange,
new_value: INT,
) -> Result<(), Box<EvalAltResult>> {
let from = INT::max(range.start, 0);
let to = INT::max(range.end, from);
set_bits(value, from, to - from, new_value)
}
#[rhai_fn(name = "set_bits", return_raw)]
pub fn set_bits_range_inclusive(
value: &mut INT,
range: InclusiveRange,
new_value: INT,
) -> Result<(), Box<EvalAltResult>> {
let from = INT::max(*range.start(), 0);
let to = INT::max(*range.end(), from - 1);
set_bits(value, from, to - from + 1, new_value)
}
#[rhai_fn(return_raw)]
pub fn set_bits(
value: &mut INT,
index: INT,
bits: INT,
new_value: INT,
) -> Result<(), Box<EvalAltResult>> {
if bits < 1 {
return Ok(());
}
let offset = if index >= 0 {
let offset = index as usize;
if offset >= BITS {
return Err(EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into());
}
offset
} else if let Some(abs_index) = index.checked_abs() {
let offset = abs_index as usize;
// Count from end if negative
if offset > BITS {
return Err(EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into());
}
BITS - offset
} else {
return Err(EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into());
};
let bits = if offset + bits as usize > BITS {
BITS - offset
} else {
bits as usize
};
let mut base = 1;
let mut mask = 0;
for _ in 0..bits {
mask |= base;
base <<= 1;
}
*value &= !(mask << index);
*value |= (new_value & mask) << index;
Ok(())
}
}

View File

@ -2,29 +2,24 @@
use crate::engine::OP_EQUALS; use crate::engine::OP_EQUALS;
use crate::plugin::*; use crate::plugin::*;
use crate::{def_package, Dynamic, ImmutableString, Map, INT}; use crate::{def_package, Dynamic, ImmutableString, Map, RhaiResultOf, INT};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
use crate::Array; use crate::Array;
def_package!(crate:BasicMapPackage:"Basic object map utilities.", lib, { def_package! {
lib.standard = true; /// Package of basic object map utilities.
crate::BasicMapPackage => |lib| {
lib.standard = true;
combine_with_exported_module!(lib, "map", map_functions); combine_with_exported_module!(lib, "map", map_functions);
}); }
}
#[export_module] #[export_module]
mod map_functions { mod map_functions {
#[rhai_fn(name = "has", pure)]
pub fn contains(map: &mut Map, prop: ImmutableString) -> bool {
if map.is_empty() {
false
} else {
map.contains_key(prop.as_str())
}
}
#[rhai_fn(pure)] #[rhai_fn(pure)]
pub fn len(map: &mut Map) -> INT { pub fn len(map: &mut Map) -> INT {
map.len() as INT map.len() as INT
@ -71,11 +66,7 @@ mod map_functions {
} }
} }
#[rhai_fn(name = "==", return_raw, pure)] #[rhai_fn(name = "==", return_raw, pure)]
pub fn equals( pub fn equals(ctx: NativeCallContext, map1: &mut Map, map2: Map) -> RhaiResultOf<bool> {
ctx: NativeCallContext,
map1: &mut Map,
map2: Map,
) -> Result<bool, Box<EvalAltResult>> {
if map1.len() != map2.len() { if map1.len() != map2.len() {
return Ok(false); return Ok(false);
} }
@ -101,11 +92,7 @@ mod map_functions {
Ok(true) Ok(true)
} }
#[rhai_fn(name = "!=", return_raw, pure)] #[rhai_fn(name = "!=", return_raw, pure)]
pub fn not_equals( pub fn not_equals(ctx: NativeCallContext, map1: &mut Map, map2: Map) -> RhaiResultOf<bool> {
ctx: NativeCallContext,
map1: &mut Map,
map2: Map,
) -> Result<bool, Box<EvalAltResult>> {
equals(ctx, map1, map2).map(|r| !r) equals(ctx, map1, map2).map(|r| !r)
} }
@ -115,7 +102,7 @@ mod map_functions {
if map.is_empty() { if map.is_empty() {
Array::new() Array::new()
} else { } else {
map.keys().cloned().map(Into::<Dynamic>::into).collect() map.keys().cloned().map(Into::into).collect()
} }
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]

View File

@ -1,7 +1,7 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
use crate::plugin::*; use crate::plugin::*;
use crate::{def_package, Position, INT}; use crate::{def_package, Position, RhaiResultOf, ERR, INT, UNSIGNED_INT};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -18,13 +18,6 @@ use rust_decimal::Decimal;
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
use super::arithmetic::make_err; use super::arithmetic::make_err;
#[allow(dead_code)]
#[cfg(feature = "only_i32")]
pub const MAX_INT: INT = i32::MAX;
#[allow(dead_code)]
#[cfg(not(feature = "only_i32"))]
pub const MAX_INT: INT = i64::MAX;
macro_rules! gen_conversion_as_functions { macro_rules! gen_conversion_as_functions {
($root:ident => $func_name:ident ( $($arg_type:ident),+ ) -> $result_type:ty) => { ($root:ident => $func_name:ident ( $($arg_type:ident),+ ) -> $result_type:ty) => {
pub mod $root { $(pub mod $arg_type { pub mod $root { $(pub mod $arg_type {
@ -58,78 +51,85 @@ macro_rules! reg_functions {
)* } )* }
} }
def_package!(crate:BasicMathPackage:"Basic mathematic functions.", lib, { def_package! {
lib.standard = true; /// Basic mathematical package.
crate::BasicMathPackage => |lib| {
lib.standard = true;
// Integer functions // Integer functions
combine_with_exported_module!(lib, "int", int_functions); combine_with_exported_module!(lib, "int", int_functions);
reg_functions!(lib += basic_to_int::to_int(char)); reg_functions!(lib += basic_to_int::to_int(char));
#[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))]
{
reg_functions!(lib += numbers_to_int::to_int(i8, u8, i16, u16, i32, u32, i64, u64));
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
reg_functions!(lib += num_128_to_int::to_int(i128, u128));
}
#[cfg(not(feature = "no_float"))]
{
// Floating point functions
combine_with_exported_module!(lib, "float", float_functions);
// Trig functions
combine_with_exported_module!(lib, "trig", trig_functions);
reg_functions!(lib += basic_to_float::to_float(INT));
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))] #[cfg(not(feature = "only_i64"))]
{ {
reg_functions!(lib += numbers_to_float::to_float(i8, u8, i16, u16, i32, u32, i64, u32)); reg_functions!(lib += numbers_to_int::to_int(i8, u8, i16, u16, i32, u32, i64, u64));
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #[cfg(not(target_arch = "wasm32"))]
reg_functions!(lib += num_128_to_float::to_float(i128, u128)); #[cfg(not(target_arch = "wasm64"))]
reg_functions!(lib += num_128_to_int::to_int(i128, u128));
}
#[cfg(not(feature = "no_float"))]
{
// Floating point functions
combine_with_exported_module!(lib, "float", float_functions);
// Trig functions
combine_with_exported_module!(lib, "trig", trig_functions);
reg_functions!(lib += basic_to_float::to_float(INT));
#[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))]
{
reg_functions!(lib += numbers_to_float::to_float(i8, u8, i16, u16, i32, u32, i64, u32));
#[cfg(not(target_arch = "wasm32"))]
#[cfg(not(target_arch = "wasm64"))]
reg_functions!(lib += num_128_to_float::to_float(i128, u128));
}
}
// Decimal functions
#[cfg(feature = "decimal")]
{
combine_with_exported_module!(lib, "decimal", decimal_functions);
reg_functions!(lib += basic_to_decimal::to_decimal(INT));
#[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))]
reg_functions!(lib += numbers_to_decimal::to_decimal(i8, u8, i16, u16, i32, u32, i64, u64));
} }
} }
}
// Decimal functions
#[cfg(feature = "decimal")]
{
combine_with_exported_module!(lib, "decimal", decimal_functions);
reg_functions!(lib += basic_to_decimal::to_decimal(INT));
#[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))]
reg_functions!(lib += numbers_to_decimal::to_decimal(i8, u8, i16, u16, i32, u32, i64, u64));
}
});
#[export_module] #[export_module]
mod int_functions { mod int_functions {
#[rhai_fn(name = "parse_int", return_raw)] #[rhai_fn(name = "parse_int", return_raw)]
pub fn parse_int_radix(string: &str, radix: INT) -> Result<INT, Box<EvalAltResult>> { pub fn parse_int_radix(string: &str, radix: INT) -> RhaiResultOf<INT> {
if !(2..=36).contains(&radix) { if !(2..=36).contains(&radix) {
return Err(EvalAltResult::ErrorArithmetic( return Err(ERR::ErrorArithmetic(
format!("Invalid radix: '{}'", radix), format!("Invalid radix: '{}'", radix),
Position::NONE, Position::NONE,
) )
.into()); .into());
} }
INT::from_str_radix(string.trim(), radix as u32).map_err(|err| { UNSIGNED_INT::from_str_radix(string.trim(), radix as u32)
EvalAltResult::ErrorArithmetic( .map(|v| v as INT)
format!("Error parsing integer number '{}': {}", string, err), .map_err(|err| {
Position::NONE, ERR::ErrorArithmetic(
) format!("Error parsing integer number '{}': {}", string, err),
.into() Position::NONE,
}) )
.into()
})
} }
#[rhai_fn(name = "parse_int", return_raw)] #[rhai_fn(name = "parse_int", return_raw)]
pub fn parse_int(string: &str) -> Result<INT, Box<EvalAltResult>> { pub fn parse_int(string: &str) -> RhaiResultOf<INT> {
parse_int_radix(string, 10) parse_int_radix(string, 10)
} }
} }
@ -258,33 +258,31 @@ mod float_functions {
x.is_infinite() x.is_infinite()
} }
#[rhai_fn(name = "to_int", return_raw)] #[rhai_fn(name = "to_int", return_raw)]
pub fn f32_to_int(x: f32) -> Result<INT, Box<EvalAltResult>> { pub fn f32_to_int(x: f32) -> RhaiResultOf<INT> {
if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f32) { if cfg!(not(feature = "unchecked")) && x > (INT::MAX as f32) {
Err(EvalAltResult::ErrorArithmetic( Err(
format!("Integer overflow: to_int({})", x), ERR::ErrorArithmetic(format!("Integer overflow: to_int({})", x), Position::NONE)
Position::NONE, .into(),
) )
.into())
} else { } else {
Ok(x.trunc() as INT) Ok(x.trunc() as INT)
} }
} }
#[rhai_fn(name = "to_int", return_raw)] #[rhai_fn(name = "to_int", return_raw)]
pub fn f64_to_int(x: f64) -> Result<INT, Box<EvalAltResult>> { pub fn f64_to_int(x: f64) -> RhaiResultOf<INT> {
if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f64) { if cfg!(not(feature = "unchecked")) && x > (INT::MAX as f64) {
Err(EvalAltResult::ErrorArithmetic( Err(
format!("Integer overflow: to_int({})", x), ERR::ErrorArithmetic(format!("Integer overflow: to_int({})", x), Position::NONE)
Position::NONE, .into(),
) )
.into())
} else { } else {
Ok(x.trunc() as INT) Ok(x.trunc() as INT)
} }
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn parse_float(string: &str) -> Result<FLOAT, Box<EvalAltResult>> { pub fn parse_float(string: &str) -> RhaiResultOf<FLOAT> {
string.trim().parse::<FLOAT>().map_err(|err| { string.trim().parse::<FLOAT>().map_err(|err| {
EvalAltResult::ErrorArithmetic( ERR::ErrorArithmetic(
format!("Error parsing floating-point number '{}': {}", string, err), format!("Error parsing floating-point number '{}': {}", string, err),
Position::NONE, Position::NONE,
) )
@ -320,7 +318,7 @@ mod decimal_functions {
} }
#[cfg(feature = "no_float")] #[cfg(feature = "no_float")]
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn parse_float(s: &str) -> Result<Decimal, Box<EvalAltResult>> { pub fn parse_float(s: &str) -> RhaiResultOf<Decimal> {
parse_decimal(s) parse_decimal(s)
} }
@ -334,12 +332,12 @@ mod decimal_functions {
x.tan() x.tan()
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn sqrt(x: Decimal) -> Result<Decimal, Box<EvalAltResult>> { pub fn sqrt(x: Decimal) -> RhaiResultOf<Decimal> {
x.sqrt() x.sqrt()
.ok_or_else(|| make_err(format!("Error taking the square root of {}", x,))) .ok_or_else(|| make_err(format!("Error taking the square root of {}", x,)))
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn exp(x: Decimal) -> Result<Decimal, Box<EvalAltResult>> { pub fn exp(x: Decimal) -> RhaiResultOf<Decimal> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
x.checked_exp() x.checked_exp()
.ok_or_else(|| make_err(format!("Exponential overflow: e ** {}", x,))) .ok_or_else(|| make_err(format!("Exponential overflow: e ** {}", x,)))
@ -348,7 +346,7 @@ mod decimal_functions {
} }
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn ln(x: Decimal) -> Result<Decimal, Box<EvalAltResult>> { pub fn ln(x: Decimal) -> RhaiResultOf<Decimal> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
x.checked_ln() x.checked_ln()
.ok_or_else(|| make_err(format!("Error taking the natural log of {}", x))) .ok_or_else(|| make_err(format!("Error taking the natural log of {}", x)))
@ -357,7 +355,7 @@ mod decimal_functions {
} }
} }
#[rhai_fn(name = "log", return_raw)] #[rhai_fn(name = "log", return_raw)]
pub fn log10(x: Decimal) -> Result<Decimal, Box<EvalAltResult>> { pub fn log10(x: Decimal) -> RhaiResultOf<Decimal> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
x.checked_log10() x.checked_log10()
.ok_or_else(|| make_err(format!("Error taking the log of {}", x))) .ok_or_else(|| make_err(format!("Error taking the log of {}", x)))
@ -378,7 +376,7 @@ mod decimal_functions {
x.round() x.round()
} }
#[rhai_fn(name = "round", return_raw)] #[rhai_fn(name = "round", return_raw)]
pub fn round_dp(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> { pub fn round_dp(x: Decimal, dp: INT) -> RhaiResultOf<Decimal> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
if dp < 0 { if dp < 0 {
return Err(make_err(format!( return Err(make_err(format!(
@ -394,7 +392,7 @@ mod decimal_functions {
Ok(x.round_dp(dp as u32)) Ok(x.round_dp(dp as u32))
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn round_up(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> { pub fn round_up(x: Decimal, dp: INT) -> RhaiResultOf<Decimal> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
if dp < 0 { if dp < 0 {
return Err(make_err(format!( return Err(make_err(format!(
@ -410,7 +408,7 @@ mod decimal_functions {
Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::AwayFromZero)) Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::AwayFromZero))
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn round_down(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> { pub fn round_down(x: Decimal, dp: INT) -> RhaiResultOf<Decimal> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
if dp < 0 { if dp < 0 {
return Err(make_err(format!( return Err(make_err(format!(
@ -426,7 +424,7 @@ mod decimal_functions {
Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::ToZero)) Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::ToZero))
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn round_half_up(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> { pub fn round_half_up(x: Decimal, dp: INT) -> RhaiResultOf<Decimal> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
if dp < 0 { if dp < 0 {
return Err(make_err(format!( return Err(make_err(format!(
@ -442,7 +440,7 @@ mod decimal_functions {
Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::MidpointAwayFromZero)) Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::MidpointAwayFromZero))
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn round_half_down(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> { pub fn round_half_down(x: Decimal, dp: INT) -> RhaiResultOf<Decimal> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
if dp < 0 { if dp < 0 {
return Err(make_err(format!( return Err(make_err(format!(
@ -466,11 +464,11 @@ mod decimal_functions {
x.fract() x.fract()
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn parse_decimal(string: &str) -> Result<Decimal, Box<EvalAltResult>> { pub fn parse_decimal(string: &str) -> RhaiResultOf<Decimal> {
Decimal::from_str(string) Decimal::from_str(string)
.or_else(|_| Decimal::from_scientific(string)) .or_else(|_| Decimal::from_scientific(string))
.map_err(|err| { .map_err(|err| {
EvalAltResult::ErrorArithmetic( ERR::ErrorArithmetic(
format!("Error parsing decimal number '{}': {}", string, err), format!("Error parsing decimal number '{}': {}", string, err),
Position::NONE, Position::NONE,
) )
@ -480,9 +478,9 @@ mod decimal_functions {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
#[rhai_fn(name = "to_decimal", return_raw)] #[rhai_fn(name = "to_decimal", return_raw)]
pub fn f32_to_decimal(x: f32) -> Result<Decimal, Box<EvalAltResult>> { pub fn f32_to_decimal(x: f32) -> RhaiResultOf<Decimal> {
Decimal::try_from(x).map_err(|_| { Decimal::try_from(x).map_err(|_| {
EvalAltResult::ErrorArithmetic( ERR::ErrorArithmetic(
format!("Cannot convert to Decimal: to_decimal({})", x), format!("Cannot convert to Decimal: to_decimal({})", x),
Position::NONE, Position::NONE,
) )
@ -491,9 +489,9 @@ mod decimal_functions {
} }
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
#[rhai_fn(name = "to_decimal", return_raw)] #[rhai_fn(name = "to_decimal", return_raw)]
pub fn f64_to_decimal(x: f64) -> Result<Decimal, Box<EvalAltResult>> { pub fn f64_to_decimal(x: f64) -> RhaiResultOf<Decimal> {
Decimal::try_from(x).map_err(|_| { Decimal::try_from(x).map_err(|_| {
EvalAltResult::ErrorArithmetic( ERR::ErrorArithmetic(
format!("Cannot convert to Decimal: to_decimal({})", x), format!("Cannot convert to Decimal: to_decimal({})", x),
Position::NONE, Position::NONE,
) )
@ -502,9 +500,9 @@ mod decimal_functions {
} }
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn to_float(x: Decimal) -> Result<FLOAT, Box<EvalAltResult>> { pub fn to_float(x: Decimal) -> RhaiResultOf<FLOAT> {
FLOAT::try_from(x).map_err(|_| { FLOAT::try_from(x).map_err(|_| {
EvalAltResult::ErrorArithmetic( ERR::ErrorArithmetic(
format!("Cannot convert to floating-point: to_float({})", x), format!("Cannot convert to floating-point: to_float({})", x),
Position::NONE, Position::NONE,
) )
@ -524,7 +522,8 @@ gen_conversion_as_functions!(numbers_to_float => to_float (i8, u8, i16, u16, i32
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))] #[cfg(not(feature = "only_i64"))]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #[cfg(not(target_arch = "wasm32"))]
#[cfg(not(target_arch = "wasm64"))]
gen_conversion_as_functions!(num_128_to_float => to_float (i128, u128) -> FLOAT); gen_conversion_as_functions!(num_128_to_float => to_float (i128, u128) -> FLOAT);
gen_conversion_as_functions!(basic_to_int => to_int (char) -> INT); gen_conversion_as_functions!(basic_to_int => to_int (char) -> INT);
@ -535,7 +534,8 @@ gen_conversion_as_functions!(numbers_to_int => to_int (i8, u8, i16, u16, i32, u3
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))] #[cfg(not(feature = "only_i64"))]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #[cfg(not(target_arch = "wasm32"))]
#[cfg(not(target_arch = "wasm64"))]
gen_conversion_as_functions!(num_128_to_int => to_int (i128, u128) -> INT); gen_conversion_as_functions!(num_128_to_int => to_int (i128, u128) -> INT);
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]

View File

@ -3,8 +3,9 @@
use crate::{Module, Shared}; use crate::{Module, Shared};
pub(crate) mod arithmetic; pub(crate) mod arithmetic;
mod array_basic; pub(crate) mod array_basic;
mod blob_basic; mod bit_field;
pub(crate) mod blob_basic;
mod fn_basic; mod fn_basic;
mod iter_basic; mod iter_basic;
mod lang_core; mod lang_core;
@ -20,10 +21,12 @@ mod time_basic;
pub use arithmetic::ArithmeticPackage; pub use arithmetic::ArithmeticPackage;
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
pub use array_basic::BasicArrayPackage; pub use array_basic::BasicArrayPackage;
pub use bit_field::BitFieldPackage;
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
pub use blob_basic::BasicBlobPackage; pub use blob_basic::BasicBlobPackage;
pub use fn_basic::BasicFnPackage; pub use fn_basic::BasicFnPackage;
pub use iter_basic::BasicIteratorPackage; pub use iter_basic::BasicIteratorPackage;
pub use lang_core::LanguageCorePackage;
pub use logic::LogicPackage; pub use logic::LogicPackage;
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
pub use map_basic::BasicMapPackage; pub use map_basic::BasicMapPackage;
@ -69,8 +72,8 @@ pub trait Package {
/// ``` /// ```
#[macro_export] #[macro_export]
macro_rules! def_package { macro_rules! def_package {
($root:ident : $package:ident : $comment:expr , $lib:ident , $block:stmt) => { ($($(#[$outer:meta])* $root:ident :: $package:ident => | $lib:ident | $block:block)+) => { $(
#[doc=$comment] $(#[$outer])*
pub struct $package($root::Shared<$root::Module>); pub struct $package($root::Shared<$root::Module>);
impl $root::packages::Package for $package { impl $root::packages::Package for $package {
@ -88,6 +91,42 @@ macro_rules! def_package {
} }
} }
impl $package {
pub fn new() -> Self {
let mut module = $root::Module::new();
<Self as $root::packages::Package>::init(&mut module);
module.build_index();
Self(module.into())
}
}
)* };
($root:ident : $package:ident : $comment:expr , $lib:ident , $block:stmt) => {
#[deprecated(since = "1.4.0", note = "this is an old syntax of `def_package!` and is deprecated; use the new syntax of `def_package!` instead")]
#[doc=$comment]
///
/// # Deprecated
///
/// This old syntax of `def_package!` is deprecated. Use the new syntax instead.
///
/// This syntax will be removed in the next major version.
pub struct $package($root::Shared<$root::Module>);
impl $root::packages::Package for $package {
fn as_shared_module(&self) -> $root::Shared<$root::Module> {
#[allow(deprecated)]
self.0.clone()
}
fn init($lib: &mut $root::Module) {
$block
}
}
impl Default for $package {
fn default() -> Self {
Self::new()
}
}
impl $package { impl $package {
pub fn new() -> Self { pub fn new() -> Self {
let mut module = $root::Module::new(); let mut module = $root::Module::new();

View File

@ -1,21 +1,25 @@
use super::arithmetic::ArithmeticPackage;
use super::fn_basic::BasicFnPackage;
use super::iter_basic::BasicIteratorPackage;
use super::lang_core::LanguageCorePackage;
use super::logic::LogicPackage;
use super::string_basic::BasicStringPackage;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
use crate::def_package; use crate::def_package;
def_package!(crate:CorePackage:"_Core_ package containing basic facilities.", lib, { def_package! {
lib.standard = true; /// Core package containing basic facilities.
///
/// # Contents
///
/// * [`LanguageCorePackage`][super::LanguageCorePackage]
/// * [`ArithmeticPackage`][super::ArithmeticPackage]
/// * [`BasicStringPackage`][super::BasicStringPackage]
/// * [`BasicIteratorPackage`][super::BasicIteratorPackage]
/// * [`BasicFnPackage`][super::BasicFnPackage]
crate::CorePackage => |lib| {
lib.standard = true;
LanguageCorePackage::init(lib); super::LanguageCorePackage::init(lib);
ArithmeticPackage::init(lib); super::ArithmeticPackage::init(lib);
LogicPackage::init(lib); super::BasicStringPackage::init(lib);
BasicStringPackage::init(lib); super::BasicIteratorPackage::init(lib);
BasicIteratorPackage::init(lib); super::BasicFnPackage::init(lib);
BasicFnPackage::init(lib); }
}); }

View File

@ -1,32 +1,37 @@
#[cfg(not(feature = "no_index"))]
use super::array_basic::BasicArrayPackage;
#[cfg(not(feature = "no_index"))]
use super::blob_basic::BasicBlobPackage;
#[cfg(not(feature = "no_object"))]
use super::map_basic::BasicMapPackage;
use super::math_basic::BasicMathPackage;
use super::pkg_core::CorePackage;
use super::string_more::MoreStringPackage;
#[cfg(not(feature = "no_std"))]
use super::time_basic::BasicTimePackage;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
use crate::def_package; use crate::def_package;
def_package!(crate:StandardPackage:"_Standard_ package containing all built-in features.", lib, { def_package! {
lib.standard = true; /// Standard package containing all built-in features.
///
/// # Contents
///
/// * [`CorePackage`][super::CorePackage]
/// * [`BitFieldPackage`][super::BitFieldPackage]
/// * [`LogicPackage`][super::LogicPackage]
/// * [`BasicMathPackage`][super::BasicMathPackage]
/// * [`BasicArrayPackage`][super::BasicArrayPackage]
/// * [`BasicBlobPackage`][super::BasicBlobPackage]
/// * [`BasicMapPackage`][super::BasicMapPackage]
/// * [`BasicTimePackage`][super::BasicTimePackage]
/// * [`MoreStringPackage`][super::MoreStringPackage]
crate::StandardPackage => |lib| {
lib.standard = true;
CorePackage::init(lib); super::CorePackage::init(lib);
BasicMathPackage::init(lib); super::BitFieldPackage::init(lib);
#[cfg(not(feature = "no_index"))] super::LogicPackage::init(lib);
{ super::BasicMathPackage::init(lib);
BasicArrayPackage::init(lib); #[cfg(not(feature = "no_index"))]
BasicBlobPackage::init(lib); super::BasicArrayPackage::init(lib);
#[cfg(not(feature = "no_index"))]
super::BasicBlobPackage::init(lib);
#[cfg(not(feature = "no_object"))]
super::BasicMapPackage::init(lib);
#[cfg(not(feature = "no_std"))]
super::BasicTimePackage::init(lib);
super::MoreStringPackage::init(lib);
} }
#[cfg(not(feature = "no_object"))] }
BasicMapPackage::init(lib);
#[cfg(not(feature = "no_std"))]
BasicTimePackage::init(lib);
MoreStringPackage::init(lib);
});

View File

@ -15,12 +15,15 @@ use crate::Map;
pub const FUNC_TO_STRING: &str = "to_string"; pub const FUNC_TO_STRING: &str = "to_string";
pub const FUNC_TO_DEBUG: &str = "to_debug"; pub const FUNC_TO_DEBUG: &str = "to_debug";
def_package!(crate:BasicStringPackage:"Basic string utilities, including printing.", lib, { def_package! {
lib.standard = true; /// Package of basic string utilities (e.g. printing)
crate::BasicStringPackage => |lib| {
lib.standard = true;
combine_with_exported_module!(lib, "print_debug", print_debug_functions); combine_with_exported_module!(lib, "print_debug", print_debug_functions);
combine_with_exported_module!(lib, "number_formatting", number_formatting); combine_with_exported_module!(lib, "number_formatting", number_formatting);
}); }
}
// Register print and debug // Register print and debug
@ -271,7 +274,8 @@ mod number_formatting {
to_binary(value) to_binary(value)
} }
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #[cfg(not(target_arch = "wasm32"))]
#[cfg(not(target_arch = "wasm64"))]
pub mod num_128 { pub mod num_128 {
#[rhai_fn(name = "to_hex")] #[rhai_fn(name = "to_hex")]
pub fn u128_to_hex(value: u128) -> ImmutableString { pub fn u128_to_hex(value: u128) -> ImmutableString {

View File

@ -1,18 +1,21 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
use crate::plugin::*; use crate::plugin::*;
use crate::{def_package, Dynamic, ExclusiveRange, InclusiveRange, StaticVec, INT}; use crate::{def_package, Dynamic, ExclusiveRange, InclusiveRange, RhaiResultOf, StaticVec, INT};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
use std::{any::TypeId, mem}; use std::{any::TypeId, mem};
use super::string_basic::{print_with_func, FUNC_TO_STRING}; use super::string_basic::{print_with_func, FUNC_TO_STRING};
def_package!(crate:MoreStringPackage:"Additional string utilities, including string building.", lib, { def_package! {
lib.standard = true; /// Package of additional string utilities over [`BasicStringPackage`][super::BasicStringPackage]
crate::MoreStringPackage => |lib| {
lib.standard = true;
combine_with_exported_module!(lib, "string", string_functions); combine_with_exported_module!(lib, "string", string_functions);
}); }
}
#[export_module] #[export_module]
mod string_functions { mod string_functions {
@ -175,7 +178,7 @@ mod string_functions {
#[rhai_fn(name = "to_upper")] #[rhai_fn(name = "to_upper")]
pub fn to_upper_char(character: char) -> char { pub fn to_upper_char(character: char) -> char {
let mut stream = character.to_uppercase(); let mut stream = character.to_uppercase();
let ch = stream.next().expect("not empty"); let ch = stream.next().unwrap();
if stream.next().is_some() { if stream.next().is_some() {
character character
} else { } else {
@ -189,7 +192,7 @@ mod string_functions {
#[rhai_fn(name = "to_lower")] #[rhai_fn(name = "to_lower")]
pub fn to_lower_char(character: char) -> char { pub fn to_lower_char(character: char) -> char {
let mut stream = character.to_lowercase(); let mut stream = character.to_lowercase();
let ch = stream.next().expect("not empty"); let ch = stream.next().unwrap();
if stream.next().is_some() { if stream.next().is_some() {
character character
} else { } else {
@ -497,7 +500,7 @@ mod string_functions {
string: &mut ImmutableString, string: &mut ImmutableString,
len: INT, len: INT,
character: char, character: char,
) -> Result<(), Box<crate::EvalAltResult>> { ) -> RhaiResultOf<()> {
if len <= 0 { if len <= 0 {
return Ok(()); return Ok(());
} }
@ -506,7 +509,7 @@ mod string_functions {
// Check if string will be over max size limit // Check if string will be over max size limit
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if _ctx.engine().max_string_size() > 0 && len as usize > _ctx.engine().max_string_size() { if _ctx.engine().max_string_size() > 0 && len as usize > _ctx.engine().max_string_size() {
return Err(crate::EvalAltResult::ErrorDataTooLarge( return Err(crate::ERR::ErrorDataTooLarge(
"Length of string".to_string(), "Length of string".to_string(),
crate::Position::NONE, crate::Position::NONE,
) )
@ -525,7 +528,7 @@ mod string_functions {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if _ctx.engine().max_string_size() > 0 && string.len() > _ctx.engine().max_string_size() if _ctx.engine().max_string_size() > 0 && string.len() > _ctx.engine().max_string_size()
{ {
return Err(crate::EvalAltResult::ErrorDataTooLarge( return Err(crate::ERR::ErrorDataTooLarge(
"Length of string".to_string(), "Length of string".to_string(),
crate::Position::NONE, crate::Position::NONE,
) )
@ -541,7 +544,7 @@ mod string_functions {
string: &mut ImmutableString, string: &mut ImmutableString,
len: INT, len: INT,
padding: &str, padding: &str,
) -> Result<(), Box<crate::EvalAltResult>> { ) -> RhaiResultOf<()> {
if len <= 0 { if len <= 0 {
return Ok(()); return Ok(());
} }
@ -550,7 +553,7 @@ mod string_functions {
// Check if string will be over max size limit // Check if string will be over max size limit
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if _ctx.engine().max_string_size() > 0 && len as usize > _ctx.engine().max_string_size() { if _ctx.engine().max_string_size() > 0 && len as usize > _ctx.engine().max_string_size() {
return Err(crate::EvalAltResult::ErrorDataTooLarge( return Err(crate::ERR::ErrorDataTooLarge(
"Length of string".to_string(), "Length of string".to_string(),
crate::Position::NONE, crate::Position::NONE,
) )
@ -576,7 +579,7 @@ mod string_functions {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if _ctx.engine().max_string_size() > 0 && string.len() > _ctx.engine().max_string_size() if _ctx.engine().max_string_size() > 0 && string.len() > _ctx.engine().max_string_size()
{ {
return Err(crate::EvalAltResult::ErrorDataTooLarge( return Err(crate::ERR::ErrorDataTooLarge(
"Length of string".to_string(), "Length of string".to_string(),
crate::Position::NONE, crate::Position::NONE,
) )
@ -596,7 +599,7 @@ mod string_functions {
if string.is_empty() { if string.is_empty() {
Array::new() Array::new()
} else { } else {
string.chars().map(Into::<Dynamic>::into).collect() string.chars().map(Into::into).collect()
} }
} }
#[rhai_fn(name = "split")] #[rhai_fn(name = "split")]
@ -621,57 +624,39 @@ mod string_functions {
} }
} }
pub fn split(string: &str, delimiter: &str) -> Array { pub fn split(string: &str, delimiter: &str) -> Array {
string.split(delimiter).map(Into::<Dynamic>::into).collect() string.split(delimiter).map(Into::into).collect()
} }
#[rhai_fn(name = "split")] #[rhai_fn(name = "split")]
pub fn splitn(string: &str, delimiter: &str, segments: INT) -> Array { pub fn splitn(string: &str, delimiter: &str, segments: INT) -> Array {
let pieces: usize = if segments < 1 { 1 } else { segments as usize }; let pieces: usize = if segments < 1 { 1 } else { segments as usize };
string string.splitn(pieces, delimiter).map(Into::into).collect()
.splitn(pieces, delimiter)
.map(Into::<Dynamic>::into)
.collect()
} }
#[rhai_fn(name = "split")] #[rhai_fn(name = "split")]
pub fn split_char(string: &str, delimiter: char) -> Array { pub fn split_char(string: &str, delimiter: char) -> Array {
string.split(delimiter).map(Into::<Dynamic>::into).collect() string.split(delimiter).map(Into::into).collect()
} }
#[rhai_fn(name = "split")] #[rhai_fn(name = "split")]
pub fn splitn_char(string: &str, delimiter: char, segments: INT) -> Array { pub fn splitn_char(string: &str, delimiter: char, segments: INT) -> Array {
let pieces: usize = if segments < 1 { 1 } else { segments as usize }; let pieces: usize = if segments < 1 { 1 } else { segments as usize };
string string.splitn(pieces, delimiter).map(Into::into).collect()
.splitn(pieces, delimiter)
.map(Into::<Dynamic>::into)
.collect()
} }
#[rhai_fn(name = "split_rev")] #[rhai_fn(name = "split_rev")]
pub fn rsplit(string: &str, delimiter: &str) -> Array { pub fn rsplit(string: &str, delimiter: &str) -> Array {
string string.rsplit(delimiter).map(Into::into).collect()
.rsplit(delimiter)
.map(Into::<Dynamic>::into)
.collect()
} }
#[rhai_fn(name = "split_rev")] #[rhai_fn(name = "split_rev")]
pub fn rsplitn(string: &str, delimiter: &str, segments: INT) -> Array { pub fn rsplitn(string: &str, delimiter: &str, segments: INT) -> Array {
let pieces: usize = if segments < 1 { 1 } else { segments as usize }; let pieces: usize = if segments < 1 { 1 } else { segments as usize };
string string.rsplitn(pieces, delimiter).map(Into::into).collect()
.rsplitn(pieces, delimiter)
.map(Into::<Dynamic>::into)
.collect()
} }
#[rhai_fn(name = "split_rev")] #[rhai_fn(name = "split_rev")]
pub fn rsplit_char(string: &str, delimiter: char) -> Array { pub fn rsplit_char(string: &str, delimiter: char) -> Array {
string string.rsplit(delimiter).map(Into::into).collect()
.rsplit(delimiter)
.map(Into::<Dynamic>::into)
.collect()
} }
#[rhai_fn(name = "split_rev")] #[rhai_fn(name = "split_rev")]
pub fn rsplitn_char(string: &str, delimiter: char, segments: INT) -> Array { pub fn rsplitn_char(string: &str, delimiter: char, segments: INT) -> Array {
let pieces: usize = if segments < 1 { 1 } else { segments as usize }; let pieces: usize = if segments < 1 { 1 } else { segments as usize };
string string.rsplitn(pieces, delimiter).map(Into::into).collect()
.rsplitn(pieces, delimiter)
.map(Into::<Dynamic>::into)
.collect()
} }
} }
} }

View File

@ -1,24 +1,28 @@
#![cfg(not(feature = "no_std"))] #![cfg(not(feature = "no_std"))]
use super::{arithmetic::make_err as make_arithmetic_err, math_basic::MAX_INT}; use super::arithmetic::make_err as make_arithmetic_err;
use crate::plugin::*; use crate::plugin::*;
use crate::{def_package, Dynamic, EvalAltResult, INT}; use crate::{def_package, Dynamic, EvalAltResult, RhaiResult, RhaiResultOf, INT};
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
use crate::FLOAT; use crate::FLOAT;
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #[cfg(not(target_arch = "wasm32"))]
#[cfg(not(target_arch = "wasm64"))]
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
use instant::{Duration, Instant}; use instant::{Duration, Instant};
def_package!(crate:BasicTimePackage:"Basic timing utilities.", lib, { def_package! {
lib.standard = true; /// Package of basic timing utilities.
crate::BasicTimePackage => |lib| {
lib.standard = true;
// Register date/time functions // Register date/time functions
combine_with_exported_module!(lib, "time", time_functions); combine_with_exported_module!(lib, "time", time_functions);
}); }
}
#[export_module] #[export_module]
mod time_functions { mod time_functions {
@ -27,7 +31,7 @@ mod time_functions {
} }
#[rhai_fn(name = "elapsed", get = "elapsed", return_raw)] #[rhai_fn(name = "elapsed", get = "elapsed", return_raw)]
pub fn elapsed(timestamp: Instant) -> Result<Dynamic, Box<EvalAltResult>> { pub fn elapsed(timestamp: Instant) -> RhaiResult {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
if timestamp > Instant::now() { if timestamp > Instant::now() {
Err(make_arithmetic_err("Time-stamp is later than now")) Err(make_arithmetic_err("Time-stamp is later than now"))
@ -39,7 +43,7 @@ mod time_functions {
{ {
let seconds = timestamp.elapsed().as_secs(); let seconds = timestamp.elapsed().as_secs();
if cfg!(not(feature = "unchecked")) && seconds > (MAX_INT as u64) { if cfg!(not(feature = "unchecked")) && seconds > (INT::MAX as u64) {
Err(make_arithmetic_err(format!( Err(make_arithmetic_err(format!(
"Integer overflow for timestamp.elapsed: {}", "Integer overflow for timestamp.elapsed: {}",
seconds seconds
@ -53,10 +57,7 @@ mod time_functions {
} }
#[rhai_fn(return_raw, name = "-")] #[rhai_fn(return_raw, name = "-")]
pub fn time_diff( pub fn time_diff(timestamp1: Instant, timestamp2: Instant) -> RhaiResult {
timestamp1: Instant,
timestamp2: Instant,
) -> Result<Dynamic, Box<EvalAltResult>> {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
return Ok(if timestamp2 > timestamp1 { return Ok(if timestamp2 > timestamp1 {
-(timestamp2 - timestamp1).as_secs_f64() as FLOAT -(timestamp2 - timestamp1).as_secs_f64() as FLOAT
@ -69,7 +70,7 @@ mod time_functions {
if timestamp2 > timestamp1 { if timestamp2 > timestamp1 {
let seconds = (timestamp2 - timestamp1).as_secs(); let seconds = (timestamp2 - timestamp1).as_secs();
if cfg!(not(feature = "unchecked")) && seconds > (MAX_INT as u64) { if cfg!(not(feature = "unchecked")) && seconds > (INT::MAX as u64) {
Err(make_arithmetic_err(format!( Err(make_arithmetic_err(format!(
"Integer overflow for timestamp duration: -{}", "Integer overflow for timestamp duration: -{}",
seconds seconds
@ -80,7 +81,7 @@ mod time_functions {
} else { } else {
let seconds = (timestamp1 - timestamp2).as_secs(); let seconds = (timestamp1 - timestamp2).as_secs();
if cfg!(not(feature = "unchecked")) && seconds > (MAX_INT as u64) { if cfg!(not(feature = "unchecked")) && seconds > (INT::MAX as u64) {
Err(make_arithmetic_err(format!( Err(make_arithmetic_err(format!(
"Integer overflow for timestamp duration: {}", "Integer overflow for timestamp duration: {}",
seconds seconds
@ -93,11 +94,11 @@ mod time_functions {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
pub mod float_functions { pub mod float_functions {
fn add_impl(timestamp: Instant, seconds: FLOAT) -> Result<Instant, Box<EvalAltResult>> { fn add_impl(timestamp: Instant, seconds: FLOAT) -> RhaiResultOf<Instant> {
if seconds < 0.0 { if seconds < 0.0 {
subtract_impl(timestamp, -seconds) subtract_impl(timestamp, -seconds)
} else if cfg!(not(feature = "unchecked")) { } else if cfg!(not(feature = "unchecked")) {
if seconds > (MAX_INT as FLOAT) { if seconds > (INT::MAX as FLOAT) {
Err(make_arithmetic_err(format!( Err(make_arithmetic_err(format!(
"Integer overflow for timestamp add: {}", "Integer overflow for timestamp add: {}",
seconds seconds
@ -116,14 +117,11 @@ mod time_functions {
Ok(timestamp + Duration::from_millis((seconds * 1000.0) as u64)) Ok(timestamp + Duration::from_millis((seconds * 1000.0) as u64))
} }
} }
fn subtract_impl( fn subtract_impl(timestamp: Instant, seconds: FLOAT) -> RhaiResultOf<Instant> {
timestamp: Instant,
seconds: FLOAT,
) -> Result<Instant, Box<EvalAltResult>> {
if seconds < 0.0 { if seconds < 0.0 {
add_impl(timestamp, -seconds) add_impl(timestamp, -seconds)
} else if cfg!(not(feature = "unchecked")) { } else if cfg!(not(feature = "unchecked")) {
if seconds > (MAX_INT as FLOAT) { if seconds > (INT::MAX as FLOAT) {
Err(make_arithmetic_err(format!( Err(make_arithmetic_err(format!(
"Integer overflow for timestamp add: {}", "Integer overflow for timestamp add: {}",
seconds seconds
@ -144,32 +142,26 @@ mod time_functions {
} }
#[rhai_fn(return_raw, name = "+")] #[rhai_fn(return_raw, name = "+")]
pub fn add(timestamp: Instant, seconds: FLOAT) -> Result<Instant, Box<EvalAltResult>> { pub fn add(timestamp: Instant, seconds: FLOAT) -> RhaiResultOf<Instant> {
add_impl(timestamp, seconds) add_impl(timestamp, seconds)
} }
#[rhai_fn(return_raw, name = "+=")] #[rhai_fn(return_raw, name = "+=")]
pub fn add_assign( pub fn add_assign(timestamp: &mut Instant, seconds: FLOAT) -> RhaiResultOf<()> {
timestamp: &mut Instant,
seconds: FLOAT,
) -> Result<(), Box<EvalAltResult>> {
*timestamp = add_impl(*timestamp, seconds)?; *timestamp = add_impl(*timestamp, seconds)?;
Ok(()) Ok(())
} }
#[rhai_fn(return_raw, name = "-")] #[rhai_fn(return_raw, name = "-")]
pub fn subtract(timestamp: Instant, seconds: FLOAT) -> Result<Instant, Box<EvalAltResult>> { pub fn subtract(timestamp: Instant, seconds: FLOAT) -> RhaiResultOf<Instant> {
subtract_impl(timestamp, seconds) subtract_impl(timestamp, seconds)
} }
#[rhai_fn(return_raw, name = "-=")] #[rhai_fn(return_raw, name = "-=")]
pub fn subtract_assign( pub fn subtract_assign(timestamp: &mut Instant, seconds: FLOAT) -> RhaiResultOf<()> {
timestamp: &mut Instant,
seconds: FLOAT,
) -> Result<(), Box<EvalAltResult>> {
*timestamp = subtract_impl(*timestamp, seconds)?; *timestamp = subtract_impl(*timestamp, seconds)?;
Ok(()) Ok(())
} }
} }
fn add_impl(timestamp: Instant, seconds: INT) -> Result<Instant, Box<EvalAltResult>> { fn add_impl(timestamp: Instant, seconds: INT) -> RhaiResultOf<Instant> {
if seconds < 0 { if seconds < 0 {
subtract_impl(timestamp, -seconds) subtract_impl(timestamp, -seconds)
} else if cfg!(not(feature = "unchecked")) { } else if cfg!(not(feature = "unchecked")) {
@ -185,7 +177,7 @@ mod time_functions {
Ok(timestamp + Duration::from_secs(seconds as u64)) Ok(timestamp + Duration::from_secs(seconds as u64))
} }
} }
fn subtract_impl(timestamp: Instant, seconds: INT) -> Result<Instant, Box<EvalAltResult>> { fn subtract_impl(timestamp: Instant, seconds: INT) -> RhaiResultOf<Instant> {
if seconds < 0 { if seconds < 0 {
add_impl(timestamp, -seconds) add_impl(timestamp, -seconds)
} else if cfg!(not(feature = "unchecked")) { } else if cfg!(not(feature = "unchecked")) {
@ -203,23 +195,20 @@ mod time_functions {
} }
#[rhai_fn(return_raw, name = "+")] #[rhai_fn(return_raw, name = "+")]
pub fn add(timestamp: Instant, seconds: INT) -> Result<Instant, Box<EvalAltResult>> { pub fn add(timestamp: Instant, seconds: INT) -> RhaiResultOf<Instant> {
add_impl(timestamp, seconds) add_impl(timestamp, seconds)
} }
#[rhai_fn(return_raw, name = "+=")] #[rhai_fn(return_raw, name = "+=")]
pub fn add_assign(timestamp: &mut Instant, seconds: INT) -> Result<(), Box<EvalAltResult>> { pub fn add_assign(timestamp: &mut Instant, seconds: INT) -> RhaiResultOf<()> {
*timestamp = add_impl(*timestamp, seconds)?; *timestamp = add_impl(*timestamp, seconds)?;
Ok(()) Ok(())
} }
#[rhai_fn(return_raw, name = "-")] #[rhai_fn(return_raw, name = "-")]
pub fn subtract(timestamp: Instant, seconds: INT) -> Result<Instant, Box<EvalAltResult>> { pub fn subtract(timestamp: Instant, seconds: INT) -> RhaiResultOf<Instant> {
subtract_impl(timestamp, seconds) subtract_impl(timestamp, seconds)
} }
#[rhai_fn(return_raw, name = "-=")] #[rhai_fn(return_raw, name = "-=")]
pub fn subtract_assign( pub fn subtract_assign(timestamp: &mut Instant, seconds: INT) -> RhaiResultOf<()> {
timestamp: &mut Instant,
seconds: INT,
) -> Result<(), Box<EvalAltResult>> {
*timestamp = subtract_impl(*timestamp, seconds)?; *timestamp = subtract_impl(*timestamp, seconds)?;
Ok(()) Ok(())
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
//! Implement deserialization support of [`Dynamic`][crate::Dynamic] for [`serde`]. //! Implement deserialization support of [`Dynamic`][crate::Dynamic] for [`serde`].
use crate::types::dynamic::Union; use crate::types::dynamic::Union;
use crate::{Dynamic, EvalAltResult, ImmutableString, LexError, Position}; use crate::{Dynamic, ImmutableString, LexError, Position, RhaiError, RhaiResultOf, ERR};
use serde::de::{DeserializeSeed, Error, IntoDeserializer, Visitor}; use serde::de::{Error, IntoDeserializer, Visitor};
use serde::{Deserialize, Deserializer}; use serde::{Deserialize, Deserializer};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -26,12 +26,12 @@ impl<'de> DynamicDeserializer<'de> {
Self { value } Self { value }
} }
/// Shortcut for a type conversion error. /// Shortcut for a type conversion error.
fn type_error<T>(&self) -> Result<T, Box<EvalAltResult>> { fn type_error<T>(&self) -> RhaiResultOf<T> {
self.type_error_str(type_name::<T>()) self.type_error_str(type_name::<T>())
} }
/// Shortcut for a type conversion error. /// Shortcut for a type conversion error.
fn type_error_str<T>(&self, error: &str) -> Result<T, Box<EvalAltResult>> { fn type_error_str<T>(&self, error: &str) -> RhaiResultOf<T> {
Err(EvalAltResult::ErrorMismatchOutputType( Err(ERR::ErrorMismatchOutputType(
error.into(), error.into(),
self.value.type_name().into(), self.value.type_name().into(),
Position::NONE, Position::NONE,
@ -42,7 +42,7 @@ impl<'de> DynamicDeserializer<'de> {
&mut self, &mut self,
v: crate::INT, v: crate::INT,
visitor: V, visitor: V,
) -> Result<V::Value, Box<EvalAltResult>> { ) -> RhaiResultOf<V::Value> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
return visitor.visit_i64(v); return visitor.visit_i64(v);
#[cfg(feature = "only_i32")] #[cfg(feature = "only_i32")]
@ -101,13 +101,11 @@ impl<'de> DynamicDeserializer<'de> {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
pub fn from_dynamic<'de, T: Deserialize<'de>>( pub fn from_dynamic<'de, T: Deserialize<'de>>(value: &'de Dynamic) -> RhaiResultOf<T> {
value: &'de Dynamic,
) -> Result<T, Box<EvalAltResult>> {
T::deserialize(&mut DynamicDeserializer::from_dynamic(value)) T::deserialize(&mut DynamicDeserializer::from_dynamic(value))
} }
impl Error for Box<EvalAltResult> { impl Error for RhaiError {
fn custom<T: fmt::Display>(err: T) -> Self { fn custom<T: fmt::Display>(err: T) -> Self {
LexError::ImproperSymbol(String::new(), err.to_string()) LexError::ImproperSymbol(String::new(), err.to_string())
.into_err(Position::NONE) .into_err(Position::NONE)
@ -116,9 +114,9 @@ impl Error for Box<EvalAltResult> {
} }
impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
type Error = Box<EvalAltResult>; type Error = RhaiError;
fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
match self.value.0 { match self.value.0 {
Union::Unit(_, _, _) => self.deserialize_unit(visitor), Union::Unit(_, _, _) => self.deserialize_unit(visitor),
Union::Bool(_, _, _) => self.deserialize_bool(visitor), Union::Bool(_, _, _) => self.deserialize_bool(visitor),
@ -172,11 +170,11 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
} }
} }
fn deserialize_bool<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_bool<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
visitor.visit_bool(self.value.as_bool().or_else(|_| self.type_error())?) visitor.visit_bool(self.value.as_bool().or_else(|_| self.type_error())?)
} }
fn deserialize_i8<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_i8<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.value.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else { } else {
@ -186,7 +184,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
} }
} }
fn deserialize_i16<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_i16<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.value.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else { } else {
@ -196,7 +194,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
} }
} }
fn deserialize_i32<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_i32<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.value.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else if cfg!(feature = "only_i32") { } else if cfg!(feature = "only_i32") {
@ -208,7 +206,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
} }
} }
fn deserialize_i64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_i64<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.value.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else if cfg!(not(feature = "only_i32")) { } else if cfg!(not(feature = "only_i32")) {
@ -220,7 +218,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
} }
} }
fn deserialize_i128<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_i128<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.value.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else if cfg!(not(feature = "only_i32")) { } else if cfg!(not(feature = "only_i32")) {
@ -232,7 +230,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
} }
} }
fn deserialize_u8<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_u8<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.value.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else { } else {
@ -242,7 +240,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
} }
} }
fn deserialize_u16<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_u16<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.value.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else { } else {
@ -252,7 +250,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
} }
} }
fn deserialize_u32<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_u32<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.value.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else { } else {
@ -262,7 +260,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
} }
} }
fn deserialize_u64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_u64<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.value.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else { } else {
@ -272,7 +270,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
} }
} }
fn deserialize_u128<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_u128<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.value.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else { } else {
@ -282,7 +280,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
} }
} }
fn deserialize_f32<V: Visitor<'de>>(self, _visitor: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_f32<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
return self return self
.value .value
@ -306,7 +304,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
return self.type_error_str("f32"); return self.type_error_str("f32");
} }
fn deserialize_f64<V: Visitor<'de>>(self, _visitor: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_f64<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
return self return self
.value .value
@ -330,30 +328,24 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
return self.type_error_str("f64"); return self.type_error_str("f64");
} }
fn deserialize_char<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_char<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
self.value self.value
.downcast_ref::<char>() .downcast_ref::<char>()
.map_or_else(|| self.type_error(), |&x| visitor.visit_char(x)) .map_or_else(|| self.type_error(), |&x| visitor.visit_char(x))
} }
fn deserialize_str<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_str<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
self.value.downcast_ref::<ImmutableString>().map_or_else( self.value.downcast_ref::<ImmutableString>().map_or_else(
|| self.type_error(), || self.type_error(),
|x| visitor.visit_borrowed_str(x.as_str()), |x| visitor.visit_borrowed_str(x.as_str()),
) )
} }
fn deserialize_string<V: Visitor<'de>>( fn deserialize_string<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
self,
visitor: V,
) -> Result<V::Value, Box<EvalAltResult>> {
self.deserialize_str(visitor) self.deserialize_str(visitor)
} }
fn deserialize_bytes<V: Visitor<'de>>( fn deserialize_bytes<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> {
self,
_visitor: V,
) -> Result<V::Value, Box<EvalAltResult>> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
return self return self
.value .value
@ -364,17 +356,11 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
return self.type_error(); return self.type_error();
} }
fn deserialize_byte_buf<V: Visitor<'de>>( fn deserialize_byte_buf<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
self,
visitor: V,
) -> Result<V::Value, Box<EvalAltResult>> {
self.deserialize_bytes(visitor) self.deserialize_bytes(visitor)
} }
fn deserialize_option<V: Visitor<'de>>( fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
self,
visitor: V,
) -> Result<V::Value, Box<EvalAltResult>> {
if self.value.is::<()>() { if self.value.is::<()>() {
visitor.visit_none() visitor.visit_none()
} else { } else {
@ -382,7 +368,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
} }
} }
fn deserialize_unit<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_unit<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
self.value self.value
.downcast_ref::<()>() .downcast_ref::<()>()
.map_or_else(|| self.type_error(), |_| visitor.visit_unit()) .map_or_else(|| self.type_error(), |_| visitor.visit_unit())
@ -392,7 +378,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
self, self,
_name: &'static str, _name: &'static str,
visitor: V, visitor: V,
) -> Result<V::Value, Box<EvalAltResult>> { ) -> RhaiResultOf<V::Value> {
self.deserialize_unit(visitor) self.deserialize_unit(visitor)
} }
@ -400,11 +386,11 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
self, self,
_name: &'static str, _name: &'static str,
visitor: V, visitor: V,
) -> Result<V::Value, Box<EvalAltResult>> { ) -> RhaiResultOf<V::Value> {
visitor.visit_newtype_struct(self) visitor.visit_newtype_struct(self)
} }
fn deserialize_seq<V: Visitor<'de>>(self, _visitor: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_seq<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
return self.value.downcast_ref::<crate::Array>().map_or_else( return self.value.downcast_ref::<crate::Array>().map_or_else(
|| self.type_error(), || self.type_error(),
@ -415,11 +401,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
return self.type_error(); return self.type_error();
} }
fn deserialize_tuple<V: Visitor<'de>>( fn deserialize_tuple<V: Visitor<'de>>(self, _len: usize, visitor: V) -> RhaiResultOf<V::Value> {
self,
_len: usize,
visitor: V,
) -> Result<V::Value, Box<EvalAltResult>> {
self.deserialize_seq(visitor) self.deserialize_seq(visitor)
} }
@ -428,11 +410,11 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
_name: &'static str, _name: &'static str,
_len: usize, _len: usize,
visitor: V, visitor: V,
) -> Result<V::Value, Box<EvalAltResult>> { ) -> RhaiResultOf<V::Value> {
self.deserialize_seq(visitor) self.deserialize_seq(visitor)
} }
fn deserialize_map<V: Visitor<'de>>(self, _visitor: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_map<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
return self.value.downcast_ref::<crate::Map>().map_or_else( return self.value.downcast_ref::<crate::Map>().map_or_else(
|| self.type_error(), || self.type_error(),
@ -453,7 +435,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
_name: &'static str, _name: &'static str,
_fields: &'static [&'static str], _fields: &'static [&'static str],
visitor: V, visitor: V,
) -> Result<V::Value, Box<EvalAltResult>> { ) -> RhaiResultOf<V::Value> {
self.deserialize_map(visitor) self.deserialize_map(visitor)
} }
@ -462,7 +444,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
_name: &'static str, _name: &'static str,
_variants: &'static [&'static str], _variants: &'static [&'static str],
visitor: V, visitor: V,
) -> Result<V::Value, Box<EvalAltResult>> { ) -> RhaiResultOf<V::Value> {
if let Some(s) = self.value.read_lock::<ImmutableString>() { if let Some(s) = self.value.read_lock::<ImmutableString>() {
visitor.visit_enum(s.as_str().into_deserializer()) visitor.visit_enum(s.as_str().into_deserializer())
} else { } else {
@ -487,17 +469,11 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
} }
} }
fn deserialize_identifier<V: Visitor<'de>>( fn deserialize_identifier<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
self,
visitor: V,
) -> Result<V::Value, Box<EvalAltResult>> {
self.deserialize_str(visitor) self.deserialize_str(visitor)
} }
fn deserialize_ignored_any<V: Visitor<'de>>( fn deserialize_ignored_any<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
self,
visitor: V,
) -> Result<V::Value, Box<EvalAltResult>> {
self.deserialize_any(visitor) self.deserialize_any(visitor)
} }
} }
@ -521,12 +497,12 @@ impl<'a, ITER: Iterator<Item = &'a Dynamic>> IterateDynamicArray<'a, ITER> {
impl<'a: 'de, 'de, ITER: Iterator<Item = &'a Dynamic>> serde::de::SeqAccess<'de> impl<'a: 'de, 'de, ITER: Iterator<Item = &'a Dynamic>> serde::de::SeqAccess<'de>
for IterateDynamicArray<'a, ITER> for IterateDynamicArray<'a, ITER>
{ {
type Error = Box<EvalAltResult>; type Error = RhaiError;
fn next_element_seed<T: DeserializeSeed<'de>>( fn next_element_seed<T: serde::de::DeserializeSeed<'de>>(
&mut self, &mut self,
seed: T, seed: T,
) -> Result<Option<T::Value>, Box<EvalAltResult>> { ) -> RhaiResultOf<Option<T::Value>> {
// Deserialize each item coming out of the iterator. // Deserialize each item coming out of the iterator.
match self.iter.next() { match self.iter.next() {
None => Ok(None), None => Ok(None),
@ -568,12 +544,12 @@ where
KEYS: Iterator<Item = &'a str>, KEYS: Iterator<Item = &'a str>,
VALUES: Iterator<Item = &'a Dynamic>, VALUES: Iterator<Item = &'a Dynamic>,
{ {
type Error = Box<EvalAltResult>; type Error = RhaiError;
fn next_key_seed<K: DeserializeSeed<'de>>( fn next_key_seed<K: serde::de::DeserializeSeed<'de>>(
&mut self, &mut self,
seed: K, seed: K,
) -> Result<Option<K::Value>, Box<EvalAltResult>> { ) -> RhaiResultOf<Option<K::Value>> {
// Deserialize each `Identifier` key coming out of the keys iterator. // Deserialize each `Identifier` key coming out of the keys iterator.
match self.keys.next() { match self.keys.next() {
None => Ok(None), None => Ok(None),
@ -583,13 +559,13 @@ where
} }
} }
fn next_value_seed<V: DeserializeSeed<'de>>( fn next_value_seed<V: serde::de::DeserializeSeed<'de>>(
&mut self, &mut self,
seed: V, seed: V,
) -> Result<V::Value, Box<EvalAltResult>> { ) -> RhaiResultOf<V::Value> {
// Deserialize each value item coming out of the iterator. // Deserialize each value item coming out of the iterator.
seed.deserialize(&mut DynamicDeserializer::from_dynamic( seed.deserialize(&mut DynamicDeserializer::from_dynamic(
self.values.next().expect("exists"), self.values.next().unwrap(),
)) ))
} }
} }
@ -602,13 +578,13 @@ struct EnumDeserializer<'t, 'de: 't> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
impl<'t, 'de> serde::de::EnumAccess<'de> for EnumDeserializer<'t, 'de> { impl<'t, 'de> serde::de::EnumAccess<'de> for EnumDeserializer<'t, 'de> {
type Error = Box<EvalAltResult>; type Error = RhaiError;
type Variant = Self; type Variant = Self;
fn variant_seed<V: DeserializeSeed<'de>>( fn variant_seed<V: serde::de::DeserializeSeed<'de>>(
self, self,
seed: V, seed: V,
) -> Result<(V::Value, Self::Variant), Self::Error> { ) -> RhaiResultOf<(V::Value, Self::Variant)> {
seed.deserialize(self.tag.into_deserializer()) seed.deserialize(self.tag.into_deserializer())
.map(|v| (v, self)) .map(|v| (v, self))
} }
@ -616,24 +592,20 @@ impl<'t, 'de> serde::de::EnumAccess<'de> for EnumDeserializer<'t, 'de> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
impl<'t, 'de> serde::de::VariantAccess<'de> for EnumDeserializer<'t, 'de> { impl<'t, 'de> serde::de::VariantAccess<'de> for EnumDeserializer<'t, 'de> {
type Error = Box<EvalAltResult>; type Error = RhaiError;
fn unit_variant(mut self) -> Result<(), Self::Error> { fn unit_variant(mut self) -> RhaiResultOf<()> {
Deserialize::deserialize(&mut self.content) Deserialize::deserialize(&mut self.content)
} }
fn newtype_variant_seed<T: DeserializeSeed<'de>>( fn newtype_variant_seed<T: serde::de::DeserializeSeed<'de>>(
mut self, mut self,
seed: T, seed: T,
) -> Result<T::Value, Self::Error> { ) -> RhaiResultOf<T::Value> {
seed.deserialize(&mut self.content) seed.deserialize(&mut self.content)
} }
fn tuple_variant<V: Visitor<'de>>( fn tuple_variant<V: Visitor<'de>>(mut self, len: usize, visitor: V) -> RhaiResultOf<V::Value> {
mut self,
len: usize,
visitor: V,
) -> Result<V::Value, Self::Error> {
self.content.deserialize_tuple(len, visitor) self.content.deserialize_tuple(len, visitor)
} }
@ -641,7 +613,7 @@ impl<'t, 'de> serde::de::VariantAccess<'de> for EnumDeserializer<'t, 'de> {
mut self, mut self,
fields: &'static [&'static str], fields: &'static [&'static str],
visitor: V, visitor: V,
) -> Result<V::Value, Self::Error> { ) -> RhaiResultOf<V::Value> {
self.content.deserialize_struct("", fields, visitor) self.content.deserialize_struct("", fields, visitor)
} }
} }

View File

@ -1,3 +1,5 @@
//! Serialization of functions metadata.
#![cfg(feature = "metadata")] #![cfg(feature = "metadata")]
use crate::module::calc_native_fn_hash; use crate::module::calc_native_fn_hash;
@ -48,33 +50,24 @@ impl From<crate::FnAccess> for FnAccess {
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
struct FnParam { struct FnParam<'a> {
pub name: Box<str>,
#[serde(rename = "type", skip_serializing_if = "Option::is_none")] #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
pub typ: Option<Box<str>>, pub name: Option<&'a str>,
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
pub typ: Option<&'a str>,
} }
impl PartialOrd for FnParam { impl PartialOrd for FnParam<'_> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(match self.name.partial_cmp(&other.name).expect("succeed") { Some(match self.name.partial_cmp(&other.name).expect("succeed") {
Ordering::Less => Ordering::Less, Ordering::Less => Ordering::Less,
Ordering::Greater => Ordering::Greater, Ordering::Greater => Ordering::Greater,
Ordering::Equal => match (self.typ.is_none(), other.typ.is_none()) { Ordering::Equal => self.typ.partial_cmp(&other.typ).expect("succeed"),
(true, true) => Ordering::Equal,
(true, false) => Ordering::Greater,
(false, true) => Ordering::Less,
(false, false) => self
.typ
.as_ref()
.expect("`Some`")
.partial_cmp(other.typ.as_ref().expect("`Some`"))
.expect("succeed"),
},
}) })
} }
} }
impl Ord for FnParam { impl Ord for FnParam<'_> {
fn cmp(&self, other: &Self) -> Ordering { fn cmp(&self, other: &Self) -> Ordering {
match self.name.cmp(&other.name) { match self.name.cmp(&other.name) {
Ordering::Equal => self.typ.cmp(&other.typ), Ordering::Equal => self.typ.cmp(&other.typ),
@ -85,7 +78,7 @@ impl Ord for FnParam {
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
struct FnMetadata { struct FnMetadata<'a> {
pub base_hash: u64, pub base_hash: u64,
pub full_hash: u64, pub full_hash: u64,
pub namespace: FnNamespace, pub namespace: FnNamespace,
@ -95,21 +88,21 @@ struct FnMetadata {
pub typ: FnType, pub typ: FnType,
pub num_params: usize, pub num_params: usize,
#[serde(default, skip_serializing_if = "Vec::is_empty")] #[serde(default, skip_serializing_if = "Vec::is_empty")]
pub params: Vec<FnParam>, pub params: Vec<FnParam<'a>>,
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
pub return_type: Option<Box<str>>, pub return_type_name: Option<&'a str>,
pub signature: String, pub signature: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")] #[serde(default, skip_serializing_if = "Vec::is_empty")]
pub doc_comments: Vec<Box<str>>, pub doc_comments: Vec<&'a str>,
} }
impl PartialOrd for FnMetadata { impl PartialOrd for FnMetadata<'_> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other)) Some(self.cmp(other))
} }
} }
impl Ord for FnMetadata { impl Ord for FnMetadata<'_> {
fn cmp(&self, other: &Self) -> Ordering { fn cmp(&self, other: &Self) -> Ordering {
match self.name.cmp(&other.name) { match self.name.cmp(&other.name) {
Ordering::Equal => match self.num_params.cmp(&other.num_params) { Ordering::Equal => match self.num_params.cmp(&other.num_params) {
@ -121,8 +114,8 @@ impl Ord for FnMetadata {
} }
} }
impl From<&crate::module::FuncInfo> for FnMetadata { impl<'a> From<&'a crate::module::FuncInfo> for FnMetadata<'a> {
fn from(info: &crate::module::FuncInfo) -> Self { fn from(info: &'a crate::module::FuncInfo) -> Self {
let base_hash = calc_fn_hash(&info.name, info.params); let base_hash = calc_fn_hash(&info.name, info.params);
let (typ, full_hash) = if info.func.is_script() { let (typ, full_hash) = if info.func.is_script() {
(FnType::Script, base_hash) (FnType::Script, base_hash)
@ -142,41 +135,38 @@ impl From<&crate::module::FuncInfo> for FnMetadata {
typ, typ,
num_params: info.params, num_params: info.params,
params: info params: info
.param_names .param_names_and_types
.iter() .iter()
.take(info.params)
.map(|s| { .map(|s| {
let mut seg = s.splitn(2, ':'); let mut seg = s.splitn(2, ':');
let name = seg let name = match seg.next().map(&str::trim).unwrap_or("_") {
.next() "_" => None,
.map(|s| s.trim().into()) s => Some(s),
.unwrap_or_else(|| "_".into()); };
let typ = seg.next().map(|s| s.trim().into()); let typ = seg.next().map(&str::trim);
FnParam { name, typ } FnParam { name, typ }
}) })
.collect(), .collect(),
return_type: info return_type_name: match info.return_type_name.as_str() {
.param_names "" | "()" => None,
.last() ty => Some(ty),
.map(|s| s.as_str().into()) },
.or_else(|| Some("()".into())),
signature: info.gen_signature(), signature: info.gen_signature(),
doc_comments: if info.func.is_script() { doc_comments: if info.func.is_script() {
#[cfg(feature = "no_function")] #[cfg(feature = "no_function")]
{ unreachable!("script-defined functions should not exist under no_function");
unreachable!("scripted functions should not exist under no_function")
}
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
{ info.func
info.func .get_script_fn_def()
.get_script_fn_def() .expect("script-defined function")
.expect("scripted function") .comments
.comments .as_ref()
.as_ref() .map_or_else(|| Vec::new(), |v| v.iter().map(|s| &**s).collect())
.map_or_else(|| Vec::new(), |v| v.to_vec())
}
} else { } else {
Vec::new() info.comments
.as_ref()
.map_or_else(|| Vec::new(), |v| v.iter().map(|s| &**s).collect())
}, },
} }
} }
@ -184,14 +174,14 @@ impl From<&crate::module::FuncInfo> for FnMetadata {
#[derive(Debug, Clone, Serialize)] #[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
struct ModuleMetadata { struct ModuleMetadata<'a> {
#[serde(skip_serializing_if = "BTreeMap::is_empty")] #[serde(skip_serializing_if = "BTreeMap::is_empty")]
pub modules: BTreeMap<String, Self>, pub modules: BTreeMap<&'a str, Self>,
#[serde(skip_serializing_if = "Vec::is_empty")] #[serde(skip_serializing_if = "Vec::is_empty")]
pub functions: Vec<FnMetadata>, pub functions: Vec<FnMetadata<'a>>,
} }
impl ModuleMetadata { impl ModuleMetadata<'_> {
#[inline(always)] #[inline(always)]
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
@ -201,22 +191,21 @@ impl ModuleMetadata {
} }
} }
impl From<&crate::Module> for ModuleMetadata { impl<'a> From<&'a crate::Module> for ModuleMetadata<'a> {
fn from(module: &crate::Module) -> Self { fn from(module: &'a crate::Module) -> Self {
let mut functions: Vec<_> = module.iter_fn().map(|f| f.into()).collect(); let mut functions: Vec<_> = module.iter_fn().map(|f| f.into()).collect();
functions.sort(); functions.sort();
Self { Self {
modules: module modules: module
.iter_sub_modules() .iter_sub_modules()
.map(|(name, m)| (name.to_string(), m.as_ref().into())) .map(|(name, m)| (name, m.as_ref().into()))
.collect(), .collect(),
functions, functions,
} }
} }
} }
#[cfg(feature = "metadata")]
impl Engine { impl Engine {
/// _(metadata)_ Generate a list of all functions (including those defined in an /// _(metadata)_ Generate a list of all functions (including those defined in an
/// [`AST`][crate::AST]) in JSON format. /// [`AST`][crate::AST]) in JSON format.
@ -235,20 +224,14 @@ impl Engine {
let _ast = ast; let _ast = ast;
let mut global = ModuleMetadata::new(); let mut global = ModuleMetadata::new();
if include_global {
self.global_modules
.iter()
.take(self.global_modules.len() - 1)
.flat_map(|m| m.iter_fn())
.for_each(|f| global.functions.push(f.into()));
}
self.global_sub_modules.iter().for_each(|(name, m)| { self.global_sub_modules.iter().for_each(|(name, m)| {
global.modules.insert(name.to_string(), m.as_ref().into()); global.modules.insert(name, m.as_ref().into());
}); });
self.global_namespace() self.global_modules
.iter_fn() .iter()
.take(if include_global { usize::MAX } else { 1 })
.flat_map(|m| m.iter_fn())
.for_each(|f| global.functions.push(f.into())); .for_each(|f| global.functions.push(f.into()));
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]

View File

@ -1,6 +1,6 @@
//! Implement serialization support of [`Dynamic`][crate::Dynamic] for [`serde`]. //! Implement serialization support of [`Dynamic`][crate::Dynamic] for [`serde`].
use crate::{Dynamic, EvalAltResult, Position, RhaiResult}; use crate::{Dynamic, Position, RhaiError, RhaiResult, RhaiResultOf, ERR};
use serde::ser::{ use serde::ser::{
Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple, SerializeTupleStruct, Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple, SerializeTupleStruct,
}; };
@ -81,55 +81,56 @@ pub fn to_dynamic<T: Serialize>(value: T) -> RhaiResult {
value.serialize(&mut s) value.serialize(&mut s)
} }
impl Error for Box<EvalAltResult> { impl Error for RhaiError {
fn custom<T: fmt::Display>(err: T) -> Self { fn custom<T: fmt::Display>(err: T) -> Self {
EvalAltResult::ErrorRuntime(err.to_string().into(), Position::NONE).into() ERR::ErrorRuntime(err.to_string().into(), Position::NONE).into()
} }
} }
impl Serializer for &mut DynamicSerializer { impl Serializer for &mut DynamicSerializer {
type Ok = Dynamic; type Ok = Dynamic;
type Error = Box<EvalAltResult>; type Error = RhaiError;
type SerializeSeq = DynamicSerializer; type SerializeSeq = DynamicSerializer;
type SerializeTuple = DynamicSerializer; type SerializeTuple = DynamicSerializer;
type SerializeTupleStruct = DynamicSerializer; type SerializeTupleStruct = DynamicSerializer;
#[cfg(not(any(feature = "no_object", feature = "no_index")))] #[cfg(not(feature = "no_object"))]
#[cfg(not(feature = "no_index"))]
type SerializeTupleVariant = TupleVariantSerializer; type SerializeTupleVariant = TupleVariantSerializer;
#[cfg(any(feature = "no_object", feature = "no_index"))] #[cfg(any(feature = "no_object", feature = "no_index"))]
type SerializeTupleVariant = serde::ser::Impossible<Dynamic, Box<EvalAltResult>>; type SerializeTupleVariant = serde::ser::Impossible<Dynamic, RhaiError>;
type SerializeMap = DynamicSerializer; type SerializeMap = DynamicSerializer;
type SerializeStruct = DynamicSerializer; type SerializeStruct = DynamicSerializer;
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
type SerializeStructVariant = StructVariantSerializer; type SerializeStructVariant = StructVariantSerializer;
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
type SerializeStructVariant = serde::ser::Impossible<Dynamic, Box<EvalAltResult>>; type SerializeStructVariant = serde::ser::Impossible<Dynamic, RhaiError>;
fn serialize_bool(self, v: bool) -> Result<Self::Ok, Box<EvalAltResult>> { fn serialize_bool(self, v: bool) -> RhaiResultOf<Self::Ok> {
Ok(v.into()) Ok(v.into())
} }
fn serialize_i8(self, v: i8) -> Result<Self::Ok, Box<EvalAltResult>> { fn serialize_i8(self, v: i8) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
return self.serialize_i64(i64::from(v)); return self.serialize_i64(i64::from(v));
#[cfg(feature = "only_i32")] #[cfg(feature = "only_i32")]
return self.serialize_i32(i32::from(v)); return self.serialize_i32(i32::from(v));
} }
fn serialize_i16(self, v: i16) -> Result<Self::Ok, Box<EvalAltResult>> { fn serialize_i16(self, v: i16) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
return self.serialize_i64(i64::from(v)); return self.serialize_i64(i64::from(v));
#[cfg(feature = "only_i32")] #[cfg(feature = "only_i32")]
return self.serialize_i32(i32::from(v)); return self.serialize_i32(i32::from(v));
} }
fn serialize_i32(self, v: i32) -> Result<Self::Ok, Box<EvalAltResult>> { fn serialize_i32(self, v: i32) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
return self.serialize_i64(i64::from(v)); return self.serialize_i64(i64::from(v));
#[cfg(feature = "only_i32")] #[cfg(feature = "only_i32")]
return Ok(v.into()); return Ok(v.into());
} }
fn serialize_i64(self, v: i64) -> Result<Self::Ok, Box<EvalAltResult>> { fn serialize_i64(self, v: i64) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
{ {
Ok(v.into()) Ok(v.into())
@ -142,7 +143,7 @@ impl Serializer for &mut DynamicSerializer {
} }
} }
fn serialize_i128(self, v: i128) -> Result<Self::Ok, Box<EvalAltResult>> { fn serialize_i128(self, v: i128) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
if v > i64::MAX as i128 { if v > i64::MAX as i128 {
Ok(Dynamic::from(v)) Ok(Dynamic::from(v))
@ -157,21 +158,21 @@ impl Serializer for &mut DynamicSerializer {
} }
} }
fn serialize_u8(self, v: u8) -> Result<Self::Ok, Box<EvalAltResult>> { fn serialize_u8(self, v: u8) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
return self.serialize_i64(i64::from(v)); return self.serialize_i64(i64::from(v));
#[cfg(feature = "only_i32")] #[cfg(feature = "only_i32")]
return self.serialize_i32(i32::from(v)); return self.serialize_i32(i32::from(v));
} }
fn serialize_u16(self, v: u16) -> Result<Self::Ok, Box<EvalAltResult>> { fn serialize_u16(self, v: u16) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
return self.serialize_i64(i64::from(v)); return self.serialize_i64(i64::from(v));
#[cfg(feature = "only_i32")] #[cfg(feature = "only_i32")]
return self.serialize_i32(i32::from(v)); return self.serialize_i32(i32::from(v));
} }
fn serialize_u32(self, v: u32) -> Result<Self::Ok, Box<EvalAltResult>> { fn serialize_u32(self, v: u32) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
{ {
self.serialize_i64(i64::from(v)) self.serialize_i64(i64::from(v))
@ -184,7 +185,7 @@ impl Serializer for &mut DynamicSerializer {
} }
} }
fn serialize_u64(self, v: u64) -> Result<Self::Ok, Box<EvalAltResult>> { fn serialize_u64(self, v: u64) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
if v > i64::MAX as u64 { if v > i64::MAX as u64 {
Ok(Dynamic::from(v)) Ok(Dynamic::from(v))
@ -199,7 +200,7 @@ impl Serializer for &mut DynamicSerializer {
} }
} }
fn serialize_u128(self, v: u128) -> Result<Self::Ok, Box<EvalAltResult>> { fn serialize_u128(self, v: u128) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
if v > i64::MAX as u128 { if v > i64::MAX as u128 {
Ok(Dynamic::from(v)) Ok(Dynamic::from(v))
@ -214,7 +215,7 @@ impl Serializer for &mut DynamicSerializer {
} }
} }
fn serialize_f32(self, v: f32) -> Result<Self::Ok, Box<EvalAltResult>> { fn serialize_f32(self, v: f32) -> RhaiResultOf<Self::Ok> {
#[cfg(any(not(feature = "no_float"), not(feature = "decimal")))] #[cfg(any(not(feature = "no_float"), not(feature = "decimal")))]
return Ok(Dynamic::from(v)); return Ok(Dynamic::from(v));
@ -230,7 +231,7 @@ impl Serializer for &mut DynamicSerializer {
} }
} }
fn serialize_f64(self, v: f64) -> Result<Self::Ok, Box<EvalAltResult>> { fn serialize_f64(self, v: f64) -> RhaiResultOf<Self::Ok> {
#[cfg(any(not(feature = "no_float"), not(feature = "decimal")))] #[cfg(any(not(feature = "no_float"), not(feature = "decimal")))]
return Ok(Dynamic::from(v)); return Ok(Dynamic::from(v));
@ -246,20 +247,20 @@ impl Serializer for &mut DynamicSerializer {
} }
} }
fn serialize_char(self, v: char) -> Result<Self::Ok, Box<EvalAltResult>> { fn serialize_char(self, v: char) -> RhaiResultOf<Self::Ok> {
Ok(v.into()) Ok(v.into())
} }
fn serialize_str(self, v: &str) -> Result<Self::Ok, Box<EvalAltResult>> { fn serialize_str(self, v: &str) -> RhaiResultOf<Self::Ok> {
Ok(v.into()) Ok(v.into())
} }
fn serialize_bytes(self, _v: &[u8]) -> Result<Self::Ok, Box<EvalAltResult>> { fn serialize_bytes(self, _v: &[u8]) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
return Ok(Dynamic::from_blob(_v.to_vec())); return Ok(Dynamic::from_blob(_v.to_vec()));
#[cfg(feature = "no_index")] #[cfg(feature = "no_index")]
return Err(EvalAltResult::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"BLOB's are not supported with 'no_index'".into(), "BLOB's are not supported with 'no_index'".into(),
Position::NONE, Position::NONE,
@ -267,22 +268,19 @@ impl Serializer for &mut DynamicSerializer {
.into()); .into());
} }
fn serialize_none(self) -> Result<Self::Ok, Box<EvalAltResult>> { fn serialize_none(self) -> RhaiResultOf<Self::Ok> {
Ok(Dynamic::UNIT) Ok(Dynamic::UNIT)
} }
fn serialize_some<T: ?Sized + Serialize>( fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> RhaiResultOf<Self::Ok> {
self,
value: &T,
) -> Result<Self::Ok, Box<EvalAltResult>> {
value.serialize(&mut *self) value.serialize(&mut *self)
} }
fn serialize_unit(self) -> Result<Self::Ok, Box<EvalAltResult>> { fn serialize_unit(self) -> RhaiResultOf<Self::Ok> {
Ok(Dynamic::UNIT) Ok(Dynamic::UNIT)
} }
fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Box<EvalAltResult>> { fn serialize_unit_struct(self, _name: &'static str) -> RhaiResultOf<Self::Ok> {
self.serialize_unit() self.serialize_unit()
} }
@ -291,7 +289,7 @@ impl Serializer for &mut DynamicSerializer {
_name: &'static str, _name: &'static str,
_variant_index: u32, _variant_index: u32,
variant: &'static str, variant: &'static str,
) -> Result<Self::Ok, Box<EvalAltResult>> { ) -> RhaiResultOf<Self::Ok> {
self.serialize_str(variant) self.serialize_str(variant)
} }
@ -299,7 +297,7 @@ impl Serializer for &mut DynamicSerializer {
self, self,
_name: &'static str, _name: &'static str,
value: &T, value: &T,
) -> Result<Self::Ok, Box<EvalAltResult>> { ) -> RhaiResultOf<Self::Ok> {
value.serialize(&mut *self) value.serialize(&mut *self)
} }
@ -309,14 +307,14 @@ impl Serializer for &mut DynamicSerializer {
_variant_index: u32, _variant_index: u32,
_variant: &'static str, _variant: &'static str,
_value: &T, _value: &T,
) -> Result<Self::Ok, Box<EvalAltResult>> { ) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
{ {
let content = to_dynamic(_value)?; let content = to_dynamic(_value)?;
make_variant(_variant, content) make_variant(_variant, content)
} }
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
return Err(EvalAltResult::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"object maps are not supported with 'no_object'".into(), "object maps are not supported with 'no_object'".into(),
Position::NONE, Position::NONE,
@ -324,11 +322,11 @@ impl Serializer for &mut DynamicSerializer {
.into()); .into());
} }
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Box<EvalAltResult>> { fn serialize_seq(self, _len: Option<usize>) -> RhaiResultOf<Self::SerializeSeq> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
return Ok(DynamicSerializer::new(crate::Array::new().into())); return Ok(DynamicSerializer::new(crate::Array::new().into()));
#[cfg(feature = "no_index")] #[cfg(feature = "no_index")]
return Err(EvalAltResult::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"arrays are not supported with 'no_index'".into(), "arrays are not supported with 'no_index'".into(),
Position::NONE, Position::NONE,
@ -336,7 +334,7 @@ impl Serializer for &mut DynamicSerializer {
.into()); .into());
} }
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Box<EvalAltResult>> { fn serialize_tuple(self, len: usize) -> RhaiResultOf<Self::SerializeTuple> {
self.serialize_seq(Some(len)) self.serialize_seq(Some(len))
} }
@ -344,7 +342,7 @@ impl Serializer for &mut DynamicSerializer {
self, self,
_name: &'static str, _name: &'static str,
len: usize, len: usize,
) -> Result<Self::SerializeTupleStruct, Box<EvalAltResult>> { ) -> RhaiResultOf<Self::SerializeTupleStruct> {
self.serialize_seq(Some(len)) self.serialize_seq(Some(len))
} }
@ -354,7 +352,7 @@ impl Serializer for &mut DynamicSerializer {
_variant_index: u32, _variant_index: u32,
_variant: &'static str, _variant: &'static str,
_len: usize, _len: usize,
) -> Result<Self::SerializeTupleVariant, Box<EvalAltResult>> { ) -> RhaiResultOf<Self::SerializeTupleVariant> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
return Ok(TupleVariantSerializer { return Ok(TupleVariantSerializer {
@ -362,7 +360,7 @@ impl Serializer for &mut DynamicSerializer {
array: crate::Array::with_capacity(_len), array: crate::Array::with_capacity(_len),
}); });
#[cfg(any(feature = "no_object", feature = "no_index"))] #[cfg(any(feature = "no_object", feature = "no_index"))]
return Err(EvalAltResult::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"tuples are not supported with 'no_index' or 'no_object'".into(), "tuples are not supported with 'no_index' or 'no_object'".into(),
Position::NONE, Position::NONE,
@ -370,11 +368,11 @@ impl Serializer for &mut DynamicSerializer {
.into()); .into());
} }
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Box<EvalAltResult>> { fn serialize_map(self, _len: Option<usize>) -> RhaiResultOf<Self::SerializeMap> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
return Ok(DynamicSerializer::new(crate::Map::new().into())); return Ok(DynamicSerializer::new(crate::Map::new().into()));
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
return Err(EvalAltResult::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"object maps are not supported with 'no_object'".into(), "object maps are not supported with 'no_object'".into(),
Position::NONE, Position::NONE,
@ -386,7 +384,7 @@ impl Serializer for &mut DynamicSerializer {
self, self,
_name: &'static str, _name: &'static str,
len: usize, len: usize,
) -> Result<Self::SerializeStruct, Box<EvalAltResult>> { ) -> RhaiResultOf<Self::SerializeStruct> {
self.serialize_map(Some(len)) self.serialize_map(Some(len))
} }
@ -396,14 +394,14 @@ impl Serializer for &mut DynamicSerializer {
_variant_index: u32, _variant_index: u32,
_variant: &'static str, _variant: &'static str,
_len: usize, _len: usize,
) -> Result<Self::SerializeStructVariant, Box<EvalAltResult>> { ) -> RhaiResultOf<Self::SerializeStructVariant> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
return Ok(StructVariantSerializer { return Ok(StructVariantSerializer {
variant: _variant, variant: _variant,
map: crate::Map::new(), map: crate::Map::new(),
}); });
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
return Err(EvalAltResult::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"object maps are not supported with 'no_object'".into(), "object maps are not supported with 'no_object'".into(),
Position::NONE, Position::NONE,
@ -414,12 +412,9 @@ impl Serializer for &mut DynamicSerializer {
impl SerializeSeq for DynamicSerializer { impl SerializeSeq for DynamicSerializer {
type Ok = Dynamic; type Ok = Dynamic;
type Error = Box<EvalAltResult>; type Error = RhaiError;
fn serialize_element<T: ?Sized + Serialize>( fn serialize_element<T: ?Sized + Serialize>(&mut self, _value: &T) -> RhaiResultOf<()> {
&mut self,
_value: &T,
) -> Result<(), Box<EvalAltResult>> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
{ {
let _value = _value.serialize(&mut *self)?; let _value = _value.serialize(&mut *self)?;
@ -428,7 +423,7 @@ impl SerializeSeq for DynamicSerializer {
Ok(()) Ok(())
} }
#[cfg(feature = "no_index")] #[cfg(feature = "no_index")]
return Err(EvalAltResult::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"arrays are not supported with 'no_index'".into(), "arrays are not supported with 'no_index'".into(),
Position::NONE, Position::NONE,
@ -437,11 +432,11 @@ impl SerializeSeq for DynamicSerializer {
} }
// Close the sequence. // Close the sequence.
fn end(self) -> Result<Self::Ok, Box<EvalAltResult>> { fn end(self) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
return Ok(self._value); return Ok(self._value);
#[cfg(feature = "no_index")] #[cfg(feature = "no_index")]
return Err(EvalAltResult::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"arrays are not supported with 'no_index'".into(), "arrays are not supported with 'no_index'".into(),
Position::NONE, Position::NONE,
@ -452,12 +447,9 @@ impl SerializeSeq for DynamicSerializer {
impl SerializeTuple for DynamicSerializer { impl SerializeTuple for DynamicSerializer {
type Ok = Dynamic; type Ok = Dynamic;
type Error = Box<EvalAltResult>; type Error = RhaiError;
fn serialize_element<T: ?Sized + Serialize>( fn serialize_element<T: ?Sized + Serialize>(&mut self, _value: &T) -> RhaiResultOf<()> {
&mut self,
_value: &T,
) -> Result<(), Box<EvalAltResult>> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
{ {
let _value = _value.serialize(&mut *self)?; let _value = _value.serialize(&mut *self)?;
@ -466,7 +458,7 @@ impl SerializeTuple for DynamicSerializer {
Ok(()) Ok(())
} }
#[cfg(feature = "no_index")] #[cfg(feature = "no_index")]
return Err(EvalAltResult::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"tuples are not supported with 'no_index'".into(), "tuples are not supported with 'no_index'".into(),
Position::NONE, Position::NONE,
@ -474,11 +466,11 @@ impl SerializeTuple for DynamicSerializer {
.into()); .into());
} }
fn end(self) -> Result<Self::Ok, Box<EvalAltResult>> { fn end(self) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
return Ok(self._value); return Ok(self._value);
#[cfg(feature = "no_index")] #[cfg(feature = "no_index")]
return Err(EvalAltResult::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"tuples are not supported with 'no_index'".into(), "tuples are not supported with 'no_index'".into(),
Position::NONE, Position::NONE,
@ -489,12 +481,9 @@ impl SerializeTuple for DynamicSerializer {
impl SerializeTupleStruct for DynamicSerializer { impl SerializeTupleStruct for DynamicSerializer {
type Ok = Dynamic; type Ok = Dynamic;
type Error = Box<EvalAltResult>; type Error = RhaiError;
fn serialize_field<T: ?Sized + Serialize>( fn serialize_field<T: ?Sized + Serialize>(&mut self, _value: &T) -> RhaiResultOf<()> {
&mut self,
_value: &T,
) -> Result<(), Box<EvalAltResult>> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
{ {
let _value = _value.serialize(&mut *self)?; let _value = _value.serialize(&mut *self)?;
@ -503,7 +492,7 @@ impl SerializeTupleStruct for DynamicSerializer {
Ok(()) Ok(())
} }
#[cfg(feature = "no_index")] #[cfg(feature = "no_index")]
return Err(EvalAltResult::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"tuples are not supported with 'no_index'".into(), "tuples are not supported with 'no_index'".into(),
Position::NONE, Position::NONE,
@ -511,11 +500,11 @@ impl SerializeTupleStruct for DynamicSerializer {
.into()); .into());
} }
fn end(self) -> Result<Self::Ok, Box<EvalAltResult>> { fn end(self) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
return Ok(self._value); return Ok(self._value);
#[cfg(feature = "no_index")] #[cfg(feature = "no_index")]
return Err(EvalAltResult::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"tuples are not supported with 'no_index'".into(), "tuples are not supported with 'no_index'".into(),
Position::NONE, Position::NONE,
@ -526,16 +515,16 @@ impl SerializeTupleStruct for DynamicSerializer {
impl SerializeMap for DynamicSerializer { impl SerializeMap for DynamicSerializer {
type Ok = Dynamic; type Ok = Dynamic;
type Error = Box<EvalAltResult>; type Error = RhaiError;
fn serialize_key<T: ?Sized + Serialize>(&mut self, _key: &T) -> Result<(), Box<EvalAltResult>> { fn serialize_key<T: ?Sized + Serialize>(&mut self, _key: &T) -> RhaiResultOf<()> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
{ {
self._key = _key.serialize(&mut *self)?; self._key = _key.serialize(&mut *self)?;
Ok(()) Ok(())
} }
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
return Err(EvalAltResult::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"object maps are not supported with 'no_object'".into(), "object maps are not supported with 'no_object'".into(),
Position::NONE, Position::NONE,
@ -543,20 +532,13 @@ impl SerializeMap for DynamicSerializer {
.into()); .into());
} }
fn serialize_value<T: ?Sized + Serialize>( fn serialize_value<T: ?Sized + Serialize>(&mut self, _value: &T) -> RhaiResultOf<()> {
&mut self,
_value: &T,
) -> Result<(), Box<EvalAltResult>> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
{ {
let key = std::mem::take(&mut self._key) let key = std::mem::take(&mut self._key)
.into_immutable_string() .into_immutable_string()
.map_err(|typ| { .map_err(|typ| {
EvalAltResult::ErrorMismatchDataType( ERR::ErrorMismatchDataType("string".into(), typ.into(), Position::NONE)
"string".into(),
typ.into(),
Position::NONE,
)
})?; })?;
let _value = _value.serialize(&mut *self)?; let _value = _value.serialize(&mut *self)?;
let map = self._value.downcast_mut::<crate::Map>().unwrap(); let map = self._value.downcast_mut::<crate::Map>().unwrap();
@ -564,7 +546,7 @@ impl SerializeMap for DynamicSerializer {
Ok(()) Ok(())
} }
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
return Err(EvalAltResult::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"object maps are not supported with 'no_object'".into(), "object maps are not supported with 'no_object'".into(),
Position::NONE, Position::NONE,
@ -576,12 +558,12 @@ impl SerializeMap for DynamicSerializer {
&mut self, &mut self,
_key: &K, _key: &K,
_value: &T, _value: &T,
) -> Result<(), Box<EvalAltResult>> { ) -> RhaiResultOf<()> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
{ {
let _key: Dynamic = _key.serialize(&mut *self)?; let _key: Dynamic = _key.serialize(&mut *self)?;
let _key = _key.into_immutable_string().map_err(|typ| { let _key = _key.into_immutable_string().map_err(|typ| {
EvalAltResult::ErrorMismatchDataType("string".into(), typ.into(), Position::NONE) ERR::ErrorMismatchDataType("string".into(), typ.into(), Position::NONE)
})?; })?;
let _value = _value.serialize(&mut *self)?; let _value = _value.serialize(&mut *self)?;
let map = self._value.downcast_mut::<crate::Map>().unwrap(); let map = self._value.downcast_mut::<crate::Map>().unwrap();
@ -589,7 +571,7 @@ impl SerializeMap for DynamicSerializer {
Ok(()) Ok(())
} }
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
return Err(EvalAltResult::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"object maps are not supported with 'no_object'".into(), "object maps are not supported with 'no_object'".into(),
Position::NONE, Position::NONE,
@ -597,11 +579,11 @@ impl SerializeMap for DynamicSerializer {
.into()); .into());
} }
fn end(self) -> Result<Self::Ok, Box<EvalAltResult>> { fn end(self) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
return Ok(self._value); return Ok(self._value);
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
return Err(EvalAltResult::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"object maps are not supported with 'no_object'".into(), "object maps are not supported with 'no_object'".into(),
Position::NONE, Position::NONE,
@ -612,13 +594,13 @@ impl SerializeMap for DynamicSerializer {
impl SerializeStruct for DynamicSerializer { impl SerializeStruct for DynamicSerializer {
type Ok = Dynamic; type Ok = Dynamic;
type Error = Box<EvalAltResult>; type Error = RhaiError;
fn serialize_field<T: ?Sized + Serialize>( fn serialize_field<T: ?Sized + Serialize>(
&mut self, &mut self,
_key: &'static str, _key: &'static str,
_value: &T, _value: &T,
) -> Result<(), Box<EvalAltResult>> { ) -> RhaiResultOf<()> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
{ {
let _value = _value.serialize(&mut *self)?; let _value = _value.serialize(&mut *self)?;
@ -627,7 +609,7 @@ impl SerializeStruct for DynamicSerializer {
Ok(()) Ok(())
} }
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
return Err(EvalAltResult::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"object maps are not supported with 'no_object'".into(), "object maps are not supported with 'no_object'".into(),
Position::NONE, Position::NONE,
@ -635,11 +617,11 @@ impl SerializeStruct for DynamicSerializer {
.into()); .into());
} }
fn end(self) -> Result<Self::Ok, Box<EvalAltResult>> { fn end(self) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
return Ok(self._value); return Ok(self._value);
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
return Err(EvalAltResult::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"object maps are not supported with 'no_object'".into(), "object maps are not supported with 'no_object'".into(),
Position::NONE, Position::NONE,
@ -648,27 +630,26 @@ impl SerializeStruct for DynamicSerializer {
} }
} }
#[cfg(not(any(feature = "no_object", feature = "no_index")))] #[cfg(not(feature = "no_object"))]
#[cfg(not(feature = "no_index"))]
struct TupleVariantSerializer { struct TupleVariantSerializer {
variant: &'static str, variant: &'static str,
array: crate::Array, array: crate::Array,
} }
#[cfg(not(any(feature = "no_object", feature = "no_index")))] #[cfg(not(feature = "no_object"))]
#[cfg(not(feature = "no_index"))]
impl serde::ser::SerializeTupleVariant for TupleVariantSerializer { impl serde::ser::SerializeTupleVariant for TupleVariantSerializer {
type Ok = Dynamic; type Ok = Dynamic;
type Error = Box<EvalAltResult>; type Error = RhaiError;
fn serialize_field<T: ?Sized + Serialize>( fn serialize_field<T: ?Sized + Serialize>(&mut self, value: &T) -> RhaiResultOf<()> {
&mut self,
value: &T,
) -> Result<(), Box<EvalAltResult>> {
let value = to_dynamic(value)?; let value = to_dynamic(value)?;
self.array.push(value); self.array.push(value);
Ok(()) Ok(())
} }
fn end(self) -> Result<Self::Ok, Box<EvalAltResult>> { fn end(self) -> RhaiResultOf<Self::Ok> {
make_variant(self.variant, self.array.into()) make_variant(self.variant, self.array.into())
} }
} }
@ -682,19 +663,19 @@ struct StructVariantSerializer {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
impl serde::ser::SerializeStructVariant for StructVariantSerializer { impl serde::ser::SerializeStructVariant for StructVariantSerializer {
type Ok = Dynamic; type Ok = Dynamic;
type Error = Box<EvalAltResult>; type Error = RhaiError;
fn serialize_field<T: ?Sized + Serialize>( fn serialize_field<T: ?Sized + Serialize>(
&mut self, &mut self,
key: &'static str, key: &'static str,
value: &T, value: &T,
) -> Result<(), Box<EvalAltResult>> { ) -> RhaiResultOf<()> {
let value = to_dynamic(value)?; let value = to_dynamic(value)?;
self.map.insert(key.into(), value); self.map.insert(key.into(), value);
Ok(()) Ok(())
} }
fn end(self) -> Result<Self::Ok, Box<EvalAltResult>> { fn end(self) -> RhaiResultOf<Self::Ok> {
make_variant(self.variant, self.map.into()) make_variant(self.variant, self.map.into())
} }
} }

View File

@ -1,6 +1,6 @@
//! Implement deserialization support of [`ImmutableString`][crate::ImmutableString] for [`serde`]. //! Implement deserialization support of [`ImmutableString`][crate::ImmutableString] for [`serde`].
use crate::{EvalAltResult, Position}; use crate::{Position, RhaiError, RhaiResultOf, ERR};
use serde::de::{Deserializer, Visitor}; use serde::de::{Deserializer, Visitor};
use std::any::type_name; use std::any::type_name;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
@ -18,102 +18,93 @@ impl<'a> StringSliceDeserializer<'a> {
Self { value } Self { value }
} }
/// Shortcut for a type conversion error. /// Shortcut for a type conversion error.
fn type_error<T>(&self) -> Result<T, Box<EvalAltResult>> { fn type_error<T>(&self) -> RhaiResultOf<T> {
Err(EvalAltResult::ErrorMismatchOutputType( Err(
type_name::<T>().into(), ERR::ErrorMismatchOutputType(type_name::<T>().into(), "string".into(), Position::NONE)
"string".into(), .into(),
Position::NONE,
) )
.into())
} }
} }
impl<'de> Deserializer<'de> for &mut StringSliceDeserializer<'de> { impl<'de> Deserializer<'de> for &mut StringSliceDeserializer<'de> {
type Error = Box<EvalAltResult>; type Error = RhaiError;
fn deserialize_any<V: Visitor<'de>>(self, v: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_any<V: Visitor<'de>>(self, v: V) -> RhaiResultOf<V::Value> {
self.deserialize_str(v) self.deserialize_str(v)
} }
fn deserialize_bool<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_bool<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error() self.type_error()
} }
fn deserialize_i8<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_i8<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error() self.type_error()
} }
fn deserialize_i16<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_i16<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error() self.type_error()
} }
fn deserialize_i32<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_i32<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error() self.type_error()
} }
fn deserialize_i64<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_i64<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error() self.type_error()
} }
fn deserialize_u8<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_u8<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error() self.type_error()
} }
fn deserialize_u16<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_u16<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error() self.type_error()
} }
fn deserialize_u32<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_u32<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error() self.type_error()
} }
fn deserialize_u64<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_u64<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error() self.type_error()
} }
fn deserialize_f32<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_f32<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error() self.type_error()
} }
fn deserialize_f64<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_f64<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error() self.type_error()
} }
fn deserialize_char<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_char<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error() self.type_error()
} }
fn deserialize_str<V: Visitor<'de>>(self, v: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_str<V: Visitor<'de>>(self, v: V) -> RhaiResultOf<V::Value> {
// Only allow deserialization into a string. // Only allow deserialization into a string.
v.visit_borrowed_str(self.value) v.visit_borrowed_str(self.value)
} }
fn deserialize_string<V: Visitor<'de>>( fn deserialize_string<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
self,
visitor: V,
) -> Result<V::Value, Box<EvalAltResult>> {
self.deserialize_str(visitor) self.deserialize_str(visitor)
} }
fn deserialize_bytes<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_bytes<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error() self.type_error()
} }
fn deserialize_byte_buf<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_byte_buf<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error() self.type_error()
} }
fn deserialize_option<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_option<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error() self.type_error()
} }
fn deserialize_unit<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_unit<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error() self.type_error()
} }
fn deserialize_unit_struct<V: Visitor<'de>>( fn deserialize_unit_struct<V: Visitor<'de>>(
self, self,
_name: &'static str, _name: &'static str,
v: V, v: V,
) -> Result<V::Value, Box<EvalAltResult>> { ) -> RhaiResultOf<V::Value> {
self.deserialize_unit(v) self.deserialize_unit(v)
} }
fn deserialize_newtype_struct<V: Visitor<'de>>( fn deserialize_newtype_struct<V: Visitor<'de>>(
self, self,
_name: &'static str, _name: &'static str,
v: V, v: V,
) -> Result<V::Value, Box<EvalAltResult>> { ) -> RhaiResultOf<V::Value> {
v.visit_newtype_struct(self) v.visit_newtype_struct(self)
} }
fn deserialize_seq<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_seq<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error() self.type_error()
} }
fn deserialize_tuple<V: Visitor<'de>>( fn deserialize_tuple<V: Visitor<'de>>(self, _len: usize, v: V) -> RhaiResultOf<V::Value> {
self,
_len: usize,
v: V,
) -> Result<V::Value, Box<EvalAltResult>> {
self.deserialize_seq(v) self.deserialize_seq(v)
} }
fn deserialize_tuple_struct<V: Visitor<'de>>( fn deserialize_tuple_struct<V: Visitor<'de>>(
@ -121,10 +112,10 @@ impl<'de> Deserializer<'de> for &mut StringSliceDeserializer<'de> {
_name: &'static str, _name: &'static str,
_len: usize, _len: usize,
v: V, v: V,
) -> Result<V::Value, Box<EvalAltResult>> { ) -> RhaiResultOf<V::Value> {
self.deserialize_seq(v) self.deserialize_seq(v)
} }
fn deserialize_map<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_map<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error() self.type_error()
} }
fn deserialize_struct<V: Visitor<'de>>( fn deserialize_struct<V: Visitor<'de>>(
@ -132,7 +123,7 @@ impl<'de> Deserializer<'de> for &mut StringSliceDeserializer<'de> {
_name: &'static str, _name: &'static str,
_fields: &'static [&'static str], _fields: &'static [&'static str],
v: V, v: V,
) -> Result<V::Value, Box<EvalAltResult>> { ) -> RhaiResultOf<V::Value> {
self.deserialize_map(v) self.deserialize_map(v)
} }
fn deserialize_enum<V: Visitor<'de>>( fn deserialize_enum<V: Visitor<'de>>(
@ -140,16 +131,13 @@ impl<'de> Deserializer<'de> for &mut StringSliceDeserializer<'de> {
_name: &'static str, _name: &'static str,
_variants: &'static [&'static str], _variants: &'static [&'static str],
_: V, _: V,
) -> Result<V::Value, Box<EvalAltResult>> { ) -> RhaiResultOf<V::Value> {
self.type_error() self.type_error()
} }
fn deserialize_identifier<V: Visitor<'de>>(self, v: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_identifier<V: Visitor<'de>>(self, v: V) -> RhaiResultOf<V::Value> {
self.deserialize_str(v) self.deserialize_str(v)
} }
fn deserialize_ignored_any<V: Visitor<'de>>( fn deserialize_ignored_any<V: Visitor<'de>>(self, v: V) -> RhaiResultOf<V::Value> {
self,
v: V,
) -> Result<V::Value, Box<EvalAltResult>> {
self.deserialize_any(v) self.deserialize_any(v)
} }
} }

View File

@ -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, LexError, StaticVec, INT}; use crate::{Engine, LexError, StaticVec, INT, UNSIGNED_INT};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
use std::{ use std::{
@ -213,6 +213,16 @@ impl Position {
#[cfg(feature = "no_position")] #[cfg(feature = "no_position")]
return true; 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. /// Print this [`Position`] for debug purposes.
#[inline] #[inline]
pub(crate) fn debug_print(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { pub(crate) fn debug_print(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -240,7 +250,7 @@ impl fmt::Display for Position {
#[cfg(not(feature = "no_position"))] #[cfg(not(feature = "no_position"))]
write!(f, "line {}, position {}", self.line, self.pos)?; write!(f, "line {}, position {}", self.line, self.pos)?;
#[cfg(feature = "no_position")] #[cfg(feature = "no_position")]
unreachable!(); unreachable!("no position");
} }
Ok(()) Ok(())
@ -275,7 +285,7 @@ impl Add for Position {
}, },
}; };
#[cfg(feature = "no_position")] #[cfg(feature = "no_position")]
unreachable!(); unreachable!("no position");
} }
} }
} }
@ -684,11 +694,9 @@ impl Token {
/// Reverse lookup a token from a piece of syntax. /// Reverse lookup a token from a piece of syntax.
#[must_use] #[must_use]
pub fn lookup_from_syntax(syntax: impl AsRef<str>) -> Option<Self> { pub fn lookup_from_syntax(syntax: &str) -> Option<Self> {
use Token::*; use Token::*;
let syntax = syntax.as_ref();
Some(match syntax { Some(match syntax {
"{" => LeftBrace, "{" => LeftBrace,
"}" => RightBrace, "}" => RightBrace,
@ -776,11 +784,16 @@ impl Token {
#[cfg(feature = "no_module")] #[cfg(feature = "no_module")]
"import" | "export" | "as" => Reserved(syntax.into()), "import" | "export" | "as" => Reserved(syntax.into()),
"===" | "!==" | "->" | "<-" | ":=" | "~" | "::<" | "(*" | "*)" | "#" | "#!" // List of reserved operators
| "public" | "protected" | "super" | "new" | "use" | "module" | "package" | "var" "===" | "!==" | "->" | "<-" | ":=" | "~" | "::<" | "(*" | "*)" | "#" | "#!" => {
| "static" | "shared" | "with" | "goto" | "exit" | "match" | "case" | "default" Reserved(syntax.into())
| "void" | "null" | "nil" | "spawn" | "thread" | "go" | "sync" | "async" | "await" }
| "yield" => Reserved(syntax.into()),
// List of reserved keywords
"public" | "protected" | "super" | "new" | "use" | "module" | "package" | "var"
| "static" | "shared" | "with" | "is" | "goto" | "exit" | "match" | "case"
| "default" | "void" | "null" | "nil" | "spawn" | "thread" | "go" | "sync"
| "async" | "await" | "yield" => Reserved(syntax.into()),
KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR
| KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_THIS | KEYWORD_IS_DEF_VAR => { | KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_THIS | KEYWORD_IS_DEF_VAR => {
@ -873,13 +886,6 @@ impl Token {
use Token::*; use Token::*;
Precedence::new(match self { Precedence::new(match self {
// Assignments are not considered expressions - set to zero
Equals | PlusAssign | MinusAssign | MultiplyAssign | DivideAssign | PowerOfAssign
| LeftShiftAssign | RightShiftAssign | AndAssign | OrAssign | XOrAssign
| ModuloAssign => 0,
ExclusiveRange | InclusiveRange => 10,
Or | XOr | Pipe => 30, Or | XOr | Pipe => 30,
And | Ampersand => 60, And | Ampersand => 60,
@ -890,6 +896,8 @@ impl Token {
LessThan | LessThanEqualsTo | GreaterThan | GreaterThanEqualsTo => 130, LessThan | LessThanEqualsTo | GreaterThan | GreaterThanEqualsTo => 130,
ExclusiveRange | InclusiveRange => 140,
Plus | Minus => 150, Plus | Minus => 150,
Divide | Multiply | Modulo => 180, Divide | Multiply | Modulo => 180,
@ -898,8 +906,6 @@ impl Token {
LeftShift | RightShift => 210, LeftShift | RightShift => 210,
Period => 240,
_ => 0, _ => 0,
}) })
} }
@ -910,14 +916,6 @@ impl Token {
use Token::*; use Token::*;
match self { match self {
// Assignments bind to the right
Equals | PlusAssign | MinusAssign | MultiplyAssign | DivideAssign | PowerOfAssign
| LeftShiftAssign | RightShiftAssign | AndAssign | OrAssign | XOrAssign
| ModuloAssign => true,
// Property access binds to the right
Period => true,
// Exponentiation binds to the right // Exponentiation binds to the right
PowerOf => true, PowerOf => true,
@ -1026,7 +1024,7 @@ pub trait InputStream {
fn peek_next(&mut self) -> Option<char>; fn peek_next(&mut self) -> Option<char>;
} }
/// _(internals)_ Parse a string literal ended by `termination_char`. /// _(internals)_ Parse a string literal ended by a specified termination character.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
/// ///
/// Returns the parsed string and a boolean indicating whether the string is /// Returns the parsed string and a boolean indicating whether the string is
@ -1034,7 +1032,7 @@ pub trait InputStream {
/// ///
/// # Returns /// # Returns
/// ///
/// |Type |Return Value |`state.is_within_text_terminated_by`| /// | Type | Return Value |`state.is_within_text_terminated_by`|
/// |---------------------------------|:--------------------------:|:----------------------------------:| /// |---------------------------------|:--------------------------:|:----------------------------------:|
/// |`"hello"` |`StringConstant("hello")` |`None` | /// |`"hello"` |`StringConstant("hello")` |`None` |
/// |`"hello`_{LF}_ or _{EOF}_ |`LexError` |`None` | /// |`"hello`_{LF}_ or _{EOF}_ |`LexError` |`None` |
@ -1061,8 +1059,8 @@ pub fn parse_string_literal(
state: &mut TokenizeState, state: &mut TokenizeState,
pos: &mut Position, pos: &mut Position,
termination_char: char, termination_char: char,
continuation: bool,
verbatim: bool, verbatim: bool,
allow_line_continuation: bool,
allow_interpolation: bool, allow_interpolation: bool,
) -> Result<(Box<str>, bool), (LexError, Position)> { ) -> Result<(Box<str>, bool), (LexError, Position)> {
let mut result = String::with_capacity(12); let mut result = String::with_capacity(12);
@ -1091,7 +1089,7 @@ pub fn parse_string_literal(
pos.advance(); pos.advance();
break; break;
} }
None if continuation && !escape.is_empty() => { None if allow_line_continuation && !escape.is_empty() => {
assert_eq!(escape, "\\", "unexpected escape {} at end of line", escape); assert_eq!(escape, "\\", "unexpected escape {} at end of line", escape);
pos.advance(); pos.advance();
break; break;
@ -1159,7 +1157,7 @@ pub fn parse_string_literal(
'x' => 2, 'x' => 2,
'u' => 4, 'u' => 4,
'U' => 8, 'U' => 8,
_ => unreachable!(), c => unreachable!("x or u or U expected but gets '{}'", c),
}; };
for _ in 0..len { for _ in 0..len {
@ -1211,14 +1209,14 @@ pub fn parse_string_literal(
} }
// Line continuation // Line continuation
'\n' if continuation && !escape.is_empty() => { '\n' if allow_line_continuation && !escape.is_empty() => {
assert_eq!(escape, "\\", "unexpected escape {} at end of line", escape); assert_eq!(escape, "\\", "unexpected escape {} at end of line", escape);
escape.clear(); escape.clear();
pos.new_line(); pos.new_line();
#[cfg(not(feature = "no_position"))] #[cfg(not(feature = "no_position"))]
{ {
let start_position = start.position().expect("start position"); let start_position = start.position().unwrap();
skip_whitespace_until = start_position + 1; skip_whitespace_until = start_position + 1;
} }
} }
@ -1239,8 +1237,7 @@ pub fn parse_string_literal(
// Whitespace to skip // Whitespace to skip
#[cfg(not(feature = "no_position"))] #[cfg(not(feature = "no_position"))]
_ if next_char.is_whitespace() _ if next_char.is_whitespace() && pos.position().unwrap() < skip_whitespace_until => {}
&& pos.position().expect("position") < skip_whitespace_until => {}
// All other characters // All other characters
_ => { _ => {
@ -1319,7 +1316,7 @@ fn scan_block_comment(
level level
} }
/// _(internals)_ Get the next token from the `stream`. /// _(internals)_ Get the next token from the input stream.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
#[inline] #[inline]
#[must_use] #[must_use]
@ -1355,9 +1352,7 @@ fn is_numeric_digit(c: char) -> bool {
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
#[inline] #[inline]
#[must_use] #[must_use]
pub fn is_doc_comment(comment: impl AsRef<str>) -> bool { pub fn is_doc_comment(comment: &str) -> bool {
let comment = comment.as_ref();
(comment.starts_with("///") && !comment.starts_with("////")) (comment.starts_with("///") && !comment.starts_with("////"))
|| (comment.starts_with("/**") && !comment.starts_with("/***")) || (comment.starts_with("/**") && !comment.starts_with("/***"))
} }
@ -1400,7 +1395,7 @@ fn get_next_token_inner(
if let Some(ch) = state.is_within_text_terminated_by.take() { if let Some(ch) = state.is_within_text_terminated_by.take() {
let start_pos = *pos; let start_pos = *pos;
return parse_string_literal(stream, state, pos, ch, false, true, true).map_or_else( return parse_string_literal(stream, state, pos, ch, true, false, true).map_or_else(
|(err, err_pos)| Some((Token::LexError(err), err_pos)), |(err, err_pos)| Some((Token::LexError(err), err_pos)),
|(result, interpolated)| { |(result, interpolated)| {
if interpolated { if interpolated {
@ -1506,14 +1501,14 @@ fn get_next_token_inner(
'x' | 'X' => is_hex_digit, 'x' | 'X' => is_hex_digit,
'o' | 'O' => is_numeric_digit, 'o' | 'O' => is_numeric_digit,
'b' | 'B' => is_numeric_digit, 'b' | 'B' => is_numeric_digit,
_ => unreachable!(), c => unreachable!("x/X or o/O or b/B expected but gets '{}'", c),
}; };
radix_base = Some(match ch { radix_base = Some(match ch {
'x' | 'X' => 16, 'x' | 'X' => 16,
'o' | 'O' => 8, 'o' | 'O' => 8,
'b' | 'B' => 2, 'b' | 'B' => 2,
_ => unreachable!(), c => unreachable!("x/X or o/O or b/B expected but gets '{}'", c),
}); });
} }
@ -1535,7 +1530,8 @@ fn get_next_token_inner(
.filter(|&&c| c != NUMBER_SEPARATOR) .filter(|&&c| c != NUMBER_SEPARATOR)
.collect(); .collect();
INT::from_str_radix(&out, radix) UNSIGNED_INT::from_str_radix(&out, radix)
.map(|v| v as INT)
.map(Token::IntegerConstant) .map(Token::IntegerConstant)
.unwrap_or_else(|_| { .unwrap_or_else(|_| {
Token::LexError(LERR::MalformedNumber(result.into_iter().collect())) Token::LexError(LERR::MalformedNumber(result.into_iter().collect()))
@ -1583,7 +1579,7 @@ fn get_next_token_inner(
// " - string literal // " - string literal
('"', _) => { ('"', _) => {
return parse_string_literal(stream, state, pos, c, true, false, false) return parse_string_literal(stream, state, pos, c, false, true, false)
.map_or_else( .map_or_else(
|(err, err_pos)| Some((Token::LexError(err), err_pos)), |(err, err_pos)| Some((Token::LexError(err), err_pos)),
|(result, _)| Some((Token::StringConstant(result), start_pos)), |(result, _)| Some((Token::StringConstant(result), start_pos)),
@ -1610,7 +1606,7 @@ fn get_next_token_inner(
_ => (), _ => (),
} }
return parse_string_literal(stream, state, pos, c, false, true, true).map_or_else( return parse_string_literal(stream, state, pos, c, true, false, true).map_or_else(
|(err, err_pos)| Some((Token::LexError(err), err_pos)), |(err, err_pos)| Some((Token::LexError(err), err_pos)),
|(result, interpolated)| { |(result, interpolated)| {
if interpolated { if interpolated {
@ -1635,7 +1631,7 @@ fn get_next_token_inner(
|(err, err_pos)| (Token::LexError(err), err_pos), |(err, err_pos)| (Token::LexError(err), err_pos),
|(result, _)| { |(result, _)| {
let mut chars = result.chars(); let mut chars = result.chars();
let first = chars.next().expect("not empty"); let first = chars.next().unwrap();
if chars.next().is_some() { if chars.next().is_some() {
( (
@ -1675,6 +1671,17 @@ fn get_next_token_inner(
// Shebang // Shebang
('#', '!') => return Some((Token::Reserved("#!".into()), start_pos)), ('#', '!') => return Some((Token::Reserved("#!".into()), start_pos)),
('#', ' ') => {
eat_next(stream, pos);
let token = if stream.peek_next() == Some('{') {
eat_next(stream, pos);
"# {"
} else {
"#"
};
return Some((Token::Reserved(token.into()), start_pos));
}
('#', _) => return Some((Token::Reserved("#".into()), start_pos)), ('#', _) => return Some((Token::Reserved("#".into()), start_pos)),
// Operators // Operators
@ -2006,8 +2013,8 @@ fn get_identifier(
/// Is this keyword allowed as a function? /// Is this keyword allowed as a function?
#[inline] #[inline]
#[must_use] #[must_use]
pub fn is_keyword_function(name: impl AsRef<str>) -> bool { pub fn is_keyword_function(name: &str) -> bool {
match name.as_ref() { match name {
KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR
| KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_IS_DEF_VAR => true, | KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_IS_DEF_VAR => true,
@ -2036,11 +2043,11 @@ pub fn is_valid_identifier(name: impl Iterator<Item = char>) -> bool {
first_alphabetic first_alphabetic
} }
/// Is a text string a valid scripted function name? /// Is a text string a valid script-defined function name?
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn is_valid_function_name(name: impl AsRef<str>) -> bool { pub fn is_valid_function_name(name: &str) -> bool {
is_valid_identifier(name.as_ref().chars()) is_valid_identifier(name.chars())
} }
/// Is a character valid to start an identifier? /// Is a character valid to start an identifier?
@ -2202,19 +2209,14 @@ impl<'a> Iterator for TokenIterator<'a> {
("(*", false) | ("*)", false) => Token::LexError(LERR::ImproperSymbol(s.to_string(), ("(*", false) | ("*)", false) => Token::LexError(LERR::ImproperSymbol(s.to_string(),
"'(* .. *)' is not a valid comment format. This is not Pascal! Should it be '/* .. */'?".to_string(), "'(* .. *)' is not a valid comment format. This is not Pascal! Should it be '/* .. */'?".to_string(),
)), )),
("#", false) => Token::LexError(LERR::ImproperSymbol(s.to_string(), ("# {", false) => Token::LexError(LERR::ImproperSymbol(s.to_string(),
"'#' is not a valid symbol. Should it be '#{'?".to_string(), "'#' is not a valid symbol. Should it be '#{'?".to_string(),
)), )),
// Reserved keyword/operator that is custom. // Reserved keyword/operator that is custom.
(_, true) => Token::Custom(s), (_, true) => Token::Custom(s),
// Reserved operator that is not custom.
(token, false) if !is_valid_identifier(token.chars()) => {
let msg = format!("'{}' is a reserved symbol", token);
Token::LexError(LERR::ImproperSymbol(s.to_string(), msg))
},
// Reserved keyword that is not custom and disabled. // Reserved keyword that is not custom and disabled.
(token, false) if self.engine.disabled_symbols.contains(token) => { (token, false) if self.engine.disabled_symbols.contains(token) => {
let msg = format!("reserved symbol '{}' is disabled", token); let msg = format!("reserved {} '{}' is disabled", if is_valid_identifier(token.chars()) { "keyword"} else {"symbol"}, token);
Token::LexError(LERR::ImproperSymbol(s.to_string(), msg)) Token::LexError(LERR::ImproperSymbol(s.to_string(), msg))
}, },
// Reserved keyword/operator that is not custom. // Reserved keyword/operator that is not custom.
@ -2224,7 +2226,7 @@ impl<'a> Iterator for TokenIterator<'a> {
Some((Token::Identifier(s), pos)) if self.engine.custom_keywords.contains_key(&*s) => { Some((Token::Identifier(s), pos)) if self.engine.custom_keywords.contains_key(&*s) => {
(Token::Custom(s), pos) (Token::Custom(s), pos)
} }
// Custom standard keyword/symbol - must be disabled // Custom keyword/symbol - must be disabled
Some((token, pos)) if self.engine.custom_keywords.contains_key(&*token.syntax()) => { Some((token, pos)) if self.engine.custom_keywords.contains_key(&*token.syntax()) => {
if self.engine.disabled_symbols.contains(&*token.syntax()) { if self.engine.disabled_symbols.contains(&*token.syntax()) {
// Disabled standard keyword/symbol // Disabled standard keyword/symbol

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
//! Module containing error definitions for the evaluation process. //! Module containing error definitions for the evaluation process.
use crate::{Dynamic, ImmutableString, ParseErrorType, Position, INT}; use crate::{Dynamic, ImmutableString, ParseErrorType, Position, RhaiError, INT};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use core_error::Error; use core_error::Error;
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
@ -36,12 +36,12 @@ pub enum EvalAltResult {
ErrorFunctionNotFound(String, Position), ErrorFunctionNotFound(String, Position),
/// An error has occurred inside a called function. /// An error has occurred inside a called function.
/// Wrapped values are the function name, function source, and the interior error. /// Wrapped values are the function name, function source, and the interior error.
ErrorInFunctionCall(String, String, Box<EvalAltResult>, Position), ErrorInFunctionCall(String, String, RhaiError, Position),
/// Usage of an unknown [module][crate::Module]. Wrapped value is the [module][crate::Module] name. /// Usage of an unknown [module][crate::Module]. Wrapped value is the [module][crate::Module] name.
ErrorModuleNotFound(String, Position), ErrorModuleNotFound(String, Position),
/// An error has occurred while loading a [module][crate::Module]. /// An error has occurred while loading a [module][crate::Module].
/// Wrapped value are the [module][crate::Module] name and the interior error. /// Wrapped value are the [module][crate::Module] name and the interior error.
ErrorInModule(String, Box<EvalAltResult>, Position), ErrorInModule(String, RhaiError, Position),
/// Access to `this` that is not bound. /// Access to `this` that is not bound.
ErrorUnboundThis(Position), ErrorUnboundThis(Position),
/// Data is not of the required type. /// Data is not of the required type.
@ -222,7 +222,7 @@ impl<T: AsRef<str>> From<T> for EvalAltResult {
} }
} }
impl<T: AsRef<str>> From<T> for Box<EvalAltResult> { impl<T: AsRef<str>> From<T> for RhaiError {
#[inline(never)] #[inline(never)]
fn from(err: T) -> Self { fn from(err: T) -> Self {
EvalAltResult::ErrorRuntime(err.as_ref().to_string().into(), Position::NONE).into() EvalAltResult::ErrorRuntime(err.as_ref().to_string().into(), Position::NONE).into()
@ -300,7 +300,7 @@ impl EvalAltResult {
format!("{:?}", self) format!("{:?}", self)
.split('(') .split('(')
.next() .next()
.expect("debug format of error is `ErrorXXX(...)`") .expect("`ErrorXXX(...)`")
.into(), .into(),
); );

View File

@ -3,8 +3,8 @@
use crate::tokenizer::is_valid_identifier; use crate::tokenizer::is_valid_identifier;
use crate::types::dynamic::Variant; use crate::types::dynamic::Variant;
use crate::{ use crate::{
Dynamic, Engine, EvalAltResult, FuncArgs, Identifier, Module, NativeCallContext, Position, Dynamic, Engine, FuncArgs, Identifier, Module, NativeCallContext, Position, RhaiError,
RhaiResult, StaticVec, AST, RhaiResult, RhaiResultOf, StaticVec, AST, ERR,
}; };
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -24,7 +24,12 @@ impl fmt::Debug for FnPtr {
if !self.is_curried() { if !self.is_curried() {
write!(f, "Fn({})", self.fn_name()) write!(f, "Fn({})", self.fn_name())
} else { } else {
f.debug_tuple("Fn").field(&self.0).field(&self.1).finish() self.1
.iter()
.fold(f.debug_tuple("Fn").field(&self.0), |f, curry| {
f.field(curry)
})
.finish()
} }
} }
} }
@ -32,7 +37,7 @@ impl fmt::Debug for FnPtr {
impl FnPtr { impl FnPtr {
/// Create a new function pointer. /// Create a new function pointer.
#[inline(always)] #[inline(always)]
pub fn new(name: impl Into<Identifier>) -> Result<Self, Box<EvalAltResult>> { pub fn new(name: impl Into<Identifier>) -> RhaiResultOf<Self> {
name.into().try_into() name.into().try_into()
} }
/// Create a new function pointer without checking its parameters. /// Create a new function pointer without checking its parameters.
@ -135,7 +140,7 @@ impl FnPtr {
engine: &Engine, engine: &Engine,
ast: &AST, ast: &AST,
args: impl FuncArgs, args: impl FuncArgs,
) -> Result<T, Box<EvalAltResult>> { ) -> RhaiResultOf<T> {
let _ast = ast; let _ast = ast;
let mut arg_values = crate::StaticVec::new_const(); let mut arg_values = crate::StaticVec::new_const();
args.parse(&mut arg_values); args.parse(&mut arg_values);
@ -157,7 +162,7 @@ impl FnPtr {
let typ = engine.map_type_name(result.type_name()); let typ = engine.map_type_name(result.type_name());
result.try_cast().ok_or_else(|| { result.try_cast().ok_or_else(|| {
EvalAltResult::ErrorMismatchOutputType( ERR::ErrorMismatchOutputType(
engine.map_type_name(type_name::<T>()).into(), engine.map_type_name(type_name::<T>()).into(),
typ.into(), typ.into(),
Position::NONE, Position::NONE,
@ -176,7 +181,7 @@ impl FnPtr {
&self, &self,
context: &NativeCallContext, context: &NativeCallContext,
args: impl FuncArgs, args: impl FuncArgs,
) -> Result<T, Box<EvalAltResult>> { ) -> RhaiResultOf<T> {
let mut arg_values = crate::StaticVec::new_const(); let mut arg_values = crate::StaticVec::new_const();
args.parse(&mut arg_values); args.parse(&mut arg_values);
@ -185,7 +190,7 @@ impl FnPtr {
let typ = context.engine().map_type_name(result.type_name()); let typ = context.engine().map_type_name(result.type_name());
result.try_cast().ok_or_else(|| { result.try_cast().ok_or_else(|| {
EvalAltResult::ErrorMismatchOutputType( ERR::ErrorMismatchOutputType(
context.engine().map_type_name(type_name::<T>()).into(), context.engine().map_type_name(type_name::<T>()).into(),
typ.into(), typ.into(),
Position::NONE, Position::NONE,
@ -204,10 +209,11 @@ impl FnPtr {
/// ///
/// This function is very low level. /// This function is very low level.
/// ///
/// ## Arguments /// # Arguments
/// ///
/// All the arguments are _consumed_, meaning that they're replaced by `()`. /// All the arguments are _consumed_, meaning that they're replaced by `()`.
/// This is to avoid unnecessarily cloning the arguments. /// This is to avoid unnecessarily cloning the arguments.
///
/// Do not use the arguments after this call. If they are needed afterwards, /// Do not use the arguments after this call. If they are needed afterwards,
/// clone them _before_ calling this function. /// clone them _before_ calling this function.
#[inline] #[inline]
@ -247,53 +253,53 @@ impl fmt::Display for FnPtr {
} }
impl TryFrom<Identifier> for FnPtr { impl TryFrom<Identifier> for FnPtr {
type Error = Box<EvalAltResult>; type Error = RhaiError;
#[inline] #[inline]
fn try_from(value: Identifier) -> Result<Self, Self::Error> { fn try_from(value: Identifier) -> RhaiResultOf<Self> {
if is_valid_identifier(value.chars()) { if is_valid_identifier(value.chars()) {
Ok(Self(value, StaticVec::new_const())) Ok(Self(value, StaticVec::new_const()))
} else { } else {
Err(EvalAltResult::ErrorFunctionNotFound(value.to_string(), Position::NONE).into()) Err(ERR::ErrorFunctionNotFound(value.to_string(), Position::NONE).into())
} }
} }
} }
impl TryFrom<crate::ImmutableString> for FnPtr { impl TryFrom<crate::ImmutableString> for FnPtr {
type Error = Box<EvalAltResult>; type Error = RhaiError;
#[inline(always)] #[inline(always)]
fn try_from(value: crate::ImmutableString) -> Result<Self, Self::Error> { fn try_from(value: crate::ImmutableString) -> RhaiResultOf<Self> {
let s: Identifier = value.into(); let s: Identifier = value.into();
Self::try_from(s) Self::try_from(s)
} }
} }
impl TryFrom<String> for FnPtr { impl TryFrom<String> for FnPtr {
type Error = Box<EvalAltResult>; type Error = RhaiError;
#[inline(always)] #[inline(always)]
fn try_from(value: String) -> Result<Self, Self::Error> { fn try_from(value: String) -> RhaiResultOf<Self> {
let s: Identifier = value.into(); let s: Identifier = value.into();
Self::try_from(s) Self::try_from(s)
} }
} }
impl TryFrom<Box<str>> for FnPtr { impl TryFrom<Box<str>> for FnPtr {
type Error = Box<EvalAltResult>; type Error = RhaiError;
#[inline(always)] #[inline(always)]
fn try_from(value: Box<str>) -> Result<Self, Self::Error> { fn try_from(value: Box<str>) -> RhaiResultOf<Self> {
let s: Identifier = value.into(); let s: Identifier = value.into();
Self::try_from(s) Self::try_from(s)
} }
} }
impl TryFrom<&str> for FnPtr { impl TryFrom<&str> for FnPtr {
type Error = Box<EvalAltResult>; type Error = RhaiError;
#[inline(always)] #[inline(always)]
fn try_from(value: &str) -> Result<Self, Self::Error> { fn try_from(value: &str) -> RhaiResultOf<Self> {
let s: Identifier = value.into(); let s: Identifier = value.into();
Self::try_from(s) Self::try_from(s)
} }

95
src/types/interner.rs Normal file
View File

@ -0,0 +1,95 @@
use crate::{Identifier, ImmutableString};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{collections::BTreeMap, ops::AddAssign};
/// _(internals)_ A factory of identifiers from text strings.
/// Exported under the `internals` feature only.
///
/// Normal identifiers, property getters and setters are interned separately.
#[derive(Debug, Clone, Default, Hash)]
pub struct StringsInterner {
/// Normal strings.
strings: BTreeMap<Identifier, ImmutableString>,
/// Property getters.
#[cfg(not(feature = "no_object"))]
getters: BTreeMap<Identifier, ImmutableString>,
/// Property setters.
#[cfg(not(feature = "no_object"))]
setters: BTreeMap<Identifier, ImmutableString>,
}
impl StringsInterner {
/// Create a new [`StringsInterner`].
#[inline]
#[must_use]
pub fn new() -> Self {
Self {
strings: BTreeMap::new(),
#[cfg(not(feature = "no_object"))]
getters: BTreeMap::new(),
#[cfg(not(feature = "no_object"))]
setters: BTreeMap::new(),
}
}
/// Get an identifier from a text string and prefix, adding it to the interner if necessary.
///
/// # Prefix
///
/// Currently recognized prefixes are:
///
/// * `""` - None (normal string)
/// * `"get$"` - Property getter, not available under `no_object`
/// * `"set$"` - Property setter, not available under `no_object`
///
/// # Panics
///
/// Panics if the prefix is not recognized.
#[inline]
#[must_use]
pub fn get(&mut self, prefix: impl AsRef<str>, text: impl AsRef<str>) -> ImmutableString {
let (dict, mapper): (_, fn(&str) -> Identifier) = match prefix.as_ref() {
"" => (&mut self.strings, |s| s.into()),
#[cfg(not(feature = "no_object"))]
crate::engine::FN_GET => (&mut self.getters, crate::engine::make_getter),
#[cfg(not(feature = "no_object"))]
crate::engine::FN_SET => (&mut self.setters, crate::engine::make_setter),
_ => unreachable!("unsupported prefix {}", prefix.as_ref()),
};
if dict.contains_key(text.as_ref()) {
dict.get(text.as_ref()).unwrap().clone()
} else {
let value: ImmutableString = mapper(text.as_ref()).into();
dict.insert(text.as_ref().into(), value.clone());
value
}
}
}
impl AddAssign<Self> for StringsInterner {
#[inline(always)]
fn add_assign(&mut self, rhs: Self) {
self.strings.extend(rhs.strings.into_iter());
#[cfg(not(feature = "no_object"))]
self.getters.extend(rhs.getters.into_iter());
#[cfg(not(feature = "no_object"))]
self.setters.extend(rhs.setters.into_iter());
}
}
impl AddAssign<&Self> for StringsInterner {
#[inline(always)]
fn add_assign(&mut self, rhs: &Self) {
self.strings
.extend(rhs.strings.iter().map(|(k, v)| (k.clone(), v.clone())));
#[cfg(not(feature = "no_object"))]
self.getters
.extend(rhs.getters.iter().map(|(k, v)| (k.clone(), v.clone())));
#[cfg(not(feature = "no_object"))]
self.setters
.extend(rhs.setters.iter().map(|(k, v)| (k.clone(), v.clone())));
}
}

View File

@ -4,6 +4,7 @@ pub mod dynamic;
pub mod error; pub mod error;
pub mod fn_ptr; pub mod fn_ptr;
pub mod immutable_string; pub mod immutable_string;
pub mod interner;
pub mod parse_error; pub mod parse_error;
pub mod scope; pub mod scope;
@ -11,5 +12,6 @@ pub use dynamic::Dynamic;
pub use error::EvalAltResult; pub use error::EvalAltResult;
pub use fn_ptr::FnPtr; pub use fn_ptr::FnPtr;
pub use immutable_string::ImmutableString; pub use immutable_string::ImmutableString;
pub use interner::StringsInterner;
pub use parse_error::{LexError, ParseError, ParseErrorType}; pub use parse_error::{LexError, ParseError, ParseErrorType};
pub use scope::Scope; pub use scope::Scope;

View File

@ -1,6 +1,7 @@
//! Module containing error definitions for the parsing process. //! Module containing error definitions for the parsing process.
use crate::{EvalAltResult, Position}; use crate::tokenizer::is_valid_identifier;
use crate::{Position, RhaiError, ERR};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use core_error::Error; use core_error::Error;
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
@ -65,7 +66,7 @@ impl LexError {
} }
} }
/// Type of error encountered when parsing a script. /// Error encountered when parsing a script.
/// ///
/// Some errors never appear when certain features are turned on. /// Some errors never appear when certain features are turned on.
/// They still exist so that the application can turn features on and off without going through /// They still exist so that the application can turn features on and off without going through
@ -108,9 +109,9 @@ pub enum ParseErrorType {
DuplicatedSwitchCase, DuplicatedSwitchCase,
/// A variable name is duplicated. Wrapped value is the variable name. /// A variable name is duplicated. Wrapped value is the variable name.
DuplicatedVariable(String), DuplicatedVariable(String),
/// An integer case of a `switch` statement is after a range case. /// An integer case of a `switch` statement is in an appropriate place.
WrongSwitchIntegerCase, WrongSwitchIntegerCase,
/// The default case of a `switch` statement is not the last. /// The default case of a `switch` statement is in an appropriate place.
WrongSwitchDefaultCase, WrongSwitchDefaultCase,
/// The case condition of a `switch` statement is not appropriate. /// The case condition of a `switch` statement is not appropriate.
WrongSwitchCaseCondition, WrongSwitchCaseCondition,
@ -120,7 +121,7 @@ pub enum ParseErrorType {
PropertyExpected, PropertyExpected,
/// Missing a variable name after the `let`, `const`, `for` or `catch` keywords. /// Missing a variable name after the `let`, `const`, `for` or `catch` keywords.
VariableExpected, VariableExpected,
/// An identifier is a reserved keyword. /// An identifier is a reserved symbol.
Reserved(String), Reserved(String),
/// An expression is of the wrong type. /// An expression is of the wrong type.
/// Wrapped values are the type requested and type of the actual result. /// Wrapped values are the type requested and type of the actual result.
@ -260,7 +261,8 @@ impl fmt::Display for ParseErrorType {
}, },
Self::LiteralTooLarge(typ, max) => write!(f, "{} exceeds the maximum limit ({})", typ, max), Self::LiteralTooLarge(typ, max) => write!(f, "{} exceeds the maximum limit ({})", typ, max),
Self::Reserved(s) => write!(f, "'{}' is a reserved keyword", s), Self::Reserved(s) if is_valid_identifier(s.chars()) => write!(f, "'{}' is a reserved keyword", s),
Self::Reserved(s) => write!(f, "'{}' is a reserved symbol", s),
Self::UnexpectedEOF => f.write_str("Script is incomplete"), Self::UnexpectedEOF => f.write_str("Script is incomplete"),
Self::WrongSwitchIntegerCase => f.write_str("Integer switch case cannot follow a range case"), Self::WrongSwitchIntegerCase => f.write_str("Integer switch case cannot follow a range case"),
Self::WrongSwitchDefaultCase => f.write_str("Default switch case must be the last"), Self::WrongSwitchDefaultCase => f.write_str("Default switch case must be the last"),
@ -308,30 +310,30 @@ impl fmt::Display for ParseError {
} }
} }
impl From<ParseErrorType> for Box<EvalAltResult> { impl From<ParseErrorType> for RhaiError {
#[inline(always)] #[inline(always)]
fn from(err: ParseErrorType) -> Self { fn from(err: ParseErrorType) -> Self {
Box::new(err.into()) Box::new(err.into())
} }
} }
impl From<ParseErrorType> for EvalAltResult { impl From<ParseErrorType> for ERR {
#[inline(always)] #[inline(always)]
fn from(err: ParseErrorType) -> Self { fn from(err: ParseErrorType) -> Self {
EvalAltResult::ErrorParsing(err, Position::NONE) ERR::ErrorParsing(err, Position::NONE)
} }
} }
impl From<ParseError> for Box<EvalAltResult> { impl From<ParseError> for RhaiError {
#[inline(always)] #[inline(always)]
fn from(err: ParseError) -> Self { fn from(err: ParseError) -> Self {
Box::new(err.into()) Box::new(err.into())
} }
} }
impl From<ParseError> for EvalAltResult { impl From<ParseError> for ERR {
#[inline(always)] #[inline(always)]
fn from(err: ParseError) -> Self { fn from(err: ParseError) -> Self {
EvalAltResult::ErrorParsing(*err.0, err.1) ERR::ErrorParsing(*err.0, err.1)
} }
} }

View File

@ -406,7 +406,7 @@ impl<'a> Scope<'a> {
self.push(name, value); self.push(name, value);
} }
Some((index, AccessMode::ReadWrite)) => { Some((index, AccessMode::ReadWrite)) => {
let value_ref = self.values.get_mut(index).expect("valid index"); let value_ref = self.values.get_mut(index).unwrap();
*value_ref = Dynamic::from(value); *value_ref = Dynamic::from(value);
} }
} }
@ -446,7 +446,7 @@ impl<'a> Scope<'a> {
} }
Some((_, AccessMode::ReadOnly)) => panic!("variable {} is constant", name.as_ref()), Some((_, AccessMode::ReadOnly)) => panic!("variable {} is constant", name.as_ref()),
Some((index, AccessMode::ReadWrite)) => { Some((index, AccessMode::ReadWrite)) => {
let value_ref = self.values.get_mut(index).expect("valid index"); let value_ref = self.values.get_mut(index).unwrap();
*value_ref = Dynamic::from(value); *value_ref = Dynamic::from(value);
} }
} }
@ -492,7 +492,7 @@ impl<'a> Scope<'a> {
#[inline] #[inline]
#[must_use] #[must_use]
pub(crate) fn get_mut_by_index(&mut self, index: usize) -> &mut Dynamic { pub(crate) fn get_mut_by_index(&mut self, index: usize) -> &mut Dynamic {
self.values.get_mut(index).expect("valid index") self.values.get_mut(index).unwrap()
} }
/// Add an alias to an entry in the [`Scope`]. /// Add an alias to an entry in the [`Scope`].
/// ///
@ -502,7 +502,7 @@ impl<'a> Scope<'a> {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[inline] #[inline]
pub(crate) fn add_entry_alias(&mut self, index: usize, alias: Identifier) -> &mut Self { pub(crate) fn add_entry_alias(&mut self, index: usize, alias: Identifier) -> &mut Self {
let (_, aliases) = self.names.get_mut(index).expect("valid index"); let (_, aliases) = self.names.get_mut(index).unwrap();
match aliases { match aliases {
None => { None => {
let mut list = StaticVec::new_const(); let mut list = StaticVec::new_const();

View File

@ -8,35 +8,48 @@ use std::{
}; };
/// Cast a type into another type. /// Cast a type into another type.
#[inline] ///
pub fn unsafe_try_cast<A: Any, B: Any>(a: A) -> Result<B, A> { /// # Undefined Behavior
///
/// It is UB if the types are not compatible.
#[inline(always)]
#[must_use]
pub fn unsafe_cast<A: Any, B: Any>(a: A) -> B {
unsafe {
let ret: B = ptr::read(&a as *const _ as *const B);
// We explicitly forget the value immediately after moving out,
// removing any chance of a destructor running or value otherwise
// being used again.
mem::forget(a);
ret
}
}
/// Cast a type into another type.
#[inline(always)]
#[must_use]
pub fn unsafe_try_cast<A: Any, B: Any>(a: A) -> Option<B> {
if TypeId::of::<B>() == a.type_id() { if TypeId::of::<B>() == a.type_id() {
// SAFETY: Just checked we have the right type. We explicitly forget the // SAFETY: Just checked we have the right type.
// value immediately after moving out, removing any chance of a destructor Some(unsafe_cast(a))
// running or value otherwise being used again.
unsafe {
let ret: B = ptr::read(&a as *const _ as *const B);
mem::forget(a);
Ok(ret)
}
} else { } else {
Err(a) None
} }
} }
/// Cast a Boxed type into another type. /// Cast a Boxed type into another type.
#[inline] #[inline(always)]
pub fn unsafe_cast_box<X: Any, T: Any>(item: Box<X>) -> Result<Box<T>, Box<X>> { #[must_use]
pub fn unsafe_cast_box<X: Any, T: Any>(item: Box<X>) -> Option<T> {
// Only allow casting to the exact same type // Only allow casting to the exact same type
if TypeId::of::<X>() == TypeId::of::<T>() { if TypeId::of::<X>() == TypeId::of::<T>() {
// SAFETY: just checked whether we are pointing to the correct type // SAFETY: just checked whether we are pointing to the correct type
unsafe { unsafe {
let raw: *mut dyn Any = Box::into_raw(item as Box<dyn Any>); let raw: *mut dyn Any = Box::into_raw(item as Box<dyn Any>);
Ok(Box::from_raw(raw as *mut T)) Some(*Box::from_raw(raw as *mut T))
} }
} else { } else {
// Return the consumed item for chaining. None
Err(item)
} }
} }

View File

@ -162,6 +162,13 @@ fn test_blobs_parse() -> Result<(), Box<EvalAltResult>> {
0x1cf588 0x1cf588
); );
assert_eq!(
engine.eval::<Blob>(
"let x = blob(16, 0); write_be(x, 0, 8, 0x1234567890abcdef); write_be(x, 8, 8, 0xabcdef1234567890); x"
)?,
vec![0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90]
);
Ok(()) Ok(())
} }
@ -233,3 +240,37 @@ fn test_blobs_parse() -> Result<(), Box<EvalAltResult>> {
Ok(()) Ok(())
} }
#[test]
fn test_blobs_write_string() -> Result<(), Box<EvalAltResult>> {
let engine = Engine::new();
assert_eq!(
engine.eval::<Blob>(r#"let x = blob(16, 0); write_ascii(x, 0, 14, "hello, world!"); x"#)?,
"hello, world!\0\0\0".as_bytes()
);
assert_eq!(
engine.eval::<Blob>(r#"let x = blob(10, 0); write_ascii(x, 3..8, "hello, world!"); x"#)?,
"\0\0\0hello\0\0".as_bytes()
);
assert_eq!(
engine.eval::<Blob>(
r#"let x = blob(10, 0); write_ascii(x, 0..9, "❤ hello, ❤ world! ❤❤❤"); x"#
)?,
" hello, \0".as_bytes()
);
assert_eq!(
engine.eval::<Blob>(r#"let x = blob(10, 0); write_utf8(x, 3..9, "❤❤❤❤"); x"#)?,
"\0\0\0\u{2764}\u{2764}\0".as_bytes()
);
assert_eq!(
engine.eval::<Blob>(r#"let x = blob(10, 0); write_utf8(x, 3..7, "❤❤❤❤"); x"#)?,
vec![0, 0, 0, 226, 157, 164, 226, 0, 0, 0]
);
Ok(())
}

View File

@ -69,6 +69,36 @@ fn test_call_fn() -> Result<(), Box<EvalAltResult>> {
Ok(()) Ok(())
} }
#[test]
fn test_call_fn_scope() -> Result<(), Box<EvalAltResult>> {
let engine = Engine::new();
let mut scope = Scope::new();
let ast = engine.compile(
"
fn foo(x) {
let hello = 42;
bar + hello + x
}
let bar = 123;
",
)?;
for _ in 0..50 {
assert_eq!(
engine
.call_fn_raw(&mut scope, &ast, true, false, "foo", None, [Dynamic::THREE])?
.as_int()?,
168
);
}
assert_eq!(scope.len(), 100);
Ok(())
}
struct Options { struct Options {
pub foo: bool, pub foo: bool,
pub bar: String, pub bar: String,
@ -296,7 +326,7 @@ fn test_call_fn_events() -> Result<(), Box<EvalAltResult>> {
"end" => engine.call_fn(scope, ast, "end", (event_data,)).unwrap(), "end" => engine.call_fn(scope, ast, "end", (event_data,)).unwrap(),
// The 'update' event maps to function 'update'. // The 'update' event maps to function 'update'.
// This event provides a default implementation when the scripted function is not found. // This event provides a default implementation when the script-defined function is not found.
"update" => engine "update" => engine
.call_fn(scope, ast, "update", (event_data,)) .call_fn(scope, ast, "update", (event_data,))
.or_else(|err| match *err { .or_else(|err| match *err {

View File

@ -25,13 +25,13 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
], ],
true, true,
|context, inputs| { |context, inputs| {
let var_name = inputs[0].get_variable_name().unwrap().to_string(); let var_name = inputs[0].get_string_value().unwrap();
let op = inputs[1].get_literal_value::<ImmutableString>().unwrap(); let op = inputs[1].get_literal_value::<ImmutableString>().unwrap();
let max = inputs[2].get_literal_value::<INT>().unwrap(); let max = inputs[2].get_literal_value::<INT>().unwrap();
let stmt = &inputs[3]; let stmt = &inputs[3];
let condition = &inputs[4]; let condition = &inputs[4];
context.scope_mut().push(var_name.clone(), 0 as INT); context.scope_mut().push(var_name.to_string(), 0 as INT);
let mut count: INT = 0; let mut count: INT = 0;
@ -151,14 +151,14 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
&["var", "$ident$", "=", "$expr$"], &["var", "$ident$", "=", "$expr$"],
true, true,
|context, inputs| { |context, inputs| {
let var_name = inputs[0].get_variable_name().unwrap().to_string(); let var_name = inputs[0].get_string_value().unwrap();
let expr = &inputs[1]; let expr = &inputs[1];
// Evaluate the expression // Evaluate the expression
let value = context.eval_expression_tree(expr)?; let value = context.eval_expression_tree(expr)?;
if !context.scope().is_constant(&var_name).unwrap_or(false) { if !context.scope().is_constant(var_name).unwrap_or(false) {
context.scope_mut().set_value(var_name, value); context.scope_mut().set_value(var_name.to_string(), value);
Ok(Dynamic::UNIT) Ok(Dynamic::UNIT)
} else { } else {
Err(format!("variable {} is constant", var_name).into()) Err(format!("variable {} is constant", var_name).into())
@ -206,7 +206,7 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
|context, inputs| { |context, inputs| {
context.scope_mut().push("foo", 999 as INT); context.scope_mut().push("foo", 999 as INT);
Ok(match inputs[0].get_variable_name().unwrap() { Ok(match inputs[0].get_string_value().unwrap() {
"world" "world"
if inputs if inputs
.last() .last()
@ -239,3 +239,38 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
Ok(()) Ok(())
} }
#[test]
fn test_custom_syntax_raw2() -> Result<(), Box<EvalAltResult>> {
let mut engine = Engine::new();
engine.register_custom_syntax_raw(
"#",
|symbols, lookahead| match symbols.len() {
1 if lookahead == "-" => Ok(Some("$symbol$".into())),
1 => Ok(Some("$int$".into())),
2 if symbols[1] == "-" => Ok(Some("$int$".into())),
2 => Ok(None),
3 => Ok(None),
_ => unreachable!(),
},
false,
move |_, inputs| {
let id = if inputs.len() == 2 {
-inputs[1].get_literal_value::<INT>().unwrap()
} else {
inputs[0].get_literal_value::<INT>().unwrap()
};
Ok(id.into())
},
);
assert_eq!(engine.eval::<INT>("#-1")?, -1);
assert_eq!(engine.eval::<INT>("let x = 41; x + #1")?, 42);
#[cfg(not(feature = "no_object"))]
assert_eq!(engine.eval::<INT>("#-42.abs()")?, 42);
assert_eq!(engine.eval::<INT>("#42/2")?, 21);
assert_eq!(engine.eval::<INT>("sign(#1)")?, 1);
Ok(())
}

View File

@ -30,7 +30,7 @@ fn test_max_string_size() -> Result<(), Box<EvalAltResult>> {
assert!(matches!( assert!(matches!(
*engine *engine
.eval::<String>( .run(
r#" r#"
let x = "hello, "; let x = "hello, ";
let y = "world!"; let y = "world!";
@ -44,7 +44,7 @@ fn test_max_string_size() -> Result<(), Box<EvalAltResult>> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
assert!(matches!( assert!(matches!(
*engine *engine
.eval::<String>( .run(
r#" r#"
let x = "hello"; let x = "hello";
x.pad(100, '!'); x.pad(100, '!');
@ -90,7 +90,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
assert!(matches!( assert!(matches!(
*engine *engine
.eval::<Array>( .run(
" "
let x = [1,2,3,4,5,6]; let x = [1,2,3,4,5,6];
let y = [7,8,9,10,11,12]; let y = [7,8,9,10,11,12];
@ -101,10 +101,22 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
EvalAltResult::ErrorDataTooLarge(_, _) EvalAltResult::ErrorDataTooLarge(_, _)
)); ));
assert!(matches!(
*engine
.run(
"
let x = [ 42 ];
loop { x[0] = x; }
"
)
.expect_err("should error"),
EvalAltResult::ErrorDataTooLarge(_, _)
));
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
assert!(matches!( assert!(matches!(
*engine *engine
.eval::<Array>( .run(
" "
let x = [1,2,3,4,5,6]; let x = [1,2,3,4,5,6];
x.pad(100, 42); x.pad(100, 42);
@ -117,7 +129,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
assert!(matches!( assert!(matches!(
*engine *engine
.eval::<Array>( .run(
" "
let x = [1,2,3]; let x = [1,2,3];
[x, x, x, x] [x, x, x, x]
@ -130,7 +142,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
assert!(matches!( assert!(matches!(
*engine *engine
.eval::<Array>( .run(
" "
let x = #{a:1, b:2, c:3}; let x = #{a:1, b:2, c:3};
[x, x, x, x] [x, x, x, x]
@ -142,7 +154,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
assert!(matches!( assert!(matches!(
*engine *engine
.eval::<Array>( .run(
" "
let x = [1]; let x = [1];
let y = [x, x]; let y = [x, x];
@ -220,7 +232,7 @@ fn test_max_map_size() -> Result<(), Box<EvalAltResult>> {
assert!(matches!( assert!(matches!(
*engine *engine
.eval::<Map>( .run(
" "
let x = #{a:1,b:2,c:3,d:4,e:5,f:6}; let x = #{a:1,b:2,c:3,d:4,e:5,f:6};
let y = #{g:7,h:8,i:9,j:10,k:11,l:12}; let y = #{g:7,h:8,i:9,j:10,k:11,l:12};
@ -233,7 +245,7 @@ fn test_max_map_size() -> Result<(), Box<EvalAltResult>> {
assert!(matches!( assert!(matches!(
*engine *engine
.eval::<Map>( .run(
" "
let x = #{a:1,b:2,c:3}; let x = #{a:1,b:2,c:3};
#{u:x, v:x, w:x, z:x} #{u:x, v:x, w:x, z:x}
@ -246,7 +258,7 @@ fn test_max_map_size() -> Result<(), Box<EvalAltResult>> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
assert!(matches!( assert!(matches!(
*engine *engine
.eval::<Map>( .run(
" "
let x = [1, 2, 3]; let x = [1, 2, 3];
#{u:x, v:x, w:x, z:x} #{u:x, v:x, w:x, z:x}

View File

@ -6,6 +6,20 @@ fn test_eval() -> Result<(), Box<EvalAltResult>> {
assert_eq!(engine.eval::<INT>(r#"eval("40 + 2")"#)?, 42); assert_eq!(engine.eval::<INT>(r#"eval("40 + 2")"#)?, 42);
assert_eq!(
engine.eval::<INT>(
r#"
let foo = 42;
eval("let foo = 123");
eval("let xyz = 10");
foo + xyz
"#
)?,
133
);
Ok(()) Ok(())
} }
@ -18,9 +32,9 @@ fn test_eval_blocks() -> Result<(), Box<EvalAltResult>> {
r#" r#"
let x = 999; let x = 999;
eval("let x = x + 123"); eval("let x = x - 1000");
let y = if x > 0 { let y = if x < 0 {
eval("let x = 42"); eval("let x = 42");
x x
} else { } else {
@ -30,7 +44,73 @@ fn test_eval_blocks() -> Result<(), Box<EvalAltResult>> {
x + y x + y
"# "#
)?, )?,
1164 41
);
assert_eq!(
engine.eval::<INT>(
r#"
let foo = 42;
eval("{ let foo = 123; }");
foo
"#
)?,
42
);
assert_eq!(
engine.eval::<INT>(
r#"
let foo = 42;
{ { {
eval("let foo = 123");
} } }
foo
"#
)?,
42
);
Ok(())
}
#[cfg(not(feature = "no_function"))]
#[cfg(not(feature = "no_module"))]
#[test]
fn test_eval_globals() -> Result<(), Box<EvalAltResult>> {
let engine = Engine::new();
assert_eq!(
engine.eval::<INT>(
r#"
const XYZ = 123;
fn foo() { global::XYZ }
{
eval("const XYZ = 42;");
}
foo()
"#
)?,
123
);
assert_eq!(
engine.eval::<INT>(
r#"
const XYZ = 123;
fn foo() { global::XYZ }
eval("const XYZ = 42;");
foo()
"#
)?,
42
); );
Ok(()) Ok(())

View File

@ -371,3 +371,32 @@ fn test_for_module_iterator() -> Result<(), Box<EvalAltResult>> {
Ok(()) Ok(())
} }
#[test]
#[cfg(not(feature = "no_index"))]
#[cfg(not(feature = "no_closure"))]
fn test_for_capture() -> Result<(), Box<EvalAltResult>> {
let engine = Engine::new();
assert_eq!(
engine.eval::<INT>(
"
let a = [];
for (x, i) in 100..110 {
a += || i + x;
}
let sum = 0;
for fp in a {
sum += call(fp);
}
sum
"
)?,
1180
);
Ok(())
}

View File

@ -181,5 +181,43 @@ fn test_functions_bang() -> Result<(), Box<EvalAltResult>> {
123 123
); );
assert_eq!(
engine.eval::<INT>(
"
fn foo() {
let hello = bar + 42;
}
let bar = 999;
let hello = 123;
foo!();
hello
",
)?,
123
);
assert_eq!(
engine.eval::<INT>(
r#"
fn foo(x) {
let hello = bar + 42 + x;
}
let bar = 999;
let hello = 123;
let f = Fn("foo");
call!(f, 1);
hello
"#,
)?,
123
);
Ok(()) Ok(())
} }

View File

@ -27,6 +27,11 @@ fn test_hex_literal() -> Result<(), Box<EvalAltResult>> {
assert_eq!(engine.eval::<INT>("let x = 0Xf; x")?, 15); assert_eq!(engine.eval::<INT>("let x = 0Xf; x")?, 15);
assert_eq!(engine.eval::<INT>("let x = 0xff; x")?, 255); assert_eq!(engine.eval::<INT>("let x = 0xff; x")?, 255);
#[cfg(not(feature = "only_i32"))]
assert_eq!(engine.eval::<INT>("let x = 0xffffffffffffffff; x")?, -1);
#[cfg(feature = "only_i32")]
assert_eq!(engine.eval::<INT>("let x = 0xffffffff; x")?, -1);
Ok(()) Ok(())
} }
@ -51,6 +56,18 @@ fn test_binary_literal() -> Result<(), Box<EvalAltResult>> {
engine.eval::<INT>("let x = 0b0011_1100_1010_0101; x")?, engine.eval::<INT>("let x = 0b0011_1100_1010_0101; x")?,
15525 15525
); );
#[cfg(not(feature = "only_i32"))]
assert_eq!(
engine.eval::<INT>(
"let x = 0b11111111_11111111_11111111_11111111_11111111_11111111_11111111_11111111; x"
)?,
-1
);
#[cfg(feature = "only_i32")]
assert_eq!(
engine.eval::<INT>("let x = 0b11111111_11111111_11111111_11111111; x")?,
-1
);
Ok(()) Ok(())
} }

View File

@ -10,8 +10,10 @@ fn test_ops() -> Result<(), Box<EvalAltResult>> {
Ok(()) Ok(())
} }
#[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))]
#[test] #[test]
fn test_ops_numbers() -> Result<(), Box<EvalAltResult>> { fn test_ops_other_number_types() -> Result<(), Box<EvalAltResult>> {
let engine = Engine::new(); let engine = Engine::new();
let mut scope = Scope::new(); let mut scope = Scope::new();

Some files were not shown because too many files have changed in this diff Show More