Merge pull request #295 from schungx/master

Update comments with links, do loops and fix custom syntax variables searching.
This commit is contained in:
Stephen Chung 2020-11-21 14:28:29 +08:00 committed by GitHub
commit 082ca81c30
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 1211 additions and 945 deletions

View File

@ -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

View File

@ -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();

View File

@ -215,15 +215,19 @@ 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 {
if ns != FnNamespaceAccess::Global {
return Err(syn::Error::new(key.span(), "conflicting namespace")); 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 {
if ns != FnNamespaceAccess::Internal {
return Err(syn::Error::new(key.span(), "conflicting namespace")); return Err(syn::Error::new(key.span(), "conflicting namespace"));
} }
}
namespace = Some(FnNamespaceAccess::Internal); namespace = Some(FnNamespaceAccess::Internal);
} }
(attr, _) => { (attr, _) => {

View File

@ -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_literal.span(),
);
set_fn_stmts.push(
syn::parse2::<syn::Stmt>(quote! {
m.set_fn(#fn_literal, FnNamespace::#ns_str, FnAccess::Public, &[#(#fn_input_types),*],
#fn_token_name().into()); #fn_token_name().into());
}), })
FnNamespaceAccess::Internal => syn::parse2::<syn::Stmt>(quote! {
m.set_fn(#fn_literal, FnNamespace::Internal, FnAccess::Public, &[#(#fn_input_types),*],
#fn_token_name().into());
}),
}
.unwrap(), .unwrap(),
); );
} }

View File

@ -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
} }

View File

@ -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

View File

@ -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)

View File

@ -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 |

View File

@ -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!(),
}, },
@ -337,7 +337,7 @@ 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`. |

View File

@ -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)
@ -84,7 +84,7 @@ 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
View 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;
```

View File

@ -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();
} }

View File

@ -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();
} }

View File

@ -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);

View File

@ -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(_, _)

View File

@ -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> {

View File

@ -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,17 +2511,25 @@ 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(
"Size of object map".to_string(),
Position::NONE,
)
.into(); .into();
} }
@ -2507,21 +2537,21 @@ impl Engine {
} }
/// 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(

View File

@ -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
/// ///

View File

@ -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"))]

View File

@ -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,)*)

View File

@ -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,8 +385,8 @@ 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),
@ -394,15 +394,17 @@ impl Engine {
EvalAltResult::ErrorInFunctionCall( EvalAltResult::ErrorInFunctionCall(
format!("{} > {}", fn_def.name, name), format!("{} > {}", fn_def.name, name),
err, err,
NO_POS, Position::NONE,
) )
.into() .into()
} }
// System errors are passed straight-through // System errors are passed straight-through
err if err.is_system_exception() => Err(Box::new(err)), err if err.is_system_exception() => Err(Box::new(err)),
// Other errors are wrapped in `ErrorInFunctionCall` // Other errors are wrapped in `ErrorInFunctionCall`
_ => EvalAltResult::ErrorInFunctionCall(fn_def.name.to_string(), err, NO_POS) _ => {
.into(), EvalAltResult::ErrorInFunctionCall(fn_def.name.to_string(), err, Position::NONE)
.into()
}
}); });
// Remove all local variables // Remove all local variables
@ -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(),
} }

View File

@ -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
/// ///

View File

@ -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())

View File

@ -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),*))
); );

View File

@ -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")]

View File

@ -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
/// ///

View File

@ -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());

View File

@ -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
/// ///

View File

@ -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() {

View File

@ -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, _) => {
match **block {
// loop { break; } -> Noop
Stmt::Break(pos) => {
// Only a single break statement
state.set_dirty(); state.set_dirty();
*stmt = Stmt::Noop(pos) optimize_stmt(block.as_mut(), state, false);
} *stmt = mem::take(block.as_mut());
_ => (),
} }
// do { block } while|until expr
Stmt::Do(block, condition, _, _) => {
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);
} }
}); });

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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,7 +83,10 @@ 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(
format!("Invalid radix: '{}'", radix),
Position::NONE,
)
.into(); .into();
} }
@ -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,7 +204,10 @@ 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(
format!("Integer overflow: to_int({})", x),
Position::NONE,
)
.into() .into()
} else { } else {
Ok((x.trunc() as INT).into()) Ok((x.trunc() as INT).into())
@ -210,7 +216,10 @@ 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(
format!("Integer overflow: to_int({})", x),
Position::NONE,
)
.into() .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()
}) })

View File

@ -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) {
// Later packages override previous ones. if self.0.is_none() {
self.0.insert(0, package); self.0 = Some(Default::default());
} }
/// Does the specified function hash key exist in the `PackagesCollection`? // Later packages override previous ones.
self.0.as_mut().unwrap().insert(0, package);
}
/// 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
/// ///

View File

@ -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();
} }

View File

@ -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))
} }
} }

View File

@ -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,8 +127,12 @@ impl<'e> ParseState<'e> {
self.allow_capture = true self.allow_capture = true
} }
if self.always_search {
None
} else {
index 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 ...
let (guard, token_pos) = match input.next().unwrap() {
(Token::While, pos) => {
ensure_not_statement_expr(input, "a boolean")?; ensure_not_statement_expr(input, "a boolean")?;
let guard = parse_expr(input, state, lib, settings.level_up())?; (parse_expr(input, state, lib, settings.level_up())?, pos)
ensure_not_assignment(input)?; }
(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

View File

@ -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() {

View File

@ -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 {

View File

@ -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,7 +37,11 @@ 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(
error.into(),
self.value.type_name().into(),
Position::NONE,
)
.into() .into()
} }
fn deserialize_int<V: Visitor<'de>>( fn deserialize_int<V: Visitor<'de>>(
@ -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,
} }

View File

@ -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;

View File

@ -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,7 +283,11 @@ 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(
"Dynamic".into(),
"map".into(),
Position::NONE,
)
.into(); .into();
} }
@ -291,7 +295,11 @@ impl Serializer for &mut DynamicSerializer {
#[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(
"Dynamic".into(),
"array".into(),
Position::NONE,
)
.into(); .into();
} }
@ -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,7 +346,11 @@ 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(
"Dynamic".into(),
"map".into(),
Position::NONE,
)
.into(); .into();
} }
@ -358,7 +375,11 @@ 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(
"Dynamic".into(),
"map".into(),
Position::NONE,
)
.into(); .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();

View File

@ -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,7 +16,11 @@ 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(
type_name::<T>().into(),
"string".into(),
Position::NONE,
)
.into() .into()
} }
} }

View File

@ -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,

View File

@ -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,

View File

@ -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

View File

@ -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)

View File

@ -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>> {

View File

@ -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!(

View File

@ -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()))

View File

@ -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),

View File

@ -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(())
}