Merge pull request #295 from schungx/master
Update comments with links, do loops and fix custom syntax variables searching.
This commit is contained in:
commit
082ca81c30
21
RELEASES.md
21
RELEASES.md
@ -7,20 +7,35 @@ Version 0.19.6
|
|||||||
This version adds the `switch` statement.
|
This version adds the `switch` statement.
|
||||||
|
|
||||||
It also allows exposing selected module functions (usually methods) to the global namespace.
|
It also allows exposing selected module functions (usually methods) to the global namespace.
|
||||||
|
This is very convenient when encapsulating the API of a custom Rust type into a module while having methods
|
||||||
|
and iterators registered on the custom type work normally.
|
||||||
|
|
||||||
|
Bug fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
* Custom syntax that introduces a shadowing variable now works properly.
|
||||||
|
|
||||||
|
Breaking changes
|
||||||
|
----------------
|
||||||
|
|
||||||
|
* `Module::set_fn`, `Module::set_raw_fn` and `Module::set_fn_XXX_mut` all take an additional parameter of `FnNamespace`.
|
||||||
|
* `unless` is now a reserved keyword.
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
|
|
||||||
* `switch` statement.
|
* `switch` statement.
|
||||||
|
* `do ... while` and `do ... until` statement.
|
||||||
* `Engine::register_module` to register a module as a sub-module in the global namespace.
|
* `Engine::register_module` to register a module as a sub-module in the global namespace.
|
||||||
* `Module::get_fn_namespace` and `Module::set_fn_namespace` can expose a module function to the global namespace. This is convenient when registering an API for a custom type.
|
|
||||||
* `set_exported_global_fn!` macro to register a plugin function and expose it to the global namespace.
|
* `set_exported_global_fn!` macro to register a plugin function and expose it to the global namespace.
|
||||||
* `#[rhai_fn(gobal)]` and `#[rhai_fn(internal)]` attributes to determine whether a function defined in a plugin module should be exposed to the global namespace. This is convenient when defining an API for a custom type.
|
* `Module::set_fn_XXX_mut` can expose a module function to the global namespace. This is convenient when registering an API for a custom type.
|
||||||
|
* `Module::set_getter_fn`, `Module::set_setter_fn`, `Module::set_indexer_get_fn`, `Module::set_indexer_set_fn` all expose the function to the global namespace by default. This is convenient when registering an API for a custom type.
|
||||||
|
* `#[rhai_fn(global)]` and `#[rhai_fn(internal)]` attributes to determine whether a function defined in a plugin module should be exposed to the global namespace. This is convenient when defining an API for a custom type.
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
------------
|
------------
|
||||||
|
|
||||||
* New constant `Dynamic::UNIT`.
|
* New constants under `Dynamic` including `UNIT`, `TRUE`, `FALSE`, `ZERO`, `ONE` etc.
|
||||||
|
|
||||||
|
|
||||||
Version 0.19.5
|
Version 0.19.5
|
||||||
|
@ -26,8 +26,7 @@ fn bench_eval_module(bench: &mut Bencher) {
|
|||||||
.compile(
|
.compile(
|
||||||
r#"
|
r#"
|
||||||
fn foo(x) { x - 1 }
|
fn foo(x) { x - 1 }
|
||||||
import "testing" as t;
|
testing::bar(41)
|
||||||
t::bar(41)
|
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -215,14 +215,18 @@ impl ExportedParams for ExportedFnParams {
|
|||||||
return Err(syn::Error::new(s.span(), "extraneous value"))
|
return Err(syn::Error::new(s.span(), "extraneous value"))
|
||||||
}
|
}
|
||||||
("global", None) => {
|
("global", None) => {
|
||||||
if namespace.is_some() {
|
if let Some(ns) = namespace {
|
||||||
return Err(syn::Error::new(key.span(), "conflicting namespace"));
|
if ns != FnNamespaceAccess::Global {
|
||||||
|
return Err(syn::Error::new(key.span(), "conflicting namespace"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
namespace = Some(FnNamespaceAccess::Global);
|
namespace = Some(FnNamespaceAccess::Global);
|
||||||
}
|
}
|
||||||
("internal", None) => {
|
("internal", None) => {
|
||||||
if namespace.is_some() {
|
if let Some(ns) = namespace {
|
||||||
return Err(syn::Error::new(key.span(), "conflicting namespace"));
|
if ns != FnNamespaceAccess::Internal {
|
||||||
|
return Err(syn::Error::new(key.span(), "conflicting namespace"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
namespace = Some(FnNamespaceAccess::Internal);
|
namespace = Some(FnNamespaceAccess::Internal);
|
||||||
}
|
}
|
||||||
|
@ -129,17 +129,18 @@ pub(crate) fn generate_body(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for fn_literal in reg_names {
|
for fn_literal in reg_names {
|
||||||
set_fn_stmts.push(
|
let ns_str = syn::Ident::new(
|
||||||
match namespace {
|
match namespace {
|
||||||
FnNamespaceAccess::Global => syn::parse2::<syn::Stmt>(quote! {
|
FnNamespaceAccess::Global => "Global",
|
||||||
m.set_fn(#fn_literal, FnNamespace::Global, FnAccess::Public, &[#(#fn_input_types),*],
|
FnNamespaceAccess::Internal => "Internal",
|
||||||
#fn_token_name().into());
|
},
|
||||||
}),
|
fn_literal.span(),
|
||||||
FnNamespaceAccess::Internal => syn::parse2::<syn::Stmt>(quote! {
|
);
|
||||||
m.set_fn(#fn_literal, FnNamespace::Internal, FnAccess::Public, &[#(#fn_input_types),*],
|
set_fn_stmts.push(
|
||||||
#fn_token_name().into());
|
syn::parse2::<syn::Stmt>(quote! {
|
||||||
}),
|
m.set_fn(#fn_literal, FnNamespace::#ns_str, FnAccess::Public, &[#(#fn_input_types),*],
|
||||||
}
|
#fn_token_name().into());
|
||||||
|
})
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ pub struct Point {
|
|||||||
#[export_module]
|
#[export_module]
|
||||||
pub mod test_module {
|
pub mod test_module {
|
||||||
pub use super::Point;
|
pub use super::Point;
|
||||||
#[rhai_fn(global, global)]
|
#[rhai_fn(global, internal)]
|
||||||
pub fn test_fn(input: Point) -> bool {
|
pub fn test_fn(input: Point) -> bool {
|
||||||
input.x > input.y
|
input.x > input.y
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
error: conflicting namespace
|
error: conflicting namespace
|
||||||
--> $DIR/rhai_fn_global_multiple.rs:12:23
|
--> $DIR/rhai_fn_global_multiple.rs:12:23
|
||||||
|
|
|
|
||||||
12 | #[rhai_fn(global, global)]
|
12 | #[rhai_fn(global, internal)]
|
||||||
| ^^^^^^
|
| ^^^^^^^^
|
||||||
|
|
||||||
error[E0433]: failed to resolve: use of undeclared crate or module `test_module`
|
error[E0433]: failed to resolve: use of undeclared crate or module `test_module`
|
||||||
--> $DIR/rhai_fn_global_multiple.rs:23:8
|
--> $DIR/rhai_fn_global_multiple.rs:23:8
|
||||||
|
@ -79,6 +79,7 @@ The Rhai Scripting Language
|
|||||||
9. [If Statement](language/if.md)
|
9. [If Statement](language/if.md)
|
||||||
10. [Switch Expression](language/switch.md)
|
10. [Switch Expression](language/switch.md)
|
||||||
11. [While Loop](language/while.md)
|
11. [While Loop](language/while.md)
|
||||||
|
11. [Do Loop](language/do.md)
|
||||||
12. [Loop Statement](language/loop.md)
|
12. [Loop Statement](language/loop.md)
|
||||||
13. [For Loop](language/for.md)
|
13. [For Loop](language/for.md)
|
||||||
14. [Return Values](language/return.md)
|
14. [Return Values](language/return.md)
|
||||||
|
@ -14,7 +14,9 @@ Keywords List
|
|||||||
| `if` | if statement | | no | |
|
| `if` | if statement | | no | |
|
||||||
| `else` | else block of if statement | | no | |
|
| `else` | else block of if statement | | no | |
|
||||||
| `switch` | matching | | no | |
|
| `switch` | matching | | no | |
|
||||||
| `while` | while loop | | no | |
|
| `do` | looping | | no | |
|
||||||
|
| `while` | 1) while loop<br/>2) condition for do loop | | no | |
|
||||||
|
| `until` | do loop | | no | |
|
||||||
| `loop` | infinite loop | | no | |
|
| `loop` | infinite loop | | no | |
|
||||||
| `for` | for loop | | no | |
|
| `for` | for loop | | no | |
|
||||||
| `in` | 1) containment test<br/>2) part of for loop | | no | |
|
| `in` | 1) containment test<br/>2) part of for loop | | no | |
|
||||||
@ -48,11 +50,11 @@ Reserved Keywords
|
|||||||
| `var` | variable declaration |
|
| `var` | variable declaration |
|
||||||
| `static` | variable declaration |
|
| `static` | variable declaration |
|
||||||
| `shared` | share value |
|
| `shared` | share value |
|
||||||
| `do` | looping |
|
|
||||||
| `each` | looping |
|
| `each` | looping |
|
||||||
| `then` | control flow |
|
| `then` | control flow |
|
||||||
| `goto` | control flow |
|
| `goto` | control flow |
|
||||||
| `exit` | control flow |
|
| `exit` | control flow |
|
||||||
|
| `unless` | control flow |
|
||||||
| `match` | matching |
|
| `match` | matching |
|
||||||
| `case` | matching |
|
| `case` | matching |
|
||||||
| `public` | function/field access |
|
| `public` | function/field access |
|
||||||
|
@ -296,7 +296,7 @@ engine.register_custom_syntax_raw(
|
|||||||
"cleanup" => Ok(None),
|
"cleanup" => Ok(None),
|
||||||
cmd => Err(ParseError(Box::new(ParseErrorType::BadInput(
|
cmd => Err(ParseError(Box::new(ParseErrorType::BadInput(
|
||||||
LexError::ImproperSymbol(format!("Improper command: {}", cmd))
|
LexError::ImproperSymbol(format!("Improper command: {}", cmd))
|
||||||
)), NO_POS)),
|
)), Position::NONE)),
|
||||||
},
|
},
|
||||||
// perform command arg ...
|
// perform command arg ...
|
||||||
3 => match (stream[1].as_str(), stream[2].as_str()) {
|
3 => match (stream[1].as_str(), stream[2].as_str()) {
|
||||||
@ -311,7 +311,7 @@ engine.register_custom_syntax_raw(
|
|||||||
LexError::ImproperSymbol(
|
LexError::ImproperSymbol(
|
||||||
format!("Invalid argument for command {}: {}", cmd, arg)
|
format!("Invalid argument for command {}: {}", cmd, arg)
|
||||||
)
|
)
|
||||||
)), NO_POS)),
|
)), Position::NONE)),
|
||||||
},
|
},
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
@ -336,8 +336,8 @@ where:
|
|||||||
|
|
||||||
The return value is `Result<Option<String>, ParseError>` where:
|
The return value is `Result<Option<String>, ParseError>` where:
|
||||||
|
|
||||||
| Value | Description |
|
| Value | Description |
|
||||||
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `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))` | next symbol to match, which can also be `"$expr$"`, `"$ident$"` or `"$block$"` |
|
| `Ok(Some(symbol))` | next symbol to match, which can also be `"$expr$"`, `"$ident$"` or `"$block$"` |
|
||||||
| `Err(ParseError)` | error that is reflected back to the [`Engine`].<br/>Normally this is `ParseError(ParseErrorType::BadInput(LexError::ImproperSymbol(message)), NO_POS)` to indicate that there is a syntax error, but it can be any `ParseError`. |
|
| `Err(ParseError)` | error that is reflected back to the [`Engine`].<br/>Normally this is `ParseError(ParseErrorType::BadInput(LexError::ImproperSymbol(message)), Position::NONE)` to indicate that there is a syntax error, but it can be any `ParseError`. |
|
||||||
|
@ -21,11 +21,11 @@ engine.on_var(|name, index, context| {
|
|||||||
"MYSTIC_NUMBER" => Ok(Some((42 as INT).into())),
|
"MYSTIC_NUMBER" => Ok(Some((42 as INT).into())),
|
||||||
// Override a variable - make it not found even if it exists!
|
// Override a variable - make it not found even if it exists!
|
||||||
"DO_NOT_USE" => Err(Box::new(
|
"DO_NOT_USE" => Err(Box::new(
|
||||||
EvalAltResult::ErrorVariableNotFound(name.to_string(), NO_POS)
|
EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::NONE)
|
||||||
)),
|
)),
|
||||||
// Silently maps 'chameleon' into 'innocent'.
|
// Silently maps 'chameleon' into 'innocent'.
|
||||||
"chameleon" => context.scope.get_value("innocent").map(Some).ok_or_else(|| Box::new(
|
"chameleon" => context.scope.get_value("innocent").map(Some).ok_or_else(|| Box::new(
|
||||||
EvalAltResult::ErrorVariableNotFound(name.to_string(), NO_POS)
|
EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::NONE)
|
||||||
)),
|
)),
|
||||||
// Return Ok(None) to continue with the normal variable resolution process.
|
// Return Ok(None) to continue with the normal variable resolution process.
|
||||||
_ => Ok(None)
|
_ => Ok(None)
|
||||||
@ -83,8 +83,8 @@ where:
|
|||||||
|
|
||||||
The return value is `Result<Option<Dynamic>, Box<EvalAltResult>>` where:
|
The return value is `Result<Option<Dynamic>, Box<EvalAltResult>>` where:
|
||||||
|
|
||||||
| Value | Description |
|
| Value | Description |
|
||||||
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `Ok(None)` | normal variable resolution process should continue, i.e. continue searching through the [`Scope`] |
|
| `Ok(None)` | normal variable resolution process should continue, i.e. continue searching through the [`Scope`] |
|
||||||
| `Ok(Some(Dynamic))` | value of the variable, treated as a constant |
|
| `Ok(Some(Dynamic))` | value of the variable, treated as a constant |
|
||||||
| `Err(Box<EvalAltResult>)` | error that is reflected back to the [`Engine`].<br/>Normally this is `EvalAltResult::ErrorVariableNotFound(var_name, NO_POS)` to indicate that the variable does not exist, but it can be any `EvalAltResult`. |
|
| `Err(Box<EvalAltResult>)` | error that is reflected back to the [`Engine`].<br/>Normally this is `EvalAltResult::ErrorVariableNotFound(var_name, Position::NONE)` to indicate that the variable does not exist, but it can be any `EvalAltResult`. |
|
||||||
|
28
doc/src/language/do.md
Normal file
28
doc/src/language/do.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
`do` Loop
|
||||||
|
=========
|
||||||
|
|
||||||
|
{{#include ../links.md}}
|
||||||
|
|
||||||
|
`do` loops have two opposite variants: `do` ... `while` and `do` ... `until`.
|
||||||
|
|
||||||
|
Like the `while` loop, `continue` can be used to skip to the next iteration, by-passing all following statements;
|
||||||
|
`break` can be used to break out of the loop unconditionally.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let x = 10;
|
||||||
|
|
||||||
|
do {
|
||||||
|
x -= 1;
|
||||||
|
if x < 6 { continue; } // skip to the next iteration
|
||||||
|
print(x);
|
||||||
|
if x == 5 { break; } // break out of do loop
|
||||||
|
} while x > 0;
|
||||||
|
|
||||||
|
|
||||||
|
do {
|
||||||
|
x -= 1;
|
||||||
|
if x < 6 { continue; } // skip to the next iteration
|
||||||
|
print(x);
|
||||||
|
if x == 5 { break; } // break out of do loop
|
||||||
|
} until x == 0;
|
||||||
|
```
|
@ -118,7 +118,7 @@ pub fn greet(context: NativeCallContext, callback: FnPtr)
|
|||||||
The native call context is also useful in another scenario: protecting a function from malicious scripts.
|
The native call context is also useful in another scenario: protecting a function from malicious scripts.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rhai::{Dynamic, Array, NativeCallContext, EvalAltResult, NO_POS};
|
use rhai::{Dynamic, Array, NativeCallContext, EvalAltResult, Position};
|
||||||
use rhai::plugin::*; // a "prelude" import for macros
|
use rhai::plugin::*; // a "prelude" import for macros
|
||||||
|
|
||||||
// This function builds an array of arbitrary size, but is protected
|
// This function builds an array of arbitrary size, but is protected
|
||||||
@ -138,7 +138,7 @@ pub fn grow(context: NativeCallContext, size: i64)
|
|||||||
"Size to grow".to_string(),
|
"Size to grow".to_string(),
|
||||||
context.engine().max_array_size(),
|
context.engine().max_array_size(),
|
||||||
size as usize,
|
size as usize,
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
).into();
|
).into();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -428,7 +428,7 @@ mod my_module {
|
|||||||
The native call context is also useful in another scenario: protecting a function from malicious scripts.
|
The native call context is also useful in another scenario: protecting a function from malicious scripts.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rhai::{Dynamic, Array, NativeCallContext, EvalAltResult, NO_POS};
|
use rhai::{Dynamic, Array, NativeCallContext, EvalAltResult, Position};
|
||||||
use rhai::plugin::*; // a "prelude" import for macros
|
use rhai::plugin::*; // a "prelude" import for macros
|
||||||
|
|
||||||
#[export_module]
|
#[export_module]
|
||||||
@ -449,7 +449,7 @@ mod my_module {
|
|||||||
"Size to grow".to_string(),
|
"Size to grow".to_string(),
|
||||||
context.engine().max_array_size(),
|
context.engine().max_array_size(),
|
||||||
size as usize,
|
size as usize,
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
).into();
|
).into();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ engine.register_module("calc", module);
|
|||||||
engine.eval::<i64>("calc::inc(41)")? == 42; // refer to the 'Calc' module
|
engine.eval::<i64>("calc::inc(41)")? == 42; // refer to the 'Calc' module
|
||||||
```
|
```
|
||||||
|
|
||||||
`Module::set_fn_namespace` can expose functions (usually _methods_) in the module
|
`Module::set_fn_XXX_mut` can expose functions (usually _methods_) in the module
|
||||||
to the _global_ namespace, so [getters/setters] and [indexers] for [custom types] can work as expected.
|
to the _global_ namespace, so [getters/setters] and [indexers] for [custom types] can work as expected.
|
||||||
|
|
||||||
Type iterators, because of their special nature, are always exposed to the _global_ namespace.
|
Type iterators, because of their special nature, are always exposed to the _global_ namespace.
|
||||||
@ -69,13 +69,12 @@ Type iterators, because of their special nature, are always exposed to the _glob
|
|||||||
use rhai::{Engine, Module, FnNamespace};
|
use rhai::{Engine, Module, FnNamespace};
|
||||||
|
|
||||||
let mut module = Module::new(); // new module
|
let mut module = Module::new(); // new module
|
||||||
let hash = module.set_fn_1_mut("inc", // add new method
|
|
||||||
|
// Expose method 'inc' to the global namespace (default is 'Internal')
|
||||||
|
module.set_fn_1_mut("inc", FnNamespace::Global,
|
||||||
|x: &mut i64| Ok(x+1)
|
|x: &mut i64| Ok(x+1)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Expose 'inc' to the global namespace (default is 'Internal')
|
|
||||||
module.set_fn_namespace(hash, FnNamespace::Global);
|
|
||||||
|
|
||||||
// Load the module into the Engine as a sub-module named 'calc'
|
// Load the module into the Engine as a sub-module named 'calc'
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
engine.register_module("calc", module);
|
engine.register_module("calc", module);
|
||||||
|
243
src/ast.rs
243
src/ast.rs
@ -1,6 +1,7 @@
|
|||||||
//! Module defining the AST (abstract syntax tree).
|
//! Module defining the AST (abstract syntax tree).
|
||||||
|
|
||||||
use crate::dynamic::Union;
|
use crate::dynamic::Union;
|
||||||
|
use crate::fn_native::shared_make_mut;
|
||||||
use crate::module::NamespaceRef;
|
use crate::module::NamespaceRef;
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
@ -18,7 +19,7 @@ use crate::syntax::FnCustomSyntaxEval;
|
|||||||
use crate::token::Token;
|
use crate::token::Token;
|
||||||
use crate::utils::StraightHasherBuilder;
|
use crate::utils::StraightHasherBuilder;
|
||||||
use crate::{
|
use crate::{
|
||||||
Dynamic, FnNamespace, FnPtr, ImmutableString, Module, Position, Shared, StaticVec, INT, NO_POS,
|
Dynamic, FnNamespace, FnPtr, ImmutableString, Module, Position, Shared, StaticVec, INT,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
@ -58,7 +59,7 @@ impl FnAccess {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _[INTERNALS]_ A type containing information on a scripted function.
|
/// _(INTERNALS)_ A type containing information on a scripted function.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// ## WARNING
|
/// ## WARNING
|
||||||
@ -108,13 +109,13 @@ impl fmt::Display for ScriptFnDef {
|
|||||||
///
|
///
|
||||||
/// # Thread Safety
|
/// # Thread Safety
|
||||||
///
|
///
|
||||||
/// Currently, `AST` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`.
|
/// Currently, [`AST`] is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct AST(
|
pub struct AST(
|
||||||
/// Global statements.
|
/// Global statements.
|
||||||
Vec<Stmt>,
|
Vec<Stmt>,
|
||||||
/// Script-defined functions.
|
/// Script-defined functions.
|
||||||
Module,
|
Shared<Module>,
|
||||||
);
|
);
|
||||||
|
|
||||||
impl Default for AST {
|
impl Default for AST {
|
||||||
@ -124,10 +125,10 @@ impl Default for AST {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AST {
|
impl AST {
|
||||||
/// Create a new `AST`.
|
/// Create a new [`AST`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new(statements: impl IntoIterator<Item = Stmt>, lib: Module) -> Self {
|
pub fn new(statements: impl IntoIterator<Item = Stmt>, lib: impl Into<Shared<Module>>) -> Self {
|
||||||
Self(statements.into_iter().collect(), lib)
|
Self(statements.into_iter().collect(), lib.into())
|
||||||
}
|
}
|
||||||
/// Get the statements.
|
/// Get the statements.
|
||||||
#[cfg(not(feature = "internals"))]
|
#[cfg(not(feature = "internals"))]
|
||||||
@ -135,7 +136,7 @@ impl AST {
|
|||||||
pub(crate) fn statements(&self) -> &[Stmt] {
|
pub(crate) fn statements(&self) -> &[Stmt] {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
/// _[INTERNALS]_ Get the statements.
|
/// _(INTERNALS)_ Get the statements.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[deprecated(note = "this method is volatile and may change")]
|
#[deprecated(note = "this method is volatile and may change")]
|
||||||
@ -149,13 +150,18 @@ impl AST {
|
|||||||
pub(crate) fn statements_mut(&mut self) -> &mut Vec<Stmt> {
|
pub(crate) fn statements_mut(&mut self) -> &mut Vec<Stmt> {
|
||||||
&mut self.0
|
&mut self.0
|
||||||
}
|
}
|
||||||
/// Get the internal `Module` containing all script-defined functions.
|
/// Get the internal shared [`Module`] containing all script-defined functions.
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn shared_lib(&self) -> Shared<Module> {
|
||||||
|
self.1.clone()
|
||||||
|
}
|
||||||
|
/// Get the internal [`Module`] containing all script-defined functions.
|
||||||
#[cfg(not(feature = "internals"))]
|
#[cfg(not(feature = "internals"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn lib(&self) -> &Module {
|
pub(crate) fn lib(&self) -> &Module {
|
||||||
&self.1
|
&self.1
|
||||||
}
|
}
|
||||||
/// _[INTERNALS]_ Get the internal `Module` containing all script-defined functions.
|
/// _(INTERNALS)_ Get the internal [`Module`] containing all script-defined functions.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[deprecated(note = "this method is volatile and may change")]
|
#[deprecated(note = "this method is volatile and may change")]
|
||||||
@ -163,7 +169,7 @@ impl AST {
|
|||||||
pub fn lib(&self) -> &Module {
|
pub fn lib(&self) -> &Module {
|
||||||
&self.1
|
&self.1
|
||||||
}
|
}
|
||||||
/// Clone the `AST`'s functions into a new `AST`.
|
/// Clone the [`AST`]'s functions into a new [`AST`].
|
||||||
/// No statements are cloned.
|
/// No statements are cloned.
|
||||||
///
|
///
|
||||||
/// This operation is cheap because functions are shared.
|
/// This operation is cheap because functions are shared.
|
||||||
@ -172,7 +178,7 @@ impl AST {
|
|||||||
pub fn clone_functions_only(&self) -> Self {
|
pub fn clone_functions_only(&self) -> Self {
|
||||||
self.clone_functions_only_filtered(|_, _, _, _, _| true)
|
self.clone_functions_only_filtered(|_, _, _, _, _| true)
|
||||||
}
|
}
|
||||||
/// Clone the `AST`'s functions into a new `AST` based on a filter predicate.
|
/// Clone the [`AST`]'s functions into a new [`AST`] based on a filter predicate.
|
||||||
/// No statements are cloned.
|
/// No statements are cloned.
|
||||||
///
|
///
|
||||||
/// This operation is cheap because functions are shared.
|
/// This operation is cheap because functions are shared.
|
||||||
@ -184,24 +190,24 @@ impl AST {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
let mut functions: Module = Default::default();
|
let mut functions: Module = Default::default();
|
||||||
functions.merge_filtered(&self.1, &mut filter);
|
functions.merge_filtered(&self.1, &mut filter);
|
||||||
Self(Default::default(), functions)
|
Self(Default::default(), functions.into())
|
||||||
}
|
}
|
||||||
/// Clone the `AST`'s script statements into a new `AST`.
|
/// Clone the [`AST`]'s script statements into a new [`AST`].
|
||||||
/// No functions are cloned.
|
/// No functions are cloned.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn clone_statements_only(&self) -> Self {
|
pub fn clone_statements_only(&self) -> Self {
|
||||||
Self(self.0.clone(), Default::default())
|
Self(self.0.clone(), Default::default())
|
||||||
}
|
}
|
||||||
/// Merge two `AST` into one. Both `AST`'s are untouched and a new, merged, version
|
/// Merge two [`AST`] into one. Both [`AST`]'s are untouched and a new, merged, version
|
||||||
/// is returned.
|
/// is returned.
|
||||||
///
|
///
|
||||||
/// Statements in the second `AST` are simply appended to the end of the first _without any processing_.
|
/// 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.
|
/// 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
|
/// Of course, if the first [`AST`] uses a `return` statement at the end, then
|
||||||
/// the second `AST` will essentially be dead code.
|
/// the second [`AST`] will essentially be dead code.
|
||||||
///
|
///
|
||||||
/// All script-defined functions in the second `AST` overwrite similarly-named functions
|
/// All script-defined functions in the second [`AST`] overwrite similarly-named functions
|
||||||
/// in the first `AST` with the same number of parameters.
|
/// in the first [`AST`] with the same number of parameters.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -245,15 +251,15 @@ impl AST {
|
|||||||
pub fn merge(&self, other: &Self) -> Self {
|
pub fn merge(&self, other: &Self) -> Self {
|
||||||
self.merge_filtered(other, |_, _, _, _, _| true)
|
self.merge_filtered(other, |_, _, _, _, _| true)
|
||||||
}
|
}
|
||||||
/// Combine one `AST` with another. The second `AST` is consumed.
|
/// 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_.
|
/// 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.
|
/// 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
|
/// Of course, if the first [`AST`] uses a `return` statement at the end, then
|
||||||
/// the second `AST` will essentially be dead code.
|
/// the second [`AST`] will essentially be dead code.
|
||||||
///
|
///
|
||||||
/// All script-defined functions in the second `AST` overwrite similarly-named functions
|
/// All script-defined functions in the second [`AST`] overwrite similarly-named functions
|
||||||
/// in the first `AST` with the same number of parameters.
|
/// in the first [`AST`] with the same number of parameters.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -297,16 +303,16 @@ impl AST {
|
|||||||
pub fn combine(&mut self, other: Self) -> &mut Self {
|
pub fn combine(&mut self, other: Self) -> &mut Self {
|
||||||
self.combine_filtered(other, |_, _, _, _, _| true)
|
self.combine_filtered(other, |_, _, _, _, _| true)
|
||||||
}
|
}
|
||||||
/// Merge two `AST` into one. Both `AST`'s are untouched and a new, merged, version
|
/// Merge two [`AST`] into one. Both [`AST`]'s are untouched and a new, merged, version
|
||||||
/// is returned.
|
/// is returned.
|
||||||
///
|
///
|
||||||
/// Statements in the second `AST` are simply appended to the end of the first _without any processing_.
|
/// 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.
|
/// 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
|
/// Of course, if the first [`AST`] uses a `return` statement at the end, then
|
||||||
/// the second `AST` will essentially be dead code.
|
/// the second [`AST`] will essentially be dead code.
|
||||||
///
|
///
|
||||||
/// All script-defined functions in the second `AST` are first selected based on a filter
|
/// 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
|
/// predicate, then overwrite similarly-named functions in the first [`AST`] with the
|
||||||
/// same number of parameters.
|
/// same number of parameters.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
@ -368,20 +374,20 @@ impl AST {
|
|||||||
(true, true) => vec![],
|
(true, true) => vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut functions = functions.clone();
|
let mut functions = functions.as_ref().clone();
|
||||||
functions.merge_filtered(&other.1, &mut filter);
|
functions.merge_filtered(&other.1, &mut filter);
|
||||||
|
|
||||||
Self::new(ast, functions)
|
Self::new(ast, functions)
|
||||||
}
|
}
|
||||||
/// Combine one `AST` with another. The second `AST` is consumed.
|
/// 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_.
|
/// 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.
|
/// 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
|
/// Of course, if the first [`AST`] uses a `return` statement at the end, then
|
||||||
/// the second `AST` will essentially be dead code.
|
/// the second [`AST`] will essentially be dead code.
|
||||||
///
|
///
|
||||||
/// All script-defined functions in the second `AST` are first selected based on a filter
|
/// 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
|
/// predicate, then overwrite similarly-named functions in the first [`AST`] with the
|
||||||
/// same number of parameters.
|
/// same number of parameters.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
@ -430,9 +436,10 @@ impl AST {
|
|||||||
other: Self,
|
other: Self,
|
||||||
mut filter: impl FnMut(FnNamespace, FnAccess, bool, &str, usize) -> bool,
|
mut filter: impl FnMut(FnNamespace, FnAccess, bool, &str, usize) -> bool,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
let Self(ref mut statements, ref mut functions) = self;
|
self.0.extend(other.0.into_iter());
|
||||||
statements.extend(other.0.into_iter());
|
if !other.1.is_empty() {
|
||||||
functions.merge_filtered(&other.1, &mut filter);
|
shared_make_mut(&mut self.1).merge_filtered(&other.1, &mut filter);
|
||||||
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Filter out the functions, retaining only some based on a filter predicate.
|
/// Filter out the functions, retaining only some based on a filter predicate.
|
||||||
@ -463,8 +470,11 @@ impl AST {
|
|||||||
pub fn retain_functions(
|
pub fn retain_functions(
|
||||||
&mut self,
|
&mut self,
|
||||||
filter: impl FnMut(FnNamespace, FnAccess, &str, usize) -> bool,
|
filter: impl FnMut(FnNamespace, FnAccess, &str, usize) -> bool,
|
||||||
) {
|
) -> &mut Self {
|
||||||
self.1.retain_script_functions(filter);
|
if !self.1.is_empty() {
|
||||||
|
shared_make_mut(&mut self.1).retain_script_functions(filter);
|
||||||
|
}
|
||||||
|
self
|
||||||
}
|
}
|
||||||
/// Iterate through all functions
|
/// Iterate through all functions
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
@ -474,13 +484,13 @@ impl AST {
|
|||||||
) -> impl Iterator<Item = (FnNamespace, FnAccess, &str, usize, Shared<ScriptFnDef>)> + 'a {
|
) -> impl Iterator<Item = (FnNamespace, FnAccess, &str, usize, Shared<ScriptFnDef>)> + 'a {
|
||||||
self.1.iter_script_fn()
|
self.1.iter_script_fn()
|
||||||
}
|
}
|
||||||
/// Clear all function definitions in the `AST`.
|
/// Clear all function definitions in the [`AST`].
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn clear_functions(&mut self) {
|
pub fn clear_functions(&mut self) {
|
||||||
self.1 = Default::default();
|
self.1 = Default::default();
|
||||||
}
|
}
|
||||||
/// Clear all statements in the `AST`, leaving only function definitions.
|
/// Clear all statements in the [`AST`], leaving only function definitions.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn clear_statements(&mut self) {
|
pub fn clear_statements(&mut self) {
|
||||||
self.0 = vec![];
|
self.0 = vec![];
|
||||||
@ -557,7 +567,7 @@ impl IdentX {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _[INTERNALS]_ A type encapsulating the mode of a `return`/`throw` statement.
|
/// _(INTERNALS)_ A type encapsulating the mode of a `return`/`throw` statement.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// ## WARNING
|
/// ## WARNING
|
||||||
@ -571,7 +581,7 @@ pub enum ReturnType {
|
|||||||
Exception,
|
Exception,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _[INTERNALS]_ A statement.
|
/// _(INTERNALS)_ A statement.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// ## WARNING
|
/// ## WARNING
|
||||||
@ -581,42 +591,42 @@ pub enum ReturnType {
|
|||||||
pub enum Stmt {
|
pub enum Stmt {
|
||||||
/// No-op.
|
/// No-op.
|
||||||
Noop(Position),
|
Noop(Position),
|
||||||
/// if expr { stmt } else { stmt }
|
/// `if` expr `{` stmt `}` `else` `{` stmt `}`
|
||||||
If(Expr, Box<(Stmt, Option<Stmt>)>, Position),
|
If(Expr, Box<(Stmt, Option<Stmt>)>, Position),
|
||||||
/// switch expr { literal or _ => stmt, ... }
|
/// `switch` expr `{` literal or _ `=>` stmt `,` ... `}`
|
||||||
Switch(
|
Switch(
|
||||||
Expr,
|
Expr,
|
||||||
Box<(HashMap<u64, Stmt, StraightHasherBuilder>, Option<Stmt>)>,
|
Box<(HashMap<u64, Stmt, StraightHasherBuilder>, Option<Stmt>)>,
|
||||||
Position,
|
Position,
|
||||||
),
|
),
|
||||||
/// while expr { stmt }
|
/// `while` expr `{` stmt `}`
|
||||||
While(Expr, Box<Stmt>, Position),
|
While(Expr, Box<Stmt>, Position),
|
||||||
/// loop { stmt }
|
/// `do` `{` stmt `}` `while`|`until` expr
|
||||||
Loop(Box<Stmt>, Position),
|
Do(Box<Stmt>, Expr, bool, Position),
|
||||||
/// for id in expr { stmt }
|
/// `for` id `in` expr `{` stmt `}`
|
||||||
For(Expr, Box<(String, Stmt)>, Position),
|
For(Expr, Box<(String, Stmt)>, Position),
|
||||||
/// [export] let id = expr
|
/// \[`export`\] `let` id `=` expr
|
||||||
Let(Box<Ident>, Option<Expr>, bool, Position),
|
Let(Box<Ident>, Option<Expr>, bool, Position),
|
||||||
/// [export] const id = expr
|
/// \[`export`\] `const` id `=` expr
|
||||||
Const(Box<Ident>, Option<Expr>, bool, Position),
|
Const(Box<Ident>, Option<Expr>, bool, Position),
|
||||||
/// expr op= expr
|
/// expr op`=` expr
|
||||||
Assignment(Box<(Expr, Cow<'static, str>, Expr)>, Position),
|
Assignment(Box<(Expr, Cow<'static, str>, Expr)>, Position),
|
||||||
/// { stmt; ... }
|
/// `{` stmt`;` ... `}`
|
||||||
Block(Vec<Stmt>, Position),
|
Block(Vec<Stmt>, Position),
|
||||||
/// try { stmt; ... } catch ( var ) { stmt; ... }
|
/// `try` `{` stmt; ... `}` `catch` `(` var `)` `{` stmt; ... `}`
|
||||||
TryCatch(Box<(Stmt, Option<Ident>, Stmt)>, Position, Position),
|
TryCatch(Box<(Stmt, Option<Ident>, Stmt)>, Position, Position),
|
||||||
/// expr
|
/// [expression][Expr]
|
||||||
Expr(Expr),
|
Expr(Expr),
|
||||||
/// continue
|
/// `continue`
|
||||||
Continue(Position),
|
Continue(Position),
|
||||||
/// break
|
/// `break`
|
||||||
Break(Position),
|
Break(Position),
|
||||||
/// return/throw
|
/// `return`/`throw`
|
||||||
Return((ReturnType, Position), Option<Expr>, Position),
|
Return((ReturnType, Position), Option<Expr>, Position),
|
||||||
/// import expr as var
|
/// `import` expr `as` var
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Import(Expr, Option<Box<IdentX>>, Position),
|
Import(Expr, Option<Box<IdentX>>, Position),
|
||||||
/// export var as var, ...
|
/// `export` var `as` var `,` ...
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Export(Vec<(IdentX, Option<IdentX>)>, Position),
|
Export(Vec<(IdentX, Option<IdentX>)>, Position),
|
||||||
/// Convert a variable to shared.
|
/// Convert a variable to shared.
|
||||||
@ -627,19 +637,19 @@ pub enum Stmt {
|
|||||||
impl Default for Stmt {
|
impl Default for Stmt {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::Noop(NO_POS)
|
Self::Noop(Position::NONE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stmt {
|
impl Stmt {
|
||||||
/// Is this statement `Noop`?
|
/// Is this statement [`Noop`][Stmt::Noop]?
|
||||||
pub fn is_noop(&self) -> bool {
|
pub fn is_noop(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Noop(_) => true,
|
Self::Noop(_) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get the `Position` of this statement.
|
/// Get the [`Position`] of this statement.
|
||||||
pub fn position(&self) -> Position {
|
pub fn position(&self) -> Position {
|
||||||
match self {
|
match self {
|
||||||
Self::Noop(pos)
|
Self::Noop(pos)
|
||||||
@ -650,7 +660,7 @@ impl Stmt {
|
|||||||
| Self::If(_, _, pos)
|
| Self::If(_, _, pos)
|
||||||
| Self::Switch(_, _, pos)
|
| Self::Switch(_, _, pos)
|
||||||
| Self::While(_, _, pos)
|
| Self::While(_, _, pos)
|
||||||
| Self::Loop(_, pos)
|
| Self::Do(_, _, _, pos)
|
||||||
| Self::For(_, _, pos)
|
| Self::For(_, _, pos)
|
||||||
| Self::Return((_, pos), _, _)
|
| Self::Return((_, pos), _, _)
|
||||||
| Self::Let(_, _, _, pos)
|
| Self::Let(_, _, _, pos)
|
||||||
@ -668,7 +678,7 @@ impl Stmt {
|
|||||||
Self::Share(x) => x.pos,
|
Self::Share(x) => x.pos,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Override the `Position` of this statement.
|
/// Override the [`Position`] of this statement.
|
||||||
pub fn set_position(&mut self, new_pos: Position) -> &mut Self {
|
pub fn set_position(&mut self, new_pos: Position) -> &mut Self {
|
||||||
match self {
|
match self {
|
||||||
Self::Noop(pos)
|
Self::Noop(pos)
|
||||||
@ -679,7 +689,7 @@ impl Stmt {
|
|||||||
| Self::If(_, _, pos)
|
| Self::If(_, _, pos)
|
||||||
| Self::Switch(_, _, pos)
|
| Self::Switch(_, _, pos)
|
||||||
| Self::While(_, _, pos)
|
| Self::While(_, _, pos)
|
||||||
| Self::Loop(_, pos)
|
| Self::Do(_, _, _, pos)
|
||||||
| Self::For(_, _, pos)
|
| Self::For(_, _, pos)
|
||||||
| Self::Return((_, pos), _, _)
|
| Self::Return((_, pos), _, _)
|
||||||
| Self::Let(_, _, _, pos)
|
| Self::Let(_, _, _, pos)
|
||||||
@ -707,7 +717,6 @@ impl Stmt {
|
|||||||
Self::If(_, _, _)
|
Self::If(_, _, _)
|
||||||
| Self::Switch(_, _, _)
|
| Self::Switch(_, _, _)
|
||||||
| Self::While(_, _, _)
|
| Self::While(_, _, _)
|
||||||
| Self::Loop(_, _)
|
|
||||||
| Self::For(_, _, _)
|
| Self::For(_, _, _)
|
||||||
| Self::Block(_, _)
|
| Self::Block(_, _)
|
||||||
| Self::TryCatch(_, _, _) => true,
|
| Self::TryCatch(_, _, _) => true,
|
||||||
@ -719,6 +728,7 @@ impl Stmt {
|
|||||||
| Self::Const(_, _, _, _)
|
| Self::Const(_, _, _, _)
|
||||||
| Self::Assignment(_, _)
|
| Self::Assignment(_, _)
|
||||||
| Self::Expr(_)
|
| Self::Expr(_)
|
||||||
|
| Self::Do(_, _, _, _)
|
||||||
| Self::Continue(_)
|
| Self::Continue(_)
|
||||||
| Self::Break(_)
|
| Self::Break(_)
|
||||||
| Self::Return(_, _, _) => false,
|
| Self::Return(_, _, _) => false,
|
||||||
@ -727,7 +737,7 @@ impl Stmt {
|
|||||||
Self::Import(_, _, _) | Self::Export(_, _) => false,
|
Self::Import(_, _, _) | Self::Export(_, _) => false,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Self::Share(_) => false,
|
Self::Share(_) => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Is this statement _pure_?
|
/// Is this statement _pure_?
|
||||||
@ -745,8 +755,9 @@ impl Stmt {
|
|||||||
&& x.0.values().all(Stmt::is_pure)
|
&& x.0.values().all(Stmt::is_pure)
|
||||||
&& x.1.as_ref().map(Stmt::is_pure).unwrap_or(true)
|
&& x.1.as_ref().map(Stmt::is_pure).unwrap_or(true)
|
||||||
}
|
}
|
||||||
Self::While(condition, block, _) => condition.is_pure() && block.is_pure(),
|
Self::While(condition, block, _) | Self::Do(block, condition, _, _) => {
|
||||||
Self::Loop(block, _) => block.is_pure(),
|
condition.is_pure() && block.is_pure()
|
||||||
|
}
|
||||||
Self::For(iterable, x, _) => iterable.is_pure() && x.1.is_pure(),
|
Self::For(iterable, x, _) => iterable.is_pure() && x.1.is_pure(),
|
||||||
Self::Let(_, _, _, _) | Self::Const(_, _, _, _) | Self::Assignment(_, _) => false,
|
Self::Let(_, _, _, _) | Self::Const(_, _, _, _) | Self::Assignment(_, _) => false,
|
||||||
Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()),
|
Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()),
|
||||||
@ -764,7 +775,7 @@ impl Stmt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _[INTERNALS]_ A custom syntax definition.
|
/// _(INTERNALS)_ A custom syntax definition.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// ## WARNING
|
/// ## WARNING
|
||||||
@ -786,19 +797,19 @@ impl fmt::Debug for CustomExpr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CustomExpr {
|
impl CustomExpr {
|
||||||
/// Get the keywords for this `CustomExpr`.
|
/// Get the keywords for this custom syntax.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn keywords(&self) -> &[Expr] {
|
pub fn keywords(&self) -> &[Expr] {
|
||||||
&self.keywords
|
&self.keywords
|
||||||
}
|
}
|
||||||
/// Get the implementation function for this `CustomExpr`.
|
/// Get the implementation function for this custom syntax.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn func(&self) -> &FnCustomSyntaxEval {
|
pub fn func(&self) -> &FnCustomSyntaxEval {
|
||||||
self.func.as_ref()
|
self.func.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _[INTERNALS]_ A binary expression.
|
/// _(INTERNALS)_ A binary expression.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// ## WARNING
|
/// ## WARNING
|
||||||
@ -812,7 +823,7 @@ pub struct BinaryExpr {
|
|||||||
pub rhs: Expr,
|
pub rhs: Expr,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _[INTERNALS]_ A function call.
|
/// _(INTERNALS)_ A function call.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// ## WARNING
|
/// ## WARNING
|
||||||
@ -822,25 +833,24 @@ pub struct BinaryExpr {
|
|||||||
pub struct FnCallExpr {
|
pub struct FnCallExpr {
|
||||||
/// Pre-calculated hash for a script-defined function of the same name and number of parameters.
|
/// Pre-calculated hash for a script-defined function of the same name and number of parameters.
|
||||||
pub hash: u64,
|
pub hash: u64,
|
||||||
/// Call native functions only? Set to `true` to skip searching for script-defined function overrides
|
/// Call native functions only? Set to [`true`] to skip searching for script-defined function overrides
|
||||||
/// when it is certain that the function must be native (e.g. an operator).
|
/// when it is certain that the function must be native (e.g. an operator).
|
||||||
pub native_only: bool,
|
pub native_only: bool,
|
||||||
/// Does this function call capture the parent scope?
|
/// Does this function call capture the parent scope?
|
||||||
pub capture: bool,
|
pub capture: bool,
|
||||||
/// Default value when the function is not found, mostly used to provide a default for comparison functions.
|
/// Default value when the function is not found, mostly used to provide a default for comparison functions.
|
||||||
/// Type is `bool` in order for `FnCallInfo` to be `Hash`
|
pub def_value: Option<Dynamic>,
|
||||||
pub def_value: Option<bool>,
|
|
||||||
/// Namespace of the function, if any. Boxed because it occurs rarely.
|
/// Namespace of the function, if any. Boxed because it occurs rarely.
|
||||||
pub namespace: Option<Box<NamespaceRef>>,
|
pub namespace: Option<Box<NamespaceRef>>,
|
||||||
/// Function name.
|
/// Function name.
|
||||||
/// Use `Cow<'static, str>` because a lot of operators (e.g. `==`, `>=`) are implemented as function calls
|
/// Use [`Cow<'static, str>`][Cow] because a lot of operators (e.g. `==`, `>=`) are implemented as
|
||||||
/// and the function names are predictable, so no need to allocate a new `String`.
|
/// function calls and the function names are predictable, so no need to allocate a new [`String`].
|
||||||
pub name: Cow<'static, str>,
|
pub name: Cow<'static, str>,
|
||||||
/// List of function call arguments.
|
/// List of function call arguments.
|
||||||
pub args: StaticVec<Expr>,
|
pub args: StaticVec<Expr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _[INTERNALS]_ An expression sub-tree.
|
/// _(INTERNALS)_ An expression sub-tree.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// ## WARNING
|
/// ## WARNING
|
||||||
@ -849,9 +859,11 @@ pub struct FnCallExpr {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Expr {
|
pub enum Expr {
|
||||||
/// Dynamic constant.
|
/// Dynamic constant.
|
||||||
/// Used to hold either an Array or Map literal for quick cloning.
|
/// Used to hold either an [`Array`] or [`Map`] literal for quick cloning.
|
||||||
/// All other primitive data types should use the appropriate variants for better speed.
|
/// All other primitive data types should use the appropriate variants for better speed.
|
||||||
DynamicConstant(Box<Dynamic>, Position),
|
DynamicConstant(Box<Dynamic>, Position),
|
||||||
|
/// Boolean constant.
|
||||||
|
BoolConstant(bool, Position),
|
||||||
/// Integer constant.
|
/// Integer constant.
|
||||||
IntegerConstant(INT, Position),
|
IntegerConstant(INT, Position),
|
||||||
/// Floating-point constant.
|
/// Floating-point constant.
|
||||||
@ -859,39 +871,35 @@ pub enum Expr {
|
|||||||
FloatConstant(FLOAT, Position),
|
FloatConstant(FLOAT, Position),
|
||||||
/// Character constant.
|
/// Character constant.
|
||||||
CharConstant(char, Position),
|
CharConstant(char, Position),
|
||||||
/// String constant.
|
/// [String][ImmutableString] constant.
|
||||||
StringConstant(ImmutableString, Position),
|
StringConstant(ImmutableString, Position),
|
||||||
/// FnPtr constant.
|
/// [`FnPtr`] constant.
|
||||||
FnPointer(ImmutableString, Position),
|
FnPointer(ImmutableString, Position),
|
||||||
/// [ expr, ... ]
|
/// [ expr, ... ]
|
||||||
Array(Box<StaticVec<Expr>>, Position),
|
Array(Box<StaticVec<Expr>>, Position),
|
||||||
/// #{ name:expr, ... }
|
/// #{ name:expr, ... }
|
||||||
Map(Box<StaticVec<(IdentX, Expr)>>, Position),
|
Map(Box<StaticVec<(IdentX, Expr)>>, Position),
|
||||||
/// true
|
|
||||||
True(Position),
|
|
||||||
/// false
|
|
||||||
False(Position),
|
|
||||||
/// ()
|
/// ()
|
||||||
Unit(Position),
|
Unit(Position),
|
||||||
/// Variable access - (optional index, optional modules, hash, variable name)
|
/// Variable access - (optional index, optional modules, hash, variable name)
|
||||||
Variable(Box<(Option<NonZeroUsize>, Option<Box<NamespaceRef>>, u64, IdentX)>),
|
Variable(Box<(Option<NonZeroUsize>, Option<Box<NamespaceRef>>, u64, IdentX)>),
|
||||||
/// Property access - (getter, setter), prop
|
/// Property access - (getter, setter), prop
|
||||||
Property(Box<((ImmutableString, ImmutableString), IdentX)>),
|
Property(Box<((ImmutableString, ImmutableString), IdentX)>),
|
||||||
/// { stmt }
|
/// { [statement][Stmt] }
|
||||||
Stmt(Box<StaticVec<Stmt>>, Position),
|
Stmt(Box<StaticVec<Stmt>>, Position),
|
||||||
/// Wrapped expression - should not be optimized away.
|
/// Wrapped [expression][`Expr`] - should not be optimized away.
|
||||||
Expr(Box<Expr>),
|
Expr(Box<Expr>),
|
||||||
/// func(expr, ... )
|
/// func `(` expr `,` ... `)`
|
||||||
FnCall(Box<FnCallExpr>, Position),
|
FnCall(Box<FnCallExpr>, Position),
|
||||||
/// lhs.rhs
|
/// lhs `.` rhs
|
||||||
Dot(Box<BinaryExpr>, Position),
|
Dot(Box<BinaryExpr>, Position),
|
||||||
/// expr[expr]
|
/// expr `[` expr `]`
|
||||||
Index(Box<BinaryExpr>, Position),
|
Index(Box<BinaryExpr>, Position),
|
||||||
/// lhs in rhs
|
/// lhs `in` rhs
|
||||||
In(Box<BinaryExpr>, Position),
|
In(Box<BinaryExpr>, Position),
|
||||||
/// lhs && rhs
|
/// lhs `&&` rhs
|
||||||
And(Box<BinaryExpr>, Position),
|
And(Box<BinaryExpr>, Position),
|
||||||
/// lhs || rhs
|
/// lhs `||` rhs
|
||||||
Or(Box<BinaryExpr>, Position),
|
Or(Box<BinaryExpr>, Position),
|
||||||
/// Custom syntax
|
/// Custom syntax
|
||||||
Custom(Box<CustomExpr>, Position),
|
Custom(Box<CustomExpr>, Position),
|
||||||
@ -900,14 +908,14 @@ pub enum Expr {
|
|||||||
impl Default for Expr {
|
impl Default for Expr {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::Unit(NO_POS)
|
Self::Unit(Position::NONE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Expr {
|
impl Expr {
|
||||||
/// Get the `Dynamic` value of a constant expression.
|
/// Get the [`Dynamic`] value of a constant expression.
|
||||||
///
|
///
|
||||||
/// Returns `None` if the expression is not constant.
|
/// Returns [`None`] if the expression is not constant.
|
||||||
pub fn get_constant_value(&self) -> Option<Dynamic> {
|
pub fn get_constant_value(&self) -> Option<Dynamic> {
|
||||||
Some(match self {
|
Some(match self {
|
||||||
Self::Expr(x) => return x.get_constant_value(),
|
Self::Expr(x) => return x.get_constant_value(),
|
||||||
@ -922,8 +930,7 @@ impl Expr {
|
|||||||
x.clone(),
|
x.clone(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
)))),
|
)))),
|
||||||
Self::True(_) => true.into(),
|
Self::BoolConstant(x, _) => (*x).into(),
|
||||||
Self::False(_) => false.into(),
|
|
||||||
Self::Unit(_) => ().into(),
|
Self::Unit(_) => ().into(),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -959,7 +966,7 @@ impl Expr {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get the `Position` of the expression.
|
/// Get the [`Position`] of the expression.
|
||||||
pub fn position(&self) -> Position {
|
pub fn position(&self) -> Position {
|
||||||
match self {
|
match self {
|
||||||
Self::Expr(x) => x.position(),
|
Self::Expr(x) => x.position(),
|
||||||
@ -968,6 +975,7 @@ impl Expr {
|
|||||||
Self::FloatConstant(_, pos) => *pos,
|
Self::FloatConstant(_, pos) => *pos,
|
||||||
|
|
||||||
Self::DynamicConstant(_, pos) => *pos,
|
Self::DynamicConstant(_, pos) => *pos,
|
||||||
|
Self::BoolConstant(_, pos) => *pos,
|
||||||
Self::IntegerConstant(_, pos) => *pos,
|
Self::IntegerConstant(_, pos) => *pos,
|
||||||
Self::CharConstant(_, pos) => *pos,
|
Self::CharConstant(_, pos) => *pos,
|
||||||
Self::StringConstant(_, pos) => *pos,
|
Self::StringConstant(_, pos) => *pos,
|
||||||
@ -981,14 +989,14 @@ impl Expr {
|
|||||||
|
|
||||||
Self::And(x, _) | Self::Or(x, _) | Self::In(x, _) => x.lhs.position(),
|
Self::And(x, _) | Self::Or(x, _) | Self::In(x, _) => x.lhs.position(),
|
||||||
|
|
||||||
Self::True(pos) | Self::False(pos) | Self::Unit(pos) => *pos,
|
Self::Unit(pos) => *pos,
|
||||||
|
|
||||||
Self::Dot(x, _) | Self::Index(x, _) => x.lhs.position(),
|
Self::Dot(x, _) | Self::Index(x, _) => x.lhs.position(),
|
||||||
|
|
||||||
Self::Custom(_, pos) => *pos,
|
Self::Custom(_, pos) => *pos,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Override the `Position` of the expression.
|
/// Override the [`Position`] of the expression.
|
||||||
pub fn set_position(&mut self, new_pos: Position) -> &mut Self {
|
pub fn set_position(&mut self, new_pos: Position) -> &mut Self {
|
||||||
match self {
|
match self {
|
||||||
Self::Expr(x) => {
|
Self::Expr(x) => {
|
||||||
@ -999,6 +1007,7 @@ impl Expr {
|
|||||||
Self::FloatConstant(_, pos) => *pos = new_pos,
|
Self::FloatConstant(_, pos) => *pos = new_pos,
|
||||||
|
|
||||||
Self::DynamicConstant(_, pos) => *pos = new_pos,
|
Self::DynamicConstant(_, pos) => *pos = new_pos,
|
||||||
|
Self::BoolConstant(_, pos) => *pos = new_pos,
|
||||||
Self::IntegerConstant(_, pos) => *pos = new_pos,
|
Self::IntegerConstant(_, pos) => *pos = new_pos,
|
||||||
Self::CharConstant(_, pos) => *pos = new_pos,
|
Self::CharConstant(_, pos) => *pos = new_pos,
|
||||||
Self::StringConstant(_, pos) => *pos = new_pos,
|
Self::StringConstant(_, pos) => *pos = new_pos,
|
||||||
@ -1010,7 +1019,7 @@ impl Expr {
|
|||||||
Self::Stmt(_, pos) => *pos = new_pos,
|
Self::Stmt(_, pos) => *pos = new_pos,
|
||||||
Self::FnCall(_, pos) => *pos = new_pos,
|
Self::FnCall(_, pos) => *pos = new_pos,
|
||||||
Self::And(_, pos) | Self::Or(_, pos) | Self::In(_, pos) => *pos = new_pos,
|
Self::And(_, pos) | Self::Or(_, pos) | Self::In(_, pos) => *pos = new_pos,
|
||||||
Self::True(pos) | Self::False(pos) | Self::Unit(pos) => *pos = new_pos,
|
Self::Unit(pos) => *pos = new_pos,
|
||||||
Self::Dot(_, pos) | Self::Index(_, pos) => *pos = new_pos,
|
Self::Dot(_, pos) | Self::Index(_, pos) => *pos = new_pos,
|
||||||
Self::Custom(_, pos) => *pos = new_pos,
|
Self::Custom(_, pos) => *pos = new_pos,
|
||||||
}
|
}
|
||||||
@ -1056,12 +1065,11 @@ impl Expr {
|
|||||||
Self::FloatConstant(_, _) => true,
|
Self::FloatConstant(_, _) => true,
|
||||||
|
|
||||||
Self::DynamicConstant(_, _)
|
Self::DynamicConstant(_, _)
|
||||||
|
| Self::BoolConstant(_, _)
|
||||||
| Self::IntegerConstant(_, _)
|
| Self::IntegerConstant(_, _)
|
||||||
| Self::CharConstant(_, _)
|
| Self::CharConstant(_, _)
|
||||||
| Self::StringConstant(_, _)
|
| Self::StringConstant(_, _)
|
||||||
| Self::FnPointer(_, _)
|
| Self::FnPointer(_, _)
|
||||||
| Self::True(_)
|
|
||||||
| Self::False(_)
|
|
||||||
| Self::Unit(_) => true,
|
| Self::Unit(_) => true,
|
||||||
|
|
||||||
// An array literal is constant if all items are constant
|
// An array literal is constant if all items are constant
|
||||||
@ -1089,14 +1097,13 @@ impl Expr {
|
|||||||
Self::FloatConstant(_, _) => false,
|
Self::FloatConstant(_, _) => false,
|
||||||
|
|
||||||
Self::DynamicConstant(_, _)
|
Self::DynamicConstant(_, _)
|
||||||
|
| Self::BoolConstant(_, _)
|
||||||
| Self::IntegerConstant(_, _)
|
| Self::IntegerConstant(_, _)
|
||||||
| Self::CharConstant(_, _)
|
| Self::CharConstant(_, _)
|
||||||
| Self::FnPointer(_, _)
|
| Self::FnPointer(_, _)
|
||||||
| Self::In(_, _)
|
| Self::In(_, _)
|
||||||
| Self::And(_, _)
|
| Self::And(_, _)
|
||||||
| Self::Or(_, _)
|
| Self::Or(_, _)
|
||||||
| Self::True(_)
|
|
||||||
| Self::False(_)
|
|
||||||
| Self::Unit(_) => false,
|
| Self::Unit(_) => false,
|
||||||
|
|
||||||
Self::StringConstant(_, _)
|
Self::StringConstant(_, _)
|
||||||
|
188
src/dynamic.rs
188
src/dynamic.rs
@ -1,4 +1,4 @@
|
|||||||
//! Helper module which defines the `Any` trait to to allow dynamic value handling.
|
//! Helper module which defines the [`Any`] trait to to allow dynamic value handling.
|
||||||
|
|
||||||
use crate::fn_native::SendSync;
|
use crate::fn_native::SendSync;
|
||||||
use crate::r#unsafe::{unsafe_cast_box, unsafe_try_cast};
|
use crate::r#unsafe::{unsafe_cast_box, unsafe_try_cast};
|
||||||
@ -35,8 +35,6 @@ mod private {
|
|||||||
use crate::stdlib::any::Any;
|
use crate::stdlib::any::Any;
|
||||||
|
|
||||||
/// A sealed trait that prevents other crates from implementing [`Variant`].
|
/// A sealed trait that prevents other crates from implementing [`Variant`].
|
||||||
///
|
|
||||||
/// [`Variant`]: super::Variant
|
|
||||||
pub trait Sealed {}
|
pub trait Sealed {}
|
||||||
|
|
||||||
impl<T: Any + Clone + SendSync> Sealed for T {}
|
impl<T: Any + Clone + SendSync> Sealed for T {}
|
||||||
@ -44,51 +42,48 @@ mod private {
|
|||||||
|
|
||||||
/// Trait to represent any type.
|
/// Trait to represent any type.
|
||||||
///
|
///
|
||||||
/// Currently, `Variant` is not `Send` nor `Sync`, so it can practically be any type.
|
/// Currently, [`Variant`] is not [`Send`] nor [`Sync`], so it can practically be any type.
|
||||||
/// Turn on the `sync` feature to restrict it to only types that implement `Send + Sync`.
|
/// Turn on the [`Sync`] feature to restrict it to only types that implement [`Send`] `+` [`Sync`].
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub trait Variant: Any + private::Sealed {
|
pub trait Variant: Any + private::Sealed {
|
||||||
/// Convert this `Variant` trait object to `&dyn Any`.
|
/// Convert this [`Variant`] trait object to [`&dyn Any`][Any].
|
||||||
fn as_any(&self) -> &dyn Any;
|
fn as_any(&self) -> &dyn Any;
|
||||||
|
|
||||||
/// Convert this `Variant` trait object to `&mut dyn Any`.
|
/// Convert this [`Variant`] trait object to [`&mut dyn Any`][Any].
|
||||||
fn as_mut_any(&mut self) -> &mut dyn Any;
|
fn as_mut_any(&mut self) -> &mut dyn Any;
|
||||||
|
|
||||||
/// Convert this `Variant` trait object to an `Any` trait object.
|
/// Convert this [`Variant`] trait object to an [`Any`] trait object.
|
||||||
fn as_box_any(self: Box<Self>) -> Box<dyn Any>;
|
fn as_box_any(self: Box<Self>) -> Box<dyn Any>;
|
||||||
|
|
||||||
/// Get the name of this type.
|
/// Get the name of this type.
|
||||||
fn type_name(&self) -> &'static str;
|
fn type_name(&self) -> &'static str;
|
||||||
|
|
||||||
/// Convert into `Dynamic`.
|
/// Convert into [`Dynamic`].
|
||||||
fn into_dynamic(self) -> Dynamic;
|
fn into_dynamic(self) -> Dynamic;
|
||||||
|
|
||||||
/// Clone into `Dynamic`.
|
/// Clone into [`Dynamic`].
|
||||||
fn clone_into_dynamic(&self) -> Dynamic;
|
fn clone_into_dynamic(&self) -> Dynamic;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait to represent any type.
|
/// Trait to represent any type.
|
||||||
///
|
|
||||||
/// `From<_>` is implemented for `i64` (`i32` if `only_i32`), `f64` (if not `no_float`),
|
|
||||||
/// `bool`, `String`, `char`, `Vec<T>` (into `Array`) and `HashMap<String, T>` (into `Map`).
|
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub trait Variant: Any + Send + Sync + private::Sealed {
|
pub trait Variant: Any + Send + Sync + private::Sealed {
|
||||||
/// Convert this `Variant` trait object to `&dyn Any`.
|
/// Convert this [`Variant`] trait object to [`&dyn Any`][Any].
|
||||||
fn as_any(&self) -> &dyn Any;
|
fn as_any(&self) -> &dyn Any;
|
||||||
|
|
||||||
/// Convert this `Variant` trait object to `&mut dyn Any`.
|
/// Convert this [`Variant`] trait object to [`&mut dyn Any`][Any].
|
||||||
fn as_mut_any(&mut self) -> &mut dyn Any;
|
fn as_mut_any(&mut self) -> &mut dyn Any;
|
||||||
|
|
||||||
/// Convert this `Variant` trait object to an `Any` trait object.
|
/// Convert this [`Variant`] trait object to an [`Any`] trait object.
|
||||||
fn as_box_any(self: Box<Self>) -> Box<dyn Any>;
|
fn as_box_any(self: Box<Self>) -> Box<dyn Any>;
|
||||||
|
|
||||||
/// Get the name of this type.
|
/// Get the name of this type.
|
||||||
fn type_name(&self) -> &'static str;
|
fn type_name(&self) -> &'static str;
|
||||||
|
|
||||||
/// Convert into `Dynamic`.
|
/// Convert into [`Dynamic`].
|
||||||
fn into_dynamic(self) -> Dynamic;
|
fn into_dynamic(self) -> Dynamic;
|
||||||
|
|
||||||
/// Clone into `Dynamic`.
|
/// Clone into [`Dynamic`].
|
||||||
fn clone_into_dynamic(&self) -> Dynamic;
|
fn clone_into_dynamic(&self) -> Dynamic;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,7 +109,7 @@ impl<T: Any + Clone + SendSync> Variant for T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl dyn Variant {
|
impl dyn Variant {
|
||||||
/// Is this `Variant` a specific type?
|
/// Is this [`Variant`] a specific type?
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is<T: Any>(&self) -> bool {
|
pub fn is<T: Any>(&self) -> bool {
|
||||||
TypeId::of::<T>() == self.type_id()
|
TypeId::of::<T>() == self.type_id()
|
||||||
@ -124,7 +119,7 @@ impl dyn Variant {
|
|||||||
/// Dynamic type containing any value.
|
/// Dynamic type containing any value.
|
||||||
pub struct Dynamic(pub(crate) Union);
|
pub struct Dynamic(pub(crate) Union);
|
||||||
|
|
||||||
/// Internal `Dynamic` representation.
|
/// Internal [`Dynamic`] representation.
|
||||||
///
|
///
|
||||||
/// Most variants are boxed to reduce the size.
|
/// Most variants are boxed to reduce the size.
|
||||||
pub enum Union {
|
pub enum Union {
|
||||||
@ -149,14 +144,14 @@ pub enum Union {
|
|||||||
Shared(crate::Shared<crate::Locked<Dynamic>>),
|
Shared(crate::Shared<crate::Locked<Dynamic>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Underlying `Variant` read guard for `Dynamic`.
|
/// Underlying [`Variant`] read guard for [`Dynamic`].
|
||||||
///
|
///
|
||||||
/// This data structure provides transparent interoperability between
|
/// This data structure provides transparent interoperability between
|
||||||
/// normal `Dynamic` and shared Dynamic values.
|
/// normal [`Dynamic`] and shared [`Dynamic`] values.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DynamicReadLock<'d, T: Variant + Clone>(DynamicReadLockInner<'d, T>);
|
pub struct DynamicReadLock<'d, T: Variant + Clone>(DynamicReadLockInner<'d, T>);
|
||||||
|
|
||||||
/// Different types of read guards for `DynamicReadLock`.
|
/// Different types of read guards for [`DynamicReadLock`].
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum DynamicReadLockInner<'d, T: Variant + Clone> {
|
enum DynamicReadLockInner<'d, T: Variant + Clone> {
|
||||||
/// A simple reference to a non-shared value.
|
/// A simple reference to a non-shared value.
|
||||||
@ -166,7 +161,7 @@ enum DynamicReadLockInner<'d, T: Variant + Clone> {
|
|||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
Guard(crate::stdlib::cell::Ref<'d, Dynamic>),
|
Guard(crate::stdlib::cell::Ref<'d, Dynamic>),
|
||||||
/// A read guard to a shared `RwLock`.
|
/// A read guard to a shared [`RwLock`][std::sync::RwLock].
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
Guard(crate::stdlib::sync::RwLockReadGuard<'d, Dynamic>),
|
Guard(crate::stdlib::sync::RwLockReadGuard<'d, Dynamic>),
|
||||||
@ -186,14 +181,14 @@ impl<'d, T: Variant + Clone> Deref for DynamicReadLock<'d, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Underlying `Variant` write guard for `Dynamic`.
|
/// Underlying [`Variant`] write guard for [`Dynamic`].
|
||||||
///
|
///
|
||||||
/// This data structure provides transparent interoperability between
|
/// This data structure provides transparent interoperability between
|
||||||
/// normal `Dynamic` and shared Dynamic values.
|
/// normal [`Dynamic`] and shared [`Dynamic`] values.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DynamicWriteLock<'d, T: Variant + Clone>(DynamicWriteLockInner<'d, T>);
|
pub struct DynamicWriteLock<'d, T: Variant + Clone>(DynamicWriteLockInner<'d, T>);
|
||||||
|
|
||||||
/// Different types of write guards for `DynamicReadLock`.
|
/// Different types of write guards for [`DynamicReadLock`].
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum DynamicWriteLockInner<'d, T: Variant + Clone> {
|
enum DynamicWriteLockInner<'d, T: Variant + Clone> {
|
||||||
/// A simple mutable reference to a non-shared value.
|
/// A simple mutable reference to a non-shared value.
|
||||||
@ -203,7 +198,7 @@ enum DynamicWriteLockInner<'d, T: Variant + Clone> {
|
|||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
Guard(crate::stdlib::cell::RefMut<'d, Dynamic>),
|
Guard(crate::stdlib::cell::RefMut<'d, Dynamic>),
|
||||||
/// A write guard to a shared `RwLock`.
|
/// A write guard to a shared [`RwLock`][std::sync::RwLock].
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
Guard(crate::stdlib::sync::RwLockWriteGuard<'d, Dynamic>),
|
Guard(crate::stdlib::sync::RwLockWriteGuard<'d, Dynamic>),
|
||||||
@ -236,7 +231,7 @@ impl<'d, T: Variant + Clone> DerefMut for DynamicWriteLock<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Dynamic {
|
impl Dynamic {
|
||||||
/// Does this `Dynamic` hold a variant data type
|
/// Does this [`Dynamic`] hold a variant data type
|
||||||
/// instead of one of the support system primitive types?
|
/// instead of one of the support system primitive types?
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_variant(&self) -> bool {
|
pub fn is_variant(&self) -> bool {
|
||||||
@ -245,7 +240,7 @@ impl Dynamic {
|
|||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Does this `Dynamic` hold a shared data type
|
/// Does this [`Dynamic`] hold a shared data type
|
||||||
/// instead of one of the supported system primitive types?
|
/// instead of one of the supported system primitive types?
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_shared(&self) -> bool {
|
pub fn is_shared(&self) -> bool {
|
||||||
@ -255,9 +250,9 @@ impl Dynamic {
|
|||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Is the value held by this `Dynamic` a particular type?
|
/// Is the value held by this [`Dynamic`] a particular type?
|
||||||
///
|
///
|
||||||
/// If the `Dynamic` is a Shared variant checking is performed on
|
/// If the [`Dynamic`] is a shared variant checking is performed on
|
||||||
/// top of it's internal value.
|
/// top of it's internal value.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is<T: Variant + Clone>(&self) -> bool {
|
pub fn is<T: Variant + Clone>(&self) -> bool {
|
||||||
@ -269,11 +264,11 @@ impl Dynamic {
|
|||||||
|
|
||||||
self.type_id() == target_type_id
|
self.type_id() == target_type_id
|
||||||
}
|
}
|
||||||
/// Get the TypeId of the value held by this `Dynamic`.
|
/// Get the [`TypeId`] of the value held by this [`Dynamic`].
|
||||||
///
|
///
|
||||||
/// # Panics or Deadlocks When Value is Shared
|
/// # Panics or Deadlocks When Value is Shared
|
||||||
///
|
///
|
||||||
/// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1).
|
/// Under the [`Sync`] feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1).
|
||||||
/// Otherwise, this call panics if the data is currently borrowed for write.
|
/// Otherwise, this call panics if the data is currently borrowed for write.
|
||||||
pub fn type_id(&self) -> TypeId {
|
pub fn type_id(&self) -> TypeId {
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
@ -302,11 +297,11 @@ impl Dynamic {
|
|||||||
Union::Shared(cell) => (*cell.read().unwrap()).type_id(),
|
Union::Shared(cell) => (*cell.read().unwrap()).type_id(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get the name of the type of the value held by this `Dynamic`.
|
/// Get the name of the type of the value held by this [`Dynamic`].
|
||||||
///
|
///
|
||||||
/// # Panics or Deadlocks When Value is Shared
|
/// # Panics or Deadlocks When Value is Shared
|
||||||
///
|
///
|
||||||
/// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1).
|
/// Under the [`Sync`] feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1).
|
||||||
/// Otherwise, this call panics if the data is currently borrowed for write.
|
/// Otherwise, this call panics if the data is currently borrowed for write.
|
||||||
pub fn type_name(&self) -> &'static str {
|
pub fn type_name(&self) -> &'static str {
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
@ -518,29 +513,29 @@ impl Default for Dynamic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Dynamic {
|
impl Dynamic {
|
||||||
/// A `Dynamic` containing a `()`.
|
/// A [`Dynamic`] containing a `()`.
|
||||||
pub const UNIT: Dynamic = Self(Union::Unit(()));
|
pub const UNIT: Dynamic = Self(Union::Unit(()));
|
||||||
/// A `Dynamic` containing a `true`.
|
/// A [`Dynamic`] containing a `true`.
|
||||||
pub const TRUE: Dynamic = Self(Union::Bool(true));
|
pub const TRUE: Dynamic = Self(Union::Bool(true));
|
||||||
/// A `Dynamic` containing a `false`.
|
/// A [`Dynamic`] containing a [`false`].
|
||||||
pub const FALSE: Dynamic = Self(Union::Bool(false));
|
pub const FALSE: Dynamic = Self(Union::Bool(false));
|
||||||
/// A `Dynamic` containing the integer zero.
|
/// A [`Dynamic`] containing the integer zero.
|
||||||
pub const ZERO: Dynamic = Self(Union::Int(0));
|
pub const ZERO: Dynamic = Self(Union::Int(0));
|
||||||
/// A `Dynamic` containing the integer one.
|
/// A [`Dynamic`] containing the integer one.
|
||||||
pub const ONE: Dynamic = Self(Union::Int(1));
|
pub const ONE: Dynamic = Self(Union::Int(1));
|
||||||
/// A `Dynamic` containing the integer negative one.
|
/// A [`Dynamic`] containing the integer negative one.
|
||||||
pub const NEGATIVE_ONE: Dynamic = Self(Union::Int(-1));
|
pub const NEGATIVE_ONE: Dynamic = Self(Union::Int(-1));
|
||||||
/// A `Dynamic` containing the floating-point zero.
|
/// A [`Dynamic`] containing the floating-point zero.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
pub const FLOAT_ZERO: Dynamic = Self(Union::Float(0.0));
|
pub const FLOAT_ZERO: Dynamic = Self(Union::Float(0.0));
|
||||||
/// A `Dynamic` containing the floating-point one.
|
/// A [`Dynamic`] containing the floating-point one.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
pub const FLOAT_ONE: Dynamic = Self(Union::Float(1.0));
|
pub const FLOAT_ONE: Dynamic = Self(Union::Float(1.0));
|
||||||
/// A `Dynamic` containing the floating-point negative one.
|
/// A [`Dynamic`] containing the floating-point negative one.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
pub const FLOAT_NEGATIVE_ONE: Dynamic = Self(Union::Float(-1.0));
|
pub const FLOAT_NEGATIVE_ONE: Dynamic = Self(Union::Float(-1.0));
|
||||||
|
|
||||||
/// Create a `Dynamic` from any type. A `Dynamic` value is simply returned as is.
|
/// Create a [`Dynamic`] from any type. A [`Dynamic`] value is simply returned as is.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
@ -548,11 +543,12 @@ impl Dynamic {
|
|||||||
///
|
///
|
||||||
/// # Notes
|
/// # Notes
|
||||||
///
|
///
|
||||||
/// Beware that you need to pass in an `Array` type for it to be recognized as an `Array`.
|
/// Beware that you need to pass in an [`Array`] type for it to be recognized as an [`Array`].
|
||||||
/// A `Vec<T>` does not get automatically converted to an `Array`, but will be a generic
|
/// A [`Vec<T>`][Vec] does not get automatically converted to an [`Array`], but will be a generic
|
||||||
/// restricted trait object instead, because `Vec<T>` is not a supported standard type.
|
/// restricted trait object instead, because [`Vec<T>`][Vec] is not a supported standard type.
|
||||||
///
|
///
|
||||||
/// Similarly, passing in a `HashMap<String, T>` will not get a `Map` but a trait object.
|
/// Similarly, passing in a [`HashMap<String, T>`][std::collections::HashMap] will not get a [`Map`]
|
||||||
|
/// but a trait object.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
@ -657,15 +653,15 @@ impl Dynamic {
|
|||||||
|
|
||||||
Self(Union::Variant(Box::new(boxed)))
|
Self(Union::Variant(Box::new(boxed)))
|
||||||
}
|
}
|
||||||
/// Turn the `Dynamic` value into a shared `Dynamic` value backed by an `Rc<RefCell<Dynamic>>`
|
/// Turn the [`Dynamic`] value into a shared [`Dynamic`] value backed by an [`Rc`][std::rc::Rc]`<`[`RefCell`][std::cell::RefCell]`<`[`Dynamic`]`>>`
|
||||||
/// or `Arc<RwLock<Dynamic>>` depending on the `sync` feature.
|
/// or [`Arc`][std::sync::Arc]`<`[`RwLock`][std::sync::RwLock]`<`[`Dynamic`]`>>` depending on the [`Sync`] feature.
|
||||||
///
|
///
|
||||||
/// Shared `Dynamic` values are relatively cheap to clone as they simply increment the
|
/// Shared [`Dynamic`] values are relatively cheap to clone as they simply increment the
|
||||||
/// reference counts.
|
/// reference counts.
|
||||||
///
|
///
|
||||||
/// Shared `Dynamic` values can be converted seamlessly to and from ordinary `Dynamic` values.
|
/// Shared [`Dynamic`] values can be converted seamlessly to and from ordinary [`Dynamic`] values.
|
||||||
///
|
///
|
||||||
/// If the `Dynamic` value is already shared, this method returns itself.
|
/// If the [`Dynamic`] value is already shared, this method returns itself.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
@ -681,16 +677,16 @@ impl Dynamic {
|
|||||||
#[cfg(feature = "no_closure")]
|
#[cfg(feature = "no_closure")]
|
||||||
panic!("converting into a shared value is not supported under 'no_closure'");
|
panic!("converting into a shared value is not supported under 'no_closure'");
|
||||||
}
|
}
|
||||||
/// Convert the `Dynamic` value into specific type.
|
/// Convert the [`Dynamic`] value into specific type.
|
||||||
///
|
///
|
||||||
/// Casting to a `Dynamic` just returns as is, but if it contains a shared value,
|
/// Casting to a [`Dynamic`] just returns as is, but if it contains a shared value,
|
||||||
/// it is cloned into a `Dynamic` with a normal value.
|
/// it is cloned into a [`Dynamic`] with a normal value.
|
||||||
///
|
///
|
||||||
/// Returns `None` if types mismatched.
|
/// Returns [`None`] if types mismatched.
|
||||||
///
|
///
|
||||||
/// # Panics or Deadlocks
|
/// # Panics or Deadlocks
|
||||||
///
|
///
|
||||||
/// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1).
|
/// Under the [`Sync`] feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1).
|
||||||
/// Otherwise, this call panics if the data is currently borrowed for write.
|
/// Otherwise, this call panics if the data is currently borrowed for write.
|
||||||
///
|
///
|
||||||
/// These normally shouldn't occur since most operations in Rhai is single-threaded.
|
/// These normally shouldn't occur since most operations in Rhai is single-threaded.
|
||||||
@ -811,19 +807,19 @@ impl Dynamic {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Convert the `Dynamic` value into a specific type.
|
/// Convert the [`Dynamic`] value into a specific type.
|
||||||
///
|
///
|
||||||
/// Casting to a `Dynamic` just returns as is, but if it contains a shared value,
|
/// Casting to a [`Dynamic`] just returns as is, but if it contains a shared value,
|
||||||
/// it is cloned into a `Dynamic` with a normal value.
|
/// it is cloned into a [`Dynamic`] with a normal value.
|
||||||
///
|
///
|
||||||
/// Returns `None` if types mismatched.
|
/// Returns [`None`] if types mismatched.
|
||||||
///
|
///
|
||||||
/// # Panics or Deadlocks
|
/// # Panics or Deadlocks
|
||||||
///
|
///
|
||||||
/// Panics if the cast fails (e.g. the type of the actual value is not the
|
/// Panics if the cast fails (e.g. the type of the actual value is not the
|
||||||
/// same as the specified type).
|
/// same as the specified type).
|
||||||
///
|
///
|
||||||
/// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1).
|
/// Under the [`Sync`] feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1).
|
||||||
/// Otherwise, this call panics if the data is currently borrowed for write.
|
/// Otherwise, this call panics if the data is currently borrowed for write.
|
||||||
///
|
///
|
||||||
/// These normally shouldn't occur since most operations in Rhai is single-threaded.
|
/// These normally shouldn't occur since most operations in Rhai is single-threaded.
|
||||||
@ -854,11 +850,11 @@ impl Dynamic {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/// Flatten the `Dynamic` and clone it.
|
/// Flatten the [`Dynamic`] and clone it.
|
||||||
///
|
///
|
||||||
/// If the `Dynamic` is not a shared value, it returns a cloned copy.
|
/// If the [`Dynamic`] is not a shared value, it returns a cloned copy.
|
||||||
///
|
///
|
||||||
/// If the `Dynamic` is a shared value, it a cloned copy of the shared value.
|
/// If the [`Dynamic`] is a shared value, it a cloned copy of the shared value.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn flatten_clone(&self) -> Self {
|
pub fn flatten_clone(&self) -> Self {
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
@ -873,11 +869,11 @@ impl Dynamic {
|
|||||||
_ => self.clone(),
|
_ => self.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Flatten the `Dynamic`.
|
/// Flatten the [`Dynamic`].
|
||||||
///
|
///
|
||||||
/// If the `Dynamic` is not a shared value, it returns itself.
|
/// If the [`Dynamic`] is not a shared value, it returns itself.
|
||||||
///
|
///
|
||||||
/// If the `Dynamic` is a shared value, it returns the shared value if there are
|
/// If the [`Dynamic`] is a shared value, it returns the shared value if there are
|
||||||
/// no outstanding references, or a cloned copy.
|
/// no outstanding references, or a cloned copy.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn flatten(self) -> Self {
|
pub fn flatten(self) -> Self {
|
||||||
@ -900,13 +896,13 @@ impl Dynamic {
|
|||||||
_ => self,
|
_ => self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Is the `Dynamic` a shared value that is locked?
|
/// Is the [`Dynamic`] a shared value that is locked?
|
||||||
///
|
///
|
||||||
/// ## Note
|
/// ## Note
|
||||||
///
|
///
|
||||||
/// Under the `sync` feature, shared values use `RwLock` and they are never locked.
|
/// Under the [`Sync`] feature, shared values use [`RwLock`][std::sync::RwLock] and they are never locked.
|
||||||
/// Access just waits until the `RwLock` is released.
|
/// Access just waits until the [`RwLock`][std::sync::RwLock] is released.
|
||||||
/// So this method always returns `false` under `sync`.
|
/// So this method always returns [`false`] under [`Sync`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_locked(&self) -> bool {
|
pub fn is_locked(&self) -> bool {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
@ -921,14 +917,14 @@ impl Dynamic {
|
|||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get a reference of a specific type to the `Dynamic`.
|
/// Get a reference of a specific type to the [`Dynamic`].
|
||||||
/// Casting to `Dynamic` just returns a reference to it.
|
/// Casting to [`Dynamic`] just returns a reference to it.
|
||||||
///
|
///
|
||||||
/// Returns `None` if the cast fails.
|
/// Returns [`None`] if the cast fails.
|
||||||
///
|
///
|
||||||
/// # Panics or Deadlocks When Value is Shared
|
/// # Panics or Deadlocks When Value is Shared
|
||||||
///
|
///
|
||||||
/// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1).
|
/// Under the [`Sync`] feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1).
|
||||||
/// Otherwise, this call panics if the data is currently borrowed for write.
|
/// Otherwise, this call panics if the data is currently borrowed for write.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn read_lock<T: Variant + Clone>(&self) -> Option<DynamicReadLock<T>> {
|
pub fn read_lock<T: Variant + Clone>(&self) -> Option<DynamicReadLock<T>> {
|
||||||
@ -953,14 +949,14 @@ impl Dynamic {
|
|||||||
.map(|r| DynamicReadLock(DynamicReadLockInner::Reference(r))),
|
.map(|r| DynamicReadLock(DynamicReadLockInner::Reference(r))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get a mutable reference of a specific type to the `Dynamic`.
|
/// Get a mutable reference of a specific type to the [`Dynamic`].
|
||||||
/// Casting to `Dynamic` just returns a mutable reference to it.
|
/// Casting to [`Dynamic`] just returns a mutable reference to it.
|
||||||
///
|
///
|
||||||
/// Returns `None` if the cast fails.
|
/// Returns [`None`] if the cast fails.
|
||||||
///
|
///
|
||||||
/// # Panics or Deadlocks When Value is Shared
|
/// # Panics or Deadlocks When Value is Shared
|
||||||
///
|
///
|
||||||
/// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1).
|
/// Under the [`Sync`] feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1).
|
||||||
/// Otherwise, this call panics if the data is currently borrowed for write.
|
/// Otherwise, this call panics if the data is currently borrowed for write.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn write_lock<T: Variant + Clone>(&mut self) -> Option<DynamicWriteLock<T>> {
|
pub fn write_lock<T: Variant + Clone>(&mut self) -> Option<DynamicWriteLock<T>> {
|
||||||
@ -985,10 +981,10 @@ impl Dynamic {
|
|||||||
.map(|r| DynamicWriteLock(DynamicWriteLockInner::Reference(r))),
|
.map(|r| DynamicWriteLock(DynamicWriteLockInner::Reference(r))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get a reference of a specific type to the `Dynamic`.
|
/// Get a reference of a specific type to the [`Dynamic`].
|
||||||
/// Casting to `Dynamic` just returns a reference to it.
|
/// Casting to [`Dynamic`] just returns a reference to it.
|
||||||
///
|
///
|
||||||
/// Returns `None` if the cast fails, or if the value is shared.
|
/// Returns [`None`] if the cast fails, or if the value is shared.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn downcast_ref<T: Variant + Clone>(&self) -> Option<&T> {
|
pub(crate) fn downcast_ref<T: Variant + Clone>(&self) -> Option<&T> {
|
||||||
// Coded this way in order to maximally leverage potentials for dead-code removal.
|
// Coded this way in order to maximally leverage potentials for dead-code removal.
|
||||||
@ -1074,10 +1070,10 @@ impl Dynamic {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get a mutable reference of a specific type to the `Dynamic`.
|
/// Get a mutable reference of a specific type to the [`Dynamic`].
|
||||||
/// Casting to `Dynamic` just returns a mutable reference to it.
|
/// Casting to [`Dynamic`] just returns a mutable reference to it.
|
||||||
///
|
///
|
||||||
/// Returns `None` if the cast fails, or if the value is shared.
|
/// Returns [`None`] if the cast fails, or if the value is shared.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn downcast_mut<T: Variant + Clone>(&mut self) -> Option<&mut T> {
|
pub(crate) fn downcast_mut<T: Variant + Clone>(&mut self) -> Option<&mut T> {
|
||||||
// Coded this way in order to maximally leverage potentials for dead-code removal.
|
// Coded this way in order to maximally leverage potentials for dead-code removal.
|
||||||
@ -1157,7 +1153,7 @@ impl Dynamic {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Cast the `Dynamic` as the system integer type `INT` and return it.
|
/// Cast the [`Dynamic`] as the system integer type [`INT`] and return it.
|
||||||
/// Returns the name of the actual type if the cast fails.
|
/// Returns the name of the actual type if the cast fails.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_int(&self) -> Result<INT, &'static str> {
|
pub fn as_int(&self) -> Result<INT, &'static str> {
|
||||||
@ -1168,7 +1164,7 @@ impl Dynamic {
|
|||||||
_ => Err(self.type_name()),
|
_ => Err(self.type_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Cast the `Dynamic` as the system floating-point type `FLOAT` and return it.
|
/// Cast the [`Dynamic`] as the system floating-point type [`FLOAT`] and return it.
|
||||||
/// Returns the name of the actual type if the cast fails.
|
/// Returns the name of the actual type if the cast fails.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -1180,7 +1176,7 @@ impl Dynamic {
|
|||||||
_ => Err(self.type_name()),
|
_ => Err(self.type_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Cast the `Dynamic` as a `bool` and return it.
|
/// Cast the [`Dynamic`] as a [`bool`] and return it.
|
||||||
/// Returns the name of the actual type if the cast fails.
|
/// Returns the name of the actual type if the cast fails.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_bool(&self) -> Result<bool, &'static str> {
|
pub fn as_bool(&self) -> Result<bool, &'static str> {
|
||||||
@ -1191,7 +1187,7 @@ impl Dynamic {
|
|||||||
_ => Err(self.type_name()),
|
_ => Err(self.type_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Cast the `Dynamic` as a `char` and return it.
|
/// Cast the [`Dynamic`] as a [`char`] and return it.
|
||||||
/// Returns the name of the actual type if the cast fails.
|
/// Returns the name of the actual type if the cast fails.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_char(&self) -> Result<char, &'static str> {
|
pub fn as_char(&self) -> Result<char, &'static str> {
|
||||||
@ -1202,7 +1198,7 @@ impl Dynamic {
|
|||||||
_ => Err(self.type_name()),
|
_ => Err(self.type_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Cast the `Dynamic` as a string and return the string slice.
|
/// Cast the [`Dynamic`] as a [`String`] and return the string slice.
|
||||||
/// Returns the name of the actual type if the cast fails.
|
/// Returns the name of the actual type if the cast fails.
|
||||||
///
|
///
|
||||||
/// Cast is failing if `self` is Shared Dynamic
|
/// Cast is failing if `self` is Shared Dynamic
|
||||||
@ -1214,7 +1210,7 @@ impl Dynamic {
|
|||||||
_ => Err(self.type_name()),
|
_ => Err(self.type_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Convert the `Dynamic` into `String` and return it.
|
/// Convert the [`Dynamic`] into a [`String`] and return it.
|
||||||
/// If there are other references to the same string, a cloned copy is returned.
|
/// If there are other references to the same string, a cloned copy is returned.
|
||||||
/// Returns the name of the actual type if the cast fails.
|
/// Returns the name of the actual type if the cast fails.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -1222,7 +1218,7 @@ impl Dynamic {
|
|||||||
self.take_immutable_string()
|
self.take_immutable_string()
|
||||||
.map(ImmutableString::into_owned)
|
.map(ImmutableString::into_owned)
|
||||||
}
|
}
|
||||||
/// Convert the `Dynamic` into `ImmutableString` and return it.
|
/// Convert the [`Dynamic`] into an [`ImmutableString`] and return it.
|
||||||
/// Returns the name of the actual type if the cast fails.
|
/// Returns the name of the actual type if the cast fails.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn take_immutable_string(self) -> Result<ImmutableString, &'static str> {
|
pub fn take_immutable_string(self) -> Result<ImmutableString, &'static str> {
|
||||||
|
264
src/engine.rs
264
src/engine.rs
@ -1,4 +1,4 @@
|
|||||||
//! Main module defining the script evaluation `Engine`.
|
//! Main module defining the script evaluation [`Engine`].
|
||||||
|
|
||||||
use crate::ast::{Expr, FnCallExpr, Ident, IdentX, ReturnType, Stmt};
|
use crate::ast::{Expr, FnCallExpr, Ident, IdentX, ReturnType, Stmt};
|
||||||
use crate::dynamic::{map_std_type_name, Union, Variant};
|
use crate::dynamic::{map_std_type_name, Union, Variant};
|
||||||
@ -25,7 +25,7 @@ use crate::syntax::CustomSyntax;
|
|||||||
use crate::utils::get_hasher;
|
use crate::utils::get_hasher;
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_native_fn_hash, Dynamic, EvalAltResult, FnPtr, ImmutableString, Module, Position, Scope,
|
calc_native_fn_hash, Dynamic, EvalAltResult, FnPtr, ImmutableString, Module, Position, Scope,
|
||||||
Shared, StaticVec, NO_POS,
|
Shared, StaticVec,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -40,7 +40,7 @@ use crate::Map;
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
pub const TYPICAL_MAP_SIZE: usize = 8; // Small maps are typical
|
pub const TYPICAL_MAP_SIZE: usize = 8; // Small maps are typical
|
||||||
|
|
||||||
/// _[INTERNALS]_ A stack of imported modules.
|
/// _(INTERNALS)_ A stack of imported modules.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// ## WARNING
|
/// ## WARNING
|
||||||
@ -52,83 +52,97 @@ pub const TYPICAL_MAP_SIZE: usize = 8; // Small maps are typical
|
|||||||
// We cannot use &str or Cow<str> here because `eval` may load a module and the module name will live beyond
|
// We cannot use &str or Cow<str> here because `eval` may load a module and the module name will live beyond
|
||||||
// the AST of the eval script text. The best we can do is a shared reference.
|
// the AST of the eval script text. The best we can do is a shared reference.
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct Imports(StaticVec<(ImmutableString, Shared<Module>)>);
|
pub struct Imports(Option<StaticVec<(ImmutableString, Shared<Module>)>>);
|
||||||
|
|
||||||
impl Imports {
|
impl Imports {
|
||||||
/// Get the length of this stack of imported modules.
|
/// Get the length of this stack of imported modules.
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.0.len()
|
self.0.as_ref().map_or(0, StaticVec::len)
|
||||||
}
|
}
|
||||||
/// Is this stack of imported modules empty?
|
/// Is this stack of imported modules empty?
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.0.is_empty()
|
self.0.as_ref().map_or(true, StaticVec::is_empty)
|
||||||
}
|
}
|
||||||
/// Get the imported module at a particular index.
|
/// Get the imported module at a particular index.
|
||||||
pub fn get(&self, index: usize) -> Option<Shared<Module>> {
|
pub fn get(&self, index: usize) -> Option<Shared<Module>> {
|
||||||
self.0.get(index).map(|(_, m)| m).cloned()
|
self.0
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|x| x.get(index))
|
||||||
|
.map(|(_, m)| m)
|
||||||
|
.cloned()
|
||||||
}
|
}
|
||||||
/// Get the index of an imported module by name.
|
/// Get the index of an imported module by name.
|
||||||
pub fn find(&self, name: &str) -> Option<usize> {
|
pub fn find(&self, name: &str) -> Option<usize> {
|
||||||
self.0
|
self.0.as_ref().and_then(|x| {
|
||||||
.iter()
|
x.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.rev()
|
.rev()
|
||||||
.find(|(_, (key, _))| key.as_str() == name)
|
.find(|(_, (key, _))| key.as_str() == name)
|
||||||
.map(|(index, _)| index)
|
.map(|(index, _)| index)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
/// Push an imported module onto the stack.
|
/// Push an imported module onto the stack.
|
||||||
pub fn push(&mut self, name: impl Into<ImmutableString>, module: impl Into<Shared<Module>>) {
|
pub fn push(&mut self, name: impl Into<ImmutableString>, module: impl Into<Shared<Module>>) {
|
||||||
self.0.push((name.into(), module.into()));
|
if self.0.is_none() {
|
||||||
|
self.0 = Some(Default::default());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.0.as_mut().unwrap().push((name.into(), module.into()));
|
||||||
}
|
}
|
||||||
/// Truncate the stack of imported modules to a particular length.
|
/// Truncate the stack of imported modules to a particular length.
|
||||||
pub fn truncate(&mut self, size: usize) {
|
pub fn truncate(&mut self, size: usize) {
|
||||||
self.0.truncate(size);
|
if self.0.is_some() {
|
||||||
|
self.0.as_mut().unwrap().truncate(size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/// Get an iterator to this stack of imported modules.
|
/// Get an iterator to this stack of imported modules.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn iter(&self) -> impl Iterator<Item = (&str, Shared<Module>)> {
|
pub fn iter(&self) -> impl Iterator<Item = (&str, Shared<Module>)> {
|
||||||
self.0
|
self.0.iter().flat_map(|lib| {
|
||||||
.iter()
|
lib.iter()
|
||||||
.map(|(name, module)| (name.as_str(), module.clone()))
|
.map(|(name, module)| (name.as_str(), module.clone()))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
/// Get an iterator to this stack of imported modules.
|
/// Get an iterator to this stack of imported modules.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) fn iter_raw<'a>(
|
pub(crate) fn iter_raw<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
) -> impl Iterator<Item = (ImmutableString, Shared<Module>)> + 'a {
|
) -> impl Iterator<Item = (ImmutableString, Shared<Module>)> + 'a {
|
||||||
self.0.iter().cloned()
|
self.0.iter().flat_map(|lib| lib.iter().cloned())
|
||||||
}
|
}
|
||||||
/// Get a consuming iterator to this stack of imported modules.
|
/// Get a consuming iterator to this stack of imported modules.
|
||||||
pub fn into_iter(self) -> impl Iterator<Item = (ImmutableString, Shared<Module>)> {
|
pub fn into_iter(self) -> impl Iterator<Item = (ImmutableString, Shared<Module>)> {
|
||||||
self.0.into_iter()
|
self.0.into_iter().flat_map(|lib| lib.into_iter())
|
||||||
}
|
}
|
||||||
/// Add a stream of imported modules.
|
/// Add a stream of imported modules.
|
||||||
pub fn extend(&mut self, stream: impl Iterator<Item = (ImmutableString, Shared<Module>)>) {
|
pub fn extend(&mut self, stream: impl Iterator<Item = (ImmutableString, Shared<Module>)>) {
|
||||||
self.0.extend(stream)
|
self.0.as_mut().unwrap().extend(stream)
|
||||||
}
|
}
|
||||||
/// Does the specified function hash key exist in this stack of imported modules?
|
/// Does the specified function hash key exist in this stack of imported modules?
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn contains_fn(&self, hash: u64) -> bool {
|
pub fn contains_fn(&self, hash: u64) -> bool {
|
||||||
self.0.iter().any(|(_, m)| m.contains_qualified_fn(hash))
|
self.0.as_ref().map_or(false, |x| {
|
||||||
|
x.iter().any(|(_, m)| m.contains_qualified_fn(hash))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
/// Get specified function via its hash key.
|
/// Get specified function via its hash key.
|
||||||
pub fn get_fn(&self, hash: u64) -> Option<&CallableFunction> {
|
pub fn get_fn(&self, hash: u64) -> Option<&CallableFunction> {
|
||||||
self.0
|
self.0
|
||||||
.iter()
|
.as_ref()
|
||||||
.rev()
|
.and_then(|x| x.iter().rev().find_map(|(_, m)| m.get_qualified_fn(hash)))
|
||||||
.find_map(|(_, m)| m.get_qualified_fn(hash))
|
|
||||||
}
|
}
|
||||||
/// Does the specified TypeId iterator exist in this stack of imported modules?
|
/// Does the specified TypeId iterator exist in this stack of imported modules?
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn contains_iter(&self, id: TypeId) -> bool {
|
pub fn contains_iter(&self, id: TypeId) -> bool {
|
||||||
self.0.iter().any(|(_, m)| m.contains_qualified_iter(id))
|
self.0.as_ref().map_or(false, |x| {
|
||||||
|
x.iter().any(|(_, m)| m.contains_qualified_iter(id))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
/// Get the specified TypeId iterator.
|
/// Get the specified TypeId iterator.
|
||||||
pub fn get_iter(&self, id: TypeId) -> Option<IteratorFn> {
|
pub fn get_iter(&self, id: TypeId) -> Option<IteratorFn> {
|
||||||
self.0
|
self.0
|
||||||
.iter()
|
.as_ref()
|
||||||
.rev()
|
.and_then(|x| x.iter().rev().find_map(|(_, m)| m.get_qualified_iter(id)))
|
||||||
.find_map(|(_, m)| m.get_qualified_iter(id))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,7 +224,7 @@ impl IndexChainValue {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
pub fn as_value(self) -> Dynamic {
|
pub fn as_value(self) -> Dynamic {
|
||||||
match self {
|
match self {
|
||||||
Self::None | Self::FnCallArgs(_) => panic!("expecting IndexChainValue::Value"),
|
Self::None | Self::FnCallArgs(_) => unreachable!("expecting IndexChainValue::Value"),
|
||||||
Self::Value(value) => value,
|
Self::Value(value) => value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -222,7 +236,7 @@ impl IndexChainValue {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
pub fn as_fn_call_args(self) -> StaticVec<Dynamic> {
|
pub fn as_fn_call_args(self) -> StaticVec<Dynamic> {
|
||||||
match self {
|
match self {
|
||||||
Self::None | Self::Value(_) => panic!("expecting IndexChainValue::FnCallArgs"),
|
Self::None | Self::Value(_) => unreachable!("expecting IndexChainValue::FnCallArgs"),
|
||||||
Self::FnCallArgs(value) => value,
|
Self::FnCallArgs(value) => value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -355,7 +369,7 @@ impl<'a> Target<'a> {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::StringChar(_, _, ch) => {
|
Self::StringChar(_, _, ch) => {
|
||||||
let char_value = ch.clone();
|
let char_value = ch.clone();
|
||||||
self.set_value((char_value, NO_POS)).unwrap();
|
self.set_value((char_value, Position::NONE)).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -449,7 +463,7 @@ impl<T: Into<Dynamic>> From<T> for Target<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _[INTERNALS]_ A type that holds all the current states of the Engine.
|
/// _(INTERNALS)_ A type that holds all the current states of the Engine.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// ## WARNING
|
/// ## WARNING
|
||||||
@ -478,7 +492,7 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _[INTERNALS]_ A type containing all the limits imposed by the `Engine`.
|
/// _(INTERNALS)_ A type containing all the limits imposed by the [`Engine`].
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// ## WARNING
|
/// ## WARNING
|
||||||
@ -528,12 +542,12 @@ pub struct EvalContext<'e, 'x, 'px: 'x, 'a, 's, 'm, 'pm: 'm, 't, 'pt: 't> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'e, 'x, 'px, 'a, 's, 'm, 'pm, 't, 'pt> EvalContext<'e, 'x, 'px, 'a, 's, 'm, 'pm, 't, 'pt> {
|
impl<'e, 'x, 'px, 'a, 's, 'm, 'pm, 't, 'pt> EvalContext<'e, 'x, 'px, 'a, 's, 'm, 'pm, 't, 'pt> {
|
||||||
/// The current `Engine`.
|
/// The current [`Engine`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn engine(&self) -> &'e Engine {
|
pub fn engine(&self) -> &'e Engine {
|
||||||
self.engine
|
self.engine
|
||||||
}
|
}
|
||||||
/// _[INTERNALS]_ The current set of modules imported via `import` statements.
|
/// _(INTERNALS)_ The current set of modules imported via `import` statements.
|
||||||
/// Available under the `internals` feature only.
|
/// Available under the `internals` feature only.
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -562,9 +576,10 @@ impl<'e, 'x, 'px, 'a, 's, 'm, 'pm, 't, 'pt> EvalContext<'e, 'x, 'px, 'a, 's, 'm,
|
|||||||
///
|
///
|
||||||
/// # Thread Safety
|
/// # Thread Safety
|
||||||
///
|
///
|
||||||
/// `Engine` is re-entrant.
|
/// [`Engine`] is re-entrant.
|
||||||
///
|
///
|
||||||
/// Currently, `Engine` is neither `Send` nor `Sync`. Use the `sync` feature to make it `Send + Sync`.
|
/// Currently, [`Engine`] is neither [`Send`] nor [`Sync`].
|
||||||
|
/// Use the [`Sync`] feature to make it [`Send`] `+` [`Sync`].
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -581,11 +596,11 @@ impl<'e, 'x, 'px, 'a, 's, 'm, 'pm, 't, 'pt> EvalContext<'e, 'x, 'px, 'a, 's, 'm,
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub struct Engine {
|
pub struct Engine {
|
||||||
/// A unique ID identifying this scripting `Engine`.
|
/// A unique ID identifying this scripting [`Engine`].
|
||||||
pub id: String,
|
pub id: String,
|
||||||
|
|
||||||
/// A module containing all functions directly loaded into the Engine.
|
/// A module containing all functions directly loaded into the Engine.
|
||||||
pub(crate) global_module: Module,
|
pub(crate) global_namespace: Module,
|
||||||
/// A collection of all library packages loaded into the Engine.
|
/// A collection of all library packages loaded into the Engine.
|
||||||
pub(crate) packages: PackagesCollection,
|
pub(crate) packages: PackagesCollection,
|
||||||
/// A collection of all sub-modules directly loaded into the Engine.
|
/// A collection of all sub-modules directly loaded into the Engine.
|
||||||
@ -670,7 +685,7 @@ fn default_print(_s: &str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Search for a module within an imports stack.
|
/// Search for a module within an imports stack.
|
||||||
/// Position in `EvalAltResult` is `None` and must be set afterwards.
|
/// [`Position`] in [`EvalAltResult`] is [`None`][Position::None] and must be set afterwards.
|
||||||
pub fn search_imports(
|
pub fn search_imports(
|
||||||
mods: &Imports,
|
mods: &Imports,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
@ -696,7 +711,7 @@ pub fn search_imports(
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
/// Create a new `Engine`
|
/// Create a new [`Engine`]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
// Create the new scripting Engine
|
// Create the new scripting Engine
|
||||||
@ -704,7 +719,7 @@ impl Engine {
|
|||||||
id: Default::default(),
|
id: Default::default(),
|
||||||
|
|
||||||
packages: Default::default(),
|
packages: Default::default(),
|
||||||
global_module: Default::default(),
|
global_namespace: Default::default(),
|
||||||
global_sub_modules: Default::default(),
|
global_sub_modules: Default::default(),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -759,15 +774,15 @@ impl Engine {
|
|||||||
engine
|
engine
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new `Engine` with minimal built-in functions.
|
/// Create a new [`Engine`] with minimal built-in functions.
|
||||||
/// Use the `load_package` method to load additional packages of functions.
|
/// Use the [`load_package`][Engine::load_package] method to load additional packages of functions.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new_raw() -> Self {
|
pub fn new_raw() -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: Default::default(),
|
id: Default::default(),
|
||||||
|
|
||||||
packages: Default::default(),
|
packages: Default::default(),
|
||||||
global_module: Default::default(),
|
global_namespace: Default::default(),
|
||||||
global_sub_modules: Default::default(),
|
global_sub_modules: Default::default(),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -915,7 +930,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Chain-evaluate a dot/index chain.
|
/// Chain-evaluate a dot/index chain.
|
||||||
/// Position in `EvalAltResult` is `None` and must be set afterwards.
|
/// [`Position`] in [`EvalAltResult`] is [`None`][Position::None] and must be set afterwards.
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
fn eval_dot_index_chain_helper(
|
fn eval_dot_index_chain_helper(
|
||||||
&self,
|
&self,
|
||||||
@ -925,7 +940,7 @@ impl Engine {
|
|||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
target: &mut Target,
|
target: &mut Target,
|
||||||
rhs: &Expr,
|
rhs: &Expr,
|
||||||
mut idx_values: StaticVec<IndexChainValue>,
|
idx_values: &mut StaticVec<IndexChainValue>,
|
||||||
chain_type: ChainType,
|
chain_type: ChainType,
|
||||||
level: usize,
|
level: usize,
|
||||||
new_val: Option<(Dynamic, Position)>,
|
new_val: Option<(Dynamic, Position)>,
|
||||||
@ -1006,14 +1021,14 @@ impl Engine {
|
|||||||
{
|
{
|
||||||
EvalAltResult::ErrorIndexingType(
|
EvalAltResult::ErrorIndexingType(
|
||||||
self.map_type_name(val_type_name).into(),
|
self.map_type_name(val_type_name).into(),
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
err => err,
|
err => err,
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Default::default())
|
Ok((Dynamic::UNIT, true))
|
||||||
}
|
}
|
||||||
// xxx[rhs]
|
// xxx[rhs]
|
||||||
_ => {
|
_ => {
|
||||||
@ -1038,7 +1053,7 @@ impl Engine {
|
|||||||
def_value,
|
def_value,
|
||||||
..
|
..
|
||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
let def_value = def_value.map(Into::<Dynamic>::into);
|
let def_value = def_value.as_ref();
|
||||||
let args = idx_val.as_fn_call_args();
|
let args = idx_val.as_fn_call_args();
|
||||||
self.make_method_call(
|
self.make_method_call(
|
||||||
mods, state, lib, name, *hash, target, args, def_value, *native, false,
|
mods, state, lib, name, *hash, target, args, def_value, *native, false,
|
||||||
@ -1112,7 +1127,7 @@ impl Engine {
|
|||||||
def_value,
|
def_value,
|
||||||
..
|
..
|
||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
let def_value = def_value.map(Into::<Dynamic>::into);
|
let def_value = def_value.as_ref();
|
||||||
let args = idx_val.as_fn_call_args();
|
let args = idx_val.as_fn_call_args();
|
||||||
let (val, _) = self
|
let (val, _) = self
|
||||||
.make_method_call(
|
.make_method_call(
|
||||||
@ -1178,7 +1193,7 @@ impl Engine {
|
|||||||
|err| match *err {
|
|err| match *err {
|
||||||
// If there is no setter, no need to feed it back because the property is read-only
|
// If there is no setter, no need to feed it back because the property is read-only
|
||||||
EvalAltResult::ErrorDotExpr(_, _) => {
|
EvalAltResult::ErrorDotExpr(_, _) => {
|
||||||
Ok(Default::default())
|
Ok((Dynamic::UNIT, false))
|
||||||
}
|
}
|
||||||
_ => Err(err.fill_position(*x_pos)),
|
_ => Err(err.fill_position(*x_pos)),
|
||||||
},
|
},
|
||||||
@ -1196,7 +1211,7 @@ impl Engine {
|
|||||||
def_value,
|
def_value,
|
||||||
..
|
..
|
||||||
} = f.as_ref();
|
} = f.as_ref();
|
||||||
let def_value = def_value.map(Into::<Dynamic>::into);
|
let def_value = def_value.as_ref();
|
||||||
let args = idx_val.as_fn_call_args();
|
let args = idx_val.as_fn_call_args();
|
||||||
let (mut val, _) = self
|
let (mut val, _) = self
|
||||||
.make_method_call(
|
.make_method_call(
|
||||||
@ -1247,19 +1262,10 @@ impl Engine {
|
|||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut idx_values = StaticVec::new();
|
let idx_values = &mut Default::default();
|
||||||
|
|
||||||
self.eval_indexed_chain(
|
self.eval_indexed_chain(
|
||||||
scope,
|
scope, mods, state, lib, this_ptr, rhs, chain_type, idx_values, 0, level,
|
||||||
mods,
|
|
||||||
state,
|
|
||||||
lib,
|
|
||||||
this_ptr,
|
|
||||||
rhs,
|
|
||||||
chain_type,
|
|
||||||
&mut idx_values,
|
|
||||||
0,
|
|
||||||
level,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
match lhs {
|
match lhs {
|
||||||
@ -1309,8 +1315,8 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a chain of indexes and store the results in a StaticVec.
|
/// Evaluate a chain of indexes and store the results in a [`StaticVec`].
|
||||||
/// StaticVec is used to avoid an allocation in the overwhelming cases of just a few levels of indexing.
|
/// [`StaticVec`] is used to avoid an allocation in the overwhelming cases of just a few levels of indexing.
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
fn eval_indexed_chain(
|
fn eval_indexed_chain(
|
||||||
&self,
|
&self,
|
||||||
@ -1384,8 +1390,8 @@ impl Engine {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the value at the indexed position of a base type
|
/// Get the value at the indexed position of a base type.
|
||||||
/// Position in `EvalAltResult` may be None and should be set afterwards.
|
/// [`Position`] in [`EvalAltResult`] may be None and should be set afterwards.
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
fn get_indexed_mut<'t>(
|
fn get_indexed_mut<'t>(
|
||||||
&self,
|
&self,
|
||||||
@ -1474,7 +1480,10 @@ impl Engine {
|
|||||||
.map(|(v, _)| v.into())
|
.map(|(v, _)| v.into())
|
||||||
.map_err(|err| match *err {
|
.map_err(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.ends_with(']') => {
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.ends_with(']') => {
|
||||||
Box::new(EvalAltResult::ErrorIndexingType(type_name.into(), NO_POS))
|
Box::new(EvalAltResult::ErrorIndexingType(
|
||||||
|
type_name.into(),
|
||||||
|
Position::NONE,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
_ => err,
|
_ => err,
|
||||||
})
|
})
|
||||||
@ -1482,13 +1491,13 @@ impl Engine {
|
|||||||
|
|
||||||
_ => EvalAltResult::ErrorIndexingType(
|
_ => EvalAltResult::ErrorIndexingType(
|
||||||
self.map_type_name(target.type_name()).into(),
|
self.map_type_name(target.type_name()).into(),
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate an 'in' expression
|
// Evaluate an 'in' expression.
|
||||||
fn eval_in_expr(
|
fn eval_in_expr(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1511,10 +1520,10 @@ impl Engine {
|
|||||||
Dynamic(Union::Array(mut rhs_value)) => {
|
Dynamic(Union::Array(mut rhs_value)) => {
|
||||||
// Call the `==` operator to compare each value
|
// Call the `==` operator to compare each value
|
||||||
let def_value = Some(false.into());
|
let def_value = Some(false.into());
|
||||||
|
let def_value = def_value.as_ref();
|
||||||
|
|
||||||
for value in rhs_value.iter_mut() {
|
for value in rhs_value.iter_mut() {
|
||||||
let args = &mut [&mut lhs_value.clone(), value];
|
let args = &mut [&mut lhs_value.clone(), value];
|
||||||
let def_value = def_value.clone();
|
|
||||||
|
|
||||||
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s.
|
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s.
|
||||||
let hash =
|
let hash =
|
||||||
@ -1552,7 +1561,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a `Target` from an expression.
|
/// Get a [`Target`] from an expression.
|
||||||
pub(crate) fn eval_expr_as_target<'s>(
|
pub(crate) fn eval_expr_as_target<'s>(
|
||||||
&self,
|
&self,
|
||||||
scope: &'s mut Scope,
|
scope: &'s mut Scope,
|
||||||
@ -1665,7 +1674,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate an expression
|
/// Evaluate an expression.
|
||||||
pub(crate) fn eval_expr(
|
pub(crate) fn eval_expr(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1705,7 +1714,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Statement block
|
// Statement block
|
||||||
Expr::Stmt(x, _) => {
|
Expr::Stmt(x, _) => {
|
||||||
self.eval_statements(scope, mods, state, lib, this_ptr, x.as_ref(), level)
|
self.eval_stmt_block(scope, mods, state, lib, this_ptr, x.as_ref(), level)
|
||||||
}
|
}
|
||||||
|
|
||||||
// lhs[idx_expr]
|
// lhs[idx_expr]
|
||||||
@ -1754,7 +1763,7 @@ impl Engine {
|
|||||||
def_value,
|
def_value,
|
||||||
..
|
..
|
||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
let def_value = def_value.map(Into::<Dynamic>::into);
|
let def_value = def_value.as_ref();
|
||||||
self.make_function_call(
|
self.make_function_call(
|
||||||
scope, mods, state, lib, this_ptr, name, args, def_value, *hash, *native,
|
scope, mods, state, lib, this_ptr, name, args, def_value, *hash, *native,
|
||||||
false, *cap_scope, level,
|
false, *cap_scope, level,
|
||||||
@ -1773,8 +1782,9 @@ impl Engine {
|
|||||||
..
|
..
|
||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
let namespace = namespace.as_ref().map(|v| v.as_ref());
|
let namespace = namespace.as_ref().map(|v| v.as_ref());
|
||||||
|
let def_value = def_value.as_ref();
|
||||||
self.make_qualified_function_call(
|
self.make_qualified_function_call(
|
||||||
scope, mods, state, lib, this_ptr, namespace, name, args, *def_value, *hash,
|
scope, mods, state, lib, this_ptr, namespace, name, args, def_value, *hash,
|
||||||
level,
|
level,
|
||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(*pos))
|
.map_err(|err| err.fill_position(*pos))
|
||||||
@ -1810,8 +1820,7 @@ impl Engine {
|
|||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::True(_) => Ok(true.into()),
|
Expr::BoolConstant(x, _) => Ok((*x).into()),
|
||||||
Expr::False(_) => Ok(false.into()),
|
|
||||||
Expr::Unit(_) => Ok(Dynamic::UNIT),
|
Expr::Unit(_) => Ok(Dynamic::UNIT),
|
||||||
|
|
||||||
Expr::Custom(custom, _) => {
|
Expr::Custom(custom, _) => {
|
||||||
@ -1839,7 +1848,8 @@ impl Engine {
|
|||||||
.map_err(|err| err.fill_position(expr.position()))
|
.map_err(|err| err.fill_position(expr.position()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn eval_statements<'a>(
|
/// Evaluate a statements block.
|
||||||
|
pub(crate) fn eval_stmt_block<'a>(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
mods: &mut Imports,
|
mods: &mut Imports,
|
||||||
@ -1849,6 +1859,7 @@ impl Engine {
|
|||||||
statements: impl IntoIterator<Item = &'a Stmt>,
|
statements: impl IntoIterator<Item = &'a Stmt>,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
let prev_always_search = state.always_search;
|
||||||
let prev_scope_len = scope.len();
|
let prev_scope_len = scope.len();
|
||||||
let prev_mods_len = mods.len();
|
let prev_mods_len = mods.len();
|
||||||
state.scope_level += 1;
|
state.scope_level += 1;
|
||||||
@ -1863,15 +1874,14 @@ impl Engine {
|
|||||||
mods.truncate(prev_mods_len);
|
mods.truncate(prev_mods_len);
|
||||||
state.scope_level -= 1;
|
state.scope_level -= 1;
|
||||||
|
|
||||||
// The impact of an eval statement goes away at the end of a block
|
// The impact of new local variables goes away at the end of a block
|
||||||
// because any new variables introduced will go out of scope
|
// because any new variables introduced will go out of scope
|
||||||
state.always_search = false;
|
state.always_search = prev_always_search;
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a statement
|
/// Evaluate a statement.
|
||||||
///
|
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
@ -1892,7 +1902,7 @@ impl Engine {
|
|||||||
|
|
||||||
let result = match stmt {
|
let result = match stmt {
|
||||||
// No-op
|
// No-op
|
||||||
Stmt::Noop(_) => Ok(Default::default()),
|
Stmt::Noop(_) => Ok(Dynamic::UNIT),
|
||||||
|
|
||||||
// Expression as statement
|
// Expression as statement
|
||||||
Stmt::Expr(expr) => self.eval_expr(scope, mods, state, lib, this_ptr, expr, level),
|
Stmt::Expr(expr) => self.eval_expr(scope, mods, state, lib, this_ptr, expr, level),
|
||||||
@ -1925,7 +1935,7 @@ impl Engine {
|
|||||||
} else {
|
} else {
|
||||||
*lhs_ptr.as_mut() = rhs_val;
|
*lhs_ptr.as_mut() = rhs_val;
|
||||||
}
|
}
|
||||||
Ok(Default::default())
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
// Op-assignment - in order of precedence:
|
// Op-assignment - in order of precedence:
|
||||||
ScopeEntryType::Normal => {
|
ScopeEntryType::Normal => {
|
||||||
@ -1939,7 +1949,7 @@ impl Engine {
|
|||||||
let hash_fn = calc_native_fn_hash(empty(), op, arg_types);
|
let hash_fn = calc_native_fn_hash(empty(), op, arg_types);
|
||||||
|
|
||||||
match self
|
match self
|
||||||
.global_module
|
.global_namespace
|
||||||
.get_fn(hash_fn, false)
|
.get_fn(hash_fn, false)
|
||||||
.or_else(|| self.packages.get_fn(hash_fn))
|
.or_else(|| self.packages.get_fn(hash_fn))
|
||||||
.or_else(|| mods.get_fn(hash_fn))
|
.or_else(|| mods.get_fn(hash_fn))
|
||||||
@ -1993,7 +2003,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Default::default())
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2035,7 +2045,7 @@ impl Engine {
|
|||||||
self.eval_dot_index_chain(
|
self.eval_dot_index_chain(
|
||||||
scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val,
|
scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val,
|
||||||
)?;
|
)?;
|
||||||
Ok(Default::default())
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
// dot_lhs.dot_rhs op= rhs
|
// dot_lhs.dot_rhs op= rhs
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
@ -2043,7 +2053,7 @@ impl Engine {
|
|||||||
self.eval_dot_index_chain(
|
self.eval_dot_index_chain(
|
||||||
scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val,
|
scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val,
|
||||||
)?;
|
)?;
|
||||||
Ok(Default::default())
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
// Non-lvalue expression (should be caught during parsing)
|
// Non-lvalue expression (should be caught during parsing)
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
@ -2052,7 +2062,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Block scope
|
// Block scope
|
||||||
Stmt::Block(statements, _) => {
|
Stmt::Block(statements, _) => {
|
||||||
self.eval_statements(scope, mods, state, lib, this_ptr, statements, level)
|
self.eval_stmt_block(scope, mods, state, lib, this_ptr, statements, level)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If statement
|
// If statement
|
||||||
@ -2067,7 +2077,7 @@ impl Engine {
|
|||||||
} else if let Some(stmt) = else_block {
|
} else if let Some(stmt) = else_block {
|
||||||
self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level)
|
self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level)
|
||||||
} else {
|
} else {
|
||||||
Ok(Default::default())
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -2105,28 +2115,40 @@ impl Engine {
|
|||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
EvalAltResult::LoopBreak(false, _) => (),
|
EvalAltResult::LoopBreak(false, _) => (),
|
||||||
EvalAltResult::LoopBreak(true, _) => return Ok(Default::default()),
|
EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT),
|
||||||
_ => return Err(err),
|
_ => return Err(err),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(false) => return Ok(Default::default()),
|
Ok(false) => return Ok(Dynamic::UNIT),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
return Err(self.make_type_mismatch_err::<bool>(err, expr.position()))
|
return Err(self.make_type_mismatch_err::<bool>(err, expr.position()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Loop statement
|
// Do loop
|
||||||
Stmt::Loop(block, _) => loop {
|
Stmt::Do(body, expr, is_while, _) => loop {
|
||||||
match self.eval_stmt(scope, mods, state, lib, this_ptr, block, level) {
|
match self.eval_stmt(scope, mods, state, lib, this_ptr, body, level) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
EvalAltResult::LoopBreak(false, _) => (),
|
EvalAltResult::LoopBreak(false, _) => continue,
|
||||||
EvalAltResult::LoopBreak(true, _) => return Ok(Default::default()),
|
EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT),
|
||||||
_ => return Err(err),
|
_ => return Err(err),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match self
|
||||||
|
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
||||||
|
.as_bool()
|
||||||
|
{
|
||||||
|
Ok(true) if !*is_while => return Ok(Dynamic::UNIT),
|
||||||
|
Ok(false) if *is_while => return Ok(Dynamic::UNIT),
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(err) => {
|
||||||
|
return Err(self.make_type_mismatch_err::<bool>(err, expr.position()))
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// For loop
|
// For loop
|
||||||
@ -2136,7 +2158,7 @@ impl Engine {
|
|||||||
let iter_type = iter_obj.type_id();
|
let iter_type = iter_obj.type_id();
|
||||||
|
|
||||||
let func = self
|
let func = self
|
||||||
.global_module
|
.global_namespace
|
||||||
.get_iter(iter_type)
|
.get_iter(iter_type)
|
||||||
.or_else(|| self.packages.get_iter(iter_type))
|
.or_else(|| self.packages.get_iter(iter_type))
|
||||||
.or_else(|| mods.get_iter(iter_type));
|
.or_else(|| mods.get_iter(iter_type));
|
||||||
@ -2177,7 +2199,7 @@ impl Engine {
|
|||||||
|
|
||||||
state.scope_level -= 1;
|
state.scope_level -= 1;
|
||||||
scope.rewind(scope.len() - 1);
|
scope.rewind(scope.len() - 1);
|
||||||
Ok(Default::default())
|
Ok(Dynamic::UNIT)
|
||||||
} else {
|
} else {
|
||||||
EvalAltResult::ErrorFor(expr.position()).into()
|
EvalAltResult::ErrorFor(expr.position()).into()
|
||||||
}
|
}
|
||||||
@ -2206,7 +2228,7 @@ impl Engine {
|
|||||||
let value = if let EvalAltResult::ErrorRuntime(ref x, _) = err {
|
let value = if let EvalAltResult::ErrorRuntime(ref x, _) = err {
|
||||||
x.clone()
|
x.clone()
|
||||||
} else {
|
} else {
|
||||||
err.set_position(NO_POS);
|
err.set_position(Position::NONE);
|
||||||
err.to_string().into()
|
err.to_string().into()
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2302,7 +2324,7 @@ impl Engine {
|
|||||||
if let Some(alias) = _alias {
|
if let Some(alias) = _alias {
|
||||||
scope.add_entry_alias(scope.len() - 1, alias);
|
scope.add_entry_alias(scope.len() - 1, alias);
|
||||||
}
|
}
|
||||||
Ok(Default::default())
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import statement
|
// Import statement
|
||||||
@ -2334,7 +2356,7 @@ impl Engine {
|
|||||||
|
|
||||||
state.modules += 1;
|
state.modules += 1;
|
||||||
|
|
||||||
Ok(Default::default())
|
Ok(Dynamic::UNIT)
|
||||||
} else {
|
} else {
|
||||||
Err(
|
Err(
|
||||||
EvalAltResult::ErrorModuleNotFound(path.to_string(), expr.position())
|
EvalAltResult::ErrorModuleNotFound(path.to_string(), expr.position())
|
||||||
@ -2359,7 +2381,7 @@ impl Engine {
|
|||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Default::default())
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Share statement
|
// Share statement
|
||||||
@ -2376,7 +2398,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
Ok(Default::default())
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2385,7 +2407,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Check a result to ensure that the data size is within allowable limit.
|
/// Check a result to ensure that the data size is within allowable limit.
|
||||||
/// Position in `EvalAltResult` may be None and should be set afterwards.
|
/// [`Position`] in [`EvalAltResult`] may be None and should be set afterwards.
|
||||||
#[cfg(feature = "unchecked")]
|
#[cfg(feature = "unchecked")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn check_data_size(
|
fn check_data_size(
|
||||||
@ -2396,7 +2418,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Check a result to ensure that the data size is within allowable limit.
|
/// Check a result to ensure that the data size is within allowable limit.
|
||||||
/// Position in `EvalAltResult` may be None and should be set afterwards.
|
/// [`Position`] in [`EvalAltResult`] may be None and should be set afterwards.
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
fn check_data_size(
|
fn check_data_size(
|
||||||
&self,
|
&self,
|
||||||
@ -2489,39 +2511,47 @@ impl Engine {
|
|||||||
let (_arr, _map, s) = calc_size(result.as_ref().unwrap());
|
let (_arr, _map, s) = calc_size(result.as_ref().unwrap());
|
||||||
|
|
||||||
if s > self.max_string_size() {
|
if s > self.max_string_size() {
|
||||||
return EvalAltResult::ErrorDataTooLarge("Length of string".to_string(), NO_POS).into();
|
return EvalAltResult::ErrorDataTooLarge(
|
||||||
|
"Length of string".to_string(),
|
||||||
|
Position::NONE,
|
||||||
|
)
|
||||||
|
.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
if _arr > self.max_array_size() {
|
if _arr > self.max_array_size() {
|
||||||
return EvalAltResult::ErrorDataTooLarge("Size of array".to_string(), NO_POS).into();
|
return EvalAltResult::ErrorDataTooLarge("Size of array".to_string(), Position::NONE)
|
||||||
|
.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
if _map > self.max_map_size() {
|
if _map > self.max_map_size() {
|
||||||
return EvalAltResult::ErrorDataTooLarge("Size of object map".to_string(), NO_POS)
|
return EvalAltResult::ErrorDataTooLarge(
|
||||||
.into();
|
"Size of object map".to_string(),
|
||||||
|
Position::NONE,
|
||||||
|
)
|
||||||
|
.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the number of operations stay within limit.
|
/// Check if the number of operations stay within limit.
|
||||||
/// Position in `EvalAltResult` is `None` and must be set afterwards.
|
/// [`Position`] in [`EvalAltResult`] is [`None`][Position::None] and must be set afterwards.
|
||||||
pub(crate) fn inc_operations(&self, state: &mut State) -> Result<(), Box<EvalAltResult>> {
|
pub(crate) fn inc_operations(&self, state: &mut State) -> Result<(), Box<EvalAltResult>> {
|
||||||
state.operations += 1;
|
state.operations += 1;
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
// Guard against too many operations
|
// Guard against too many operations
|
||||||
if self.max_operations() > 0 && state.operations > self.max_operations() {
|
if self.max_operations() > 0 && state.operations > self.max_operations() {
|
||||||
return EvalAltResult::ErrorTooManyOperations(NO_POS).into();
|
return EvalAltResult::ErrorTooManyOperations(Position::NONE).into();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Report progress - only in steps
|
// Report progress - only in steps
|
||||||
if let Some(progress) = &self.progress {
|
if let Some(progress) = &self.progress {
|
||||||
if let Some(token) = progress(&state.operations) {
|
if let Some(token) = progress(&state.operations) {
|
||||||
// Terminate script if progress returns a termination token
|
// Terminate script if progress returns a termination token
|
||||||
return EvalAltResult::ErrorTerminated(token, NO_POS).into();
|
return EvalAltResult::ErrorTerminated(token, Position::NONE).into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2537,7 +2567,7 @@ impl Engine {
|
|||||||
.unwrap_or_else(|| map_std_type_name(name))
|
.unwrap_or_else(|| map_std_type_name(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Make a Box<EvalAltResult<ErrorMismatchDataType>>.
|
/// Make a `Box<`[`EvalAltResult<ErrorMismatchDataType>`][EvalAltResult::ErrorMismatchDataType]`>`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn make_type_mismatch_err<T>(&self, typ: &str, pos: Position) -> Box<EvalAltResult> {
|
pub(crate) fn make_type_mismatch_err<T>(&self, typ: &str, pos: Position) -> Box<EvalAltResult> {
|
||||||
EvalAltResult::ErrorMismatchDataType(
|
EvalAltResult::ErrorMismatchDataType(
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//! Module that defines the extern API of `Engine`.
|
//! Module that defines the extern API of [`Engine`].
|
||||||
|
|
||||||
use crate::dynamic::Variant;
|
use crate::dynamic::Variant;
|
||||||
use crate::engine::{EvalContext, Imports};
|
use crate::engine::{EvalContext, Imports};
|
||||||
@ -13,7 +13,7 @@ use crate::stdlib::{
|
|||||||
use crate::utils::get_hasher;
|
use crate::utils::get_hasher;
|
||||||
use crate::{
|
use crate::{
|
||||||
scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, NativeCallContext,
|
scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, NativeCallContext,
|
||||||
ParseError, AST, NO_POS,
|
ParseError, Position, AST,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -31,14 +31,14 @@ fn calc_hash_for_scripts<'a>(scripts: impl IntoIterator<Item = &'a &'a str>) ->
|
|||||||
|
|
||||||
/// Engine public API
|
/// Engine public API
|
||||||
impl Engine {
|
impl Engine {
|
||||||
/// Register a function of the `Engine`.
|
/// Register a function of the [`Engine`].
|
||||||
///
|
///
|
||||||
/// ## WARNING - Low Level API
|
/// ## WARNING - Low Level API
|
||||||
///
|
///
|
||||||
/// This function is very low level. It takes a list of `TypeId`'s indicating the actual types of the parameters.
|
/// This function is very low level. It takes a list of [`TypeId`]'s indicating the actual types of the parameters.
|
||||||
///
|
///
|
||||||
/// Arguments are simply passed in as a mutable array of `&mut Dynamic`,
|
/// Arguments are simply passed in as a mutable array of [`&mut Dynamic`][Dynamic],
|
||||||
/// The arguments are guaranteed to be of the correct types matching the `TypeId`'s.
|
/// The arguments are guaranteed to be of the correct types matching the [`TypeId`]'s.
|
||||||
///
|
///
|
||||||
/// To access a primary parameter value (i.e. cloning is cheap), use: `args[n].clone().cast::<T>()`
|
/// To access a primary parameter value (i.e. cloning is cheap), use: `args[n].clone().cast::<T>()`
|
||||||
///
|
///
|
||||||
@ -56,12 +56,17 @@ impl Engine {
|
|||||||
+ SendSync
|
+ SendSync
|
||||||
+ 'static,
|
+ 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.global_module
|
self.global_namespace.set_raw_fn(
|
||||||
.set_raw_fn(name, FnNamespace::Global, FnAccess::Public, arg_types, func);
|
name,
|
||||||
|
FnNamespace::Global,
|
||||||
|
FnAccess::Public,
|
||||||
|
arg_types,
|
||||||
|
func,
|
||||||
|
);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Register a custom type for use with the `Engine`.
|
/// Register a custom type for use with the [`Engine`].
|
||||||
/// The type must implement `Clone`.
|
/// The type must implement [`Clone`].
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -101,8 +106,8 @@ impl Engine {
|
|||||||
pub fn register_type<T: Variant + Clone>(&mut self) -> &mut Self {
|
pub fn register_type<T: Variant + Clone>(&mut self) -> &mut Self {
|
||||||
self.register_type_with_name::<T>(type_name::<T>())
|
self.register_type_with_name::<T>(type_name::<T>())
|
||||||
}
|
}
|
||||||
/// Register a custom type for use with the `Engine`, with a pretty-print name
|
/// Register a custom type for use with the [`Engine`], with a pretty-print name
|
||||||
/// for the `type_of` function. The type must implement `Clone`.
|
/// for the `type_of` function. The type must implement [`Clone`].
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -151,7 +156,7 @@ impl Engine {
|
|||||||
self.type_names.insert(type_name::<T>().into(), name.into());
|
self.type_names.insert(type_name::<T>().into(), name.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Register an iterator adapter for an iterable type with the `Engine`.
|
/// Register an iterator adapter for an iterable type with the [`Engine`].
|
||||||
/// This is an advanced feature.
|
/// This is an advanced feature.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn register_iterator<T>(&mut self) -> &mut Self
|
pub fn register_iterator<T>(&mut self) -> &mut Self
|
||||||
@ -159,10 +164,10 @@ impl Engine {
|
|||||||
T: Variant + Clone + Iterator,
|
T: Variant + Clone + Iterator,
|
||||||
<T as Iterator>::Item: Variant + Clone,
|
<T as Iterator>::Item: Variant + Clone,
|
||||||
{
|
{
|
||||||
self.global_module.set_iterable::<T>();
|
self.global_namespace.set_iterable::<T>();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// 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`].
|
||||||
///
|
///
|
||||||
/// The function signature must start with `&mut self` and not `&self`.
|
/// The function signature must start with `&mut self` and not `&self`.
|
||||||
///
|
///
|
||||||
@ -206,8 +211,7 @@ impl Engine {
|
|||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
crate::RegisterFn::register_fn(self, &crate::engine::make_getter(name), callback)
|
crate::RegisterFn::register_fn(self, &crate::engine::make_getter(name), callback)
|
||||||
}
|
}
|
||||||
/// 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`].
|
||||||
/// Returns `Result<Dynamic, Box<EvalAltResult>>`.
|
|
||||||
///
|
///
|
||||||
/// The function signature must start with `&mut self` and not `&self`.
|
/// The function signature must start with `&mut self` and not `&self`.
|
||||||
///
|
///
|
||||||
@ -257,7 +261,7 @@ impl Engine {
|
|||||||
callback,
|
callback,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/// 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`].
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -302,8 +306,7 @@ impl Engine {
|
|||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
crate::RegisterFn::register_fn(self, &crate::engine::make_setter(name), callback)
|
crate::RegisterFn::register_fn(self, &crate::engine::make_setter(name), callback)
|
||||||
}
|
}
|
||||||
/// 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`].
|
||||||
/// Returns `Result<(), Box<EvalAltResult>>`.
|
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -356,7 +359,7 @@ impl Engine {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
/// 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`].
|
||||||
///
|
///
|
||||||
/// All function signatures must start with `&mut self` and not `&self`.
|
/// All function signatures must start with `&mut self` and not `&self`.
|
||||||
///
|
///
|
||||||
@ -403,13 +406,13 @@ impl Engine {
|
|||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.register_get(name, get_fn).register_set(name, set_fn)
|
self.register_get(name, get_fn).register_set(name, set_fn)
|
||||||
}
|
}
|
||||||
/// Register an index getter for a custom type with the `Engine`.
|
/// Register an index getter for a custom type with the [`Engine`].
|
||||||
///
|
///
|
||||||
/// The function signature must start with `&mut self` and not `&self`.
|
/// The function signature must start with `&mut self` and not `&self`.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if the type is `Array` or `Map`.
|
/// Panics if the type is [`Array`], [`Map`], [`String`], [`ImmutableString`][crate::ImmutableString] or `&str`.
|
||||||
/// Indexers for arrays, object maps and strings cannot be registered.
|
/// Indexers for arrays, object maps and strings cannot be registered.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
@ -466,14 +469,13 @@ impl Engine {
|
|||||||
|
|
||||||
crate::RegisterFn::register_fn(self, crate::engine::FN_IDX_GET, callback)
|
crate::RegisterFn::register_fn(self, crate::engine::FN_IDX_GET, callback)
|
||||||
}
|
}
|
||||||
/// Register an index getter for a custom type with the `Engine`.
|
/// Register an index getter for a custom type with the [`Engine`].
|
||||||
/// Returns `Result<Dynamic, Box<EvalAltResult>>`.
|
|
||||||
///
|
///
|
||||||
/// The function signature must start with `&mut self` and not `&self`.
|
/// The function signature must start with `&mut self` and not `&self`.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if the type is `Array` or `Map`.
|
/// Panics if the type is [`Array`], [`Map`], [`String`], [`ImmutableString`][crate::ImmutableString] or `&str`.
|
||||||
/// Indexers for arrays, object maps and strings cannot be registered.
|
/// Indexers for arrays, object maps and strings cannot be registered.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
@ -532,11 +534,11 @@ impl Engine {
|
|||||||
|
|
||||||
crate::RegisterResultFn::register_result_fn(self, crate::engine::FN_IDX_GET, callback)
|
crate::RegisterResultFn::register_result_fn(self, crate::engine::FN_IDX_GET, callback)
|
||||||
}
|
}
|
||||||
/// Register an index setter for a custom type with the `Engine`.
|
/// Register an index setter for a custom type with the [`Engine`].
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if the type is `Array` or `Map`.
|
/// Panics if the type is [`Array`], [`Map`], [`String`], [`ImmutableString`][crate::ImmutableString] or `&str`.
|
||||||
/// Indexers for arrays, object maps and strings cannot be registered.
|
/// Indexers for arrays, object maps and strings cannot be registered.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
@ -595,12 +597,11 @@ impl Engine {
|
|||||||
|
|
||||||
crate::RegisterFn::register_fn(self, crate::engine::FN_IDX_SET, callback)
|
crate::RegisterFn::register_fn(self, crate::engine::FN_IDX_SET, callback)
|
||||||
}
|
}
|
||||||
/// Register an index setter for a custom type with the `Engine`.
|
/// Register an index setter for a custom type with the [`Engine`].
|
||||||
/// Returns `Result<(), Box<EvalAltResult>>`.
|
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if the type is `Array` or `Map`.
|
/// Panics if the type is [`Array`], [`Map`], [`String`], [`ImmutableString`][crate::ImmutableString] or `&str`.
|
||||||
/// Indexers for arrays, object maps and strings cannot be registered.
|
/// Indexers for arrays, object maps and strings cannot be registered.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
@ -670,11 +671,11 @@ impl Engine {
|
|||||||
move |obj: &mut T, index: X, value: U| callback(obj, index, value).map(Into::into),
|
move |obj: &mut T, index: X, value: U| callback(obj, index, value).map(Into::into),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/// Short-hand for register both index getter and setter functions for a custom type with the `Engine`.
|
/// Short-hand for register both index getter and setter functions for a custom type with the [`Engine`].
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if the type is `Array` or `Map`.
|
/// Panics if the type is [`Array`], [`Map`], [`String`], [`ImmutableString`][crate::ImmutableString] or `&str`.
|
||||||
/// Indexers for arrays, object maps and strings cannot be registered.
|
/// Indexers for arrays, object maps and strings cannot be registered.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
@ -720,7 +721,7 @@ impl Engine {
|
|||||||
self.register_indexer_get(getter)
|
self.register_indexer_get(getter)
|
||||||
.register_indexer_set(setter)
|
.register_indexer_set(setter)
|
||||||
}
|
}
|
||||||
/// Register a `Module` as a fixed module namespace with the `Engine`.
|
/// Register a [`Module`][crate::Module] as a fixed module namespace with the [`Engine`].
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -759,7 +760,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Compile a string into an `AST`, which can be used later for evaluation.
|
/// Compile a string into an [`AST`], which can be used later for evaluation.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -782,10 +783,10 @@ impl Engine {
|
|||||||
pub fn compile(&self, script: &str) -> Result<AST, ParseError> {
|
pub fn compile(&self, script: &str) -> Result<AST, ParseError> {
|
||||||
self.compile_with_scope(&Default::default(), script)
|
self.compile_with_scope(&Default::default(), 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.
|
||||||
///
|
///
|
||||||
/// The scope is useful for passing constants into the script for optimization
|
/// The scope is useful for passing constants into the script for optimization
|
||||||
/// when using `OptimizationLevel::Full`.
|
/// when using [`OptimizationLevel::Full`].
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -825,10 +826,10 @@ impl Engine {
|
|||||||
self.compile_scripts_with_scope(scope, &[script])
|
self.compile_scripts_with_scope(scope, &[script])
|
||||||
}
|
}
|
||||||
/// When passed a list of strings, first join the strings into one large script,
|
/// When passed a list of strings, first join the strings into one large script,
|
||||||
/// and then compile them into an `AST` using own scope, which can be used later for evaluation.
|
/// and then compile them into an [`AST`] using own scope, which can be used later for evaluation.
|
||||||
///
|
///
|
||||||
/// The scope is useful for passing constants into the script for optimization
|
/// The scope is useful for passing constants into the script for optimization
|
||||||
/// when using `OptimizationLevel::Full`.
|
/// when using [`OptimizationLevel::Full`].
|
||||||
///
|
///
|
||||||
/// ## Note
|
/// ## Note
|
||||||
///
|
///
|
||||||
@ -878,7 +879,7 @@ impl Engine {
|
|||||||
) -> Result<AST, ParseError> {
|
) -> Result<AST, ParseError> {
|
||||||
self.compile_with_scope_and_optimization_level(scope, scripts, self.optimization_level)
|
self.compile_with_scope_and_optimization_level(scope, scripts, self.optimization_level)
|
||||||
}
|
}
|
||||||
/// Join a list of strings and compile into an `AST` using own scope at a specific optimization level.
|
/// Join a list of strings and compile into an [`AST`] using own scope at a specific optimization level.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn compile_with_scope_and_optimization_level(
|
pub(crate) fn compile_with_scope_and_optimization_level(
|
||||||
&self,
|
&self,
|
||||||
@ -915,7 +916,7 @@ impl Engine {
|
|||||||
|
|
||||||
Ok(contents)
|
Ok(contents)
|
||||||
}
|
}
|
||||||
/// Compile a script file into an `AST`, which can be used later for evaluation.
|
/// Compile a script file into an [`AST`], which can be used later for evaluation.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -944,10 +945,10 @@ impl Engine {
|
|||||||
) -> Result<AST, Box<EvalAltResult>> {
|
) -> Result<AST, Box<EvalAltResult>> {
|
||||||
self.compile_file_with_scope(&Default::default(), path)
|
self.compile_file_with_scope(&Default::default(), 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.
|
||||||
///
|
///
|
||||||
/// The scope is useful for passing constants into the script for optimization
|
/// The scope is useful for passing constants into the script for optimization
|
||||||
/// when using `OptimizationLevel::Full`.
|
/// when using [`OptimizationLevel::Full`].
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -1025,7 +1026,7 @@ impl Engine {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
pub fn parse_json(&self, json: &str, has_null: bool) -> Result<Map, Box<EvalAltResult>> {
|
pub fn parse_json(&self, json: &str, has_null: bool) -> Result<Map, Box<EvalAltResult>> {
|
||||||
use crate::token::{Position, Token};
|
use crate::token::Token;
|
||||||
|
|
||||||
let mut scope = Default::default();
|
let mut scope = Default::default();
|
||||||
|
|
||||||
@ -1071,7 +1072,7 @@ impl Engine {
|
|||||||
|
|
||||||
self.eval_ast_with_scope(&mut scope, &ast)
|
self.eval_ast_with_scope(&mut scope, &ast)
|
||||||
}
|
}
|
||||||
/// Compile a string containing an expression into an `AST`,
|
/// Compile a string containing an expression into an [`AST`],
|
||||||
/// which can be used later for evaluation.
|
/// which can be used later for evaluation.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
@ -1095,11 +1096,11 @@ impl Engine {
|
|||||||
pub fn compile_expression(&self, script: &str) -> Result<AST, ParseError> {
|
pub fn compile_expression(&self, script: &str) -> Result<AST, ParseError> {
|
||||||
self.compile_expression_with_scope(&Default::default(), script)
|
self.compile_expression_with_scope(&Default::default(), 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,
|
||||||
/// which can be used later for evaluation.
|
/// which can be used later for evaluation.
|
||||||
///
|
///
|
||||||
/// The scope is useful for passing constants into the script for optimization
|
/// The scope is useful for passing constants into the script for optimization
|
||||||
/// when using `OptimizationLevel::Full`.
|
/// when using [`OptimizationLevel::Full`].
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -1308,7 +1309,7 @@ impl Engine {
|
|||||||
|
|
||||||
self.eval_ast_with_scope(scope, &ast)
|
self.eval_ast_with_scope(scope, &ast)
|
||||||
}
|
}
|
||||||
/// Evaluate an `AST`.
|
/// Evaluate an [`AST`].
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -1330,7 +1331,7 @@ impl Engine {
|
|||||||
pub fn eval_ast<T: Variant + Clone>(&self, ast: &AST) -> Result<T, Box<EvalAltResult>> {
|
pub fn eval_ast<T: Variant + Clone>(&self, ast: &AST) -> Result<T, Box<EvalAltResult>> {
|
||||||
self.eval_ast_with_scope(&mut Default::default(), ast)
|
self.eval_ast_with_scope(&mut Default::default(), ast)
|
||||||
}
|
}
|
||||||
/// Evaluate an `AST` with own scope.
|
/// Evaluate an [`AST`] with own scope.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -1375,12 +1376,12 @@ impl Engine {
|
|||||||
EvalAltResult::ErrorMismatchOutputType(
|
EvalAltResult::ErrorMismatchOutputType(
|
||||||
self.map_type_name(type_name::<T>()).into(),
|
self.map_type_name(type_name::<T>()).into(),
|
||||||
typ.into(),
|
typ.into(),
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
/// Evaluate an `AST` with own scope.
|
/// Evaluate an [`AST`] with own scope.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn eval_ast_with_scope_raw<'a>(
|
pub(crate) fn eval_ast_with_scope_raw<'a>(
|
||||||
&self,
|
&self,
|
||||||
@ -1439,7 +1440,7 @@ impl Engine {
|
|||||||
pub fn consume_ast(&self, ast: &AST) -> Result<(), Box<EvalAltResult>> {
|
pub fn consume_ast(&self, ast: &AST) -> Result<(), Box<EvalAltResult>> {
|
||||||
self.consume_ast_with_scope(&mut Default::default(), ast)
|
self.consume_ast_with_scope(&mut Default::default(), ast)
|
||||||
}
|
}
|
||||||
/// Evaluate an `AST` with own scope, but throw away the result and only return error (if any).
|
/// Evaluate an [`AST`] with own scope, but throw away the result and only return error (if any).
|
||||||
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn consume_ast_with_scope(
|
pub fn consume_ast_with_scope(
|
||||||
@ -1452,7 +1453,7 @@ impl Engine {
|
|||||||
self.eval_statements_raw(scope, &mut mods, ast.statements(), &[ast.lib()])
|
self.eval_statements_raw(scope, &mut mods, ast.statements(), &[ast.lib()])
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
}
|
}
|
||||||
/// Call a script function defined in an `AST` with multiple arguments.
|
/// Call a script function defined in an [`AST`] with multiple arguments.
|
||||||
/// Arguments are passed as a tuple.
|
/// Arguments are passed as a tuple.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
@ -1509,13 +1510,13 @@ impl Engine {
|
|||||||
EvalAltResult::ErrorMismatchOutputType(
|
EvalAltResult::ErrorMismatchOutputType(
|
||||||
self.map_type_name(type_name::<T>()).into(),
|
self.map_type_name(type_name::<T>()).into(),
|
||||||
typ.into(),
|
typ.into(),
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
/// Call a script function defined in an `AST` with multiple `Dynamic` arguments
|
/// Call a script function defined in an [`AST`] with multiple `Dynamic` arguments
|
||||||
/// and optionally a value for binding to the 'this' pointer.
|
/// and optionally a value for binding to the `this` pointer.
|
||||||
///
|
///
|
||||||
/// ## WARNING
|
/// ## WARNING
|
||||||
///
|
///
|
||||||
@ -1577,7 +1578,7 @@ impl Engine {
|
|||||||
|
|
||||||
self.call_fn_dynamic_raw(scope, &[lib.as_ref()], name, &mut this_ptr, args.as_mut())
|
self.call_fn_dynamic_raw(scope, &[lib.as_ref()], name, &mut this_ptr, args.as_mut())
|
||||||
}
|
}
|
||||||
/// Call a script function defined in an `AST` with multiple `Dynamic` arguments.
|
/// Call a script function defined in an [`AST`] with multiple `Dynamic` arguments.
|
||||||
///
|
///
|
||||||
/// ## WARNING
|
/// ## WARNING
|
||||||
///
|
///
|
||||||
@ -1598,7 +1599,7 @@ impl Engine {
|
|||||||
let fn_def = lib
|
let fn_def = lib
|
||||||
.iter()
|
.iter()
|
||||||
.find_map(|&m| m.get_script_fn(name, args.len(), true))
|
.find_map(|&m| m.get_script_fn(name, args.len(), true))
|
||||||
.ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), NO_POS))?;
|
.ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::NONE))?;
|
||||||
|
|
||||||
let mut state = Default::default();
|
let mut state = Default::default();
|
||||||
let mut mods = self.global_sub_modules.clone();
|
let mut mods = self.global_sub_modules.clone();
|
||||||
@ -1610,17 +1611,19 @@ impl Engine {
|
|||||||
|
|
||||||
self.call_script_fn(scope, &mut mods, &mut state, lib, this_ptr, fn_def, args, 0)
|
self.call_script_fn(scope, &mut mods, &mut state, lib, this_ptr, fn_def, args, 0)
|
||||||
}
|
}
|
||||||
/// Optimize the `AST` with constants defined in an external Scope.
|
/// Optimize the [`AST`] with constants defined in an external Scope.
|
||||||
/// An optimized copy of the `AST` is returned while the original `AST` is consumed.
|
/// An optimized copy of the [`AST`] is returned while the original [`AST`] is consumed.
|
||||||
///
|
///
|
||||||
/// Although optimization is performed by default during compilation, sometimes it is necessary to
|
/// Although optimization is performed by default during compilation, sometimes it is necessary to
|
||||||
/// _re_-optimize an AST. For example, when working with constants that are passed in via an
|
/// _re_-optimize an [`AST`]. For example, when working with constants that are passed in via an
|
||||||
/// external scope, it will be more efficient to optimize the `AST` once again to take advantage
|
/// external scope, it will be more efficient to optimize the [`AST`] once again to take advantage
|
||||||
/// of the new constants.
|
/// of the new constants.
|
||||||
///
|
///
|
||||||
/// With this method, it is no longer necessary to recompile a large script. The script `AST` can be
|
/// With this method, it is no longer necessary to recompile a large script.
|
||||||
/// compiled just once. Before evaluation, constants are passed into the `Engine` via an external scope
|
/// The script [`AST`] can be compiled just once. Before evaluation,
|
||||||
/// (i.e. with `scope.push_constant(...)`). Then, the `AST is cloned and the copy re-optimized before running.
|
/// constants are passed into the [`Engine`] via an external scope
|
||||||
|
/// (i.e. with [`scope.push_constant(...)`][crate::Scope::push_constant]).
|
||||||
|
/// Then, the [`AST`] is cloned and the copy re-optimized before running.
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn optimize_ast(
|
pub fn optimize_ast(
|
||||||
@ -1727,7 +1730,7 @@ impl Engine {
|
|||||||
self.progress = Some(Box::new(callback));
|
self.progress = Some(Box::new(callback));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Override default action of `print` (print to stdout using `println!`)
|
/// Override default action of `print` (print to stdout using [`println!`])
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -1756,7 +1759,7 @@ impl Engine {
|
|||||||
self.print = Box::new(callback);
|
self.print = Box::new(callback);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Override default action of `debug` (print to stdout using `println!`)
|
/// Override default action of `debug` (print to stdout using [`println!`])
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//! Configuration settings for `Engine`.
|
//! Configuration settings for [`Engine`].
|
||||||
|
|
||||||
use crate::packages::PackageLibrary;
|
use crate::packages::PackageLibrary;
|
||||||
use crate::stdlib::{format, string::String};
|
use crate::stdlib::{format, string::String};
|
||||||
@ -9,8 +9,8 @@ use crate::Engine;
|
|||||||
use crate::stdlib::boxed::Box;
|
use crate::stdlib::boxed::Box;
|
||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
/// Load a new package into the `Engine`.
|
/// Load a new package into the [`Engine`].
|
||||||
/// Anything that can be converted into a `PackageLibrary` is accepted, including a simple `Module`.
|
/// A simple [`Module`][crate::Module] is automatically converted into a package.
|
||||||
///
|
///
|
||||||
/// When searching for functions, packages loaded later are preferred.
|
/// When searching for functions, packages loaded later are preferred.
|
||||||
/// In other words, loaded packages are searched in reverse order.
|
/// In other words, loaded packages are searched in reverse order.
|
||||||
@ -19,7 +19,7 @@ impl Engine {
|
|||||||
self.packages.add(package.into());
|
self.packages.add(package.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Control whether and how the `Engine` will optimize an AST after compilation.
|
/// Control whether and how the [`Engine`] will optimize an [`AST`][crate::AST] after compilation.
|
||||||
///
|
///
|
||||||
/// Not available under the `no_optimize` feature.
|
/// Not available under the `no_optimize` feature.
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
@ -32,7 +32,7 @@ impl Engine {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// The current optimization level.
|
/// The current optimization level.
|
||||||
/// It controls whether and how the `Engine` will optimize an AST after compilation.
|
/// It controls whether and how the [`Engine`] will optimize an [`AST`][crate::AST] after compilation.
|
||||||
///
|
///
|
||||||
/// Not available under the `no_optimize` feature.
|
/// Not available under the `no_optimize` feature.
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
@ -166,7 +166,7 @@ impl Engine {
|
|||||||
pub fn max_map_size(&self) -> usize {
|
pub fn max_map_size(&self) -> usize {
|
||||||
self.limits.max_map_size
|
self.limits.max_map_size
|
||||||
}
|
}
|
||||||
/// Set the module resolution service used by the `Engine`.
|
/// Set the module resolution service used by the [`Engine`].
|
||||||
///
|
///
|
||||||
/// Not available under the `no_module` feature.
|
/// Not available under the `no_module` feature.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//! Helper module which defines `FuncArgs` to make function calling easier.
|
//! Helper module which defines [`FuncArgs`] to make function calling easier.
|
||||||
|
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
@ -9,12 +9,12 @@ use crate::{Dynamic, StaticVec};
|
|||||||
/// Any data type that can be converted into a `Vec<Dynamic>` can be used
|
/// Any data type that can be converted into a `Vec<Dynamic>` can be used
|
||||||
/// as arguments to a function call.
|
/// as arguments to a function call.
|
||||||
pub trait FuncArgs {
|
pub trait FuncArgs {
|
||||||
/// Convert to a `StaticVec<Dynamic>` of the function call arguments.
|
/// Convert to a [`StaticVec`]`<`[`Dynamic`]`>` of the function call arguments.
|
||||||
fn into_vec(self) -> StaticVec<Dynamic>;
|
fn into_vec(self) -> StaticVec<Dynamic>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Macro to implement `FuncArgs` for tuples of standard types (each can be
|
/// Macro to implement [`FuncArgs`] for tuples of standard types (each can be
|
||||||
/// converted into `Dynamic`).
|
/// converted into a [`Dynamic`]).
|
||||||
macro_rules! impl_args {
|
macro_rules! impl_args {
|
||||||
($($p:ident),*) => {
|
($($p:ident),*) => {
|
||||||
impl<$($p: Variant + Clone),*> FuncArgs for ($($p,)*)
|
impl<$($p: Variant + Clone),*> FuncArgs for ($($p,)*)
|
||||||
|
108
src/fn_call.rs
108
src/fn_call.rs
@ -1,4 +1,4 @@
|
|||||||
//! Implement function-calling mechanism for `Engine`.
|
//! Implement function-calling mechanism for [`Engine`].
|
||||||
|
|
||||||
use crate::ast::{Expr, Stmt};
|
use crate::ast::{Expr, Stmt};
|
||||||
use crate::engine::{
|
use crate::engine::{
|
||||||
@ -23,7 +23,7 @@ use crate::stdlib::{
|
|||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_native_fn_hash, calc_script_fn_hash, Dynamic, Engine, EvalAltResult, FnPtr,
|
calc_native_fn_hash, calc_script_fn_hash, Dynamic, Engine, EvalAltResult, FnPtr,
|
||||||
ImmutableString, Module, ParseErrorType, Scope, StaticVec, INT, NO_POS,
|
ImmutableString, Module, ParseErrorType, Position, Scope, StaticVec, INT,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
@ -140,7 +140,7 @@ pub fn ensure_no_data_race(
|
|||||||
{
|
{
|
||||||
return EvalAltResult::ErrorDataRace(
|
return EvalAltResult::ErrorDataRace(
|
||||||
format!("argument #{} of function '{}'", n + 1 + skip, fn_name),
|
format!("argument #{} of function '{}'", n + 1 + skip, fn_name),
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
@ -150,8 +150,8 @@ pub fn ensure_no_data_race(
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
/// Call a native Rust function registered with the `Engine`.
|
/// Call a native Rust function registered with the [`Engine`].
|
||||||
/// Position in `EvalAltResult` is `None` and must be set afterwards.
|
/// [`Position`] in [`EvalAltResult`] is [`None`][Position::None] and must be set afterwards.
|
||||||
///
|
///
|
||||||
/// ## WARNING
|
/// ## WARNING
|
||||||
///
|
///
|
||||||
@ -168,7 +168,7 @@ impl Engine {
|
|||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
is_ref: bool,
|
is_ref: bool,
|
||||||
pub_only: bool,
|
pub_only: bool,
|
||||||
def_val: Option<Dynamic>,
|
def_val: Option<&Dynamic>,
|
||||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||||
self.inc_operations(state)?;
|
self.inc_operations(state)?;
|
||||||
|
|
||||||
@ -176,7 +176,7 @@ impl Engine {
|
|||||||
// First search registered functions (can override packages)
|
// First search registered functions (can override packages)
|
||||||
// Then search packages
|
// Then search packages
|
||||||
let func = //lib.get_fn(hash_fn, pub_only)
|
let func = //lib.get_fn(hash_fn, pub_only)
|
||||||
self.global_module.get_fn(hash_fn, pub_only)
|
self.global_namespace.get_fn(hash_fn, pub_only)
|
||||||
.or_else(|| self.packages.get_fn(hash_fn))
|
.or_else(|| self.packages.get_fn(hash_fn))
|
||||||
.or_else(|| mods.get_fn(hash_fn));
|
.or_else(|| mods.get_fn(hash_fn));
|
||||||
|
|
||||||
@ -206,7 +206,7 @@ impl Engine {
|
|||||||
EvalAltResult::ErrorMismatchOutputType(
|
EvalAltResult::ErrorMismatchOutputType(
|
||||||
self.map_type_name(type_name::<ImmutableString>()).into(),
|
self.map_type_name(type_name::<ImmutableString>()).into(),
|
||||||
typ.into(),
|
typ.into(),
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
)
|
)
|
||||||
})?)
|
})?)
|
||||||
.into(),
|
.into(),
|
||||||
@ -217,7 +217,7 @@ impl Engine {
|
|||||||
EvalAltResult::ErrorMismatchOutputType(
|
EvalAltResult::ErrorMismatchOutputType(
|
||||||
self.map_type_name(type_name::<ImmutableString>()).into(),
|
self.map_type_name(type_name::<ImmutableString>()).into(),
|
||||||
typ.into(),
|
typ.into(),
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
)
|
)
|
||||||
})?)
|
})?)
|
||||||
.into(),
|
.into(),
|
||||||
@ -237,7 +237,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Return default value (if any)
|
// Return default value (if any)
|
||||||
if let Some(val) = def_val {
|
if let Some(val) = def_val {
|
||||||
return Ok((val, false));
|
return Ok((val.clone(), false));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getter function not found?
|
// Getter function not found?
|
||||||
@ -249,7 +249,7 @@ impl Engine {
|
|||||||
prop,
|
prop,
|
||||||
self.map_type_name(args[0].type_name())
|
self.map_type_name(args[0].type_name())
|
||||||
),
|
),
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
@ -264,7 +264,7 @@ impl Engine {
|
|||||||
self.map_type_name(args[0].type_name()),
|
self.map_type_name(args[0].type_name()),
|
||||||
self.map_type_name(args[1].type_name()),
|
self.map_type_name(args[1].type_name()),
|
||||||
),
|
),
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
@ -278,7 +278,7 @@ impl Engine {
|
|||||||
self.map_type_name(args[0].type_name()),
|
self.map_type_name(args[0].type_name()),
|
||||||
self.map_type_name(args[1].type_name()),
|
self.map_type_name(args[1].type_name()),
|
||||||
),
|
),
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
@ -292,7 +292,7 @@ impl Engine {
|
|||||||
self.map_type_name(args[0].type_name()),
|
self.map_type_name(args[0].type_name()),
|
||||||
self.map_type_name(args[1].type_name()),
|
self.map_type_name(args[1].type_name()),
|
||||||
),
|
),
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
@ -311,13 +311,13 @@ impl Engine {
|
|||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", ")
|
.join(", ")
|
||||||
),
|
),
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call a script-defined function.
|
/// Call a script-defined function.
|
||||||
/// Position in `EvalAltResult` is `None` and must be set afterwards.
|
/// [`Position`] in [`EvalAltResult`] is [`None`][Position::None] and must be set afterwards.
|
||||||
///
|
///
|
||||||
/// ## WARNING
|
/// ## WARNING
|
||||||
///
|
///
|
||||||
@ -342,7 +342,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
if level > self.max_call_levels() {
|
if level > self.max_call_levels() {
|
||||||
return Err(Box::new(EvalAltResult::ErrorStackOverflow(NO_POS)));
|
return Err(Box::new(EvalAltResult::ErrorStackOverflow(Position::NONE)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let orig_scope_level = state.scope_level;
|
let orig_scope_level = state.scope_level;
|
||||||
@ -385,25 +385,27 @@ impl Engine {
|
|||||||
// Evaluate the function at one higher level of call depth
|
// Evaluate the function at one higher level of call depth
|
||||||
let stmt = &fn_def.body;
|
let stmt = &fn_def.body;
|
||||||
|
|
||||||
let result =
|
let result = self
|
||||||
self.eval_stmt(scope, mods, state, unified_lib, this_ptr, stmt, level + 1)
|
.eval_stmt(scope, mods, state, unified_lib, this_ptr, stmt, level + 1)
|
||||||
.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),
|
EvalAltResult::Return(x, _) => Ok(x),
|
||||||
EvalAltResult::ErrorInFunctionCall(name, err, _) => {
|
EvalAltResult::ErrorInFunctionCall(name, err, _) => {
|
||||||
EvalAltResult::ErrorInFunctionCall(
|
EvalAltResult::ErrorInFunctionCall(
|
||||||
format!("{} > {}", fn_def.name, name),
|
format!("{} > {}", fn_def.name, name),
|
||||||
err,
|
err,
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
)
|
)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
// System errors are passed straight-through
|
||||||
|
err if err.is_system_exception() => Err(Box::new(err)),
|
||||||
|
// Other errors are wrapped in `ErrorInFunctionCall`
|
||||||
|
_ => {
|
||||||
|
EvalAltResult::ErrorInFunctionCall(fn_def.name.to_string(), err, Position::NONE)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
// System errors are passed straight-through
|
});
|
||||||
err if err.is_system_exception() => Err(Box::new(err)),
|
|
||||||
// Other errors are wrapped in `ErrorInFunctionCall`
|
|
||||||
_ => EvalAltResult::ErrorInFunctionCall(fn_def.name.to_string(), err, NO_POS)
|
|
||||||
.into(),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Remove all local variables
|
// Remove all local variables
|
||||||
scope.rewind(prev_scope_len);
|
scope.rewind(prev_scope_len);
|
||||||
@ -440,14 +442,14 @@ impl Engine {
|
|||||||
hash_script: u64,
|
hash_script: u64,
|
||||||
pub_only: bool,
|
pub_only: bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// NOTE: We skip script functions for global_module and packages, and native functions for lib
|
// NOTE: We skip script functions for global_namespace and packages, and native functions for lib
|
||||||
|
|
||||||
// First check script-defined functions
|
// First check script-defined functions
|
||||||
lib.iter().any(|&m| m.contains_fn(hash_script, pub_only))
|
lib.iter().any(|&m| m.contains_fn(hash_script, pub_only))
|
||||||
//|| lib.iter().any(|&m| m.contains_fn(hash_fn, pub_only))
|
//|| lib.iter().any(|&m| m.contains_fn(hash_fn, pub_only))
|
||||||
// Then check registered functions
|
// Then check registered functions
|
||||||
//|| self.global_module.contains_fn(hash_script, pub_only)
|
//|| self.global_namespace.contains_fn(hash_script, pub_only)
|
||||||
|| self.global_module.contains_fn(hash_fn, false)
|
|| self.global_namespace.contains_fn(hash_fn, false)
|
||||||
// Then check packages
|
// Then check packages
|
||||||
|| self.packages.contains_fn(hash_script)
|
|| self.packages.contains_fn(hash_script)
|
||||||
|| self.packages.contains_fn(hash_fn)
|
|| self.packages.contains_fn(hash_fn)
|
||||||
@ -457,7 +459,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Perform an actual function call, native Rust or scripted, taking care of special functions.
|
/// Perform an actual function call, native Rust or scripted, taking care of special functions.
|
||||||
/// Position in `EvalAltResult` is `None` and must be set afterwards.
|
/// [`Position`] in [`EvalAltResult`] is [`None`][Position::None] and must be set afterwards.
|
||||||
///
|
///
|
||||||
/// ## WARNING
|
/// ## WARNING
|
||||||
///
|
///
|
||||||
@ -476,7 +478,7 @@ impl Engine {
|
|||||||
_is_method: bool,
|
_is_method: bool,
|
||||||
pub_only: bool,
|
pub_only: bool,
|
||||||
_capture_scope: Option<Scope>,
|
_capture_scope: Option<Scope>,
|
||||||
def_val: Option<Dynamic>,
|
def_val: Option<&Dynamic>,
|
||||||
_level: usize,
|
_level: usize,
|
||||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||||
// Check for data race.
|
// Check for data race.
|
||||||
@ -512,7 +514,7 @@ impl Engine {
|
|||||||
fn_name, fn_name
|
fn_name, fn_name
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
@ -524,7 +526,7 @@ impl Engine {
|
|||||||
let func = lib
|
let func = lib
|
||||||
.iter()
|
.iter()
|
||||||
.find_map(|&m| m.get_fn(hash_script, pub_only))
|
.find_map(|&m| m.get_fn(hash_script, pub_only))
|
||||||
//.or_else(|| self.global_module.get_fn(hash_script, pub_only))
|
//.or_else(|| self.global_namespace.get_fn(hash_script, pub_only))
|
||||||
.or_else(|| self.packages.get_fn(hash_script))
|
.or_else(|| self.packages.get_fn(hash_script))
|
||||||
//.or_else(|| mods.get_fn(hash_script))
|
//.or_else(|| mods.get_fn(hash_script))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -606,7 +608,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a list of statements with an empty state and no `this` pointer.
|
/// Evaluate a list of statements with an empty state and no `this` pointer.
|
||||||
/// This is commonly used to evaluate a list of statements in an `AST` or a script function body.
|
/// This is commonly used to evaluate a list of statements in an [`AST`] or a script function body.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn eval_statements_raw<'a>(
|
pub(crate) fn eval_statements_raw<'a>(
|
||||||
&self,
|
&self,
|
||||||
@ -631,7 +633,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a text string as a script - used primarily for 'eval'.
|
/// Evaluate a text string as a script - used primarily for 'eval'.
|
||||||
/// Position in `EvalAltResult` is `None` and must be set afterwards.
|
/// [`Position`] in [`EvalAltResult`] is [`None`][Position::None] and must be set afterwards.
|
||||||
fn eval_script_expr(
|
fn eval_script_expr(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -652,7 +654,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
if _level > self.max_call_levels() {
|
if _level > self.max_call_levels() {
|
||||||
return Err(Box::new(EvalAltResult::ErrorStackOverflow(NO_POS)));
|
return Err(Box::new(EvalAltResult::ErrorStackOverflow(Position::NONE)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile the script text
|
// Compile the script text
|
||||||
@ -678,7 +680,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Call a dot method.
|
/// Call a dot method.
|
||||||
/// Position in `EvalAltResult` is `None` and must be set afterwards.
|
/// [`Position`] in [`EvalAltResult`] is [`None`][Position::None] and must be set afterwards.
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
pub(crate) fn make_method_call(
|
pub(crate) fn make_method_call(
|
||||||
&self,
|
&self,
|
||||||
@ -689,7 +691,7 @@ impl Engine {
|
|||||||
hash_script: u64,
|
hash_script: u64,
|
||||||
target: &mut crate::engine::Target,
|
target: &mut crate::engine::Target,
|
||||||
mut call_args: StaticVec<Dynamic>,
|
mut call_args: StaticVec<Dynamic>,
|
||||||
def_val: Option<Dynamic>,
|
def_val: Option<&Dynamic>,
|
||||||
native: bool,
|
native: bool,
|
||||||
pub_only: bool,
|
pub_only: bool,
|
||||||
level: usize,
|
level: usize,
|
||||||
@ -830,7 +832,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Call a function in normal function-call style.
|
/// Call a function in normal function-call style.
|
||||||
/// Position in `EvalAltResult` is `None` and must be set afterwards.
|
/// [`Position`] in [`EvalAltResult`] is [`None`][Position::None] and must be set afterwards.
|
||||||
pub(crate) fn make_function_call(
|
pub(crate) fn make_function_call(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -840,7 +842,7 @@ impl Engine {
|
|||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
args_expr: impl AsRef<[Expr]>,
|
args_expr: impl AsRef<[Expr]>,
|
||||||
def_val: Option<Dynamic>,
|
def_val: Option<&Dynamic>,
|
||||||
mut hash_script: u64,
|
mut hash_script: u64,
|
||||||
native: bool,
|
native: bool,
|
||||||
pub_only: bool,
|
pub_only: bool,
|
||||||
@ -1075,7 +1077,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Call a namespace-qualified function in normal function-call style.
|
/// Call a namespace-qualified function in normal function-call style.
|
||||||
/// Position in `EvalAltResult` is `None` and must be set afterwards.
|
/// [`Position`] in [`EvalAltResult`] is [`None`][Position::None] and must be set afterwards.
|
||||||
pub(crate) fn make_qualified_function_call(
|
pub(crate) fn make_qualified_function_call(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1086,7 +1088,7 @@ impl Engine {
|
|||||||
namespace: Option<&NamespaceRef>,
|
namespace: Option<&NamespaceRef>,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
args_expr: impl AsRef<[Expr]>,
|
args_expr: impl AsRef<[Expr]>,
|
||||||
def_val: Option<bool>,
|
def_val: Option<&Dynamic>,
|
||||||
hash_script: u64,
|
hash_script: u64,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
@ -1202,7 +1204,7 @@ impl Engine {
|
|||||||
f.get_native_fn().clone()((self, &*mods, lib).into(), args.as_mut())
|
f.get_native_fn().clone()((self, &*mods, lib).into(), args.as_mut())
|
||||||
}
|
}
|
||||||
Some(_) => unreachable!(),
|
Some(_) => unreachable!(),
|
||||||
None if def_val.is_some() => Ok(def_val.unwrap().into()),
|
None if def_val.is_some() => Ok(def_val.unwrap().clone()),
|
||||||
None => EvalAltResult::ErrorFunctionNotFound(
|
None => EvalAltResult::ErrorFunctionNotFound(
|
||||||
format!(
|
format!(
|
||||||
"{}{} ({})",
|
"{}{} ({})",
|
||||||
@ -1217,7 +1219,7 @@ impl Engine {
|
|||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", ")
|
.join(", ")
|
||||||
),
|
),
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,8 @@ use crate::{Engine, EvalAltResult, ParseError, Scope, AST};
|
|||||||
pub trait Func<ARGS, RET> {
|
pub trait Func<ARGS, RET> {
|
||||||
type Output;
|
type Output;
|
||||||
|
|
||||||
/// Create a Rust closure from an `AST`.
|
/// Create a Rust closure from an [`AST`].
|
||||||
/// The `Engine` and `AST` are consumed and basically embedded into the closure.
|
/// The [`Engine`] and [`AST`] are consumed and basically embedded into the closure.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -43,7 +43,7 @@ pub trait Func<ARGS, RET> {
|
|||||||
fn create_from_ast(self, ast: AST, entry_point: &str) -> Self::Output;
|
fn create_from_ast(self, ast: AST, entry_point: &str) -> Self::Output;
|
||||||
|
|
||||||
/// Create a Rust closure from a script.
|
/// Create a Rust closure from a script.
|
||||||
/// The `Engine` is consumed and basically embedded into the closure.
|
/// The [`Engine`] is consumed and basically embedded into the closure.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
|
@ -7,7 +7,7 @@ use crate::stdlib::{boxed::Box, convert::TryFrom, fmt, iter::empty, mem, string:
|
|||||||
use crate::token::is_valid_identifier;
|
use crate::token::is_valid_identifier;
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_script_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, ImmutableString, Module,
|
calc_script_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, ImmutableString, Module,
|
||||||
StaticVec, NO_POS,
|
Position, StaticVec,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
@ -78,7 +78,7 @@ impl<'e, 'm, 'pm: 'm, M: AsRef<[&'pm Module]> + ?Sized> From<(&'e Engine, &'m M)
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'e, 'a, 'm, 'pm> NativeCallContext<'e, 'a, 'm, 'pm> {
|
impl<'e, 'a, 'm, 'pm> NativeCallContext<'e, 'a, 'm, 'pm> {
|
||||||
/// Create a new `NativeCallContext`.
|
/// Create a new [`NativeCallContext`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new(engine: &'e Engine, lib: &'m impl AsRef<[&'pm Module]>) -> Self {
|
pub fn new(engine: &'e Engine, lib: &'m impl AsRef<[&'pm Module]>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -87,7 +87,7 @@ impl<'e, 'a, 'm, 'pm> NativeCallContext<'e, 'a, 'm, 'pm> {
|
|||||||
lib: lib.as_ref(),
|
lib: lib.as_ref(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// _[INTERNALS]_ Create a new `NativeCallContext`.
|
/// _(INTERNALS)_ Create a new [`NativeCallContext`].
|
||||||
/// Available under the `internals` feature only.
|
/// Available under the `internals` feature only.
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -103,12 +103,12 @@ impl<'e, 'a, 'm, 'pm> NativeCallContext<'e, 'a, 'm, 'pm> {
|
|||||||
lib: lib.as_ref(),
|
lib: lib.as_ref(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// The current `Engine`.
|
/// The current [`Engine`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn engine(&self) -> &'e Engine {
|
pub fn engine(&self) -> &'e Engine {
|
||||||
self.engine
|
self.engine
|
||||||
}
|
}
|
||||||
/// _[INTERNALS]_ The current set of modules imported via `import` statements.
|
/// _(INTERNALS)_ The current set of modules imported via `import` statements.
|
||||||
/// Available under the `internals` feature only.
|
/// Available under the `internals` feature only.
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -127,10 +127,11 @@ impl<'e, 'a, 'm, 'pm> NativeCallContext<'e, 'a, 'm, 'pm> {
|
|||||||
///
|
///
|
||||||
/// All arguments may be _consumed_, meaning that they may be replaced by `()`.
|
/// All arguments may be _consumed_, meaning that they may be 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.
|
||||||
///
|
///
|
||||||
/// If `is_method` is `true`, the first argument is assumed to be passed
|
/// If `is_method` is [`true`], the first argument is assumed to be passed
|
||||||
/// by reference and is not consumed.
|
/// by reference and is not consumed.
|
||||||
pub fn call_fn_dynamic_raw(
|
pub fn call_fn_dynamic_raw(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -138,7 +139,7 @@ impl<'e, 'a, 'm, 'pm> NativeCallContext<'e, 'a, 'm, 'pm> {
|
|||||||
is_method: bool,
|
is_method: bool,
|
||||||
public_only: bool,
|
public_only: bool,
|
||||||
args: &mut [&mut Dynamic],
|
args: &mut [&mut Dynamic],
|
||||||
def_value: Option<Dynamic>,
|
def_value: Option<&Dynamic>,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let mut mods = self.mods.cloned().unwrap_or_default();
|
let mut mods = self.mods.cloned().unwrap_or_default();
|
||||||
|
|
||||||
@ -171,7 +172,7 @@ impl<'e, 'a, 'm, 'pm> NativeCallContext<'e, 'a, 'm, 'pm> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consume a `Shared` resource and return a mutable reference to the wrapped value.
|
/// Consume a [`Shared`] resource and return a mutable reference to the wrapped value.
|
||||||
/// If the resource is shared (i.e. has other outstanding references), a cloned copy is used.
|
/// If the resource is shared (i.e. has other outstanding references), a cloned copy is used.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn shared_make_mut<T: Clone>(value: &mut Shared<T>) -> &mut T {
|
pub fn shared_make_mut<T: Clone>(value: &mut Shared<T>) -> &mut T {
|
||||||
@ -181,13 +182,13 @@ pub fn shared_make_mut<T: Clone>(value: &mut Shared<T>) -> &mut T {
|
|||||||
return Arc::make_mut(value);
|
return Arc::make_mut(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consume a `Shared` resource if is unique (i.e. not shared), or clone it otherwise.
|
/// Consume a [`Shared`] resource if is unique (i.e. not shared), or clone it otherwise.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn shared_take_or_clone<T: Clone>(value: Shared<T>) -> T {
|
pub fn shared_take_or_clone<T: Clone>(value: Shared<T>) -> T {
|
||||||
shared_try_take(value).unwrap_or_else(|v| v.as_ref().clone())
|
shared_try_take(value).unwrap_or_else(|v| v.as_ref().clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consume a `Shared` resource if is unique (i.e. not shared).
|
/// Consume a [`Shared`] resource if is unique (i.e. not shared).
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn shared_try_take<T>(value: Shared<T>) -> Result<T, Shared<T>> {
|
pub fn shared_try_take<T>(value: Shared<T>) -> Result<T, Shared<T>> {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
@ -196,7 +197,7 @@ pub fn shared_try_take<T>(value: Shared<T>) -> Result<T, Shared<T>> {
|
|||||||
return Arc::try_unwrap(value);
|
return Arc::try_unwrap(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consume a `Shared` resource, assuming that it is unique (i.e. not shared).
|
/// Consume a [`Shared`] resource, assuming that it is unique (i.e. not shared).
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
@ -300,7 +301,7 @@ impl TryFrom<ImmutableString> for FnPtr {
|
|||||||
if is_valid_identifier(value.chars()) {
|
if is_valid_identifier(value.chars()) {
|
||||||
Ok(Self(value, Default::default()))
|
Ok(Self(value, Default::default()))
|
||||||
} else {
|
} else {
|
||||||
EvalAltResult::ErrorFunctionNotFound(value.into(), NO_POS).into()
|
EvalAltResult::ErrorFunctionNotFound(value.into(), Position::NONE).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -485,7 +486,8 @@ impl CallableFunction {
|
|||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if the `CallableFunction` is not `Pure` or `Method`.
|
/// Panics if the [`CallableFunction`] is not [`Pure`][CallableFunction::Pure] or
|
||||||
|
/// [`Method`][CallableFunction::Method].
|
||||||
pub fn get_native_fn(&self) -> &Shared<FnAny> {
|
pub fn get_native_fn(&self) -> &Shared<FnAny> {
|
||||||
match self {
|
match self {
|
||||||
Self::Pure(f) | Self::Method(f) => f,
|
Self::Pure(f) | Self::Method(f) => f,
|
||||||
@ -499,7 +501,7 @@ impl CallableFunction {
|
|||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if the `CallableFunction` is not `Script`.
|
/// Panics if the [`CallableFunction`] is not [`Script`][CallableFunction::Script].
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub fn get_fn_def(&self) -> &Shared<ScriptFnDef> {
|
pub fn get_fn_def(&self) -> &Shared<ScriptFnDef> {
|
||||||
match self {
|
match self {
|
||||||
@ -511,7 +513,7 @@ impl CallableFunction {
|
|||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if the `CallableFunction` is not `Iterator`.
|
/// Panics if the [`CallableFunction`] is not [`Iterator`][CallableFunction::Iterator].
|
||||||
pub fn get_iter_fn(&self) -> IteratorFn {
|
pub fn get_iter_fn(&self) -> IteratorFn {
|
||||||
match self {
|
match self {
|
||||||
Self::Iterator(f) => *f,
|
Self::Iterator(f) => *f,
|
||||||
@ -525,7 +527,7 @@ impl CallableFunction {
|
|||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if the `CallableFunction` is not `Plugin`.
|
/// Panics if the [`CallableFunction`] is not [`Plugin`][CallableFunction::Plugin].
|
||||||
pub fn get_plugin_fn<'s>(&'s self) -> &Shared<FnPlugin> {
|
pub fn get_plugin_fn<'s>(&'s self) -> &Shared<FnPlugin> {
|
||||||
match self {
|
match self {
|
||||||
Self::Plugin(f) => f,
|
Self::Plugin(f) => f,
|
||||||
@ -535,17 +537,17 @@ impl CallableFunction {
|
|||||||
Self::Script(_) => unreachable!(),
|
Self::Script(_) => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Create a new `CallableFunction::Pure`.
|
/// Create a new [`CallableFunction::Pure`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn from_pure(func: Box<FnAny>) -> Self {
|
pub fn from_pure(func: Box<FnAny>) -> Self {
|
||||||
Self::Pure(func.into())
|
Self::Pure(func.into())
|
||||||
}
|
}
|
||||||
/// Create a new `CallableFunction::Method`.
|
/// Create a new [`CallableFunction::Method`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn from_method(func: Box<FnAny>) -> Self {
|
pub fn from_method(func: Box<FnAny>) -> Self {
|
||||||
Self::Method(func.into())
|
Self::Method(func.into())
|
||||||
}
|
}
|
||||||
/// Create a new `CallableFunction::Plugin`.
|
/// Create a new [`CallableFunction::Plugin`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn from_plugin(func: impl PluginFunction + 'static + SendSync) -> Self {
|
pub fn from_plugin(func: impl PluginFunction + 'static + SendSync) -> Self {
|
||||||
Self::Plugin((Box::new(func) as Box<FnPlugin>).into())
|
Self::Plugin((Box::new(func) as Box<FnPlugin>).into())
|
||||||
|
@ -187,7 +187,7 @@ macro_rules! def_register {
|
|||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn register_fn(&mut self, name: &str, f: FN) -> &mut Self {
|
fn register_fn(&mut self, name: &str, f: FN) -> &mut Self {
|
||||||
self.global_module.set_fn(name, FnNamespace::Global, FnAccess::Public,
|
self.global_namespace.set_fn(name, FnNamespace::Global, FnAccess::Public,
|
||||||
&[$(map_type_id::<$par>()),*],
|
&[$(map_type_id::<$par>()),*],
|
||||||
CallableFunction::$abi(make_func!(f : map_dynamic ; $($par => $let => $clone => $arg),*))
|
CallableFunction::$abi(make_func!(f : map_dynamic ; $($par => $let => $clone => $arg),*))
|
||||||
);
|
);
|
||||||
@ -202,7 +202,7 @@ macro_rules! def_register {
|
|||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn register_result_fn(&mut self, name: &str, f: FN) -> &mut Self {
|
fn register_result_fn(&mut self, name: &str, f: FN) -> &mut Self {
|
||||||
self.global_module.set_fn(name, FnNamespace::Global, FnAccess::Public,
|
self.global_namespace.set_fn(name, FnNamespace::Global, FnAccess::Public,
|
||||||
&[$(map_type_id::<$par>()),*],
|
&[$(map_type_id::<$par>()),*],
|
||||||
CallableFunction::$abi(make_func!(f : map_result ; $($par => $let => $clone => $arg),*))
|
CallableFunction::$abi(make_func!(f : map_result ; $($par => $let => $clone => $arg),*))
|
||||||
);
|
);
|
||||||
|
10
src/lib.rs
10
src/lib.rs
@ -50,7 +50,7 @@
|
|||||||
//!
|
//!
|
||||||
//! # Documentation
|
//! # Documentation
|
||||||
//!
|
//!
|
||||||
//! See [The Rhai Book](https://schungx.github.io/rhai) for details on the Rhai script engine and language.
|
//! See [The Rhai Book](https://schungx.github.io/rhai) for details on the Rhai scripting engine and language.
|
||||||
|
|
||||||
#![cfg_attr(feature = "no_std", no_std)]
|
#![cfg_attr(feature = "no_std", no_std)]
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ pub use parse_error::{LexError, ParseError, ParseErrorType};
|
|||||||
pub use result::EvalAltResult;
|
pub use result::EvalAltResult;
|
||||||
pub use scope::Scope;
|
pub use scope::Scope;
|
||||||
pub use syntax::Expression;
|
pub use syntax::Expression;
|
||||||
pub use token::{Position, NO_POS};
|
pub use token::Position;
|
||||||
pub use utils::ImmutableString;
|
pub use utils::ImmutableString;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -157,7 +157,7 @@ pub use module::ModuleResolver;
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
pub use crate::module::resolvers as module_resolvers;
|
pub use crate::module::resolvers as module_resolvers;
|
||||||
|
|
||||||
/// _[SERDE]_ Serialization and deserialization support for [`serde`](https://crates.io/crates/serde).
|
/// _(SERDE)_ Serialization and deserialization support for [`serde`](https://crates.io/crates/serde).
|
||||||
/// Exported under the `serde` feature.
|
/// Exported under the `serde` feature.
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
pub mod serde {
|
pub mod serde {
|
||||||
@ -187,13 +187,13 @@ pub use engine::{Imports, Limits, State as EvalState};
|
|||||||
#[deprecated(note = "this type is volatile and may change")]
|
#[deprecated(note = "this type is volatile and may change")]
|
||||||
pub use module::NamespaceRef;
|
pub use module::NamespaceRef;
|
||||||
|
|
||||||
/// _[INTERNALS]_ Alias to [`smallvec::SmallVec<[T; 4]>`](https://crates.io/crates/smallvec),
|
/// _(INTERNALS)_ Alias to [`smallvec::SmallVec<[T; 4]>`](https://crates.io/crates/smallvec),
|
||||||
/// which is a specialized `Vec` backed by a small, fixed-size array when there are <= 4 items stored.
|
/// which is a specialized `Vec` backed by a small, fixed-size array when there are <= 4 items stored.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
#[cfg(not(feature = "internals"))]
|
#[cfg(not(feature = "internals"))]
|
||||||
type StaticVec<T> = smallvec::SmallVec<[T; 4]>;
|
type StaticVec<T> = smallvec::SmallVec<[T; 4]>;
|
||||||
|
|
||||||
/// _[INTERNALS]_ Alias to [`smallvec::SmallVec<[T; 4]>`](https://crates.io/crates/smallvec),
|
/// _(INTERNALS)_ Alias to [`smallvec::SmallVec<[T; 4]>`](https://crates.io/crates/smallvec),
|
||||||
/// which is a specialized `Vec` backed by a small, fixed-size array when there are <= 4 items stored.
|
/// which is a specialized `Vec` backed by a small, fixed-size array when there are <= 4 items stored.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
|
@ -20,7 +20,7 @@ use crate::stdlib::{
|
|||||||
use crate::token::Token;
|
use crate::token::Token;
|
||||||
use crate::utils::StraightHasherBuilder;
|
use crate::utils::StraightHasherBuilder;
|
||||||
use crate::{
|
use crate::{
|
||||||
Dynamic, EvalAltResult, ImmutableString, NativeCallContext, Shared, StaticVec, NO_POS,
|
Dynamic, EvalAltResult, ImmutableString, NativeCallContext, Position, Shared, StaticVec,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -42,6 +42,12 @@ pub enum FnNamespace {
|
|||||||
Internal,
|
Internal,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for FnNamespace {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Internal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FnNamespace {
|
impl FnNamespace {
|
||||||
/// Is this namespace global?
|
/// Is this namespace global?
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -258,7 +264,7 @@ impl Module {
|
|||||||
self.get_var(name).and_then(Dynamic::try_cast::<T>)
|
self.get_var(name).and_then(Dynamic::try_cast::<T>)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a module variable as a `Dynamic`.
|
/// Get a module variable as a [`Dynamic`][crate::Dynamic].
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -297,15 +303,15 @@ impl Module {
|
|||||||
/// Get a reference to a namespace-qualified variable.
|
/// Get a reference to a namespace-qualified variable.
|
||||||
/// Name and Position in `EvalAltResult` are None and must be set afterwards.
|
/// Name and Position in `EvalAltResult` are None and must be set afterwards.
|
||||||
///
|
///
|
||||||
/// The `u64` hash is calculated by the function `crate::calc_native_fn_hash`.
|
/// The [`u64`] hash is calculated by the function [`crate::calc_native_fn_hash`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn get_qualified_var(&self, hash_var: u64) -> Result<&Dynamic, Box<EvalAltResult>> {
|
pub(crate) fn get_qualified_var(&self, hash_var: u64) -> Result<&Dynamic, Box<EvalAltResult>> {
|
||||||
if hash_var == 0 {
|
if hash_var == 0 {
|
||||||
Err(EvalAltResult::ErrorVariableNotFound(String::new(), NO_POS).into())
|
Err(EvalAltResult::ErrorVariableNotFound(String::new(), Position::NONE).into())
|
||||||
} else {
|
} else {
|
||||||
self.all_variables
|
self.all_variables.get(&hash_var).ok_or_else(|| {
|
||||||
.get(&hash_var)
|
EvalAltResult::ErrorVariableNotFound(String::new(), Position::NONE).into()
|
||||||
.ok_or_else(|| EvalAltResult::ErrorVariableNotFound(String::new(), NO_POS).into())
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -437,7 +443,7 @@ impl Module {
|
|||||||
|
|
||||||
/// Does the particular Rust function exist in the module?
|
/// Does the particular Rust function exist in the module?
|
||||||
///
|
///
|
||||||
/// The `u64` hash is calculated by the function `crate::calc_native_fn_hash`.
|
/// The [`u64`] hash is calculated by the function [`crate::calc_native_fn_hash`].
|
||||||
/// It is also returned by the `set_fn_XXX` calls.
|
/// It is also returned by the `set_fn_XXX` calls.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
@ -511,12 +517,13 @@ impl Module {
|
|||||||
hash_fn
|
hash_fn
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust function taking a reference to the scripting `Engine`, the current set of functions,
|
/// Set a Rust function taking a reference to the scripting [`Engine`][crate::Engine],
|
||||||
/// plus a list of mutable `Dynamic` references into the module, returning a hash key.
|
/// the current set of functions, plus a list of mutable [`Dynamic`][crate::Dynamic] references
|
||||||
|
/// into the module, returning a hash key.
|
||||||
///
|
///
|
||||||
/// Use this to register a built-in function which must reference settings on the scripting
|
/// Use this to register a built-in function which must reference settings on the scripting
|
||||||
/// `Engine` (e.g. to prevent growing an array beyond the allowed maximum size), or to call a
|
/// [`Engine`][crate::Engine] (e.g. to prevent growing an array beyond the allowed maximum size),
|
||||||
/// script-defined function in the current evaluation context.
|
/// or to call a script-defined function in the current evaluation context.
|
||||||
///
|
///
|
||||||
/// If there is a similar existing Rust function, it is replaced.
|
/// If there is a similar existing Rust function, it is replaced.
|
||||||
///
|
///
|
||||||
@ -524,9 +531,9 @@ impl Module {
|
|||||||
///
|
///
|
||||||
/// This function is very low level.
|
/// This function is very low level.
|
||||||
///
|
///
|
||||||
/// A list of `TypeId`'s is taken as the argument types.
|
/// A list of [`TypeId`]'s is taken as the argument types.
|
||||||
///
|
///
|
||||||
/// Arguments are simply passed in as a mutable array of `&mut Dynamic`,
|
/// Arguments are simply passed in as a mutable array of [`&mut Dynamic`][crate::Dynamic],
|
||||||
/// which is guaranteed to contain enough arguments of the correct types.
|
/// which is guaranteed to contain enough arguments of the correct types.
|
||||||
///
|
///
|
||||||
/// The function is assumed to be a _method_, meaning that the first argument should not be consumed.
|
/// The function is assumed to be a _method_, meaning that the first argument should not be consumed.
|
||||||
@ -545,8 +552,7 @@ impl Module {
|
|||||||
/// use rhai::{Module, FnNamespace, FnAccess};
|
/// use rhai::{Module, FnNamespace, FnAccess};
|
||||||
///
|
///
|
||||||
/// let mut module = Module::new();
|
/// let mut module = Module::new();
|
||||||
/// let hash = module.set_raw_fn("double_or_not",
|
/// let hash = module.set_raw_fn("double_or_not", FnNamespace::Internal, FnAccess::Public,
|
||||||
/// FnNamespace::Internal, FnAccess::Public,
|
|
||||||
/// // Pass parameter types via a slice with TypeId's
|
/// // Pass parameter types via a slice with TypeId's
|
||||||
/// &[std::any::TypeId::of::<i64>(), std::any::TypeId::of::<bool>()],
|
/// &[std::any::TypeId::of::<i64>(), std::any::TypeId::of::<bool>()],
|
||||||
/// // Fixed closure signature
|
/// // Fixed closure signature
|
||||||
@ -594,30 +600,6 @@ impl Module {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the namespace of a registered function.
|
|
||||||
/// Returns `None` if a function with the hash does not exist.
|
|
||||||
///
|
|
||||||
/// The `u64` hash is calculated by the function `crate::calc_native_fn_hash`.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn get_fn_namespace(&self, hash: u64) -> Option<FnNamespace> {
|
|
||||||
self.functions.get(&hash).map(|f| f.namespace)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the namespace of a registered function.
|
|
||||||
/// Returns the original namespace or `None` if a function with the hash does not exist.
|
|
||||||
///
|
|
||||||
/// The `u64` hash is calculated by the function `crate::calc_native_fn_hash`.
|
|
||||||
#[inline]
|
|
||||||
pub fn set_fn_namespace(&mut self, hash: u64, namespace: FnNamespace) -> Option<FnNamespace> {
|
|
||||||
if let Some(f) = self.functions.get_mut(&hash) {
|
|
||||||
let old_ns = f.namespace;
|
|
||||||
f.namespace = namespace;
|
|
||||||
Some(old_ns)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set a Rust function taking no parameters into the module, returning a hash key.
|
/// Set a Rust function taking no parameters into the module, returning a hash key.
|
||||||
///
|
///
|
||||||
/// If there is a similar existing Rust function, it is replaced.
|
/// If there is a similar existing Rust function, it is replaced.
|
||||||
@ -687,16 +669,19 @@ impl Module {
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use rhai::Module;
|
/// use rhai::{Module, FnNamespace};
|
||||||
///
|
///
|
||||||
/// let mut module = Module::new();
|
/// let mut module = Module::new();
|
||||||
/// let hash = module.set_fn_1_mut("calc", |x: &mut i64| { *x += 1; Ok(*x) });
|
/// let hash = module.set_fn_1_mut("calc", FnNamespace::Internal,
|
||||||
|
/// |x: &mut i64| { *x += 1; Ok(*x) }
|
||||||
|
/// );
|
||||||
/// assert!(module.contains_fn(hash, true));
|
/// assert!(module.contains_fn(hash, true));
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_fn_1_mut<A: Variant + Clone, T: Variant + Clone>(
|
pub fn set_fn_1_mut<A: Variant + Clone, T: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
|
namespace: FnNamespace,
|
||||||
func: impl Fn(&mut A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
func: impl Fn(&mut A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
@ -705,7 +690,7 @@ impl Module {
|
|||||||
let arg_types = [TypeId::of::<A>()];
|
let arg_types = [TypeId::of::<A>()];
|
||||||
self.set_fn(
|
self.set_fn(
|
||||||
name,
|
name,
|
||||||
FnNamespace::Internal,
|
namespace,
|
||||||
FnAccess::Public,
|
FnAccess::Public,
|
||||||
&arg_types,
|
&arg_types,
|
||||||
CallableFunction::from_method(Box::new(f)),
|
CallableFunction::from_method(Box::new(f)),
|
||||||
@ -713,6 +698,7 @@ impl Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust getter function taking one mutable parameter, returning a hash key.
|
/// Set a Rust getter function taking one mutable parameter, returning a hash key.
|
||||||
|
/// This function is automatically exposed to the global namespace.
|
||||||
///
|
///
|
||||||
/// If there is a similar existing Rust getter function, it is replaced.
|
/// If there is a similar existing Rust getter function, it is replaced.
|
||||||
///
|
///
|
||||||
@ -732,7 +718,11 @@ impl Module {
|
|||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
func: impl Fn(&mut A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
func: impl Fn(&mut A) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
self.set_fn_1_mut(crate::engine::make_getter(&name.into()), func)
|
self.set_fn_1_mut(
|
||||||
|
crate::engine::make_getter(&name.into()),
|
||||||
|
FnNamespace::Global,
|
||||||
|
func,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust function taking two parameters into the module, returning a hash key.
|
/// Set a Rust function taking two parameters into the module, returning a hash key.
|
||||||
@ -780,18 +770,22 @@ impl Module {
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use rhai::{Module, ImmutableString};
|
/// use rhai::{Module, FnNamespace, ImmutableString};
|
||||||
///
|
///
|
||||||
/// let mut module = Module::new();
|
/// let mut module = Module::new();
|
||||||
/// let hash = module.set_fn_2_mut("calc", |x: &mut i64, y: ImmutableString| {
|
/// let hash = module.set_fn_2_mut("calc", FnNamespace::Internal,
|
||||||
/// *x += y.len() as i64; Ok(*x)
|
/// |x: &mut i64, y: ImmutableString| {
|
||||||
/// });
|
/// *x += y.len() as i64;
|
||||||
|
/// Ok(*x)
|
||||||
|
/// }
|
||||||
|
/// );
|
||||||
/// assert!(module.contains_fn(hash, true));
|
/// assert!(module.contains_fn(hash, true));
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_fn_2_mut<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
pub fn set_fn_2_mut<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
|
namespace: FnNamespace,
|
||||||
func: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
func: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
@ -803,7 +797,7 @@ impl Module {
|
|||||||
let arg_types = [TypeId::of::<A>(), TypeId::of::<B>()];
|
let arg_types = [TypeId::of::<A>(), TypeId::of::<B>()];
|
||||||
self.set_fn(
|
self.set_fn(
|
||||||
name,
|
name,
|
||||||
FnNamespace::Internal,
|
namespace,
|
||||||
FnAccess::Public,
|
FnAccess::Public,
|
||||||
&arg_types,
|
&arg_types,
|
||||||
CallableFunction::from_method(Box::new(f)),
|
CallableFunction::from_method(Box::new(f)),
|
||||||
@ -812,6 +806,7 @@ impl Module {
|
|||||||
|
|
||||||
/// Set a Rust setter function taking two parameters (the first one mutable) into the module,
|
/// Set a Rust setter function taking two parameters (the first one mutable) into the module,
|
||||||
/// returning a hash key.
|
/// returning a hash key.
|
||||||
|
/// This function is automatically exposed to the global namespace.
|
||||||
///
|
///
|
||||||
/// If there is a similar existing setter Rust function, it is replaced.
|
/// If there is a similar existing setter Rust function, it is replaced.
|
||||||
///
|
///
|
||||||
@ -834,17 +829,22 @@ impl Module {
|
|||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
func: impl Fn(&mut A, B) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
func: impl Fn(&mut A, B) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
self.set_fn_2_mut(crate::engine::make_setter(&name.into()), func)
|
self.set_fn_2_mut(
|
||||||
|
crate::engine::make_setter(&name.into()),
|
||||||
|
FnNamespace::Global,
|
||||||
|
func,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust index getter taking two parameters (the first one mutable) into the module,
|
/// Set a Rust index getter taking two parameters (the first one mutable) into the module,
|
||||||
/// returning a hash key.
|
/// returning a hash key.
|
||||||
|
/// This function is automatically exposed to the global namespace.
|
||||||
///
|
///
|
||||||
/// If there is a similar existing setter Rust function, it is replaced.
|
/// If there is a similar existing setter Rust function, it is replaced.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if the type is `Array` or `Map`.
|
/// Panics if the type is [`Array`] or [`Map`].
|
||||||
/// Indexers for arrays, object maps and strings cannot be registered.
|
/// Indexers for arrays, object maps and strings cannot be registered.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
@ -878,7 +878,7 @@ impl Module {
|
|||||||
panic!("Cannot register indexer for strings.");
|
panic!("Cannot register indexer for strings.");
|
||||||
}
|
}
|
||||||
|
|
||||||
self.set_fn_2_mut(crate::engine::FN_IDX_GET, func)
|
self.set_fn_2_mut(crate::engine::FN_IDX_GET, FnNamespace::Global, func)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust function taking three parameters into the module, returning a hash key.
|
/// Set a Rust function taking three parameters into the module, returning a hash key.
|
||||||
@ -932,12 +932,15 @@ impl Module {
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use rhai::{Module, ImmutableString};
|
/// use rhai::{Module, FnNamespace, ImmutableString};
|
||||||
///
|
///
|
||||||
/// let mut module = Module::new();
|
/// let mut module = Module::new();
|
||||||
/// let hash = module.set_fn_3_mut("calc", |x: &mut i64, y: ImmutableString, z: i64| {
|
/// let hash = module.set_fn_3_mut("calc", FnNamespace::Internal,
|
||||||
/// *x += y.len() as i64 + z; Ok(*x)
|
/// |x: &mut i64, y: ImmutableString, z: i64| {
|
||||||
/// });
|
/// *x += y.len() as i64 + z;
|
||||||
|
/// Ok(*x)
|
||||||
|
/// }
|
||||||
|
/// );
|
||||||
/// assert!(module.contains_fn(hash, true));
|
/// assert!(module.contains_fn(hash, true));
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -949,6 +952,7 @@ impl Module {
|
|||||||
>(
|
>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
|
namespace: FnNamespace,
|
||||||
func: impl Fn(&mut A, B, C) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
func: impl Fn(&mut A, B, C) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
@ -961,7 +965,7 @@ impl Module {
|
|||||||
let arg_types = [TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
|
let arg_types = [TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
|
||||||
self.set_fn(
|
self.set_fn(
|
||||||
name,
|
name,
|
||||||
FnNamespace::Internal,
|
namespace,
|
||||||
FnAccess::Public,
|
FnAccess::Public,
|
||||||
&arg_types,
|
&arg_types,
|
||||||
CallableFunction::from_method(Box::new(f)),
|
CallableFunction::from_method(Box::new(f)),
|
||||||
@ -970,12 +974,13 @@ impl Module {
|
|||||||
|
|
||||||
/// Set a Rust index setter taking three parameters (the first one mutable) into the module,
|
/// Set a Rust index setter taking three parameters (the first one mutable) into the module,
|
||||||
/// returning a hash key.
|
/// returning a hash key.
|
||||||
|
/// This function is automatically exposed to the global namespace.
|
||||||
///
|
///
|
||||||
/// If there is a similar existing Rust function, it is replaced.
|
/// If there is a similar existing Rust function, it is replaced.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if the type is `Array` or `Map`.
|
/// Panics if the type is [`Array`] or [`Map`].
|
||||||
/// Indexers for arrays, object maps and strings cannot be registered.
|
/// Indexers for arrays, object maps and strings cannot be registered.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
@ -1028,13 +1033,14 @@ impl Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Set a pair of Rust index getter and setter functions, returning both hash keys.
|
/// Set a pair of Rust index getter and setter functions, returning both hash keys.
|
||||||
/// This is a short-hand for `set_indexer_get_fn` and `set_indexer_set_fn`.
|
/// This is a short-hand for [`set_indexer_get_fn`][Module::set_indexer_get_fn] and
|
||||||
|
/// [`set_indexer_set_fn`][Module::set_indexer_set_fn].
|
||||||
///
|
///
|
||||||
/// If there are similar existing Rust functions, they are replaced.
|
/// If there are similar existing Rust functions, they are replaced.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if the type is `Array` or `Map`.
|
/// Panics if the type is [`Array`] or [`Map`].
|
||||||
/// Indexers for arrays, object maps and strings cannot be registered.
|
/// Indexers for arrays, object maps and strings cannot be registered.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
@ -1126,12 +1132,15 @@ impl Module {
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use rhai::{Module, ImmutableString};
|
/// use rhai::{Module, FnNamespace, ImmutableString};
|
||||||
///
|
///
|
||||||
/// let mut module = Module::new();
|
/// let mut module = Module::new();
|
||||||
/// let hash = module.set_fn_4_mut("calc", |x: &mut i64, y: ImmutableString, z: i64, _w: ()| {
|
/// let hash = module.set_fn_4_mut("calc", FnNamespace::Internal,
|
||||||
/// *x += y.len() as i64 + z; Ok(*x)
|
/// |x: &mut i64, y: ImmutableString, z: i64, _w: ()| {
|
||||||
/// });
|
/// *x += y.len() as i64 + z;
|
||||||
|
/// Ok(*x)
|
||||||
|
/// }
|
||||||
|
/// );
|
||||||
/// assert!(module.contains_fn(hash, true));
|
/// assert!(module.contains_fn(hash, true));
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -1144,6 +1153,7 @@ impl Module {
|
|||||||
>(
|
>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
|
namespace: FnNamespace,
|
||||||
func: impl Fn(&mut A, B, C, D) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
func: impl Fn(&mut A, B, C, D) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
let f = move |_: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
@ -1162,7 +1172,7 @@ impl Module {
|
|||||||
];
|
];
|
||||||
self.set_fn(
|
self.set_fn(
|
||||||
name,
|
name,
|
||||||
FnNamespace::Internal,
|
namespace,
|
||||||
FnAccess::Public,
|
FnAccess::Public,
|
||||||
&arg_types,
|
&arg_types,
|
||||||
CallableFunction::from_method(Box::new(f)),
|
CallableFunction::from_method(Box::new(f)),
|
||||||
@ -1171,7 +1181,7 @@ impl Module {
|
|||||||
|
|
||||||
/// Get a Rust function.
|
/// Get a Rust function.
|
||||||
///
|
///
|
||||||
/// The `u64` hash is calculated by the function `crate::calc_native_fn_hash`.
|
/// The [`u64`] hash is calculated by the function [`crate::calc_native_fn_hash`].
|
||||||
/// It is also returned by the `set_fn_XXX` calls.
|
/// It is also returned by the `set_fn_XXX` calls.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn get_fn(&self, hash_fn: u64, public_only: bool) -> Option<&CallableFunction> {
|
pub(crate) fn get_fn(&self, hash_fn: u64, public_only: bool) -> Option<&CallableFunction> {
|
||||||
@ -1190,8 +1200,8 @@ impl Module {
|
|||||||
|
|
||||||
/// Does the particular namespace-qualified function exist in the module?
|
/// Does the particular namespace-qualified function exist in the module?
|
||||||
///
|
///
|
||||||
/// The `u64` hash is calculated by the function `crate::calc_native_fn_hash` and must match
|
/// The [`u64`] hash is calculated by the function [`crate::calc_native_fn_hash`] and must match
|
||||||
/// the hash calculated by `build_index`.
|
/// the hash calculated by [`build_index`][Module::build_index].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn contains_qualified_fn(&self, hash_fn: u64) -> bool {
|
pub fn contains_qualified_fn(&self, hash_fn: u64) -> bool {
|
||||||
self.all_functions.contains_key(&hash_fn)
|
self.all_functions.contains_key(&hash_fn)
|
||||||
@ -1200,8 +1210,8 @@ impl Module {
|
|||||||
/// Get a namespace-qualified function.
|
/// Get a namespace-qualified function.
|
||||||
/// Name and Position in `EvalAltResult` are None and must be set afterwards.
|
/// Name and Position in `EvalAltResult` are None and must be set afterwards.
|
||||||
///
|
///
|
||||||
/// The `u64` hash is calculated by the function `crate::calc_native_fn_hash` and must match
|
/// The [`u64`] hash is calculated by the function [`crate::calc_native_fn_hash`] and must match
|
||||||
/// the hash calculated by `build_index`.
|
/// the hash calculated by [`build_index`][Module::build_index].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn get_qualified_fn(&self, hash_qualified_fn: u64) -> Option<&CallableFunction> {
|
pub(crate) fn get_qualified_fn(&self, hash_qualified_fn: u64) -> Option<&CallableFunction> {
|
||||||
self.all_functions.get(&hash_qualified_fn)
|
self.all_functions.get(&hash_qualified_fn)
|
||||||
@ -1386,10 +1396,11 @@ impl Module {
|
|||||||
/// Get an iterator over all script-defined functions in the module.
|
/// Get an iterator over all script-defined functions in the module.
|
||||||
///
|
///
|
||||||
/// Function metadata includes:
|
/// Function metadata includes:
|
||||||
/// 1) Access mode (`FnAccess::Public` or `FnAccess::Private`).
|
/// 1) Namespace ([`FnNamespace::Global`] or [`FnNamespace::Internal`]).
|
||||||
/// 2) Function name (as string slice).
|
/// 2) Access mode ([`FnAccess::Public`] or [`FnAccess::Private`]).
|
||||||
/// 3) Number of parameters.
|
/// 3) Function name (as string slice).
|
||||||
/// 4) Shared reference to function definition `ScriptFnDef`.
|
/// 4) Number of parameters.
|
||||||
|
/// 5) Shared reference to function definition [`ScriptFnDef`][crate::ScriptFnDef].
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn iter_script_fn<'a>(
|
pub(crate) fn iter_script_fn<'a>(
|
||||||
@ -1418,9 +1429,10 @@ impl Module {
|
|||||||
/// Get an iterator over all script-defined functions in the module.
|
/// Get an iterator over all script-defined functions in the module.
|
||||||
///
|
///
|
||||||
/// Function metadata includes:
|
/// Function metadata includes:
|
||||||
/// 1) Access mode (`FnAccess::Public` or `FnAccess::Private`).
|
/// 1) Namespace ([`FnNamespace::Global`] or [`FnNamespace::Internal`]).
|
||||||
/// 2) Function name (as string slice).
|
/// 2) Access mode ([`FnAccess::Public`] or [`FnAccess::Private`]).
|
||||||
/// 3) Number of parameters.
|
/// 3) Function name (as string slice).
|
||||||
|
/// 4) Number of parameters.
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(not(feature = "internals"))]
|
#[cfg(not(feature = "internals"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -1441,11 +1453,12 @@ impl Module {
|
|||||||
/// Get an iterator over all script-defined functions in the module.
|
/// Get an iterator over all script-defined functions in the module.
|
||||||
///
|
///
|
||||||
/// Function metadata includes:
|
/// Function metadata includes:
|
||||||
/// 1) Access mode (`FnAccess::Public` or `FnAccess::Private`).
|
/// 1) Namespace ([`FnNamespace::Global`] or [`FnNamespace::Internal`]).
|
||||||
/// 2) Function name (as string slice).
|
/// 2) Access mode ([`FnAccess::Public`] or [`FnAccess::Private`]).
|
||||||
/// 3) Number of parameters.
|
/// 3) Function name (as string slice).
|
||||||
/// 4) _[INTERNALS]_ Shared reference to function definition `ScriptFnDef`.
|
/// 4) Number of parameters.
|
||||||
/// Exported under the internals feature only.
|
/// 5) _(INTERNALS)_ Shared reference to function definition [`ScriptFnDef`][crate::ScriptFnDef].
|
||||||
|
/// Exported under the `internals` feature only.
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -1455,9 +1468,9 @@ impl Module {
|
|||||||
self.iter_script_fn()
|
self.iter_script_fn()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new `Module` by evaluating an `AST`.
|
/// Create a new module by evaluating an [`AST`][crate::AST].
|
||||||
///
|
///
|
||||||
/// The entire `AST` is encapsulated into each function, allowing functions
|
/// The entire [`AST`][crate::AST] is encapsulated into each function, allowing functions
|
||||||
/// to cross-call each other. Functions in the global namespace, plus all functions
|
/// to cross-call each other. Functions in the global namespace, plus all functions
|
||||||
/// defined in the module, are _merged_ into a _unified_ namespace before each call.
|
/// defined in the module, are _merged_ into a _unified_ namespace before each call.
|
||||||
/// Therefore, all functions will be found.
|
/// Therefore, all functions will be found.
|
||||||
@ -1513,14 +1526,12 @@ impl Module {
|
|||||||
// Non-private functions defined become module functions
|
// Non-private functions defined become module functions
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
{
|
{
|
||||||
let ast_lib: Shared<Module> = ast.lib().clone().into();
|
|
||||||
|
|
||||||
ast.iter_functions()
|
ast.iter_functions()
|
||||||
.filter(|(_, access, _, _, _)| !access.is_private())
|
.filter(|(_, access, _, _, _)| !access.is_private())
|
||||||
.for_each(|(_, _, _, _, func)| {
|
.for_each(|(_, _, _, _, func)| {
|
||||||
// Encapsulate AST environment
|
// Encapsulate AST environment
|
||||||
let mut func = func.as_ref().clone();
|
let mut func = func.as_ref().clone();
|
||||||
func.lib = Some(ast_lib.clone());
|
func.lib = Some(ast.shared_lib());
|
||||||
func.mods = func_mods.clone();
|
func.mods = func_mods.clone();
|
||||||
module.set_script_fn(func.into());
|
module.set_script_fn(func.into());
|
||||||
});
|
});
|
||||||
@ -1580,7 +1591,7 @@ impl Module {
|
|||||||
..
|
..
|
||||||
},
|
},
|
||||||
)| {
|
)| {
|
||||||
// Flatten all methods so they can be available without namespace qualifiers
|
// Flatten all functions with global namespace
|
||||||
if namespace.is_global() {
|
if namespace.is_global() {
|
||||||
functions.insert(hash, func.clone());
|
functions.insert(hash, func.clone());
|
||||||
}
|
}
|
||||||
@ -1596,7 +1607,7 @@ impl Module {
|
|||||||
// 1) Calculate a hash in a similar manner to script-defined functions,
|
// 1) Calculate a hash in a similar manner to script-defined functions,
|
||||||
// i.e. qualifiers + function name + number of arguments.
|
// i.e. qualifiers + function name + number of arguments.
|
||||||
// 2) Calculate a second hash with no qualifiers, empty function name,
|
// 2) Calculate a second hash with no qualifiers, empty function name,
|
||||||
// and the actual list of argument `TypeId`'.s
|
// and the actual list of argument [`TypeId`]'.s
|
||||||
let hash_fn_args = crate::calc_native_fn_hash(
|
let hash_fn_args = crate::calc_native_fn_hash(
|
||||||
empty(),
|
empty(),
|
||||||
"",
|
"",
|
||||||
@ -1686,13 +1697,13 @@ impl Module {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _[INTERNALS]_ A chain of module names to namespace-qualify a variable or function call.
|
/// _(INTERNALS)_ A chain of module names to namespace-qualify a variable or function call.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// A `u64` hash key is cached for quick search purposes.
|
/// A [`u64`] hash key is cached for quick search purposes.
|
||||||
///
|
///
|
||||||
/// A `StaticVec` is used because most namespace-qualified access contains only one level,
|
/// A [`StaticVec`] is used because most namespace-qualified access contains only one level,
|
||||||
/// and it is wasteful to always allocate a `Vec` with one element.
|
/// and it is wasteful to always allocate a [`Vec`] with one element.
|
||||||
///
|
///
|
||||||
/// ## WARNING
|
/// ## WARNING
|
||||||
///
|
///
|
||||||
|
@ -22,7 +22,7 @@ use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, Shared};
|
|||||||
pub struct ModuleResolversCollection(Vec<Box<dyn ModuleResolver>>);
|
pub struct ModuleResolversCollection(Vec<Box<dyn ModuleResolver>>);
|
||||||
|
|
||||||
impl ModuleResolversCollection {
|
impl ModuleResolversCollection {
|
||||||
/// Create a new `ModuleResolversCollection`.
|
/// Create a new [`ModuleResolversCollection`].
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -62,18 +62,18 @@ impl ModuleResolversCollection {
|
|||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.0.clear();
|
self.0.clear();
|
||||||
}
|
}
|
||||||
/// Is this `ModuleResolversCollection` empty?
|
/// Is this [`ModuleResolversCollection`] empty?
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.0.is_empty()
|
self.0.is_empty()
|
||||||
}
|
}
|
||||||
/// Get the number of module resolvers in this `ModuleResolversCollection`.
|
/// Get the number of module resolvers in this [`ModuleResolversCollection`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.0.len()
|
self.0.len()
|
||||||
}
|
}
|
||||||
/// Add another `ModuleResolversCollection` to the end of this collection.
|
/// Add another [`ModuleResolversCollection`] to the end of this collection.
|
||||||
/// The other `ModuleResolversCollection` is consumed.
|
/// The other [`ModuleResolversCollection`] is consumed.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn append(&mut self, other: Self) {
|
pub fn append(&mut self, other: Self) {
|
||||||
self.0.extend(other.0.into_iter());
|
self.0.extend(other.0.into_iter());
|
||||||
|
@ -7,7 +7,8 @@ use crate::{Engine, EvalAltResult, Locked, Module, ModuleResolver, Position, Sha
|
|||||||
///
|
///
|
||||||
/// Script files are cached so they are are not reloaded and recompiled in subsequent requests.
|
/// Script files are cached so they are are not reloaded and recompiled in subsequent requests.
|
||||||
///
|
///
|
||||||
/// The `new_with_path` and `new_with_path_and_extension` constructor functions
|
/// The [`new_with_path`][FileModuleResolver::new_with_path] and
|
||||||
|
/// [`new_with_path_and_extension`][FileModuleResolver::new_with_path_and_extension] constructor functions
|
||||||
/// allow specification of a base directory with module path used as a relative path offset
|
/// allow specification of a base directory with module path used as a relative path offset
|
||||||
/// to the base directory. The script file is then forced to be in a specified extension
|
/// to the base directory. The script file is then forced to be in a specified extension
|
||||||
/// (default `.rhai`).
|
/// (default `.rhai`).
|
||||||
@ -47,7 +48,7 @@ impl Default for FileModuleResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FileModuleResolver {
|
impl FileModuleResolver {
|
||||||
/// Create a new `FileModuleResolver` with a specific base path.
|
/// Create a new [`FileModuleResolver`] with a specific base path.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -67,7 +68,7 @@ impl FileModuleResolver {
|
|||||||
Self::new_with_path_and_extension(path, "rhai")
|
Self::new_with_path_and_extension(path, "rhai")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new `FileModuleResolver` with a specific base path and file extension.
|
/// Create a new [`FileModuleResolver`] with a specific base path and file extension.
|
||||||
///
|
///
|
||||||
/// The default extension is `.rhai`.
|
/// The default extension is `.rhai`.
|
||||||
///
|
///
|
||||||
@ -96,7 +97,7 @@ impl FileModuleResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new `FileModuleResolver` with the current directory as base path.
|
/// Create a new [`FileModuleResolver`] with the current directory as base path.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
|
@ -22,7 +22,7 @@ use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, Shared};
|
|||||||
pub struct StaticModuleResolver(HashMap<String, Shared<Module>>);
|
pub struct StaticModuleResolver(HashMap<String, Shared<Module>>);
|
||||||
|
|
||||||
impl StaticModuleResolver {
|
impl StaticModuleResolver {
|
||||||
/// Create a new `StaticModuleResolver`.
|
/// Create a new [`StaticModuleResolver`].
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -83,18 +83,18 @@ impl StaticModuleResolver {
|
|||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.0.clear();
|
self.0.clear();
|
||||||
}
|
}
|
||||||
/// Is this `StaticModuleResolver` empty?
|
/// Is this [`StaticModuleResolver`] empty?
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.0.is_empty()
|
self.0.is_empty()
|
||||||
}
|
}
|
||||||
/// Get the number of modules in this `StaticModuleResolver`.
|
/// Get the number of modules in this [`StaticModuleResolver`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.0.len()
|
self.0.len()
|
||||||
}
|
}
|
||||||
/// Merge another `StaticModuleResolver` into this.
|
/// Merge another [`StaticModuleResolver`] into this.
|
||||||
/// The other `StaticModuleResolver` is consumed.
|
/// The other [`StaticModuleResolver`] is consumed.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn merge(&mut self, other: Self) {
|
pub fn merge(&mut self, other: Self) {
|
||||||
if !other.is_empty() {
|
if !other.is_empty() {
|
||||||
|
@ -18,9 +18,7 @@ use crate::stdlib::{
|
|||||||
};
|
};
|
||||||
use crate::token::is_valid_identifier;
|
use crate::token::is_valid_identifier;
|
||||||
use crate::utils::get_hasher;
|
use crate::utils::get_hasher;
|
||||||
use crate::{
|
use crate::{calc_native_fn_hash, Dynamic, Engine, Module, Position, Scope, StaticVec, AST};
|
||||||
calc_native_fn_hash, Dynamic, Engine, Module, Position, Scope, StaticVec, AST, NO_POS,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Level of optimization performed.
|
/// Level of optimization performed.
|
||||||
///
|
///
|
||||||
@ -273,7 +271,7 @@ fn optimize_stmt_block(
|
|||||||
fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
||||||
match stmt {
|
match stmt {
|
||||||
// expr op= expr
|
// expr op= expr
|
||||||
Stmt::Assignment(ref mut x, _) => match x.0 {
|
Stmt::Assignment(x, _) => match x.0 {
|
||||||
Expr::Variable(_) => optimize_expr(&mut x.2, state),
|
Expr::Variable(_) => optimize_expr(&mut x.2, state),
|
||||||
_ => {
|
_ => {
|
||||||
optimize_expr(&mut x.0, state);
|
optimize_expr(&mut x.0, state);
|
||||||
@ -282,17 +280,17 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// if false { if_block } -> Noop
|
// if false { if_block } -> Noop
|
||||||
Stmt::If(Expr::False(pos), x, _) if x.1.is_none() => {
|
Stmt::If(Expr::BoolConstant(false, pos), x, _) if x.1.is_none() => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*stmt = Stmt::Noop(*pos);
|
*stmt = Stmt::Noop(*pos);
|
||||||
}
|
}
|
||||||
// if true { if_block } -> if_block
|
// if true { if_block } -> if_block
|
||||||
Stmt::If(Expr::True(_), x, _) if x.1.is_none() => {
|
Stmt::If(Expr::BoolConstant(true, _), x, _) if x.1.is_none() => {
|
||||||
*stmt = mem::take(&mut x.0);
|
*stmt = mem::take(&mut x.0);
|
||||||
optimize_stmt(stmt, state, true);
|
optimize_stmt(stmt, state, true);
|
||||||
}
|
}
|
||||||
// if expr { Noop }
|
// if expr { Noop }
|
||||||
Stmt::If(ref mut condition, x, _) if x.1.is_none() && matches!(x.0, Stmt::Noop(_)) => {
|
Stmt::If(condition, x, _) if x.1.is_none() && matches!(x.0, Stmt::Noop(_)) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
|
|
||||||
let pos = condition.position();
|
let pos = condition.position();
|
||||||
@ -311,22 +309,22 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
// if expr { if_block }
|
// if expr { if_block }
|
||||||
Stmt::If(ref mut condition, ref mut x, _) if x.1.is_none() => {
|
Stmt::If(condition, x, _) if x.1.is_none() => {
|
||||||
optimize_expr(condition, state);
|
optimize_expr(condition, state);
|
||||||
optimize_stmt(&mut x.0, state, true);
|
optimize_stmt(&mut x.0, state, true);
|
||||||
}
|
}
|
||||||
// if false { if_block } else { else_block } -> else_block
|
// if false { if_block } else { else_block } -> else_block
|
||||||
Stmt::If(Expr::False(_), x, _) if x.1.is_some() => {
|
Stmt::If(Expr::BoolConstant(false, _), x, _) if x.1.is_some() => {
|
||||||
*stmt = mem::take(x.1.as_mut().unwrap());
|
*stmt = mem::take(x.1.as_mut().unwrap());
|
||||||
optimize_stmt(stmt, state, true);
|
optimize_stmt(stmt, state, true);
|
||||||
}
|
}
|
||||||
// if true { if_block } else { else_block } -> if_block
|
// if true { if_block } else { else_block } -> if_block
|
||||||
Stmt::If(Expr::True(_), x, _) => {
|
Stmt::If(Expr::BoolConstant(true, _), x, _) => {
|
||||||
*stmt = mem::take(&mut x.0);
|
*stmt = mem::take(&mut x.0);
|
||||||
optimize_stmt(stmt, state, true);
|
optimize_stmt(stmt, state, true);
|
||||||
}
|
}
|
||||||
// if expr { if_block } else { else_block }
|
// if expr { if_block } else { else_block }
|
||||||
Stmt::If(ref mut condition, ref mut x, _) => {
|
Stmt::If(condition, x, _) => {
|
||||||
optimize_expr(condition, state);
|
optimize_expr(condition, state);
|
||||||
optimize_stmt(&mut x.0, state, true);
|
optimize_stmt(&mut x.0, state, true);
|
||||||
if let Some(else_block) = x.1.as_mut() {
|
if let Some(else_block) = x.1.as_mut() {
|
||||||
@ -375,15 +373,10 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// while false { block } -> Noop
|
// while false { block } -> Noop
|
||||||
Stmt::While(Expr::False(pos), _, _) => {
|
Stmt::While(Expr::BoolConstant(false, pos), _, _) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*stmt = Stmt::Noop(*pos)
|
*stmt = Stmt::Noop(*pos)
|
||||||
}
|
}
|
||||||
// while true { block } -> loop { block }
|
|
||||||
Stmt::While(Expr::True(_), block, pos) => {
|
|
||||||
optimize_stmt(block, state, false);
|
|
||||||
*stmt = Stmt::Loop(Box::new(mem::take(block)), *pos)
|
|
||||||
}
|
|
||||||
// while expr { block }
|
// while expr { block }
|
||||||
Stmt::While(condition, block, _) => {
|
Stmt::While(condition, block, _) => {
|
||||||
optimize_stmt(block, state, false);
|
optimize_stmt(block, state, false);
|
||||||
@ -404,32 +397,30 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// loop { block }
|
// do { block } while false | do { block } until true -> { block }
|
||||||
Stmt::Loop(block, _) => {
|
Stmt::Do(block, Expr::BoolConstant(true, _), false, _)
|
||||||
optimize_stmt(block, state, false);
|
| Stmt::Do(block, Expr::BoolConstant(false, _), true, _) => {
|
||||||
|
state.set_dirty();
|
||||||
match **block {
|
optimize_stmt(block.as_mut(), state, false);
|
||||||
// loop { break; } -> Noop
|
*stmt = mem::take(block.as_mut());
|
||||||
Stmt::Break(pos) => {
|
}
|
||||||
// Only a single break statement
|
// do { block } while|until expr
|
||||||
state.set_dirty();
|
Stmt::Do(block, condition, _, _) => {
|
||||||
*stmt = Stmt::Noop(pos)
|
optimize_stmt(block.as_mut(), state, false);
|
||||||
}
|
optimize_expr(condition, state);
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// for id in expr { block }
|
// for id in expr { block }
|
||||||
Stmt::For(ref mut iterable, ref mut x, _) => {
|
Stmt::For(iterable, x, _) => {
|
||||||
optimize_expr(iterable, state);
|
optimize_expr(iterable, state);
|
||||||
optimize_stmt(&mut x.1, state, false);
|
optimize_stmt(&mut x.1, state, false);
|
||||||
}
|
}
|
||||||
// let id = expr;
|
// let id = expr;
|
||||||
Stmt::Let(_, Some(ref mut expr), _, _) => optimize_expr(expr, state),
|
Stmt::Let(_, Some(expr), _, _) => optimize_expr(expr, state),
|
||||||
// let id;
|
// let id;
|
||||||
Stmt::Let(_, None, _, _) => (),
|
Stmt::Let(_, None, _, _) => (),
|
||||||
// import expr as var;
|
// import expr as var;
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Stmt::Import(ref mut expr, _, _) => optimize_expr(expr, state),
|
Stmt::Import(expr, _, _) => optimize_expr(expr, state),
|
||||||
// { block }
|
// { block }
|
||||||
Stmt::Block(statements, pos) => {
|
Stmt::Block(statements, pos) => {
|
||||||
*stmt = optimize_stmt_block(mem::take(statements), *pos, state, preserve_result, true);
|
*stmt = optimize_stmt_block(mem::take(statements), *pos, state, preserve_result, true);
|
||||||
@ -448,7 +439,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
*stmt = Stmt::Block(statements, pos);
|
*stmt = Stmt::Block(statements, pos);
|
||||||
}
|
}
|
||||||
// try { block } catch ( var ) { block }
|
// try { block } catch ( var ) { block }
|
||||||
Stmt::TryCatch(ref mut x, _, _) => {
|
Stmt::TryCatch(x, _, _) => {
|
||||||
optimize_stmt(&mut x.0, state, false);
|
optimize_stmt(&mut x.0, state, false);
|
||||||
optimize_stmt(&mut x.2, state, false);
|
optimize_stmt(&mut x.2, state, false);
|
||||||
}
|
}
|
||||||
@ -463,7 +454,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
*stmt = Stmt::Block(mem::take(x).into_vec(), *pos);
|
*stmt = Stmt::Block(mem::take(x).into_vec(), *pos);
|
||||||
}
|
}
|
||||||
// expr;
|
// expr;
|
||||||
Stmt::Expr(ref mut expr) => optimize_expr(expr, state),
|
Stmt::Expr(expr) => optimize_expr(expr, state),
|
||||||
// return expr;
|
// return expr;
|
||||||
Stmt::Return(_, Some(ref mut expr), _) => optimize_expr(expr, state),
|
Stmt::Return(_, Some(ref mut expr), _) => optimize_expr(expr, state),
|
||||||
|
|
||||||
@ -576,32 +567,23 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
|||||||
// "xxx" in "xxxxx"
|
// "xxx" in "xxxxx"
|
||||||
(Expr::StringConstant(a, pos), Expr::StringConstant(b, _)) => {
|
(Expr::StringConstant(a, pos), Expr::StringConstant(b, _)) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*expr = if b.contains(a.as_str()) { Expr::True(*pos) } else { Expr::False(*pos) };
|
*expr = Expr::BoolConstant( b.contains(a.as_str()), *pos);
|
||||||
}
|
}
|
||||||
// 'x' in "xxxxx"
|
// 'x' in "xxxxx"
|
||||||
(Expr::CharConstant(a, pos), Expr::StringConstant(b, _)) => {
|
(Expr::CharConstant(a, pos), Expr::StringConstant(b, _)) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*expr = if b.contains(*a) { Expr::True(*pos) } else { Expr::False(*pos) };
|
*expr = Expr::BoolConstant(b.contains(*a), *pos);
|
||||||
}
|
}
|
||||||
// "xxx" in #{...}
|
// "xxx" in #{...}
|
||||||
(Expr::StringConstant(a, pos), Expr::Map(b, _)) => {
|
(Expr::StringConstant(a, pos), Expr::Map(b, _)) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*expr = if b.iter().find(|(x, _)| x.name == *a).is_some() {
|
*expr = Expr::BoolConstant(b.iter().find(|(x, _)| x.name == *a).is_some(), *pos);
|
||||||
Expr::True(*pos)
|
|
||||||
} else {
|
|
||||||
Expr::False(*pos)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
// 'x' in #{...}
|
// 'x' in #{...}
|
||||||
(Expr::CharConstant(a, pos), Expr::Map(b, _)) => {
|
(Expr::CharConstant(a, pos), Expr::Map(b, _)) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
let ch = a.to_string();
|
let ch = a.to_string();
|
||||||
|
*expr = Expr::BoolConstant(b.iter().find(|(x, _)| x.name == &ch).is_some(), *pos);
|
||||||
*expr = if b.iter().find(|(x, _)| x.name == &ch).is_some() {
|
|
||||||
Expr::True(*pos)
|
|
||||||
} else {
|
|
||||||
Expr::False(*pos)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
// lhs in rhs
|
// lhs in rhs
|
||||||
(lhs, rhs) => { optimize_expr(lhs, state); optimize_expr(rhs, state); }
|
(lhs, rhs) => { optimize_expr(lhs, state); optimize_expr(rhs, state); }
|
||||||
@ -609,18 +591,18 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
|||||||
// 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::True(_), rhs) => {
|
(Expr::BoolConstant(true, _), rhs) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
optimize_expr(rhs, state);
|
optimize_expr(rhs, state);
|
||||||
*expr = mem::take(rhs);
|
*expr = mem::take(rhs);
|
||||||
}
|
}
|
||||||
// false && rhs -> false
|
// false && rhs -> false
|
||||||
(Expr::False(pos), _) => {
|
(Expr::BoolConstant(false, pos), _) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*expr = Expr::False(*pos);
|
*expr = Expr::BoolConstant(false, *pos);
|
||||||
}
|
}
|
||||||
// lhs && true -> lhs
|
// lhs && true -> lhs
|
||||||
(lhs, Expr::True(_)) => {
|
(lhs, Expr::BoolConstant(true, _)) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
optimize_expr(lhs, state);
|
optimize_expr(lhs, state);
|
||||||
*expr = mem::take(lhs);
|
*expr = mem::take(lhs);
|
||||||
@ -631,18 +613,18 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
|||||||
// 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::False(_), rhs) => {
|
(Expr::BoolConstant(false, _), rhs) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
optimize_expr(rhs, state);
|
optimize_expr(rhs, state);
|
||||||
*expr = mem::take(rhs);
|
*expr = mem::take(rhs);
|
||||||
}
|
}
|
||||||
// true || rhs -> true
|
// true || rhs -> true
|
||||||
(Expr::True(pos), _) => {
|
(Expr::BoolConstant(true, pos), _) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*expr = Expr::True(*pos);
|
*expr = Expr::BoolConstant(true, *pos);
|
||||||
}
|
}
|
||||||
// lhs || false
|
// lhs || false
|
||||||
(lhs, Expr::False(_)) => {
|
(lhs, Expr::BoolConstant(false, _)) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
optimize_expr(lhs, state);
|
optimize_expr(lhs, state);
|
||||||
*expr = mem::take(lhs);
|
*expr = mem::take(lhs);
|
||||||
@ -712,7 +694,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
|||||||
Some(arg_for_type_of.to_string().into())
|
Some(arg_for_type_of.to_string().into())
|
||||||
} else {
|
} else {
|
||||||
// Otherwise use the default value, if any
|
// Otherwise use the default value, if any
|
||||||
x.def_value.map(|v| v.into())
|
x.def_value.clone()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.and_then(|result| map_dynamic_to_expr(result, *pos))
|
.and_then(|result| map_dynamic_to_expr(result, *pos))
|
||||||
@ -768,7 +750,7 @@ fn optimize(
|
|||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, typ, _)| *typ)
|
.filter(|(_, typ, _)| *typ)
|
||||||
.for_each(|(name, _, value)| {
|
.for_each(|(name, _, value)| {
|
||||||
if let Some(val) = map_dynamic_to_expr(value, NO_POS) {
|
if let Some(val) = map_dynamic_to_expr(value, Position::NONE) {
|
||||||
state.push_constant(name, val);
|
state.push_constant(name, val);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::stdlib::{format, string::String};
|
use crate::stdlib::{format, string::String};
|
||||||
use crate::{def_package, EvalAltResult, INT, NO_POS};
|
use crate::{def_package, EvalAltResult, Position, INT};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
use crate::FLOAT;
|
use crate::FLOAT;
|
||||||
@ -13,7 +13,7 @@ use num_traits::float::Float;
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn make_err(msg: impl Into<String>) -> Box<EvalAltResult> {
|
pub fn make_err(msg: impl Into<String>) -> Box<EvalAltResult> {
|
||||||
EvalAltResult::ErrorArithmetic(msg.into(), NO_POS).into()
|
EvalAltResult::ErrorArithmetic(msg.into(), Position::NONE).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! gen_arithmetic_functions {
|
macro_rules! gen_arithmetic_functions {
|
||||||
|
@ -5,8 +5,8 @@ use crate::engine::{OP_EQUALS, TYPICAL_ARRAY_SIZE};
|
|||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::stdlib::{any::TypeId, boxed::Box, cmp::max, cmp::Ordering, string::ToString};
|
use crate::stdlib::{any::TypeId, boxed::Box, cmp::max, cmp::Ordering, string::ToString};
|
||||||
use crate::{
|
use crate::{
|
||||||
def_package, Array, Dynamic, EvalAltResult, FnPtr, ImmutableString, NativeCallContext, INT,
|
def_package, Array, Dynamic, EvalAltResult, FnPtr, ImmutableString, NativeCallContext,
|
||||||
NO_POS,
|
Position, INT,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
@ -42,7 +42,7 @@ macro_rules! gen_array_functions {
|
|||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
if _ctx.engine().max_array_size() > 0 && len > 0 && (len as usize) > _ctx.engine().max_array_size() {
|
if _ctx.engine().max_array_size() > 0 && len > 0 && (len as usize) > _ctx.engine().max_array_size() {
|
||||||
return EvalAltResult::ErrorDataTooLarge(
|
return EvalAltResult::ErrorDataTooLarge(
|
||||||
"Size of array".to_string(), NO_POS
|
"Size of array".to_string(), Position::NONE
|
||||||
).into();
|
).into();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,7 +215,7 @@ mod array_functions {
|
|||||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
"map".to_string(),
|
"map".to_string(),
|
||||||
err,
|
err,
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
))
|
))
|
||||||
})?,
|
})?,
|
||||||
);
|
);
|
||||||
@ -246,7 +246,7 @@ mod array_functions {
|
|||||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
"filter".to_string(),
|
"filter".to_string(),
|
||||||
err,
|
err,
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
))
|
))
|
||||||
})?
|
})?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
@ -279,7 +279,7 @@ mod array_functions {
|
|||||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
"index_of".to_string(),
|
"index_of".to_string(),
|
||||||
err,
|
err,
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
))
|
))
|
||||||
})?
|
})?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
@ -312,7 +312,7 @@ mod array_functions {
|
|||||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
"some".to_string(),
|
"some".to_string(),
|
||||||
err,
|
err,
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
))
|
))
|
||||||
})?
|
})?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
@ -345,7 +345,7 @@ mod array_functions {
|
|||||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
"all".to_string(),
|
"all".to_string(),
|
||||||
err,
|
err,
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
))
|
))
|
||||||
})?
|
})?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
@ -380,7 +380,7 @@ mod array_functions {
|
|||||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
"reduce".to_string(),
|
"reduce".to_string(),
|
||||||
err,
|
err,
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
@ -398,7 +398,7 @@ mod array_functions {
|
|||||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
"reduce".to_string(),
|
"reduce".to_string(),
|
||||||
err,
|
err,
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@ -417,7 +417,7 @@ mod array_functions {
|
|||||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
"reduce".to_string(),
|
"reduce".to_string(),
|
||||||
err,
|
err,
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
@ -447,7 +447,7 @@ mod array_functions {
|
|||||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
"reduce_rev".to_string(),
|
"reduce_rev".to_string(),
|
||||||
err,
|
err,
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
@ -465,7 +465,7 @@ mod array_functions {
|
|||||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
"reduce_rev".to_string(),
|
"reduce_rev".to_string(),
|
||||||
err,
|
err,
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@ -484,7 +484,7 @@ mod array_functions {
|
|||||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
"reduce_rev".to_string(),
|
"reduce_rev".to_string(),
|
||||||
err,
|
err,
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
@ -554,7 +554,7 @@ mod array_functions {
|
|||||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
"drain".to_string(),
|
"drain".to_string(),
|
||||||
err,
|
err,
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
))
|
))
|
||||||
})?
|
})?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
@ -613,7 +613,7 @@ mod array_functions {
|
|||||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
"retain".to_string(),
|
"retain".to_string(),
|
||||||
err,
|
err,
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
))
|
))
|
||||||
})?
|
})?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
@ -665,7 +665,7 @@ mod array_functions {
|
|||||||
|
|
||||||
for (a1, a2) in arr1.iter_mut().zip(arr2.iter_mut()) {
|
for (a1, a2) in arr1.iter_mut().zip(arr2.iter_mut()) {
|
||||||
let equals = ctx
|
let equals = ctx
|
||||||
.call_fn_dynamic_raw(OP_EQUALS, true, false, &mut [a1, a2], def_value.clone())
|
.call_fn_dynamic_raw(OP_EQUALS, true, false, &mut [a1, a2], def_value.as_ref())
|
||||||
.map(|v| v.as_bool().unwrap_or(false))?;
|
.map(|v| v.as_bool().unwrap_or(false))?;
|
||||||
|
|
||||||
if !equals {
|
if !equals {
|
||||||
|
@ -61,7 +61,7 @@ mod map_functions {
|
|||||||
for (m1, v1) in map1.iter_mut() {
|
for (m1, v1) in map1.iter_mut() {
|
||||||
if let Some(v2) = map2.get_mut(m1) {
|
if let Some(v2) = map2.get_mut(m1) {
|
||||||
let equals = ctx
|
let equals = ctx
|
||||||
.call_fn_dynamic_raw(OP_EQUALS, true, false, &mut [v1, v2], def_value.clone())
|
.call_fn_dynamic_raw(OP_EQUALS, true, false, &mut [v1, v2], def_value.as_ref())
|
||||||
.map(|v| v.as_bool().unwrap_or(false))?;
|
.map(|v| v.as_bool().unwrap_or(false))?;
|
||||||
|
|
||||||
if !equals {
|
if !equals {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::{def_package, INT, NO_POS};
|
use crate::{def_package, Position, INT};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
use crate::FLOAT;
|
use crate::FLOAT;
|
||||||
@ -83,8 +83,11 @@ mod int_functions {
|
|||||||
#[rhai_fn(name = "parse_int", return_raw)]
|
#[rhai_fn(name = "parse_int", return_raw)]
|
||||||
pub fn parse_int_radix(s: &str, radix: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn parse_int_radix(s: &str, radix: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
if radix < 2 || radix > 36 {
|
if radix < 2 || radix > 36 {
|
||||||
return EvalAltResult::ErrorArithmetic(format!("Invalid radix: '{}'", radix), NO_POS)
|
return EvalAltResult::ErrorArithmetic(
|
||||||
.into();
|
format!("Invalid radix: '{}'", radix),
|
||||||
|
Position::NONE,
|
||||||
|
)
|
||||||
|
.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
INT::from_str_radix(s.trim(), radix as u32)
|
INT::from_str_radix(s.trim(), radix as u32)
|
||||||
@ -92,7 +95,7 @@ mod int_functions {
|
|||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
EvalAltResult::ErrorArithmetic(
|
EvalAltResult::ErrorArithmetic(
|
||||||
format!("Error parsing integer number '{}': {}", s, err),
|
format!("Error parsing integer number '{}': {}", s, err),
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
@ -201,8 +204,11 @@ mod float_functions {
|
|||||||
#[rhai_fn(name = "to_int", return_raw)]
|
#[rhai_fn(name = "to_int", return_raw)]
|
||||||
pub fn f32_to_int(x: f32) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn f32_to_int(x: f32) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f32) {
|
if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f32) {
|
||||||
EvalAltResult::ErrorArithmetic(format!("Integer overflow: to_int({})", x), NO_POS)
|
EvalAltResult::ErrorArithmetic(
|
||||||
.into()
|
format!("Integer overflow: to_int({})", x),
|
||||||
|
Position::NONE,
|
||||||
|
)
|
||||||
|
.into()
|
||||||
} else {
|
} else {
|
||||||
Ok((x.trunc() as INT).into())
|
Ok((x.trunc() as INT).into())
|
||||||
}
|
}
|
||||||
@ -210,8 +216,11 @@ mod float_functions {
|
|||||||
#[rhai_fn(name = "to_int", return_raw)]
|
#[rhai_fn(name = "to_int", return_raw)]
|
||||||
pub fn f64_to_int(x: f64) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn f64_to_int(x: f64) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f64) {
|
if cfg!(not(feature = "unchecked")) && x > (MAX_INT as f64) {
|
||||||
EvalAltResult::ErrorArithmetic(format!("Integer overflow: to_int({})", x), NO_POS)
|
EvalAltResult::ErrorArithmetic(
|
||||||
.into()
|
format!("Integer overflow: to_int({})", x),
|
||||||
|
Position::NONE,
|
||||||
|
)
|
||||||
|
.into()
|
||||||
} else {
|
} else {
|
||||||
Ok((x.trunc() as INT).into())
|
Ok((x.trunc() as INT).into())
|
||||||
}
|
}
|
||||||
@ -224,7 +233,7 @@ mod float_functions {
|
|||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
EvalAltResult::ErrorArithmetic(
|
EvalAltResult::ErrorArithmetic(
|
||||||
format!("Error parsing floating-point number '{}': {}", s, err),
|
format!("Error parsing floating-point number '{}': {}", s, err),
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
|
@ -44,39 +44,50 @@ pub trait Package {
|
|||||||
fn get(&self) -> PackageLibrary;
|
fn get(&self) -> PackageLibrary;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A sharable `Module` to facilitate sharing library instances.
|
/// A sharable [`Module`][crate::Module] to facilitate sharing library instances.
|
||||||
pub type PackageLibrary = Shared<Module>;
|
pub type PackageLibrary = Shared<Module>;
|
||||||
|
|
||||||
/// Type containing a collection of `PackageLibrary` instances.
|
/// Type containing a collection of [`PackageLibrary`] instances.
|
||||||
/// All function and type iterator keys in the loaded packages are indexed for fast access.
|
/// All function and type iterator keys in the loaded packages are indexed for fast access.
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub(crate) struct PackagesCollection(StaticVec<PackageLibrary>);
|
pub(crate) struct PackagesCollection(Option<StaticVec<PackageLibrary>>);
|
||||||
|
|
||||||
impl PackagesCollection {
|
impl PackagesCollection {
|
||||||
/// Add a `PackageLibrary` into the `PackagesCollection`.
|
/// Add a [`PackageLibrary`] into the [`PackagesCollection`].
|
||||||
///
|
///
|
||||||
/// Packages are searched in reverse order.
|
/// Packages are searched in reverse order.
|
||||||
pub fn add(&mut self, package: PackageLibrary) {
|
pub fn add(&mut self, package: PackageLibrary) {
|
||||||
|
if self.0.is_none() {
|
||||||
|
self.0 = Some(Default::default());
|
||||||
|
}
|
||||||
// Later packages override previous ones.
|
// Later packages override previous ones.
|
||||||
self.0.insert(0, package);
|
self.0.as_mut().unwrap().insert(0, package);
|
||||||
}
|
}
|
||||||
/// Does the specified function hash key exist in the `PackagesCollection`?
|
/// Does the specified function hash key exist in the [`PackagesCollection`]?
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn contains_fn(&self, hash: u64) -> bool {
|
pub fn contains_fn(&self, hash: u64) -> bool {
|
||||||
self.0.iter().any(|p| p.contains_fn(hash, false))
|
self.0
|
||||||
|
.as_ref()
|
||||||
|
.map_or(false, |x| x.iter().any(|p| p.contains_fn(hash, false)))
|
||||||
}
|
}
|
||||||
/// Get specified function via its hash key.
|
/// Get specified function via its hash key.
|
||||||
pub fn get_fn(&self, hash: u64) -> Option<&CallableFunction> {
|
pub fn get_fn(&self, hash: u64) -> Option<&CallableFunction> {
|
||||||
self.0.iter().find_map(|p| p.get_fn(hash, false))
|
self.0
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|x| x.iter().find_map(|p| p.get_fn(hash, false)))
|
||||||
}
|
}
|
||||||
/// Does the specified TypeId iterator exist in the `PackagesCollection`?
|
/// Does the specified [`TypeId`] iterator exist in the [`PackagesCollection`]?
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn contains_iter(&self, id: TypeId) -> bool {
|
pub fn contains_iter(&self, id: TypeId) -> bool {
|
||||||
self.0.iter().any(|p| p.contains_iter(id))
|
self.0
|
||||||
|
.as_ref()
|
||||||
|
.map_or(false, |x| x.iter().any(|p| p.contains_iter(id)))
|
||||||
}
|
}
|
||||||
/// Get the specified TypeId iterator.
|
/// Get the specified [`TypeId`] iterator.
|
||||||
pub fn get_iter(&self, id: TypeId) -> Option<IteratorFn> {
|
pub fn get_iter(&self, id: TypeId) -> Option<IteratorFn> {
|
||||||
self.0.iter().find_map(|p| p.get_iter(id))
|
self.0
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|x| x.iter().find_map(|p| p.get_iter(id)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +95,7 @@ impl PackagesCollection {
|
|||||||
/// and register functions into it.
|
/// and register functions into it.
|
||||||
///
|
///
|
||||||
/// Functions can be added to the package using the standard module methods such as
|
/// Functions can be added to the package using the standard module methods such as
|
||||||
/// `set_fn_2`, `set_fn_3_mut`, `set_fn_0` etc.
|
/// [`set_fn_2`][Module::set_fn_2], [`set_fn_3_mut`][Module::set_fn_3_mut], [`set_fn_0`][Module::set_fn_0] etc.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
|
@ -252,7 +252,7 @@ mod string_functions {
|
|||||||
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 crate::EvalAltResult::ErrorDataTooLarge(
|
return crate::EvalAltResult::ErrorDataTooLarge(
|
||||||
"Length of string".to_string(),
|
"Length of string".to_string(),
|
||||||
crate::NO_POS,
|
crate::Position::NONE,
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
@ -272,7 +272,7 @@ mod string_functions {
|
|||||||
{
|
{
|
||||||
return crate::EvalAltResult::ErrorDataTooLarge(
|
return crate::EvalAltResult::ErrorDataTooLarge(
|
||||||
"Length of string".to_string(),
|
"Length of string".to_string(),
|
||||||
crate::NO_POS,
|
crate::Position::NONE,
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
@ -293,7 +293,7 @@ mod string_functions {
|
|||||||
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 crate::EvalAltResult::ErrorDataTooLarge(
|
return crate::EvalAltResult::ErrorDataTooLarge(
|
||||||
"Length of string".to_string(),
|
"Length of string".to_string(),
|
||||||
crate::NO_POS,
|
crate::Position::NONE,
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
@ -320,7 +320,7 @@ mod string_functions {
|
|||||||
{
|
{
|
||||||
return crate::EvalAltResult::ErrorDataTooLarge(
|
return crate::EvalAltResult::ErrorDataTooLarge(
|
||||||
"Length of string".to_string(),
|
"Length of string".to_string(),
|
||||||
crate::NO_POS,
|
crate::Position::NONE,
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,9 @@ use crate::stdlib::{
|
|||||||
fmt,
|
fmt,
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
};
|
};
|
||||||
use crate::{EvalAltResult, Position, NO_POS};
|
use crate::{EvalAltResult, Position};
|
||||||
|
|
||||||
/// _[INTERNALS]_ Error encountered when tokenizing the script text.
|
/// _(INTERNALS)_ Error encountered when tokenizing the script text.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// ## WARNING
|
/// ## WARNING
|
||||||
@ -65,7 +65,7 @@ impl LexError {
|
|||||||
Self::ImproperSymbol(_) => "Invalid symbol encountered",
|
Self::ImproperSymbol(_) => "Invalid symbol encountered",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Convert a `&LexError` into a `ParseError`.
|
/// Convert a `&LexError` into a [`ParseError`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn into_err(&self, pos: Position) -> ParseError {
|
pub fn into_err(&self, pos: Position) -> ParseError {
|
||||||
ParseError(Box::new(self.clone().into()), pos)
|
ParseError(Box::new(self.clone().into()), pos)
|
||||||
@ -160,7 +160,7 @@ pub enum ParseErrorType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ParseErrorType {
|
impl ParseErrorType {
|
||||||
/// Make a `ParseError` using the current type and position.
|
/// Make a [`ParseError`] using the current type and position.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn into_err(self, pos: Position) -> ParseError {
|
pub(crate) fn into_err(self, pos: Position) -> ParseError {
|
||||||
ParseError(Box::new(self), pos)
|
ParseError(Box::new(self), pos)
|
||||||
@ -281,7 +281,7 @@ impl fmt::Display for ParseError {
|
|||||||
impl From<ParseErrorType> for Box<EvalAltResult> {
|
impl From<ParseErrorType> for Box<EvalAltResult> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(err: ParseErrorType) -> Self {
|
fn from(err: ParseErrorType) -> Self {
|
||||||
Box::new(EvalAltResult::ErrorParsing(err, NO_POS))
|
Box::new(EvalAltResult::ErrorParsing(err, Position::NONE))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
112
src/parser.rs
112
src/parser.rs
@ -26,7 +26,7 @@ use crate::token::{is_keyword_function, is_valid_identifier, Token, TokenStream}
|
|||||||
use crate::utils::{get_hasher, StraightHasherBuilder};
|
use crate::utils::{get_hasher, StraightHasherBuilder};
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_script_fn_hash, Dynamic, Engine, FnAccess, ImmutableString, LexError, ParseError,
|
calc_script_fn_hash, Dynamic, Engine, FnAccess, ImmutableString, LexError, ParseError,
|
||||||
ParseErrorType, Position, Scope, StaticVec, AST, NO_POS,
|
ParseErrorType, Position, Scope, StaticVec, AST,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
@ -49,6 +49,8 @@ struct ParseState<'e> {
|
|||||||
/// Tracks a list of external variables (variables that are not explicitly declared in the scope).
|
/// Tracks a list of external variables (variables that are not explicitly declared in the scope).
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
externals: HashMap<ImmutableString, Position>,
|
externals: HashMap<ImmutableString, Position>,
|
||||||
|
/// Always search for variables instead of direct indexing into the scope.
|
||||||
|
always_search: bool,
|
||||||
/// An indicator that disables variable capturing into externals one single time
|
/// An indicator that disables variable capturing into externals one single time
|
||||||
/// up until the nearest consumed Identifier token.
|
/// up until the nearest consumed Identifier token.
|
||||||
/// If set to false the next call to `access_var` will not capture the variable.
|
/// If set to false the next call to `access_var` will not capture the variable.
|
||||||
@ -92,6 +94,7 @@ impl<'e> ParseState<'e> {
|
|||||||
allow_capture: true,
|
allow_capture: true,
|
||||||
strings: HashMap::with_capacity(64),
|
strings: HashMap::with_capacity(64),
|
||||||
stack: Vec::with_capacity(16),
|
stack: Vec::with_capacity(16),
|
||||||
|
always_search: false,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
modules: Default::default(),
|
modules: Default::default(),
|
||||||
}
|
}
|
||||||
@ -103,6 +106,7 @@ impl<'e> ParseState<'e> {
|
|||||||
///
|
///
|
||||||
/// The return value is the offset to be deducted from `Stack::len`,
|
/// The return value is the offset to be deducted from `Stack::len`,
|
||||||
/// i.e. the top element of the `ParseState` is offset 1.
|
/// i.e. the top element of the `ParseState` is offset 1.
|
||||||
|
///
|
||||||
/// Return `None` when the variable name is not found in the `stack`.
|
/// Return `None` when the variable name is not found in the `stack`.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn access_var(&mut self, name: &str, _pos: Position) -> Option<NonZeroUsize> {
|
fn access_var(&mut self, name: &str, _pos: Position) -> Option<NonZeroUsize> {
|
||||||
@ -123,7 +127,11 @@ impl<'e> ParseState<'e> {
|
|||||||
self.allow_capture = true
|
self.allow_capture = true
|
||||||
}
|
}
|
||||||
|
|
||||||
index
|
if self.always_search {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
index
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find a module by name in the `ParseState`, searching in reverse.
|
/// Find a module by name in the `ParseState`, searching in reverse.
|
||||||
@ -464,8 +472,7 @@ fn parse_index_chain(
|
|||||||
| Expr::And(_, _)
|
| Expr::And(_, _)
|
||||||
| Expr::Or(_, _)
|
| Expr::Or(_, _)
|
||||||
| Expr::In(_, _)
|
| Expr::In(_, _)
|
||||||
| Expr::True(_)
|
| Expr::BoolConstant(_, _)
|
||||||
| Expr::False(_)
|
|
||||||
| Expr::Unit(_) => {
|
| Expr::Unit(_) => {
|
||||||
return Err(PERR::MalformedIndexExpr(
|
return Err(PERR::MalformedIndexExpr(
|
||||||
"Only arrays, object maps and strings can be indexed".into(),
|
"Only arrays, object maps and strings can be indexed".into(),
|
||||||
@ -499,8 +506,7 @@ fn parse_index_chain(
|
|||||||
| Expr::And(_, _)
|
| Expr::And(_, _)
|
||||||
| Expr::Or(_, _)
|
| Expr::Or(_, _)
|
||||||
| Expr::In(_, _)
|
| Expr::In(_, _)
|
||||||
| Expr::True(_)
|
| Expr::BoolConstant(_, _)
|
||||||
| Expr::False(_)
|
|
||||||
| Expr::Unit(_) => {
|
| Expr::Unit(_) => {
|
||||||
return Err(PERR::MalformedIndexExpr(
|
return Err(PERR::MalformedIndexExpr(
|
||||||
"Only arrays, object maps and strings can be indexed".into(),
|
"Only arrays, object maps and strings can be indexed".into(),
|
||||||
@ -541,7 +547,7 @@ fn parse_index_chain(
|
|||||||
.into_err(x.position()))
|
.into_err(x.position()))
|
||||||
}
|
}
|
||||||
// lhs[true], lhs[false]
|
// lhs[true], lhs[false]
|
||||||
x @ Expr::True(_) | x @ Expr::False(_) => {
|
x @ Expr::BoolConstant(_, _) => {
|
||||||
return Err(PERR::MalformedIndexExpr(
|
return Err(PERR::MalformedIndexExpr(
|
||||||
"Array access expects integer index, not a boolean".into(),
|
"Array access expects integer index, not a boolean".into(),
|
||||||
)
|
)
|
||||||
@ -999,8 +1005,8 @@ fn parse_primary(
|
|||||||
Token::LeftBracket => parse_array_literal(input, state, lib, settings.level_up())?,
|
Token::LeftBracket => parse_array_literal(input, state, lib, settings.level_up())?,
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Token::MapStart => parse_map_literal(input, state, lib, settings.level_up())?,
|
Token::MapStart => parse_map_literal(input, state, lib, settings.level_up())?,
|
||||||
Token::True => Expr::True(settings.pos),
|
Token::True => Expr::BoolConstant(true, settings.pos),
|
||||||
Token::False => Expr::False(settings.pos),
|
Token::False => Expr::BoolConstant(false, settings.pos),
|
||||||
Token::LexError(err) => return Err(err.into_err(settings.pos)),
|
Token::LexError(err) => return Err(err.into_err(settings.pos)),
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
@ -1197,7 +1203,7 @@ fn parse_unary(
|
|||||||
native_only: true,
|
native_only: true,
|
||||||
hash,
|
hash,
|
||||||
args,
|
args,
|
||||||
def_value: Some(false), // NOT operator, when operating on invalid operand, defaults to false
|
def_value: Some(false.into()), // NOT operator, when operating on invalid operand, defaults to false
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
pos,
|
pos,
|
||||||
@ -1451,8 +1457,7 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseErr
|
|||||||
| (_, x @ Expr::And(_, _))
|
| (_, x @ Expr::And(_, _))
|
||||||
| (_, x @ Expr::Or(_, _))
|
| (_, x @ Expr::Or(_, _))
|
||||||
| (_, x @ Expr::In(_, _))
|
| (_, x @ Expr::In(_, _))
|
||||||
| (_, x @ Expr::True(_))
|
| (_, x @ Expr::BoolConstant(_, _))
|
||||||
| (_, x @ Expr::False(_))
|
|
||||||
| (_, x @ Expr::Unit(_)) => {
|
| (_, x @ Expr::Unit(_)) => {
|
||||||
return Err(PERR::MalformedInExpr(
|
return Err(PERR::MalformedInExpr(
|
||||||
"'in' expression expects a string, array or object map".into(),
|
"'in' expression expects a string, array or object map".into(),
|
||||||
@ -1492,8 +1497,7 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseErr
|
|||||||
(x @ Expr::And(_, _), Expr::StringConstant(_, _))
|
(x @ Expr::And(_, _), Expr::StringConstant(_, _))
|
||||||
| (x @ Expr::Or(_, _), Expr::StringConstant(_, _))
|
| (x @ Expr::Or(_, _), Expr::StringConstant(_, _))
|
||||||
| (x @ Expr::In(_, _), Expr::StringConstant(_, _))
|
| (x @ Expr::In(_, _), Expr::StringConstant(_, _))
|
||||||
| (x @ Expr::True(_), Expr::StringConstant(_, _))
|
| (x @ Expr::BoolConstant(_, _), Expr::StringConstant(_, _)) => {
|
||||||
| (x @ Expr::False(_), Expr::StringConstant(_, _)) => {
|
|
||||||
return Err(PERR::MalformedInExpr(
|
return Err(PERR::MalformedInExpr(
|
||||||
"'in' expression for a string expects a string, not a boolean".into(),
|
"'in' expression for a string expects a string, not a boolean".into(),
|
||||||
)
|
)
|
||||||
@ -1545,8 +1549,7 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseErr
|
|||||||
(x @ Expr::And(_, _), Expr::Map(_, _))
|
(x @ Expr::And(_, _), Expr::Map(_, _))
|
||||||
| (x @ Expr::Or(_, _), Expr::Map(_, _))
|
| (x @ Expr::Or(_, _), Expr::Map(_, _))
|
||||||
| (x @ Expr::In(_, _), Expr::Map(_, _))
|
| (x @ Expr::In(_, _), Expr::Map(_, _))
|
||||||
| (x @ Expr::True(_), Expr::Map(_, _))
|
| (x @ Expr::BoolConstant(_, _), Expr::Map(_, _)) => {
|
||||||
| (x @ Expr::False(_), Expr::Map(_, _)) => {
|
|
||||||
return Err(PERR::MalformedInExpr(
|
return Err(PERR::MalformedInExpr(
|
||||||
"'in' expression for an object map expects a string, not a boolean".into(),
|
"'in' expression for an object map expects a string, not a boolean".into(),
|
||||||
)
|
)
|
||||||
@ -1657,7 +1660,7 @@ fn parse_binary_op(
|
|||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
||||||
|
|
||||||
let cmp_def = Some(false);
|
let cmp_def = Some(false.into());
|
||||||
let op = op_token.syntax();
|
let op = op_token.syntax();
|
||||||
let hash = calc_script_fn_hash(empty(), &op, 2);
|
let hash = calc_script_fn_hash(empty(), &op, 2);
|
||||||
|
|
||||||
@ -1697,7 +1700,7 @@ fn parse_binary_op(
|
|||||||
Box::new(FnCallExpr {
|
Box::new(FnCallExpr {
|
||||||
hash,
|
hash,
|
||||||
args,
|
args,
|
||||||
def_value: Some(true),
|
def_value: Some(true.into()),
|
||||||
..op_base
|
..op_base
|
||||||
}),
|
}),
|
||||||
pos,
|
pos,
|
||||||
@ -1790,6 +1793,7 @@ fn parse_custom_syntax(
|
|||||||
state.stack.len() + delta as usize,
|
state.stack.len() + delta as usize,
|
||||||
("".into(), ScopeEntryType::Normal),
|
("".into(), ScopeEntryType::Normal),
|
||||||
);
|
);
|
||||||
|
state.always_search = true;
|
||||||
}
|
}
|
||||||
delta if delta < 0 && state.stack.len() <= delta.abs() as usize => state.stack.clear(),
|
delta if delta < 0 && state.stack.len() <= delta.abs() as usize => state.stack.clear(),
|
||||||
delta if delta < 0 => state
|
delta if delta < 0 => state
|
||||||
@ -1975,49 +1979,68 @@ fn parse_if(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a while loop.
|
/// Parse a while loop.
|
||||||
fn parse_while(
|
fn parse_while_loop(
|
||||||
input: &mut TokenStream,
|
input: &mut TokenStream,
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
lib: &mut FunctionsLib,
|
lib: &mut FunctionsLib,
|
||||||
mut settings: ParseSettings,
|
mut settings: ParseSettings,
|
||||||
) -> Result<Stmt, ParseError> {
|
) -> Result<Stmt, ParseError> {
|
||||||
// while ...
|
|
||||||
let token_pos = eat_token(input, Token::While);
|
|
||||||
settings.pos = token_pos;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
||||||
|
|
||||||
// while guard { body }
|
// while|loops ...
|
||||||
ensure_not_statement_expr(input, "a boolean")?;
|
let (guard, token_pos) = match input.next().unwrap() {
|
||||||
let guard = parse_expr(input, state, lib, settings.level_up())?;
|
(Token::While, pos) => {
|
||||||
ensure_not_assignment(input)?;
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
|
(parse_expr(input, state, lib, settings.level_up())?, pos)
|
||||||
|
}
|
||||||
|
(Token::Loop, pos) => (Expr::BoolConstant(true, pos), pos),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
settings.pos = token_pos;
|
||||||
|
|
||||||
|
ensure_not_assignment(input)?;
|
||||||
settings.is_breakable = true;
|
settings.is_breakable = true;
|
||||||
let body = Box::new(parse_block(input, state, lib, settings.level_up())?);
|
let body = Box::new(parse_block(input, state, lib, settings.level_up())?);
|
||||||
|
|
||||||
Ok(Stmt::While(guard, body, token_pos))
|
Ok(Stmt::While(guard, body, token_pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a loop statement.
|
/// Parse a do loop.
|
||||||
fn parse_loop(
|
fn parse_do(
|
||||||
input: &mut TokenStream,
|
input: &mut TokenStream,
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
lib: &mut FunctionsLib,
|
lib: &mut FunctionsLib,
|
||||||
mut settings: ParseSettings,
|
mut settings: ParseSettings,
|
||||||
) -> Result<Stmt, ParseError> {
|
) -> Result<Stmt, ParseError> {
|
||||||
// loop ...
|
// do ...
|
||||||
let token_pos = eat_token(input, Token::Loop);
|
let token_pos = eat_token(input, Token::Do);
|
||||||
settings.pos = token_pos;
|
settings.pos = token_pos;
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
||||||
|
|
||||||
// loop { body }
|
// do { body } [while|until] guard
|
||||||
settings.is_breakable = true;
|
settings.is_breakable = true;
|
||||||
let body = Box::new(parse_block(input, state, lib, settings.level_up())?);
|
let body = Box::new(parse_block(input, state, lib, settings.level_up())?);
|
||||||
|
|
||||||
Ok(Stmt::Loop(body, token_pos))
|
let is_while = match input.next().unwrap() {
|
||||||
|
(Token::While, _) => true,
|
||||||
|
(Token::Until, _) => false,
|
||||||
|
(_, pos) => {
|
||||||
|
return Err(
|
||||||
|
PERR::MissingToken(Token::While.into(), "for the do statement".into())
|
||||||
|
.into_err(pos),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
|
settings.is_breakable = false;
|
||||||
|
let guard = parse_expr(input, state, lib, settings.level_up())?;
|
||||||
|
ensure_not_assignment(input)?;
|
||||||
|
|
||||||
|
Ok(Stmt::Do(body, guard, is_while, token_pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a for loop.
|
/// Parse a for loop.
|
||||||
@ -2270,6 +2293,7 @@ fn parse_block(
|
|||||||
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
||||||
|
|
||||||
let mut statements = Vec::with_capacity(8);
|
let mut statements = Vec::with_capacity(8);
|
||||||
|
let prev_always_search = state.always_search;
|
||||||
let prev_stack_len = state.stack.len();
|
let prev_stack_len = state.stack.len();
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -2319,6 +2343,10 @@ fn parse_block(
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
state.modules.truncate(prev_mods_len);
|
state.modules.truncate(prev_mods_len);
|
||||||
|
|
||||||
|
// The impact of new local variables goes away at the end of a block
|
||||||
|
// because any new variables introduced will go out of scope
|
||||||
|
state.always_search = prev_always_search;
|
||||||
|
|
||||||
Ok(Stmt::Block(statements, settings.pos))
|
Ok(Stmt::Block(statements, settings.pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2358,10 +2386,11 @@ fn parse_stmt(
|
|||||||
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
||||||
|
|
||||||
match token {
|
match token {
|
||||||
// Semicolon - empty statement
|
// ; - empty statement
|
||||||
Token::SemiColon => Ok(Some(Stmt::Noop(settings.pos))),
|
Token::SemiColon => Ok(Some(Stmt::Noop(settings.pos))),
|
||||||
|
|
||||||
Token::LeftBrace => parse_block(input, state, lib, settings.level_up()).map(Some),
|
// { - statements block
|
||||||
|
Token::LeftBrace => Ok(Some(parse_block(input, state, lib, settings.level_up())?)),
|
||||||
|
|
||||||
// fn ...
|
// fn ...
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
@ -2418,8 +2447,10 @@ fn parse_stmt(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Token::If => parse_if(input, state, lib, settings.level_up()).map(Some),
|
Token::If => parse_if(input, state, lib, settings.level_up()).map(Some),
|
||||||
Token::While => parse_while(input, state, lib, settings.level_up()).map(Some),
|
Token::While | Token::Loop => {
|
||||||
Token::Loop => parse_loop(input, state, lib, settings.level_up()).map(Some),
|
parse_while_loop(input, state, lib, settings.level_up()).map(Some)
|
||||||
|
}
|
||||||
|
Token::Do => parse_do(input, state, lib, settings.level_up()).map(Some),
|
||||||
Token::For => parse_for(input, state, lib, settings.level_up()).map(Some),
|
Token::For => parse_for(input, state, lib, settings.level_up()).map(Some),
|
||||||
|
|
||||||
Token::Continue if settings.is_breakable => {
|
Token::Continue if settings.is_breakable => {
|
||||||
@ -2838,7 +2869,7 @@ impl Engine {
|
|||||||
is_function_scope: false,
|
is_function_scope: false,
|
||||||
is_breakable: false,
|
is_breakable: false,
|
||||||
level: 0,
|
level: 0,
|
||||||
pos: NO_POS,
|
pos: Position::NONE,
|
||||||
};
|
};
|
||||||
let expr = parse_expr(input, &mut state, &mut functions, settings)?;
|
let expr = parse_expr(input, &mut state, &mut functions, settings)?;
|
||||||
|
|
||||||
@ -2891,7 +2922,7 @@ impl Engine {
|
|||||||
is_function_scope: false,
|
is_function_scope: false,
|
||||||
is_breakable: false,
|
is_breakable: false,
|
||||||
level: 0,
|
level: 0,
|
||||||
pos: NO_POS,
|
pos: Position::NONE,
|
||||||
};
|
};
|
||||||
|
|
||||||
let stmt = match parse_stmt(input, &mut state, &mut functions, settings)? {
|
let stmt = match parse_stmt(input, &mut state, &mut functions, settings)? {
|
||||||
@ -2961,8 +2992,7 @@ pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option<Expr> {
|
|||||||
Union::Int(value) => Some(Expr::IntegerConstant(value, pos)),
|
Union::Int(value) => Some(Expr::IntegerConstant(value, pos)),
|
||||||
Union::Char(value) => Some(Expr::CharConstant(value, pos)),
|
Union::Char(value) => Some(Expr::CharConstant(value, pos)),
|
||||||
Union::Str(value) => Some(Expr::StringConstant(value, pos)),
|
Union::Str(value) => Some(Expr::StringConstant(value, pos)),
|
||||||
Union::Bool(true) => Some(Expr::True(pos)),
|
Union::Bool(value) => Some(Expr::BoolConstant(value, pos)),
|
||||||
Union::Bool(false) => Some(Expr::False(pos)),
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Union::Array(array) => {
|
Union::Array(array) => {
|
||||||
let items: Vec<_> = array
|
let items: Vec<_> = array
|
||||||
|
@ -6,15 +6,16 @@ use crate::stdlib::{
|
|||||||
fmt,
|
fmt,
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
};
|
};
|
||||||
use crate::{Dynamic, ImmutableString, ParseErrorType, Position, INT, NO_POS};
|
use crate::{Dynamic, ImmutableString, ParseErrorType, Position, INT};
|
||||||
|
|
||||||
/// Evaluation result.
|
/// Evaluation result.
|
||||||
///
|
///
|
||||||
/// All wrapped `Position` values represent the location in the script where the error occurs.
|
/// All wrapped [`Position`] values represent the location in the script where the error occurs.
|
||||||
///
|
///
|
||||||
/// # Thread Safety
|
/// # Thread Safety
|
||||||
///
|
///
|
||||||
/// Currently, `EvalAltResult` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`.
|
/// Currently, [`EvalAltResult`] is neither [`Send`] nor [`Sync`].
|
||||||
|
/// Turn on the `sync` feature to make it [`Send`] `+` [`Sync`].
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum EvalAltResult {
|
pub enum EvalAltResult {
|
||||||
@ -246,7 +247,7 @@ impl fmt::Display for EvalAltResult {
|
|||||||
impl<T: AsRef<str>> From<T> for EvalAltResult {
|
impl<T: AsRef<str>> From<T> for EvalAltResult {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(err: T) -> Self {
|
fn from(err: T) -> Self {
|
||||||
Self::ErrorRuntime(err.as_ref().to_string().into(), NO_POS)
|
Self::ErrorRuntime(err.as_ref().to_string().into(), Position::NONE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,7 +256,7 @@ impl<T: AsRef<str>> From<T> for Box<EvalAltResult> {
|
|||||||
fn from(err: T) -> Self {
|
fn from(err: T) -> Self {
|
||||||
Box::new(EvalAltResult::ErrorRuntime(
|
Box::new(EvalAltResult::ErrorRuntime(
|
||||||
err.as_ref().to_string().into(),
|
err.as_ref().to_string().into(),
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -313,10 +314,10 @@ impl EvalAltResult {
|
|||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get the `Position` of this error.
|
/// Get the [`Position`] of this error.
|
||||||
pub fn position(&self) -> Position {
|
pub fn position(&self) -> Position {
|
||||||
match self {
|
match self {
|
||||||
Self::ErrorSystem(_, _) => NO_POS,
|
Self::ErrorSystem(_, _) => Position::NONE,
|
||||||
|
|
||||||
Self::ErrorParsing(_, pos)
|
Self::ErrorParsing(_, pos)
|
||||||
| Self::ErrorFunctionNotFound(_, pos)
|
| Self::ErrorFunctionNotFound(_, pos)
|
||||||
@ -346,7 +347,7 @@ impl EvalAltResult {
|
|||||||
| Self::Return(_, pos) => *pos,
|
| Self::Return(_, pos) => *pos,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Override the `Position` of this error.
|
/// Override the [`Position`] of this error.
|
||||||
pub fn set_position(&mut self, new_position: Position) {
|
pub fn set_position(&mut self, new_position: Position) {
|
||||||
match self {
|
match self {
|
||||||
Self::ErrorSystem(_, _) => (),
|
Self::ErrorSystem(_, _) => (),
|
||||||
@ -379,8 +380,8 @@ impl EvalAltResult {
|
|||||||
| Self::Return(_, pos) => *pos = new_position,
|
| Self::Return(_, pos) => *pos = new_position,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Consume the current `EvalAltResult` and return a new one with the specified `Position`
|
/// Consume the current [`EvalAltResult`] and return a new one with the specified [`Position`]
|
||||||
/// if the current position is `Position::None`.
|
/// if the current position is [`Position::None`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn fill_position(mut self: Box<Self>, new_position: Position) -> Box<Self> {
|
pub(crate) fn fill_position(mut self: Box<Self>, new_position: Position) -> Box<Self> {
|
||||||
if self.position().is_none() {
|
if self.position().is_none() {
|
||||||
|
65
src/scope.rs
65
src/scope.rs
@ -1,4 +1,4 @@
|
|||||||
//! Module that defines the `Scope` type representing a function call-stack scope.
|
//! Module that defines the [`Scope`] type representing a function call-stack scope.
|
||||||
|
|
||||||
use crate::dynamic::Variant;
|
use crate::dynamic::Variant;
|
||||||
use crate::stdlib::{borrow::Cow, boxed::Box, iter, string::String, vec::Vec};
|
use crate::stdlib::{borrow::Cow, boxed::Box, iter, string::String, vec::Vec};
|
||||||
@ -25,11 +25,12 @@ impl EntryType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Type containing information about the current scope.
|
/// Type containing information about the current scope.
|
||||||
/// Useful for keeping state between `Engine` evaluation runs.
|
/// Useful for keeping state between [`Engine`][crate::Engine] evaluation runs.
|
||||||
///
|
///
|
||||||
/// # Thread Safety
|
/// # Thread Safety
|
||||||
///
|
///
|
||||||
/// Currently, `Scope` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`.
|
/// Currently, [`Scope`] is neither [`Send`] nor [`Sync`].
|
||||||
|
/// Turn on the [`Sync`] feature to make it [`Send`] `+` [`Sync`].
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -57,12 +58,12 @@ impl EntryType {
|
|||||||
//
|
//
|
||||||
// # Implementation Notes
|
// # Implementation Notes
|
||||||
//
|
//
|
||||||
// `Scope` is implemented as three `Vec`'s of exactly the same length. Variables data (name, type, etc.)
|
// [`Scope`] is implemented as three [`Vec`]'s of exactly the same length. Variables data (name, type, etc.)
|
||||||
// is manually split into three equal-length arrays. That's because variable names take up the most space,
|
// is manually split into three equal-length arrays. That's because variable names take up the most space,
|
||||||
// with `Cow<str>` being four words long, but in the vast majority of cases the name is NOT used to look up
|
// with [`Cow<str>`][Cow] being four words long, but in the vast majority of cases the name is NOT used to look up
|
||||||
// a variable's value. Variable lookup is usually via direct index, by-passing the name altogether.
|
// a variable's value. Variable lookup is usually via direct index, by-passing the name altogether.
|
||||||
//
|
//
|
||||||
// Since `Dynamic` is reasonably small, packing it tightly improves cache locality when variables are accessed.
|
// Since [`Dynamic`] is reasonably small, packing it tightly improves cache locality when variables are accessed.
|
||||||
// The variable type is packed separately into another array because it is even smaller.
|
// The variable type is packed separately into another array because it is even smaller.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Scope<'a> {
|
pub struct Scope<'a> {
|
||||||
@ -85,7 +86,7 @@ impl Default for Scope<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Scope<'a> {
|
impl<'a> Scope<'a> {
|
||||||
/// Create a new Scope.
|
/// Create a new [`Scope`].
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -101,7 +102,7 @@ impl<'a> Scope<'a> {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
/// Empty the Scope.
|
/// Empty the [`Scope`].
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -127,7 +128,7 @@ impl<'a> Scope<'a> {
|
|||||||
self.values.clear();
|
self.values.clear();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Get the number of entries inside the Scope.
|
/// Get the number of entries inside the [`Scope`].
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -144,7 +145,7 @@ impl<'a> Scope<'a> {
|
|||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.values.len()
|
self.values.len()
|
||||||
}
|
}
|
||||||
/// Is the Scope empty?
|
/// Is the [`Scope`] empty?
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -161,7 +162,7 @@ impl<'a> Scope<'a> {
|
|||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.values.len() == 0
|
self.values.len() == 0
|
||||||
}
|
}
|
||||||
/// Add (push) a new entry to the Scope.
|
/// Add (push) a new entry to the [`Scope`].
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -181,7 +182,7 @@ impl<'a> Scope<'a> {
|
|||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.push_dynamic_value(name, EntryType::Normal, Dynamic::from(value))
|
self.push_dynamic_value(name, EntryType::Normal, Dynamic::from(value))
|
||||||
}
|
}
|
||||||
/// Add (push) a new `Dynamic` entry to the Scope.
|
/// Add (push) a new [`Dynamic`] entry to the [`Scope`].
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -197,13 +198,10 @@ impl<'a> Scope<'a> {
|
|||||||
pub fn push_dynamic(&mut self, name: impl Into<Cow<'a, str>>, value: Dynamic) -> &mut Self {
|
pub fn push_dynamic(&mut self, name: impl Into<Cow<'a, str>>, value: Dynamic) -> &mut Self {
|
||||||
self.push_dynamic_value(name, EntryType::Normal, value)
|
self.push_dynamic_value(name, EntryType::Normal, value)
|
||||||
}
|
}
|
||||||
/// Add (push) a new constant to the Scope.
|
/// Add (push) a new constant to the [`Scope`].
|
||||||
///
|
///
|
||||||
/// Constants are immutable and cannot be assigned to. Their values never change.
|
/// Constants are immutable and cannot be assigned to. Their values never change.
|
||||||
/// Constants propagation is a technique used to optimize an AST.
|
/// Constants propagation is a technique used to optimize an [`AST`][crate::AST].
|
||||||
///
|
|
||||||
/// However, in order to be used for optimization, constants must be in one of the recognized types:
|
|
||||||
/// `INT` (default to `i64`, `i32` if `only_i32`), `f64`, `String`, `char` and `bool`.
|
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -223,14 +221,10 @@ impl<'a> Scope<'a> {
|
|||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.push_dynamic_value(name, EntryType::Constant, Dynamic::from(value))
|
self.push_dynamic_value(name, EntryType::Constant, Dynamic::from(value))
|
||||||
}
|
}
|
||||||
/// Add (push) a new constant with a `Dynamic` value to the Scope.
|
/// Add (push) a new constant with a [`Dynamic`] value to the Scope.
|
||||||
///
|
///
|
||||||
/// Constants are immutable and cannot be assigned to. Their values never change.
|
/// Constants are immutable and cannot be assigned to. Their values never change.
|
||||||
/// Constants propagation is a technique used to optimize an AST.
|
/// Constants propagation is a technique used to optimize an [`AST`][crate::AST].
|
||||||
///
|
|
||||||
/// However, in order to be used for optimization, the `Dynamic` value must be in one of the
|
|
||||||
/// recognized types:
|
|
||||||
/// `INT` (default to `i64`, `i32` if `only_i32`), `f64`, `String`, `char` and `bool`.
|
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -250,7 +244,7 @@ impl<'a> Scope<'a> {
|
|||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.push_dynamic_value(name, EntryType::Constant, value)
|
self.push_dynamic_value(name, EntryType::Constant, value)
|
||||||
}
|
}
|
||||||
/// Add (push) a new entry with a `Dynamic` value to the Scope.
|
/// Add (push) a new entry with a [`Dynamic`] value to the [`Scope`].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn push_dynamic_value(
|
pub(crate) fn push_dynamic_value(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -263,7 +257,7 @@ impl<'a> Scope<'a> {
|
|||||||
self.values.push(value.into());
|
self.values.push(value.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Truncate (rewind) the Scope to a previous size.
|
/// Truncate (rewind) the [`Scope`] to a previous size.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -296,7 +290,7 @@ impl<'a> Scope<'a> {
|
|||||||
self.values.truncate(size);
|
self.values.truncate(size);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Does the scope contain the entry?
|
/// Does the [`Scope`] contain the entry?
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -316,7 +310,7 @@ impl<'a> Scope<'a> {
|
|||||||
.rev() // Always search a Scope in reverse order
|
.rev() // Always search a Scope in reverse order
|
||||||
.any(|(key, _)| name == key.as_ref())
|
.any(|(key, _)| name == key.as_ref())
|
||||||
}
|
}
|
||||||
/// Find an entry in the Scope, starting from the last.
|
/// Find an entry in the [`Scope`], starting from the last.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn get_index(&self, name: &str) -> Option<(usize, EntryType)> {
|
pub(crate) fn get_index(&self, name: &str) -> Option<(usize, EntryType)> {
|
||||||
self.names
|
self.names
|
||||||
@ -331,7 +325,7 @@ impl<'a> Scope<'a> {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/// Get the value of an entry in the Scope, starting from the last.
|
/// Get the value of an entry in the [`Scope`], starting from the last.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -352,7 +346,8 @@ impl<'a> Scope<'a> {
|
|||||||
.find(|(_, (key, _))| name == key.as_ref())
|
.find(|(_, (key, _))| name == key.as_ref())
|
||||||
.and_then(|(index, _)| self.values[index].flatten_clone().try_cast())
|
.and_then(|(index, _)| self.values[index].flatten_clone().try_cast())
|
||||||
}
|
}
|
||||||
/// Update the value of the named entry.
|
/// Update the value of the named entry in the [`Scope`].
|
||||||
|
///
|
||||||
/// Search starts backwards from the last, and only the first entry matching the specified name is updated.
|
/// Search starts backwards from the last, and only the first entry matching the specified name is updated.
|
||||||
/// If no entry matching the specified name is found, a new one is added.
|
/// If no entry matching the specified name is found, a new one is added.
|
||||||
///
|
///
|
||||||
@ -386,7 +381,7 @@ impl<'a> Scope<'a> {
|
|||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Get a mutable reference to an entry in the Scope.
|
/// Get a mutable reference to an entry in the [`Scope`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn get_mut(&mut self, index: usize) -> (&mut Dynamic, EntryType) {
|
pub(crate) fn get_mut(&mut self, index: usize) -> (&mut Dynamic, EntryType) {
|
||||||
(
|
(
|
||||||
@ -394,7 +389,7 @@ impl<'a> Scope<'a> {
|
|||||||
self.types[index],
|
self.types[index],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/// Update the access type of an entry in the Scope.
|
/// Update the access type of an entry in the [`Scope`].
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn add_entry_alias(&mut self, index: usize, alias: String) -> &mut Self {
|
pub(crate) fn add_entry_alias(&mut self, index: usize, alias: String) -> &mut Self {
|
||||||
@ -404,7 +399,7 @@ impl<'a> Scope<'a> {
|
|||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Clone the Scope, keeping only the last instances of each variable name.
|
/// Clone the [`Scope`], keeping only the last instances of each variable name.
|
||||||
/// Shadowed variables are omitted in the copy.
|
/// Shadowed variables are omitted in the copy.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn clone_visible(&self) -> Self {
|
pub(crate) fn clone_visible(&self) -> Self {
|
||||||
@ -424,7 +419,7 @@ impl<'a> Scope<'a> {
|
|||||||
|
|
||||||
entries
|
entries
|
||||||
}
|
}
|
||||||
/// Get an iterator to entries in the Scope.
|
/// Get an iterator to entries in the [`Scope`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) fn into_iter(
|
pub(crate) fn into_iter(
|
||||||
@ -435,7 +430,7 @@ impl<'a> Scope<'a> {
|
|||||||
.zip(self.types.into_iter().zip(self.values.into_iter()))
|
.zip(self.types.into_iter().zip(self.values.into_iter()))
|
||||||
.map(|((name, alias), (typ, value))| (name, typ, value, alias.to_vec()))
|
.map(|((name, alias), (typ, value))| (name, typ, value, alias.to_vec()))
|
||||||
}
|
}
|
||||||
/// Get an iterator to entries in the Scope.
|
/// Get an iterator to entries in the [`Scope`].
|
||||||
/// Shared values are flatten-cloned.
|
/// Shared values are flatten-cloned.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
@ -465,7 +460,7 @@ impl<'a> Scope<'a> {
|
|||||||
self.iter_raw()
|
self.iter_raw()
|
||||||
.map(|(name, constant, value)| (name, constant, value.flatten_clone()))
|
.map(|(name, constant, value)| (name, constant, value.flatten_clone()))
|
||||||
}
|
}
|
||||||
/// Get an iterator to entries in the Scope.
|
/// Get an iterator to entries in the [`Scope`].
|
||||||
/// Shared values are not expanded.
|
/// Shared values are not expanded.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn iter_raw<'x: 'a>(&'x self) -> impl Iterator<Item = (&'a str, bool, &'x Dynamic)> + 'x {
|
pub fn iter_raw<'x: 'a>(&'x self) -> impl Iterator<Item = (&'a str, bool, &'x Dynamic)> + 'x {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
//! Implement deserialization support of `Dynamic` for [`serde`](https://crates.io/crates/serde).
|
//! Implement deserialization support of [`Dynamic`][crate::Dynamic] for [`serde`].
|
||||||
|
|
||||||
use super::str::ImmutableStringDeserializer;
|
use super::str::ImmutableStringDeserializer;
|
||||||
use crate::dynamic::Union;
|
use crate::dynamic::Union;
|
||||||
use crate::stdlib::{any::type_name, boxed::Box, fmt, string::ToString};
|
use crate::stdlib::{any::type_name, boxed::Box, fmt, string::ToString};
|
||||||
use crate::{Dynamic, EvalAltResult, ImmutableString, LexError, ParseErrorType, NO_POS};
|
use crate::{Dynamic, EvalAltResult, ImmutableString, LexError, ParseErrorType, Position};
|
||||||
use serde::de::{
|
use serde::de::{
|
||||||
DeserializeSeed, Deserializer, Error, IntoDeserializer, MapAccess, SeqAccess, Visitor,
|
DeserializeSeed, Deserializer, Error, IntoDeserializer, MapAccess, SeqAccess, Visitor,
|
||||||
};
|
};
|
||||||
@ -15,19 +15,19 @@ use crate::Array;
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
use crate::Map;
|
use crate::Map;
|
||||||
|
|
||||||
/// Deserializer for `Dynamic` which is kept as a reference.
|
/// Deserializer for [`Dynamic`][crate::Dynamic] which is kept as a reference.
|
||||||
///
|
///
|
||||||
/// The reference is necessary because the deserialized type may hold references
|
/// The reference is necessary because the deserialized type may hold references
|
||||||
/// (especially `&str`) to the source `Dynamic`.
|
/// (especially `&str`) to the source [`Dynamic`][crate::Dynamic].
|
||||||
pub struct DynamicDeserializer<'a> {
|
pub struct DynamicDeserializer<'a> {
|
||||||
value: &'a Dynamic,
|
value: &'a Dynamic,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> DynamicDeserializer<'de> {
|
impl<'de> DynamicDeserializer<'de> {
|
||||||
/// Create a `DynamicDeserializer` from a reference to a `Dynamic` value.
|
/// Create a [`DynamicDeserializer`] from a reference to a [`Dynamic`][crate::Dynamic] value.
|
||||||
///
|
///
|
||||||
/// The reference is necessary because the deserialized type may hold references
|
/// The reference is necessary because the deserialized type may hold references
|
||||||
/// (especially `&str`) to the source `Dynamic`.
|
/// (especially `&str`) to the source [`Dynamic`][crate::Dynamic].
|
||||||
pub fn from_dynamic(value: &'de Dynamic) -> Self {
|
pub fn from_dynamic(value: &'de Dynamic) -> Self {
|
||||||
Self { value }
|
Self { value }
|
||||||
}
|
}
|
||||||
@ -37,8 +37,12 @@ impl<'de> DynamicDeserializer<'de> {
|
|||||||
}
|
}
|
||||||
/// 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) -> Result<T, Box<EvalAltResult>> {
|
||||||
EvalAltResult::ErrorMismatchOutputType(error.into(), self.value.type_name().into(), NO_POS)
|
EvalAltResult::ErrorMismatchOutputType(
|
||||||
.into()
|
error.into(),
|
||||||
|
self.value.type_name().into(),
|
||||||
|
Position::NONE,
|
||||||
|
)
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
fn deserialize_int<V: Visitor<'de>>(
|
fn deserialize_int<V: Visitor<'de>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -56,7 +60,7 @@ impl<'de> DynamicDeserializer<'de> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deserialize a `Dynamic` value into a Rust type that implements `serde::Deserialize`.
|
/// Deserialize a [`Dynamic`][crate::Dynamic] value into a Rust type that implements [`serde::Deserialize`].
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -117,7 +121,7 @@ impl Error for Box<EvalAltResult> {
|
|||||||
fn custom<T: fmt::Display>(err: T) -> Self {
|
fn custom<T: fmt::Display>(err: T) -> Self {
|
||||||
EvalAltResult::ErrorParsing(
|
EvalAltResult::ErrorParsing(
|
||||||
ParseErrorType::BadInput(LexError::ImproperSymbol(err.to_string())),
|
ParseErrorType::BadInput(LexError::ImproperSymbol(err.to_string())),
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
@ -445,7 +449,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
|
|||||||
|
|
||||||
/// `SeqAccess` implementation for arrays.
|
/// `SeqAccess` implementation for arrays.
|
||||||
struct IterateArray<'a, ITER: Iterator<Item = &'a Dynamic>> {
|
struct IterateArray<'a, ITER: Iterator<Item = &'a Dynamic>> {
|
||||||
/// Iterator for a stream of `Dynamic` values.
|
/// Iterator for a stream of [`Dynamic`][crate::Dynamic] values.
|
||||||
iter: ITER,
|
iter: ITER,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -479,9 +483,9 @@ where
|
|||||||
KEYS: Iterator<Item = &'a ImmutableString>,
|
KEYS: Iterator<Item = &'a ImmutableString>,
|
||||||
VALUES: Iterator<Item = &'a Dynamic>,
|
VALUES: Iterator<Item = &'a Dynamic>,
|
||||||
{
|
{
|
||||||
// Iterator for a stream of `Dynamic` keys.
|
// Iterator for a stream of [`Dynamic`][crate::Dynamic] keys.
|
||||||
keys: KEYS,
|
keys: KEYS,
|
||||||
// Iterator for a stream of `Dynamic` values.
|
// Iterator for a stream of [`Dynamic`][crate::Dynamic] values.
|
||||||
values: VALUES,
|
values: VALUES,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//! Helper module defining serialization/deserialization support for [`serde`](https://crates.io/crates/serde).
|
//! Helper module defining serialization/deserialization support for [`serde`].
|
||||||
|
|
||||||
pub mod de;
|
pub mod de;
|
||||||
pub mod ser;
|
pub mod ser;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! Implement serialization support of `Dynamic` for [`serde`](https://crates.io/crates/serde).
|
//! Implement serialization support of [`Dynamic`][crate::Dynamic] for [`serde`].
|
||||||
|
|
||||||
use crate::stdlib::{boxed::Box, fmt, string::ToString};
|
use crate::stdlib::{boxed::Box, fmt, string::ToString};
|
||||||
use crate::{Dynamic, EvalAltResult, NO_POS};
|
use crate::{Dynamic, EvalAltResult, Position};
|
||||||
use serde::ser::{
|
use serde::ser::{
|
||||||
Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple, SerializeTupleStruct,
|
Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple, SerializeTupleStruct,
|
||||||
Serializer,
|
Serializer,
|
||||||
@ -14,7 +14,7 @@ use crate::Array;
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
use crate::Map;
|
use crate::Map;
|
||||||
|
|
||||||
/// Serializer for `Dynamic` which is kept as a reference.
|
/// Serializer for [`Dynamic`][crate::Dynamic] which is kept as a reference.
|
||||||
pub struct DynamicSerializer {
|
pub struct DynamicSerializer {
|
||||||
/// Buffer to hold a temporary key.
|
/// Buffer to hold a temporary key.
|
||||||
_key: Dynamic,
|
_key: Dynamic,
|
||||||
@ -23,7 +23,7 @@ pub struct DynamicSerializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DynamicSerializer {
|
impl DynamicSerializer {
|
||||||
/// Create a `DynamicSerializer` from a `Dynamic` value.
|
/// Create a [`DynamicSerializer`] from a [`Dynamic`][crate::Dynamic] value.
|
||||||
pub fn new(_value: Dynamic) -> Self {
|
pub fn new(_value: Dynamic) -> Self {
|
||||||
Self {
|
Self {
|
||||||
_key: Default::default(),
|
_key: Default::default(),
|
||||||
@ -32,7 +32,7 @@ impl DynamicSerializer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serialize a Rust type that implements `serde::Serialize` into a `Dynamic`.
|
/// Serialize a Rust type that implements [`serde::Serialize`] into a [`Dynamic`][crate::Dynamic].
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -87,7 +87,7 @@ pub fn to_dynamic<T: Serialize>(value: T) -> Result<Dynamic, Box<EvalAltResult>>
|
|||||||
|
|
||||||
impl Error for Box<EvalAltResult> {
|
impl Error for Box<EvalAltResult> {
|
||||||
fn custom<T: fmt::Display>(err: T) -> Self {
|
fn custom<T: fmt::Display>(err: T) -> Self {
|
||||||
EvalAltResult::ErrorRuntime(err.to_string().into(), NO_POS).into()
|
EvalAltResult::ErrorRuntime(err.to_string().into(), Position::NONE).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,16 +283,24 @@ impl Serializer for &mut DynamicSerializer {
|
|||||||
make_variant(_variant, content)
|
make_variant(_variant, content)
|
||||||
}
|
}
|
||||||
#[cfg(feature = "no_object")]
|
#[cfg(feature = "no_object")]
|
||||||
return EvalAltResult::ErrorMismatchOutputType("Dynamic".into(), "map".into(), NO_POS)
|
return EvalAltResult::ErrorMismatchOutputType(
|
||||||
.into();
|
"Dynamic".into(),
|
||||||
|
"map".into(),
|
||||||
|
Position::NONE,
|
||||||
|
)
|
||||||
|
.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Box<EvalAltResult>> {
|
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Box<EvalAltResult>> {
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
return Ok(DynamicSerializer::new(Array::new().into()));
|
return Ok(DynamicSerializer::new(Array::new().into()));
|
||||||
#[cfg(feature = "no_index")]
|
#[cfg(feature = "no_index")]
|
||||||
return EvalAltResult::ErrorMismatchOutputType("Dynamic".into(), "array".into(), NO_POS)
|
return EvalAltResult::ErrorMismatchOutputType(
|
||||||
.into();
|
"Dynamic".into(),
|
||||||
|
"array".into(),
|
||||||
|
Position::NONE,
|
||||||
|
)
|
||||||
|
.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Box<EvalAltResult>> {
|
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Box<EvalAltResult>> {
|
||||||
@ -325,7 +333,12 @@ impl Serializer for &mut DynamicSerializer {
|
|||||||
let err_type = "map";
|
let err_type = "map";
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
let err_type = "array";
|
let err_type = "array";
|
||||||
EvalAltResult::ErrorMismatchOutputType("Dynamic".into(), err_type.into(), NO_POS).into()
|
EvalAltResult::ErrorMismatchOutputType(
|
||||||
|
"Dynamic".into(),
|
||||||
|
err_type.into(),
|
||||||
|
Position::NONE,
|
||||||
|
)
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,8 +346,12 @@ impl Serializer for &mut DynamicSerializer {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
return Ok(DynamicSerializer::new(Map::new().into()));
|
return Ok(DynamicSerializer::new(Map::new().into()));
|
||||||
#[cfg(feature = "no_object")]
|
#[cfg(feature = "no_object")]
|
||||||
return EvalAltResult::ErrorMismatchOutputType("Dynamic".into(), "map".into(), NO_POS)
|
return EvalAltResult::ErrorMismatchOutputType(
|
||||||
.into();
|
"Dynamic".into(),
|
||||||
|
"map".into(),
|
||||||
|
Position::NONE,
|
||||||
|
)
|
||||||
|
.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_struct(
|
fn serialize_struct(
|
||||||
@ -358,8 +375,12 @@ impl Serializer for &mut DynamicSerializer {
|
|||||||
map: Map::with_capacity(_len),
|
map: Map::with_capacity(_len),
|
||||||
});
|
});
|
||||||
#[cfg(feature = "no_object")]
|
#[cfg(feature = "no_object")]
|
||||||
return EvalAltResult::ErrorMismatchOutputType("Dynamic".into(), "map".into(), NO_POS)
|
return EvalAltResult::ErrorMismatchOutputType(
|
||||||
.into();
|
"Dynamic".into(),
|
||||||
|
"map".into(),
|
||||||
|
Position::NONE,
|
||||||
|
)
|
||||||
|
.into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -468,7 +489,11 @@ impl SerializeMap for DynamicSerializer {
|
|||||||
let key = crate::stdlib::mem::take(&mut self._key)
|
let key = crate::stdlib::mem::take(&mut self._key)
|
||||||
.take_immutable_string()
|
.take_immutable_string()
|
||||||
.map_err(|typ| {
|
.map_err(|typ| {
|
||||||
EvalAltResult::ErrorMismatchOutputType("string".into(), typ.into(), NO_POS)
|
EvalAltResult::ErrorMismatchOutputType(
|
||||||
|
"string".into(),
|
||||||
|
typ.into(),
|
||||||
|
Position::NONE,
|
||||||
|
)
|
||||||
})?;
|
})?;
|
||||||
let _value = _value.serialize(&mut *self)?;
|
let _value = _value.serialize(&mut *self)?;
|
||||||
let map = self._value.downcast_mut::<Map>().unwrap();
|
let map = self._value.downcast_mut::<Map>().unwrap();
|
||||||
@ -488,7 +513,7 @@ impl SerializeMap for DynamicSerializer {
|
|||||||
{
|
{
|
||||||
let _key: Dynamic = _key.serialize(&mut *self)?;
|
let _key: Dynamic = _key.serialize(&mut *self)?;
|
||||||
let _key = _key.take_immutable_string().map_err(|typ| {
|
let _key = _key.take_immutable_string().map_err(|typ| {
|
||||||
EvalAltResult::ErrorMismatchOutputType("string".into(), typ.into(), NO_POS)
|
EvalAltResult::ErrorMismatchOutputType("string".into(), typ.into(), Position::NONE)
|
||||||
})?;
|
})?;
|
||||||
let _value = _value.serialize(&mut *self)?;
|
let _value = _value.serialize(&mut *self)?;
|
||||||
let map = self._value.downcast_mut::<Map>().unwrap();
|
let map = self._value.downcast_mut::<Map>().unwrap();
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! Implement deserialization support of `ImmutableString` for [`serde`](https://crates.io/crates/serde).
|
//! Implement deserialization support of [`ImmutableString`][crate::ImmutableString] for [`serde`].
|
||||||
|
|
||||||
use crate::stdlib::{any::type_name, boxed::Box};
|
use crate::stdlib::{any::type_name, boxed::Box};
|
||||||
use crate::{EvalAltResult, ImmutableString, NO_POS};
|
use crate::{EvalAltResult, ImmutableString, Position};
|
||||||
use serde::de::{Deserializer, Visitor};
|
use serde::de::{Deserializer, Visitor};
|
||||||
|
|
||||||
/// Deserializer for `ImmutableString`.
|
/// Deserializer for `ImmutableString`.
|
||||||
@ -16,8 +16,12 @@ impl<'a> ImmutableStringDeserializer<'a> {
|
|||||||
}
|
}
|
||||||
/// 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) -> Result<T, Box<EvalAltResult>> {
|
||||||
EvalAltResult::ErrorMismatchOutputType(type_name::<T>().into(), "string".into(), NO_POS)
|
EvalAltResult::ErrorMismatchOutputType(
|
||||||
.into()
|
type_name::<T>().into(),
|
||||||
|
"string".into(),
|
||||||
|
Position::NONE,
|
||||||
|
)
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//! Module implementing custom syntax for `Engine`.
|
//! Module implementing custom syntax for [`Engine`].
|
||||||
|
|
||||||
use crate::ast::Expr;
|
use crate::ast::Expr;
|
||||||
use crate::engine::{EvalContext, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT};
|
use crate::engine::{EvalContext, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT};
|
||||||
@ -11,7 +11,7 @@ use crate::stdlib::{
|
|||||||
use crate::token::{is_valid_identifier, Token};
|
use crate::token::{is_valid_identifier, Token};
|
||||||
use crate::{
|
use crate::{
|
||||||
Dynamic, Engine, EvalAltResult, ImmutableString, LexError, ParseError, Position, Shared,
|
Dynamic, Engine, EvalAltResult, ImmutableString, LexError, ParseError, Position, Shared,
|
||||||
StaticVec, NO_POS,
|
StaticVec,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A general expression evaluation trait object.
|
/// A general expression evaluation trait object.
|
||||||
@ -31,7 +31,7 @@ pub type FnCustomSyntaxParse = dyn Fn(&[String]) -> Result<Option<String>, Parse
|
|||||||
pub type FnCustomSyntaxParse =
|
pub type FnCustomSyntaxParse =
|
||||||
dyn Fn(&[String]) -> Result<Option<String>, ParseError> + Send + Sync;
|
dyn Fn(&[String]) -> Result<Option<String>, ParseError> + Send + Sync;
|
||||||
|
|
||||||
/// An expression sub-tree in an AST.
|
/// An expression sub-tree in an [`AST`][crate::AST].
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Expression<'a>(&'a Expr);
|
pub struct Expression<'a>(&'a Expr);
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ impl<'a> From<&'a Expr> for Expression<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Expression<'_> {
|
impl Expression<'_> {
|
||||||
/// If this expression is a variable name, return it. Otherwise `None`.
|
/// If this expression is a variable name, return it. Otherwise [`None`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn get_variable_name(&self) -> Option<&str> {
|
pub fn get_variable_name(&self) -> Option<&str> {
|
||||||
self.0.get_variable_access(true)
|
self.0.get_variable_access(true)
|
||||||
@ -65,7 +65,7 @@ impl EvalContext<'_, '_, '_, '_, '_, '_, '_, '_, '_> {
|
|||||||
///
|
///
|
||||||
/// ## WARNING - Low Level API
|
/// ## WARNING - Low Level API
|
||||||
///
|
///
|
||||||
/// This function is very low level. It evaluates an expression from an AST.
|
/// This function is very low level. It evaluates an expression from an [`AST`][crate::AST].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn eval_expression_tree(
|
pub fn eval_expression_tree(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -95,7 +95,7 @@ pub struct CustomSyntax {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
/// Register a custom syntax with the `Engine`.
|
/// Register a custom syntax with the [`Engine`].
|
||||||
///
|
///
|
||||||
/// * `keywords` holds a slice of strings that define the custom syntax.
|
/// * `keywords` holds a slice of strings that define the custom syntax.
|
||||||
/// * `new_vars` is the number of new variables declared by this custom syntax, or the number of variables removed (if negative).
|
/// * `new_vars` is the number of new variables declared by this custom syntax, or the number of variables removed (if negative).
|
||||||
@ -142,7 +142,7 @@ impl Engine {
|
|||||||
segments.len() + 1,
|
segments.len() + 1,
|
||||||
s
|
s
|
||||||
))
|
))
|
||||||
.into_err(NO_POS)
|
.into_err(Position::NONE)
|
||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
// Identifier in first position
|
// Identifier in first position
|
||||||
@ -159,7 +159,7 @@ impl Engine {
|
|||||||
segments.len() + 1,
|
segments.len() + 1,
|
||||||
s
|
s
|
||||||
))
|
))
|
||||||
.into_err(NO_POS)
|
.into_err(Position::NONE)
|
||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -191,7 +191,7 @@ impl Engine {
|
|||||||
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
/// Register a custom syntax with the `Engine`.
|
/// Register a custom syntax with the [`Engine`].
|
||||||
///
|
///
|
||||||
/// ## WARNING - Low Level API
|
/// ## WARNING - Low Level API
|
||||||
///
|
///
|
||||||
@ -201,7 +201,7 @@ impl Engine {
|
|||||||
/// * `parse` is the parsing function.
|
/// * `parse` is the parsing function.
|
||||||
/// * `func` is the implementation function.
|
/// * `func` is the implementation function.
|
||||||
///
|
///
|
||||||
/// All custom keywords must be manually registered via `Engine::register_custom_operator`.
|
/// All custom keywords must be manually registered via [`register_custom_operator`][Engine::register_custom_operator].
|
||||||
/// Otherwise, custom keywords won't be recognized.
|
/// Otherwise, custom keywords won't be recognized.
|
||||||
pub fn register_custom_syntax_raw(
|
pub fn register_custom_syntax_raw(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
58
src/token.rs
58
src/token.rs
@ -37,19 +37,16 @@ pub struct Position {
|
|||||||
pos: u16,
|
pos: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// No `Position`.
|
|
||||||
pub const NO_POS: Position = Position::NONE;
|
|
||||||
|
|
||||||
impl Position {
|
impl Position {
|
||||||
/// A `Position` representing no position.
|
/// A [`Position`] representing no position.
|
||||||
pub const NONE: Self = Self { line: 0, pos: 0 };
|
pub const NONE: Self = Self { line: 0, pos: 0 };
|
||||||
/// A `Position` representing the first position.
|
/// A [`Position`] representing the first position.
|
||||||
pub const START: Self = Self { line: 1, pos: 0 };
|
pub const START: Self = Self { line: 1, pos: 0 };
|
||||||
|
|
||||||
/// Create a new `Position`.
|
/// Create a new [`Position`].
|
||||||
///
|
///
|
||||||
/// `line` must not be zero.
|
/// `line` must not be zero.
|
||||||
/// If `position` is zero, then it is at the beginning of a line.
|
/// If [`Position`] is zero, then it is at the beginning of a line.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
@ -63,7 +60,7 @@ impl Position {
|
|||||||
pos: position,
|
pos: position,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get the line number (1-based), or `None` if there is no position.
|
/// Get the line number (1-based), or [`None`] if there is no position.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn line(self) -> Option<usize> {
|
pub fn line(self) -> Option<usize> {
|
||||||
if self.is_none() {
|
if self.is_none() {
|
||||||
@ -72,7 +69,7 @@ impl Position {
|
|||||||
Some(self.line as usize)
|
Some(self.line as usize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get the character position (1-based), or `None` if at beginning of a line.
|
/// Get the character position (1-based), or [`None`] if at beginning of a line.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn position(self) -> Option<usize> {
|
pub fn position(self) -> Option<usize> {
|
||||||
if self.is_none() || self.pos == 0 {
|
if self.is_none() || self.pos == 0 {
|
||||||
@ -113,12 +110,12 @@ impl Position {
|
|||||||
self.pos = 0;
|
self.pos = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Is this `Position` at the beginning of a line?
|
/// Is this [`Position`] at the beginning of a line?
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_beginning_of_line(self) -> bool {
|
pub fn is_beginning_of_line(self) -> bool {
|
||||||
self.line == 0 && !self.is_none()
|
self.line == 0 && !self.is_none()
|
||||||
}
|
}
|
||||||
/// Is there no `Position`?
|
/// Is there no [`Position`]?
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_none(self) -> bool {
|
pub fn is_none(self) -> bool {
|
||||||
self == Self::NONE
|
self == Self::NONE
|
||||||
@ -150,7 +147,7 @@ impl fmt::Debug for Position {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _[INTERNALS]_ A Rhai language token.
|
/// _(INTERNALS)_ A Rhai language token.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// ## WARNING
|
/// ## WARNING
|
||||||
@ -235,8 +232,12 @@ pub enum Token {
|
|||||||
Else,
|
Else,
|
||||||
/// `switch`
|
/// `switch`
|
||||||
Switch,
|
Switch,
|
||||||
|
/// `do`
|
||||||
|
Do,
|
||||||
/// `while`
|
/// `while`
|
||||||
While,
|
While,
|
||||||
|
/// `until`
|
||||||
|
Until,
|
||||||
/// `loop`
|
/// `loop`
|
||||||
Loop,
|
Loop,
|
||||||
/// `for`
|
/// `for`
|
||||||
@ -383,7 +384,9 @@ impl Token {
|
|||||||
If => "if",
|
If => "if",
|
||||||
Else => "else",
|
Else => "else",
|
||||||
Switch => "switch",
|
Switch => "switch",
|
||||||
|
Do => "do",
|
||||||
While => "while",
|
While => "while",
|
||||||
|
Until => "until",
|
||||||
Loop => "loop",
|
Loop => "loop",
|
||||||
For => "for",
|
For => "for",
|
||||||
In => "in",
|
In => "in",
|
||||||
@ -470,7 +473,9 @@ impl Token {
|
|||||||
"if" => If,
|
"if" => If,
|
||||||
"else" => Else,
|
"else" => Else,
|
||||||
"switch" => Switch,
|
"switch" => Switch,
|
||||||
|
"do" => Do,
|
||||||
"while" => While,
|
"while" => While,
|
||||||
|
"until" => Until,
|
||||||
"loop" => Loop,
|
"loop" => Loop,
|
||||||
"for" => For,
|
"for" => For,
|
||||||
"in" => In,
|
"in" => In,
|
||||||
@ -527,8 +532,8 @@ impl Token {
|
|||||||
"import" | "export" | "as" => Reserved(syntax.into()),
|
"import" | "export" | "as" => Reserved(syntax.into()),
|
||||||
|
|
||||||
"===" | "!==" | "->" | "<-" | ":=" | "::<" | "(*" | "*)" | "#" | "public" | "new"
|
"===" | "!==" | "->" | "<-" | ":=" | "::<" | "(*" | "*)" | "#" | "public" | "new"
|
||||||
| "use" | "module" | "package" | "var" | "static" | "shared" | "with" | "do"
|
| "use" | "module" | "package" | "var" | "static" | "shared" | "with" | "each"
|
||||||
| "each" | "then" | "goto" | "exit" | "match" | "case" | "default" | "void"
|
| "then" | "goto" | "unless" | "exit" | "match" | "case" | "default" | "void"
|
||||||
| "null" | "nil" | "spawn" | "thread" | "go" | "sync" | "async" | "await" | "yield" => {
|
| "null" | "nil" | "spawn" | "thread" | "go" | "sync" | "async" | "await" | "yield" => {
|
||||||
Reserved(syntax.into())
|
Reserved(syntax.into())
|
||||||
}
|
}
|
||||||
@ -544,7 +549,7 @@ impl Token {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is this token EOF?
|
// Is this token [`EOF`][Token::EOF]?
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_eof(&self) -> bool {
|
pub fn is_eof(&self) -> bool {
|
||||||
use Token::*;
|
use Token::*;
|
||||||
@ -556,7 +561,7 @@ impl Token {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If another operator is after these, it's probably an unary operator
|
// If another operator is after these, it's probably an unary operator
|
||||||
// (not sure about fn name).
|
// (not sure about `fn` name).
|
||||||
pub fn is_next_unary(&self) -> bool {
|
pub fn is_next_unary(&self) -> bool {
|
||||||
use Token::*;
|
use Token::*;
|
||||||
|
|
||||||
@ -589,7 +594,9 @@ impl Token {
|
|||||||
Ampersand |
|
Ampersand |
|
||||||
And |
|
And |
|
||||||
If |
|
If |
|
||||||
|
Do |
|
||||||
While |
|
While |
|
||||||
|
Until |
|
||||||
PlusAssign |
|
PlusAssign |
|
||||||
MinusAssign |
|
MinusAssign |
|
||||||
MultiplyAssign |
|
MultiplyAssign |
|
||||||
@ -693,8 +700,8 @@ impl Token {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Import | Export | As => true,
|
Import | Export | As => true,
|
||||||
|
|
||||||
True | False | Let | Const | If | Else | While | Loop | For | In | Continue | Break
|
True | False | Let | Const | If | Else | Do | While | Until | Loop | For | In
|
||||||
| Return | Throw | Try | Catch => true,
|
| Continue | Break | Return | Throw | Try | Catch => true,
|
||||||
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
@ -736,7 +743,7 @@ impl From<Token> for String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _[INTERNALS]_ State of the tokenizer.
|
/// _(INTERNALS)_ State of the tokenizer.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// ## WARNING
|
/// ## WARNING
|
||||||
@ -750,13 +757,13 @@ pub struct TokenizeState {
|
|||||||
pub non_unary: bool,
|
pub non_unary: bool,
|
||||||
/// Is the tokenizer currently inside a block comment?
|
/// Is the tokenizer currently inside a block comment?
|
||||||
pub comment_level: usize,
|
pub comment_level: usize,
|
||||||
/// Return `None` at the end of the stream instead of `Some(Token::EOF)`?
|
/// Return [`None`] at the end of the stream instead of [`Some(Token::EOF)`][Token::EOF]?
|
||||||
pub end_with_none: bool,
|
pub end_with_none: bool,
|
||||||
/// Include comments?
|
/// Include comments?
|
||||||
pub include_comments: bool,
|
pub include_comments: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _[INTERNALS]_ Trait that encapsulates a peekable character input stream.
|
/// _(INTERNALS)_ Trait that encapsulates a peekable character input stream.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// ## WARNING
|
/// ## WARNING
|
||||||
@ -770,7 +777,7 @@ pub trait InputStream {
|
|||||||
fn peek_next(&mut self) -> Option<char>;
|
fn peek_next(&mut self) -> Option<char>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _[INTERNALS]_ Parse a string literal wrapped by `enclosing_char`.
|
/// _(INTERNALS)_ Parse a string literal wrapped by `enclosing_char`.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// ## WARNING
|
/// ## WARNING
|
||||||
@ -959,7 +966,7 @@ fn scan_comment(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _[INTERNALS]_ Get the next token from the `InputStream`.
|
/// _(INTERNALS)_ Get the next token from the `stream`.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// ## WARNING
|
/// ## WARNING
|
||||||
@ -1528,6 +1535,7 @@ pub fn can_override_keyword(name: &str) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Is a text string a valid identifier?
|
||||||
pub fn is_valid_identifier(name: impl Iterator<Item = char>) -> bool {
|
pub fn is_valid_identifier(name: impl Iterator<Item = char>) -> bool {
|
||||||
let mut first_alphabetic = false;
|
let mut first_alphabetic = false;
|
||||||
|
|
||||||
@ -1568,7 +1576,7 @@ fn is_id_continue(x: char) -> bool {
|
|||||||
x.is_ascii_alphanumeric() || x == '_'
|
x.is_ascii_alphanumeric() || x == '_'
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type that implements the `InputStream` trait.
|
/// A type that implements the [`InputStream`] trait.
|
||||||
/// Multiple character streams are jointed together to form one single stream.
|
/// Multiple character streams are jointed together to form one single stream.
|
||||||
pub struct MultiInputsStream<'a> {
|
pub struct MultiInputsStream<'a> {
|
||||||
/// Buffered character, if any.
|
/// Buffered character, if any.
|
||||||
@ -1625,7 +1633,7 @@ impl InputStream for MultiInputsStream<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An iterator on a `Token` stream.
|
/// An iterator on a [`Token`] stream.
|
||||||
pub struct TokenIterator<'a, 'e> {
|
pub struct TokenIterator<'a, 'e> {
|
||||||
/// Reference to the scripting `Engine`.
|
/// Reference to the scripting `Engine`.
|
||||||
engine: &'e Engine,
|
engine: &'e Engine,
|
||||||
|
@ -44,11 +44,11 @@ pub fn unsafe_cast_box<X: Variant, T: Variant>(item: Box<X>) -> Result<Box<T>, B
|
|||||||
///
|
///
|
||||||
/// A dangerous function that blindly casts a `&str` from one lifetime to a `&str` of
|
/// A dangerous function that blindly casts a `&str` from one lifetime to a `&str` of
|
||||||
/// another lifetime. This is mainly used to let us push a block-local variable into the
|
/// another lifetime. This is mainly used to let us push a block-local variable into the
|
||||||
/// current `Scope` without cloning the variable name. Doing this is safe because all local
|
/// current [`Scope`][crate::Scope] without cloning the variable name. Doing this is safe because all local
|
||||||
/// variables in the `Scope` are cleared out before existing the block.
|
/// variables in the [`Scope`][crate::Scope] are cleared out before existing the block.
|
||||||
///
|
///
|
||||||
/// Force-casting a local variable's lifetime to the current `Scope`'s larger lifetime saves
|
/// Force-casting a local variable's lifetime to the current [`Scope`][crate::Scope]'s larger lifetime saves
|
||||||
/// on allocations and string cloning, thus avoids us having to maintain a chain of `Scope`'s.
|
/// on allocations and string cloning, thus avoids us having to maintain a chain of [`Scope`][crate::Scope]'s.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn unsafe_cast_var_name_to_lifetime<'s>(name: &str) -> &'s str {
|
pub fn unsafe_cast_var_name_to_lifetime<'s>(name: &str) -> &'s str {
|
||||||
// WARNING - force-cast the variable name into the scope's lifetime to avoid cloning it
|
// WARNING - force-cast the variable name into the scope's lifetime to avoid cloning it
|
||||||
|
29
src/utils.rs
29
src/utils.rs
@ -15,11 +15,11 @@ use crate::stdlib::{
|
|||||||
};
|
};
|
||||||
use crate::Shared;
|
use crate::Shared;
|
||||||
|
|
||||||
/// A hasher that only takes one single `u64` and returns it as a hash key.
|
/// A hasher that only takes one single [`u64`] and returns it as a hash key.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics when hashing any data type other than a `u64`.
|
/// Panics when hashing any data type other than a [`u64`].
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
|
||||||
pub struct StraightHasher(u64);
|
pub struct StraightHasher(u64);
|
||||||
|
|
||||||
@ -49,11 +49,11 @@ impl BuildHasher for StraightHasherBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _[INTERNALS]_ Calculate a `u64` hash key from a namespace-qualified function name and parameter types.
|
/// _(INTERNALS)_ Calculate a [`u64`] hash key from a namespace-qualified function name and parameter types.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// Module names are passed in via `&str` references from an iterator.
|
/// Module names are passed in via `&str` references from an iterator.
|
||||||
/// Parameter types are passed in via `TypeId` values from an iterator.
|
/// Parameter types are passed in via [`TypeId`] values from an iterator.
|
||||||
///
|
///
|
||||||
/// # Note
|
/// # Note
|
||||||
///
|
///
|
||||||
@ -67,12 +67,12 @@ pub fn calc_native_fn_hash<'a>(
|
|||||||
calc_fn_hash(modules, fn_name, None, params)
|
calc_fn_hash(modules, fn_name, None, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _[INTERNALS]_ Calculate a `u64` hash key from a namespace-qualified function name
|
/// _(INTERNALS)_ Calculate a [`u64`] hash key from a namespace-qualified function name
|
||||||
/// and the number of parameters, but no parameter types.
|
/// and the number of parameters, but no parameter types.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// Module names are passed in via `&str` references from an iterator.
|
/// Module names are passed in via `&str` references from an iterator.
|
||||||
/// Parameter types are passed in via `TypeId` values from an iterator.
|
/// Parameter types are passed in via [`TypeId`] values from an iterator.
|
||||||
///
|
///
|
||||||
/// # Note
|
/// # Note
|
||||||
///
|
///
|
||||||
@ -96,10 +96,10 @@ pub fn get_hasher() -> impl Hasher {
|
|||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate a `u64` hash key from a namespace-qualified function name and parameter types.
|
/// Calculate a [`u64`] hash key from a namespace-qualified function name and parameter types.
|
||||||
///
|
///
|
||||||
/// Module names are passed in via `&str` references from an iterator.
|
/// Module names are passed in via `&str` references from an iterator.
|
||||||
/// Parameter types are passed in via `TypeId` values from an iterator.
|
/// Parameter types are passed in via [`TypeId`] values from an iterator.
|
||||||
///
|
///
|
||||||
/// # Note
|
/// # Note
|
||||||
///
|
///
|
||||||
@ -116,9 +116,9 @@ fn calc_fn_hash<'a>(
|
|||||||
modules.next().is_some().hash(s);
|
modules.next().is_some().hash(s);
|
||||||
// We always skip the first module
|
// We always skip the first module
|
||||||
modules.for_each(|m| m.hash(s));
|
modules.for_each(|m| m.hash(s));
|
||||||
s.write(fn_name.as_bytes());
|
fn_name.hash(s);
|
||||||
if let Some(num) = num {
|
if let Some(num) = num {
|
||||||
s.write_usize(num);
|
num.hash(s);
|
||||||
} else {
|
} else {
|
||||||
params.for_each(|t| t.hash(s));
|
params.for_each(|t| t.hash(s));
|
||||||
}
|
}
|
||||||
@ -127,7 +127,8 @@ fn calc_fn_hash<'a>(
|
|||||||
|
|
||||||
/// The system immutable string type.
|
/// The system immutable string type.
|
||||||
///
|
///
|
||||||
/// An `ImmutableString` wraps an `Rc<String>` (or `Arc<String>` under the `sync` feature)
|
/// An [`ImmutableString`] wraps an [`Rc`][std::rc::Rc]`<`[`String`]`>`
|
||||||
|
/// (or [`Arc`][std::sync::Arc]`<`[`String`]`>` under the `sync` feature)
|
||||||
/// so that it can be simply shared and not cloned.
|
/// so that it can be simply shared and not cloned.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
@ -469,15 +470,15 @@ impl PartialOrd<ImmutableString> for String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ImmutableString {
|
impl ImmutableString {
|
||||||
/// Consume the `ImmutableString` and convert it into a `String`.
|
/// Consume the [`ImmutableString`] and convert it into a [`String`].
|
||||||
/// If there are other references to the same string, a cloned copy is returned.
|
/// If there are other references to the same string, a cloned copy is returned.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn into_owned(mut self) -> String {
|
pub fn into_owned(mut self) -> String {
|
||||||
self.make_mut(); // Make sure it is unique reference
|
self.make_mut(); // Make sure it is unique reference
|
||||||
shared_take(self.0) // Should succeed
|
shared_take(self.0) // Should succeed
|
||||||
}
|
}
|
||||||
/// Make sure that the `ImmutableString` is unique (i.e. no other outstanding references).
|
/// Make sure that the [`ImmutableString`] is unique (i.e. no other outstanding references).
|
||||||
/// Then return a mutable reference to the `String`.
|
/// Then return a mutable reference to the [`String`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn make_mut(&mut self) -> &mut String {
|
pub fn make_mut(&mut self) -> &mut String {
|
||||||
shared_make_mut(&mut self.0)
|
shared_make_mut(&mut self.0)
|
||||||
|
@ -8,7 +8,7 @@ fn test_eval() -> Result<(), Box<EvalAltResult>> {
|
|||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
r#"
|
r#"
|
||||||
eval("40 + 2")
|
eval("40 + 2")
|
||||||
"#
|
"#
|
||||||
)?,
|
)?,
|
||||||
42
|
42
|
||||||
);
|
);
|
||||||
@ -16,6 +16,33 @@ fn test_eval() -> Result<(), Box<EvalAltResult>> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_eval_blocks() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
r#"
|
||||||
|
let x = 999;
|
||||||
|
|
||||||
|
eval("let x = x + 123");
|
||||||
|
|
||||||
|
let y = if x > 0 {
|
||||||
|
eval("let x = 42");
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
x + y
|
||||||
|
"#
|
||||||
|
)?,
|
||||||
|
1164
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
fn test_eval_function() -> Result<(), Box<EvalAltResult>> {
|
fn test_eval_function() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
@ -22,9 +22,11 @@ fn test_module_sub_module() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut sub_module2 = Module::new();
|
let mut sub_module2 = Module::new();
|
||||||
sub_module2.set_var("answer", 41 as INT);
|
sub_module2.set_var("answer", 41 as INT);
|
||||||
|
|
||||||
let hash_inc = sub_module2.set_fn_1("inc", |x: INT| Ok(x + 1));
|
let hash_inc = sub_module2.set_fn_1_mut("inc", FnNamespace::Internal, |x: &mut INT| Ok(*x + 1));
|
||||||
let hash_super_inc = sub_module2.set_fn_1_mut("super_inc", |x: &mut INT| Ok(*x + 1));
|
sub_module2.set_fn_1_mut("super_inc", FnNamespace::Global, |x: &mut INT| Ok(*x + 1));
|
||||||
sub_module2.set_fn_namespace(hash_super_inc, FnNamespace::Global);
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
sub_module2.set_getter_fn("doubled", |x: &mut INT| Ok(*x * 2));
|
||||||
|
|
||||||
sub_module.set_sub_module("universe", sub_module2);
|
sub_module.set_sub_module("universe", sub_module2);
|
||||||
module.set_sub_module("life", sub_module);
|
module.set_sub_module("life", sub_module);
|
||||||
@ -57,6 +59,11 @@ fn test_module_sub_module() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert!(engine
|
assert!(engine
|
||||||
.eval::<INT>("inc(question::life::universe::answer)")
|
.eval::<INT>("inc(question::life::universe::answer)")
|
||||||
.is_err());
|
.is_err());
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>("question::life::universe::answer.doubled")?,
|
||||||
|
82
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>("super_inc(question::life::universe::answer)")?,
|
engine.eval::<INT>("super_inc(question::life::universe::answer)")?,
|
||||||
42
|
42
|
||||||
@ -72,17 +79,16 @@ fn test_module_resolver() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut module = Module::new();
|
let mut module = Module::new();
|
||||||
|
|
||||||
module.set_var("answer", 42 as INT);
|
module.set_var("answer", 42 as INT);
|
||||||
module.set_fn_4("sum".to_string(), |x: INT, y: INT, z: INT, w: INT| {
|
module.set_fn_4("sum", |x: INT, y: INT, z: INT, w: INT| Ok(x + y + z + w));
|
||||||
Ok(x + y + z + w)
|
module.set_fn_1_mut("double", FnNamespace::Global, |x: &mut INT| {
|
||||||
});
|
|
||||||
module.set_fn_1_mut("double".to_string(), |x: &mut INT| {
|
|
||||||
*x *= 2;
|
*x *= 2;
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
module.set_fn_4_mut(
|
module.set_fn_4_mut(
|
||||||
"sum_of_three_args".to_string(),
|
"sum_of_three_args",
|
||||||
|
FnNamespace::Internal,
|
||||||
|target: &mut INT, a: INT, b: INT, c: rhai::FLOAT| {
|
|target: &mut INT, a: INT, b: INT, c: rhai::FLOAT| {
|
||||||
*target = a + b + c as INT;
|
*target = a + b + c as INT;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -105,6 +111,15 @@ fn test_module_resolver() -> Result<(), Box<EvalAltResult>> {
|
|||||||
42
|
42
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert!(engine
|
||||||
|
.eval::<INT>(
|
||||||
|
r#"
|
||||||
|
import "hello" as h;
|
||||||
|
sum(h::answer, -10, 3, 7)
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
.is_err());
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
r#"
|
r#"
|
||||||
@ -141,6 +156,17 @@ fn test_module_resolver() -> Result<(), Box<EvalAltResult>> {
|
|||||||
)?,
|
)?,
|
||||||
42
|
42
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
r#"
|
||||||
|
import "hello" as h;
|
||||||
|
let x = 21;
|
||||||
|
double(x);
|
||||||
|
x
|
||||||
|
"#
|
||||||
|
)?,
|
||||||
|
42
|
||||||
|
);
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
{
|
{
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use rhai::{Dynamic, Engine, EvalAltResult, LexError, ParseError, ParseErrorType, INT, NO_POS};
|
use rhai::{Dynamic, Engine, EvalAltResult, LexError, ParseError, ParseErrorType, Position, INT};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
||||||
@ -52,6 +52,16 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
|||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
r"
|
||||||
|
let x = 0;
|
||||||
|
exec |x| -> { x += 1 } while x < 42;
|
||||||
|
x
|
||||||
|
"
|
||||||
|
)?,
|
||||||
|
42
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
r"
|
r"
|
||||||
@ -91,13 +101,15 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
|
|||||||
Box::new(ParseErrorType::BadInput(LexError::ImproperSymbol(
|
Box::new(ParseErrorType::BadInput(LexError::ImproperSymbol(
|
||||||
s.to_string(),
|
s.to_string(),
|
||||||
))),
|
))),
|
||||||
NO_POS,
|
Position::NONE,
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
0,
|
1,
|
||||||
|_, inputs| {
|
|context, inputs| {
|
||||||
|
context.scope.push("foo", 999 as INT);
|
||||||
|
|
||||||
Ok(match inputs[0].get_variable_name().unwrap() {
|
Ok(match inputs[0].get_variable_name().unwrap() {
|
||||||
"world" => 123 as INT,
|
"world" => 123 as INT,
|
||||||
"kitty" => 42 as INT,
|
"kitty" => 42 as INT,
|
||||||
@ -109,6 +121,11 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
assert_eq!(engine.eval::<INT>("hello world")?, 123);
|
assert_eq!(engine.eval::<INT>("hello world")?, 123);
|
||||||
assert_eq!(engine.eval::<INT>("hello kitty")?, 42);
|
assert_eq!(engine.eval::<INT>("hello kitty")?, 42);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>("let foo = 0; (hello kitty) + foo")?,
|
||||||
|
1041
|
||||||
|
);
|
||||||
|
assert_eq!(engine.eval::<INT>("(hello kitty) + foo")?, 1041);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*engine.compile("hello hey").expect_err("should error").0,
|
*engine.compile("hello hey").expect_err("should error").0,
|
||||||
ParseErrorType::BadInput(LexError::ImproperSymbol("hey".to_string()))
|
ParseErrorType::BadInput(LexError::ImproperSymbol("hey".to_string()))
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use rhai::{Engine, EvalAltResult, Scope, INT, NO_POS};
|
use rhai::{Engine, EvalAltResult, Position, Scope, INT};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_var_scope() -> Result<(), Box<EvalAltResult>> {
|
fn test_var_scope() -> Result<(), Box<EvalAltResult>> {
|
||||||
@ -67,7 +67,7 @@ fn test_var_resolver() -> Result<(), Box<EvalAltResult>> {
|
|||||||
"MYSTIC_NUMBER" => Ok(Some((42 as INT).into())),
|
"MYSTIC_NUMBER" => Ok(Some((42 as INT).into())),
|
||||||
// Override a variable - make it not found even if it exists!
|
// Override a variable - make it not found even if it exists!
|
||||||
"DO_NOT_USE" => {
|
"DO_NOT_USE" => {
|
||||||
Err(EvalAltResult::ErrorVariableNotFound(name.to_string(), NO_POS).into())
|
Err(EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::NONE).into())
|
||||||
}
|
}
|
||||||
// Silently maps 'chameleon' into 'innocent'.
|
// Silently maps 'chameleon' into 'innocent'.
|
||||||
"chameleon" => context
|
"chameleon" => context
|
||||||
@ -75,7 +75,7 @@ fn test_var_resolver() -> Result<(), Box<EvalAltResult>> {
|
|||||||
.get_value("innocent")
|
.get_value("innocent")
|
||||||
.map(Some)
|
.map(Some)
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
EvalAltResult::ErrorVariableNotFound(name.to_string(), NO_POS).into()
|
EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::NONE).into()
|
||||||
}),
|
}),
|
||||||
// Return Ok(None) to continue with the normal variable resolution process.
|
// Return Ok(None) to continue with the normal variable resolution process.
|
||||||
_ => Ok(None),
|
_ => Ok(None),
|
||||||
|
@ -24,3 +24,28 @@ fn test_while() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_do() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
r"
|
||||||
|
let x = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
x += 1;
|
||||||
|
if x > 5 { break; }
|
||||||
|
if x > 3 { continue; }
|
||||||
|
x += 3;
|
||||||
|
} while x < 10;
|
||||||
|
|
||||||
|
x
|
||||||
|
",
|
||||||
|
)?,
|
||||||
|
6
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user